【问题标题】:PIL create blank images when croppingPIL 在裁剪时创建空白图像
【发布时间】:2019-10-31 15:08:07
【问题描述】:

我正在处理约 50MB(约 19000 像素 x 25500 像素)的图像文件,并将它们裁剪成大小为 4705 像素 x 8375 像素的图像。我写了一个循环遍历一个包含 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)

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

【问题讨论】:

  • location+folder 应该是os.path.join(location, folder)
  • 是不是有些图片不够大?
  • 图像大于我裁剪的区域。关于 location+folder 与 os.path.join(location, folder) 我可以尝试,但我不明白为什么它会改变间歇性裁剪问题。谢谢!
  • 不太可能,但某些位置可能缺少尾部斜杠?
  • 图片文件名都是用同样的方式构造的(使用循环),更奇怪的是当过程正常工作时,所有图像都是正确的,当过程出错时,第一个子图像是正确的,但后面的 11 个将是空白图像。

标签: python python-imaging-library crop


【解决方案1】:

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

实际上,如果调用crop 方法超出了 PIL 图像对象上的图像像素大小,则不会引发错误:而是静默创建归零(黑色)图像。

你没有提到,但如果你会检查切片的图像 现在失败了,情况是您的原件可能已经被裁剪为较小的尺寸

此外,由于没有检查您正在裁剪的哪些图像,如果您多次运行此代码,已保存的裁剪将再次被处理为大图像。 p>

也就是说,在此脚本的第一次运行时,“image.jpg”将被保存并裁剪为“image1.jpg”到“image12.jpg” - 但在第二次运行时,这些“imageN.jpg”中的每一个都将被保存。 jpg”将变为“imageNM.jpg”——“M”再次从“1”变为“12”。此外,第一次运行的第 11 和第 12 图像“image11.jpg”和“image12.jpg”将被第二次运行的第一个和第二个输出替换。

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

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

  • 利用 Python 的“f-strings”来操作名称,
  • 使用 Python 的 pathlib.Path 来操作文件夹名称并获取图像文件(这是 Python 3.5 中的新功能,周围的示例很少),
  • 避免在代码周围硬编码数字 - 只需将 then 作为变量放在列表的开头
  • 在 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)

【讨论】:

  • 嗨 jsbueno,感谢您的评论。我是编程世界的新手,所以学习最佳实践总是有益的。为了解决您的一些问题,当我裁剪图像时,它们会将原始图像裁剪成单独的文件,而实际上根本没有更改原始图像。 2.就您而言,没有错误检查来检查图像的大小,以确保它的大小正确,所以我在我的代码中使用像素宽度变量对此进行了更正,但仍然没有解决了空白图像问题。 3. 即使在运行你的代码时,我仍然遇到了这个问题
猜你喜欢
  • 2011-04-19
  • 2014-09-01
  • 1970-01-01
  • 2018-05-15
  • 1970-01-01
  • 2010-12-12
  • 2013-05-11
  • 2012-10-01
相关资源
最近更新 更多