CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pillow-heif

Python interface for libheif library providing HEIF/AVIF image processing with both standalone and Pillow plugin capabilities

Overall
score

94%

Overview
Eval results
Files

pillow-integration.mddocs/

Pillow Integration

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.

Capabilities

Plugin Registration

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 modes

Automatic Plugin Loading

Alternative 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")

HeifImageFile - Pillow ImageFile Class

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)

Integration Patterns

Standard Pillow Workflows

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)

Batch Processing with Pillow

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/")

Multi-Frame Animation Support

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")

Metadata Preservation

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'))

Format Detection and Conversion

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")

HDR Image Handling

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/depth

Error Handling and Compatibility

from 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")

Checking Format Support

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-heif

docs

encoding.md

file-operations.md

image-classes.md

index.md

metadata-utilities.md

pillow-integration.md

tile.json