PIL Drawing Primitives
The ImageDraw module from PIL lets you draw graphic
primitives — lines, rectangles, ellipses, polygons, and text — directly onto images
using Python code.
Learning Objectives
- 12.6.2.3 Apply graphic primitives to create drawings
Conceptual Anchor
The Canvas & Brush Analogy
Image.new() creates the canvas. ImageDraw.Draw(img)
gives you a brush. You then call methods like draw.rectangle() or
draw.ellipse() to paint shapes on the canvas — just like a graphics editor, but in
code.
Rules & Theory
Setting Up a Drawing
from PIL import Image, ImageDraw
# Create canvas and draw object
img = Image.new("RGB", (400, 300), (255, 255, 255)) # white bg
draw = ImageDraw.Draw(img)Drawing Primitives
| Method | Parameters | Description |
|---|---|---|
draw.line() |
[(x1,y1), (x2,y2)], fill, width |
Draw a straight line |
draw.rectangle() |
[(x1,y1), (x2,y2)], fill, outline |
Draw a rectangle (top-left, bottom-right) |
draw.ellipse() |
[(x1,y1), (x2,y2)], fill, outline |
Draw an ellipse within bounding box |
draw.polygon() |
[(x1,y1), (x2,y2), ...], fill, outline |
Draw a polygon with any number of vertices |
draw.arc() |
[(x1,y1), (x2,y2)], start, end, fill |
Draw an arc (part of ellipse outline) |
draw.point() |
(x, y), fill |
Draw a single pixel |
draw.text() |
(x, y), "text", fill, font |
Draw text on the image |
Parameter Reference
| Parameter | Meaning |
|---|---|
fill |
Interior color as RGB tuple, e.g. (255, 0, 0) |
outline |
Border color as RGB tuple |
width |
Line/border thickness in pixels |
(x1, y1) |
Top-left corner of bounding box |
(x2, y2) |
Bottom-right corner of bounding box |
Worked Examples
1 Drawing Basic Shapes
from PIL import Image, ImageDraw
img = Image.new("RGB", (400, 300), (255, 255, 255))
draw = ImageDraw.Draw(img)
# Red rectangle
draw.rectangle([(50, 50), (150, 120)],
fill=(255, 0, 0), outline=(0, 0, 0))
# Blue circle (ellipse in square bounding box)
draw.ellipse([(200, 50), (300, 150)],
fill=(0, 0, 255), outline=(0, 0, 0))
# Green line
draw.line([(50, 200), (350, 200)],
fill=(0, 128, 0), width=3)
img.save("shapes.png")2 Drawing a Simple House
from PIL import Image, ImageDraw
img = Image.new("RGB", (400, 400), (135, 206, 235)) # sky blue bg
draw = ImageDraw.Draw(img)
# Ground
draw.rectangle([(0, 300), (400, 400)], fill=(34, 139, 34))
# House body
draw.rectangle([(100, 180), (300, 300)], fill=(210, 180, 140),
outline=(0, 0, 0))
# Roof (triangle)
draw.polygon([(90, 180), (200, 100), (310, 180)],
fill=(139, 69, 19), outline=(0, 0, 0))
# Door
draw.rectangle([(175, 230), (225, 300)], fill=(101, 67, 33),
outline=(0, 0, 0))
# Window
draw.rectangle([(120, 210), (160, 250)], fill=(173, 216, 230),
outline=(0, 0, 0))
# Sun
draw.ellipse([(320, 30), (380, 90)], fill=(255, 255, 0))
img.save("house.png")3 Adding Text
from PIL import Image, ImageDraw, ImageFont
img = Image.new("RGB", (400, 200), (0, 0, 0))
draw = ImageDraw.Draw(img)
# Default font (basic)
draw.text((20, 80), "Hello, PIL!", fill=(255, 255, 255))
# Custom font (if available)
try:
font = ImageFont.truetype("arial.ttf", 36)
draw.text((20, 30), "Big Text", fill=(255, 200, 0),
font=font)
except:
pass
img.save("text_demo.png")Common Pitfalls
Bounding Box — Not Center + Radius
Circles/ellipses use a bounding box [(x1,y1), (x2,y2)], NOT center + radius. For
a circle at center (200, 150) with radius 50, use: [(150, 100), (250, 200)].
Tasks
Draw a traffic light: black rectangle with three circles (red, yellow, green) inside.
Draw a smiley face using ellipses and arcs. Save it as "smiley.png".
Create a landscape scene (sky, ground, sun, trees) using at least 5 different drawing primitives.
Self-Check Quiz
Q1: How do you create a draw object?
draw = ImageDraw.Draw(img) — pass the image you want to draw on.Q2: How do you draw a circle with PIL?
draw.ellipse()
with a square bounding box, e.g. draw.ellipse([(50,50),(150,150)], fill="red").
Q3: What's the difference between fill and outline?
fill is the
interior color of the shape. outline is the border color.