How-to guides
- GuideResize an image and keep the aspect ratio with Pillow
Resize images in Python without distortion: thumbnail(), ImageOps.contain, cover, fit and pad compared with real outputs, plus EXIF rotation, batch resizing and measured speed tips.
- GuideAdd text to an image with Pillow
Draw text on images with Pillow's ImageDraw: load fonts, centre with anchors, wrap long text, add outlines and semi-transparent boxes, fit text to a box, emoji and non-Latin scripts. Real outputs included.
- GuideConvert images between PNG, JPEG, WebP, AVIF and more with Pillow
Convert between PNG, JPEG, WebP, AVIF, HEIC, GIF, ICO and PDF with Pillow. Measured file sizes, keeping transparency and EXIF, animated GIF to WebP, and a batch converter script.
- GuideConvert between PIL images and NumPy arrays
PIL Image to NumPy array and back: shapes and dtypes for every mode, why arrays are read-only, float and int64 pitfalls, black-image bugs, and applying a matplotlib colormap. Tested code and real output.
- GuideGet an image's size, format, mode and file size with Pillow
Read an image's width and height, format, mode, channels, DPI, file size and metadata with Pillow, without decoding the pixels. Includes the EXIF-rotated size gotcha and a fast folder scanner. Tested output.
- GuideOpen an image from a URL, bytes or a base64 string
Load images into Pillow straight from a URL, raw bytes, a base64 string, a data URI or an upload, without temporary files. Handles errors, large files and streaming. Tested examples with real output.
- GuideConvert a PIL image to bytes, base64 or a data URI
Turn a Pillow image into PNG/JPEG bytes, a base64 string or a data URI, and send it from Flask, FastAPI or Django without saving a file. Explains tobytes() vs save(). Tested examples with real output.
- GuideReduce image file size with Pillow, measured
Shrink JPEG, PNG and WebP files with Pillow: quality, optimize, progressive, subsampling, palette PNGs, metadata stripping and resizing, each measured on a real photo. Plus a target-size compressor.
- GuideDisplay a PIL image: show(), Jupyter, matplotlib and Tkinter
How to display a PIL image: Image.show() and why it sometimes does nothing, inline in Jupyter and Colab, with matplotlib (and the "can not convert to float" error), and in a Tkinter window.
- GuideConvert between Pillow and OpenCV without swapping colours
Convert PIL images to OpenCV arrays and back without the blue-skin bug: BGR vs RGB, alpha channels, greyscale, cv2.imread vs Image.open differences, and speed. Tested with OpenCV and Pillow, real output.
- GuideRead EXIF data with Pillow: date taken, camera, GPS and more
Read EXIF metadata with Pillow's getexif(): tag names, the date a photo was taken, camera and lens, GPS coordinates in decimal degrees, orientation, plus editing and stripping EXIF. Tested output.
- GuideCombine images side by side, stacked or in a grid
Merge several images into one with Pillow: side by side, stacked, or as a grid / contact sheet, with images of different sizes, gaps, background colours and labels. Tested code with the real output images.
- GuideCrop an image with Pillow
How Image.crop() boxes work (left, upper, right, lower, not x, y, width, height), centre and aspect-ratio crops, circular avatars, and auto-cropping white borders, including the JPEG noise that breaks getbbox().
- GuideGet and change pixel values
Read a pixel's RGB value, get all pixels, and change pixel colours with Pillow: getpixel, load(), get_flattened_data (getdata is deprecated), NumPy, and how fast each one is on a real photo.
- GuideOverlay a transparent PNG onto another image
Paste a transparent PNG (logo, watermark, sticker) onto a photo with Pillow without black corners. When to use paste with a mask vs alpha_composite, opacity, positioning, and the "bad transparency mask" error.
- GuideMake a white background transparent
Turn white (or any colour) pixels transparent with Pillow: why exact matching fails on JPEGs, tolerance and soft edges with NumPy, flood fill to keep white parts of the subject, and replacing one colour.
- GuideSplit an image into tiles
Cut an image into an N x M grid, fixed-size tiles, overlapping patches for machine learning, or a seamless Instagram carousel, handling sizes that don't divide evenly. Tested code with real output.
- GuideInstall Pillow (the maintained PIL) the right way
Install Pillow correctly with pip in a virtual environment, conda, Jupyter or Docker; fix "externally-managed-environment", pip install PIL failing, and old Python versions. Tested commands.
- GuideConvert an image to greyscale or black and white
Greyscale with convert("L"), pure black and white with convert("1"), why the result is speckled (dithering), choosing a threshold (fixed or automatic Otsu), and keeping transparency. Real output.
- GuideImage modes explained
What each Pillow image mode stores, how many channels it has, what getpixel returns, which mode each file format opens as, the difference between P and L, and conversions that silently lose data.
- GuideLoad and process every image in a folder
Read every image in a directory into a list, resize or convert a whole folder, sort file names naturally, skip bad files, avoid "Too many open files", and use all CPU cores. Tested on Linux, Windows and macOS.
- GuideCreate, read and resize animated GIFs
Make an animated GIF or WebP from frames, read frame count and durations, extract frames, resize without losing the animation, and fix trails in transparent GIFs (disposal). Tested code, real output.
- GuideSave images as a PDF
Save one or many images as a PDF with Pillow: why the pages come out huge, setting the page size with resolution, fitting images on A4 with margins, file size and quality, and transparent PNGs.
- GuideDraw rectangles, circles and lines on an image
Draw shapes with ImageDraw: how box coordinates and outline width work, semi-transparent fills (and why alpha is ignored on RGB images), smooth anti-aliased circles, thick lines, polygons and labelled boxes.
- GuideRotate or flip an image
Image.rotate() turns counter-clockwise and crops by default. How to rotate clockwise, keep the whole image with expand=True, fill the corners, avoid jagged edges, flip/mirror, and fix sideways phone photos.
- GuideRead and write TIFF files
Open multi-page TIFFs, save stacks, choose compression, keep 16-bit and float data intact through NumPy, convert TIFF to JPEG, and the 48-bit colour TIFFs Pillow silently reduces to 8 bits. Tested, real output.
- GuideInvert image colours
Make a negative with ImageOps.invert, and fix "not supported for mode RGBA" and "mode P support coming soon": invert transparent PNGs without losing the alpha, palette, CMYK and 16-bit images. Tested per mode.
Error fixes
Each one reproduces the error on a real Pillow install, explains the cause and shows the fix.
- Error fixModuleNotFoundError: No module named 'PIL'
Fix "No module named 'PIL'": install Pillow (not PIL) into the same interpreter that runs your code. Exact commands for venv, Windows, macOS, Linux, conda, Jupyter, VS Code and Docker.
- Error fixAttributeError: module 'PIL.Image' has no attribute 'ANTIALIAS'
Image.ANTIALIAS was removed in Pillow 10. Replace it with Image.LANCZOS (same filter, works on every version since 2.7), patch old libraries, and see why pinning Pillow<10 fails on Python 3.12+.
- Error fixAttributeError: 'ImageDraw' object has no attribute 'textsize'
ImageDraw.textsize() and FreeTypeFont.getsize() were removed in Pillow 10. Replace them with textbbox() or textlength(), with a drop-in helper, a centring fix and real measurements.
- Error fixPIL.UnidentifiedImageError: cannot identify image file
Why Pillow says "cannot identify image file" and how to fix it: HTML saved as .jpg, empty downloads, BytesIO not rewound, HEIC without pillow-heif, SVG. Includes a byte-sniffing diagnostic script.
- Error fixOSError: cannot open resource (ImageFont.truetype)
ImageFont.truetype() raises "cannot open resource" when FreeType cannot find the font file. Where Pillow searches on Windows, macOS and Linux, when the name is case-sensitive, and a robust loader with fallback.
- Error fixTypeError: Cannot handle this data type: (1, 1, 3), <f4
Image.fromarray raises "Cannot handle this data type: (1, 1, 3), <f4" or "<i8" when the NumPy dtype or shape has no Pillow mode. How to read the message, and the exact conversion for floats, int64 and channel-first arrays.
- Error fixOSError: cannot write mode F as JPEG (and P, LA, I;16, CMYK…)
"cannot write mode X as JPEG/PNG" means the format cannot store that pixel mode. A tested table of which modes each format accepts, and the right conversion for F, P, LA, I, I;16, CMYK and RGBA images.
- Error fixOSError: image file is truncated (N bytes not processed)
Pillow raises "image file is truncated (N bytes not processed)" when a file ends early. Why open() works but load() fails, how to detect bad files, and when LOAD_TRUNCATED_IMAGES is safe. Tested on real images.
- Error fixDecompressionBombWarning: Image size exceeds limit, could be decompression bomb DOS attack
Pillow warns above 89,478,485 pixels and raises DecompressionBombError above twice that. What the limit protects against, how to raise it safely for trusted images, and how to process huge images without it.
- Error fixTypeError: Image data of dtype object cannot be converted to float
matplotlib's imshow() raises "Image data of dtype object cannot be converted to float" (older versions: "Image data can not convert to float") when it gets None, a path or a list instead of an image. Causes and fixes.
- Error fixTypeError: 'tuple' object does not support item assignment
Why changing one channel of a Pillow pixel, or img.size, raises "'tuple' object does not support item assignment", the fixes, and the NumPy version of the same mistake: "assignment destination is read-only".
- Error fixscipy.misc has no attribute imread (or imresize, imsave, toimage)
scipy.misc.imread, imresize, imsave and toimage were removed from SciPy. Drop-in replacements with Pillow, imageio and matplotlib, and the dtype, size-order and rescaling differences that silently change results.
- Error fixmodule 'PIL' has no attribute 'Image', and other import mix-ups
Fix AttributeError: module 'PIL' has no attribute 'Image', No module named 'Image', type object 'Image' has no attribute 'open' and NameError: name 'Image' is not defined. Each reproduced, with the import that works.
- Error fixImportError: cannot import name '_imaging' from 'PIL'
Pillow's compiled core failed to load: cannot import name '_imaging', DLL load failed, "built for another version of Pillow", missing .so libraries in Lambda/Docker, _imagingft and "decoder jpeg not available".
- Error fixAttributeError: module 'PIL.Image' has no attribute 'Resampling'
Image.Resampling, Image.Transpose and the other enums were added in Pillow 9.1. Reproduced on Pillow 9.0.1, with the upgrade, a constant that works on every version, and how to find what pins an old Pillow.
- Error fixCannot use ImageField because Pillow is not installed
Django's fields.E210 error, reproduced: when Pillow really is missing, when it is installed in a different environment, and when it is installed but broken (Django hides the real ImportError). Tested with Django 6.
More short fixes are in the searchable error index, and browser tools that generate working code are on the tools page.
How these guides are made
Every code example is executed against Pillow 12.3.0 on Python 3.13 before publishing, and the printed output and images on each page come from that run. Version history is checked against Pillow's official release notes. Read more about our editorial process.