I try to show a image in a label when I push a button, but the image are too large and I have tried to resize the image. I have created this function:
def image_resize(imageFile):
width = 500
height = 300
image = Image.open(imageFile)
im2 = image.resize((width, height), Image.ANTIALIAS)
return im2
To show the image I have created this function:
def show_image():
label_originalimage ['image'] = image_tk
And the button with the 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)
I get only this:
TypeError : __str__ returned non-string (type instance)
The PhotoImage
class from tkinter
takes a filename as an argument, and as it cannot convert the image
into a string, it complains. Instead, use the PhotoImage
class from the PIL.ImageTk
module. This works for me:
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()
Notice the change from image_tk = PhotoImage(image_resize)
to image_tk = ImageTk.PhotoImage(image_resize)
.
I had the same problem when I try to construct a canvas image item for tkinter from a tkinter PhotoImage. The latter was constructed from some image data in memory (in my case an opencv image). The same exception occurs if I simply try to convert the PhotoImage to a string.
I guess there is a bug in the conversion method __str__
of the PhotoImage,
making it simply returns the image source. If constructed from a file name
(see below) this works fine. If constructed from some image data, this is not
of type string and yields an exception.
Unfortunately, using the compatible PhotoImage from PIL's ImageTk module like matsjoyce suggested didn't help me either because I experienced an even worse problem, probably a platform or library version dependent bug (I used OS X 10.11.6, python 3.5, tkinter 8.6, PIL 1.1.7): Now the python script crashed at the construction of the canvas image item with a "Bus Error".
The only workaround I am aware of was to store the image data into a temporary file and use a tkinter PhotoImage constructed from that file name. (Trying the same with the PIL PhotoImage still crashes.)
#!/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()
I do not think it's elegant, but it works.
Yes it works, but yeeeucchh - what I way to have to do it.
Surely there is a better way.
Here is my test code I got to starting from here....
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()
Here it is: I found that the input data to photoimage can be a byte array that looks like a ppm file, although it only appears to work on a subset of legal ppm (e.g. 16 bit values don't work)
So for future reference.......
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()
来源:https://stackoverflow.com/questions/28762116/cannot-construct-tkinter-photoimage-from-pil-image