Reputation: 1
I'm working with image files that are ~50MB (~19000 pixels x 25500 pixels) and cropping them into images of size 4705 pixels x 8375 pixels. I wrote an for-loop that iterates through a folder of 95 images. For the most part, the cropping works fine, but on random images, when the code crops the image, it sub-image comes out as a blank image. When this occurs, the first of the 12 images will come out normal (cropped correctly), but the remaining 11 images will come out blank. When the issue does not happen all 12 images come out cropped correctly.
I'm running the code on Spyder 3.3.4 on a MBP 10.14.5. PIL is version 1.1.7 and Python 3.6. I've checked that I'm looping correctly across the images. Rerunning images that have failed (cropped incorrectly), work fine when I crop them on their on and not a part of a for loop.
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)
Again, I expect the images to be sub-images of the larger image, not blank images. Not sure if this is a memory issue, or something else not code related.
Upvotes: 0
Views: 3307
Reputation: 110631
It is hard to tell from looking the program - it would work if each image was like you were describing - however, your code for naming the target images is not using programming patterns that are error proof, as they are not taking the best advantage of some of the language facilities. That code works now, but there might have been some trial and error to get there. So, my bet is that at some point in time, an incorrect version of this script was run, that had an incorrect behavior on generating the target slice files. This run did overwrite some of the images, which now are the size of a single slice.
Actually, if one calls the crop
method beyond the images pixel sizes on a PIL image object, no error is raised: a zeroed (black) image is silently created instead.
You did not mention, but if you will check the images for which the slicing is failing now, the case is that your originals are likely already cropped to the smaller size.
Also, as there are no checks for which images you are cropping, if you run this code more than once, the crops already saved will be processed as if they were big images again.
That is, on the first run of this script, an "image.jpg" would be saved and cropped to "image1.jpg" through "image12.jpg" - but on the second run, each of these "imageN.jpg" would become "imageNM.jpg" - with "M" going again from "1" to "12". Also, the 11th and 12th images of the first run "image11.jpg" and "image12.jpg" would be replaced by the first and second outputs of the second run.
So, if you can still restore your original folders with the images that are exactly 25500 x 19000 pixels, and only those, a refactored version of this code could be run, that would ensure not to reprocess the slices already made. A single check for the image width can avoid that and a more explicit naming schema could also be better.
Also, as some coding advice:
pathlib.Path
to manipulate folder names and get to image files (this is new from Python 3.5, and there are sparse examples around), There is also a chance you really did hit a bug in PIL, due to the large image sizes, and the big images are failing to be loaded from the second-time on - but that is rather unlikely. A problem with this would rather stop the program with a 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)
Upvotes: 1