Skip to content

Make HEIC decoding an optional [heic] extra backed by pillow-heif - #530

Merged
digaobarbosa merged 2 commits into
mainfrom
iuri/heic-extra
Sep 18, 2026
Merged

digaobarbosa merged 2 commits into
mainfrom
iuri/heic-extra

Conversation

@iurisilvio

@iurisilvio iurisilvio commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

What this does

Makes HEIC/HEIF decoding an optional extra backed by pillow-heif, and removes pi-heif from the default install.

  • requirements.txt: drops pi-heif<2.
  • setup.py: adds a heic extra, pip install "roboflow[heic]", which installs pillow-heif>=1.7.0.
  • roboflow/util/image_utils.py: registers the pillow_heif Pillow opener when it is installed, with the same register_heif_opener(thumbnails=False) call. It never registers pi_heif.
  • pyproject.toml: adds pillow_heif.* to the mypy optional-import overrides, next to IPython and ipywidgets.
  • tests/util/test_image_utils.py: covers both registration rules, that pillow_heif is registered when importable and that pi_heif is not.
  • README.md and CHANGELOG.md document the extra and its licence.
  • Bumps the version to 1.5.0. The CHANGELOG's Unreleased section, which holds hosted auto-label support and this change, becomes the 1.5.0 entry. It is a minor bump because the default install drops a dependency.

Why

pi-heif is discontinued: 1.4.0 is its final release (bigcat88/pillow_heif#431). That release bundles libheif 1.23.0, which is affected by the libheif security advisories fixed in 1.23.2 and 1.23.3, including CVE-2026-84383, a critical heap overflow reachable from crafted HEIC and AVIF files. The pi-heif<2 pin can never resolve to a fixed build.

Because image_utils registered the opener at import time, every process that imported roboflow also gained a native HEIF parser for all later PIL.Image.open calls, including processes that never handle HEIC on purpose.

pillow-heif 1.7.0, the maintained package from the same project, bundles libheif 1.23.3 and libde265 1.1.2. Its binary wheels also bundle the x265 encoder, which makes them GPL-2.0. That is the reason for #398 and for the move to pi-heif, so the dependency is opt-in rather than a default requirement.

Impact on users

  • Uploads work without the extra. Project.upload() sends the original bytes, and Project.check_valid_image() detects HEIC from its magic bytes with filetype. Neither decodes the image, and the server handles the HEIC.
  • Local decoding needs the extra. Examples are model.predict("photo.heic") with a local file, the two-stage helpers, and the prediction plotting utilities. Without it they raise Pillow's UnidentifiedImageError.
  • Upgraded environments. If pi-heif is still installed, roboflow no longer registers it. HEIC decoding stops until the user installs roboflow[heic]. The CHANGELOG entry says so.
  • roboflow-slim is unchanged; it never imported image_utils.

Verification

Environment Result
Python 3.10, pip install ".[dev]" No HEIF package installed. make check_code_quality passes. python -m unittest: 1040 tests OK.
Python 3.12, pip install ".[heic,dev]" pillow-heif 1.7.0 (libheif 1.23.3) installed. import roboflow registers pillow_heif.as_plugin. tests/images/sky-rabbit.heic decodes (HEIF, RGB, 432×640), and file2jpeg converts it. Full test suite OK.
Python 3.10, default install plus a leftover pi-heif==1.4.0 No HEIF opener is registered. Opening the HEIC fixture raises UnidentifiedImageError.

🤖 Generated with Claude Code

iurisilvio and others added 2 commits September 18, 2026 19:00
pi-heif is discontinued upstream: its final release, 1.4.0, bundles
libheif 1.23.0, which is affected by the security advisories fixed in
libheif 1.23.2 and 1.23.3 (including CVE-2026-84383). The default install
no longer depends on it.

`pip install "roboflow[heic]"` installs pillow-heif>=1.7.0 (libheif
1.23.3), and image_utils registers its Pillow opener when it is installed.
It never registers pi-heif, so environments that still have the old wheel
stop decoding HEIC with it. The extra is opt-in because pillow-heif's
binary wheels bundle the x265 encoder, which makes them GPL-2.0 (#398).

Uploads and Project.check_valid_image() do not decode images, so they
handle HEIC without the extra. Decoding a local HEIC file needs it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The CHANGELOG's Unreleased section becomes 1.5.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@digaobarbosa
digaobarbosa merged commit f721164 into main Sep 18, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants