Skip to content

v0.2.0

Choose a tag to compare

@kevinjohncutler kevinjohncutler released this 10 Sep 11:09
· 105 commits to main since this release

Breaking

avif.decode() on an image sequence and webp.decode() on an animation now return every frame as a stack, not the first image. That is what gif has always done here and what imagecodecs returns for the same files; returning frame 0 silently discarded the rest. A still image is untouched, and out= is refused for a sequence rather than filled with frame 0. heif deliberately does not follow: its multi-image files are unrelated images, not a time sequence.

New

  • Whole-slide pyramids. Readers for DICOM VL Whole Slide Microscopy (a slide is a series of instances, ordered by total pixel matrix size) and for Olympus VSI/ETS, both behind the shared PyramidReader interface.
  • Offset reads. Readers take a byte range instead of swallowing the whole file, so a 300 GB slide no longer has to fit anywhere.
  • URLs everywhere. Every codec and reader accepts an http(s) URL alongside a path, bytes, memoryview, mmap, BytesIO or an open file.
  • Parallel decode wherever the pieces were already independent, with a cfitsio thread-safety fix underneath it (three file-scope statics needed thread-local storage before HCOMPRESS tiles could be threaded at all).
  • AVIF tiling and encoder choice: tile_cols_log2 / tile_rows_log2 / auto_tiling, yuv_format, codec and codec_options. Tiling defaults to 4x4 once the long axis reaches 1024 px, measured ~2x faster encode for +4.4% bytes at unchanged PSNR.

Fixed

  • Five codecs were holding the GIL through their decode. _jpeg2k, _mozjpeg, _charls, _zfp and _openjph all declared nogil on the declaration, which means "safe to call without the GIL", not "called without it". They now release it, so threading them actually helps.
  • Four advertised codecs shipped in no wheel. jpegls (CharLS), snappy, gif and deflate(backend="isal") are in the README codec table, and reading the published 0.1.13 wheels back shows _charls, _isal and _snappy absent on every platform, _gif present only on macOS. Their libraries appeared in no CI install path, so the header probe dropped each extension, pytest.importorskip removed the tests that would have noticed, and both the build and the suite stayed green. Every platform now ships an identical 39 extensions, and ci/check_wheel_contents.py fails the build if any of them goes missing again.
  • macOS wheels were unbuildable and nothing reported it, because Homebrew bottles follow whichever image GitHub calls macos-latest and that moved to macOS 26, leaving delocate to reject a macosx_15_0 wheel. The runner is pinned to macos-15 so the supported floor stays a decision rather than drifting upward with each image roll.
  • _plio bypassed the shadow-copy loader and hung the test suite on macOS.
  • Vendored cfitsio was missing two upstream bounds checks.

Also

Our own EER decoder (1.7x faster) and TIFF LZW encoder (1.4x faster); RGBE decode 1.7x faster after auditing every vendored source against upstream; no imagecodecs-derived Cython source remains, with attribution restored where it does apply.

Full detail in CHANGES.rst.