Forum Archive

How to set the image of an ImageView to an image from Pillow?

halloleooo

I have a image in the pillow format PIL.Image (generate via Image.new()) and I want to display it in a ui.ImageView component.

ImageView has an image property, but it is of type ui.Image, not of type PIL.Image.

What can I do? How do I convert PIL.Image to ui.Image?

Thanks heaps for any pointers!

cvp

@halloleooo use

import io
def pil2ui(imgIn):
  with io.BytesIO() as bIO:
    imgIn.save(bIO, 'PNG')
    imgOut = ui.Image.from_data(bIO.getvalue())
    #del bIO
  return imgOut
ui_image = pil2ui(pil_image)
mikael

@cvp, curious about the del bIO, what is it for?

cvp

@mikael as usual, you're right, not needed at all.
Old statement before using "with"
Sorry @halloleooo ,now commented

halloleooo

@cvp, very cool! Works perfectly. Thanks a lot!

ccc

with will autoclose() bIO so you do not need to call bIO.close() but closing a file (in this case, an in-memory file) does not force the garbage collector to deallocate the memory. del bIO is a strong suggestion to the garbage collector to deallocate the memory. If you are only picking one file, then you should have no worries. However, if you process many large image/movie files one-after-another in Pythonista, you might find that calling del will prevent out-of-memory crashes.

cvp

@ccc thanks a lot. Sincerely, I like to get clear explanations of technical matters.

cvp

@halloleooo please, uncomment the del bio, thanks to @ccc

JonB

Hmm, not sure I agree.

bIO is defined within the scope of the function def. When the function returns, bIO falls out of scope. As we have created no other references to bIO, it's reference count should then be 0 and it should then be immediately deallocated.

Does defining bIO in a context manager create a reference cycle? I don't think so.
The context manager probably has a reference to bIO, but bIO doesn't have a reference to the context manager. As long as we don't create a reference cycle, the periodic gc is not needed to resolve . It might be different if you had this within a for loop, instead of a function (though I don't think so).

I suppose it would be easy to use something like the objgraph module to check either way.

cvp

@JonB , @ccc and @mikael you're too amazing for me

the garbage collector reminds me of the days of basic on Tandy Trs80. Afterwards, I developed a lot on ibm mainframe and unix, in languages ​​where you didn't have to think so much about memory problems. but it is true that in Pythonista, this kind of limitation often crashes my scripts which handle a lot of images.

ccc

@JonB Try to create a picture frame app and see if Pythonista can cycle thru the 50 most recent pictures in Photos without del and without crashing.

halloleooo

@ccc @cvp @JonB Very interesting! I guess del bIO cannot hurt....

JonB

@ccc I will post something in a few days when I clean some things up. I created an endless loop calling a function that wrote a large amount of data to a BytesIO. No crash. I downloaded objgraph, and this never showed any increase in BytesIO objects.

My attempt at writing an image viewer that uses pil2ui failed after 4 photos (with or without del bIO) because of apparently a bad image in my photos (at least one that PIL choked on). I will say that, in the case of displaying photos assets, one should be using .get_ui_image instead of .get_image. That avoids any round trip through PIL, which is probably where any leaks or bugs occur. A simple image viewer using get_ui_image ran for longer than I cared to wait without a crash.

Creating ui.Images also requires some care, because these might not get gc'd automatically. I believe the best practice is to include a with objc_util.autoreleasepool(): when many UIImages are created -- though I haven't had time to explore whether setting the imageview image causes the old image to get leaked (in which case it would be necessary to manually handle the deallocation of the UIImage object)