Is there a way to open a closed Image object?

Viewed 389

I'm working on a project where I need to open multiple image files and then add filter, resize the image and then save.

My Code is like this.

edited_images = [] # list where edited image object will be saved

def add_filter():
  img_dir = "C:/Users/Images"
  for image in os.listdir(img_dir):
     image = Image.open(image)
     image = image.filter(BLUR)
     edited_images.append(image)
     image.close()

def change_size(): 
  ...

Here I'm closing the Image object each time for memory management. Otherwise, it'll cross 5 GBs of RAM for 500+ images and so on.

Now I want to open those Image objects saved in edited_images to save them. How I can achieve this?

for image in edited_images:
   print(image)

I get these results for above code:

 <PIL.Image.Image image mode=RGB size=1024x1024 at 0x2343078A250>
 <PIL.Image.Image image mode=RGB size=1024x1024 at 0x2343078A220>
 <PIL.Image.Image image mode=RGB size=1024x1024 at 0x2343078AFD0>

When I run this:

for image in edited_images:
   image = Image.open(image)
   image.save("C:\Users\Edited_Images")

For the above code I get these results:

 File "c:\Users\Images\Image Processor.py", line 512, in <module>
  apply_edits(image_folder, selected_images, finalized_edit_selection)
 File "c:\Users\Images\Image Processor.py", line 409, in apply_edits
  image = Image.open(image)
 File "C:\Program Files\Python38\lib\site-packages\PIL\Image.py", line 2913, in open
  prefix = fp.read(16)
 AttributeError: 'Image' object has no attribute 'read'

I need to use these Image objects saved in the edited_images list to save images.

1 Answers

From the documentation on PIL.Image.close:

This operation will destroy the image core and release its memory. The image data will be unusable afterward.

So, first of all, any modification to your images will be lost, if they weren't saved before closing!

The Image object header survives, that's why you get valid output for print(image). Compare this code:

from PIL import Image

image = Image.open('path/to/your/image.png')
print(image, '\n')
print(image.getdata(), '\n')

image.close()
print(image, '\n')
print(image.getdata(), '\n')

The corresponding output could be:

<PIL.PngImagePlugin.PngImageFile image mode=RGB size=400x400 at 0x19C1F291BE0> 

<ImagingCore object at 0x0000019C1F797370> 

<PIL.PngImagePlugin.PngImageFile image mode=RGB size=400x400 at 0x19C1F291BE0> 

Traceback (most recent call last):
  File "...", line 9, in <module>
    print(image.getdata(), '\n')
  File "...\lib\site-packages\PIL\Image.py", line 1282, in getdata
    self.load()
  File "...\lib\site-packages\PIL\ImageFile.py", line 160, in load
    pixel = Image.Image.load(self)
  File "...\lib\site-packages\PIL\Image.py", line 849, in load
    return self.im.pixel_access(self.readonly)
  File "...\lib\site-packages\PIL\_util.py", line 19, in __getattr__
    raise self.ex
ValueError: Operation on closed image

Your actual error is caused by trying to feed some Image object to Image.open, which is simply not possible, the Image object in question closed or not.

The main question is: Why don't you just open, filter, resize, and save each image one after the other in one loop? For example, like so:

img_dir = "C:/Users/Images"
for filename in os.listdir(img_dir):
   image = Image.open(filename)
   image = image.filter(BLUR)
   image = image.resize(...)
   image.save(filename)
   image.close()

Or, if you really want to have all the processes distinct, just save at the end of your add_filter function. Then, you'd need re-opening all files in your change_size function. So, code-wise something like this:

def add_filter():
  img_dir = "C:/Users/Images"
  for filename in os.listdir(img_dir):
     image = Image.open(filename)
     image = image.filter(BLUR)
     image.save(filename)
     image.close()

def change_size(): 
  img_dir = "C:/Users/Images"
  for filename in os.listdir(img_dir):
     image = Image.open(filename)
     image = image.resize(...)
     image.save(filename)
     image.close()

As you see, there's no need for your edited_images list (anymore).

Related