Python interface for libheif library providing HEIF/AVIF image processing with both standalone and Pillow plugin capabilities
Overall
score
94%
Plugin functionality that adds seamless HEIF/AVIF support to Pillow. This integration allows existing PIL/Pillow applications to work with HEIF/AVIF files without code changes, providing full format support through Pillow's standard interface.
Registers HEIF/AVIF format support with Pillow, enabling standard PIL operations on HEIF/AVIF files.
def register_heif_opener(**kwargs) -> None:
"""
Register HEIF/AVIF format support with Pillow.
Parameters:
- **kwargs: options passed to HeifImageFile for all opened files
Options:
- convert_hdr_to_8bit: bool, convert HDR images to 8-bit (default: True)
- bgr_mode: bool, use BGR pixel order (default: False)
Notes:
- Must be called before using PIL.Image.open() on HEIF/AVIF files
- Registration persists for the entire Python session
- Can be called multiple times with different options
"""Usage example:
from PIL import Image
from pillow_heif import register_heif_opener
# Register HEIF support with default settings
register_heif_opener()
# Now Pillow can handle HEIF files
im = Image.open("image.heic")
im = im.resize((800, 600))
im.save("resized.jpg")
# Register with custom options
register_heif_opener(convert_hdr_to_8bit=False, bgr_mode=False)
# HDR images will preserve their bit depth
hdr_image = Image.open("hdr.heic")
print(f"Mode: {hdr_image.mode}") # May show high bit depth modesAlternative method for plugin registration through import:
# Automatic registration - imports and registers the plugin
import pillow_heif.HeifImagePlugin
# Now PIL can handle HEIF files
from PIL import Image
im = Image.open("image.heic")Pillow ImageFile subclass that provides HEIF/AVIF format support with multi-frame capabilities.
class HeifImageFile:
"""
Pillow ImageFile subclass for HEIF/AVIF files.
Class Attributes:
- format: str = "HEIF", Pillow format identifier
- format_description: str = "HEIF container", format description
Properties:
- n_frames: int, number of frames/images in file
- is_animated: bool, True if file contains multiple frames
Inherited Properties (from PIL.ImageFile.ImageFile):
- size: tuple[int, int], image dimensions
- mode: str, color mode
- info: dict, image metadata including EXIF, XMP, IPTC
"""
def seek(self, frame):
"""
Seek to specific frame in multi-image file.
Parameters:
- frame: int, frame number to seek to (0-indexed)
Raises:
EOFError: If frame number is out of range
"""
def tell(self) -> int:
"""
Get current frame number.
Returns:
int: Current frame index (0-indexed)
"""
def verify(self):
"""
Verify image file integrity.
Raises:
Exception: If file is corrupted or invalid
"""Usage example:
from PIL import Image
from pillow_heif import register_heif_opener
register_heif_opener()
# Open multi-frame HEIF file
with Image.open("burst_photos.heic") as im:
print(f"Format: {im.format}") # "HEIF"
print(f"Total frames: {im.n_frames}")
print(f"Is animated: {im.is_animated}")
# Process each frame
for frame in range(im.n_frames):
im.seek(frame)
print(f"Frame {frame}: {im.size} {im.mode}")
im.save(f"frame_{frame}.jpg")
# Return to first frame
im.seek(0)from PIL import Image, ImageFilter, ImageEnhance
from pillow_heif import register_heif_opener
register_heif_opener()
# Standard PIL operations work seamlessly
image = Image.open("photo.heic")
# Image processing
enhanced = ImageEnhance.Sharpness(image).enhance(1.5)
blurred = enhanced.filter(ImageFilter.BLUR)
# Format conversion
blurred.save("processed.jpg", quality=95)
blurred.save("processed.png")
# Save back to HEIF
blurred.save("processed.heic", quality=90)from PIL import Image
from pillow_heif import register_heif_opener
import os
register_heif_opener()
def process_images(input_dir, output_dir, size=(800, 600)):
"""Process all images in directory using standard PIL operations."""
os.makedirs(output_dir, exist_ok=True)
for filename in os.listdir(input_dir):
if filename.lower().endswith(('.heic', '.heif', '.avif', '.jpg', '.png')):
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
try:
with Image.open(input_path) as im:
# Standard PIL operations
im.thumbnail(size, Image.Resampling.LANCZOS)
im.save(output_path, quality=85)
print(f"Processed: {filename}")
except Exception as e:
print(f"Error processing {filename}: {e}")
process_images("photos/", "thumbnails/")from PIL import Image
from pillow_heif import register_heif_opener
register_heif_opener()
def create_gif_from_heif(heif_path, gif_path, duration=500):
"""Convert multi-frame HEIF to animated GIF."""
frames = []
with Image.open(heif_path) as im:
for frame in range(im.n_frames):
im.seek(frame)
# Convert to RGB for GIF compatibility
frame_rgb = im.convert('RGB')
frames.append(frame_rgb.copy())
# Save as animated GIF
frames[0].save(
gif_path,
save_all=True,
append_images=frames[1:],
duration=duration,
loop=0
)
create_gif_from_heif("burst.heic", "animation.gif")from PIL import Image
from pillow_heif import register_heif_opener
register_heif_opener()
# Open image with metadata
with Image.open("photo_with_exif.heic") as im:
print("Original metadata:")
for key, value in im.info.items():
print(f" {key}: {type(value).__name__}")
# Process image
resized = im.resize((1024, 768))
# Save with metadata preserved
resized.save("resized_with_metadata.heic",
quality=90,
exif=im.info.get('exif'),
icc_profile=im.info.get('icc_profile'))from PIL import Image
from pillow_heif import register_heif_opener
import os
register_heif_opener()
def convert_to_heif(source_path, target_path, quality=85):
"""Convert any supported format to HEIF."""
with Image.open(source_path) as im:
print(f"Source format: {im.format}")
print(f"Size: {im.size}")
print(f"Mode: {im.mode}")
# Convert to RGB if necessary
if im.mode in ('RGBA', 'LA'):
# Keep transparency
im.save(target_path, quality=quality)
elif im.mode not in ('RGB', 'L'):
# Convert to RGB
rgb_im = im.convert('RGB')
rgb_im.save(target_path, quality=quality)
else:
im.save(target_path, quality=quality)
# Convert various formats to HEIF
convert_to_heif("photo.jpg", "photo.heic")
convert_to_heif("image.png", "image.heic")
convert_to_heif("old_photo.tiff", "old_photo.heic")from PIL import Image
from pillow_heif import register_heif_opener
# Register with HDR preservation
register_heif_opener(convert_hdr_to_8bit=False)
with Image.open("hdr_photo.heic") as im:
print(f"HDR mode: {im.mode}") # May show modes like 'RGB;10' or 'RGB;12'
# For display, convert to 8-bit
display_image = im.copy()
if ';' in display_image.mode: # HDR mode
display_image = display_image.convert('RGB')
display_image.show()
# Save preserving HDR
im.save("hdr_copy.heic", quality=-1) # Preserve original quality/depthfrom PIL import Image
from pillow_heif import register_heif_opener
import os
def safe_image_open(filepath):
"""Safely open image with fallback handling."""
try:
# Try to register HEIF support
register_heif_opener()
except ImportError:
print("HEIF support not available")
try:
with Image.open(filepath) as im:
print(f"Successfully opened {os.path.basename(filepath)}")
print(f"Format: {im.format}, Size: {im.size}, Mode: {im.mode}")
return im.copy()
except Exception as e:
print(f"Error opening {filepath}: {e}")
return None
# Usage
image = safe_image_open("test.heic")
if image:
image.save("converted.jpg")from PIL import Image
from pillow_heif import register_heif_opener
# Check if HEIF support is available
try:
register_heif_opener()
print("HEIF support available")
# Check which formats are supported
print("Supported formats:", Image.registered_extensions())
# Test HEIF opening
if '.heic' in Image.registered_extensions():
print("HEIC files supported")
if '.avif' in Image.registered_extensions():
print("AVIF files supported")
except ImportError as e:
print(f"HEIF support not available: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-pillow-heifevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10