Pillow: the PIL fork that most Python image pipelines still depend on
Python Imaging Library (fork)
At a glance
- What is it?
- Pillow adds image processing to the Python interpreter through a C core and a Python API. It is a mature library with a narrow job, and the install step is where most new users get stuck.
- Who is it for?
- Pillow fits scripts and services that need to open, convert, resize or save images in a handful of formats, and it is already present in most Python environments that touch image files. It is the wrong tool when you need GPU kernels, tensor operations or video decoding, where OpenCV, NumPy and torchvision cover ground Pillow does not.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Pillow actually does for a Python program
Pillow is a fork of the Python Imaging Library, originally written by Fredrik Lundh, and it is maintained by Jeffrey 'Alex' Clark and contributors. The README describes it as adding image processing capabilities to your Python interpreter, with file format support, an internal representation, and processing operations. That sentence is the whole scope. Pillow is not a computer vision library, not a plotting library, and not a video toolkit. It is the layer that turns a JPEG, PNG, TIFF, WebP or BMP file into a Python object you can index, crop, resize, rotate, paste onto, draw on, and write back out.
The audience is anyone whose Python code has to touch an image file at all. A web service that resizes user avatars, a script that converts a folder of TIFFs to PNG, a data pipeline that strips metadata before archiving, a test suite that generates placeholder images. Those jobs are ordinary, and Pillow handles them without pulling in a numerical stack. The pyproject.toml classifiers list the project as Development Status 6, Mature, and the supported interpreters are CPython and PyPy from Python 3.11 through 3.15. That is a deliberately narrow support window: 3.11 is the floor, and setup.py warns that Pillow does not provide prebuilt Windows wheels for interpreter versions it has not declared support for.
The C core and the Python layer above it
The repository is not pure Python. The top level holds src/, depends/, patches/, _custom_build/ and winbuild/, and setup.py builds setuptools Extension objects for a list of optional native libraries: AVIF, FreeType, HarfBuzz, FriBidi, ImageQuant, JPEG2K, JPEG, LCMS, Raqm, TIFF, WebP and Zlib. Each of those is a separate codec or rendering dependency, and each is detected at build time. That is the architecture in one sentence: a thin Python API over a C core, with optional third party libraries compiled in when the build finds them.
The build system is unusual. pyproject.toml sets build-backend to backend with backend-path pointing at _custom_build, and requires pybind11 and setuptools>=77. setup.py parses arguments of the form --pillow-configuration=key=value before setuptools sees the command line, and installs ParallelCompile from pybind11.setup_helpers with a MAX_CONCURRENCY value taken from that configuration. So the parallel build setting is not an environment variable you export; it is a flag routed through the custom backend.
For most users none of this matters, because a wheel is published and the C core arrives precompiled. It matters when you build from source, when you are on a platform without a wheel, or when you need a codec that the default build did not include. The optional dependency groups in pyproject.toml hint at which formats need extra Python packages: fpx and mic both pull in olefile, and test-arrow pulls in arro3-compute, arro3-core, nanoarrow and pyarrow.
Installing Pillow and opening a first image
The README points at the installation page of the documentation, and the package is published on PyPI as pillow. The distribution name is lowercase; the import name is PIL, inherited from the original library. That mismatch trips people up constantly, and it is the single most common source of confusion for new users.
Pillow is installed from PyPI, for example with the pip install line the project's own documentation gives. On a supported platform this pulls a prebuilt wheel, so no compiler is needed. The project's pyproject.toml sets requires-python to >=3.11, so on an older interpreter pip will either refuse or resolve to an older Pillow release. If you need a format that the default wheel omits, the extras are named fpx and mic, and both install olefile alongside Pillow.
Once installed, the import comes from PIL rather than from pillow, and the basic cycle is to open a file, inspect it, and save a converted copy. Image.open is lazy: the file handle stays open until you either load the pixel data or close the image. Reading the size does not force a decode; an operation such as convert or resize does. If you keep a reference to the image without closing it, on Windows the underlying file can stay locked. The repository's own selftest.py at the top level exercises the installed build and reports which optional components were compiled in, which is the fastest way to check whether your JPEG or WebP support is real.
Where Pillow is the wrong choice
Pillow is a per-image library with a Python call boundary. Every operation that crosses into the C core has overhead, and a loop over ten thousand small images will spend most of its time in that boundary rather than in pixel work. For batch jobs at that scale, a pipeline built on a vectorized array library or a dedicated batch tool will beat a naive Pillow loop, and no amount of tuning the Pillow side changes the shape of the problem.
It is also not a video library. There is no frame-accurate seeking, no container demuxing, no audio track handling. If your input is an MP4, Pillow is the wrong layer entirely.
There is a security dimension too. Image decoders parse untrusted binary input, and Pillow's own README has a dedicated section telling users to report vulnerabilities privately on GitHub or through the Tidelift security contact, and explicitly not in public. That instruction exists because the library is a parsing surface. If you accept images from users, the version you run is a security-relevant decision, and the release cadence reflects that: 12.1.1 arrived on 2026-02-11, 12.2.0 on 2026-04-01, and 12.3.0 on 2026-07-01. Quarterly feature releases with point releases in between is a maintenance rhythm you have to keep up with.
The licence is also worth reading rather than assuming. The repository's LICENSE file is the authority, and pyproject.toml declares license as MIT-CMU with license-files pointing at that file. The GitHub metadata reports the licence as NOASSERTION, which means the automated classifier could not map it to a standard SPDX identifier. If your organisation runs licence scanning, MIT-CMU may need to be added to your allow list by hand.
How Pillow differs from OpenCV and NumPy-based stacks
The honest alternative for most Pillow users is OpenCV, and the difference is not speed so much as what the two libraries assume you are doing. Pillow's central object is an Image with a mode string and a size tuple, and its API is organised around file formats and human-readable operations: open, convert, resize, crop, paste, save. OpenCV's central object is a NumPy array with a channel layout and a dtype, and its API is organised around matrix operations, filtering, feature detection and video capture. If your next step after loading is arithmetic on pixel arrays, OpenCV or NumPy gets you there without a conversion step. If your next step is writing a correctly encoded PNG with the right colour profile, Pillow is the shorter path.
The two are not exclusive. A common pattern is to let Pillow handle decode and encode, and hand the pixel buffer to a numerical library for the middle. That works because Pillow images expose their data in a form you can convert, and the conversion cost is one copy.
The other real alternative is not a library at all: shelling out to ImageMagick or libvips. Those are separate processes with their own dependency trees, and they win on throughput for large batch conversions because they avoid the Python interpreter loop. They lose on integration, error handling and packaging. A container that already has Python and Pillow needs nothing else; a container that shells out to ImageMagick needs that binary installed, pinned and kept patched.
Maintenance, releases and the cost of staying current
The repository is not archived, and the last push was on 2026-09-21. Releases are cut on a quarterly rhythm, with the most recent three being 12.3.0 on 2026-07-01, 12.2.0 on 2026-04-01 and 12.1.1 on 2026-02-11. The version is read at build time from src/PIL/_version.py, so a source checkout carries its own version string rather than deriving it from git tags.
Upgrade cost is mostly low, but not zero. Pillow has a long history of deprecations that become removals, and the release notes linked from the README are where those are recorded; the CHANGES.rst file at the top level goes back to the pre-fork era. The practical pattern is to pin a version, read the release notes for the next one, and only then bump. Because the library parses untrusted files, skipping several releases at once means accumulating decoder fixes you did not take.
Building from source is the expensive path, and it is a real cost for anyone outside the wheel matrix. You need setuptools>=77 and pybind11, a C toolchain, and whichever of AVIF, FreeType, HarfBuzz, FriBidi, ImageQuant, JPEG2K, JPEG, LCMS, Raqm, TIFF, WebP or Zlib you want compiled in. The Makefile at the top level provides development targets including clean, coverage, doc, lint and test, and its help target prints the full list, but those are contributor targets, not an installation procedure. If you are building for a platform without a published wheel, budget for dependency discovery rather than assuming the build will find everything.
On licensing, MIT-CMU is a permissive licence, and pyproject.toml points license-files at the LICENSE file for the exact terms. That is a statement about the Pillow source. The optional native libraries it links against carry their own licences, and a wheel bundles some of them. If you redistribute Pillow inside a product, the set of obligations depends on which codecs your build enabled, which is not something this article can settle for you.
Editorial conclusion
Pillow fits scripts and services that need to open, convert, resize or save images in a handful of formats, and it is already present in most Python environments that touch image files. It is the wrong tool when you need GPU kernels, tensor operations or video decoding, where OpenCV, NumPy and torchvision cover ground Pillow does not. Before adopting it, check that your interpreter is Python 3.11 or newer, since pyproject.toml sets requires-python to >=3.11, and confirm that the wheel for your platform ships the codecs you need rather than assuming every format is compiled in.
Frequently asked questions
How do I install Pillow in Python?
Install the distribution named pillow from PyPI, following the installation page linked from the README. The package requires Python 3.11 or newer according to pyproject.toml, and on supported platforms a prebuilt wheel is used so no compiler is needed.
How do I use Pillow in Python?
Import from PIL, not from pillow, and use Image.open to read a file. Image.open is lazy, so close the image when you are done, and call an operation such as convert or resize before saving the result.
What Python versions does Pillow support?
The pyproject.toml classifiers list Python 3.11 through 3.15, for both CPython and PyPy, and requires-python is set to >=3.11. setup.py also warns that prebuilt Windows wheels are not provided for interpreter versions it does not support.
What licence does Pillow use?
pyproject.toml declares license as MIT-CMU and points license-files at the LICENSE file in the repository. GitHub's automated classifier reports the licence as NOASSERTION, so licence scanners may not recognise it without configuration.
How do I report a security vulnerability in Pillow?
The README asks for sensitive vulnerability information to be reported privately on GitHub through the security advisories page, or through the Tidelift security contact if GitHub is not usable. It states explicitly not to report sensitive vulnerability information in public.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/python-pillow-pillow)