### Example of use Source: https://pillow-heif.readthedocs.io/en/latest/options.html Example of setting multiple options at once. ```APIDOC ## Example of use ### Description Example showing how to set multiple options at once. ### Example ```python import pillow_heif pillow_heif.options.THUMBNAILS = False pillow_heif.options.DECODE_THREADS = 1 ``` ``` -------------------------------- ### Install Pillow-Heif from source on FreeBSD Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Install Pillow-Heif from the source directory on FreeBSD. ```bash python3 -m pip install . ``` -------------------------------- ### Install Pillow-Heif with pip Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Recommended way to install Pillow-Heif. Upgrades pip first, then installs pillow-heif. ```bash python3 -m pip install --upgrade pip python3 -m pip install --upgrade pillow-heif ``` -------------------------------- ### Install Pillow-Heif from source directory Source: https://pillow-heif.readthedocs.io/en/latest/installation.html Installs Pillow-Heif from within the uncompressed source directory. ```bash python3 -m pip install . ``` -------------------------------- ### Install Pillow-Heif from source with --no-binary Source: https://pillow-heif.readthedocs.io/en/latest/installation.html Installs Pillow-Heif from source, forcing a source build rather than using binary wheels. ```bash python3 -m pip install --upgrade pillow-heif --no-binary :all: ``` -------------------------------- ### Install Pillow-Heif from source (Linux) Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Install Pillow-Heif from source using pip with --no-binary :all: or from the source directory. ```bash python3 -m pip install --upgrade pillow-heif --no-binary :all: ``` ```bash python3 -m pip install . ``` -------------------------------- ### Install Pillow-Heif from source on Windows Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Install Pillow-Heif from source using pip in the Pillow-Heif directory. ```bash python -m pip install . ``` -------------------------------- ### Install dependencies on macOS Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Install Homebrew packages and upgrade pip before installing Pillow-Heif from source. ```bash brew install x265 libjpeg libde265 libheif python3 -m pip install --upgrade pip ``` -------------------------------- ### Install dependencies and build libheif on FreeBSD Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Commands to install required packages and build libheif on FreeBSD. ```bash - pkg install -y gcc cmake aom x265 - pkg install -y py39-pip - pkg install -y py39-pillow py39-numpy - python3 libheif/build_libs.py ``` -------------------------------- ### Install dependencies on macOS with Homebrew Source: https://pillow-heif.readthedocs.io/en/latest/installation.html Installs required libraries and upgrades pip on macOS. ```bash brew install x265 libjpeg libde265 libheif python3 -m pip install --upgrade pip ``` -------------------------------- ### Install Python and Pillow on FreeBSD Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Commands to install Python and Pillow packages on FreeBSD. ```bash pkg install -y py39-pip pkg install -y py39-pillow ``` -------------------------------- ### Build libheif on Ubuntu Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Commands to install build dependencies and build libheif using the provided build script on Ubuntu. ```bash sudo apt update sudo apt -y install cmake nasm build-essential curl sudo python3 libheif/build_libs.py ``` -------------------------------- ### Build libheif on Alpine Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Commands to install build dependencies and build libheif using the provided build script on Alpine. ```bash sudo apk add --no-cache cmake nasm build-base curl sudo python3 libheif/build_libs.py ``` -------------------------------- ### Build libheif on Windows with msys2 Source: https://pillow-heif.readthedocs.io/en/latest/_sources/installation.rst.txt Commands to build and install libheif using msys2 terminal. ```bash cd .../pillow_heif/libheif/windows/mingw-w64-libheif makepkg-mingw --syncdeps pacman -U mingw-w64-x86_64-libheif-*-any.pkg.tar.zst ``` -------------------------------- ### Save HeifFile to file Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Saves the HeifFile to a file using save(). The example uses quality=-1. Refer to save() for additional parameters and to the Saving images section. ```python heif_file.save("output.heif", quality=-1) ``` -------------------------------- ### Create HeifFile from bytes with OpenCV Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Creates a HeifFile from raw bytes using pillow_heif.from_bytes. The example reads a 16-bit PNG with OpenCV and passes its mode, size, and byte data. ```python import cv2 # OpenCV cv_img = cv2.imread("image_16bit.png", cv2.IMREAD_UNCHANGED) heif_file = pillow_heif.from_bytes( mode="BGRA;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img) ) ``` -------------------------------- ### Override multiple options when registering plugin Source: https://pillow-heif.readthedocs.io/en/latest/options.html Example of passing multiple options to register_heif_opener() to override defaults when registering the Pillow plugin. This sets thumbnails to False, quality to 100, and decode_threads to 10. ```python register_heif_opener(thumbnails=False, quality=100, decode_threads=10) ``` -------------------------------- ### Overriding multiple options at once Source: https://pillow-heif.readthedocs.io/en/latest/options.html Example of overriding multiple options when registering the Pillow plugin. ```APIDOC ## Overriding multiple options at once ### Description When registering a Pillow plugin with `pillow_heif.register_heif_opener()`, you can pass multiple options at once. ### Example ```python register_heif_opener(thumbnails=False, quality=100, decode_threads=10) ``` ``` -------------------------------- ### Set global options: THUMBNAILS and DECODE_THREADS Source: https://pillow-heif.readthedocs.io/en/latest/options.html Example of setting pillow_heif options globally by assigning to the options attributes. This disables thumbnails and sets decode threads to 1. ```python import pillow_heif pillow_heif.options.THUMBNAILS = False pillow_heif.options.DECODE_THREADS = 1 ``` -------------------------------- ### Get numpy array of image by index Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Gets the decoded image data as a numpy array for a specific image index (for multi-frame images). ```python heif_file = pillow_heif.open_heif("file.heif") np_array = np.asarray(heif_file[0]) # accessing image by index. ``` -------------------------------- ### Get numpy array of primary image Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Gets the decoded primary image data as a numpy array using np.asarray(heif_file). The array format matches what Pillow would produce. ```python heif_file = pillow_heif.open_heif("file.heif") np_array = np.asarray(heif_file) ``` -------------------------------- ### _LibHeifImageFile.load_prepare method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Prepares image memory before loading, recreating the core image when size or mode changed due to seek or draft. Required for Pillow 11+ behavior. ```python def load_prepare(self) -> None: # Pillow 11+ creates the image memory only when there is none, `seek` and `draft` can change the size or mode if self._im is not None and (self.im.size != self.size or self.im.mode != self.mode): self.im = Image.core.new(self.mode, self.size) # pylint: disable=too-many-function-args super().load_prepare() ``` -------------------------------- ### open Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/heif.html Opens an image file and returns a HeifFile object. The image data is loaded lazily; call load() on each image to decode it. ```APIDOC ## open ### Description Opens an image file and returns a HeifFile object. The image data is loaded lazily; call load() on each image to decode it. ### Method open ### Parameters - **fp** - See parameter fp in is_supported. - **convert_hdr_to_8bit** (boolean) - Optional - Indicates whether 10-bit or 12-bit images should be converted to 8-bit during decoding. Otherwise, they will open in 16-bit mode. Does not affect monochrome or depth images. - **bgr_mode** (boolean) - Optional - Indicates whether RGB(A) images should be opened in BGR(A) mode. - **kwargs** - Optional - Additional keyword arguments. Supports hdr_to_16bit: a boolean indicating that 10/12-bit image data should be converted to 16-bit mode during decoding. Has lower priority than convert_hdr_to_8bit. Default is True. ### Returns - HeifFile object. ### Exceptions - ValueError: invalid input data. - EOFError: corrupted image data. - SyntaxError: unsupported feature. - RuntimeError: some other error. - OSError: out of memory. ``` -------------------------------- ### Create HEIC with primary image index Source: https://pillow-heif.readthedocs.io/en/latest/_sources/pillow-plugin.rst.txt Opens three PNG images and saves them as a HEIC file with the second image as primary using primary_index=1 and quality=-1. ```python img1 = Image.open(Path("images/jpeg_gif_png/1.png")) img2 = Image.open(Path("images/jpeg_gif_png/2.png")) img3 = Image.open(Path("images/jpeg_gif_png/3.png")) img1.save("1_2P_3.heic", append_images=[img2, img3], save_all=True, primary_index=1, quality=-1) ``` -------------------------------- ### Manual plugin registration with register_heif_opener Source: https://pillow-heif.readthedocs.io/en/latest/_sources/pillow-plugin.rst.txt Registers the plugin manually by calling register_heif_opener() before opening a HEIC image and saving a rotated version. ```python from PIL import Image from pillow_heif import register_heif_opener register_heif_opener() with Image.open("image.heic") as im: im.rotate(45).save("rotated_image.heic") ``` -------------------------------- ### pillow_heif.get_file_mimetype Source: https://pillow-heif.readthedocs.io/en/latest/reference/API.html Gets the MIME type of the HEIF or AVIF object. Returns one of the recognized MIME strings or an empty string if not recognized. ```APIDOC ## get_file_mimetype ### Description Gets the MIME type of the HEIF(or AVIF) object. ### Method Function ### Signature `pillow_heif.get_file_mimetype(_fp_) → str` ### Parameters - **fp** - string, pathlib.Path, file object, or bytes - Required - A filename (string), pathlib.Path object, file object or bytes. The file object must implement `file.read`, `file.seek` and `file.tell` methods, and be opened in binary mode. ### Returns “image/heic”, “image/heif”, “image/heic-sequence”, “image/heif-sequence”, “image/avif”, “image/avif-sequence” or “”. ``` -------------------------------- ### _output_nclx_params: compute NCLX color parameters Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Returns NCLX color parameters (primaries, transfer, matrix, full range) for saving. Uses existing profile, explicit kwargs, or libheif sRGB defaults (1, 13, 6, 1) when saving is enabled and no profile or explicit parameters are given. Falls back to -1 values otherwise. ```python def _output_nclx_params(kwargs: dict, nclx_profile=None) -> tuple[int, int, int, int]: nclx_keys = ("color_primaries", "transfer_characteristics", "matrix_coefficients", "full_range_flag") save_nclx = kwargs.get("save_nclx_profile", options.SAVE_NCLX_PROFILE) has_explicit_nclx = any(k in kwargs for k in nclx_keys) if save_nclx and nclx_profile and not has_explicit_nclx: return ( nclx_profile["color_primaries"], nclx_profile["transfer_characteristics"], nclx_profile["matrix_coefficients"], nclx_profile["full_range_flag"], ) # When NCLX saving is enabled and no existing NCLX profile is on the image and no explicit NCLX parameters # were provided, write an NCLX box that matches libheif internal sRGB encoding defaults. # Without this, no NCLX box is written and viewers must guess the color space, which causes color shifts. # See issue #365. # Values match libheif nclx_profile::set_sRGB_defaults() exactly: # primaries=1 (BT.709) # transfer=13 (sRGB) # matrix=6 (BT.601-6) # full_range=1 if save_nclx and not kwargs.get("nclx_profile") and not has_explicit_nclx: return 1, 13, 6, 1 return ( kwargs.get("color_primaries", -1), kwargs.get("transfer_characteristics", -1), kwargs.get("matrix_coefficients", -1), kwargs.get("full_range_flag", -1), ) ``` -------------------------------- ### Access primary image data Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Accesses the decoded data of the primary image via the data property. The example prints the length of the data. ```python print(len(heif_file.data)) ``` -------------------------------- ### Pass multiple config values to register_heif_opener Source: https://pillow-heif.readthedocs.io/en/latest/_sources/pillow-plugin.rst.txt Passes multiple configuration values to register_heif_opener at once, disabling thumbnails and setting quality to -1. ```python register_heif_opener(thumbnails=False, quality=-1) ``` -------------------------------- ### _LibHeifImageFile class and _open method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Base Pillow plugin class that opens a HEIF file via HeifFile, converting HDR to 8-bit and 16-bit, and raising SyntaxError on decode failures. Initializes frame state, custom mimetype, and clears the tile list. ```python class _LibHeifImageFile(ImageFile.ImageFile): """Base class with all functionality for ``HeifImageFile`` class.""" _heif_file: HeifFile | None = None _close_exclusive_fp_after_loading = True _mode: str def __init__(self, *args, **kwargs): self.__frame = 0 self.__thumbnail: HeifThumbnail | None = None self.__loaded = False super().__init__(*args, **kwargs) def _open(self): try: # when Pillow starts supporting 16-bit multichannel images change `convert_hdr_to_8bit` to False heif_file = HeifFile(self.fp, convert_hdr_to_8bit=True, hdr_to_16bit=True, remove_stride=False) except (OSError, ValueError, SyntaxError, RuntimeError, EOFError) as exception: raise SyntaxError(str(exception)) from None self.custom_mimetype = heif_file.mimetype self._heif_file = heif_file self.__frame = heif_file.primary_index self._init_from_heif_file(self.__frame) self.tile = [] ``` -------------------------------- ### Orientation helper functions (_get_orientation_for_encoder, _get_orientation_xmp, _get_orientation) Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Internal functions that parse EXIF and XMP orientation tags. _get_orientation returns the original orientation (or None) and optionally resets it to 1 in the info dictionary. _get_orientation_xmp handles XMP orientation and resets it if requested. _get_orientation_for_encoder returns 1 when no orientation is found. ```python def _get_orientation_for_encoder(info: dict) -> int: image_orientation = _get_orientation(info, False) return 1 if image_orientation is None else image_orientation def _get_orientation_xmp(info: dict, exif_orientation: int | None, reset: bool = False) -> int | None: xmp_orientation = 1 if info.get("xmp"): xmp_data = info["xmp"].rsplit(b"\x00", 1) if xmp_data[0]: decoded_xmp_data = None for encoding in ("utf-8", "latin1"): try: decoded_xmp_data = xmp_data[0].decode(encoding) break except Exception: # noqa # pylint: disable=broad-except pass if decoded_xmp_data: match = re.search(r'tiff:Orientation(="|>)([0-9])', decoded_xmp_data) if match: xmp_orientation = int(match[2]) if reset: decoded_xmp_data = re.sub(r'tiff:Orientation="([0-9])"', "", decoded_xmp_data) decoded_xmp_data = re.sub(r"([0-9])", "", decoded_xmp_data) # should encode in "utf-8" anyway, as `defusedxml` do not work with `latin1` encoding. if encoding != "utf-8" or xmp_orientation != 1: info["xmp"] = b"".join([decoded_xmp_data.encode("utf-8"), b"\x00" if len(xmp_data) > 1 else b""]) return xmp_orientation if exif_orientation is None and xmp_orientation != 1 else None def _get_orientation(info: dict, reset: bool = False) -> int | None: original_orientation = None if info.get("exif"): try: tif_tag = info["exif"] skipped_exif00 = False if tif_tag.startswith(b"Exif\x00\x00"): skipped_exif00 = True tif_tag = tif_tag[6:] endian_mark = "<" if tif_tag[0:2] == b"\x49\x49" else ">" pointer = unpack(endian_mark + "L", tif_tag[4:8])[0] tag_count = unpack(endian_mark + "H", tif_tag[pointer : pointer + 2])[0] offset = pointer + 2 for tag_n in range(tag_count): pointer = offset + 12 * tag_n if unpack(endian_mark + "H", tif_tag[pointer : pointer + 2])[0] != 274: continue value = tif_tag[pointer + 8 : pointer + 12] t_original_orientation = unpack(endian_mark + "H", value[0:2])[0] if t_original_orientation != 1: original_orientation = t_original_orientation if not reset: break p_value = pointer + 8 if skipped_exif00: p_value += 6 new_orientation = pack(endian_mark + "H", 1) info["exif"] = info["exif"][:p_value] + new_orientation + info["exif"][p_value + 2 :] break except Exception: # noqa # pylint: disable=broad-except pass xmp_orientation = _get_orientation_xmp(info, original_orientation, reset=reset) return xmp_orientation or original_orientation ``` -------------------------------- ### Encode image to HEIF with pillow_heif.encode Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Encodes a raw image (read with OpenCV) directly to a HEIF file using pillow_heif.encode. The example uses mode="BGRA;16" and quality=-1. ```python cv_img = cv2.imread("images/non_heif/RGBA_16__128x128.png", cv2.IMREAD_UNCHANGED) pillow_heif.encode( mode="BGRA;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img), fp="RGBA_10bit.heic", quality=-1) ``` -------------------------------- ### Get embedded thumbnail Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Decodes an embedded thumbnail without decoding the full image. Checks info["thumbnails"] for available sizes, then uses get_thumbnail(0) on the primary image and converts to a Pillow image. ```python heif_file = pillow_heif.open_heif("image.heif") if heif_file.info["thumbnails"]: thumbnail = heif_file[0].get_thumbnail(0) print(thumbnail.size, thumbnail.mode) pil_image = thumbnail.to_pillow() ``` -------------------------------- ### Add all images from another HeifFile Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Adds all images from a second HEIF file to the current HeifFile using add_from_heif. ```python heif_file_to_add = pillow_heif.open_heif("file2.heif") heif_file.add_from_heif(heif_file_to_add) ``` -------------------------------- ### size property Source: https://pillow-heif.readthedocs.io/en/latest/reference/HeifImage.html Width and height of the image. ```APIDOC ## size property ### Description Width and height of the image. ### Returns `tuple[int, int]` ``` -------------------------------- ### Edit info["exif"] in place Source: https://pillow-heif.readthedocs.io/en/latest/_sources/pillow-plugin.rst.txt Edits info["exif"] in place for each frame and saves the modified image with save_all=True. ```python heic_pillow = Image.open(Path("test.heic")) for frame in ImageSequence.Iterator(heic_pillow): frame.info["exif"] = None heic_pillow.save("no_exif.heic", save_all=True) ``` -------------------------------- ### _LibHeifImageFile.draft method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Selects an embedded thumbnail for draft decoding, ignoring the requested mode and choosing the smallest thumbnail not smaller than size that matches the image's properties. Returns (mode, box) when a thumbnail is selected, otherwise None. ```python def draft(self, mode: str | None, size: tuple[int, int] | None) -> tuple[str, tuple[int, int, float, float]] | None: """Configures the loader to decode an embedded thumbnail instead of the image. The smallest thumbnail that is not smaller than ``size`` and is a scaled copy of the image (same mode, aspect ratio, crop, rotation and mirroring, no other color profile) is selected, ``mode`` is ignored. :external:py:meth:`~PIL.Image.Image.thumbnail` calls this with ``size`` multiplied by its ``reducing_gap``. :returns: ``(mode, box)`` when a thumbnail was selected, ``None`` otherwise. """ if not size or not self._heif_file or self.__loaded or self.__thumbnail: return None thumbnail = _thumbnail_for_size(self._heif_file[self.tell()], size) if thumbnail is None: return None self.__thumbnail = thumbnail self._size = thumbnail.size self._mode = thumbnail.mode return self.mode, (0, 0, *thumbnail.size) ``` -------------------------------- ### load_libheif_plugin function Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Loads a specified LibHeif plugin by delegating to `_pillow_heif.load_plugin`. The `plugin_path` can be a string or `Path`. ```python def load_libheif_plugin(plugin_path: str | Path) -> None: """Load specified LibHeif plugin.""" _pillow_heif.load_plugin(plugin_path) ``` -------------------------------- ### _LibHeifImageFile.load method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Loads the current frame's raw data, updating size and mode, and closes the exclusive file pointer after decoding. Handles truncated images when ImageFile.LOAD_TRUNCATED_IMAGES is enabled, and releases the HeifFile for non-animated images. ```python def load(self): if self._heif_file and not self.__loaded: frame_heif = self.__thumbnail or self._heif_file[self.tell()] try: data = frame_heif.data # Size of Image can change during decoding self._size = frame_heif.size # noqa self.load_prepare() self.frombytes(data, "raw", (frame_heif.mode, frame_heif.stride)) except (EOFError, ValueError): if not ImageFile.LOAD_TRUNCATED_IMAGES: raise self.load_prepare() self.__loaded = True # In any case, we close `fp`, since the input data bytes are held by the `HeifFile` class. if self.fp and getattr(self, "_exclusive_fp", False) and hasattr(self.fp, "close"): self.fp.close() self.fp = None if not self.is_animated: self._heif_file = None self.__thumbnail = None return super().load() ``` -------------------------------- ### Save HEIF with HDR metadata using content_light_level Source: https://pillow-heif.readthedocs.io/en/latest/saving-images.html Pass HDR metadata as save keyword arguments. The metadata is written as is and also passed through when opening with convert_hdr_to_8bit=True. Remove a key from info before saving to exclude it. ```python im.save(buf, format="HEIF", content_light_level={"max_content_light_level": 1000, "max_pic_average_light_level": 400}) ``` -------------------------------- ### _add_image_single method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Handles the encoding of a single image by creating the image context and adding planes. ```python def _add_image_single(self, size: tuple[int, int], mode: str, data, **kwargs) -> None: premultiplied_alpha = int(mode.split(sep=";")[0][-1] == "a") # creating image im_out = self.ctx_write.create_image(size, MODE_INFO[mode][2], MODE_INFO[mode][3], premultiplied_alpha) # image data _add_planes(im_out, size, mode, data, kwargs.get("stride", 0), _output_bit_depth(mode, **kwargs)) self._finish_add_image(im_out, size, mode, **kwargs) ``` -------------------------------- ### CtxEncode: encoder context wrapper Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Wraps the C encoder context (_pillow_heif.CtxWrite) for HEIF/AVIF compression. Sets quality, preferred encoder, chroma subsampling, and passes encoder parameters as strings. Stores grid images and item count for later finalization. ```python class CtxEncode: """Encoder bindings from python to python C module.""" def __init__(self, compression_format: HeifCompressionFormat, **kwargs): quality = kwargs.get("quality", options.QUALITY) self.ctx_write = _pillow_heif.CtxWrite( compression_format, -2 if quality is None else quality, options.PREFERRED_ENCODER.get("HEIF" if compression_format == HeifCompressionFormat.HEVC else "AVIF", ""), ) enc_params = kwargs.get("enc_params", {}) chroma = None if "subsampling" in kwargs: chroma = SUBSAMPLING_CHROMA_MAP.get(kwargs["subsampling"]) if chroma is None: chroma = kwargs.get("chroma") if chroma: enc_params["chroma"] = chroma for key, value in enc_params.items(): self.ctx_write.set_parameter(key, value if isinstance(value, str) else str(value)) self._compression_format = compression_format self._chroma = str(enc_params.get("chroma", "")) self._grid_images: list = [] # the `output_nclx_color_profile` of a grid must outlive `finalize` self._items_count = 0 ``` -------------------------------- ### _LibHeifImageFile.seek method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Seeks to a specific frame, reinitializing from the HeifFile and creating a new core image for Pillow 11+. Resets the EXIF loaded flag so EXIF is not reloaded between frames. ```python def seek(self, frame: int): if not self._seek_check(frame): return self.__frame = frame self._init_from_heif_file(frame) # Pillow 11.0+ # We need to create a new core image object on second and # subsequent frames in the image. Image may be different size/mode. # https://github.com/python-pillow/Pillow/issues/8439 self.im = Image.core.new(self._mode, self._size) # pylint: disable=too-many-function-args exif = getattr(self, "_exif", None) # Pillow 9.2+ do no reload exif between frames. if exif is not None and getattr(exif, "_loaded", None): exif._loaded = False # pylint: disable=protected-access ``` -------------------------------- ### Lossless HEIF encoding with matrix_coefficients=0 and chroma=444 Source: https://pillow-heif.readthedocs.io/en/latest/saving-images.html Requires Pillow-Heif 0.14.0 or later for matrix_coefficients. Specify matrix_coefficients=0 to convert in RGB color space and chroma=444 for 4:4:4 subsampling. quality=-1 enables lossless encoding. ```python im_rgb = Image.merge( "RGB", [ Image.linear_gradient(mode="L"), Image.linear_gradient(mode="L").transpose(Image.ROTATE_90), Image.linear_gradient(mode="L").transpose(Image.ROTATE_180), ], ) buf = BytesIO() im_rgb.save(buf, format="HEIF", quality=-1, chroma=444, matrix_coefficients=0) ``` -------------------------------- ### Automatic plugin registration with HeifImagePlugin Source: https://pillow-heif.readthedocs.io/en/latest/_sources/pillow-plugin.rst.txt Registers the plugin automatically by importing HeifImagePlugin, then opens a HEIC image and saves a blurred version. ```python from PIL import Image, ImageFilter from pillow_heif import HeifImagePlugin with Image.open("image.heic") as im: im.filter(filter=ImageFilter.BLUR).save("blurred_image.heic") ``` -------------------------------- ### Save HeifFile with quality -1 Source: https://pillow-heif.readthedocs.io/en/latest/_sources/heif-file.rst.txt Saves the HeifFile to a file with quality -1 (lossless). Refer to HeifFile.save for additional parameters. ```python heif_file.save("output.heif", quality=-1) ``` -------------------------------- ### Add grid tiles to encoder (_add_grid_tiles) Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Internal helper that splits an image plane into grid tiles and adds each tile to the encoder. Requires a valid grid_handle, tile_size in kwargs, and sufficient data; raises ValueError if the plane is too small. Applies ICC profile to each tile when provided. ```python def _add_grid_tiles(self, grid_handle, size: tuple[int, int], mode: str, data, **kwargs) -> None: tile_size = kwargs["tile_size"] bit_depth_out = _output_bit_depth(mode, **kwargs) bytes_per_pixel = MODE_INFO[mode][0] * (2 if MODE_INFO[mode][1] > 8 else 1) src_stride = kwargs.get("stride", 0) or (size[0] * bytes_per_pixel) if len(data) < src_stride * (size[1] - 1) + size[0] * bytes_per_pixel: raise ValueError("Image plane does not contain enough data.") icc_profile = kwargs.get("icc_profile") tile_wh = (tile_size, tile_size) for row in range(ceil(size[1] / tile_size)): for col in range(ceil(size[0] / tile_size)): tile_box = ( col * tile_size, row * tile_size, min(tile_size, size[0] - col * tile_size), min(tile_size, size[1] - row * tile_size), ) tile_data = _extract_tile(data, src_stride, bytes_per_pixel, tile_box, tile_size) tile_im = self.ctx_write.create_image( tile_wh, MODE_INFO[mode][2], MODE_INFO[mode][3], int(mode.split(sep=";")[0][-1] == "a") ) _add_planes(tile_im, tile_wh, mode, tile_data, 0, bit_depth_out) if icc_profile is not None: tile_im.set_icc_profile(kwargs.get("icc_profile_type", "prof"), icc_profile) self.ctx_write.add_tile(grid_handle, col, row, tile_im) ``` -------------------------------- ### Verify primary image flags in HEIF file Source: https://pillow-heif.readthedocs.io/en/latest/saving-images.html Asserts that the primary image is correctly identified in both HeifFile and Pillow objects. For HeifFile, indexing returns the primary image at index 1; for Pillow, seeking to index 1 sets the primary flag to True. ```python assert im.info["primary"] == True assert im[0].info["primary"] == False assert im[1].info["primary"] == True assert im[2].info["primary"] == False assert im_pil.info["primary"] == True im_pil.seek(0) assert im_pil.info["primary"] == False im_pil.seek(1) assert im_pil.info["primary"] == True im_pil.seek(2) assert im_pil.info["primary"] == False ``` -------------------------------- ### open function returns loaded HeifFile Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/heif.html Implementation of the open function in pillow_heif.heif. It creates a HeifFile, loads all images in it, and returns the object. Raises ValueError, EOFError, SyntaxError, RuntimeError, or OSError on invalid input. ```python ret = HeifFile(fp, convert_hdr_to_8bit, bgr_mode, **kwargs) for img in ret: img.load() return ret ``` -------------------------------- ### to_pillow() Source: https://pillow-heif.readthedocs.io/en/latest/reference/HeifImage.html Helper method to create a PIL Image from the thumbnail. ```APIDOC ## to_pillow() ### Description Helper method to create `Image` class from the thumbnail. ### Method `to_pillow()` ### Returns `Image` class created from an image. ``` -------------------------------- ### _add_image_grid method Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/misc.html Handles the encoding of images as a grid, including validation of grid dimensions and metadata management. ```python def _add_image_grid(self, size: tuple[int, int], mode: str, data, tile_size: int, **kwargs) -> None: tile_columns = ceil(size[0] / tile_size) tile_rows = ceil(size[1] / tile_size) if tile_columns > 256 or tile_rows > 256: # the ISO grid payload stores tile counts as uint8 raise ValueError("Grid image cannot have more than 256 rows or columns, increase `tile_size`.") self._items_count += tile_columns * tile_rows + 1 if self._items_count > 1000: # default libheif security limit during reading is 1000 items per file raise ValueError(MAX_ITEMS_ERROR) grid_handle = self.ctx_write.create_grid( size[0], size[1], tile_columns, tile_rows, kwargs.get("save_nclx_profile", options.SAVE_NCLX_PROFILE), *_output_nclx_params(kwargs, kwargs.get("nclx_profile")), kwargs.get("image_orientation", 1), ) self._add_grid_tiles(grid_handle, size, mode, data, tile_size=tile_size, **kwargs) pixel_aspect_ratio = kwargs.get("pixel_aspect_ratio") if pixel_aspect_ratio: grid_handle.set_pixel_aspect_ratio(pixel_aspect_ratio[0], pixel_aspect_ratio[1]) self._add_hdr_metadata(grid_handle, **kwargs) if kwargs.get("primary"): grid_handle.set_primary(self.ctx_write) self._add_metadata(grid_handle, **kwargs) thumbnails = [i for i in kwargs.get("thumbnails", []) if max(size) > i > 3] if thumbnails: pixels_im = self.ctx_write.create_image(size, MODE_INFO[mode][2], MODE_INFO[mode][3], 0) _add_planes(pixels_im, size, mode, data, kwargs.get("stride", 0), _output_bit_depth(mode, **kwargs)) image_orientation = kwargs.get("image_orientation", 1) self._items_count += len(thumbnails) for thumb_box in thumbnails: grid_handle.encode_thumbnail(self.ctx_write, thumb_box, image_orientation, pixels_im) self._grid_images.append(grid_handle) ``` -------------------------------- ### Create HeifFile from Pillow image Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Creates a HeifFile object from a Pillow image using pillow_heif.from_pillow. ```python heif_file = pillow_heif.from_pillow(Image.open("image.gif")): ``` -------------------------------- ### load() Source: https://pillow-heif.readthedocs.io/en/latest/reference/HeifImage.html Method to decode the image. In normal cases, you should not call this method directly; reading data or stride will load automatically. ```APIDOC ## load() ### Description Method to decode image. In normal cases, you should not call this method directly, when reading data or stride property of image will be loaded automatically. ### Method `load()` ### Returns `None` ``` -------------------------------- ### Open HEIF file with open_heif Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Opens a HEIF file using pillow_heif.open_heif. This is preferred over read_heif because it does not decode images immediately; all image data supports lazy loading and is decoded on access, such as when the data property is accessed. ```python if pillow_heif.is_supported("image.heif"): heif_file = pillow_heif.open_heif("image.heif") ``` -------------------------------- ### HeifFile.add_frombytes() Source: https://pillow-heif.readthedocs.io/en/latest/reference/HeifFile.html Adds an image from raw bytes to the HeifFile container. ```APIDOC ## HeifFile.add_frombytes() ### Description Adds an image from bytes to the container. Supports `stride` value if needed. ### Method `add_frombytes(mode: str, size: tuple[int, int], data, **kwargs)` ### Parameters - **mode** (str) - Required - See Modes. - **size** (tuple[int, int]) - Required - Tuple with `width` and `height` of image. - **data** (bytes) - Required - Bytes object with raw image data. ### Returns - **HeifImage** - The added `HeifImage` object. ``` -------------------------------- ### Open HEIF file with multiple images Source: https://pillow-heif.readthedocs.io/en/latest/saving-images.html Opens a HEIF file with multiple images using both the pillow-heif API and Pillow's Image.open. Both objects point to the primary image in the file. ```python im = open_heif("image.heif") im_pil = Image.open("image.heif") ``` -------------------------------- ### from_bytes Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/heif.html Creates a HeifFile from raw image bytes. Supports a stride value if needed. ```APIDOC ## from_bytes ### Description Creates a HeifFile from raw image bytes. Supports a stride value if needed. ### Method from_bytes ### Parameters - **mode** (string) - Required - See image-modes reference. - **size** (tuple[int, int]) - Required - Tuple with width and height of the image. - **data** - Required - Bytes object with raw image data. - **kwargs** - Optional - Additional keyword arguments, including stride. ### Returns - New HeifFile object. ``` -------------------------------- ### Create HeifFile from bytes with OpenCV Source: https://pillow-heif.readthedocs.io/en/latest/_sources/heif-file.rst.txt Creates a HeifFile from raw bytes using OpenCV to read a 16-bit image. The mode is set to "BGRA;16" and the size is derived from the image shape. ```python import cv2 # OpenCV cv_img = cv2.imread("image_16bit.png", cv2.IMREAD_UNCHANGED) heif_file = pillow_heif.from_bytes( mode="BGRA;16", size=(cv_img.shape[1], cv_img.shape[0]), data=bytes(cv_img) ) ``` -------------------------------- ### Update HEIF options from kwargs Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Internal helper that maps keyword arguments to the corresponding module-level options. Unknown keys produce a warning with stacklevel=1. ```python def __options_update(**kwargs): """Internal function to set options from `register_heif_opener` method.""" for k, v in kwargs.items(): if k == "thumbnails": options.THUMBNAILS = v elif k == "depth_images": options.DEPTH_IMAGES = v elif k == "aux_images": options.AUX_IMAGES = v elif k == "quality": options.QUALITY = v elif k == "save_to_12bit": options.SAVE_HDR_TO_12_BIT = v elif k == "decode_threads": options.DECODE_THREADS = v elif k == "save_nclx_profile": options.SAVE_NCLX_PROFILE = v elif k == "preferred_encoder": options.PREFERRED_ENCODER = v elif k == "preferred_decoder": options.PREFERRED_DECODER = v elif k == "grid_tile_size": options.GRID_TILE_SIZE = v else: warn(f"Unknown option: {k}", stacklevel=1) ``` -------------------------------- ### Remove EXIF and XMP via save() parameters Source: https://pillow-heif.readthedocs.io/en/latest/_sources/pillow-plugin.rst.txt Removes EXIF and XMP by passing exif=None and xmp=None to save(). Note that this only affects the primary image when the file has multiple images. ```python image = Image.open(Path("test.heic")) image.save("output.heic", exif=None, xmp=None) ``` -------------------------------- ### Add only first image from another HeifFile Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Adds only the first image from a second HEIF file by passing the image object (heif_file_to_add[0]) to add_from_heif. ```python heif_file_to_add = pillow_heif.open_heif("file2.heif") heif_file.add_from_heif(heif_file_to_add[0]) ``` -------------------------------- ### Enumerate images in HeifFile Source: https://pillow-heif.readthedocs.io/en/latest/heif-file.html Prints the number of images in the file and iterates over each image. Note that HeifFile itself points to the primary image in the container. ```python print("number of images in file:", len(heif_file)) for img in heif_file: print(img) ``` -------------------------------- ### pillow_heif.as_plugin module imports Source: https://pillow-heif.readthedocs.io/en/latest/_modules/pillow_heif/as_plugin.html Module-level imports and deferred error handling for the pillow_heif.as_plugin module. Imports Pillow classes and internal helpers, and falls back to a DeferredError if the native _pillow_heif extension is unavailable. ```python """Plugins for the Pillow library.""" from itertools import chain from typing import IO from warnings import warn from PIL import Image, ImageFile, ImageSequence from . import options from .constants import HeifCompressionFormat from .heif import HeifFile, HeifImage, HeifThumbnail from .misc import ( CtxEncode, _exif_from_pillow, _get_bytes, _get_orientation_for_encoder, _get_primary_index, _pil_to_supported_mode, _xmp_from_pillow, set_orientation, ) try: import _pillow_heif except ImportError as ex: from ._deffered_error import DeferredError _pillow_heif = DeferredError(ex) ``` -------------------------------- ### HeifFile.add_from_heif() Source: https://pillow-heif.readthedocs.io/en/latest/reference/HeifFile.html Adds an existing HeifImage to the container. ```APIDOC ## HeifFile.add_from_heif() ### Description Adds an image to the container from an existing `HeifImage`. ### Method `add_from_heif(image: HeifImage) -> HeifImage` ### Parameters - **image** (HeifImage) - Required - `HeifImage` class to add from. ### Returns - **HeifImage** - The added `HeifImage` object. ``` -------------------------------- ### class pillow_heif.as_plugin._LibHeifImageFile Source: https://pillow-heif.readthedocs.io/en/latest/reference/HeifImagePlugin.html Base class with all functionality for the HeifImageFile class. Provides methods for loading, seeking, verifying, and configuring thumbnail decoding. ```APIDOC ## class pillow_heif.as_plugin._LibHeifImageFile ### Description Base class with all functionality for `HeifImageFile` class. Supports decoding and encoding multiple image frames. ### Methods #### get_format_mimetype() Returns the same as `get_file_mimetype()`. #### load() Load image data based on tile list. #### draft(mode: str | None, size: tuple[int, int] | None) -> tuple[str, tuple[int, int, float, float]] | None Configures the loader to decode an embedded thumbnail instead of the image. The smallest thumbnail that is not smaller than `size` and is a scaled copy of the image (same mode, aspect ratio, crop, rotation and mirroring, no other color profile) is selected, `mode` is ignored. `thumbnail()` calls this with `size` multiplied by its `reducing_gap`. **Returns:** `(mode, box)` when a thumbnail was selected, `None` otherwise. #### seek(frame: int) Seeks to the given frame in this sequence file. If you seek beyond the end of the sequence, the method raises an `EOFError` exception. When a sequence file is opened, the library automatically seeks to frame 0. See `tell()`. If defined, `n_frames` refers to the number of available frames. **Parameters:** - **frame** (int) - Required - Frame number, starting at 0. **Raises:** - **EOFError** - If the call attempts to seek beyond the end of the sequence. #### tell() -> int Returns the current frame number. See `seek()`. If defined, `n_frames` refers to the number of available frames. **Returns:** Frame number, starting with 0. #### verify() -> None Check file integrity. ### Properties #### info: dict A dictionary holding data associated with the image. Known keys and values in dictionary will be saved to the image. They are the same as in `HeifImage` class. Specific keys for this plugin that are always present are: `exif`, `metadata`, `primary`, `bit_depth`, `thumbnails`, `depth_images`, `aux`, `original_orientation`. Optional there can be also such keys: `xmp`, `chroma`, `icc_profile`, `icc_profile_type`, `nclx_profile`, `content_light_level`, `mastering_display_colour_volume`, `ambient_viewing_environment`, `nominal_diffuse_white_luminance`, `pixel_aspect_ratio`, `tiling`, `heif`, `entity_groups`. #### info["original_orientation"]: int Orientation that the `EXIF`/`XMP` tags had in the file, or `None` when there was none. The plugin calls `set_orientation()` for every image, so the tags themselves are already reset when the image is opened. #### n_frames: int Number of available frames. **Returns:** Frame number, starting with 0. #### is_animated: bool `True` if this image contains more than one frame, `False` otherwise. ```