Image Processing with Pillow¶
By the end of this lesson you'll be able to:
- Open and display images with Pillow (
PIL) - Inspect image size, mode, and pixel data
- Resize, crop, flip, and rotate images
- Convert between color modes (RGB, grayscale, RGBA)
- Apply basic filters and save to different formats
A photograph on your phone is just a grid of colored pixels. Python can read, analyze, and transform that grid — automating tasks like resizing profile pictures, converting formats, adding watermarks, or applying artistic filters.
Welcome to Chapter 34!
Pillow is Python's go-to library for image work — and it's surprisingly simple.
From a school photo to a satellite image, the same functions apply!
Let's start editing! Let's code it together!
What is Pillow?¶
Pillow is the maintained fork of the classic PIL (Python Imaging Library). You install it with:
1 | |
But you import it using the original name:
1 | |
Pillow supports over 30 image formats including JPEG, PNG, GIF, BMP, TIFF, and WebP.
Opening and Inspecting Images¶
Image.open(filepath) loads an image file into memory.
The returned object has several useful attributes:
| Attribute | What it tells you |
|---|---|
image.size |
(width, height) in pixels |
image.width |
Width in pixels |
image.height |
Height in pixels |
image.mode |
Color mode: "RGB", "RGBA", "L" (grayscale), "P" (palette) |
image.format |
File format: "JPEG", "PNG", etc. (None if created in code) |
1 2 3 4 5 6 | |
What Do You Think Will Happen?
The code below creates a tiny image from scratch using pixel data.
What color do you think pixel (1,1) will be — red, green, or blue?
Predict first, then run it!
Understanding Pixel Data¶
A pixel in RGB mode is a tuple of three integers — (red, green, blue) — each ranging from 0 to 255.
| Color | RGB |
|---|---|
| Red | (255, 0, 0) |
| Green | (0, 255, 0) |
| Blue | (0, 0, 255) |
| White | (255, 255, 255) |
| Black | (0, 0, 0) |
| Yellow | (255, 255, 0) |
| Cyan | (0, 255, 255) |
| Gray | (128, 128, 128) |
img.getpixel((x, y)) returns the color of one pixel. Note: x = column (left→right), y = row (top→bottom).
Resizing and Thumbnailing¶
image.resize((width, height)) creates a new image at the requested size.
The original is not modified — Pillow operations always return a new image.
1 2 3 4 5 | |
image.thumbnail((max_width, max_height)) is smarter — it shrinks the image to fit within the box while keeping the aspect ratio. It modifies the image in place:
1 | |
Cropping Images¶
image.crop((left, upper, right, lower)) extracts a rectangular region.
The four numbers are pixel coordinates measured from the top-left corner.
1 2 3 | |
Flipping and Rotating¶
image.transpose(method) flips or rotates the image.
The method constants live in Image.Transpose (or the older Image namespace):
1 2 3 4 5 6 | |
img.rotate(angle) rotates by the given angle — positive angles rotate counter-clockwise.
Color Mode Conversion¶
image.convert(mode) changes the color mode of an image.
The most common conversions are:
| Conversion | What it does |
|---|---|
img.convert("L") |
Convert to grayscale (L = luminance) |
img.convert("RGB") |
Convert to 3-channel color |
img.convert("RGBA") |
Add transparency channel |
img.convert("1") |
Convert to 1-bit black and white |
Converting to grayscale is a simple way to apply a classic photo effect:
1 2 3 4 5 | |
Applying Filters¶
Pillow's ImageFilter module provides ready-made filters:
1 2 3 4 5 6 7 8 9 10 | |
Filters work by examining each pixel's neighbors and computing a new value — a process called convolution.
Saving Images¶
image.save(filepath) saves to any supported format.
Pillow detects the format from the file extension:
1 2 3 | |
For JPEG: lower quality = smaller file but more visual artifacts. Quality 80–90 is usually a good balance.
Learning Check¶
Your Turn — Write a Brightness Booster
The code below has a function stub for brighten(image, amount).
Complete it: for every pixel, add amount to each R, G, B channel — but clamp the result between 0 and 255!
Experiments¶
Try these changes. Predict what will happen first, then run it to check!
-
Create a 100×100 image where each pixel's R value equals its x-coordinate and G value equals its y-coordinate. Save it as a PNG and open it. You'll know it worked when you see a gradient that shifts from dark to bright in two directions.
-
Open a JPEG photo and use
thumbnail((300, 300))to shrink it, then save as PNG. You'll know it worked when the output file is a smaller image in PNG format. -
Convert an RGB image to grayscale and then convert it back to RGB. Print a pixel — is it the same as the original? You'll know it worked when you see the gray color has equal R, G, B values.
-
Use
img.rotate(45)on a photo. Note that the corners are black (background fill). Addexpand=Trueto see the difference. You'll know it worked whenexpand=Truegrows the canvas to show the full rotated image. -
Apply
ImageFilter.FIND_EDGESto a photo. Save the result and compare it to the original. You'll know it worked when you see bright lines where the original had color edges.
Image Wizard!
You can now open, inspect, transform, filter, and save images with Python!
These same techniques power computer vision systems, photo-editing apps, and machine learning pipelines.
The next chapter returns to turtle graphics for event-driven animation. Let's keep coding!