PIL Image and Closing DataStreams/ImageFile
good day Pythonistas!
I was having an issue with
PIL(your choice 😅) with File Context for
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
⒈ 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..
img.open(fn)Opens the image BUT does not load any of the data
img.load()Loads the pixel data to the stream
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)
img.save(fn, format)Closes Image.
problem was both
img.resize((w, h), filter)each loaded thier own separate DataStream.. i assume the auto-implemented steam was the primary.
If your running your
PILImage through operations... one shouldn't call
load()explicitly if you working with Single Paged Images.
This worked wonderfully... till i found that ☝︎ still remained...
Turns out that
"tiffformat 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
tifffile so i changed format to
pngand problem "disapeared"..
According to everything I gathered, both of my issues are very common while using
Pillacross the whole
Pythoncomunity. 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
PILto produce such an issue..
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/2*self.ScaleMod), int(img.size/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)
@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.
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 🤓🤓
I also forgot to include my
finallyblock 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)