A HEIF/HEIC and AVIF plugin for Pillow based on libheif.
You can install heif-image-plugin from PyPI:
pip install heif-image-plugin
pip install 'heif-image-plugin[libheif]'The [libheif] extra installs the libheif-binary package, which provides libheif
and codecs. The plugin uses this package when installed; otherwise, it requires
system libheif.
Ubuntu 24.04:
apt-get install --no-install-recommends \
libheif1 \
libheif-plugin-libde265 \
libheif-plugin-x265 \
libheif-plugin-aomencMinimal supported libheif version is 1.17.x.
The macOS wheel expects @rpath/libheif.1.dylib, while Homebrew's libheif uses
an absolute install name. Build the extension against Homebrew's library so it
records the correct path.
With a C compiler and Python development headers available, run the following
from the repository root in a Python environment without libheif-binary:
brew install libheif
pip install 'setuptools>=77.0.3' 'cffi>=1.15.1'
pip install --no-build-isolation .--no-build-isolation prevents pip from installing libheif-binary
as a build dependency.
On macOS, the build automatically detects Homebrew's libheif when brew is
on PATH and libheif-binary is absent. If Homebrew's libheif is not installed,
the build uses the standard system library search.
Just import once before opening an image.
from PIL import Image, ImageOps
import HeifImagePlugin
image = Image.open('test.heic')
ImageOps.exif_transpose(image, in_place=True)
# requires libheif with a HEVC or AV1 encoder
image.save('test.avif')Decoding errors are exposed as HeifImagePlugin.HeifError, with code,
subcode and message fields.
The plugin follows Pillow's Image.MAX_IMAGE_PIXELS error threshold. Set it to
None to disable pixel and decoding memory limits; 0 rejects nonempty images.
HeifImagePlugin.MAX_METADATA_BYTES defaults to 50 MiB and limits opening memory
and individual ICC profiles, not total process memory. Set it before opening an
image; None disables it, while 0 is clamped to one byte. These limits require
libheif 1.19+. Decoded pixels can exceed this opening budget.
For direct use of _heif_image_plugin.reader.open(), max_pixels sets the pixel
cap and a decoding memory budget of four bytes per pixel; max_metadata_bytes
sets the opening memory budget and ICC-profile cap. Each argument defaults to
None (preserve native defaults); 0 disables its limits independently.
Downsampling defaults to 'best', which selects 'sharp-yuv' for RGB/RGBA images
with 4:2:0 subsampling when supported, and 'average' otherwise.
Encoder-specific parameters can be passed when saving:
image.save(
'test.avif',
quality=90,
subsampling='420',
downsampling='average',
encoder='aom',
concurrency=4,
encoder_params={'speed': 6},
)To encode AVIF with SVT-AV1, use encoder='svt'. SVT supports only 4:2:0
subsampling; omit subsampling or set it to '420':
image.save('test.avif', encoder='svt', subsampling='420')Contributions are welcome:
- Clone the repository:
git clone https://github.com/uploadcare/heif-image-plugin.git. - Build the development image:
make docker_build. - Start a development shell:
make docker_shell. - Make your changes and add tests.
- Run
make lintandmake testinside the container. - Commit your changes on a new branch and open a pull request against uploadcare/heif-image-plugin.
Building from source requires a C compiler and Python development headers.
On Linux and macOS, a normal pip install . automatically installs libheif
headers and libraries in an isolated build environment. Install runtime libheif
separately, using the [libheif] extra or system packages described above.
To build against system libheif without libheif-binary, install libheif
development headers and its library, plus setuptools and CFFI, then run:
pip install --no-build-isolation .Update the version in pyproject.toml and the changelog, commit, and push.
Wait for CI to pass, then download the wheels, build the source distribution,
and check the release files:
make -C dist prepareFiles are saved in dist/<version>/. Upload them to PyPI locally:
make -C dist uploadThese commands require Python 3.11+, build, twine, and an authenticated
GitHub CLI (gh).
- Reading and saving now use libheif directly. The
pyheifdependency is removed. Theheif-encexecutable andHEIF_ENC_BINsetting are no longer used. Saving no longer uses temporary files or subprocesses. - Added the
[libheif]installation extra, which installslibheif-binary. Without this extra, system libheif 1.17 or later is required. - Wheels for CPython 3.9+ are provided for Linux (glibc 2.28+ and musl 1.2+, on x86_64 and ARM64) and macOS 11+ (universal2). Other platforms require a source build, as does using libheif from Homebrew.
- Decoding errors now use
HeifImagePlugin.HeifErrorinstead ofpyheif.error.HeifError. - Applied Pillow's pixel error threshold to native opening and decoding limits.
Added
MAX_METADATA_BYTESwith a default opening memory budget of 50 MiB. - Changed default chroma downsampling from
averagetobest: usessharp-yuvwhen possible, andaverageotherwise. - Enabled bilinear chroma upsampling during decoding for 4:2:2 with libheif 1.19.0+ and for 4:2:0 with libheif 1.23.6+.
- Added saving of palette images (
PandPAmodes), including transparency. - Grayscale images with alpha (
LAmode) are now encoded directly, without converting to RGBA on older libheif versions. - Saving now preserves the displayed EXIF orientation across supported libheif versions.
- Added SVT-AV1 support for AVIF encoding with
encoder='svt'(4:2:0 subsampling only)
- Minimal supported Python version is 3.9
- Minimal supported libheif version is 1.17.x
- Added
encoder_paramsparameter for passing custom encoder parameters when saving - Changed the default chroma downsampling algorithm from
nntoaverage - Default chroma subsampling is now explicitly set to 4:2:0
- Depends on pyheif>=0.8.0; dropped support for older versions
- Fix for buggy LA mode in libheif 1.17.0 - 1.18.2
- Fixed unsupported color conversion for some images
- Added compatibility with Pillow 10.1+
- Minimal supported pyheif is 0.7.1
- Added
downsamplingparameter for saving. Works only withsubsampling== 2. - Transformations support updated to the latest libheif and pyheif
- Fixed HEIF saving in '1' mode
- Added HEIF saving support if
heif-encis installed (part of libheif) - Fixed
HeifImageFile.verify()call - Extensions
.heic,.avif,.heif,.hifare handled by the plugin
- Bypass some decoding errors when
ImageFile.LOAD_TRUNCATED_IMAGESis True.
- Depends on latest pyheif.
! This version requires pyheif with pyheif.open API. As of 2021.11.25 this API
isn't released and is in pyheif's master. See install-pyheif-master-pillow-latest
target in the Makefile to install it.
- Fixed potential vulnerability with arbitrary data in exif metadata.
! This version requires pyheif with pyheif.open API. As of 2021.11.25 this API
isn't released and is in pyheif's master. See install-pyheif-master-pillow-latest
target in the Makefile to install it.
pyheif.openAPI is used for lazy images loading.- Fixed an error when the plugin tries to load any ISOBMFF files.
- AVIF files should work before, but now this is official.
- Patched versions of
pyheifandlibheifwith exposed transformations are supported. In this case opened image isn't transformed on loading and orientation is stored in EXIFOrientationtag like for all other image formats. This is faster and consumes less memory.
- No need to register, works after import.
- Fill
info['icc_profile']on loading. - Close and release file pointer after loading.
- Decoding without custom HeifDecoder(ImageFile.PyDecoder).
This project started with Christian Bianciotto’s pyheif-pillow-opener.