pillow-heif

repository·master·Indexed 43 days ago

https://github.com/bigcat88/pillow_heif

Python bindings to libheif for reading and writing HEIF/HEIC images, including support for 8, 10, and 12-bit encoding/decoding. It can be used as a standalone library or as a plugin for the Pillow library via register_heif_opener().

Tokens
10.8K
Snippets
42
Records
59
Agent score
55%

What's inside pillow-heif

  1. Overview of pillow-heif

    master

    pillow-heif is a library designed for reading and writing HEIF image files. It provides two primary ways to use it:

    1. As a Pillow plugin: Integrates directly with the Pillow library to allow HEIF files to be opened using standard Pillow functions (like Image.open()).
    2. As a standalone library: Can be used independently of Pillow for HEIF operations.

    The library uses libheif as its underlying implementation layer.

  2. Access Depth Images

    master

    When using Pillow via register_heif_opener(), depth images are stored in the im.info["depth_images"] list. Each depth image is an instance of HeifDepthImage(BaseImage). You can convert these to Pillow images using .to_pillow() or to NumPy arrays using np.asarray().

    from PIL import Image
    from pillow_heif import register_heif_opener
    import numpy as np
    
    register_heif_opener()
    
    im = Image.open("../tests/images/heif_other/pug.heic")
    if im.info["depth_images"]:
        depth_im = im.info["depth_images"][0]  # Access the first depth image
        pil_im = depth_im.to_pillow()
        np_im = np.asarray(depth_im)
        print(pil_im)
        print(pil_im.info["metadata"])
  3. Handle Exif/XMP Orientation tags in HEIF

    master

    The HEIF standard treats Exif orientation metadata as purely informational, meaning standard-compliant viewers may ignore it. However, some applications (like older macOS Preview versions) behave inconsistently when rotating images based on these tags.

    To ensure consistent behavior across different image viewers, it is recommended to either remove the orientation tag and physically rotate the image data, or use pillow_heif utilities to normalize the orientation.

    Important Note for Pillow Plugin users (pillow_heif >= 1.14.0): When encoding, the rotation value will not be removed from EXIF; instead, it will also be set in the HEIF header. This ensures that JPEGs or PNGs with orientation tags are correctly handled when encoded to HEIF.

  4. Manage Primary Images in sequences

    master

    In HEIF files with multiple images, one image can be designated as the PrimaryImage.

    • Detection: When opening a file, the object (either HeifFile or Pillow.Image) points to the primary image by default. You can check im.info["primary"] to see if the current frame is the primary one.
    • Saving sequences: When saving an image sequence, the first image becomes the primary image unless a later image has info["primary"] set to True. The last image with info["primary"] == True will be the primary image.
    • Manual override: To change which image is primary, set info["primary"] = True for your desired image and ensure all subsequent images have it set to False.
    • primary_index parameter: The save method accepts a primary_index parameter. Pass the index of the desired image, or -1 to set the last image as the PrimaryImage. This parameter has the highest priority.
    # Set the last image in the sequence as the primary image during save
    im.save(buf, format="HEIF", primary_index=-1)
  5. Understand original_orientation in image info

    master

    The info["original_orientation"] key in the image metadata dictionary contains an integer representing the orientation that the EXIF/XMP tags had in the original file, or None if no orientation was present.

    Note that the plugin automatically calls pillow_heif.set_orientation for every image, meaning the orientation tags are typically reset/normalized when the image is opened.

  6. Use embedded thumbnails for faster decoding

    master

    When a HEIF file contains an embedded thumbnail, Pillow's .thumbnail() and .draft() methods can decode only that thumbnail instead of the full-resolution image. This is significantly faster for large images.

    Important details:

    • An embedded thumbnail is only used if it is a scaled copy of the image (matching mode, aspect ratio, crop, rotation, mirroring, and color profile).
    • By default, .thumbnail() uses a reducing_gap of 2.0. This means the thumbnail is only used if it is at least twice as large as the requested size.
    • To use a thumbnail that is closer to the requested size, pass reducing_gap=1.0 or call .draft(None, (width, height)) before resizing.
    from PIL import Image
    
    # If the image is 12240x16320 and has a 384x512 thumbnail:
    im = Image.open("IMG_0001.heic")
    
    # This decodes only the 384x512 thumbnail
    im.thumbnail((128, 128))
    
    # This would decode the FULL image because 384 < (256 * 2.0)
    im.thumbnail((256, 256))
    
    # To allow the 384px thumbnail to be used for a 256px request:
    im.thumbnail((256, 256), reducing_gap=1.0)
  7. Enumerate and access images in a HeifFile

    master

    A HeifFile acts as a container that can hold multiple images.

    • The HeifFile object itself points to the primary image in the container.
    • You can iterate over the file to access individual images.
    • Use len(heif_file) to get the number of images.
    • Use indexing (e.g., heif_file[0]) to access specific images.
    print("number of images in file:", len(heif_file))
    for img in heif_file:
        print(img)
  8. Change the order of images in a HEIF container

    master

    While there is no direct way to reorder images like the HeifFile API, you can use standard Pillow methods. To change which image is the primary image, use the primary_index parameter in the .save() method.

    To completely reorder the sequence, you must iterate through the source image, copy the frames, and save them with a new append_images list.

    from PIL import Image, ImageSequence
    from pathlib import Path
    
    # Make the second image (index 1) the primary image
    img1 = Image.open(Path("1.png"))
    img2 = Image.open(Path("2.png"))
    img3 = Image.open(Path("3.png"))
    img1.save("1_2P_3.heic", append_images=[img2, img3], save_all=True, primary_index=1)
    
    # Change the primary image of an existing file
    img = Image.open(Path("1_2_3P.heic"))
    img.save("1_2_3P_new.heic", save_all=True, primary_index=-1)
    
    # Completely reorder images (e.g., from 1,2,3 to 3,1,2)
    src_img = Image.open(Path("1_2_3P.heic"))
    frames = list(ImageSequence.Iterator(src_img))
    # Reorder frames manually
    img3 = frames[2].copy()
    img1 = frames[0].copy()
    img2 = frames[1].copy()
    img3.save("3_1_2.heic", save_all=True, append_images=[img1, img2])
  9. Save 16-bit images in 12-bit mode

    master

    By default, 16-bit images created via HeifFile.add_from_pillow, HeifFile.add_frombytes, or opened in Pillow's I mode will be saved in 10-bit mode.

    To save a 16-bit image in 12-bit mode, set the save_to_12bit option to True in the options() configuration.

    # Set options to save in 12-bit instead of the default 10-bit
    options().save_to_12bit = True
    im.save(buf, format="HEIF")
  10. Register pillow-heif as a Pillow plugin

    master

    To enable Pillow to recognize and handle HEIF/HEIC files automatically, you must register the plugin using register_heif_opener(). Once registered, HEIF files can be opened using standard Pillow functions like Image.open().

    from PIL import Image
    from pillow_heif import register_heif_opener
    
    register_heif_opener()
    
    # Now you can open HEIF files normally
    img = Image.open("image.heic")
  11. Access decoded image data and Numpy arrays

    master

    Decoded image data is available via the HeifImage.data property.

    To work with images in numerical libraries like NumPy, you can use np.asarray() on either the HeifFile (for the primary image) or a specific HeifImage index (for multi-frame files). This returns a NumPy array in the same format as Pillow.

    import numpy as np
    
    # Access primary image data via property
    print(len(heif_file.data))
    
    # Get primary image as numpy array
    heif_file = pillow_heif.open_heif("file.heif")
    np_array = np.asarray(heif_file)
    
    # Get specific image index as numpy array
    np_array = np.asarray(heif_file[0])