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
-
good day Pythonistas!
I was having an issue with
Image
|pillow
|PIL
(your choice 😅) with File Context forImage.open()
,Image.load()
andImage.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..
-
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.load()
andimg.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 callload()
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 topng
and problem "disapeared"..
According to everything I gathered, both of my issues are very common while using
Pill
across the wholePython
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 thetiff
insidePIL
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)
-
@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 🤓🤓
##EDIT
@mikael
I also forgot to include myfinally
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)