ImageDraw draws onto an existing image, in place. You make a drawing object from the image, then call methods on it.
from PIL import Image, ImageDraw
im = Image.new("RGB", (400, 300), "white")
draw = ImageDraw.Draw(im)
draw.line((0, 0, 400, 300), fill="black", width=2)
im.save("out.png")
| Method | Draws |
|---|---|
line(xy, fill, width) | A line, or a path through several points |
rectangle(xy, fill, outline, width) | A box from two corners |
rounded_rectangle(xy, radius, ...) | The same with rounded corners |
ellipse(xy, fill, outline, width) | An ellipse inside a box |
arc(xy, start, end, fill) | Part of an ellipse outline |
chord and pieslice | An arc closed with a line, or with two radii |
polygon(xy, fill, outline) | A closed shape through any number of points |
point(xy, fill) | Single pixels |
Coordinates are either a flat sequence, (x0, y0, x1, y1), or a list of tuples. Colours can be names, hex strings or tuples matching the image mode.
from PIL import ImageFont
font = ImageFont.truetype("DejaVuSans.ttf", 24)
draw.text((10, 10), "hello", font=font, fill="black")
Without a font argument you get a small built in bitmap font that cannot be resized, which is why nearly all real code loads a TrueType file.
draw.textsize() was removed in Pillow 10.w, h = draw.textsize("hello", font=font)
left, top, right, bottom = draw.textbbox((0, 0), "hello", font=font)
w = right - left
h = bottom - top
For a single line where you only need the width, draw.textlength("hello", font=font) is simpler and returns a float. The converter flags all of this.
The anchor argument removes most of the arithmetic:
draw.text((200, 150), "hello", font=font, fill="black", anchor="mm")
mm means middle horizontally and middle vertically, so the point you give is the centre of the text rather than its top left corner.
To draw semi transparent shapes, draw onto a separate RGBA layer and composite it:
layer = Image.new("RGBA", im.size, (0, 0, 0, 0))
d = ImageDraw.Draw(layer)
d.rectangle((50, 50, 200, 150), fill=(255, 0, 0, 96))
im = Image.alpha_composite(im.convert("RGBA"), layer)