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
The 'ImageDraw' module provides simple 2D graphics support for Image Object. Generally, we use this module to create new images, annotate or retouch existing images and to generate graphics on the fly for web use. The graphics commands support the drawing of shapes and annotation of text.
line() Draws a line between the coordinates in the xy list. Parameters: xy – Sequence of either 2-tuples like [(x, y), (x, y), …] or numeric values like [x, y, x, y, …]. fill – Color to use for the line.
You can use this module to create new images, annotate or retouch existing images, and to generate graphics on the fly for web use. ImageDraw.Draw.text () Draws the string at the given position.
A simple 2D drawing interface for PIL images. :param im: The image to draw in. :param mode: Optional mode to use for color values. For RGB images, this argument can be RGB or RGBA (to blend the drawing into the image). For all other modes, this argument must be the same as the image mode. If omitted, the mode defaults to the mode of the image.
Python’s Pillow which is a fork of the discontinued Python Imaging Library (PIL) is a powerful library that is capable of adding image processing capabilities to your python code. Pillow offers many modules that ease the process of working and modifying images. In this article, we will have a look at the ImageDraw module of this library.
You can use this module to create new images, annotate or retouch existing images, and to generate graphics on the fly for web use. For a more advanced drawing library for PIL, see the aggdraw module. The graphics interface uses the same coordinate system as PIL itself, with (0, 0) in the upper left corner.
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:
ImageDraw
if you want to draw something complicated on your PIL ImageImageDraw
paints directly on the image you've given it during construction timeImageDraw
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:
ImageDraw
instance to a separate variabledraw.rectangle
-- it actually returns None
, as most other drawing functions do.draw
variable after we're done drawingcreate_image
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With