Pillow (PIL) Integration¶
pylibheif provides first-class, seamless integration with Pillow. Pillow is strictly an optional dependency; pylibheif operates fully without it.
Automatic Opener & Saver Registration¶
By calling register_pillow_opener(), Pillow gains native support for opening and saving .heic, .heif, and .avif files.
import pylibheif
from PIL import Image
# Register the plugin
pylibheif.register_pillow_opener()
# Open any HEIF/HEIC or AVIF image
with Image.open("sample.heic") as img:
print(img.format) # "HEIF"
print(img.size) # (4032, 3024)
print(img.mode) # "RGB"
# Save as AVIF with preset
img.save("sample.avif", quality=80, preset="fast")
To clean up or unregister the handler:
Bidirectional Zero-Copy Conversion¶
When working with existing pylibheif.HeifImage instances, you can convert back and forth between Pillow Image objects:
Metadata Preservation (EXIF, XMP, ICC)¶
When opening an image through the Pillow plugin, camera metadata is fully preserved inside img.info:
img.info["exif"]: Raw EXIF bytes (with TIFF header offsets normalized).img.info["xmp"]: Embedded XMP metadata string or bytes.img.info["icc_profile"]: Color profile binary bytes.img.info["nclx"]: Normalized Color NCLX parameters dictionary.
When saving via img.save(), you can forward these parameters:
img.save(
"output.heic",
quality=90,
preset="balanced",
exif=img.info.get("exif"),
icc_profile=img.info.get("icc_profile")
)
Auxiliary Image Extraction & Saving (Depth & Gain Maps)¶
HEIF files often bundle auxiliary tracks such as metric depth maps, Apple portrait segmentation masks, or HDR gain maps. When using Pillow, these auxiliary channels are accessible directly on img.info:
with Image.open("portrait.heic") as img:
# 1. Check for Depth Map
if img.info.get("has_depth_image"):
depth = img.info["depth_map"]
print(f"Depth dimensions: {depth.width}x{depth.height}")
print("Depth metadata:", img.info.get("depth_metadata"))
# Convert to real-world metric depth (float32 meters) if calibrated
if depth.info and depth.info.has_metric_depth:
metric_depth = depth.to_metric_depth()
# Render high-contrast scientific pseudocolor (Turbo colormap)
# Automatically resize to match master photo dimensions (size=img.size)
depth_pil = depth.to_pillow(colormap="turbo", size=img.size)
depth_pil.save("depth_turbo.png")
# 2. Check for Apple Portrait Matte (hair/subject segmentation alpha)
if img.info.get("has_portrait_matte"):
matte = img.info["portrait_matte"]
matte_pil = matte.to_pillow(colormap="grayscale", size=img.size)
matte_pil.save("portrait_matte.png")
# 3. Check for HDR Gain Map
if img.info.get("has_gain_map") or "gain_map" in img.info:
gain_map_pil = img.info["gain_map"]
gain_map_pil.save("gain_map.png")
Saving with Auxiliary Depth Maps¶
You can save an image with an embedded depth map or portrait matte directly through img.save():