omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    PIL Image and Closing DataStreams/ImageFile

    Pythonista
    warning image pil openfile pillow
    2
    3
    5251
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • stephen
      stephen last edited by stephen

      good day Pythonistas!


      I was having an issue with Image|pillow|PIL (your choice 😅) with File Context for Image.open(), Image.load() and Image.close().

      I was reciveing 19 warnings for: ↴


      __warningregistry__[("unclosed file <_io܂BufferedReader name='my_image܂png'>", <class 'ResourceWarning'>, 8)]
      

      • This Warning was located at the end (bottom) of the Console Inspector

      Example of my use at the time ↴


      from PIL import Image
      
      img = Image.open('my_image.png')
      img.load() # This will have been my first issue besides bad practice
      img = img.resize((256, 256), 1) # The 1 in pos2 is for AA, original was 3600x3600
      img.save('resized.png', 'png') # pos2 not needed for str type filename but good practice
      

      Example Corrected

      ⒈ General Implementation ↴


      from PIL import Image
      
      with Image.open('my_image.png') as img:
      	img = img.resize((256, 256), 1)
      	img.save('resized.png', 'png')
      

      ⒉ Dated (I believe) Implementation ↴


      from PIL import Image
      
      try:
      	img = Image.open('my_image.png')
      	img = img.resize((256, 256), 1)
      	img.save('resized.png', 'png')
      	
      finally:
      	img.close()
      

      ⒊ Alternative (this is what I went with) Implementation ↴


      from PIL import Image
      
      try:
      	with open('my_image.png', 'rb') as f:
      	img = Image.open(f)
      	img = img.resize((256, 256), 1)
      	img.save('resized.png', 'png')
      	
      	
      finally:
      	f.close()
      	img.close()
      

      This is how I understand whats going on..

      1. img.open(fn) Opens the image BUT does not load any of the data

      2. img.load() Loads the pixel data to the stream

      3. Calling on an operation to the Image Object the first time ALSO load pixel data to the stream. In this case img.resize((w, h), filter)

      4. img.save(fn, format) Closes Image.

      problem was both img.load() and img.resize((w, h), filter) each loaded thier own separate DataStream.. i assume the auto-implemented steam was the primary.

      ###Finally

      If your running your PIL Image through operations... one shouldn't call load() explicitly if you working with Single Paged Images.



      This worked wonderfully... till i found that ☝︎ still remained...

      my_image.tiff ...

      Turns out that "tiff format will always be treated as multipage..

      " 𝄆𝄞♫♪⁼𝄫 ... should of read the brochure 🧐..."

      I tried many Approches to this part.. And after hours of wonderfull Warning Messages, decided i didnt NEED the tiff file so i changed format to png and problem "disapeared"..



      According to everything I gathered, both of my issues are very common while using Pill across the whole Python comunity. so I came here fore hopes that my first segment will help someone and that even though I removed my problem I would like to try to understand what is happening with the tiff inside PIL to produce such an issue..

      Thank You!


      Here is my current snippet for anyone that wanted or needed ↴


      
      class Loop(scene.Scene, metaclass=GCore):
      	def __init__(self, *args, **kwargs):
      		s.Scene.__init__(self, *args, **kwargs)
      		super().setup()
      		GCore.PopulateCache(self)
      		
      
      	def setup(self):
      		try:
      			```
      			cache starts with allnthe values being Str paths
      			pulled from .txt file.
      			
      			and then here wevchange that to out Texture objects to use in 
      			the next initialization stage.
      			```
      			for k,v in self.cache.items():
      			
      				with open(v, 'rb') as f:
      				
      					img = Image.open(f)	
      					
      					img=img.resize((int(img.size[0]/2*self.ScaleMod), 
      									int(img.size[1]/2*self.ScaleMod)), 1)
      					
      					```
      					Convert Image back to Byte Data so we can implement
      					the scale for Retina Screens with from_data in ui.Image
      					```
      					iodata = io.BytesIO()	
      					img.save(iodata, 'png')
      					
      					ns.cache[k]=scene.Texture(ui.Image.from_data(iodata.getvalue(), 
      													scene.get_screen_scale()))		
      		```
      		finally close all image and datastream objects.
      		then add the scene.Texture to the Cache Dict.
      		```										
      		finally:
      			iodata.close()
      		
      			f.close()
      			img.close()
      			del img
      			del f
      			del iodata
      			
      			for x in locals():
      				print(x)
      				
      



      mikael 1 Reply Last reply Reply Quote 0
      • mikael
        mikael @stephen last edited by

        @stephen, I think your examples share an issue where you assign the resized and copied image to a variable with the same name as the original image, thus losing the reference to the original and then closing the wrong thing.

        1 Reply Last reply Reply Quote 0
        • stephen
          stephen last edited by stephen

          Hello there @mikael !

          I see what your saying but what you cannot see is originally (and before i fixed the duplicate streams) i had somthing similar to this:

          
          img = Image.open('my_image.png')
          img.load() 
          img_rs = img.resize((256, 256), 1)
          
          with io.BytesIO()	as iodata:
              img_rs.save(iodata, 'png')
          					
              texture=scene.Texture(ui.Image.from_data(iodata.getvalue(), ...
          								
          ...
          

          i changed it to img=img... to reduce code knowing ill never need that exact ref to original again during this loop session.reason is i place the Texture object in a cache dict and from here on is called from there 🤓🤓

          ##EDIT

          @mikael
          I also forgot to include my finally block that handles any mishaps once caching is complete..

              finally:
                      iodata.close()
                  
                      f.close()
                      img.close()
                      del img
                      del f
                      del iodata
                      
                      for x in locals():
                          print(x)
          
          1 Reply Last reply Reply Quote 0
          • First post
            Last post
          Powered by NodeBB Forums | Contributors