【问题标题】:Cannot construct tkinter.PhotoImage from PIL Image无法从 PIL Image 构造 tkinter.PhotoImage
【发布时间】:2016-12-20 18:47:04
【问题描述】:

当我按下按钮时,我尝试在标签中显示图像,但图像太大,我尝试调整图像大小。我创建了这个函数:

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)

【问题讨论】:

    标签: python tkinter resize python-imaging-library resize-image


    【解决方案1】:

    来自tkinterPhotoImage 类将文件名作为参数,并且由于它无法将image 转换为字符串,因此它会报错。相反,请使用 PIL.ImageTk 模块中的 PhotoImage 类。这对我有用:

    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) 的变化。

    【讨论】:

      【解决方案2】:

      当我尝试为 来自 tkinter PhotoImage 的 tkinter。后者是由一些 内存中的图像数据(在我的例子中是一个 opencv 图像)。同样的例外 如果我只是尝试将 PhotoImage 转换为字符串,则会发生。

      我猜是PhotoImage的转换方法__str__有bug, 使它简单地返回图像源。如果从文件名构造 (见下文)这工作正常。如果从一些图像数据构造,这不是 字符串类型并产生异常。

      不幸的是,使用 PIL 的兼容 PhotoImage 像 matsjoyce 建议的 ImageTk 模块也没有帮助我,因为我遇到了一个更严重的问题,可能是平台或库版本相关的错误(我使用了 OS X 10.11.6、python 3.5、tkinter 8.6、PIL 1.1.7):现在python 脚本在构造画布图像项时崩溃,出现“总线错误”。

      我知道的唯一解决方法是将图像数据存储到临时文件中,并使用从该文件名构造的 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()
      

      我不认为它很优雅,但它确实有效。

      【讨论】:

        【解决方案3】:

        是的,它有效,但是 yeeeucchh - 我必须这样做。

        肯定有更好的办法。

        这是我从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()
        

        【讨论】:

          【解决方案4】:

          在这里:我发现 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()
          

          【讨论】:

            猜你喜欢
            • 2015-12-18
            • 1970-01-01
            • 1970-01-01
            • 2019-01-29
            • 1970-01-01
            • 1970-01-01
            • 2021-06-27
            • 2021-04-28
            • 1970-01-01
            相关资源
            最近更新 更多