opencodecs 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opencodecs-0.1.1 → opencodecs-0.1.2}/CHANGES.rst +27 -0
- {opencodecs-0.1.1/src/opencodecs.egg-info → opencodecs-0.1.2}/PKG-INFO +128 -40
- {opencodecs-0.1.1 → opencodecs-0.1.2}/README.md +127 -39
- {opencodecs-0.1.1 → opencodecs-0.1.2}/pyproject.toml +1 -1
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/__init__.py +1 -1
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ndtiff_writer.py +2 -2
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_bcdec.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_bmp.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_eer.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_hcomp.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_png.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_qoi.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_rcomp.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_rgbe.c +152 -152
- {opencodecs-0.1.1 → opencodecs-0.1.2/src/opencodecs.egg-info}/PKG-INFO +128 -40
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/bcdec/bcdec.h +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/bitshuffle/bitshuffle_core.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/bitshuffle/iochain.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/cfitsio/fits_hdecompress.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/cfitsio/ricecomp.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/imcd_eer/eer.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/imcd_eer/eer.h +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/imcd_lzw/lzw.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/imcd_lzw/lzw.h +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/libspng/spng.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/oc_tifflzw/oc_tifflzw.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/oc_tifflzw/oc_tifflzw.h +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/3rdparty/rgbe/rgbe.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/MANIFEST.in +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/bench/build_libjxl.sh +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/patches/libjxl/README.md +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/setup.cfg +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/setup.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_aec_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_avif_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_b2nd_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_bcn_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_bitshuffle_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_blosc2_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_bmp_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_brotli_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_brunsli_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_byteshuffle_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_bz2_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_cms_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_czi_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_czi_reader.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_czi_writer.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_deflate_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_dicomrle_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_dicomweb.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_eer_reader.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ets.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_fits.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_fits_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_fits_compressed.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_gif_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_gzip_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_hdf5_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_hdf5_http.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_heif_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_htj2k_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_jpeg2k_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_jpeg_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_jpegls_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_jxl_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_lerc_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_lif_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_lif_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_lz4_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_lzma_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_mozjpeg_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_nd2_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_nd2_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ndtiff.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ndtiff_pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_none_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_numpy_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_oib_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_oib_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_oir_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ole2.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ome_xml.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_omezarr.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_omezarr_writer.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_packints_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_pcodec_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_png_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_predictor_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_pyramid_build.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_qoi_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_quantize_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_rcomp_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_rgbe.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_rgbe_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_snappy_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_sperr_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_sz3_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_tiff_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_tiff_http.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_tiff_pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_tiff_writer.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_ultrahdr_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_vsi_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_webp_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_zarr_codecs.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_zfp_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/_zstd_codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/__init__.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_bitshuffle.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_bytetools.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_deflate.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_lz4.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_ndtiff.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_registry.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_tiff.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/codecs/_zstd.c +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/__init__.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/_io_helpers.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/_optional_backend.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/codec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/color.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/errors.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/io.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/core/segment_compression.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/jxl.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/parallel.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/tiff_reader.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/tifffile_patch.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs/zarr.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs.egg-info/SOURCES.txt +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs.egg-info/dependency_links.txt +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs.egg-info/requires.txt +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/src/opencodecs.egg-info/top_level.txt +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_auto_pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_bcdec.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_brunsli.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_charls.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_core_codec_api.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_corpus.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_corpus_codec_decode.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_corpus_other.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_corpus_png.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_corpus_tiff.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_corpus_vendors.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_coverage_final.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_coverage_finishing.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_coverage_last.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_coverage_more.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_coverage_push.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_czi_fixture.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_czi_pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_czi_writer.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_data_source.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_deflate.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_dicomweb.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_edge_cases.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_eer.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_encode_comprehensive.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_error_handling.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_fits.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_gif.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_hdf5_http.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_hdf5_local.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_heif_avif_features.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_http_byte_savings.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_http_prefetch.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_http_unified.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_io_chunked.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_isal.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_jxl.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_lif_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_live_archives.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_mozjpeg.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_native_parity.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_nd2_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_ndtiff.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_oib_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_ome_xml.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_omezarr.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_omezarr_sharded_range.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_open_pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_openjph.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_optional_backend.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_parallel_jxl.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_phase4_filters.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_phase5_icc.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_phase6_codecs.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_phase7_cms.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_rgbe.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_snappy.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tier1_scientific.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tiff_codec_encode.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tiff_http.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tiff_native.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tiff_pyramid.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tiff_reader.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tiff_writer.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_tifffile_patch.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_ultrahdr.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_unified_api.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_vsi_oir.py +0 -0
- {opencodecs-0.1.1 → opencodecs-0.1.2}/tests/test_zarr_adapters.py +0 -0
|
@@ -10,6 +10,33 @@ Versions follow the same ``YYYY.M.D`` cadence as upstream when we
|
|
|
10
10
|
publish; the entries below cluster work by date rather than by
|
|
11
11
|
release because most of it has shipped continuously to ``main``.
|
|
12
12
|
|
|
13
|
+
0.1.2 (2026-05-22)
|
|
14
|
+
------------------
|
|
15
|
+
|
|
16
|
+
**Windows wheels get the Tier 1 scientific compressors back**
|
|
17
|
+
|
|
18
|
+
* Restored ``_sz3``, ``_pcodec``, ``_sperr``, ``_brunsli`` on Windows.
|
|
19
|
+
Root cause was conda's bash putting ``gcc.exe`` ahead of ``cl.exe``
|
|
20
|
+
on PATH; CMake then produced gnu-format ``libSZ3c.dll.a`` import
|
|
21
|
+
libraries that cibuildwheel's MSVC link.exe couldn't consume.
|
|
22
|
+
* Workflow now uses ``ilammy/msvc-dev-cmd`` to source vcvars64.bat
|
|
23
|
+
before the SZ3+pcodec source-build step; CMake's auto-detect picks
|
|
24
|
+
cl.exe and produces MSVC-format ``SZ3c.lib`` / ``cpcodec.lib``.
|
|
25
|
+
* Validated end-to-end on a Windows 11 VM (clean SZ3 install with
|
|
26
|
+
Ninja + cl.exe + vcvars-sourced env).
|
|
27
|
+
* Windows wheels now match the macOS / Linux codec set.
|
|
28
|
+
|
|
29
|
+
**README rewrite for the released project**
|
|
30
|
+
|
|
31
|
+
* ``pip install opencodecs`` + PyPI badge at the top.
|
|
32
|
+
* New "Why opencodecs" table mapping common scientific-imaging needs
|
|
33
|
+
to concrete shipping capabilities.
|
|
34
|
+
* New "Streaming-reader examples" section with 3 copy-paste recipes:
|
|
35
|
+
HTTP region-fetch from a remote Aperio TIFF, TIFF → OME-Zarr v3
|
|
36
|
+
sharded conversion, and the native progressive JXL thumbnail path.
|
|
37
|
+
* Status / Install sections updated for the 0.1.x cadence.
|
|
38
|
+
|
|
39
|
+
|
|
13
40
|
0.1.1 (2026-05-21)
|
|
14
41
|
------------------
|
|
15
42
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencodecs
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)
|
|
5
5
|
Author: Kevin Cutler
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -28,41 +28,65 @@ Requires-Dist: zarr; extra == "test"
|
|
|
28
28
|
|
|
29
29
|
# opencodecs
|
|
30
30
|
|
|
31
|
+
[](https://pypi.org/project/opencodecs/)
|
|
31
32
|
[](https://github.com/kevinjohncutler/opencodecs/actions/workflows/tests.yml)
|
|
32
33
|
[](https://github.com/kevinjohncutler/opencodecs/actions/workflows/build_wheels.yml)
|
|
33
34
|
|
|
34
|
-
Native, parallel-
|
|
35
|
-
/ Reader / Writer API across compression streams,
|
|
36
|
-
multi-frame stacks, and chunked containers
|
|
35
|
+
Native, parallel, cloud-aware codecs for scientific imaging. One
|
|
36
|
+
unified Codec / Reader / Writer API across compression streams,
|
|
37
|
+
single images, multi-frame stacks, and chunked containers — with
|
|
38
|
+
HTTP range-fetch and per-chunk parallelism wired in at the bottom
|
|
39
|
+
of the stack, not bolted on.
|
|
37
40
|
|
|
38
|
-
Built for fast modern storage (NVMe, 10 G NAS) where the
|
|
39
|
-
codec dispatch and per-tile parallelism, not raw I/O
|
|
40
|
-
implementations of every codec — no runtime
|
|
41
|
-
[imagecodecs](https://github.com/cgohlke/imagecodecs) —
|
|
42
|
-
its excellent test suite as a parity reference.
|
|
41
|
+
Built for fast modern storage (NVMe, 10 G NAS, S3) where the
|
|
42
|
+
bottleneck is codec dispatch and per-tile parallelism, not raw I/O
|
|
43
|
+
bandwidth. Native implementations of every codec — no runtime
|
|
44
|
+
delegation to [imagecodecs](https://github.com/cgohlke/imagecodecs) —
|
|
45
|
+
though we use its excellent test suite as a parity reference.
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pip install opencodecs
|
|
49
|
+
```
|
|
43
50
|
|
|
44
51
|
```python
|
|
45
52
|
import opencodecs as oc
|
|
46
53
|
|
|
54
|
+
# 1. Look at any scientific image file
|
|
47
55
|
arr = oc.read("scan.czi") # auto-detect by extension
|
|
48
56
|
arr = oc.read("photo.jxl")
|
|
49
57
|
arr = oc.read(blob) # auto-detect by magic bytes
|
|
50
58
|
|
|
59
|
+
# 2. Write with the right codec for the data
|
|
51
60
|
oc.write("out.jxl", arr, lossless=True)
|
|
52
61
|
oc.write("out.zst", b"...payload...", level=10)
|
|
53
62
|
|
|
54
|
-
#
|
|
63
|
+
# 3. Stream multi-frame / chunked formats
|
|
55
64
|
with oc.get_codec("czi").open(path) as r:
|
|
56
65
|
print(r.shape, r.dtype, r.n_frames)
|
|
57
66
|
for tile in r: # iter_frames
|
|
58
67
|
...
|
|
59
68
|
tile5 = r[5] # random access
|
|
60
69
|
|
|
70
|
+
# 4. Fetch tiles of a remote pyramidal TIFF over HTTPS by range request
|
|
71
|
+
with oc.open_pyramid("https://example.com/slide.svs") as p:
|
|
72
|
+
region = p.read_region(level=2, y=(1024, 2048), x=(1024, 2048))
|
|
73
|
+
# → 2-3 HTTP Range requests, not a full slide download
|
|
74
|
+
|
|
61
75
|
# Discovery
|
|
62
76
|
oc.list_codecs() # capability table
|
|
63
77
|
oc.has_codec("avif")
|
|
64
78
|
```
|
|
65
79
|
|
|
80
|
+
## Why opencodecs
|
|
81
|
+
|
|
82
|
+
| Need | What you get |
|
|
83
|
+
|---|---|
|
|
84
|
+
| **Decode regions of cloud-hosted TIFF/Zarr/HDF5 without downloading the whole file** | Native `HTTPDataSource` with range-coalescing + adaptive read-ahead, wired into the TIFF/NDTiff/HDF5/Zarr/FITS pyramid readers |
|
|
85
|
+
| **Per-chunk parallel decode of CZI/OME-TIFF/NDTiff stacks** | Built-in `ThreadPoolExecutor` orchestration with nogil-released codec calls; 3–10× over single-threaded reference readers on large stacks |
|
|
86
|
+
| **Modern codec coverage (JPEG XL, AVIF, HEIF, JPEG-LS, Brunsli, Ultra HDR, OME-Zarr v3 sharded)** | All shipped, all with native bindings — no `pip install ten-other-packages` |
|
|
87
|
+
| **Tier-1 scientific compressors (LERC, ZFP, SZ3, SPERR, pcodec, bitshuffle, blosc2, libaec)** | All shipped, source-built with `-O3 + LTO + hidden-visibility` for Pareto wins over distro builds |
|
|
88
|
+
| **Lossless drop-in replacement for `imagecodecs`** | `tifffile_patch` opt-in shim reroutes tifffile's codec dispatch through opencodecs without changing your tifffile code |
|
|
89
|
+
|
|
66
90
|
## Codec capability matrix
|
|
67
91
|
|
|
68
92
|
All codecs below are native implementations linking against system or
|
|
@@ -365,61 +389,125 @@ reader.dataset_names # all numeric datasets in the file
|
|
|
365
389
|
reader.select(name) # switch to a different dataset
|
|
366
390
|
```
|
|
367
391
|
|
|
368
|
-
##
|
|
392
|
+
## Streaming-reader examples
|
|
369
393
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
394
|
+
### 1. Fetch a region of a remote Aperio whole-slide TIFF
|
|
395
|
+
|
|
396
|
+
```python
|
|
397
|
+
import opencodecs as oc
|
|
398
|
+
|
|
399
|
+
# Pyramidal SVS (Aperio) hosted on S3 / any HTTPS endpoint with Range support.
|
|
400
|
+
with oc.open_pyramid("https://example.com/slide.svs") as p:
|
|
401
|
+
print(p.levels) # [(80000, 60000, 3), (40000, 30000, 3), ...]
|
|
402
|
+
region = p.read_region(level=2, y=(1024, 3072), x=(2048, 4096))
|
|
403
|
+
# Total HTTP traffic: ~6 Range requests covering only the tiles
|
|
404
|
+
# that intersect this 2048×2048 bbox — typically 200 KB–2 MB,
|
|
405
|
+
# not the 4 GB whole slide.
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The pyramid reader auto-detects the best level for the requested
|
|
409
|
+
region, fetches only the intersecting TIFF tiles via HTTP Range,
|
|
410
|
+
and assembles the output in-memory. Works the same on local files,
|
|
411
|
+
NFS, SMB, S3, or any range-capable HTTP server.
|
|
412
|
+
|
|
413
|
+
### 2. Convert a multi-level pyramid to OME-Zarr v3 sharded
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
import opencodecs as oc
|
|
417
|
+
|
|
418
|
+
with oc.open_pyramid("input.ome.tiff") as p:
|
|
419
|
+
levels = [p.read_region(level=i) for i in range(len(p.levels))]
|
|
420
|
+
|
|
421
|
+
oc.write_omezarr_pyramid(
|
|
422
|
+
"output.zarr",
|
|
423
|
+
levels,
|
|
424
|
+
chunks=(512, 512),
|
|
425
|
+
shards=(2048, 2048), # 16 chunks per shard, one file each
|
|
426
|
+
compressor="zstd",
|
|
427
|
+
zarr_format=3,
|
|
428
|
+
)
|
|
429
|
+
# 1 file per shard on disk instead of 1 file per chunk; per-chunk
|
|
430
|
+
# random access still works via Range fetches into the shard.
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
For data going to S3, sharded Zarr v3 cuts your `PUT` and `LIST`
|
|
434
|
+
costs by 1–2 orders of magnitude vs unsharded chunks while
|
|
435
|
+
preserving per-chunk random-access via HTTP Range — the reader
|
|
436
|
+
above understands the shard index automatically.
|
|
437
|
+
|
|
438
|
+
### 3. Fast JPEG XL thumbnails (native progressive decode)
|
|
439
|
+
|
|
440
|
+
```python
|
|
441
|
+
import opencodecs.jxl as jxl
|
|
442
|
+
|
|
443
|
+
# downsample=8 uses libjxl's native progressive decoder — stops at
|
|
444
|
+
# the DC pass without reconstructing full-resolution pixels.
|
|
445
|
+
thumb = jxl.read("scan.jxl", downsample=8, subsample="center")
|
|
446
|
+
# 4Kx4K input → 512x512 ndarray in ~28 ms on macOS arm64
|
|
447
|
+
# (vs ~40 ms for a full decode), positionally centroid-correct
|
|
448
|
+
# so SVG / GL renderers don't get a ½-block shift.
|
|
449
|
+
|
|
450
|
+
# For a partial JXL bitstream usable as a tiny browser-direct
|
|
451
|
+
# thumbnail (works in Safari + modern Chrome):
|
|
452
|
+
prefix = jxl.thumbnail_bytes("scan.jxl")
|
|
453
|
+
# → ~85 KB out of a 3.5 MB source for a 4Kx4K image
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## Install
|
|
374
457
|
|
|
375
458
|
```sh
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
459
|
+
pip install opencodecs
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Wheels are published for CPython 3.10–3.13 on macOS (arm64),
|
|
463
|
+
Linux (x86_64 + aarch64), and Windows (amd64). Each wheel
|
|
464
|
+
bundles libjxl, libavif, libheif, libwebp, libdeflate,
|
|
465
|
+
c-blosc2, and friends — no system dependencies needed.
|
|
379
466
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
liblz4-dev libspng-dev libtiff-dev libhdf5-dev \
|
|
384
|
-
libdeflate-dev libopenjph-dev zlib1g-dev
|
|
467
|
+
For a source install, system development headers, or to build a
|
|
468
|
+
tuned local libjxl, see [INSTALL.md](INSTALL.md). Wheel publishing
|
|
469
|
+
runs through [docs/publishing.md](docs/publishing.md).
|
|
385
470
|
|
|
386
|
-
|
|
471
|
+
```sh
|
|
472
|
+
# Source install — auto-detects system libs, source-builds libjxl
|
|
473
|
+
git clone https://github.com/kevinjohncutler/opencodecs.git
|
|
387
474
|
cd opencodecs
|
|
388
475
|
pip install -e .
|
|
389
|
-
# or
|
|
390
|
-
python setup.py build_ext --inplace
|
|
391
476
|
```
|
|
392
477
|
|
|
393
478
|
The build skips cleanly for any system library that's missing — useful
|
|
394
|
-
extensions still build, missing ones print a one-line notice.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
479
|
+
extensions still build, missing ones print a one-line notice. libjxl
|
|
480
|
+
0.11.2 is auto-built from source via `bench/build_libjxl.sh` and
|
|
481
|
+
cached under `~/Library/Caches/opencodecs/` (macOS) /
|
|
482
|
+
`~/.cache/opencodecs/` (Linux). See INSTALL.md for the rationale
|
|
483
|
+
(Homebrew/apt builds are 0.5-0.7× slower than a tuned `-O3 + LTO`
|
|
484
|
+
build).
|
|
400
485
|
|
|
401
486
|
## Status
|
|
402
487
|
|
|
403
|
-
- Core API stable; **1066 tests passing**
|
|
488
|
+
- **v0.1.1** on PyPI (May 2026). Core API stable; **1066 tests passing**
|
|
489
|
+
on Mac M1 Ultra + Linux x86_64/aarch64 + Windows VM
|
|
404
490
|
- Native readers + writers for the common scientific containers
|
|
405
|
-
(TIFF, BigTIFF, OME-TIFF, CZI, NDTiff, HDF5, JXL
|
|
491
|
+
(TIFF, BigTIFF, OME-TIFF, CZI, NDTiff, HDF5, JXL, FITS,
|
|
492
|
+
OME-Zarr v2 + v3 sharded)
|
|
406
493
|
- Cross-platform bench coverage: Mac arm64 (canonical), Windows 11 LTSC
|
|
407
|
-
(libvirt VM), Linux x86_64 (Threadripper)
|
|
494
|
+
(libvirt VM), Linux x86_64 (Threadripper-class)
|
|
408
495
|
- Compression backend auto-detect (libdeflate → zlib-ng-compat → stdlib)
|
|
409
|
-
- Cloud I/O primitives (`HTTPDataSource
|
|
410
|
-
|
|
496
|
+
- Cloud I/O primitives (`HTTPDataSource` with covering-cache + adaptive
|
|
497
|
+
read-ahead) wired into TIFF / HDF5 / DICOMweb / CZI / FITS / Zarr v3
|
|
498
|
+
readers
|
|
411
499
|
- `tifffile_patch` opt-in shim reroutes tifffile's codec dispatch through
|
|
412
500
|
opencodecs for users who want only a partial swap
|
|
413
501
|
|
|
414
502
|
Deferred work (see [`docs/TODO_DEFERRED.md`](docs/TODO_DEFERRED.md)):
|
|
415
503
|
|
|
416
|
-
-
|
|
417
|
-
|
|
504
|
+
- **Windows wheels currently miss `_sz3`, `_pcodec`, `_sperr`, `_brunsli`**
|
|
505
|
+
— toolchain mismatch (conda's bash picks GCC over MSVC for CMake);
|
|
506
|
+
v0.1.2 will restore them. macOS + Linux wheels have the full set.
|
|
418
507
|
- CCITT Fax3/Fax4 encode — legacy fax; zero scientific users
|
|
419
508
|
- JPEG-XR — abandoned format outside niche DICOM
|
|
420
509
|
- libspng `filter_sum` SIMD — off the bench-tracked workload (`h2h_png_4mp_rgb`
|
|
421
510
|
is at 1.14× already); filter-bound PNG-encode users could see another 2-3×
|
|
422
|
-
- Wheels / PyPI release — install from source for now
|
|
423
511
|
|
|
424
512
|
## License
|
|
425
513
|
|
|
@@ -2,41 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
# opencodecs
|
|
4
4
|
|
|
5
|
+
[](https://pypi.org/project/opencodecs/)
|
|
5
6
|
[](https://github.com/kevinjohncutler/opencodecs/actions/workflows/tests.yml)
|
|
6
7
|
[](https://github.com/kevinjohncutler/opencodecs/actions/workflows/build_wheels.yml)
|
|
7
8
|
|
|
8
|
-
Native, parallel-
|
|
9
|
-
/ Reader / Writer API across compression streams,
|
|
10
|
-
multi-frame stacks, and chunked containers
|
|
9
|
+
Native, parallel, cloud-aware codecs for scientific imaging. One
|
|
10
|
+
unified Codec / Reader / Writer API across compression streams,
|
|
11
|
+
single images, multi-frame stacks, and chunked containers — with
|
|
12
|
+
HTTP range-fetch and per-chunk parallelism wired in at the bottom
|
|
13
|
+
of the stack, not bolted on.
|
|
11
14
|
|
|
12
|
-
Built for fast modern storage (NVMe, 10 G NAS) where the
|
|
13
|
-
codec dispatch and per-tile parallelism, not raw I/O
|
|
14
|
-
implementations of every codec — no runtime
|
|
15
|
-
[imagecodecs](https://github.com/cgohlke/imagecodecs) —
|
|
16
|
-
its excellent test suite as a parity reference.
|
|
15
|
+
Built for fast modern storage (NVMe, 10 G NAS, S3) where the
|
|
16
|
+
bottleneck is codec dispatch and per-tile parallelism, not raw I/O
|
|
17
|
+
bandwidth. Native implementations of every codec — no runtime
|
|
18
|
+
delegation to [imagecodecs](https://github.com/cgohlke/imagecodecs) —
|
|
19
|
+
though we use its excellent test suite as a parity reference.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pip install opencodecs
|
|
23
|
+
```
|
|
17
24
|
|
|
18
25
|
```python
|
|
19
26
|
import opencodecs as oc
|
|
20
27
|
|
|
28
|
+
# 1. Look at any scientific image file
|
|
21
29
|
arr = oc.read("scan.czi") # auto-detect by extension
|
|
22
30
|
arr = oc.read("photo.jxl")
|
|
23
31
|
arr = oc.read(blob) # auto-detect by magic bytes
|
|
24
32
|
|
|
33
|
+
# 2. Write with the right codec for the data
|
|
25
34
|
oc.write("out.jxl", arr, lossless=True)
|
|
26
35
|
oc.write("out.zst", b"...payload...", level=10)
|
|
27
36
|
|
|
28
|
-
#
|
|
37
|
+
# 3. Stream multi-frame / chunked formats
|
|
29
38
|
with oc.get_codec("czi").open(path) as r:
|
|
30
39
|
print(r.shape, r.dtype, r.n_frames)
|
|
31
40
|
for tile in r: # iter_frames
|
|
32
41
|
...
|
|
33
42
|
tile5 = r[5] # random access
|
|
34
43
|
|
|
44
|
+
# 4. Fetch tiles of a remote pyramidal TIFF over HTTPS by range request
|
|
45
|
+
with oc.open_pyramid("https://example.com/slide.svs") as p:
|
|
46
|
+
region = p.read_region(level=2, y=(1024, 2048), x=(1024, 2048))
|
|
47
|
+
# → 2-3 HTTP Range requests, not a full slide download
|
|
48
|
+
|
|
35
49
|
# Discovery
|
|
36
50
|
oc.list_codecs() # capability table
|
|
37
51
|
oc.has_codec("avif")
|
|
38
52
|
```
|
|
39
53
|
|
|
54
|
+
## Why opencodecs
|
|
55
|
+
|
|
56
|
+
| Need | What you get |
|
|
57
|
+
|---|---|
|
|
58
|
+
| **Decode regions of cloud-hosted TIFF/Zarr/HDF5 without downloading the whole file** | Native `HTTPDataSource` with range-coalescing + adaptive read-ahead, wired into the TIFF/NDTiff/HDF5/Zarr/FITS pyramid readers |
|
|
59
|
+
| **Per-chunk parallel decode of CZI/OME-TIFF/NDTiff stacks** | Built-in `ThreadPoolExecutor` orchestration with nogil-released codec calls; 3–10× over single-threaded reference readers on large stacks |
|
|
60
|
+
| **Modern codec coverage (JPEG XL, AVIF, HEIF, JPEG-LS, Brunsli, Ultra HDR, OME-Zarr v3 sharded)** | All shipped, all with native bindings — no `pip install ten-other-packages` |
|
|
61
|
+
| **Tier-1 scientific compressors (LERC, ZFP, SZ3, SPERR, pcodec, bitshuffle, blosc2, libaec)** | All shipped, source-built with `-O3 + LTO + hidden-visibility` for Pareto wins over distro builds |
|
|
62
|
+
| **Lossless drop-in replacement for `imagecodecs`** | `tifffile_patch` opt-in shim reroutes tifffile's codec dispatch through opencodecs without changing your tifffile code |
|
|
63
|
+
|
|
40
64
|
## Codec capability matrix
|
|
41
65
|
|
|
42
66
|
All codecs below are native implementations linking against system or
|
|
@@ -339,61 +363,125 @@ reader.dataset_names # all numeric datasets in the file
|
|
|
339
363
|
reader.select(name) # switch to a different dataset
|
|
340
364
|
```
|
|
341
365
|
|
|
342
|
-
##
|
|
366
|
+
## Streaming-reader examples
|
|
343
367
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
368
|
+
### 1. Fetch a region of a remote Aperio whole-slide TIFF
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
import opencodecs as oc
|
|
372
|
+
|
|
373
|
+
# Pyramidal SVS (Aperio) hosted on S3 / any HTTPS endpoint with Range support.
|
|
374
|
+
with oc.open_pyramid("https://example.com/slide.svs") as p:
|
|
375
|
+
print(p.levels) # [(80000, 60000, 3), (40000, 30000, 3), ...]
|
|
376
|
+
region = p.read_region(level=2, y=(1024, 3072), x=(2048, 4096))
|
|
377
|
+
# Total HTTP traffic: ~6 Range requests covering only the tiles
|
|
378
|
+
# that intersect this 2048×2048 bbox — typically 200 KB–2 MB,
|
|
379
|
+
# not the 4 GB whole slide.
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
The pyramid reader auto-detects the best level for the requested
|
|
383
|
+
region, fetches only the intersecting TIFF tiles via HTTP Range,
|
|
384
|
+
and assembles the output in-memory. Works the same on local files,
|
|
385
|
+
NFS, SMB, S3, or any range-capable HTTP server.
|
|
386
|
+
|
|
387
|
+
### 2. Convert a multi-level pyramid to OME-Zarr v3 sharded
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
import opencodecs as oc
|
|
391
|
+
|
|
392
|
+
with oc.open_pyramid("input.ome.tiff") as p:
|
|
393
|
+
levels = [p.read_region(level=i) for i in range(len(p.levels))]
|
|
394
|
+
|
|
395
|
+
oc.write_omezarr_pyramid(
|
|
396
|
+
"output.zarr",
|
|
397
|
+
levels,
|
|
398
|
+
chunks=(512, 512),
|
|
399
|
+
shards=(2048, 2048), # 16 chunks per shard, one file each
|
|
400
|
+
compressor="zstd",
|
|
401
|
+
zarr_format=3,
|
|
402
|
+
)
|
|
403
|
+
# 1 file per shard on disk instead of 1 file per chunk; per-chunk
|
|
404
|
+
# random access still works via Range fetches into the shard.
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
For data going to S3, sharded Zarr v3 cuts your `PUT` and `LIST`
|
|
408
|
+
costs by 1–2 orders of magnitude vs unsharded chunks while
|
|
409
|
+
preserving per-chunk random-access via HTTP Range — the reader
|
|
410
|
+
above understands the shard index automatically.
|
|
411
|
+
|
|
412
|
+
### 3. Fast JPEG XL thumbnails (native progressive decode)
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
import opencodecs.jxl as jxl
|
|
416
|
+
|
|
417
|
+
# downsample=8 uses libjxl's native progressive decoder — stops at
|
|
418
|
+
# the DC pass without reconstructing full-resolution pixels.
|
|
419
|
+
thumb = jxl.read("scan.jxl", downsample=8, subsample="center")
|
|
420
|
+
# 4Kx4K input → 512x512 ndarray in ~28 ms on macOS arm64
|
|
421
|
+
# (vs ~40 ms for a full decode), positionally centroid-correct
|
|
422
|
+
# so SVG / GL renderers don't get a ½-block shift.
|
|
423
|
+
|
|
424
|
+
# For a partial JXL bitstream usable as a tiny browser-direct
|
|
425
|
+
# thumbnail (works in Safari + modern Chrome):
|
|
426
|
+
prefix = jxl.thumbnail_bytes("scan.jxl")
|
|
427
|
+
# → ~85 KB out of a 3.5 MB source for a 4Kx4K image
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## Install
|
|
348
431
|
|
|
349
432
|
```sh
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
433
|
+
pip install opencodecs
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Wheels are published for CPython 3.10–3.13 on macOS (arm64),
|
|
437
|
+
Linux (x86_64 + aarch64), and Windows (amd64). Each wheel
|
|
438
|
+
bundles libjxl, libavif, libheif, libwebp, libdeflate,
|
|
439
|
+
c-blosc2, and friends — no system dependencies needed.
|
|
353
440
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
liblz4-dev libspng-dev libtiff-dev libhdf5-dev \
|
|
358
|
-
libdeflate-dev libopenjph-dev zlib1g-dev
|
|
441
|
+
For a source install, system development headers, or to build a
|
|
442
|
+
tuned local libjxl, see [INSTALL.md](INSTALL.md). Wheel publishing
|
|
443
|
+
runs through [docs/publishing.md](docs/publishing.md).
|
|
359
444
|
|
|
360
|
-
|
|
445
|
+
```sh
|
|
446
|
+
# Source install — auto-detects system libs, source-builds libjxl
|
|
447
|
+
git clone https://github.com/kevinjohncutler/opencodecs.git
|
|
361
448
|
cd opencodecs
|
|
362
449
|
pip install -e .
|
|
363
|
-
# or
|
|
364
|
-
python setup.py build_ext --inplace
|
|
365
450
|
```
|
|
366
451
|
|
|
367
452
|
The build skips cleanly for any system library that's missing — useful
|
|
368
|
-
extensions still build, missing ones print a one-line notice.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
453
|
+
extensions still build, missing ones print a one-line notice. libjxl
|
|
454
|
+
0.11.2 is auto-built from source via `bench/build_libjxl.sh` and
|
|
455
|
+
cached under `~/Library/Caches/opencodecs/` (macOS) /
|
|
456
|
+
`~/.cache/opencodecs/` (Linux). See INSTALL.md for the rationale
|
|
457
|
+
(Homebrew/apt builds are 0.5-0.7× slower than a tuned `-O3 + LTO`
|
|
458
|
+
build).
|
|
374
459
|
|
|
375
460
|
## Status
|
|
376
461
|
|
|
377
|
-
- Core API stable; **1066 tests passing**
|
|
462
|
+
- **v0.1.1** on PyPI (May 2026). Core API stable; **1066 tests passing**
|
|
463
|
+
on Mac M1 Ultra + Linux x86_64/aarch64 + Windows VM
|
|
378
464
|
- Native readers + writers for the common scientific containers
|
|
379
|
-
(TIFF, BigTIFF, OME-TIFF, CZI, NDTiff, HDF5, JXL
|
|
465
|
+
(TIFF, BigTIFF, OME-TIFF, CZI, NDTiff, HDF5, JXL, FITS,
|
|
466
|
+
OME-Zarr v2 + v3 sharded)
|
|
380
467
|
- Cross-platform bench coverage: Mac arm64 (canonical), Windows 11 LTSC
|
|
381
|
-
(libvirt VM), Linux x86_64 (Threadripper)
|
|
468
|
+
(libvirt VM), Linux x86_64 (Threadripper-class)
|
|
382
469
|
- Compression backend auto-detect (libdeflate → zlib-ng-compat → stdlib)
|
|
383
|
-
- Cloud I/O primitives (`HTTPDataSource
|
|
384
|
-
|
|
470
|
+
- Cloud I/O primitives (`HTTPDataSource` with covering-cache + adaptive
|
|
471
|
+
read-ahead) wired into TIFF / HDF5 / DICOMweb / CZI / FITS / Zarr v3
|
|
472
|
+
readers
|
|
385
473
|
- `tifffile_patch` opt-in shim reroutes tifffile's codec dispatch through
|
|
386
474
|
opencodecs for users who want only a partial swap
|
|
387
475
|
|
|
388
476
|
Deferred work (see [`docs/TODO_DEFERRED.md`](docs/TODO_DEFERRED.md)):
|
|
389
477
|
|
|
390
|
-
-
|
|
391
|
-
|
|
478
|
+
- **Windows wheels currently miss `_sz3`, `_pcodec`, `_sperr`, `_brunsli`**
|
|
479
|
+
— toolchain mismatch (conda's bash picks GCC over MSVC for CMake);
|
|
480
|
+
v0.1.2 will restore them. macOS + Linux wheels have the full set.
|
|
392
481
|
- CCITT Fax3/Fax4 encode — legacy fax; zero scientific users
|
|
393
482
|
- JPEG-XR — abandoned format outside niche DICOM
|
|
394
483
|
- libspng `filter_sum` SIMD — off the bench-tracked workload (`h2h_png_4mp_rgb`
|
|
395
484
|
is at 1.14× already); filter-bound PNG-encode users could see another 2-3×
|
|
396
|
-
- Wheels / PyPI release — install from source for now
|
|
397
485
|
|
|
398
486
|
## License
|
|
399
487
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opencodecs"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -333,12 +333,12 @@ class NDTiffWriter:
|
|
|
333
333
|
self._cur_path = self._dir / self._stack_filename(self._stack_index)
|
|
334
334
|
flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC | getattr(os, "O_BINARY", 0)
|
|
335
335
|
self._fd = os.open(str(self._cur_path), flags, 0o644)
|
|
336
|
-
if sys.platform == "win32": # pragma: no cover - covered by
|
|
336
|
+
if sys.platform == "win32": # pragma: no cover - covered by windows-vm bench
|
|
337
337
|
# On Windows NTFS we *skip* the 4 GiB pre-extension
|
|
338
338
|
# entirely. Both ``os.ftruncate`` and ``lseek + write
|
|
339
339
|
# sentinel`` perturb the file's valid-data-length so that
|
|
340
340
|
# the close-time ``ftruncate(actual)`` measurably stalls
|
|
341
|
-
# (3.9 s -> 7.7 s bimodal in
|
|
341
|
+
# (3.9 s -> 7.7 s bimodal in windows-vm/qcow2). The on-demand
|
|
342
342
|
# extension path is fast and correct; we only lose the
|
|
343
343
|
# marginal contiguous-extent hint that pre-allocation
|
|
344
344
|
# would have given the filesystem.
|