bigcat88 / pillow_heif

Python library for working with HEIF images and plugin for Pillow.
BSD 3-Clause "New" or "Revised" License
219 stars 17 forks source link

Lossless encoding for the non-primary image #308

Open bigcat88 opened 2 weeks ago

bigcat88 commented 2 weeks ago

Discussed in https://github.com/bigcat88/pillow_heif/discussions/307

Originally posted by **Foxify52** November 5, 2024 The following is an encoder i wrote to take advantage of the multi layer support avif has. Normally when using this code to encode data by itself in the first layer, it can encode and decode perfectly without any issue. as soon as you try and encode data and place it in a layer, it seems to ignore the lossless encoding i specified for those layers. I dont think its an issue with the package as much as a user error but it is one of those edge cases that might now have been accounted for. Any help would be appreciated. Encoder: ```python3 import numpy as np from PIL import Image import pillow_heif import zlib pillow_heif.register_avif_opener() def encode_to_image(data): binary_data = zlib.compress(data) side_length = int(np.ceil(np.sqrt((len(binary_data) + 3) // 4))) binary_array = np.pad( np.frombuffer(binary_data, dtype=np.uint8), (0, side_length**2 * 4 - len(binary_data)), mode="constant", ).reshape((side_length, side_length, 4)) return Image.fromarray(binary_array, "RGBA") base = Image.open("base.jpg") encoded_images = [encode_to_image(b"Some binary data to encode")] # Save the image base.save( "output.avif", format="AVIF", save_all=True, primary_index=0, append_images=encoded_images, quality=-1, # Required for lossless compression chroma=444, # Required for correct color subsampling matrix_coefficients=0, # Required to convert to RGB color space ) ``` Decoder: ```python3 import zlib import numpy as np import pillow_heif from PIL import Image, ImageSequence pillow_heif.register_avif_opener() img = Image.open("output.avif") bin_data = ImageSequence.Iterator(img)[1].copy() reshaped_binary_array = np.array(bin_data) flattened_binary_array = reshaped_binary_array.flatten() binary_data_with_padding = flattened_binary_array.tobytes() binary_data_without_padding = zlib.decompress(binary_data_with_padding.rstrip(b"\x00")) original_binary_data = b"Some binary data to encode" print(f"Original binary data: {original_binary_data}") print(f"Decoded binary data: {binary_data_without_padding}") print( f"Does the decoded data match the original data? {binary_data_without_padding == original_binary_data}" ) ```
bigcat88 commented 2 weeks ago

REMOVED, too tired, wrote a mess

Foxify52 commented 2 weeks ago

I'm not sure if it would be a big inconvenience but could information about this be added into the documentations for future references? I don't believe there's much on configuration for non primary images.

bigcat88 commented 2 weeks ago

I'll do a PR with implementation on the weekend and add it to the documentation.