π Advanced Python Pillow Cheat Sheet #
A deep dive into the Pillow library, focusing on advanced techniques, performance optimization, and best practices for modern image manipulation in Python.
π Setup & Best Practices #
- Always use a context manager: Ensures files are properly closed.
- Load image data: Use
im.load()before accessing pixel data for the first time to load the image data from the file. - Prefer
LANCZOSfor resizing:Image.Resampling.LANCZOS(formerlyImage.ANTIALIAS) provides the highest quality downscaling. - Close images: Explicitly call
im.close()if not using a context manager, to free up resources.
from PIL import Image, ImageFilter, ImageEnhance, ImageDraw, ImageFont, ImageOps
import numpy as np
# Best practice for opening files
try:
with Image.open('image.jpg') as im:
im.load() # Lazily loads the image data
# ... process image
except FileNotFoundError:
print("File not found.")
π¨ Modes, Bands & Color Spaces #
Understanding modes is key to advanced manipulation.
- Bands: An image can have one or more bands. An RGB image has three bands (Red, Green, Blue).
- Modes: Defines the type and depth of a pixel. Common modes:
L(luminance),RGB,RGBA,CMYK,1(binary).
with Image.open('image.jpg') as im:
# Split image into individual bands
r, g, b = im.split()
# Merge bands back together
im_merged = Image.merge('RGB', (b, r, g)) # Swapping red and blue channels
# im_merged.show()
# Convert between modes
grayscale_im = im.convert('L')
cmyk_im = im.convert('CMYK')
πΎ Advanced File Operations #
Loading & Saving #
# Reading from a URL (requires requests)
# import requests
# from io import BytesIO
# response = requests.get('URL_TO_IMAGE')
# with Image.open(BytesIO(response.content)) as im_from_url:
# im_from_url.show()
# Draft mode for faster thumbnail creation
with Image.open('large_image.jpg') as im:
# Load a half-resolution grayscale version
im.draft('L', (im.width // 2, im.height // 2))
# im.show()
# Saving with quality options (for JPEG)
with Image.open('image.jpg') as im:
im.save('high_quality.jpg', quality=95)
im.save('low_quality.jpg', quality=20, optimize=True)
π Masking & Pasting #
A mask is a grayscale image where the value of each pixel determines the transparency for pasting.
0= Fully transparent255= Fully opaque
with Image.open('background.jpg') as bg, Image.open('logo.png') as logo:
# Create a circular mask
mask = Image.new('L', logo.size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0) + logo.size, fill=255)
# Paste logo onto background using the mask
# The box defines the top-left corner for the paste
bg.paste(logo, (50, 50), mask)
# bg.show()
β¨ Image Enhancement #
ImageEnhance Module
#
Provides classes for adjusting contrast, brightness, color, and sharpness.
with Image.open('image.jpg') as im:
# Brightness: 0.0 (black) -> 1.0 (original) -> >1.0 (brighter)
enhancer = ImageEnhance.Brightness(im)
bright_im = enhancer.enhance(1.8)
# Contrast: 0.0 (solid gray) -> 1.0 (original) -> >1.0 (more contrast)
enhancer = ImageEnhance.Contrast(im)
contrast_im = enhancer.enhance(1.5)
# Sharpness: 0.0 (blurred) -> 1.0 (original) -> 2.0 (sharpened)
enhancer = ImageEnhance.Sharpness(im)
sharp_im = enhancer.enhance(2.0)
# sharp_im.show()
ImageOps Module
#
Ready-made image processing operations.
with Image.open('image.jpg') as im:
# Auto-contrast: Maximizes image contrast
auto_contrast_im = ImageOps.autocontrast(im, cutoff=5)
# Equalize: Equalizes the image histogram
equalized_im = ImageOps.equalize(im)
# Invert colors
inverted_im = ImageOps.invert(im.convert('RGB'))
# Solarize: Invert all pixel values above a threshold
solarized_im = ImageOps.solarize(im, threshold=128)
# solarized_im.show()
π¬ Pixel-Level Operations #
point() Method & eval() Function
#
im.point(lambda i: i * 1.5): Applies a function to each pixel. Fast for single-band images.Image.eval(im, func): Applies a function to each pixel of each band in an image.
with Image.open('image.jpg') as im:
# Increase brightness of a grayscale image
brighter_l = im.convert('L').point(lambda p: p + 50)
# Apply a function to each band of an RGB image
def tint_red(pixel):
return tuple(p + 30 if i == 0 else p for i, p in enumerate(pixel))
# Note: eval is not available in Pillow 10.0.0+, use numpy instead
# tinted_im = Image.eval(im, tint_red) # Deprecated
# Modern approach with NumPy
arr = np.array(im)
arr[:, :, 0] = np.clip(arr[:, :, 0] + 30, 0, 255) # Add 30 to Red channel
tinted_im = Image.fromarray(arr)
# tinted_im.show()
Custom Convolution Kernels #
Apply custom filters using ImageFilter.Kernel.
from PIL import ImageFilter
# A simple edge detection kernel
kernel = ImageFilter.Kernel(
(3, 3), # size
(-1, -1, -1, -1, 8, -1, -1, -1, -1), # weights
1, # scale
0 # offset
)
with Image.open('image.jpg') as im:
edge_im = im.filter(kernel)
# edge_im.show()
π’ Integration with NumPy #
For complex, fast, per-pixel operations, converting to a NumPy array is the best practice.
with Image.open('image.jpg') as im:
# Image to NumPy array
arr = np.array(im)
# Example: Set a rectangular region to black
arr[100:300, 200:500] = 0 # [y1:y2, x1:x2]
# Example: Create a sepia effect
sepia_filter = np.array([
[0.393, 0.769, 0.189],
[0.349, 0.686, 0.168],
[0.272, 0.534, 0.131]
])
sepia_arr = arr @ sepia_filter.T
sepia_arr = np.clip(sepia_arr, 0, 255).astype(np.uint8)
# NumPy array back to Image
processed_im = Image.fromarray(sepia_arr)
# processed_im.show()
ποΈ Creating Animations (GIFs) #
Save a list of Image objects as an animated GIF.
frames = []
for i in range(10):
# Create a new frame for each step
frame = Image.new('RGB', (200, 200))
draw = ImageDraw.Draw(frame)
draw.ellipse((i*10, i*10, 50+i*10, 50+i*10), fill='blue')
frames.append(frame)
# Save the frames as an animated GIF
frames[0].save(
'animation.gif',
save_all=True,
append_images=frames[1:],
duration=100, # milliseconds per frame
loop=0 # 0 means loop forever
)