How can I convert a PIL Image to a ui.Image?
How can I convert a PIL Image to a ui.Image?
Taken more or less directly from filenav:
try: import cStringIO as StringIO except ImportError: import StringIO import ui def pil_to_ui(img): strio = StringIO.StringIO() img.save(strio, img.format) data = strio.getvalue() strio.close() return ui.Image.from_data(data)
import ui, io from PIL import Image as ImageP ip = ImageP.open('ionicons-ios7-reload-32') with io.BytesIO() as bIO: ip.save(bIO, ip.format) ui.Button(image = ui.Image.from_data(bIO.getvalue())).present()
Python Tutorial 6.2.1
It is good practice to use the with keyword when dealing with file objects. This has the advantage that the file is properly closed after its suite finishes, even if an exception is raised on the way.
Thanks guys! :D
this code works with the above image, but not with 'Test_Lenna' image.
How can convert a pil image like Lenna into ui.image? Thanks
@jmv38 If you want a
ui.Imagefrom a built-in image, the easiest/fastest would be to just not create a PIL image to start with:
import ui img = ui.Image.named('Test_Lenna') img.show() # ...
@omz thanks, but i know this way, it is not what i want to achieve. Ok let me be more detailed:
A/ i must have an array from numpy that contains an image. i must have this because of fft functions.
B/ i must have a ui image because i want to display my array A and touch it and do actions from the touch. I could use a scene image, but i also want buttons, so i thought i'll have to go through ui anyway, which supports images, and i dont need 60 hz updates, hence my choice not to add another lib on top of it. (maybe wrong choice?).
C/ i want to draw a mask on top of my image, and i assume this mask will be a PIL image, because all drawing functions are there. But i'll have to convert this mask to array to multiply it to A. Note It is a gray scale mask, not binary one.
A,B and C will be updated all the time, so they are not built in images, but computed images.
And i will have 2, maybe 3 such sets of images in the same display.
Due to A,B and C, i will have to convert from one type to the other, and rather efficiently. I though i should not save the Array on disk each time i want to update the ui image, because i assume it will be too slow, but maybe i am wrong.
So to summarize, i expect to use:
PIL -> ui.
ui -> PIL.
array -> ui.
ui -> array.
array -> PIL. (these i found in the docs how to do).
PIL -> array.
If i could avoid all these conversions, i'd love to, but it doesnt seem possible at first sight, if i want to use the built-in libraries. I did not expect that each lib module had its own image format, not compatible with the other ones...
I guess i am not the only one to face that type of question..?
Now if you can suggest a better way to achieve what i want, using only one library module, please let me know, i have no experience in Python. Btw, Pythonista is really fantastic, the possibilty + quality of the whole thing is just .. AWSOME! That is a incredible good job you have done here!
Thanks for your help!
Have you tried using @tony's code, but replacing
i just tried
import ui, io from PIL import Image as ImageP def test(ip): with io.BytesIO() as bIO: ip.save(bIO, 'PNG') ui.Button(image = ui.Image.from_data(bIO.getvalue())).present() ip = ImageP.open('Test_Lenna') #ip = ImageP.open('ionicons-ios7-reload-32') test(ip)
but the result is a big blue square, not lenna color image...
I see... The image is probably alright, it's just that it doesn't work well as a button image. By default, a button only uses the alpha component of an image (which is completely opaque in this case) and tints that with its
tint_color(which is blue by default). This works well with icons, but not so much with photos. To create a button with a full-color image, you have to convert it to an image with "original" rendering mode:
import ui, io from PIL import Image as ImageP def test(ip): with io.BytesIO() as bIO: ip.save(bIO, 'PNG') img = ui.Image.from_data(bIO.getvalue()) img = img.with_rendering_mode(ui.RENDERING_MODE_ORIGINAL) ui.Button(image=img).present() ip = ImageP.open('Test_Lenna') test(ip)
Thanks a lot!
here are the various conversions i have set. Tell me if there is some better code. Thanks.
 modified according to @ccc comment below.
 modified to add functions and tests.
#coding: utf-8 import ui import io from PIL import ImageOps, ImageDraw from PIL import Image import numpy as np import StringIO import console # numpy <=> pil def np2pil(arrayIn): imgOut = Image.fromarray(arrayIn) return imgOut def pil2np(imgIn,arrayOut=None): if arrayOut == None: arrayOut = np.array(imgIn) return arrayOut else: arrayOut[:] = np.array(imgIn) return None # pil <=> ui def pil2ui(imgIn): with io.BytesIO() as bIO: imgIn.save(bIO, 'PNG') imgOut = ui.Image.from_data(bIO.getvalue()) del bIO return imgOut def ui2pil(imgIn): # create a fake png file in memory memoryFile = StringIO.StringIO( imgIn.to_png() ) # this creates the pil image, but does not read the data imgOut = Image.open(memoryFile) # this force the data to be read imgOut.load() # this releases the memory from the png file memoryFile.close() return imgOut # numpy <=> ui def np2ui(arrayIn): # this is a lazy implementation, maybe could be more efficient? return pil2ui( np2pil(arrayIn) ) def ui2np(imgIn): # this is a lazy implementation, maybe could be more efficient? return pil2np( ui2pil(imgIn) ) if __name__ == "__main__": # testing the functions above img = Image.open('Test_Lenna') s = 256 img = img.resize((s, s), Image.BILINEAR) img = ImageOps.grayscale(img) console.clear() print( 'test: open a pil image:') img.show() print('- ') print( 'test: pil image => return a new np array') print(' ') arr = pil2np(img) print(arr) print('- ') print( 'test: pil image => write into existing np array') print(' ') pil2np(img.rotate(90), arr) print(arr) print('- ') print( 'test: np array => return a new pil image') img1 = np2pil(arr) img1.show() # test: pil2ui verification is done via a popover iv = ui.ImageView(frame=(0,0,256,256)) iv.name = 'pil2ui' iv.image = pil2ui(img) iv.present('popover', popover_location=(600,100)) print('- ') print( 'test: ui image => return a new pil image (rotated by -90° to prove pil type)') img2 = ui2pil(iv.image).rotate(-90) print( type(img2)) img2.show() # test: np2ui verification is done via a popover iv2 = ui.ImageView(frame=(0,0,256,256)) iv2.name = 'np2ui' iv2.image = np2ui(arr) iv2.present('popover', popover_location=(300,100)) print('- ') print( 'test: ui image => return a new np array') arr2 = ui2np(iv2.image) print( arr2)
np2pil()always return imgOut?
pil2np()always return arrayOut?
elseclauses are supposed to
return Nonethen you should do that explicitly.
@ccc changes done. Thanks.
I have updated my code above:
- corrected for a bug.
- added ui -> pil and np conversions.
- Added tests for all functions.
This is certainly not the best code possible,
but i hope it hepls someone.
Helped me! I used your pil2ui(), thanks!
Is there a faster way? I have a user take a picture with the camera, then have it displayed in an ImageView, but it takes a while
Which function above are you using? Can you try wrapping the call to the conversion function in a
with timer():block (or similar) and tell us how long the conversion function takes to execute?
Using the first method with a photos.capture_image(). Strange behavior:
x = Image.open('test.jpg') a = time.time() pil_to_ui(x) b = time.time() print b-a
prints 0.97... but
x = photos.capture_image() a = time.time() pil_to_ui(x) b = time.time() print b-a
I don't think this is a resolution difference, they're both about the same size.
Wait, I had changed the
img.formatto PNG to fix an error, JPG makes it take 0.2 seconds. Related to my PNG crash?
one thing that helps, not with speed, but with the problem of
capture_imagereturning, yet the conversion is not yet complete, is to add an ActivityIndicator which starts animating before starting conversion, and stops animation (and removes the indicator ) afterwards. the ActivityIndicator can be added on top of your imageview, or else it can be added to your root view, and sized to the root view, so that it essentially blocks anything else from happening while the processing happens.
on my ipad3, pil2ui takes a second or two for an image captured by the camera... an indicator at least doesn't leave the user wondering if something didn't work right, and prevents you from hitting the capture button again