在保存图像时,PIL出现“SystemError: tile cannot extend outside image”的错误。

14

我有这张图片 =>

在此输入图片描述

这里是所有上面黄色框的坐标,它们写在 3.txt 文件中。

#Y   X Height     Width 

46 135 158 118 
46 281 163 104 
67 494 188 83 
70 372 194 101 
94 591 207 98 
252 132 238 123 
267 278 189 105 
320 741 69 141 
322 494 300 135 
323 389 390 124 
380 726 299 157 
392 621 299 108 
449 312 227 93 
481 161 425 150 
678 627 285 91 
884 13 650 437 
978 731 567 158 
983 692 60 43 
1402 13 157 114 

我的意图是裁剪这些框并将所有框保存为图像。我已经编写了相应的代码,但遇到了错误。

这是我的代码 =>

from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
from os import listdir
#from scipy.misc import imsave

ARR = np.empty([1,4])
# print(ARR)

i = 0
k = 0
img = Image.open('3.png')

fo = open("3.txt", "r")
for line in fo:
    if not line.startswith('#'):
        for word in line.split():

            ARR[0][i] = int(word)
            print(int(word))
            # ARR[0][i] = int(word)
            i = i +1

    img2 = img.crop((int(ARR[0][1]), int(ARR[0][0]), int(ARR[0][0] + ARR[0][2]), int(ARR[0][1] + ARR[0][3])))
    name = "new-img" + str(k) + ".png"
    img2.save(name)
    k = k + 1
    i = 0

我遇到了这个错误 =>

Traceback (most recent call last): File "reshape.py", line 26, in img2.save(name) File "/usr/lib/python2.7/dist-packages/PIL/Image.py", line 1468, in save save_handler(self, fp, filename) File "/usr/lib/python2.7/dist-packages/PIL/PngImagePlugin.py", line 624, in _save ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) File "/usr/lib/python2.7/dist-packages/PIL/ImageFile.py", line 462, in _save e.setimage(im.im, b) SystemError: tile cannot extend outside image

我该如何解决这些问题?


1
错误提示为 图块不能超出图像范围,这意味着您正在尝试裁剪超出图像尺寸的区域。请检查文本文件中是否包含正确的坐标。您可能会对图像的坐标系统感到困惑(我认为)。在继续之前,请加强对此方面的了解... - Jeru Luke
谢谢,我认为你是正确的。@JeruLuke - Sudip Das
2
完成后,代码为:img2 = img.crop((int(ARR[0][0]), int(ARR[0][1]), int(ARR[0][0] + ARR[0][2]), int(ARR[0][1] + ARR[0][3])))。感谢@JeruLuke的帮助,它已经可以正常运行了。 - Sudip Das
1
太棒了!!我在OpenCV中犯了同样的错误很多次。 - Jeru Luke
5个回答

17

关于评论中提到的错误,是由于将坐标不正确地传递给了PIL的crop()函数所致。

文档所述,该函数返回一个图像,需要输入一个四元组(xywidthheight)。

在给定的文本文件中,y坐标在第一列中提到,x坐标在第二列中提到。然而,crop()函数接受的x坐标值作为第一个参数,y坐标值作为第二个参数。

同样适用于OpenCV。


2
指出对我来说AmirHosseins的答案实际上是正确的。参数应该是imageScreenshot.crop((x, y, x + width, y + height))而不是imageScreenshot.crop((x, y, width, height)) - Max Kuchenkiller
1
Pillow文档表示两种方式都是正确的,尽管我发现上述答案也能正常工作。 - kmario23
在相应的文档中我没有看到这样的信息。它说“四个坐标;左,上,右,下”。这意味着该函数需要该区域的四个坐标。最后两个参数不是宽度和高度。相反,如文档中所解释的那样,是x+width和y+height。也许人们会因为其他函数采用(x,y,width,height)(例如ImageDraw.rectangle)而感到困惑。 - Can

12

在互联网上提到的方法如下:

imageScreenshot.crop((x, y, width, height))

但是正确的方法是这样的:

imageScreenshot.crop((x, y, x + width, y + height))

这意味着您应将x添加到width,并将y添加到height


这是一个简单的示例(driver适用于Python Selenium):

def screenShotPart(x, y, width, height) -> str:
    screenshotBytes = driver.get_screenshot_as_png()
    imageScreenshot = Image.open(BytesIO(screenshotBytes))
    imageScreenshot = imageScreenshot.crop((x, y, x + width, y + height))
    imagePath = pathPrefix + "_____temp_" + str(time.time()).replace(".", "") + ".png"
    imageScreenshot.save(imagePath)

希望它能有所帮助。


4

在我的情况下,问题在于我指定了起始和结束坐标,其中起始的 X 和 Y 坐标并不总是小于结束的 X 和 Y。您不能这样做。

例如,

起点:(0,50) 终点:(50,0)

这些坐标对我来说是有意义的,但实际上应该指定为:

起点:(0,0) 终点:(50,50)

从视觉上看是相同的矩形,但后者是 Pillow 裁剪所必需的。


0

我在根据注释文本裁剪对象时遇到了同样的问题,但代码是正确的,不像上面所说的那样:

imageScreenshot.crop((x, y, width, height))

应该是

imageScreenshot.crop((x, y, x + width, y + height))

错误在注释中,值为0。我修改了该值并解决了问题。

我的代码是:

tmp = [x1, y1, x2, y2]
img2 = img.crop(tmp)
sav_path = 'valid/result' + '/' + l[0] + '/' + str(linec) + img_name[1] 
img2 = img2.save(sav_path)`

0

如果您正在使用DeepLearning软件来检测图像中的对象,并尝试剪切掉检测结果,那么您很可能会看到以下错误信息

我在使用ImageAI检测图像中的对象时遇到了同样的错误。 我通过编辑虚拟环境中PIL包中的ImageFile.py文件来解决了这个问题。

ImageFile位于

\home\myuser\anaconda3\envs\envsName\lib\python3.7\site-packages\PIL\ImageFile.py

找到以 _save 开头的函数行:

def _save(im, fp, tile, bufsize=0):

之后:

 tile.sort(key=_tilesort)
    # FIXME: make MAXBLOCK a configuration parameter
    # It would be great if we could have the encoder specify what it needs
    # But, it would need at least the image size in most cases. RawEncode is
    # a tricky case.

插入:

tile = [e for e in tile if e[1][2] > 0 and e[1][3]>0]

这将跳过保存所有检测到的对象,其宽度和高度与图像(瓦片)的大小不匹配,有时会报告为0,可能是因为检测到的对象仅部分显示在图像边缘。

------------

但是,如果您有一个情况,您知道所有图像都是1920x1080,并且您想要丢弃所有未报告大致相同大小的图像,那么您可以执行以下操作。

tile = [e for e in tile if e[1][2] > 1720 and e[1][3]>880]

为了允许保存较小的、可能仍然正确的图像(瓦片),宽度和高度被减少了200像素,这将丢弃所有其他图像。

来源


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接