无法从 PIL Image 构建 tkinter.PhotoImage
Cannot construct tkinter.PhotoImage from PIL 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
以及带有 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)
我只得到这个:
TypeError : __str__ returned non-string (type instance)
来自 tkinter
的 PhotoImage
class 将文件名作为参数,并且由于它无法将 image
转换为字符串,它会抱怨。相反,使用 PIL.ImageTk
模块中的 PhotoImage
class。这对我有用:
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()
请注意从 image_tk = PhotoImage(image_resize)
到 image_tk = ImageTk.PhotoImage(image_resize)
的变化。
我在尝试为 canvas 构建图像项时遇到了同样的问题
来自 tkinter PhotoImage 的 tkinter。后者是由一些
内存中的图像数据(在我的例子中是 opencv 图像)。一样的异常
如果我只是尝试将 PhotoImage 转换为字符串,就会发生这种情况。
估计PhotoImage的转换方法__str__
有问题,
使其简单地 returns 图像源。如果从文件名构造
(见下文)这很好用。如果从一些图像数据构建,这不是
字符串类型并产生异常。
不幸的是,使用 PIL 的兼容 PhotoImage
像 matsjoyce 建议的 ImageTk 模块也没有帮助我,因为我遇到了一个更糟糕的问题,可能是平台或库版本相关的错误(我使用 OS X 10.11.6,python 3.5,tkinter 8.6, PIL 1.1.7):现在 python 脚本在使用 "Bus Error".
构建 canvas 图像项时崩溃
我知道的唯一解决方法是将图像数据存储到一个临时文件中,并使用从该文件名构造的 tkinter PhotoImage。 (尝试对 PIL PhotoImage 进行同样的操作仍然会崩溃。)
#!/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()
我不认为它很优雅,但它确实有效。
是的,它有效,但是 yeeeuchchh - 我必须这样做。
肯定有更好的方法。
这是我从 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()
这里是:我发现 photoimage 的输入数据可以是一个字节数组,看起来像一个 ppm 文件,尽管它似乎只适用于合法 ppm 的一个子集(例如 16 位值不起作用)
所以为了以后参考......
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()
我尝试在按下按钮时在标签中显示图像,但图像太大,我已尝试调整图像大小。我创建了这个函数:
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
以及带有 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)
我只得到这个:
TypeError : __str__ returned non-string (type instance)
来自 tkinter
的 PhotoImage
class 将文件名作为参数,并且由于它无法将 image
转换为字符串,它会抱怨。相反,使用 PIL.ImageTk
模块中的 PhotoImage
class。这对我有用:
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()
请注意从 image_tk = PhotoImage(image_resize)
到 image_tk = ImageTk.PhotoImage(image_resize)
的变化。
我在尝试为 canvas 构建图像项时遇到了同样的问题 来自 tkinter PhotoImage 的 tkinter。后者是由一些 内存中的图像数据(在我的例子中是 opencv 图像)。一样的异常 如果我只是尝试将 PhotoImage 转换为字符串,就会发生这种情况。
估计PhotoImage的转换方法__str__
有问题,
使其简单地 returns 图像源。如果从文件名构造
(见下文)这很好用。如果从一些图像数据构建,这不是
字符串类型并产生异常。
不幸的是,使用 PIL 的兼容 PhotoImage 像 matsjoyce 建议的 ImageTk 模块也没有帮助我,因为我遇到了一个更糟糕的问题,可能是平台或库版本相关的错误(我使用 OS X 10.11.6,python 3.5,tkinter 8.6, PIL 1.1.7):现在 python 脚本在使用 "Bus Error".
构建 canvas 图像项时崩溃我知道的唯一解决方法是将图像数据存储到一个临时文件中,并使用从该文件名构造的 tkinter PhotoImage。 (尝试对 PIL PhotoImage 进行同样的操作仍然会崩溃。)
#!/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()
我不认为它很优雅,但它确实有效。
是的,它有效,但是 yeeeuchchh - 我必须这样做。
肯定有更好的方法。
这是我从 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()
这里是:我发现 photoimage 的输入数据可以是一个字节数组,看起来像一个 ppm 文件,尽管它似乎只适用于合法 ppm 的一个子集(例如 16 位值不起作用)
所以为了以后参考......
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()