No se puede construir tkinter.PhotoImage desde PIL Image

Intento mostrar una imagen en una etiqueta cuando presiono un botón, pero la imagen es demasiado grande y he intentado cambiar el tamaño de la imagen. He creado esta función:

def image_resize(imageFile): width = 500 height = 300 image = Image.open(imageFile) im2 = image.resize((width, height), Image.ANTIALIAS) return im2 

Para mostrar la imagen he creado esta función:

 def show_image(): label_originalimage ['image'] = image_tk 

Y el botón con el command=show_image :

 filename = 'bild_1.jpg' image_resize = image_resize(filename) image_tk = PhotoImage(image_resize) button_open = Button(frame_open, text='Open Image', command=show_image) 

Solo consigo esto:

 TypeError : __str__ returned non-string (type instance) 

La clase PhotoImage de tkinter toma un nombre de archivo como argumento, y como no puede convertir la image en una cadena, se queja. En su lugar, use la clase PIL.ImageTk módulo PIL.ImageTk . Esto funciona para mí:

 from tkinter import * from PIL import ImageTk, Image def image_resize(imageFile): width = 500 height = 300 image = Image.open(imageFile) im2 = image.resize((width,height), Image.ANTIALIAS) return im2 def show_image(): label_originalimage ['image'] = image_tk root = Tk() filename = './Pictures/Space/AP923487321702.jpg' image_resize = image_resize(filename) image_tk = ImageTk.PhotoImage(image_resize) label_originalimage = Label(root) label_originalimage.pack() button_open = Button(root, text='Open Image', command=show_image) button_open.pack() root.mainloop() 

Observe el cambio de image_tk = PhotoImage(image_resize) a image_tk = ImageTk.PhotoImage(image_resize) .

Tuve el mismo problema cuando trato de construir un elemento de imagen de canvas para tkinter desde un tkinter PhotoImage. El último fue construido a partir de algunos datos de imagen en la memoria (en mi caso, una imagen a cielo abierto). La misma excepción ocurre si simplemente trato de convertir PhotoImage a una cadena.

Supongo que hay un error en el método de conversión __str__ de PhotoImage, por lo que simplemente devuelve la fuente de la imagen. Si se construye a partir de un nombre de archivo (ver más abajo) esto funciona bien. Si se construye a partir de algunos datos de imagen, esto no es de tipo cadena y produce una excepción.

Desafortunadamente, el uso de PhotoImage compatible del módulo ImageTk de PIL como matsjoyce sugirió tampoco me ayudó porque experimenté un problema aún peor, probablemente un error dependiente de la versión de la plataforma o la biblioteca (utilicé OS X 10.11.6, python 3.5, tkinter 8.6, PIL 1.1.7): Ahora la secuencia de comandos de python se bloqueó en la construcción del elemento de imagen de canvas con un “Error de bus”.

La única solución alternativa que conozco fue almacenar los datos de la imagen en un archivo temporal y usar una imagen fotográfica tkinter construida a partir de ese nombre de archivo. (Intentar lo mismo con PIL PhotoImage todavía falla).

 #!/usr/bin/env python3 import tkinter import tempfile import cv2 def opencv2photoimg(opencv_img): """Convert OpenCV (numpy) image to tkinter photo image.""" # ugly workaround: store as file & load file, because direct # construction leads to a crash on my platform tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=True) # ^^^ I am using PNGs only, you might want to use another suffix cv2.imwrite(tmpfile.name, opencv_img) return tkinter.PhotoImage(file=tmpfile.name) # load image img = cv2.imread('test.png') # do something w/ the image ... # setup tk window w/ canvas containing an image root = tkinter.Tk() canvas = tkinter.Canvas(root, width=img.shape[1], height=img.shape[0]) canvas.pack() # keep reference to PhotoImage to avoid it being garbage collected # (well known tkinter bug for canvas image items) photo_img = opencv2photoimg(img) # create a canvas item img_item = canvas.create_image(0, 0, anchor=tkinter.NW, image=photo_img) # display the window tkinter.mainloop() 

No creo que sea elegante, pero funciona.

Sí funciona, pero yeeeucchh – qué manera tengo que hacerlo.

Seguramente hay una mejor manera.

Aquí está mi código de prueba que tengo que comenzar desde aquí …

 import tkinter from PIL import Image import numpy import time import io #python2 version (original) -> 120fps #full physical file io and new image each cycle -> 130fps #reuse PIL Image instead of create new each time -> 160fps class mainWindow(): times=1 timestart=time.clock() data=numpy.array(numpy.random.random((400,500))*100,dtype=int) theimage = Image.frombytes('L', (data.shape[1],data.shape[0]),data.astype('b').tostring()) def __init__(self): self.root = tkinter.Tk() self.frame = tkinter.Frame(self.root, width=500, height=400) self.frame.pack() self.canvas = tkinter.Canvas(self.frame, width=500,height=400) self.canvas.place(x=-2,y=-2) self.root.after(0,self.start) # INCREASE THE 0 TO SLOW IT DOWN self.root.mainloop() def start(self): global data global theimage self.theimage.frombytes(self.data.astype('b').tobytes()) self.theimage.save('work.pgm') self.photo = tkinter.PhotoImage(file='work.pgm') self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW) self.root.update() self.times+=1 if self.times%33==0: print("%.02f FPS"%(self.times/(time.clock()-self.timestart))) self.root.after(10,self.start) self.data=numpy.roll(self.data,-1,1) if __name__ == '__main__': x=mainWindow() 

Aquí está: Encontré que los datos de entrada a la fotoimagen pueden ser una matriz de bytes que parece un archivo de ppm, aunque parece que solo funciona en un subconjunto de ppm legales (por ejemplo, los valores de 16 bits no funcionan)

Así que para futuras referencias …….

 import tkinter import numpy import time #python2 version (original) -> 120fps #full physical file io and new image each cycle -> 130fps #reuse PIL Image instead of create new each time -> 160fps #and... direct image into tkinter using ppm byte array -> 240 fps class mainWindow(): times=1 timestart=time.clock() data=numpy.array(numpy.random.random((400,500))*900,dtype=numpy.uint16) def __init__(self): self.root = tkinter.Tk() self.frame = tkinter.Frame(self.root, width=500, height=400) self.frame.pack() self.canvas = tkinter.Canvas(self.frame, width=500,height=400) self.canvas.place(x=-2,y=-2) xdata = b'P5 500 400 255 ' + self.data.tobytes() self.photo = tkinter.PhotoImage(width=500, height=400, data=xdata, format='PPM') self.imid = self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW) self.root.after(1,self.start) # INCREASE THE 0 TO SLOW IT DOWN self.root.mainloop() def start(self): global data xdata = b'P5 500 400 255 ' + numpy.clip(self.data,0,255).tobytes() self.photo = tkinter.PhotoImage(width=500, height=400, data=xdata, format='PPM') if True: self.canvas.itemconfig(self.imid, image = self.photo) else: self.canvas.delete(self.imid) self.imid = self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW) self.times+=1 if self.times%33==0: print("%.02f FPS"%(self.times/(time.clock()-self.timestart))) self.root.update() self.root.after(0,self.start) self.data=numpy.roll(self.data,-1,1) if __name__ == '__main__': x=mainWindow()