使用PIL的ImageDraw模块

5
我将尝试使用PIL的ImageDraw模块进行单个像素的操作。下面的代码旨在创建Tkinter画布小部件。然后打开一张图片,将一个像素的颜色更改为红色,然后将该图片嵌入到画布小部件中。然而,似乎并没有起作用。
我的代码:
import Tkinter
from PIL import ImageTk, Image, ImageDraw


class image_manip(Tkinter.Tk):

    def __init__(self):
        Tkinter.Tk.__init__(self)

        self.configure(bg='red')

        self.ImbImage = Tkinter.Canvas(self, highlightthickness=0, bd=0, bg='blue')
        self.ImbImage.pack()

        im = Image.open(r'C:\Python26\Suite\test.png')

        print im.format, im.size, im.mode

        im = ImageDraw.Draw(im)

        im = im.point((0, 0), fill="red")

        self.i = ImageTk.PhotoImage(im)
        self.ImbImage.create_image(139, 59, image=self.i)




def run():
    image_manip().mainloop()
if __name__ == "__main__":
    run()

运行我的代码时,我遇到了以下错误:
Exception AttributeError: "PhotoImage instance has no attribute '_PhotoImage__photo'" in <bound method PhotoImage.__del__ of <PIL.ImageTk.PhotoImage instance at 0x05DF7698>> ignored
Traceback (most recent call last):
  File "<string>", line 245, in run_nodebug
  File "C:\Python26\Suite\test_image.py", line 30, in <module>
    run()
  File "C:\Python26\Suite\test_image.py", line 28, in run
    image_manip().mainloop()
  File "C:\Python26\Suite\test_image.py", line 20, in __init__
    self.i = ImageTk.PhotoImage(im)
  File "C:\Python26\lib\site-packages\PIL\ImageTk.py", line 109, in __init__
    mode = Image.getmodebase(mode)
  File "C:\Python26\lib\site-packages\PIL\Image.py", line 245, in getmodebase
    return ImageMode.getmode(mode).basemode
  File "C:\Python26\lib\site-packages\PIL\ImageMode.py", line 50, in getmode
    return _modes[mode]
KeyError: None
1个回答

9

你的问题是你正在将im重新分配给多个东西。

im = Image.open(r'C:\Python26\Suite\test.png')
im = ImageDraw.Draw(im)
im = im.point((0, 0), fill="red")

当您调用ImageTk.PhotoImage(im)时,函数期望一个PIL图像对象,但是您已经将im分配给了point()函数的结果,该函数实际上返回None。这就是您的问题所在。
我认为您误解了ImageDraw的工作方式。请参考此处的示例。基本上:
  • 如果要在PIL图像上绘制复杂的东西,则需要一个ImageDraw实例
  • 仍然需要将PIL图像保存在某个变量中
  • ImageDraw在构建期间直接在给定的图像上进行绘制
  • 您可以随时丢弃ImageDraw对象。它不包含任何重要信息,因为所有内容都直接写入图像。
这是修复后的__init__方法:
def __init__(self):
    Tkinter.Tk.__init__(self)
    self.configure(bg='red')
    im = Image.open(r'C:\Python26\Suite\test.png')
    width, height = im.size
    self.ImbImage = Tkinter.Canvas(self, highlightthickness=0, bd=0, bg='red', width=width, height=height)
    self.ImbImage.pack()
    print im.format, im.size, im.mode

    draw = ImageDraw.Draw(im)
    draw.rectangle([0, 0, 40, 40 ],  fill="green")
    del draw

    self.i = ImageTk.PhotoImage(im)
    self.ImbImage.create_image(width/2, height/2, image=self.i)

你会注意到我修复了一些东西:

  • 将画布大小设置为图像大小。显然,您需要加载图像才能找到图像大小,因此我稍微调整了一下。
  • ImageDraw实例分配给单独的变量
  • 绘制绿色矩形而不是点,因为这样更加醒目。请注意,您不需要获取draw.rectangle的返回值--它实际上返回None,就像大多数其他绘图函数一样。
  • 在完成绘图后删除draw变量
  • 在调用create_image时将图像居中于画布

2
请注意,您必须将ImageTk.PhotoImage(im)分配给一个持久的(非垃圾回收)变量。在Tkinter中存在一个错误,如果您只将其分配给本地变量并在create_image中使用它,则图像将被垃圾回收。请参见http://infohost.nmt.edu/tcc/help/pubs/pil/image-tk.html。 - mckoss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接