From 84177a18e6e094b132da50fd37c34392e5c090ba Mon Sep 17 00:00:00 2001 From: Aleksandr Karpinskii Date: Tue, 10 Sep 2024 18:00:43 +0400 Subject: [PATCH 1/4] Depends on pyheif>=0.8.0 --- Makefile | 4 ++-- README.md | 4 ++++ setup.py | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index eab6bb7..4b3d6b7 100644 --- a/Makefile +++ b/Makefile @@ -27,11 +27,11 @@ docker_shell: docker_build .PHONY: install-pillow-latest install-pillow-latest: pip install .[test] \ - git+https://github.com/uploadcare/pyheif.git@v0.8.0-transforms#egg=pyheif + git+https://github.com/carsales/pyheif.git@release-0.8.0#egg=pyheif .PHONY: install-pillow-prod install-pillow-prod: pip install .[test] \ ./pip-stubs/pillow \ git+https://github.com/uploadcare/pillow-simd.git@simd/9.5-png-truncated#egg=pillow-simd \ - git+https://github.com/uploadcare/pyheif.git@v0.8.0-transforms#egg=pyheif + git+https://github.com/carsales/pyheif.git@release-0.8.0#egg=pyheif diff --git a/README.md b/README.md index 6678958..2aa43cf 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ This is not a big library but if you want to contribute is very easy! ## Changelog +### 0.6.3 + +* Depends on pyheif>=0.8.0 + ### 0.6.2 * Fix for buggy LA mode in libheif 1.17.0 - 1.18.2 diff --git a/setup.py b/setup.py index 902bc0f..08afc46 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -__version__ = '0.6.2' +__version__ = '0.6.3' github_url = 'https://github.com/uploadcare' package_name = 'heif-image-plugin' @@ -26,7 +26,7 @@ download_url='%s/%s/archive/v%s.tar.gz' % (github_url, package_name, __version__), keywords=['heif', 'heic', 'Pillow', 'plugin', 'pyhief'], install_requires=[ - "pyheif>=0.7.1", + "pyheif>=0.8.0", "piexif>=1.1.3", ], extras_require={ From a1038ded6e8bd8482e3e0fcca47c21b1efb5c6a2 Mon Sep 17 00:00:00 2001 From: Aleksandr Karpinskii Date: Tue, 10 Sep 2024 18:13:40 +0400 Subject: [PATCH 2/4] Update readme, add exif_transpose usage and heif-enc installation --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2aa43cf..d3c7603 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,24 @@ You can install **heif-image-plugin** from *PyPI*: `pip install heif-image-plugin` +### Install libheif binaries for saving capabilities + +Ubuntu: + +`apt install libheif-examples libheif-plugin-x265 libheif-plugin-aomenc` + ## How to use Just import once before opening an image. ```python -from PIL import Image +from PIL import Image, ImageOps import HeifImagePlugin image = Image.open('test.heic') -image.load() +ImageOps.exif_transpose(image, in_place=True) +# requires `heif-enc` binary with installed codecs or plugins +image.save('test.avif') ``` ## How to contribute From d48fd9239b48daffc9420c95888ed9d5d905ce85 Mon Sep 17 00:00:00 2001 From: Aleksandr Karpinskii Date: Tue, 10 Sep 2024 18:29:26 +0400 Subject: [PATCH 3/4] Test stock pyheif, drop older pyheif versions --- .github/workflows/Check.yml | 6 +++--- HeifImagePlugin.py | 11 ++--------- README.md | 4 ++-- setup.py | 2 +- tests/test_reading.py | 28 +++++++++++++--------------- tests/test_transformations.py | 30 +++++------------------------- 6 files changed, 26 insertions(+), 55 deletions(-) diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml index 79dd49e..53f88d0 100644 --- a/.github/workflows/Check.yml +++ b/.github/workflows/Check.yml @@ -10,12 +10,12 @@ jobs: python: ['3.8', '3.11'] pillow: [prod, latest] libheif: ['1.16.2-6ee6762-3f6b709', '1.18.2-bf35e9e-47f4fc0'] - pyheif: ['', '0.7.1'] + pyheif: ['', '0.8.0'] exclude: - python: '3.11' - pyheif: '0.7.1' + pyheif: '0.8.0' - libheif: '1.18.2-bf35e9e-47f4fc0' - pyheif: '0.7.1' + pyheif: '0.8.0' steps: - uses: actions/checkout@v2 diff --git a/HeifImagePlugin.py b/HeifImagePlugin.py index 9d84369..200c093 100644 --- a/HeifImagePlugin.py +++ b/HeifImagePlugin.py @@ -11,12 +11,6 @@ from pyheif.error import HeifError -try: - from pyheif.transformations import Transformations -except ImportError: - Transformations = None - - @dataclass class LibheifError: code: int @@ -159,7 +153,7 @@ def _open_heif_file(self, apply_transformations): def _open(self): self.tile = [] - self.heif_file = self._open_heif_file(Transformations is None) + self.heif_file = self._open_heif_file(False) def load(self): heif_file, self.heif_file = self.heif_file, None @@ -183,8 +177,7 @@ def load(self): self.load_prepare() if heif_file.data: - if Transformations is not None: - heif_file = _crop_heif_file(heif_file) + heif_file = _crop_heif_file(heif_file) self.frombytes(heif_file.data, "raw", (self.mode, heif_file.stride)) heif_file.data = None diff --git a/README.md b/README.md index d3c7603..283430e 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,9 @@ This is not a big library but if you want to contribute is very easy! ## Changelog -### 0.6.3 +### 0.7.0 -* Depends on pyheif>=0.8.0 +* Depends on pyheif>=0.8.0, drop older versions support ### 0.6.2 diff --git a/setup.py b/setup.py index 08afc46..4bef201 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -__version__ = '0.6.3' +__version__ = '0.7.0' github_url = 'https://github.com/uploadcare' package_name = 'heif-image-plugin' diff --git a/tests/test_reading.py b/tests/test_reading.py index 64131c1..31adbcc 100644 --- a/tests/test_reading.py +++ b/tests/test_reading.py @@ -5,8 +5,9 @@ import pytest from PIL import Image, ImageCms, ImageOps from pyheif.error import HeifError +from pyheif.transformations import Transformations -from HeifImagePlugin import Transformations, check_heif_magic +from HeifImagePlugin import check_heif_magic from . import avg_diff, respath @@ -69,8 +70,7 @@ def test_open_image_metadata(open_mock): m.size = (10, 20) m.mode = 'RGB' m.data = b'rgb' * 10 * 20 - if Transformations is not None: - m.transformations = Transformations(10, 20) + m.transformations = Transformations(10, 20) m.metadata = [ {'type': 'foo', 'data': 'bar'}, {'type': 'bar', 'data': 'foo'}, @@ -102,25 +102,23 @@ def test_check_heif_magic_wrong(): def test_orientation(orientation, orientation_ref_image): image = Image.open(respath('orientation', f'Landscape_{orientation}.heic')) - if Transformations is not None: - # There should be exif in each image, even if Orientation is 0 - assert 'exif' in image.info + # There should be exif in each image, even if Orientation is 0 + assert 'exif' in image.info - # There should be Orientation tag for each image - exif = image.getexif() - assert 0x0112 in exif + # There should be Orientation tag for each image + exif = image.getexif() + assert 0x0112 in exif - # And this orientation should be the same as in filename - assert exif[0x0112] == orientation + # And this orientation should be the same as in filename + assert exif[0x0112] == orientation # Transposed image shoud be Landscape transposed = ImageOps.exif_transpose(image) assert transposed.size == (600, 450) - if Transformations is not None: - # Image should change after transposition - if orientation != 1: - assert image != transposed + # Image should change after transposition + if orientation != 1: + assert image != transposed # The average diff between transposed and original image should be small avg_diffs = avg_diff(transposed, orientation_ref_image, threshold=20) diff --git a/tests/test_transformations.py b/tests/test_transformations.py index d066a3f..ad9e140 100644 --- a/tests/test_transformations.py +++ b/tests/test_transformations.py @@ -1,24 +1,12 @@ from unittest import mock -import pyheif -import pytest from PIL import Image from pyheif import open as pyheif_open - -from HeifImagePlugin import Transformations +from pyheif.transformations import Transformations from . import avg_diff, respath -skip_no_transformations = pytest.mark.skipif( - Transformations is None, - reason="pyheif doesn't support transformations") - -skip_libheif_not_16 = pytest.mark.skipif( - pyheif.libheif_version() < '1.16.0', - reason="libheif < 1.16.0 can't decode odd sizes") - - def open_with_custom_meta(path, *, exif_data=None, exif=None, crop=None, orientation=0): def my_pyheif_open(*args, **kwargs): nonlocal exif_data @@ -32,11 +20,10 @@ def my_pyheif_open(*args, **kwargs): heif.metadata = [{'type': 'Exif', 'data': exif_data}] else: heif.metadata = None - if Transformations is not None: - heif.transformations = Transformations(*heif.size) - heif.transformations.orientation_tag = orientation - if crop: - heif.transformations.crop = crop + heif.transformations = Transformations(*heif.size) + heif.transformations.orientation_tag = orientation + if crop: + heif.transformations.crop = crop return heif with mock.patch('pyheif.open') as open_mock: @@ -52,14 +39,12 @@ def test_no_orientation_and_no_exif(): assert 'exif' not in image.info -@skip_no_transformations def test_empty_exif(): image = open_with_custom_meta(respath('test2.heic'), exif_data=b'', orientation=1) assert 'exif' in image.info assert image.getexif()[274] == 1 -@skip_no_transformations def test_broken_exif(): broken = b'Exif\x00\x00II*\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00' image = open_with_custom_meta(respath('test2.heic'), @@ -68,7 +53,6 @@ def test_broken_exif(): assert image.getexif()[274] == 1 -@skip_no_transformations def test_orientation_and_no_exif(): image = open_with_custom_meta(respath('test2.heic'), orientation=7) @@ -84,7 +68,6 @@ def test_no_orientation_and_exif_with_rotation(): assert image.getexif()[274] == 7 -@skip_no_transformations def test_orientation_and_exif_with_rotation(): # Orientation tag from file should suppress Exif value image = open_with_custom_meta( @@ -94,7 +77,6 @@ def test_orientation_and_exif_with_rotation(): assert image.getexif()[274] == 1 -@skip_no_transformations def test_orientation_and_exif_without_rotation(): image = open_with_custom_meta( respath('test2.heic'), orientation=1, exif={270: "Sample image"}) @@ -103,7 +85,6 @@ def test_orientation_and_exif_without_rotation(): assert image.getexif()[274] == 1 -@skip_no_transformations def test_crop_on_load(): ref_image = Image.open(respath('test2.heic')) assert ref_image.size == (1280, 720) @@ -117,7 +98,6 @@ def test_crop_on_load(): assert image.copy() == ref_image.crop((99, 33, 611, 289)) -@skip_libheif_not_16 def test_fallback_to_transforms(): # Image with 695x472 color and 696x472 alpha with crop image = Image.open(respath('unreadable-wo-transf.heic')) From f4449b215dec67f22eadabc7e07bf616c2ee09e8 Mon Sep 17 00:00:00 2001 From: Aleksandr Karpinskii Date: Tue, 10 Sep 2024 18:46:41 +0400 Subject: [PATCH 4/4] install pyheif from pypi --- .github/workflows/Check.yml | 15 +++++---------- Makefile | 10 +++++----- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml index 53f88d0..b0c9dbb 100644 --- a/.github/workflows/Check.yml +++ b/.github/workflows/Check.yml @@ -2,7 +2,7 @@ on: [push] jobs: check: - name: py${{ matrix.python }} pillow-${{ matrix.pillow }} libheif-${{ matrix.libheif }} pyheif-${{ matrix.pyheif }} + name: py${{ matrix.python }} pillow-${{ matrix.pillow }} libheif-${{ matrix.libheif }} no-binary-${{ matrix.no-binary }} runs-on: ubuntu-20.04 timeout-minutes: 3 strategy: @@ -10,12 +10,12 @@ jobs: python: ['3.8', '3.11'] pillow: [prod, latest] libheif: ['1.16.2-6ee6762-3f6b709', '1.18.2-bf35e9e-47f4fc0'] - pyheif: ['', '0.8.0'] + no-binary: ['pyheif', ':none:'] exclude: - python: '3.11' - pyheif: '0.8.0' + no-binary: ':none:' - libheif: '1.18.2-bf35e9e-47f4fc0' - pyheif: '0.8.0' + no-binary: ':none:' steps: - uses: actions/checkout@v2 @@ -32,12 +32,7 @@ jobs: - name: Update pip run: pip install pip==23.2.1 - name: Install dependencies - run: make install-pillow-${{ matrix.pillow }} - - name: Install ovsolete pyheif - if: ${{ matrix.pyheif }} - run: | - pip uninstall -y pyheif - pip install pyheif==${{ matrix.pyheif }} + run: make install-pillow-${{ matrix.pillow }} no-binary=${{ matrix.no-binary }} - name: Check run: make check - name: Upload coverage to Codecov diff --git a/Makefile b/Makefile index 4b3d6b7..035b61b 100644 --- a/Makefile +++ b/Makefile @@ -24,14 +24,14 @@ docker_build: docker_shell: docker_build docker run --platform=linux/amd64 --rm -it -v .:/src heif-image-plugin:latest +no-binary ?= pyheif + .PHONY: install-pillow-latest install-pillow-latest: - pip install .[test] \ - git+https://github.com/carsales/pyheif.git@release-0.8.0#egg=pyheif + pip install --no-binary $(no-binary) .[test] .PHONY: install-pillow-prod install-pillow-prod: - pip install .[test] \ + pip install --no-binary $(no-binary) .[test] \ ./pip-stubs/pillow \ - git+https://github.com/uploadcare/pillow-simd.git@simd/9.5-png-truncated#egg=pillow-simd \ - git+https://github.com/carsales/pyheif.git@release-0.8.0#egg=pyheif + git+https://github.com/uploadcare/pillow-simd.git@simd/9.5-png-truncated#egg=pillow-simd