0% found this document useful (0 votes)
4 views56 pages

Mastering Python Pillow

The document is a comprehensive guide to mastering the Python Pillow library, covering everything from installation to advanced image processing techniques. It includes detailed explanations of image operations, drawing, filters, and real-world projects, along with a complete API reference. The content is structured into eight parts, catering to both beginners and advanced users, and emphasizes practical applications and examples.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views56 pages

Mastering Python Pillow

The document is a comprehensive guide to mastering the Python Pillow library, covering everything from installation to advanced image processing techniques. It includes detailed explanations of image operations, drawing, filters, and real-world projects, along with a complete API reference. The content is structured into eight parts, catering to both beginners and advanced users, and emphasizes practical applications and examples.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Mastering

Python Pillow
PIL / Pillow
The Complete Programmer's Reference

From Beginner Foundations to Advanced Mastery


Every Module · Every Function · Every Parameter · Real Projects

200+ Code Full API


8 Parts 35+ Chapters
Examples Reference

Written for Python 3.x · Pillow 10+


Covering every function, module, error, and real-world use case
Table of Contents
◆ PART 1 — FOUNDATIONS
Chapter 1 · Introduction to PIL and Pillow
› What is PIL?
› The history of PIL
› Why Pillow?
› What Pillow can do
Chapter 2 · Installation and Environment Setup
› pip install Pillow
› Virtual environments
› Verifying installation
› IDE setup tips
Chapter 3 · Understanding Digital Images
› Pixels explained
› Colour models
› Resolution and DPI
› Bit depth
Chapter 4 · Image Modes and Colour Spaces
› All 11 modes explained
› Mode conversion
› When to use each mode
Chapter 5 · Supported File Formats
› JPEG, PNG, GIF, WebP, TIFF and more
› Format comparison table
› Format-specific options
Chapter 6 · Pillow Architecture and Module Overview
› How Pillow is structured
› Module dependency map
› Choosing the right module

◆ PART 2 — CORE IMAGE OPERATIONS


Chapter 7 · The Image Module — Foundation
› Opening images
› Image attributes
› Creating new images
› Saving images
Chapter 8 · Resizing and Scaling
› resize()
› thumbnail()
› Resampling algorithms
› Aspect ratio helpers
Chapter 9 · Cropping and Geometry
› crop()
› rotate()
› transpose()
› Affine transforms
Chapter 10 · Pixel-Level Operations
› getpixel / putpixel
› Fast load() access
› point() transforms
MASTERING PYTHON PILLOW Complete Programmer's Reference

› NumPy integration
Chapter 11 · Colour Operations and Channels
› split() and merge()
› Alpha channel manipulation
› ImageChops arithmetic

◆ PART 3 — DRAWING AND GRAPHICS


Chapter 12 · The ImageDraw Module
› Draw context setup
› All drawing primitives
› Colours and fills
Chapter 13 · The ImageFont Module
› Loading fonts
› TrueType and default fonts
› Font metrics
Chapter 14 · Text Rendering Techniques
› Drawing text
› Centring and anchoring
› Multiline text
› Stroke effects

◆ PART 4 — FILTERS AND IMAGE ENHANCEMENT


Chapter 15 · The ImageFilter Module
› All built-in filters
› Parametric filters
› Custom kernels
Chapter 16 · The ImageEnhance Module
› Brightness, Contrast, Colour, Sharpness
› Chaining enhancements
Chapter 17 · The ImageOps Module
› Geometric ops
› Colour ops
› Fit, pad, contain
Chapter 18 · The ImageColor Module
› Parsing colours
› All supported formats

◆ PART 5 — ADVANCED IMAGE PROCESSING


Chapter 19 · Image Statistics and Histograms
› ImageStat module
› Histogram analysis
› Colour distribution
Chapter 20 · Alpha Compositing and Masking
› Compositing modes
› Alpha blending
› Mask-based compositing
Chapter 21 · Colour Management with ImageCms
› ICC profiles
› Colour space conversion
› sRGB workflow
Chapter 22 · Animated Images and ImageSequence
› Reading animated GIF
› Creating animations
› ImageSequence iterator

—3—
MASTERING PYTHON PILLOW Complete Programmer's Reference

Chapter 23 · Screen Capture with ImageGrab


› Capturing the screen
› Capturing a region
› Cross-platform notes
Chapter 24 · Advanced Pixel Manipulation
› Convolution
› Morphological operations
› Frequency domain basics

◆ PART 6 — AUTOMATION AND BATCH PROCESSING


Chapter 25 · Batch Processing Thousands of Images
› Folder scanning
› Parallel processing
› Progress tracking
Chapter 26 · Performance Optimization
› Memory management
› Lazy loading
› Caching strategies
Chapter 27 · Integrating Pillow with Other Libraries
› NumPy integration
› OpenCV interop
› Django/Flask web use

◆ PART 7 — REAL WORLD PROJECTS


Chapter 28 · Project: Batch Image Resizer
› Full step-by-step code
› CLI interface
› Logging and error handling
Chapter 29 · Project: Watermark Generator
› Text and image watermarks
› Opacity control
› Batch mode
Chapter 30 · Project: Photo Collage Generator
› Grid and mosaic layouts
› Auto-scaling
› Output options
Chapter 31 · Project: Thumbnail Generator
› Web-ready thumbnails
› EXIF rotation fix
› Batch mode
Chapter 32 · Project: Meme Creator
› Impact font rendering
› Top/bottom text
› Command-line tool
Chapter 33 · Project: Dataset Preprocessing Tool
› ML dataset prep
› Augmentation pipeline
› Format normalisation

◆ PART 8 — FULL API REFERENCE


Chapter 34 · Complete API Reference
› Image module
› ImageDraw
› ImageFont

—4—
MASTERING PYTHON PILLOW Complete Programmer's Reference

› ImageFilter
› ImageEnhance
› ImageOps
› ImageChops
› ImageColor
› ImageStat
› ImageSequence
› ImageCms
› ImageGrab
Chapter 35 · Common Errors and Troubleshooting
› FileNotFoundError
› OSError
› Mode errors
› Font errors
› Solutions
Chapter 36 · Glossary
› PIL, RGBA, EXIF, DPI, JPEG and 40+ terms

—5—
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART I

FOUNDATIONS

History · Installation · Digital Images · Colour Modes · File Formats · Architecture

1 Introduction to PIL and Pillow

1.1 What is PIL?


PIL stands for Python Imaging Library. It is an open-source library that gives Python programs the
ability to open, manipulate, analyze, and save a wide variety of raster image file formats. With just a few
lines of Python code you can resize thousands of images, add watermarks to photos, draw shapes,
render text, apply photographic filters, and combine images together in creative ways.

At its core, PIL treats every image as a rectangular grid of pixels. Each pixel holds a colour value —
usually expressed as a combination of red, green, and blue intensities. All image operations in Pillow
are ultimately about reading, computing, and writing these pixel values, whether you're cropping a
region, blurring the whole image, or drawing a line across the canvas.

1.2 The History of PIL and the Birth of Pillow


PIL was originally created by Fredrik Lundh at Secret Labs AB (Sweden) in the mid-1990s. At the time,
Python had almost no built-in image handling, and PIL filled that gap by providing a consistent, Pythonic
API for image processing. The library quickly became a standard tool in the Python ecosystem.

PIL development slowed and eventually stopped around 2009. The last official release was version
1.1.7. As Python 2 gave way to Python 3, PIL's lack of Python 3 support became a serious problem. In
2010, Alex Clark and a team of contributors created Pillow — a community-maintained fork of PIL that
immediately added Python 3 support and resumed active development. Today Pillow is maintained on
GitHub by a team of volunteers and releases new versions regularly.

Year Event

1995 Fredrik Lundh begins development of PIL at Secret Labs AB

1998 PIL 1.0 released — supports JPEG, PNG, BMP and many others

2006 PIL 1.1.6 released — last version with new features

2009 PIL 1.1.7 released — final official PIL release

2010 Alex Clark forks PIL as 'Pillow' on GitHub, adds Python 3 support

—6—
MASTERING PYTHON PILLOW Complete Programmer's Reference

Year Event

2013 Pillow 2.0 — full Python 3 support, major improvements

2020 Pillow 7.0 — drops Python 2 support entirely

2023 Pillow 10.0 — major modernisation, type hints, new APIs

2024 Pillow 10.2+ — active development, WebP/AVIF improvements

1.3 What Can Pillow Do? — Full Capability Overview


Pillow is a comprehensive image processing toolkit. The list below covers every major category of
functionality. Later chapters dedicate full coverage to each area.

• File I/O: Open, read, write and save 30+ image formats including JPEG, PNG, GIF, WebP, TIFF,
BMP, ICO, PSD, EPS, TGA, and PDF
• Geometry: Resize, crop, rotate, flip, transpose, warp, and apply affine/perspective transforms
• Colour: Convert between 10+ colour modes (RGB, RGBA, L, CMYK, HSV, LAB...), manipulate
channels, adjust histograms
• Drawing: Draw lines, rectangles, circles, ellipses, arcs, polygons, and curves onto images
• Text: Render text in any TrueType or OpenType font at any size with full Unicode support
• Filters: Apply 15+ built-in convolution filters including Gaussian blur, sharpen, edge detection,
emboss
• Enhancement: Adjust brightness, contrast, colour saturation, and sharpness with precise
numerical control
• Compositing: Paste, blend, and composite images together using alpha channels and masks
• Statistics: Analyse pixel distributions, compute histograms, mean, standard deviation per channel
• Sequences: Read and write animated GIF, multi-frame TIFF, and other multi-page formats
• Screen capture: Take screenshots of the entire screen or a specific region (ImageGrab)
• Colour management: Apply ICC colour profiles for accurate cross-device colour reproduction
• Pixel access: Read and write individual pixels, apply per-pixel transforms via lookup tables

1.4 Who Uses Pillow?


• Web developers — generating thumbnails, resizing user-uploaded photos, creating OG images
• Data scientists — preprocessing image datasets for machine learning models
• Game developers — sprite sheets, texture atlases, icon generation
• Photographers — batch watermarking, format conversion, metadata extraction
• Automation engineers — document generation, PDF creation, report rendering
• System administrators — screenshot tools, monitoring dashboards with image output

■ Note: Pillow is the most downloaded image processing library on PyPI with hundreds of millions of
downloads. It is a dependency of Django, Matplotlib, scikit-image, and many other major Python libraries.

—7—
MASTERING PYTHON PILLOW Complete Programmer's Reference

2 Installation and Environment Setup

2.1 Installing Pillow


Pillow is distributed on the Python Package Index (PyPI). Installation requires Python 3.8 or higher. The
install command is straightforward:

pip install Pillow

The package name is Pillow (capital P) on PyPI. However, when you import it in Python, you use the
original name PIL — this is because Pillow maintains backward compatibility with the original PIL
namespace.

# INSTALL command (terminal / command prompt) pip install Pillow # UPGRADE existing
installation pip install --upgrade Pillow # Install a specific version pip install
Pillow==10.2.0 # Install with optional dependencies (WebP, AVIF, JPEG2000 etc.) pip
install Pillow[all]

2.2 Virtual Environments — Best Practice


A virtual environment is an isolated Python installation. It is strongly recommended to always install
Pillow (and all Python packages) inside a virtual environment rather than globally. This prevents version
conflicts between projects and keeps your system Python clean.

# Step 1: Create a virtual environment python -m venv myproject_env # Step 2: Activate it #


Windows: myproject_env\Scripts\activate # macOS / Linux: source myproject_env/bin/activate
# Step 3: Install Pillow inside the environment pip install Pillow # Step 4: Verify python
-c "from PIL import Image; print('Pillow ready!')"

2.3 Verifying Your Installation


from PIL import Image import PIL # Print version print(PIL.__version__) # e.g. 10.2.0 #
Full capability report — shows which formats and features are available from PIL import
features [Link]() # prints detailed info # Check a specific feature
print([Link]('webp')) # True if WebP is supported print([Link]('jpg')) #
True if JPEG is supported

10.2.0 --- PIL CORE support ok, compiled for 10.2.0 --- JPEG support ok --- PNG support ok
--- WebP support ok ...

2.4 System Dependencies


On most systems, pip will install a pre-built binary wheel that includes all common format decoders.
However some formats require external libraries to be installed on your operating system:

Format Dependency Install (Ubuntu/Debian) Install (macOS/Homebrew)

JPEG libjpeg apt install libjpeg-dev brew install jpeg

PNG zlib apt install zlib1g-dev Built-in on macOS

—8—
MASTERING PYTHON PILLOW Complete Programmer's Reference

Format Dependency Install (Ubuntu/Debian) Install (macOS/Homebrew)

TIFF libtiff apt install libtiff-dev brew install libtiff

WebP libwebp apt install libwebp-dev brew install webp

AVIF libavif apt install libavif-dev brew install libavif

EPS/PDF Ghostscript apt install ghostscript brew install ghostscript

■ Tip: If you install Pillow via pip on Windows, macOS, or modern Linux, the pre-built wheel includes libjpeg,
zlib, libpng, and libwebp automatically. You only need to install system dependencies if you are compiling
from source.

—9—
MASTERING PYTHON PILLOW Complete Programmer's Reference

3 Understanding Digital Images

3.1 What is a Digital Image?


A digital image is a rectangular grid of tiny coloured squares called pixels (short for 'picture elements').
Each pixel represents the colour at one point in the image. When pixels are small enough and packed
tightly enough together, your eyes perceive them as a smooth, continuous picture rather than a grid.

In Pillow, the coordinate system places the origin (0, 0) at the top-left corner of the image. The x-axis
increases to the right, and the y-axis increases downward. This is the standard convention for computer
graphics (note: this is different from mathematics where y increases upward).

x=0 x=1 x=2 ... →

y=0 (0,0) (1,0) (2,0) → increasing x

y=1 (0,1) (1,1) (2,1)

y=2 (0,2) (1,2) (2,2)

↓ ↓ increasing y

Figure 3.1 — Pixel coordinate system in Pillow

3.2 Pixels and Colour Values


In a standard RGB (Red, Green, Blue) colour image, each pixel is stored as three numbers, one for
each colour channel. Each channel ranges from 0 (none of that colour) to 255 (maximum intensity). This
gives us 256 × 256 × 256 = 16,777,216 possible colours per pixel.

# Some example RGB pixel values pixel_red = (255, 0, 0) # pure red pixel_green = ( 0, 255,
0) # pure green pixel_blue = ( 0, 0, 255) # pure blue pixel_white = (255, 255, 255) # white
pixel_black = ( 0, 0, 0) # black pixel_yellow = (255, 255, 0) # yellow pixel_gray = (128,
128, 128) # 50% gray

3.3 Image Resolution and DPI


Image resolution refers to the number of pixels in an image, typically expressed as width × height (e.g.
1920 × 1080). A higher resolution means more pixels, more detail, but also a larger file size.

DPI (Dots Per Inch) is a printing concept — it tells a printer how many pixels to place per inch of paper.
A 300 DPI image will look sharp when printed. A 72 DPI image is fine for screen display but will look
blurry when printed large. DPI does not affect how an image looks on screen — only its print size.

Resolution (px) DPI Print size at that DPI Common Use

72 × 72 72 1" × 1" Screen / web icons

— 10 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Resolution (px) DPI Print size at that DPI Common Use

800 × 600 72 ~11" × 8" Web images

1920 × 1080 96 ~20" × 11" Full HD screen

3000 × 2000 300 10" × 6.7" Print photography

7952 × 5304 300 26.5" × 17.7" Large format print

3.4 Bit Depth


Bit depth (also called colour depth) determines how many possible values each colour channel can
hold. Higher bit depth = more possible colours and smoother gradients.

• 1-bit: Each pixel is 0 or 1 (black or white only). Used for fax images and masks.
• 8-bit per channel (24-bit total for RGB): The standard for photographs. 256 levels per channel.
• 16-bit per channel (48-bit total): Used in professional photography and RAW image editing.
Smoother gradients.
• 32-bit float: Used in scientific imaging and HDR. Values not limited to 0–255.

■ Note: Standard Pillow images use 8-bit per channel (mode 'L' for grayscale, 'RGB' for colour). For 16-bit
images use mode 'I' (32-bit integer) or 'F' (32-bit float). Most consumer cameras and monitors work with 8-bit.

— 11 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

4 Image Modes and Colour Spaces

The mode of an image is one of the most important concepts in Pillow. It defines what kind of data each
pixel holds: how many channels (colour components) there are, what each channel means, and how
many bits per pixel are used. Getting the mode right is essential — many Pillow operations only work on
specific modes.

4.1 Complete Mode Reference


Mode Full Name Channels Bits/px Pixel Value Type Description & When to Use

1 Binary 1 1 0 or 255 Pure black & white. Each pixel is either off (0) or on (255). Used fo

L Luminance 1 8 int 0–255 Grayscale. One channel, 256 shades of gray. Used for B&W phot

P Palette 1 8 int 0–255 (index) Indexed colour. Each pixel is an index into a 256-colour palette ta

RGB Red Green Blue 3 24 (R,G,B) tuple Standard full colour. The most common mode. Used for JPEG, BM

RGBA RGB + Alpha 4 32 (R,G,B,A) tuple RGB with transparency. Alpha channel 0=transparent, 255=opaqu

CMYK Cyan Magenta Yellow Black


4 32 (C,M,Y,K) tuple Subtractive colour for printing. Values are INVERTED — 0=full ink

YCbCr Luminance + Chrominance


3 24 (Y,Cb,Cr) tuple Separates luminance from colour info. Used internally by JPEG. G

LAB CIE L*a*b* 3 24 (L,a,b) tuple Perceptually uniform colour space. Good for colour-difference ope

HSV Hue Saturation Value 3 24 (H,S,V) tuple Intuitive colour control. H=colour type, S=vividness, V=brightness

I Integer pixels 1 32 int32 32-bit signed integer pixels. Used for scientific and medical imagin

F Float pixels 1 32 float32 32-bit floating point pixels. Used for scientific data, HDR, and inte

4.2 Reading and Setting the Mode


from PIL import Image img = [Link]('[Link]') print([Link]) # 'RGB'
print([Link]()) # ('R', 'G', 'B') # Create images in specific modes rgb =
[Link]('RGB', (400, 300), (255, 200, 100)) rgba = [Link]('RGBA', (400, 300), (255,
200, 100, 128)) gray = [Link]('L', (400, 300), 128) bw = [Link]('1', (400, 300), 255)

4.3 Converting Between Modes


Use [Link](mode) to change the colour space. This always returns a new Image object — the
original is never modified.

img = [Link]('[Link]') # RGB # Common conversions gray = [Link]('L') # colour


→ grayscale rgba = [Link]('RGBA') # add alpha channel (fully opaque) cmyk =
[Link]('CMYK') # for printing # RGBA back to RGB — transparent pixels become BLACK
(common mistake!) rgb = [Link]('RGB') # Correct way: RGBA to RGB with white
background bg = [Link]('RGB', [Link], 'white') [Link](rgba, mask=[Link]()[3]) #
split()[3] = alpha channel # bg is now rgba composited on white background, in RGB mode #
Grayscale back to RGB (needed before colour drawing) rgb_from_gray = [Link]('RGB')

— 12 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

■ Warning: Converting RGBA → RGB with .convert('RGB') will make transparent pixels solid black. Always
composite onto a background colour first if you want transparent areas to have a specific fill colour.

4.4 Common Mode Errors


Mode mismatches are one of the most common sources of errors for Pillow beginners. Here are the
situations to watch for:

Situation Error / Problem Solution

Transparent
Calling .paste() with RGBA onto RGB areas paste as black Use mask=[Link]()[3] parameter

Colours rendered as gray


Drawing colour on an 'L' image Convert to RGB first: [Link]('RGB')

Saving RGBA to JPEG OSError: cannot write mode RGBA as JPEG


Convert to RGB: [Link]('RGB').save(...)

Operations
Loading a PNG and getting palette fail or look wrong
('P') Convert: [Link]('RGBA') or [Link]('RGB')

Trying to getpixel on 'CMYK' Returns 4-tuple, not 3 Remember CMYK has 4 channels (C,M,Y,K)

— 13 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

5 Supported File Formats

Pillow supports an impressive range of image file formats. Understanding the strengths and limitations
of each format helps you choose the right one for every use case and configure saving options
correctly.

5.1 Format Comparison Table


Format Ext Lossy? Alpha? Animation? Best For Key Weakness

JPEG .jpg Yes No No Photos, web images Each save degrades quality

PNG .png No Yes No Screenshots, graphics Large file size for photos

GIF .gif No* Yes* Yes Simple animations Max 256 colours per frame

WebP .webp Both Yes Yes Web — best compression Not all software supports it

TIFF .tiff Both Yes Yes Print, archival, scientific Very large files

BMP .bmp No No No Windows compatibility No compression, huge files

ICO .ico No Yes No Windows application icons Limited to 256×256px per size

PDF .pdf Dep No Yes Multi-page document output Read-only in most editors

AVIF .avif Both Yes Yes Next-gen web images Requires libavif, newer

PSD .psd No Yes No Photoshop source files Read-only in Pillow

EPS .eps No No No Vector/PostScript graphics Requires Ghostscript

TGA .tga No Yes No Game development Rare outside game engines

* GIF supports only 1-bit transparency (one transparent colour), not full alpha. Lossy via 256-colour quantisation.

5.2 JPEG — Joint Photographic Experts Group


JPEG is the most common format for photographs and complex natural images. It uses lossy
compression — each save discards some image data to reduce file size. The quality setting controls the
trade-off between file size and image fidelity. JPEG does not support transparency — any
transparent (RGBA) image must be converted to RGB before saving as JPEG.

from PIL import Image img = [Link]('[Link]').convert('RGB') # ensure RGB # Standard


quality save [Link]('[Link]', quality=85) # Maximum quality (larger file, minimal loss)
[Link]('out_hq.jpg', quality=95, subsampling=0, optimize=True) # Progressive JPEG (loads
gradually in browsers) [Link]('out_prog.jpg', quality=85, progressive=True) # quality=95
is usually the sweet spot for quality vs size # quality=75 is the standard web default #
Never repeatedly save a JPEG — quality degrades each time!

■ Warning: Never repeatedly open and re-save a JPEG. Each save is lossy — the image degrades further
with each generation. If you need to edit a JPEG multiple times, keep the working copy as PNG or TIFF, and
only export to JPEG as the final step.

— 14 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

5.3 PNG — Portable Network Graphics


PNG uses lossless compression — saving and re-opening a PNG never degrades quality. PNG fully
supports transparency via an alpha channel, making it ideal for logos, screenshots, and graphics. The
compression level controls file size only — quality is always 100% regardless of compression level.

# Save PNG with maximum compression (smallest file, slower save) [Link]('[Link]',
optimize=True, compress_level=9) # Save PNG with fast compression (faster save, slightly
larger file) [Link]('[Link]', compress_level=1) # Save RGBA PNG (with transparency)
rgba_img.save('[Link]') # transparency preserved automatically

5.4 WebP — The Modern Choice


WebP is a modern format developed by Google that produces files 25–35% smaller than JPEG at
equivalent quality. It supports both lossy and lossless compression, full alpha transparency, and
animation. WebP is now supported by all major browsers and operating systems. For web projects,
WebP is usually the best choice.

# Lossy WebP (like JPEG but better compression) [Link]('[Link]', quality=85) #


Lossless WebP (like PNG but smaller) [Link]('[Link]', lossless=True) # Animated WebP
frames[0].save('[Link]', save_all=True, append_images=frames[1:], duration=100, loop=0)

— 15 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

6 Pillow Architecture and Module Overview

Understanding how Pillow is organised helps you know which module to reach for when you need a
specific feature. Pillow is structured as a set of sub-modules, each focused on a specific aspect of
image processing.

6.1 Module Overview Table


Module Purpose Typical Import

Image Core module — open, save, create, resize, crop, convert, paste from PIL import Image

ImageDraw Draw shapes, lines, arcs, polygons, text onto images from PIL import ImageDraw

ImageFont Load TrueType/OpenType fonts for use with ImageDraw from PIL import ImageFont

ImageFilter Predefined and custom convolution filters (blur, sharpen, edges...) from PIL import ImageFilter

ImageEnhance Adjust brightness, contrast, colour saturation, sharpness from PIL import ImageEnhance

ImageOps High-level one-liner operations (grayscale, invert, fit, pad...) from PIL import ImageOps

ImageChops Arithmetic operations on image channels (add, subtract, multiply...) from PIL import ImageChops

ImageColor Parse colour strings ('red', '#ff0000', 'hsl(0,100%,50%)') from PIL import ImageColor

ImageStat Statistical analysis of images (mean, std, histogram, extrema) from PIL import ImageStat

ImageSequence Iterate over frames in animated GIFs and multi-page TIFFs from PIL import ImageSequence

ImageCms Colour management using ICC profiles for print-accurate colour from PIL import ImageCms

ImageGrab Take screenshots of the screen or a specific screen region from PIL import ImageGrab

ImagePath 2D path geometry operations for drawing complex shapes from PIL import ImagePath

ImagePalette Manage colour palettes for indexed-colour ('P' mode) images from PIL import ImagePalette

ImageTk Convert Pillow images for display in Tkinter GUI applications from PIL import ImageTk

ImageQt Convert Pillow images for display in Qt (PyQt5/PySide2) apps from PIL import ImageQt

ExifTags Dictionary mapping EXIF tag ID numbers to human-readable names from PIL import ExifTags

6.2 How Pillow Handles Images Internally


When you call [Link](), Pillow performs a lazy read — it reads the file header to determine the
format, mode, and size, but does not load all pixel data into memory immediately. Pixel data is loaded
on demand when you first access it. This makes opening large images very fast.

Once loaded, an Image object stores its pixel data in a C-level buffer. Python operations on this buffer
go through the ImagingCore C extension which provides high performance. When you apply a filter or
resize an image, Pillow calls optimised C code internally — not a slow Python loop.

— 16 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

■ Tip: Always call [Link]() explicitly if you need to ensure pixel data is in memory before closing the
source file or before passing the image to another thread.

— 17 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART II

CORE IMAGE OPERATIONS

Image Module · Opening · Saving · Resizing · Cropping · Rotating · Pixel Access

7 The Image Module — Foundation of Everything

The Image module is the single most important module in Pillow. Every image processing task begins
here. An Image object is the Python representation of an image — it holds the pixel data, mode, size,
and metadata. Nearly every other Pillow module works by receiving or returning Image objects.

7.1 Image Module Function Summary


Function / Method Description Typical Use Case

[Link](fp) Open an image file from disk or stream Loading any image

[Link](mode, size) Create a blank image Creating a canvas for drawing

[Link](arr) Create image from NumPy array Machine learning pipelines

Create image from raw byte data


[Link](mode,size,data) Low-level image construction

[Link](mode, bands) Merge single-channel images into one Reassembling colour channels

[Link](im1,im2,a) Cross-fade two images Dissolve transitions

[Link](i1,i2,m) Blend using a mask Masked compositing

Porter-Duff alpha compositing


Image.alpha_composite(dst,src) Correct RGBA layer compositing

[Link](fp) Save image to file or stream Export to any format

[Link]() Open in system image viewer Quick debug preview

[Link]() Return an independent copy Non-destructive editing

[Link](mode) Change colour mode RGB→grayscale, RGB→RGBA etc.

[Link](size) Resize to exact dimensions Scaling images

[Link](size) Resize in-place, preserve aspect ratio Making thumbnails

[Link](box) Extract a rectangular region Cropping

[Link](angle) Rotate by degrees Correcting orientation

[Link](method) Flip or rotate 90/180/270 Mirroring, right-angle rotation

[Link](f) Apply an ImageFilter Blur, sharpen, edge detection

— 18 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Function / Method Description Typical Use Case

[Link](src, box) Paste image onto this one Watermarks, collages

[Link]() Load pixels; return pixel access object Fast pixel-level editing

[Link](xy) Get pixel value at (x,y) Reading individual pixels

[Link](xy, val) Set pixel value at (x,y) Writing individual pixels

[Link](lut) Apply per-pixel lookup table Fast tonal adjustments

[Link]() Split into individual channels Channel manipulation

[Link]() Bounding box of non-zero content Auto-cropping whitespace

[Link]() List of (count, colour) pairs Palette analysis

[Link]() Pixel value histogram Exposure analysis

[Link]() EXIF metadata dictionary Reading camera metadata

[Link](colors) Reduce to N colours ('P' mode) GIF optimisation

[Link]() Check file integrity Validating image files

[Link](frame) Go to frame N in multi-frame image Animated GIF navigation

[Link]() Return raw pixel bytes Interfacing with other libs

7.2 Opening Images


[Link]() is the most fundamental Pillow function. It accepts a file path, a URL string (via
[Link]), or any file-like object. It performs a lazy read — the header is read immediately but pixel
data is loaded on first access.

[Link]() Module: [Link]

Purpose: Open and identify the given image file.


[Link](fp, mode='r', formats=None)

Parameter Type Default Description

fp str/Path/IO required File path string, [Link], or file-like object opened in binary mode

mode str 'r' Must be 'r' (read). Provided for API compatibility.

formats list/None None List of format names to try. If None, all supported formats are tried.

Returns: An Image object. The file is not fully loaded until pixel data is first accessed.

from PIL import Image # Open from a file path (string) img = [Link]('[Link]') # Open
from a [Link] from pathlib import Path img = [Link](Path('photos') /
'[Link]') # Open from a URL (download first) import [Link] from io import
BytesIO url = '[Link] with [Link](url) as resp:
img = [Link](BytesIO([Link]())) # Always convert mode after opening if you need a
specific mode img = [Link]('[Link]').convert('RGB') # force RGB (strips alpha) img
= [Link]('[Link]').convert('RGBA') # ensure RGBA (adds alpha if missing)

— 19 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Common Errors:
• FileNotFoundError: The file path does not exist. Check spelling, forward/backward slashes, and
working directory.
• UnidentifiedImageError: The file is not a valid image or is corrupted.
• OSError: cannot identify image file: Same as above — file may be empty or not an image.

7.3 Creating New Images

[Link]() Module: [Link]

[Link](mode, size, color=0)

Parameter Type Default Description

mode str required Colour mode: 'RGB', 'RGBA', 'L', '1', 'CMYK', etc.

size tuple required (width, height) in pixels

color int/tuple/str 0 Fill colour. Can be int (for L/1), tuple (for RGB/RGBA), or colour name string.

# White RGB canvas canvas = [Link]('RGB', (800, 600), (255, 255, 255)) # Black RGB
canvas (default) canvas = [Link]('RGB', (800, 600)) # Colour by name blue_canvas =
[Link]('RGB', (400, 300), 'royalblue') # Fully transparent RGBA canvas transp =
[Link]('RGBA', (400, 300), (0, 0, 0, 0)) # Semi-transparent red RGBA red_50 =
[Link]('RGBA', (400, 300), (255, 0, 0, 128)) # Grayscale canvas — 50% gray gray =
[Link]('L', (400, 300), 128)

7.4 Image Attributes in Detail


img = [Link]('[Link]') # Dimensions print([Link]) # (width, height) → (1920,
1080) print([Link]) # 1920 print([Link]) # 1080 # Mode and format print([Link]) #
'RGB' print([Link]) # 'JPEG' (None if created in memory) # Metadata print([Link]) #
{'dpi': (72,72), 'jfif': 257, ...} # For animated/multi-frame images print(img.n_frames) #
number of frames (1 for still images) print(img.is_animated) # True for animated GIF etc. #
Bands print([Link]()) # ('R', 'G', 'B') for RGB # Read EXIF data exif = [Link]()
from PIL import ExifTags for tag_id, value in [Link](): tag =
[Link](tag_id, tag_id) print(f"{tag}: {value}")

— 20 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

8 Resizing and Scaling Images

Resizing is one of the most common image operations. Pillow provides two primary methods: resize()
for exact dimensions and thumbnail() for aspect-ratio-safe scaling. The choice of resampling algorithm
significantly affects both output quality and processing speed.

8.1 resize() — Exact Dimensions

[Link]() Module: [Link]

[Link](size, resample=None, box=None, reducing_gap=None)

Parameter Type Default Description

size tuple required (width, height) in pixels — the exact output size

resample constant [Link] Resampling filter. See table below.

box tuple None (left, upper, right, lower) source region to resize. Resizes a crop of the source.

reducing_gap float None Speed optimisation for large downscaling. 2.0 or 3.0 gives good results.

from PIL import Image img = [Link]('[Link]') # Basic resize — LANCZOS for best
quality small = [Link]((640, 480), [Link]) # Fast resize for preview (lower
quality but fast) preview = [Link]((320, 240), [Link]) # Resize a specific
region of the source image region_resized = [Link]((200, 200), [Link],
box=(100, 100, 500, 500)) # Enlarge (upscale) large = [Link](([Link] * 2, [Link]
* 2), [Link]) # Resize maintaining aspect ratio manually def
resize_keep_aspect(img, max_w, max_h): ratio = min(max_w / [Link], max_h / [Link])
new_w = int([Link] * ratio) new_h = int([Link] * ratio) return [Link]((new_w,
new_h), [Link])

8.2 Resampling Filter Comparison


Filter Constant Quality Speed Best For

Nearest Neighbour [Link] Low Fastest Pixel art, speed-critical previews

Box [Link] Medium Fast Downscaling only

Bilinear [Link] Medium Fast Upscaling previews, UI thumbnails

Hamming [Link] Medium+ Medium Downscaling with sharp edges

Bicubic [Link] High Medium General-purpose (good default)

Lanczos/Sinc [Link] Best Slowest Final output, print, maximum quality

■ Tip: Use [Link] for any final output that people will see. Use [Link] or
[Link] for quick previews during development. The difference in quality is especially visible when
upscaling.

— 21 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

8.3 thumbnail() — Aspect-Ratio Safe Scaling

[Link]() Module: [Link]

[Link](size, resample=[Link], reducing_gap=2.0)

thumbnail() is different from resize() in several important ways:

• It modifies the image in-place — it does not return a new image


• It never enlarges — if the image is already smaller than size, it is unchanged
• It preserves aspect ratio — the image fits within size without distortion
• The resulting image may be smaller than size in one dimension
img = [Link]('[Link]') # e.g. 4000 × 3000 # Resize to fit within 800×800 (becomes
800×600, aspect preserved) [Link]((800, 800)) print([Link]) # (800, 600) # Fit
within 200×400 — width is the limiting factor img2 = [Link]('[Link]') # 4000 × 3000
[Link]((200, 400)) # width limits: 200 × 150 print([Link]) # (200, 150) # Common
pattern: create thumbnail and save img3 = [Link]('large_photo.jpg')
[Link]((256, 256), [Link]) [Link]('[Link]', quality=85)

■ Note: Because thumbnail() modifies in-place, always work on a copy if you want to preserve the original:
copy = [Link](); [Link]((256, 256))

— 22 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

9 Cropping, Rotating, and Geometric Transforms

9.1 crop() — Extract a Region

[Link]() Module: [Link]

[Link](box=None)

Parameter Type Default Description

box tuple None (left, upper, right, lower) pixel coordinates. Returns full image if None.

The box coordinates use the standard Pillow coordinate system: (0,0) = top-left. The box is exclusive
on the right and bottom — crop((0,0,100,100)) returns a 100×100 image with pixels at x=0..99, y=0..99.

img = [Link]('[Link]') # 1920 × 1080 # Basic crop cropped = [Link]((100, 50, 700,
450)) # 600 × 400 region # Crop the top half top_half = [Link]((0, 0, [Link],
[Link] // 2)) # Crop the left third left_third = [Link]((0, 0, [Link] // 3,
[Link])) # Crop the centre square side = min([Link], [Link]) cx, cy = [Link]
// 2, [Link] // 2 center_square = [Link]((cx - side//2, cy - side//2, cx + side//2,
cy + side//2)) # crop() can extend BEYOND image bounds — extra area is filled with 0 beyond
= [Link]((-50, -50, [Link]+50, [Link]+50)) # The outer 50 pixels on all sides will
be black

9.2 rotate() — Rotate by Arbitrary Angle

[Link]() Module: [Link]

[Link](angle, resample=0, expand=False, center=None, translate=None,


fillcolor=None)

Parameter Type Default Description

angle float required Rotation angle in degrees. Positive = anti-clockwise.

resample constant NEAREST Resampling filter. Use [Link] or [Link] for smooth rotation.

expand bool False If True, expand the canvas to fit the entire rotated image. If False, crop to original size.

center tuple None (x, y) rotation center. Defaults to center of image.

translate tuple None (x, y) post-rotation translation offset.

fillcolor str/tuple None Colour for pixels outside the original image after rotation.

# 45° anti-clockwise, canvas unchanged (corners cropped) rot = [Link](45) # 45°


anti-clockwise, canvas expanded to fit full image rot_exp = [Link](45, expand=True) #
45° with smooth interpolation and white fill rot_smooth = [Link](45, expand=True,
resample=[Link], fillcolor='white') # Rotate around a specific point (top-left
corner) rot_corner = [Link](15, center=(0, 0), expand=True) # Correct a slightly
tilted scanned document straightened = [Link](0.5, resample=[Link],
expand=False, fillcolor='white')

— 23 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

9.3 transpose() — Flip and Right-Angle Rotations

[Link]() Module: [Link]

[Link](method)

Method Constant Description Use Case

Image.FLIP_LEFT_RIGHT Mirror horizontally (left↔right) Creating mirror image effects

Image.FLIP_TOP_BOTTOM Mirror vertically (top↔bottom) Flipping upside-down

Image.ROTATE_90 Rotate 90° anti-clockwise Portrait→landscape correction

Image.ROTATE_180 Rotate 180° Upside-down correction

Image.ROTATE_270 Rotate 270° anti-clockwise (= 90° clockwise) Landscape→portrait

[Link] Transpose along main diagonal (reflect x and y) Rare — mathematical transpose

[Link] Transpose along anti-diagonal Rare — mathematical transverse

# Flip horizontally — create a mirror image mirrored =


[Link](Image.FLIP_LEFT_RIGHT) # Rotate 90° clockwise (= ROTATE_270 anti-clockwise)
cw_90 = [Link](Image.ROTATE_270) # Fix a photo taken with phone rotated sideways
fixed = [Link](Image.ROTATE_90) # Automatic EXIF-based rotation fix (best approach
for phone photos) from PIL import ImageOps fixed_auto = ImageOps.exif_transpose(img)

■ Tip: Always use ImageOps.exif_transpose() on photos taken with a smartphone. Phone cameras often
store images in their native sensor orientation and record the correct viewing orientation in EXIF metadata.
Without this fix, many photos will appear sideways.

— 24 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

10 Pixel-Level Operations

Sometimes the operations you need are so specific that you must work on individual pixels. Pillow
provides several approaches, from simple but slow (getpixel/putpixel) to fast bulk access (load()) to
vectorised transforms (point()). Choosing the right approach is critical for performance.

10.1 Performance Comparison


Method Speed Memory Best For

getpixel / putpixel Very slow Low Accessing 1–10 pixels for inspection

load() pixel access Medium Low Loops over all pixels in Python

point() LUT Very fast Low Per-pixel tonal/colour transforms

NumPy array Fastest High Mathematical operations on all pixels

10.2 getpixel() and putpixel()


img = [Link]('[Link]').convert('RGB') # Read a pixel r, g, b = [Link]((100,
200)) # returns (R, G, B) tuple print(f'R={r}, G={g}, B={b}') # e.g. R=142, G=98, B=65 #
Write a pixel [Link]((100, 200), (255, 0, 0)) # set pixel at (100,200) to red # For
grayscale ('L' mode) gray = [Link]('L') value = [Link]((100, 200)) # returns a
single int 0-255 [Link]((100, 200), 128) # set pixel to 50% gray # For RGBA rgba =
[Link]('RGBA') r, g, b, a = [Link]((100, 200)) # 4-tuple

■ Warning: Never use getpixel/putpixel inside a loop over all pixels. A 1000×1000 image has 1,000,000
pixels. Python function call overhead makes this approach 100x slower than using load() and 1000x slower
than NumPy.

10.3 load() — Fast Pixel Access


pixels = [Link]() # returns a PixelAccess object # Read pixel r, g, b = pixels[100, 200]
# Write pixel pixels[100, 200] = (255, 0, 0) # Invert all pixels (Python loop — acceptable
for demonstration) for x in range([Link]): for y in range([Link]): r, g, b =
pixels[x, y] pixels[x, y] = (255-r, 255-g, 255-b) # Create a custom gradient canvas =
[Link]('RGB', (256, 256)) px = [Link]() for x in range(256): for y in range(256):
px[x, y] = (x, y, 128) # R varies left-right, G varies top-bottom

10.4 point() — Vectorised Per-Pixel Transform


point() applies a function or lookup table to every pixel value simultaneously. It runs in C internally and is
dramatically faster than any Python loop.

# Apply a lambda to every pixel value inverted = [Link](lambda p: 255 - p) bright =


[Link](lambda p: min(int(p * 1.4), 255)) darken = [Link](lambda p: int(p * 0.6)) #
Threshold: convert to black-and-white bw = [Link]('L').point(lambda p: 255 if p > 128
else 0) # Convert to '1' (binary) mode bw_mask = [Link]('L').point(lambda p: 255 if p
> 128 else 0, '1') # Pre-build a lookup table for maximum speed # Create a gamma correction
LUT (gamma = 0.5) import math lut = [int(255 * [Link](i / 255, 0.5)) for i in range(256)]

— 25 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

# Apply to all channels at once (multiply by 3 for RGB) gamma_corrected = [Link](lut *


3)

10.5 NumPy Integration


For heavy numerical pixel operations, converting to a NumPy array gives the best performance. NumPy
operations are vectorised — they run in optimised C/FORTRAN code over the entire array at once.

import numpy as np from PIL import Image img = [Link]('[Link]').convert('RGB') #


Convert to NumPy array arr = [Link](img) # shape: (height, width, 3), dtype: uint8
print([Link]) # (1080, 1920, 3) print([Link]) # uint8 # --- Examples of NumPy pixel
operations --- # Invert (vectorised, very fast) inverted = 255 - arr # Increase brightness
by 50% (clip to 255) bright = [Link](arr * 1.5, 0, 255).astype(np.uint8) # Extract red
channel only (zero out G and B) red_only = [Link]() red_only[:, :, 1] = 0 # zero green
red_only[:, :, 2] = 0 # zero blue # Convert back to Pillow Image result =
[Link](bright) [Link]('[Link]')

— 26 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

11 Colour Operations and Channel Manipulation

11.1 Splitting and Merging Channels


Many image processing tasks require operating on individual colour channels separately. split()
decomposes a multi-channel image into individual grayscale images, and merge() reassembles them.

img = [Link]('[Link]') # RGB # Split into individual channels (each becomes an 'L'
mode image) r, g, b = [Link]() # Visualise a single channel [Link]() # shows red channel
as grayscale # Swap red and blue channels swapped = [Link]('RGB', (b, g, r)) #
Increase only the red channel r_arr = [Link](r) r_boosted =
[Link]([Link](r_arr * 1.5, 0, 255).astype(np.uint8)) boosted =
[Link]('RGB', (r_boosted, g, b)) # For RGBA — split out alpha r, g, b, a =
[Link]('RGBA').split() # ... modify ... result = [Link]('RGBA', (r, g, b, a))

11.2 Alpha Channel Operations


# Add alpha channel to an RGB image rgba = [Link]('RGBA') # Make entire image 50%
transparent [Link](128) # 0=fully transparent, 255=fully opaque # Vary transparency
based on pixel position (gradient fade) img = [Link]('[Link]').convert('RGBA') r,
g, b, a = [Link]() # Create a gradient alpha (transparent on left, opaque on right)
import numpy as np alpha_arr = [Link](0, 255, [Link], dtype=np.uint8) alpha_arr =
[Link](alpha_arr, ([Link], 1)) alpha = [Link](alpha_arr, mode='L')
[Link](alpha) [Link]('[Link]')

11.3 ImageChops — Channel Arithmetic


The ImageChops module provides mathematical operations on image channels, similar to blend
modes in Photoshop. All functions take two images of the same size and mode and return a new image.

Function / Method Description Typical Use Case

[Link](im1,im2) Add pixel values: (im1+im2)/scale+offset Lightening blend effect

[Link](im1,im2) Subtract: (im1-im2)/scale+offset Darkening blend effect

[Link](im1,im2) Multiply channels (darkens) Multiply blend mode

[Link](im1,im2) Screen blend (lightens) Screen blend mode

Absolute difference between pixels


[Link](im1,im2) Change detection

[Link](im1,im2) Maximum of each pixel Lighten blend

[Link](im1,im2) Minimum of each pixel Darken blend

[Link](im) Invert all pixel values Negative effect

[Link](im,x,y) Shift image (wraps around) Texture tiling

Soft light blend mode


ImageChops.soft_light(im1,im2) Subtle light/dark

Hard light blend mode


ImageChops.hard_light(im1,im2) Strong light/dark

— 27 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Function / Method Description Typical Use Case

[Link](im1,im2) Overlay blend mode Contrast enhance

from PIL import ImageChops img1 = [Link]('[Link]').convert('RGB') img2 =


[Link]('[Link]').convert('RGB').resize([Link]) # Detect changes between two images
diff = [Link](img1, img2) bbox = [Link]() # bounding box of changed
area (or None) # Multiply blend — darkens multiplied = [Link](img1, img2) #
Shift image 50 pixels right and 30 pixels down shifted = [Link](img1, 50, 30)

— 28 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART III

DRAWING AND GRAPHICS

ImageDraw · ImageFont · Shapes · Text · Typography

12 The ImageDraw Module

The ImageDraw module provides 2D graphics drawing capabilities. It lets you draw lines, shapes, text,
and other geometric primitives directly onto an Image object. The drawing context is created once and
all drawing operations modify the underlying image pixel data in real time.

12.1 Setting Up a Drawing Context


from PIL import Image, ImageDraw # Draw on an existing image img =
[Link]('[Link]').convert('RGB') draw = [Link](img) # Draw on a fresh blank
canvas canvas = [Link]('RGB', (600, 400), 'white') draw = [Link](canvas) # Draw
on an RGBA image (supports transparent drawing) canvas_rgba = [Link]('RGBA', (600,
400), (255,255,255,255)) draw_rgba = [Link](canvas_rgba) # After all drawing is
done, save or use the image [Link]('[Link]')

■ Note: The Draw object holds a reference to the image. Any drawing method called on 'draw' immediately
modifies the underlying image pixels. There is no 'commit' step — changes take effect the moment the
method returns.

12.2 Complete Drawing Method Reference


Function / Method Description Typical Use Case

[Link](xy, fill, width) Straight line(s) through a sequence of points Graphs, borders, underlining

Axis-aligned
[Link](xy, fill, outline, width)rectangle Boxes, borders, highlight regions

Rectangle
draw.rounded_rectangle(xy, radius, with rounded corners
...) Modern UI-style boxes

Ellipse
[Link](xy, fill, outline, inside a bounding box
width) Circles, ovals

[Link](xy, radius, fill,Circle centred at (x,y) with given radius


outline) Dots, circular markers

Arcwidth)
[Link](xy, start, end, fill, (ellipse outline portion) Progress bars, dials

Arc + ...)
[Link](xy, start, end, fill, closing straight line Filled arc segments

[Link](xy, start, end,Pie/wedge shape from ellipse centre


fill, ...) Pie charts

Filled
[Link](xy, fill, outline, polygon from list of vertices
width) Triangles, stars, custom shapes

Regular
draw.regular_polygon(bc, n, rot, ...)N-sided polygon inscribed in circle Triangles, pentagons, hexagons

— 29 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Function / Method Description Typical Use Case

[Link](xy, fill) Single pixel or list of pixels Scatter plots, dot markers

Draw...)
[Link](xy, text, fill, font, text string at position Labels, captions, titles

draw.multiline_text(xy, text,Draw
...)text with newline support Paragraphs, multi-line labels

[Link](xy, text, font,Returns


...) bounding box WITHOUT drawing Measuring text before drawing

Returns width of a single text line


[Link](text, font, ...) Centering, wrapping calculations

[Link](xy, bitmap, fill)Paste a 1-bit bitmap at position Custom icons, custom glyphs

[Link](xy, value) Flood fill from (x,y) with value Filling enclosed regions

12.3 Lines
# Single straight line from (x1,y1) to (x2,y2) [Link]([(10, 10), (590, 390)],
fill='black', width=3) # Multiple connected line segments [Link]([(50,50), (200,100),
(350,50), (500,100)], fill='blue', width=2) # Horizontal rule [Link]([(0, 200),
([Link], 200)], fill='gray', width=1) # Diagonal cross [Link]([(0, 0),
([Link], [Link])], fill='red', width=2) [Link]([([Link], 0), (0,
[Link])], fill='red', width=2)

12.4 Rectangles
# Filled rectangle [Link]([50, 50, 250, 150], fill='skyblue') # Outline only (no
fill) [Link]([50, 50, 250, 150], outline='navy', width=3) # Filled with outline
[Link]([50, 50, 250, 150], fill='lightyellow', outline='goldenrod', width=2) #
Rounded rectangle (Pillow 8.2+) draw.rounded_rectangle([50, 50, 250, 150], radius=20,
fill='coral', outline='red', width=2) # Draw a grid for x in range(0, [Link], 50):
[Link]([(x, 0), (x, [Link])], fill='lightgray', width=1) for y in range(0,
[Link], 50): [Link]([(0, y), ([Link], y)], fill='lightgray', width=1)

12.5 Circles and Ellipses


# Ellipse defined by its bounding box [Link]([100, 100, 400, 300], fill='gold',
outline='orange', width=3) # Perfect circle (square bounding box) [Link]([150, 100,
350, 300], fill='skyblue') # 200×200 circle # Circle by centre + radius (Pillow 8.2+)
[Link]((300, 200), radius=80, fill='purple', outline='white', width=3) # Concentric
circles for r in range(10, 100, 10): [Link]((300, 200), radius=r, outline='navy',
width=1)

12.6 Polygons and Pie Charts


# Triangle [Link]([(300, 50), (500, 300), (100, 300)], fill='mediumorchid',
outline='purple', width=2) # 5-pointed star (using regular_polygon with rotation)
draw.regular_polygon(bounding_circle=(300, 200, 100), n_sides=5, rotation=90, fill='gold',
outline='goldenrod') # Hexagon draw.regular_polygon(bounding_circle=(300, 200, 100),
n_sides=6, fill='teal', outline='darkteal') # Pie chart (3 slices) [Link]([100, 50,
500, 450], start=0, end=120, fill='tomato') [Link]([100, 50, 500, 450], start=120,
end=240, fill='steelblue') [Link]([100, 50, 500, 450], start=240, end=360,
fill='seagreen')

— 30 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

13 The ImageFont Module and Text Rendering

Text rendering in Pillow combines two modules: ImageDraw provides the [Link]() methods, and
ImageFont provides the font objects. You can use a built-in bitmap font (no files needed) or load any
TrueType (.ttf) or OpenType (.otf) font file for professional typography.

13.1 ImageFont Function Reference


Function / Method Description Typical Use Case

ImageFont.load_default(size) Load built-in bitmap font Quick tests, no font file needed

Load TrueType/OpenType font from file


[Link](font, size) Professional text rendering

[Link](file) Load a .pil bitmap font file Legacy bitmap fonts

[Link](text) Get bounding box of text with this font Layout calculations

[Link](text) Get pixel width of text Text width measurement

[Link]() Get (ascent, descent) in pixels Line height calculations

[Link] Font size in points (attribute, not method) Checking loaded font size

13.2 Loading Fonts


from PIL import ImageFont # Built-in default font (no file needed) font_small =
ImageFont.load_default() font_larger = ImageFont.load_default(size=24) # Pillow 10+ only #
TrueType font from file path font = [Link]('[Link]', size=36) # Absolute
path examples # Windows: font = [Link]('C:/Windows/Fonts/[Link]', 36) #
macOS: font = [Link]('/Library/Fonts/[Link]', 36) # Linux: font =
[Link]('/usr/share/fonts/truetype/dejavu/[Link]', 36) # Load from
BytesIO (e.g. font downloaded from web) from io import BytesIO with open('[Link]',
'rb') as f: font = [Link](BytesIO([Link]()), 48) # Use different weights from
the same font family font_regular = [Link]('[Link]', 24) font_bold
= [Link]('[Link]', 24) font_italic =
[Link]('[Link]', 24)

■ Note: Common font locations: Windows → C:/Windows/Fonts/ | macOS → /Library/Fonts/ or


/System/Library/Fonts/ | Linux → /usr/share/fonts/ (install with: apt install fonts-dejavu)

13.3 Drawing Text


from PIL import Image, ImageDraw, ImageFont img = [Link]('RGB', (600, 300), 'white')
draw = [Link](img) font = [Link]('[Link]', 48) # Basic text
[Link]((20, 120), 'Hello, World!', fill='black', font=font) # Coloured text
[Link]((20, 120), 'Coloured!', fill=(220, 50, 50), font=font) [Link]((20, 120), 'Hex
colour', fill='#3498db', font=font) # Text with drop shadow (draw shadow first, then text)
[Link]((22, 122), 'Shadowed', fill=(180, 180, 180), font=font) [Link]((20, 120),
'Shadowed', fill='black', font=font) # Text with stroke (outline effect) [Link]((20,
120), 'Outlined', fill='white', font=font, stroke_width=3, stroke_fill='black') #
Semi-transparent text (requires RGBA image) img_rgba = [Link]('RGBA', (600, 300),
'white') draw2 = [Link](img_rgba) [Link]((20, 120), 'Faded', fill=(0, 0, 0,

— 31 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

100), font=font)

13.4 Measuring and Centring Text


Always measure text before positioning it. The textbbox() method returns the exact pixel bounding box
of the rendered text without actually drawing anything.

font = [Link]('[Link]', 48) text = 'Centred!' # Get bounding box #


textbbox((anchor_x, anchor_y), text, font) bbox = [Link]((0, 0), text, font=font)
text_w = bbox[2] - bbox[0] # right - left text_h = bbox[3] - bbox[1] # bottom - top #
Centre on the image x = ([Link] - text_w) // 2 y = ([Link] - text_h) // 2
[Link]((x, y), text, fill='navy', font=font) # Align right x_right = [Link] - text_w
- 20 [Link]((x_right, y), text, fill='navy', font=font) # Using anchor parameter
(Pillow 8+) # 'mm' = middle-middle centre point [Link](([Link]//2, [Link]//2),
text, fill='navy', font=font, anchor='mm') # Multiline centred text
draw.multiline_text(([Link]//2, [Link]//2), 'Line One\nLine Two\nLine Three',
fill='black', font=font, anchor='mm', align='center', spacing=8)

■ Tip: Use the anchor='mm' parameter to position text from its visual centre rather than its top-left corner.
Common anchor values: 'la'=left-ascender, 'mm'=centre, 'rs'=right-baseline, 'lt'=left-top, 'rb'=right-bottom.

— 32 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART IV

FILTERS AND IMAGE ENHANCEMENT

ImageFilter · ImageEnhance · ImageOps · ImageColor

15 The ImageFilter Module

The ImageFilter module provides a collection of convolution filters and rank filters for image processing.
A convolution filter works by sliding a small matrix (called a kernel) over every pixel in the image,
computing a weighted average of the pixel and its neighbours. Different kernel values produce different
effects — blurring, sharpening, edge detection, and more.

15.1 How Convolution Filters Work


To understand filters, imagine a 3×3 grid centred on each pixel. The filter's kernel defines the weight
given to each of those 9 positions. For a blur filter, all weights are equal (averaging neighbours). For a
sharpen filter, the centre weight is high and neighbours have negative weights (enhancing differences).

# Applying any filter is always the same one line: result = [Link]([Link]) #
Stacking multiple filters result =
[Link]([Link](3)).filter([Link])

15.2 Complete Filter Reference


Filter Constant Type Parameters Effect Description

[Link] Kernel None Simple 5×5 box blur. Uniformly softens the image.

[Link] Kernel None Traces edges and renders them as sketch-like lines on gray background.

[Link] Kernel None Enhances local contrast and fine texture detail slightly.

ImageFilter.EDGE_ENHANCE Kernel None Sharpens edges throughout the image at moderate strength.

Kernel
ImageFilter.EDGE_ENHANCE_MORE None Stronger version of EDGE_ENHANCE. Very pronounced edge sharpenin

[Link] Kernel None Produces a 3D embossed/relief effect. Bright=facing light, dark=shadow.

ImageFilter.FIND_EDGES Kernel None Edge detection. Bright lines on black where colour changes rapidly.

[Link] Kernel None Standard sharpening kernel. Enhances detail across the entire image.

[Link] Kernel None Gentle smoothing. Reduces noise while preserving edges lightly.

ImageFilter.SMOOTH_MORE Kernel None Stronger smoothing than SMOOTH. More blurring, more noise reduction.

[Link](r)Gaussian radius (float) High-quality bell-curve blur. More natural than box blur.

— 33 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Filter Constant Type Parameters Effect Description

[Link](r) Box radius (int) Fast averaging blur. Less natural than Gaussian but faster.

USM
[Link](r,p,t) radius,percent,threshold Professional sharpening technique. Best for final output sharpening.

[Link](s)Rank size (odd int) Replaces each pixel with median of neighbourhood. Excellent for noise.

[Link](s) Rank size (odd int) Replaces pixel with minimum in neighbourhood. Erodes bright areas.

[Link](s) Rank size (odd int) Replaces pixel with maximum in neighbourhood. Dilates bright areas.

[Link](s) Rank size (odd int) Replaces pixel with most common value. Smooths areas of uniform colou

Custom
[Link](sz,k,s,o) size,kernel,scale,offset Apply your own custom convolution kernel matrix.

15.3 Parametric Filters in Detail


GaussianBlur
from PIL import ImageFilter # GaussianBlur(radius) — higher radius = more blur slightly =
[Link]([Link](radius=1)) moderate =
[Link]([Link](radius=3)) heavy =
[Link]([Link](radius=8)) extreme =
[Link]([Link](radius=20)) # Common use: background blur while
keeping foreground sharp blurred_bg = [Link]([Link](radius=10)) #
Then paste the sharp subject back on top

UnsharpMask — Professional Sharpening


UnsharpMask is the industry-standard sharpening technique, used in professional photo editing.
Despite its name, it sharpens images. It works by subtracting a blurred version of the image from itself,
which enhances edges.

# UnsharpMask(radius, percent, threshold) # radius: how wide the sharpening halo is (1-3
typical) # percent: sharpening strength (100-200 typical) # threshold: minimum contrast to
sharpen (0-10, prevents noise sharpening) subtle =
[Link]([Link](radius=1, percent=100, threshold=3)) normal =
[Link]([Link](radius=2, percent=150, threshold=3)) strong =
[Link]([Link](radius=2, percent=250, threshold=2)) crisp =
[Link]([Link](radius=0.5, percent=200, threshold=0))

Custom Kernel Filters


# Custom 3x3 sharpen kernel custom_sharpen = [Link]( size=(3, 3), kernel=[ 0,
-1, 0, -1, 5, -1, 0, -1, 0], scale=1, offset=0 ) sharpened = [Link](custom_sharpen) #
Edge detection (Laplacian kernel) laplacian = [Link]( size=(3, 3), kernel=[-1,
-1, -1, -1, 8, -1, -1, -1, -1], scale=1, offset=128 # offset 128 so edges appear as gray,
not black ) edges = [Link](laplacian)

— 34 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

16 The ImageEnhance Module

The ImageEnhance module provides four classes for adjusting visual properties of images with precise
numerical control. Each class has a single method: enhance(factor). A factor of 1.0 always returns
the original unchanged.

Function / Method Description Typical Use Case

[Link] Overall luminance / exposure Fixing under/over-exposed photos

[Link] Difference between light and dark Punchy vs flat/faded look

[Link] Colour saturation level Vivid vs desaturated/grayscale

[Link] Edge clarity and fine detail Crispness vs softness

Class Factor 0.0 Factor 0.5 Factor 1.0 Factor 1.5 Factor 2.0

Brightness Pure black 50% darker Original 50% brighter Twice as bright

Contrast Solid grey Low contrast Original More contrast Very high contrast

Color Grayscale Muted tones Original More vivid Very saturated

Sharpness Very blurry Soft Original Sharpened Over-sharpened

from PIL import ImageEnhance # Brightness enhancer = [Link](img) result =


[Link](1.4) # 40% brighter # Contrast result =
[Link](img).enhance(1.6) # Colour / Saturation result =
[Link](img).enhance(1.8) # vivid result = [Link](img).enhance(0.0)
# grayscale # Sharpness result = [Link](img).enhance(2.0) # Chain multiple
enhancements (professional photo auto-correction) def auto_correct(img, brightness=1.1,
contrast=1.3, saturation=1.2, sharpness=1.4): img =
[Link](img).enhance(brightness) img =
[Link](img).enhance(contrast) img =
[Link](img).enhance(saturation) img =
[Link](img).enhance(sharpness) return img corrected =
auto_correct([Link]('[Link]'))

— 35 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

17 The ImageOps Module

The ImageOps module provides ready-made, single-call operations that would otherwise require
combining multiple lower-level steps. Think of it as a toolkit of the most common photo-editing and
layout operations.

Function / Method Description Typical Use Case

[Link](img) Convert to grayscale (returns RGB not L) Desaturating photos

[Link](img) Invert all pixel values (photographic negative) Negative effects

[Link](img) Flip vertically (top to bottom) Upside-down correction

[Link](img) Flip horizontally (left to right) Mirror images

[Link](img) Stretch histogram to full 0-255 range Fix washed-out images

[Link](img) Histogram equalisation for even distribution Fix uneven exposure

[Link](img, bits)Reduce colour depth (bits per channel) Poster / graphic art effect

[Link](img, thr) Invert pixels above threshold Psychedelic / artistic effect

Colourize grayscale image


[Link](img,blk,wht) Sepia, duotone effects

[Link](img, size) Resize to fit inside size, add padding Letterbox / pillarbox

[Link](img, size) Resize to fill size exactly, crop excess Social media thumbnails

[Link](img, size) Resize to fit inside size, no crop Email-safe resizing

[Link](img, border) Remove border pixels from all 4 sides Trimming whitespace

[Link](img, factor) Scale by float factor (e.g. 0.5 = half size) Quick proportional scaling

ImageOps.exif_transpose(img) Auto-rotate based on EXIF orientation tag Fix sideways phone photos

[Link](img, border) Add a border/padding around image Adding picture frame

from PIL import ImageOps # Fix phone photo orientation fixed =


ImageOps.exif_transpose(img) # Add a 20px white frame framed = [Link](img,
border=20, fill='white') # Auto-fix washed-out image (cuts darkest/lightest 2%) auto =
[Link](img, cutoff=2) # Sepia tone effect gray = [Link]('L') sepia =
[Link](gray, black='#704214', white='#C8A96E') # Fit to 1080x1080 (Instagram
square) — crop to fill sq = [Link](img, (1080, 1080)) # Letterbox to 1920x1080
(YouTube thumbnail) — add black bars youtube = [Link](img, (1920, 1080),
color='black') # Poster effect poster = [Link](img, bits=2) # only 4 colours
per channel

— 36 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

18 The ImageColor Module

The ImageColor module provides utilities for parsing colour strings into pixel values. You rarely need to
import it directly — Pillow calls it internally when you pass a string colour to fill or outline parameters.
But it is useful when you need to convert colour names to RGB tuples in your own code.

from PIL import ImageColor # Parse colour names [Link]('red') # (255, 0, 0)


[Link]('white') # (255, 255, 255) [Link]('black') # (0, 0, 0)
[Link]('transparent') # (0, 0, 0, 0) # Parse hex codes
[Link]('#ff6347') # (255, 99, 71) tomato [Link]('#fff') # (255, 255,
255) short hex # Parse HSL [Link]('hsl(0, 100%, 50%)') # (255, 0, 0) red
[Link]('hsl(120, 100%, 50%)') # (0, 255, 0) green # Parse HSB/HSV
[Link]('hsv(0, 100%, 100%)') # (255, 0, 0) # 140+ CSS colour names are
supported: # 'tomato', 'coral', 'skyblue', 'mediumorchid', 'goldenrod', 'limegreen'... #
getcolor() respects the image mode (returns correct tuple type) [Link]('red',
'RGB') # (255, 0, 0) [Link]('red', 'RGBA') # (255, 0, 0, 255)
[Link]('red', 'L') # 76 (luminance)

— 37 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART V

ADVANCED IMAGE PROCESSING

ImageStat · Alpha Compositing · ImageCms · ImageSequence · ImageGrab · Advanced Pixels

19 Image Statistics and Histograms

The ImageStat module computes statistical properties of an image's pixel distribution. This is useful for
determining if an image is over- or under-exposed, comparing images, detecting anomalies, and making
data-driven processing decisions.

Function / Method Description Typical Use Case

[Link] list of (min,max) tuples Min and max pixel value per channel

[Link] list of ints Total number of pixels counted per channel

[Link] list of floats Sum of all pixel values per channel

stat.sum2 list of floats Sum of squared pixel values (for variance)

[Link] list of floats Average (mean) pixel value per channel

[Link] list of floats Median pixel value per channel

[Link] list of floats Root mean squared pixel value per channel

[Link] list of floats Variance of pixel values per channel

[Link] list of floats Standard deviation per channel

from PIL import Image, ImageStat img = [Link]('[Link]').convert('RGB') stat =


[Link](img) # Channel means (average brightness per channel) r_mean, g_mean,
b_mean = [Link] print(f'R mean: {r_mean:.1f}, G mean: {g_mean:.1f}, B mean:
{b_mean:.1f}') # Output: R mean: 142.3, G mean: 128.7, B mean: 105.2 # Overall brightness
(average of all channels) brightness = sum([Link]) / len([Link]) print(f'Average
brightness: {brightness:.1f} / 255') # Check if image is too dark or too bright if
brightness < 80: print('Image is underexposed') elif brightness > 200: print('Image is
overexposed') else: print('Image exposure is good') # Standard deviation (high = high
contrast, low = flat/dull) contrast = sum([Link]) / len([Link]) print(f'Contrast
score: {contrast:.1f}') # high = contrasty, low = flat # Min and max values for i, (mn, mx)
in enumerate([Link]): print(f'Channel {i}: min={mn}, max={mx}') # Statistics on a
masked region mask = [Link]('L', [Link], 0) # ... draw white on mask for region of
interest ... stat_region = [Link](img, mask=mask)

19.2 Histogram Analysis

— 38 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

A histogram shows the distribution of pixel values across all 256 levels for each channel. It is the
fundamental tool for understanding an image's exposure and tonal range.

# [Link]() returns a list of 256 * num_channels values hist = [Link]() # 768


values for RGB (256 per channel) # Split by channel r_hist = hist[0:256] g_hist =
hist[256:512] b_hist = hist[512:768] # Find most common brightness level most_common_r =
r_hist.index(max(r_hist)) print(f'Most common red value: {most_common_r}') # Check if
image is clipped (pixels at 0 or 255) clipped_dark = sum(1 for h in
[r_hist[0],g_hist[0],b_hist[0]] if h > 1000) clipped_light = sum(1 for h in
[r_hist[255],g_hist[255],b_hist[255]] if h > 1000) if clipped_dark: print('Warning: shadow
clipping detected') if clipped_light: print('Warning: highlight clipping detected') # Plot
histogram using matplotlib import [Link] as plt [Link](figsize=(10, 4)) for
channel, color in zip([r_hist, g_hist, b_hist], ['red', 'green', 'blue']):
[Link](channel, color=color, alpha=0.7) [Link]('RGB Histogram') [Link]('Pixel
Value (0-255)') [Link]('Pixel Count') [Link]('[Link]')

— 39 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

20 Alpha Compositing and Masking

Alpha compositing is the process of combining images using transparency information. It is fundamental
to creating professional overlays, watermarks, UI elements, and multi-layer image compositions.
Understanding the different compositing methods and when to use each one is an important skill.

20.1 The Four Compositing Methods Compared


Method Modifies? Requires RGBA? Alpha Aware? Best For

paste(im, box, mask) In-place No (mask optional) Yes (with mask) Overlaying any image with optional transparency

alpha_composite(dst, src) Returns new Yes — both RGBA Yes — full Correct layer compositing, UI elements

blend(im1, im2, alpha) Returns new No — same mode No Cross-fade / dissolve transitions

composite(im1, im2, mask) Returns new No Via mask Masked blending, custom alpha shapes

20.2 paste() with Mask


from PIL import Image # Paste a PNG logo with transparency onto a background bg =
[Link]('[Link]').convert('RGBA') logo = [Link]('[Link]').convert('RGBA')
# Position logo at bottom-right corner x = [Link] - [Link] - 20 y = [Link] -
[Link] - 20 # Use alpha channel as paste mask [Link](logo, (x, y), mask=logo) #
mask=logo uses logo's alpha # OR explicitly: [Link](logo, (x, y), mask=[Link]()[3])
[Link]('[Link]')

20.3 alpha_composite() — The Right Way


# Both images MUST be RGBA and the SAME SIZE for alpha_composite() bg = [Link]('RGBA',
(800, 600), (255, 255, 255, 255)) top = [Link]('[Link]').convert('RGBA') # Resize
overlay if needed if [Link] != [Link]: top = [Link]([Link], [Link]) #
Composite: implements Porter-Duff 'over' operation result = Image.alpha_composite(bg, top)
[Link]('[Link]') # Layer multiple images def layer_images(base, *layers):
result = [Link]('RGBA') for layer in layers: layer = [Link]('RGBA') if
[Link] != [Link]: layer = [Link]([Link], [Link]) result =
Image.alpha_composite(result, layer) return result

20.4 Creative Masking


from PIL import Image, ImageDraw # Create a circular mask img =
[Link]('[Link]').convert('RGBA') mask = [Link]('L', [Link], 0) # black
(transparent) draw = [Link](mask) # Draw white circle in the centre cx, cy =
[Link]//2, [Link]//2 r = min(cx, cy) - 10 [Link]([cx-r, cy-r, cx+r, cy+r],
fill=255) # Apply mask (circular crop) [Link](mask) [Link]('circular_crop.png') #
Gradient fade (transparent on right) fade_mask = [Link]('L', [Link], 0) import numpy
as np arr = [Link](255, 0, [Link], dtype=np.uint8) arr = [Link](arr, ([Link],
1)) fade_mask = [Link](arr) [Link](fade_mask) [Link]('faded_right.png')

— 40 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

21 Colour Management with ImageCms

The ImageCms module provides colour management features using ICC (International Color
Consortium) profiles. Colour management ensures that colours look consistent across different devices
— cameras, monitors, and printers. For web work this is usually not needed, but for print production it is
essential.

Function / Method Description Typical Use Case

[Link](name) Create a built-in colour profile Creating sRGB profile

[Link](path)Load ICC profile from .icc file Loading device profiles

Build a colour space transform


[Link](src,dst,im,om) RGB→CMYK conversion

[Link](img, Apply a colour transform to image


transform) Converting colour spaces

One-shot profile conversion


[Link](img,s,d) Quick colour conversion

Read profile description text


[Link](p) Identifying profiles

[Link](p) Get default rendering intent Print workflow

from PIL import Image, ImageCms # Convert from sRGB to AdobeRGB img =
[Link]('[Link]').convert('RGB') srgb_profile = [Link]('sRGB') #
adobe_profile = [Link]('[Link]') # Build a reusable transform
transform = [Link]( srgb_profile, srgb_profile, 'RGB', 'RGB' ) converted
= [Link](img, transform) # Read embedded ICC profile from JPEG profile =
[Link]('icc_profile') if profile: cms_profile =
[Link]([Link](profile)) desc =
[Link](cms_profile) print(f'Embedded profile: {desc}')

— 41 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

22 Animated Images and ImageSequence

The ImageSequence module provides tools for working with multi-frame images such as animated
GIFs and multi-page TIFFs. Each frame is a regular Image object that can be processed individually.

from PIL import Image, ImageSequence # --- READING an animated GIF --- gif =
[Link]('[Link]') print(f'Total frames: {gif.n_frames}') # Method 1:
[Link] for i, frame in enumerate([Link](gif)):
[Link](f'frame_{i:03d}.png') # Method 2: Manual seek for i in range(gif.n_frames):
[Link](i) # [Link]() returns current frame index duration = [Link]('duration',
100) # ms per frame print(f'Frame {i}: {[Link]}, {duration}ms') frame_copy =
[Link]().convert('RGB') # copy before seeking away frame_copy.save(f'frame_{i}.jpg') #
--- CREATING an animated GIF --- frames = [] for i in range(36): frame = [Link]('RGB',
(200, 200), 'white') from PIL import ImageDraw draw = [Link](frame) # Draw rotating
line import math angle = [Link](i * 10) cx, cy = 100, 100 [Link]([(cx, cy), (cx +
80*[Link](angle), cy + 80*[Link](angle))], fill='navy', width=3) [Link](frame)
# Save as animated GIF frames[0].save('[Link]', save_all=True,
append_images=frames[1:], loop=0, # 0 = loop forever duration=50, # 50ms per frame = 20fps
optimize=True)

— 42 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

23 Screen Capture with ImageGrab

The ImageGrab module can take screenshots of the screen or a specific region. It returns a regular PIL
Image object, so all standard Pillow operations can be applied immediately. ImageGrab is available on
Windows and macOS natively. On Linux it requires an additional dependency.

from PIL import ImageGrab # Capture the entire screen screenshot = [Link]()
[Link]('[Link]') # Capture a specific region: (left, upper, right, lower)
region = [Link](bbox=(100, 100, 800, 600)) [Link]('[Link]') # On Linux —
requires: pip install mss # from mss import mss # with mss() as sct: # screenshot =
[Link](output='[Link]') # Grab from all monitors (include_layered_windows=False,
all_screens=True) all_screens = [Link](all_screens=True) # Continuous screen
recording — save a frame every second import time for i in range(10): frame =
[Link]() [Link](f'screen_{i:03d}.png') [Link](1.0)

■ Note: ImageGrab requires the display/screen to be accessible. It will not work in headless server
environments. For Linux, install with: pip install Pillow[extras]

— 43 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART VI

AUTOMATION AND BATCH PROCESSING

Batch Processing · Performance · Integration with NumPy, OpenCV, Django

25 Batch Processing Thousands of Images

One of Pillow's most powerful real-world uses is batch processing — automatically performing the same
operation on hundreds or thousands of images. This is essential for web development, data science,
photography workflows, and e-commerce.

25.1 Basic Batch Pattern


from PIL import Image, ImageOps import os from pathlib import Path def
batch_process(src_dir, dst_dir, operation): """ Apply operation() to every image in
src_dir and save to dst_dir. operation: callable that takes an Image and returns an Image
""" src = Path(src_dir) dst = Path(dst_dir) [Link](parents=True, exist_ok=True)
supported = {'.jpg', '.jpeg', '.png', '.webp', '.bmp', '.tiff'} files = [f for f in
[Link]() if [Link]() in supported] print(f'Processing {len(files)}
images...') errors = 0 for i, path in enumerate(files, 1): try: img = [Link](path) img
= ImageOps.exif_transpose(img) # fix phone rotation result = operation(img) out = dst /
[Link] [Link](out) print(f'[{i}/{len(files)}] {[Link]}', end='\r') except
Exception as e: print(f'ERROR on {[Link]}: {e}') errors += 1 print(f'\nDone.
{len(files)-errors} succeeded, {errors} failed.') # Example usage batch_process('input/',
'output/', lambda img: [Link]('RGB').resize((1200, 900), [Link]))

25.2 Parallel Batch Processing


For large batches, parallel processing can dramatically reduce processing time. Python's
multiprocessing module distributes work across CPU cores.

from PIL import Image, ImageOps from pathlib import Path from [Link] import
ProcessPoolExecutor, as_completed import os def process_one(args): src_path, dst_path =
args try: img = [Link](src_path) img = ImageOps.exif_transpose(img)
[Link]((800, 800), [Link]) [Link]('RGB').save(dst_path, quality=85,
optimize=True) return src_path.name, True, None except Exception as e: return
src_path.name, False, str(e) def parallel_batch(src_dir, dst_dir, max_workers=None): src =
Path(src_dir) dst = Path(dst_dir) [Link](parents=True, exist_ok=True) if max_workers is
None: max_workers = os.cpu_count() # use all CPU cores files = list([Link]('*.jpg')) +
list([Link]('*.png')) jobs = [(f, dst / [Link] + '.jpg') for f in files] with
ProcessPoolExecutor(max_workers=max_workers) as ex: futures = {[Link](process_one, j):
j for j in jobs} done = 0 for fut in as_completed(futures): name, ok, err = [Link]()
done += 1 status = 'OK' if ok else f'FAIL: {err}' print(f'[{done}/{len(jobs)}] {name}:
{status}') parallel_batch('photos/', 'thumbnails/')

— 44 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

26 Performance Optimisation

When processing large images or large numbers of images, performance matters. Understanding how
Pillow handles memory and computation allows you to write significantly faster code.

26.1 Memory-Efficient Processing


from PIL import Image # BAD: Loads entire image then resizes (uses full memory) img =
[Link]('huge_image.jpg') # 50MB in memory img = [Link]((800, 600)) # GOOD: Use
draft() to tell JPEG decoder to decode at smaller size # Only works for JPEG. Reduces
memory during decoding. img = [Link]('huge_image.jpg') [Link]('RGB', (800, 600)) #
tell decoder: only decode up to this size [Link]() # now triggers the reduced decode img
= [Link]((800, 600), [Link]) # final precise resize # GOOD: Process in chunks
for very large images def process_large(path, chunk_height=500): img = [Link](path)
chunks = [] for y in range(0, [Link], chunk_height): chunk = [Link]((0, y, [Link],
min(y+chunk_height, [Link]))) # process chunk here... [Link](chunk) #
reassemble result = [Link]([Link], [Link]) y = 0 for chunk in chunks:
[Link](chunk, (0, y)) y += [Link] return result

26.2 Performance Tips Summary


Technique Speed Gain Notes

Use [Link] for non-final resizes 4–10× Good for previews; bad for final output

Use [Link]() for JPEG thumbnails 3–5× JPEG only. Reduces decode memory too.

Use point() instead of pixel loops 50–200× Built-in C-speed LUT application

Use NumPy for mathematical ops 100–500× Best for per-pixel math

Parallel processing (multiprocessing) N× CPUs Great for batch jobs. 8 cores = ~8× faster

Save JPEG with optimize=True 10–30% smaller Slower save but smaller files

Use WebP instead of JPEG/PNG 25–50% smaller Modern format, great browser support

Open images lazily, process on-demand Low RAM Don't load all images at once

— 45 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART VII

REAL WORLD PROJECTS

Batch Resizer · Watermark · Collage · Thumbnail · Meme Creator · ML Dataset Tool

28 Project: Batch Image Resizer

This project creates a professional command-line batch image resizer. It scans a folder for images, fixes
EXIF rotation, resizes to a specified maximum dimension while preserving aspect ratio, and saves the
results with detailed logging.

"""batch_resize.py — Professional batch image resizer""" from PIL import Image, ImageOps
from pathlib import Path import argparse import logging import time [Link](
level=[Link], format='%(asctime)s [%(levelname)s] %(message)s', datefmt='%H:%M:%S' )
log = [Link](__name__) SUPPORTED = {'.jpg', '.jpeg', '.png', '.webp', '.bmp',
'.tiff', '.gif'} def resize_image(src: Path, dst: Path, max_size: int, quality: int = 85,
fmt: str = None) -> dict: """Resize a single image. Returns a stats dict.""" t0 =
[Link]() img = [Link](src) img = ImageOps.exif_transpose(img) # fix phone rotation
orig_size = [Link] orig_mode = [Link] # Resize to fit within max_size × max_size
[Link]((max_size, max_size), [Link]) # Determine output format out_fmt = fmt
or [Link]('.').upper() if out_fmt in ('JPG',): out_fmt = 'JPEG' # Ensure
correct mode for format if out_fmt == 'JPEG' and [Link] in ('RGBA', 'P'): bg =
[Link]('RGB', [Link], 'white') if [Link] == 'P': img = [Link]('RGBA')
[Link](img, mask=[Link]()[3] if [Link]=='RGBA' else None) img = bg out_path = dst /
([Link] + f'.{out_fmt.lower()}') save_kwargs = {'quality': quality, 'optimize': True} if
out_fmt == 'JPEG' else {} [Link](out_path, format=out_fmt, **save_kwargs) return {
'src': [Link], 'orig': orig_size, 'new': [Link], 'mode': orig_mode, 'time':
[Link]() - t0, 'src_bytes': [Link]().st_size, 'dst_bytes': out_path.stat().st_size, }
def batch_resize(src_dir, dst_dir, max_size=1920, quality=85, fmt=None): src =
Path(src_dir) dst = Path(dst_dir) [Link](parents=True, exist_ok=True) files = [f for f
in [Link]() if [Link]() in SUPPORTED] [Link](f'Found {len(files)} images in
{src}') total_saved = 0 errors = 0 for i, path in enumerate(files, 1): try: stats =
resize_image(path, dst, max_size, quality, fmt) saved = stats['src_bytes'] -
stats['dst_bytes'] total_saved += saved [Link](f'[{i}/{len(files)}] {stats["src"]:30s} '
f'{stats["orig"]} → {stats["new"]} ' f'({stats["time"]*1000:.0f}ms, {saved//1024}KB
saved)') except Exception as e: [Link](f'[{i}/{len(files)}] FAILED {[Link]}: {e}')
errors += 1 [Link](f'Complete. {len(files)-errors} OK, {errors} failed. ' f'Total saved:
{total_saved//1024//1024:.1f} MB') # --- CLI interface --- if __name__ == '__main__': p =
[Link](description='Batch Image Resizer') p.add_argument('src',
help='Source folder') p.add_argument('dst', help='Output folder') p.add_argument('--size',
type=int, default=1920, help='Max dimension (default: 1920)') p.add_argument('--quality',
type=int, default=85, help='JPEG quality (default: 85)') p.add_argument('--format',
default=None, help='Output format (JPEG/PNG/WEBP etc)') args = p.parse_args()
batch_resize([Link], [Link], [Link], [Link], [Link]) # Run with: #
python batch_resize.py photos/ resized/ --size 1200 --quality 90 --format JPEG

— 46 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

29 Project: Watermark Generator

This project creates a flexible watermarking tool that can add text or image watermarks to photos, with
configurable position, opacity, size, and batch mode.

"""[Link] — Professional watermark generator""" from PIL import Image, ImageDraw,


ImageFont, ImageOps from pathlib import Path def add_text_watermark(img: [Link],
text: str, position: str = 'bottom-right', opacity: int = 160, font_ratio: float = 0.05) ->
[Link]: """ Add a semi-transparent text watermark to an image. position: 'top-left',
'top-right', 'bottom-left', 'bottom-right', 'centre' opacity: 0 (invisible) to 255 (fully
opaque) font_ratio: font size as fraction of image width """ img = [Link]('RGBA')
layer = [Link]('RGBA', [Link], (0, 0, 0, 0)) draw = [Link](layer) font_size =
max(12, int([Link] * font_ratio)) try: font = [Link]('[Link]',
font_size) except OSError: font = ImageFont.load_default(font_size) # Measure text bbox =
[Link]((0, 0), text, font=font) tw, th = bbox[2]-bbox[0], bbox[3]-bbox[1] margin =
20 # Calculate position positions = { 'top-left': (margin, margin), 'top-right':
([Link] - tw - margin, margin), 'bottom-left': (margin, [Link] - th - margin),
'bottom-right': ([Link] - tw - margin, [Link] - th - margin), 'centre': (([Link]
- tw)//2, ([Link] - th)//2), } x, y = [Link](position,
positions['bottom-right']) # Draw drop shadow then text [Link]((x+2, y+2), text,
fill=(0, 0, 0, opacity//2), font=font) [Link]((x, y ), text, fill=(255, 255, 255,
opacity), font=font) return Image.alpha_composite(img, layer) def add_image_watermark(img:
[Link], watermark_path: str, position: str = 'bottom-right', scale: float = 0.15,
opacity: int = 180) -> [Link]: """Add an image (logo) watermark.""" img =
[Link]('RGBA') wm = [Link](watermark_path).convert('RGBA') # Scale watermark
new_w = int([Link] * scale) ratio = new_w / [Link] new_h = int([Link] * ratio) wm =
[Link]((new_w, new_h), [Link]) # Adjust opacity r, g, b, a = [Link]() a =
[Link](lambda p: int(p * opacity / 255)) [Link](a) # Position margin = 15 if 'right'
in position: x = [Link] - [Link] - margin else: x = margin if 'bottom' in position: y
= [Link] - [Link] - margin else: y = margin layer = [Link]('RGBA', [Link], (0,
0, 0, 0)) [Link](wm, (x, y), wm) return Image.alpha_composite(img, layer) def
batch_watermark(src_dir, dst_dir, text='© My Brand', position='bottom-right',
opacity=160): src = Path(src_dir) dst = Path(dst_dir) [Link](parents=True,
exist_ok=True) exts = {'.jpg', '.jpeg', '.png', '.webp'} files = [f for f in [Link]()
if [Link]() in exts] for f in files: img = [Link](f) img =
ImageOps.exif_transpose(img) result = add_text_watermark(img, text, position, opacity) out
= dst / [Link] [Link]('RGB').save(out, quality=92) print(f'Watermarked: {[Link]}')
# Example usage: # batch_watermark('photos/', 'watermarked/', '© 2024 John Doe')

— 47 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

32 Project: Meme Creator

"""meme_creator.py — Classic impact-font meme generator""" from PIL import Image,


ImageDraw, ImageFont, ImageOps from pathlib import Path import textwrap def
make_meme(image_path: str, top_text: str = '', bottom_text: str = '', output_path: str =
'[Link]', font_size_ratio: float = 0.08, max_chars_per_line: int = 28) -> None: """
Create a classic meme with top and bottom Impact text. Supports multi-line text with word
wrapping. """ img = [Link](image_path).convert('RGB') img =
ImageOps.exif_transpose(img) draw = [Link](img) font_size = max(24, int([Link]
* font_size_ratio)) # Try to load Impact font, fall back to bold default try: font =
[Link]('[Link]', font_size) except OSError: try: font =
[Link]('/usr/share/fonts/truetype/msttcorefonts/[Link]', font_size) except
OSError: font = ImageFont.load_default(font_size) def draw_text_block(text: str, vertical:
str): """Draw wrapped text at top or bottom of image.""" if not [Link](): return # Wrap
long lines lines = [Link]([Link](), width=max_chars_per_line) margin =
int(font_size * 0.3) lh = font_size + margin # line height total_h = lh * len(lines) if
vertical == 'top': start_y = margin * 2 else: start_y = [Link] - total_h - margin * 2
for i, line in enumerate(lines): bbox = [Link]((0, 0), line, font=font) tw = bbox[2]
- bbox[0] x = ([Link] - tw) // 2 y = start_y + i * lh # Draw thick black outline for dx
in range(-3, 4): for dy in range(-3, 4): if dx != 0 or dy != 0: [Link]((x+dx, y+dy),
line, fill='black', font=font) # Draw white fill [Link]((x, y), line, fill='white',
font=font) draw_text_block(top_text, 'top') draw_text_block(bottom_text, 'bottom')
[Link](output_path, quality=92) print(f'Meme saved: {output_path}') # Usage: make_meme(
'[Link]', top_text='WHEN YOU DISCOVER PILLOW', bottom_text='AND IT DOES EVERYTHING',
output_path='pillow_meme.jpg' )

— 48 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

33 Project: ML Dataset Preprocessing Tool

Machine learning models require images to be a consistent size, mode, and format. This tool
preprocesses an entire image dataset for training, with augmentation options.

"""ml_preprocess.py — Image dataset preprocessing for machine learning""" from PIL import
Image, ImageOps, ImageEnhance, ImageFilter import random from pathlib import Path def
preprocess_for_ml( img: [Link], target_size: tuple = (224, 224), mode: str = 'RGB',
normalize: bool = False # returns float array if True ) -> [Link]: """ Standard
preprocessing for ML: resize, mode convert, centre-crop. Equivalent to torchvision
[Link]([ [Link](256), [Link](224) ]) """ img =
ImageOps.exif_transpose(img) img = [Link](mode) # Step 1: Resize shortest side to
target w, h = [Link] tw, th = target_size scale = max(tw/w, th/h) img =
[Link]((int(w*scale), int(h*scale)), [Link]) # Step 2: Centre crop to exact
target img = [Link](img, target_size) return img def augment( img: [Link],
flip_prob: float = 0.5, rotate_range: float = 15.0, brightness_range: tuple = (0.8, 1.2),
contrast_range: tuple = (0.8, 1.2), blur_prob: float = 0.1, ) -> [Link]: """ Random
data augmentation to increase dataset diversity. """ # Random horizontal flip if
[Link]() < flip_prob: img = [Link](Image.FLIP_LEFT_RIGHT) # Random rotation
angle = [Link](-rotate_range, rotate_range) if abs(angle) > 0.5: img =
[Link](angle, resample=[Link], fillcolor=0) # Random brightness bf =
[Link](*brightness_range) img = [Link](img).enhance(bf) # Random
contrast cf = [Link](*contrast_range) img = [Link](img).enhance(cf)
# Occasional blur (simulates camera blur/motion) if [Link]() < blur_prob: img =
[Link]([Link](radius=0.8)) return img def prepare_dataset( src_dir:
str, dst_dir: str, target_size: tuple = (224, 224), augment_n: int = 3, # extra augmented
copies per image fmt: str = 'PNG', ): src = Path(src_dir) dst = Path(dst_dir)
[Link](parents=True, exist_ok=True) exts = {'.jpg', '.jpeg', '.png', '.webp', '.bmp'}
files = [f for f in [Link]('*') if [Link]() in exts] total = 0 for path in
files: try: img = [Link](path).convert('RGB') img = preprocess_for_ml(img,
target_size) # Save original preprocessed [Link](dst /
f'{[Link]}_orig.{[Link]()}') total += 1 # Save augmented copies for i in
range(augment_n): aug = augment(img) [Link](dst / f'{[Link]}_aug{i}.{[Link]()}')
total += 1 except Exception as e: print(f'ERROR: {[Link]}: {e}') print(f'Dataset ready:
{total} images in {dst}') # prepare_dataset('raw_dataset/', 'processed/',
target_size=(224,224), augment_n=4)

— 49 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

PART VIII

FULL API REFERENCE AND APPENDICES

Common Errors · Troubleshooting · Complete Glossary

35 Common Errors and Troubleshooting

This chapter documents every common Pillow error, its cause, and its solution. Understanding these
errors will help you debug your programs quickly.

■ Error: FileNotFoundError: [Errno 2] No such file or directory: '[Link]'

Cause: The file path you passed to [Link]() does not exist.

Solutions:
1. Check the file path for typos (including upper/lower case differences on Linux)
2. Print the absolute path: print(Path('[Link]').resolve())
3. Check that your working directory is what you expect: import os; print([Link]())
4. Use [Link]() to check before opening: if Path('[Link]').exists(): ...

■ Error: [Link]: cannot identify image file '[Link]'

Cause: The file exists but Pillow cannot recognise it as an image. Either it is not an image file, or it is corrupted.

Solutions:
1. Verify the file is actually an image: open it in an image viewer
2. Check the file extension — a file called .jpg might actually be a PDF or text file
3. Use [Link]() right after opening to check integrity
4. Check if the file downloaded completely (partial downloads are common)

■ Error: OSError: cannot write mode RGBA as JPEG

Cause: You are trying to save an RGBA (transparent) image as JPEG. JPEG does not support transparency.

Solutions:
1. Convert to RGB first: [Link]('RGB').save('[Link]')
2. If you want to preserve transparency, save as PNG instead: [Link]('[Link]')
3. If you want a specific background for transparent areas: bg = [Link]('RGB', [Link],
'white'); [Link](img, mask=[Link]()[3]); [Link]('[Link]')

■ Error: OSError: cannot write mode P as JPEG

Cause: Palette ('P' mode) images cannot be saved as JPEG.

— 50 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Solutions:
1. Convert: [Link]('RGB').save('[Link]')
2. GIF images are P mode — always convert before saving as JPEG

■ Error: ValueError: image has wrong mode

Cause: You are passing an image with the wrong mode to a function that requires a specific mode.

Solutions:
1. Check [Link] to see the current mode
2. Convert to the required mode: img = [Link]('RGB')
3. Read the function documentation to see which modes are accepted

■ Error: OSError: image file is truncated (X bytes not processed)

Cause: The image file is incomplete — it was not fully downloaded or is corrupted.

Solutions:
1. Download the file again
2. Add: from PIL import ImageFile; ImageFile.LOAD_TRUNCATED_IMAGES = True (use with
caution — may cause garbled images)

■ Error: IOError: encoder error -2 when writing image file

Cause: Usually means you are trying to save to a path you don't have write permission for, or the disk is full.

Solutions:
1. Check that the output directory exists: [Link](dst_dir, exist_ok=True)
2. Check disk space
3. Check file/directory permissions

■ Error: AttributeError: 'JpegImageFile' object has no attribute 'n_frames'

Cause: n_frames attribute does not exist for still images (only multi-frame formats like GIF/TIFF).

Solutions:
1. Use: n = getattr(img, 'n_frames', 1) to safely default to 1 for still images
2. Check: hasattr(img, 'n_frames') before accessing it

■ Error: OSError: cannot open resource (font file)

Cause: [Link]() cannot find the font file.

Solutions:
1. Use the full absolute path to the font file
2. Check font file locations: Windows: C:/Windows/Fonts/, macOS: /Library/Fonts/, Linux:
/usr/share/fonts/
3. Fall back to default: try: font = [Link]('[Link]', 36) except OSError: font =
ImageFont.load_default(36)
4. Install common fonts on Linux: apt install fonts-dejavu fonts-liberation

— 51 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

■ Error: MemoryError when processing large image

Cause: The image is too large to fit in available RAM.

Solutions:
1. Use [Link]() for JPEG to resize during decode
2. Process image in strips/chunks
3. Reduce image size before processing
4. Use 64-bit Python (can address more memory)

— 52 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

36 Glossary

This glossary explains all important terms, abbreviations, and concepts used in image processing and in
this book.

Alpha / Alpha Channel


An additional colour channel (beyond R, G, B) that stores transparency information. Alpha 0 = fully
transparent. Alpha 255 = fully opaque. Images with alpha channels are stored in RGBA mode. Only
formats that support alpha (PNG, WebP, GIF) preserve it.

Anti-aliasing
A technique for reducing the jagged 'staircase' appearance of diagonal lines and curves in raster images.
Achieved by blending edge pixels with surrounding colours. Pillow's LANCZOS and BICUBIC resampling
filters include anti-aliasing.

AVIF — AV1 Image File Format


A modern image format based on the AV1 video codec. Produces smaller files than JPEG and PNG with
equal or better quality. Supports transparency and HDR. Supported in Pillow 9.1+ with the libavif library.

Bit depth / Colour depth


The number of bits used to store each colour channel. 8-bit per channel (standard) gives 256 possible
values per channel. 16-bit gives 65,536. Higher bit depth means smoother gradients and more editing
headroom.

BMP — Bitmap
An uncompressed raster image format developed by Microsoft. Files are large because there is no
compression. Most commonly encountered in Windows environments.

Canvas
In the context of Pillow, a canvas is a blank Image object (usually created with [Link]()) onto which
you draw shapes, paste other images, or render text.

Channel
A single component of a pixel's colour value. An RGB image has three channels (Red, Green, Blue). An
RGBA image has four. A grayscale ('L') image has one.

CMYK — Cyan Magenta Yellow Black


A subtractive colour model used in colour printing. Each value represents the amount of ink applied — 0 =
maximum ink, 255 = no ink (the opposite of RGB). Conversion from RGB to CMYK is needed for
print-ready artwork.

Codec
Short for coder-decoder. A codec is the algorithm used to compress and decompress image (or video)
data. JPEG uses the DCT (Discrete Cosine Transform) codec.

Compositing
The process of combining two or more images into a single image, respecting transparency and blend
modes. See: alpha compositing, paste(), blend().

— 53 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

Convolution
A mathematical operation where a small kernel matrix is slid over every pixel, computing a weighted
average of the pixel and its neighbours. Most image filters (blur, sharpen, edge detect) are implemented
as convolutions.

DPI — Dots Per Inch


A measure of print resolution. It tells a printer how many pixels to place per inch of paper. 72 DPI is
screen resolution. 300 DPI is standard print quality. DPI does not affect on-screen appearance — only
print output size.

Entropy (of an image)


A measure of the amount of information / randomness in an image. A uniform solid colour image has very
low entropy. A complex photograph has high entropy. [Link]() computes this in Pillow.

EXIF — Exchangeable Image File Format


A standard for embedding metadata in image files (especially JPEG and TIFF). Contains camera settings
(aperture, shutter speed, ISO), GPS coordinates, orientation, date/time, and more. Access via
[Link]() in Pillow.

Filter (image)
An operation that transforms pixel values based on local neighbourhood information. In Pillow, filters are
applied via [Link]([Link]) etc.

GIF — Graphics Interchange Format


A lossless image format developed in 1987 that supports animation and transparency. Limited to 256
colours per frame (8-bit palette). Still widely used for animations on the web.

Grayscale
An image where each pixel has only one value (luminance) instead of three. Mode 'L' in Pillow. Values 0
(black) to 255 (white). Grayscale images are half the memory of equivalent RGB images.

Histogram
A bar chart showing the distribution of pixel values from 0 to 255 in an image. Used to evaluate exposure:
a histogram bunched on the left = dark image; bunched on the right = bright image; spread evenly = well
exposed.

HSV — Hue Saturation Value


A colour model that describes colours more intuitively than RGB. Hue = the type of colour (0-360°, where
0=red, 120=green, 240=blue). Saturation = vividness (0=gray, 100%=pure colour). Value = brightness
(0=black, 100%=full brightness).

ICC Profile
A standardised file that describes the colour characteristics of a device (camera, monitor, printer). Used in
colour management workflows to ensure consistent colour reproduction across devices. See ImageCms.

JPEG — Joint Photographic Experts Group


The most common format for photographs. Uses lossy compression based on the Discrete Cosine
Transform (DCT). Each save discards more image data. Quality setting 1-95 controls the trade-off
between file size and visual quality. Does not support transparency.

Kernel (convolution kernel)

— 54 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

A small matrix (typically 3×3 or 5×5) of numbers used in convolution filters. The values define what
transformation is applied. A blur kernel averages neighbours. A sharpen kernel amplifies differences from
neighbours.

LAB — CIE L*a*b* Colour Space


A colour space designed to be perceptually uniform — meaning that equal numerical distances
correspond to equal perceived colour differences. L = lightness, a = green-red axis, b = blue-yellow axis.

Lossless compression
A compression method that reduces file size without discarding any image data. The original pixel values
are perfectly reconstructed on decompression. PNG and GIF use lossless compression.

Lossy compression
A compression method that discards some image data to achieve smaller file sizes. The image cannot be
perfectly recovered after lossy compression. JPEG and lossy WebP use lossy compression.

Mode (Pillow image mode)


A string describing the type and depth of pixel data in a Pillow Image. Common modes: 'RGB' (colour),
'RGBA' (colour+transparency), 'L' (grayscale), '1' (binary), 'CMYK' (print colour), 'P' (palette/indexed).

NumPy
A Python library for numerical computing with fast array operations. Pillow images can be converted to
NumPy arrays for fast pixel-level mathematics, then converted back. Essential for machine learning
workflows.

Palette (colour palette)


A fixed table of colours used by indexed-colour images (mode 'P'). Each pixel stores an index (0-255) into
this 256-colour table rather than a full RGB value. Used by GIF format. Limited to 256 distinct colours per
frame.

PIL — Python Imaging Library


The original Python image processing library, created by Fredrik Lundh. Development stopped in 2009.
Pillow is the actively maintained fork of PIL.

Pillow
The modern, actively maintained fork of PIL. Installable via pip install Pillow. Imported as 'from PIL import
Image'. Supports Python 3.8+ and is maintained on GitHub at [Link]/python-pillow/Pillow.

Pixel — Picture Element


The smallest addressable unit in a raster image. Each pixel holds one colour value. A 1920×1080 image
contains exactly 2,073,600 pixels. In Pillow, pixel coordinates are (x, y) with (0,0) at the top-left corner.

PNG — Portable Network Graphics


A lossless image format that fully supports transparency via an alpha channel. Files are larger than JPEG
but pixel-perfect quality is always preserved. Ideal for screenshots, logos, and graphics. Replaced GIF for
most static use cases.

Raster image
An image stored as a grid of pixels (as opposed to vector graphics which store mathematical shapes). All
formats that Pillow handles are raster images. Raster images have a fixed resolution — enlarging them
causes pixelation.

Resampling

— 55 —
MASTERING PYTHON PILLOW Complete Programmer's Reference

The algorithm used to compute pixel values when resizing an image. Higher quality algorithms
(LANCZOS) produce sharper results but are slower. NEAREST is fastest but produces blocky results.
BICUBIC is a good balance.

RGB — Red Green Blue


The standard additive colour model for digital displays. Each pixel combines three channels: Red (0-255),
Green (0-255), Blue (0-255). All 16.7 million combinations produce a visible colour. This is the most
common Pillow mode.

RGBA — Red Green Blue Alpha


RGB with an additional Alpha channel for transparency. Alpha 0 = completely transparent, Alpha 255 =
completely opaque. Used for PNG images with transparent backgrounds. JPEG does not support RGBA.

Thumbnail
A small preview version of an image, typically 100-300 pixels in its largest dimension. In Pillow,
[Link]() creates a thumbnail in-place while preserving aspect ratio and never enlarging the image.

TIFF — Tagged Image File Format


A flexible, high-quality format used in professional photography and print production. Supports lossless
compression, multiple colour modes (including CMYK), 16-bit channels, multiple pages per file, and EXIF
metadata.

WebP
A modern image format developed by Google. Supports both lossy and lossless compression, full alpha
transparency, and animation. Produces 25-35% smaller files than JPEG/PNG at equivalent quality.
Recommended for web use.

Thank you for reading Mastering Python Pillow. We hope this book serves as your definitive reference
for every image processing task you encounter. Pillow is a remarkably capable library — and now you
have the knowledge to use every part of it. Happy coding!

For the latest Pillow documentation, visit: [Link]

Source code and issue tracker: [Link]

— 56 —

You might also like