Using PIL's ImageDraw Module
I'm trying to do individual pixel manipulation using PIL's ImageDraw Module. The code bellow is supposed to create Tkinter canvas widget. Then open an image, change one pixel's color to red, then embed the image in the canvas widget. However, it doesn't seem to be working.
My Code:
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()
I get the following error upon running my code:
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
Your problem is you're reassigning im
to multiple things.
im = Image.open(r'C:\Python26\Suite\test.png')
im = ImageDraw.Draw(im)
im = im.point((0, 0), fill="red")
When you call ImageTk.PhotoImage(im)
, the function expects a PIL image object, but you've already assigned im
to the result of the point()
function, which actually returns None
. This is the cause of your problem.
I think you're misunderstanding how ImageDraw works. Have a look here for an example. Basically:
- You need an instance of
ImageDraw
if you want to draw something complicated on your PIL Image - You still need to keep your PIL image in some variable
ImageDraw
paints directly on the image you've given it during construction time- You can throw away the
ImageDraw
object at any point. It doesn't contain any important information because everything is written directly to the image.
Here's the fixed __init__
method:
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)
You'll notice I've fixed a couple of things:
- Set the canvas size to the size of the image. Obviously, you need to load the image before you can find the image size, so I've moved things around a bit.
- Assign the
ImageDraw
instance to a separate variable - Draw a green rectangle instead of a dot, cause this will stand out more. Note you don't need to grab the return value of
draw.rectangle
-- it actually returnsNone
, as most other drawing functions do. - Delete the
draw
variable after we're done drawing - Center the image in the canvas when calling
create_image
精彩评论