PIL Image Basics
Learn how Python treats an image as both a file and a grid of pixels. With Pillow you can load, create, inspect, resize, crop, rotate, convert, edit and save images.
Learning Objectives
- 12.6.2.2 Use commands of module Image in PIL library (load, create, size, save) to manipulate images
Conceptual Anchor
The Digital Photo Lab
Pillow is your digital photo lab. Image.open() takes a
photo from storage, Image.new() prepares a blank canvas, .size
measures it, image methods transform it, and .save() writes the result back
to a file.
Image.open()Load an existing image
img.sizeInspect width and height
resize / cropTransform the image
getpixel()Read or change pixels
img.save()Save the final result
Images Are Pixel Grids
Every pixel has a coordinate (x, y). In Pillow,
(0, 0) is at the top-left. x increases to the right and y
increases downward.
Coordinate System
The coordinate order is always (x, y).
Width × Height
img.size returns (width, height).
w, h = img.sizeResultw = 640, h = 360
Important
For an image of size (640, 360), valid pixel coordinates run from
x = 0...639 and y = 0...359.
Image Modes
The image mode tells Pillow how pixel values are stored. RGB uses the same Red, Green and Blue channel values from the previous lesson.
| Mode | Meaning | Typical pixel value |
|---|---|---|
"RGB" |
Red, Green and Blue | (142, 87, 201) |
"RGBA" |
RGB + Alpha transparency | (142, 87, 201, 128) |
"L" |
8-bit grayscale | 0...255 |
"1" |
Binary black and white | black / white |
Core Image Commands
Installing Pillow
# Install once in your terminal
pip install Pillow
# Import in Python
from PIL import Image| Command | Purpose | Example |
|---|---|---|
Image.open(path) |
Open an image file | img = Image.open("photo.jpg") |
Image.new(mode, size, color) |
Create a new image | Image.new("RGB", (400,300), (255,0,0)) |
img.size |
Read (width, height) | w, h = img.size |
img.mode |
Read the image mode | print(img.mode) |
img.save(path) |
Save to a file | img.save("output.png") |
img.show() |
Open a preview | img.show() |
img.resize((w, h)) |
Return a resized copy | small = img.resize((320,180)) |
img.crop(box) |
Return a selected rectangle | part = img.crop((50,40,250,180)) |
img.rotate(angle) |
Return a rotated copy | turned = img.rotate(45, expand=True) |
img.convert(mode) |
Convert image mode | gray = img.convert("L") |
Interactive Image Lab
Use the buttons to preview common Pillow operations. The browser animation is only a visual model; the Python command below is what you would write with Pillow.
img = Image.open("scene.png")original
480 × 300
resize()
new dimensions
crop()
selected region
rotate()
turned copy
Resize, Crop & Rotate
1 Resize
resize() expects a new (width, height) tuple and returns a
new image object.
from PIL import Image
img = Image.open("landscape.jpg")
small = img.resize((320, 180))
small.save("landscape_small.png")2 Crop
The crop box is (left, upper, right, lower).
Crop Box
Python
img = Image.open("photo.jpg")
box = (100, 60, 420, 260)
cropped = img.crop(box)
cropped.save("cropped.png")Crop width = right - left
Crop height =
lower - upper
3 Rotate
img = Image.open("photo.jpg")
rotated = img.rotate(45, expand=True)
rotated.save("rotated.png")Why expand=True?
At angles such as 45°, corners may fall outside the original canvas.
expand=True enlarges the output canvas so the full rotated image can
fit.
Worked Examples
1 Open → Inspect → Resize → Save
from PIL import Image
img = Image.open("landscape.jpg")
print("Mode:", img.mode)
print("Size:", img.size)
thumbnail = img.resize((320, 180))
thumbnail.save("landscape_thumb.png")2 Create a New Image
from PIL import Image
red_img = Image.new(
"RGB",
(400, 300),
(255, 0, 0)
)
red_img.save("red_canvas.png")output: red_canvas.png
3 Create a Gradient Pixel by Pixel
from PIL import Image
img = Image.new("RGB", (256, 256))
for x in range(256):
for y in range(256):
img.putpixel((x, y), (x, y, 128))
img.save("gradient.png")R changes with x · G changes with y
4 Convert to Grayscale
from PIL import Image
img = Image.open("photo.jpg")
gray = img.convert("L")
gray.save("photo_gray.png")
print(gray.mode) # LPixel Explorer
Hover over or tap a pixel. The readout shows what
img.getpixel((x, y)) would return.
- Coordinate
- (3, 4)
- RGB
- (109, 158, 197)
- Python
- img.getpixel((3, 4))
5 Read and Change a Pixel
from PIL import Image
img = Image.open("photo.jpg").convert("RGB")
pixel = img.getpixel((10, 20))
print(pixel)
img.putpixel((10, 20), (255, 255, 255))
img.save("edited.png")From One Pixel to a Whole Image
Once you can read and write one pixel, nested loops can process every
(x, y) position.
6 Invert Every RGB Pixel
from PIL import Image
img = Image.open("photo.jpg").convert("RGB")
w, h = img.size
for x in range(w):
for y in range(h):
r, g, b = img.getpixel((x, y))
img.putpixel((x, y), (255-r, 255-g, 255-b))
img.save("inverted.png")
Each channel becomes 255 - value
Why convert to RGB first?
An input file may use another mode. .convert("RGB") makes each pixel
predictable so it can be unpacked as r, g, b.
Video Demonstration
Working with Images Using Pillow
Practical demonstration of loading, resizing, rotating, cropping and processing images in Python.
Common Pitfalls
resize(), crop(), rotate() and convert() return new images
Store the result: small = img.resize((200, 100)).
Width/height vs x/y
img.size is (width, height); pixel coordinates are
(x, y).
Wrong crop-box order
Use (left, upper, right, lower), not (x, y, width, height).
Pixel mode mismatch
r, g, b = img.getpixel(...) assumes RGB-like pixels. Convert when necessary:
img = img.convert("RGB").
Distorted aspect ratio
Changing a 16:9 image directly to a square changes its proportions. Decide whether stretching is acceptable before resizing.
Practice Tasks
Create an 800×500 RGB image with colour (30, 144, 255).
An image is (1200, 800). State the largest valid x-coordinate and
y-coordinate.
Load an image, print its mode and size, resize it to 400×300, and save
it as small.png.
Crop the rectangle from (100, 50) to (500, 350) and save it
as crop.png.
Explain why (100, 50, 400, 300) does not mean x=100, y=50, width=400,
height=300 in crop().
Swap the red and blue values of every pixel: (r, g, b) → (b, g, r).
Build a pipeline: open → crop → resize → grayscale → rotate → save.
Self-Check Quiz
Q1: What does Image.open("photo.jpg") do?
Q2: What does img.size return?
(width, height) in pixels.Q3: Where is coordinate (0, 0)?
Q4: What does img.crop((10, 20, 110, 80)) select?
Q5: Why might you use img.convert("RGB") before pixel
processing?
r, g, b.Q6: Does img.resize((200, 100)) change
img itself?
small = img.resize((200, 100)).