裁切时PIL创建空白图像

时间:2019-06-18 02:54:46

标签: python python-imaging-library crop

我正在处理约50MB(约19000像素x 25500像素)的图像文件,并将它们裁剪为4705像素x 8375像素的图像。我编写了一个for循环,该循环遍历了95张图像的文件夹。在大多数情况下,裁剪效果很好,但是在随机图像上,当代码裁剪图像时,其子图像将显示为空白图像。发生这种情况时,12张图像中的第一张将正常显示(正确裁剪),而其余11张图像将空白显示。如果没有发生此问题,则可以正确裁剪所有12张图像。

我正在MBP 10.14.5上的Spyder 3.3.4上运行代码。 PIL是版本1.1.7和Python 3.6。我检查过我是否在图像之间正确循环。重新运行失败的图像(裁剪不正确),当我在其上裁剪而不是for循环的一部分时,它们可以正常工作。

stepCounter = 4705

for folder in os.listdir(location):
    if folder == "MyFolder":
        for file in os.listdir(location+folder):
            resetCounter = -8375
            for i in range(12):
                print("Iteration", i, " on file", file)
                if i%4 == 0:
                    resetCounter += 8375
                    left = 0 
                    top = 0 + resetCounter
                    right = 4705
                    bottom = 8375 + resetCounter
                    fileLocation = location + folder + "/" + file
                    newLocation = location + folder + "/" + file[:-4] + str(i+1) + ".jpg"
                    img = Image.open(fileLocation)
                    img = img.crop((left, top, right, bottom))
                    img.save(newLocation)
                    img.close()
                else:
                    left = left + stepCounter
                    top = top 
                    right = right + stepCounter
                    bottom = bottom
                    fileLocation = location + folder + "/" + file
                    newLocation = location + folder + "/" + file[:-4] + str(i+1) + ".jpg"
                    img = Image.open(fileLocation)
                    img = img.crop((left, top, right, bottom))
                    img.save(newLocation)
                    img.close()
    else:
        print("Skipping", folder)

我再次希望这些图像是较大图像的子图像,而不是空白图像。不知道这是内存问题还是其他与代码无关的问题。

1 个答案:

答案 0 :(得分:1)

从看程序很难分辨- 如果每个图像都像您所描述的那样,它将正常工作-但是,您的代码 用于命名目标图像的程序没有使用防错的编程模式,因为它们没有充分利用某些语言工具的优势。该代码现在可以运行,但是可能要经过反复试验才能到达那里。所以,我敢打赌,在某个时间点, 该脚本已运行,在生成 目标切片文件。此运行确实覆盖了一些图像,这些图像现在是 单个切片的大小。

实际上,如果在PIL图像对象上超出图像像素大小的地方调用crop方法,则不会出现错误:会自动创建一个零位(黑色)图像。

您没有提及,但是如果您要检查切片的图像 现在失败了,原因是您的原件可能已经裁切成较小的尺寸

此外,由于不会检查要裁剪的 张图像,因此,如果您多次运行此代码,则已保存的裁剪图像将再次被处理为大图像。

>

也就是说,在此脚本的第一次运行中,将保存一个“ image.jpg”并通过“ image12.jpg”将其裁剪为“ image1.jpg”-但是在第二次运行中,将分别使用这些“ imageN”。 jpg”将变为“ imageNM.jpg”-“ M”再次从“ 1”变为“ 12”。同样,第一次运行的“ image11.jpg”和“ image12.jpg”的第11和第12张图像将替换为第二次运行的第一和第二输出。

因此,如果您仍可以使用25500 x 19000像素的图像还原原始文件夹,并且只有这些像素,则可以运行此代码的重构版本,以确保不重新处理已制成的切片。一次检查图像宽度可以避免这种情况,并且更明确的命名架构也可能更好。

此外,作为一些编码建议:

  • 利用Python的“ f字符串”来操纵名称,
  • 使用Python的pathlib.Path来操纵文件夹名称并获取图像文件(这是Python 3.5的新功能,并且周围有稀疏的示例),
  • 避免在代码周围使用硬编码的数字-只需将其作为变量放在清单的开头
  • 在x和y上使用显式迭代,而不是线性计数器,然后使用一些容易出错的算法来达到裁剪的极限
  • 最后,如上所述,请注意不要多次重复读取相同的图像,该脚本看起来更容易使用,并且不易出错。

由于图像较大,您还有可能确实遇到了PIL中的错误,并且第二次以后无法加载大图像-但这不太可能。与此相关的问题宁可通过MemoryError停止程序。

import pathlib
from PIL import Image

# Whatever code you have to get the "location" variable
...

x_step = 8375
y_step = 4705

full_width = 25500

for image_path in pathlib.Path(location).glob("**/*.jpg"):
    # the Path.glob method automatically iterates in subfolders, for
    # all files matching the expressions
    if "MyFolder" not in image_path.parts:
        # Skips processing if "MyFolder" not in the relative path to the image file
        continue
    # Loads the original image a single time:
    img = Image.open(image_path)
    if img.width < full_width:
        if "crop" not in image_path.name:
            # Do not print warnings for slices - just skip then
            print(f"Image at {image_path} has width of only {img.width}. Skipping")
        continue
    for y in range(3):
        for x in range(4):
            print(f"Iteration {y * 4 + x} on file {image_path.name}")
            # Store the cropped image object into a new variable - the original image is kept on "img"
            left = x_step * x
            top = y_step * y
            sliced_img = img.crop((left, top, left + x_step, top + y_step))
            new_path = image_path.with_name(f"{image_path.stem}_crop_{y * 4 + x + 1}{image_path.suffix}")
            sliced_img.save(new_path)