ImagePath holds one thing, a Path: a sequence of coordinates stored compactly, which ImageDraw accepts anywhere it takes a list of points.
from PIL import Image, ImageDraw, ImagePath
points = [(10, 10), (100, 40), (180, 10), (180, 120), (10, 120)]
path = ImagePath.Path(points)
im = Image.new("RGB", (200, 140), "white")
draw = ImageDraw.Draw(im)
draw.polygon(path, fill="skyblue", outline="navy")
For a handful of points, a plain list is fine. Paths pay off when you have thousands, from a plot, a map outline or a traced contour, because the coordinates are held as a flat array of floats rather than a list of Python tuples. It is the same reason you would use array rather than list for numbers.
| Method | What it does |
|---|---|
getbbox() | The bounding box of every point |
tolist(flat=False) | Back to a list of tuples, or a flat list |
compact(distance=2) | Drops points closer together than the distance, and returns how many went |
map(function) | Runs every point through a function, in place |
transform(matrix) | Applies an affine transform to every point |
path.transform((2, 0, 10, 0, 2, 20))
The six numbers are the affine matrix (a, b, c, d, e, f), giving x = a*x + b*y + c and y = d*x + e*y + f. So that call scales by two and moves the result 10 across and 20 down.
path = ImagePath.Path(traced_points)
removed = path.compact(3)
print(removed, "points dropped,", len(path.tolist()), "left")
Contours traced from a bitmap tend to hold a point per pixel, most of which sit on the same straight lines. compact thins them out before drawing, which is faster and produces smaller output if you are exporting.