python - PIL 在裁剪时创建空白图像
问题描述
我正在处理约 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)
同样,我希望图像是较大图像的子图像,而不是空白图像。不确定这是内存问题,还是其他与代码无关的问题。
解决方案
通过查看程序很难判断 - 如果每个图像都像您描述的那样,它会起作用 - 但是,您命名目标图像的代码没有使用防错的编程模式,因为它们没有充分利用一些语言设施。该代码现在可以工作,但可能需要一些试验和错误才能到达那里。所以,我敢打赌,在某个时间点,运行了此脚本的不正确版本,在生成目标切片文件时行为不正确。这次运行确实覆盖了一些图像,这些图像现在是单个切片的大小。
实际上,如果crop
在 PIL 图像对象上调用超出图像像素大小的方法,则不会引发错误:而是静默创建零(黑色)图像。
您没有提到,但是如果您现在检查切片失败的图像,情况是您的原件可能已经被裁剪为较小的尺寸。
此外,由于没有检查您正在裁剪哪些图像,如果您多次运行此代码,已保存的裁剪将再次被处理为大图像。
也就是说,在此脚本的第一次运行时,“image.jpg”将被保存并裁剪为“image1.jpg”到“image12.jpg” - 但在第二次运行时,这些“imageN.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)
推荐阅读
- azure-blob-storage - Cannot use azcopy from one blob to another with SAS
- pyspark - 将 jupyter sparkmagic 内核连接到 kerberized livy 服务器的问题
- python-3.x - 此代码未给出新排序列表的值。有人可以告诉我这段代码中的错误吗?
- python - Find N largest elements in a list with a minimum distance
- python - 如何使用 pandas 合并绘图表上的标题?
- hyperledger-fabric - 如何解决此错误 Blockchain_chaincode
- c# - 是否可以在父目录中构建文件?
- javascript - 使用nestjs将hbs渲染保存到变量中
- reactjs - 使用 Typescript 映射对象数组
- javascript - 分配给 axios 响应数据的变量会破坏应用程序