opencodecs 0.7.0__tar.gz → 0.7.1__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.7.0 → opencodecs-0.7.1}/CHANGES.rst +79 -0
- {opencodecs-0.7.0/src/opencodecs.egg-info → opencodecs-0.7.1}/PKG-INFO +13 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/pyproject.toml +16 -2
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/__init__.py +1 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_czi_reader.py +4 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_czi_writer.py +68 -6
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jxl_codec.py +17 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_omezarr.py +4 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_omezarr_writer.py +19 -7
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/segment_compression.py +6 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1/src/opencodecs.egg-info}/PKG-INFO +13 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/SOURCES.txt +2 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/requires.txt +12 -0
- opencodecs-0.7.1/tests/test_czi_foreign_readers.py +163 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_pread_indices.py +3 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_read_dispatch.py +7 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_recompress.py +102 -1
- opencodecs-0.7.1/tests/test_foreign_readers.py +653 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_segment_encoder_binding.py +3 -1
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/VENDOR.toml +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bcdec/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bcdec/README.md +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bcdec/bcdec.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/README.rst +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle_core.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle_core.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/bitshuffle_internals.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/iochain.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/bitshuffle/iochain.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/License.txt +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fits_hcompress.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fits_hdecompress.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fits_hdecompress_api.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/fitsio2.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/pliocomp.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/ricecomp.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/cfitsio/ricecomp.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/libspng/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/libspng/spng.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/libspng/spng.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_eer/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_eer/oc_eer.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_eer/oc_eer.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_giflzw/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_giflzw/oc_giflzw.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_giflzw/oc_giflzw.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_tifflzw/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_tifflzw/oc_tifflzw.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/oc_tifflzw/oc_tifflzw.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/qoi/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/qoi/qoi.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/rgbe/rgbe.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/rgbe/rgbe.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/rgbe/rgbe.txt +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/3rdparty/vendor_hashes.json +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/LICENSE +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/MANIFEST.in +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/README.md +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/THIRD-PARTY.md +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/bench/build_libjxl.sh +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/patches/libjxl/README.md +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/setup.cfg +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/setup.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_aec_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_avif_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_avif_heif_params.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_b2nd_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bcn_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bitshuffle_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_blosc2_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bmp_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_brotli_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_brunsli_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_byteshuffle_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_bz2_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_cms_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_czi_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_deflate_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicom.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicom_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicom_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicomrle_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dicomweb.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dm.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_dm_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_eer_reader.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_emd.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_emd_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ets.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_filter_args.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_fits.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_fits_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_fits_compressed.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_gif_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_gzip_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_h5_common.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_hdf5_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_hdf5_http.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_heif_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_htj2k_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_htj2k_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_imaris.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg2k_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg2k_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpeg_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_jpegls_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lerc_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lif_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lif_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lz4_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_lzma_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mozjpeg_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mozjpeg_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mrc.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mrc_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_mrc_writer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_n5.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nd2_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nd2_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ndtiff.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ndtiff_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ndtiff_writer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nifti.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nifti_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nifti_writer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_none_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nrrd.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_nrrd_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_numpy_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_numpy_reader.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_oib_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_oib_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_oir_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ole2.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ome_xml.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_packints_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_pcodec_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_png_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_predictor_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_pyramid_build.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_qoi_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_quantize_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_rcomp_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_rgbe.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_rgbe_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_scaled_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_snappy_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_sperr_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_sz3_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_http.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_tiff_writer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_ultrahdr_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_vsi_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_vsi_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_webp_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_zarr_codecs.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_zfp_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/_zstd_codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/__init__.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_aec.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_avif.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_b2nd.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bcdec.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bitshuffle.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_blosc2.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bmp.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_brotli.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_brunsli.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_bytetools.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_charls.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_deflate.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_eer.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_gif.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_hcomp.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_heif.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_isal.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpeg.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpeg2k.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpeg_common.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jpegxr.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_jxl.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_lerc.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_lz4.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_mozjpeg.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_ndtiff.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_openjph.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_pcodec.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_plio.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_png.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_qoi.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_rcomp.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_registry.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_rgbe.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_snappy.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_sperr.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_sz3.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_tiff.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_uhdr.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_webp.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_zfp.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/_zstd.pyx +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/avif.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/b2nd.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/b2nd_helpers.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/b2nd_helpers.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/bcdec.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/bitshuffle.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/blosc2.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/brotli.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/brunsli.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/byteplanes.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/charls.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/eer.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/giflib.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/heif.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/heif_shim.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/isal_shim.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/jpegxr_shim.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/jpegxr_shim.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/lerc.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/libaec.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/libjxl.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/libuhdr.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/lz4.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/mozjpeg.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/oc_decoder_vtable.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjpeg.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjph.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjph_shim.cpp +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/openjph_shim.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/pcodec.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/qoi.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/rgbe.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/snappy.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/sperr.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/spng.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/sz3c.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/turbojpeg.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/webp.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/webp_shim.c +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/webp_shim.h +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/zfp.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/zlib_h.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/codecs/zstd.pxd +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/__init__.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_io_helpers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_optional_backend.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_range_index.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_sidecar_header.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/_write_helpers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/buffers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/checksums.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/codec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/color.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/errors.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/io.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/native_source.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/parallel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/pipeline.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/rows.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/scratch.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/streaming.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/tile_cache.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/core/verification.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/jxl.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/parallel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/tiff_reader.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/uhdr.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs/zarr.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/dependency_links.txt +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/src/opencodecs.egg-info/top_level.txt +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_auto_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_avif_heif_params.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_avif_sequence.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_b2nd_file_reader.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bcdec.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bcn_bands.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bcn_rounding.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bitshuffle_format.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_blosc2_partial.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_blosc2_threads.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bmp_headers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bounded_pipeline.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_brunsli.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_byteplanes.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_bytestream_standards.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_capability_manifest.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_charls.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_chunked_means_cheap.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_codec_registry.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_container_pipeline.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_container_streaming.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_core_codec_api.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_codec_decode.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_manifest.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_native_fixtures.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_other.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_png.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_tiff.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_corpus_vendors.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_final.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_finishing.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_last.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_more.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_coverage_push.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_bounded_iteration.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_direct_decode.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_fixture.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_mixed_subblocks.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_parallel_write.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_read_out.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_region_index.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_region_windows.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_segment_parts.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_slide_geometry.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_verification.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_write_pipeline.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_writer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_czi_writer_shuffle.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_data_source.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_decode_releases_gil.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_deflate.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_delta_parity.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_encodings.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_parallel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_slide_selection.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicom_wsi_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dicomweb.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_direct_filter_outputs.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dm.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dm_offsets.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_dm_synthetic.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_edge_cases.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_eer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_eer_parallel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_eer_worker_budget.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_emd.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_encode_comprehensive.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_error_handling.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_fd_writes.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_filter_compat.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_filter_scratch.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_fits.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_fits_parallel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_gif.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_gif_compositing.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_gzip_presized.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_http.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_local.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_parallel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_hdf5_remote.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_heif_avif_features.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_heif_multi_image.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_htj2k_compat.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_htj2k_conformance.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_http_byte_savings.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_http_prefetch.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_http_unified.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_imaris.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_incremental_byte_codecs.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_io_chunked.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_isal.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpeg.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpeg2k_compat.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpeg2k_region.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jpegxr.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_jxl.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_lerc.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_lif_indexing.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_lif_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_live_archives.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mozjpeg.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mrc.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mrc_mrcz_http.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_mrc_nifti_writers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_n5.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_native_output_sinks.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_native_parity.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_native_source_callbacks.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nd2_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_ndtiff.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nifti.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nrrd.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nrrd_iteration.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_nrrd_slices.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_numpy_codec_format.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_offset_readers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_oib_indexing.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_oib_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_ome_xml.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_omezarr.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_omezarr_sharded_range.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_open_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_openjph.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_optional_backend.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_owned_color_context.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_owned_image_decoders.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_parallel_jxl.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase4_filters.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase5_icc.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase6_codecs.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_phase7_cms.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pipeline_budget.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pipeline_catalog.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pipeline_executor.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_platform_branches.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_png_row_sessions.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_png_standard.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_public_output_buffers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_pyramid_batched_regions.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_qoi_standard.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_range_pipeline_completion.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_rcomp_truncated_input.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_read_file_into.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reader_contract.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reader_source_kinds.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reader_streaming.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_reference_parity.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_rgbe.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_rgbe_toplevel.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_scaled_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_shared_io_regressions.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_sidecar_header.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_snappy.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_source_pool_lifetime.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_streaming_means_streaming.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_threading_policy.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tier1_scientific.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_codec_encode.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_fused_decode.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_http.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_native.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_parallel_decode.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_pipeline.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_predictor2_interop.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_predictor_loops.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_read_path.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_reader.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_spool.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_stack_read.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_workers.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_writer.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_writer_float_predictor.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tiff_writer_verification.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_tile_cache.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_uhdr.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_unified_api.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_url_sources.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_vendor_provenance.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_verified_encoding.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_version_consistency.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_vsi_oir.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_vsi_pyramid.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_webp_animation.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_webp_decode_options.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_webp_encode_standard.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_wrapper_streams.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_writer_contract.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_writer_pipeline_completion.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_writer_verification.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zarr_adapters.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zarr_region_fast_path.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zfp_blocks.py +0 -0
- {opencodecs-0.7.0 → opencodecs-0.7.1}/tests/test_zstd_owned_output.py +0 -0
|
@@ -10,6 +10,85 @@ 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.7.1 (unreleased)
|
|
14
|
+
------------------
|
|
15
|
+
|
|
16
|
+
Every encoder is now tested against a decoder that is not ours.
|
|
17
|
+
``tests/test_foreign_readers.py`` encodes with each of the 46 codecs that can
|
|
18
|
+
encode and decodes with the format's own library where it has a Python
|
|
19
|
+
binding (zstandard, lz4, brotli, python-blosc2, cramjam, lerc, zfpy, pcodec,
|
|
20
|
+
qoi, pillow-heif, the standard library), imagecodecs, and format readers
|
|
21
|
+
(tifffile, mrcfile, nibabel, pydicom, numcodecs, numpy); it also opens the
|
|
22
|
+
files every writer produces (TIFF with each compression, pyramids, NDTiff,
|
|
23
|
+
OME-Zarr, MRC, NIfTI, RGBE, JPEG XL animation, CZI pyramids). A codec that
|
|
24
|
+
gains an encoder without a case fails the suite. CI installs these readers.
|
|
25
|
+
It found:
|
|
26
|
+
|
|
27
|
+
- Fix: the JPEG XL encoder refused ``level``, so ``TiffWriter`` and the NDTiff
|
|
28
|
+
writer raised for JPEG XL with ``compression_level``, which their
|
|
29
|
+
documentation promised to pass through. ``level`` now means what it means
|
|
30
|
+
in ``imagecodecs.jpegxl_encode``: 100 or below is lossy at that quality,
|
|
31
|
+
above 100 lossless. Passing both ``level`` and ``quality`` raises.
|
|
32
|
+
- Fix: OME-Zarr stores written with ``compressor="blosc2"`` opened only in
|
|
33
|
+
opencodecs. No zarr specification defines Blosc2; the one numcodecs plugin
|
|
34
|
+
for it, ocf-blosc2 (Open Climate Fix), registers the id ``blosc2`` with
|
|
35
|
+
the parameters ``cname`` and ``clevel``, where the writer recorded
|
|
36
|
+
``level``, which that plugin refuses. v2 stores now record ``cname`` and
|
|
37
|
+
``clevel``, and v3 stores name the codec ``numcodecs.blosc2``, as zarr
|
|
38
|
+
names a numcodecs codec. With ocf-blosc2 installed, zarr opens both and
|
|
39
|
+
reads the pixels exactly. The chunks themselves are unchanged, and stores
|
|
40
|
+
written the old way still read here.
|
|
41
|
+
- Fix: on Windows, which has no ``os.preadv``, the CZI reader maps the file,
|
|
42
|
+
and ``close()`` left the closed mapping in place: the next call raised the
|
|
43
|
+
mapping's ``ValueError: mmap closed or invalid`` instead of the reader's
|
|
44
|
+
own closed-reader ``CziError``. Two tests encoded the positional-read path
|
|
45
|
+
as universal (the payload index is kept on every path; a stored stack is
|
|
46
|
+
one copy out of the mapping on one worker there) and now assert each
|
|
47
|
+
path's own behavior.
|
|
48
|
+
|
|
49
|
+
- Fix: a CZI this library wrote with default metadata could not be opened by
|
|
50
|
+
libCZI, so not by czicompress and not by ZEN. ``CziWriter``'s default
|
|
51
|
+
metadata was a bare ``<Metadata/>``, and libCZI requires the document to be
|
|
52
|
+
rooted at ``ImageDocument``; it refuses anything else outright with
|
|
53
|
+
``Root-node "ImageDocument" not found``. Every round-trip test in this
|
|
54
|
+
suite passed because every one of them read the file back with our own
|
|
55
|
+
reader, which accepts it. Metadata lacking the root is now wrapped in one,
|
|
56
|
+
so a caller writing a new file cannot produce an unopenable one.
|
|
57
|
+
``czi_recompress`` passes ``metadata_as_is=True``: it promises the source's
|
|
58
|
+
metadata is carried across unchanged, and rewriting it there would break
|
|
59
|
+
that promise, while silently "fixing" a source libCZI could not open either
|
|
60
|
+
is a surprise rather than a service. Its output is then exactly as openable
|
|
61
|
+
as its input.
|
|
62
|
+
- ``tests/test_czi_foreign_readers.py`` never uses opencodecs to read. It
|
|
63
|
+
writes with opencodecs and opens with pylibCZIrw (libCZI itself, so what
|
|
64
|
+
ZEN and czicompress see), czifile (an independent implementation) and
|
|
65
|
+
aicspylibczi, skipping any reader that is absent rather than passing
|
|
66
|
+
silently. It covers all three compressions, a foreign decoder reading
|
|
67
|
+
pixels back exactly, both metadata regressions, a recompressed real file,
|
|
68
|
+
and 481 JPEG XR sub-blocks carried across. Its header records what to
|
|
69
|
+
assume when one fails: the writer is wrong, not the reader, because all
|
|
70
|
+
three open ZEN files and we are the new participant in a format we did not
|
|
71
|
+
define.
|
|
72
|
+
- ``tests/_czi_fixture.py`` takes the same default. That serializer is an
|
|
73
|
+
independent reimplementation of the layout, and
|
|
74
|
+
``test_file_bytes_match_the_fixture_serializer`` compares the writer's bytes
|
|
75
|
+
against it, so the two have to agree on the default; it is written as a
|
|
76
|
+
literal on both sides rather than imported, because importing would make
|
|
77
|
+
them agree by construction and hollow the test out. Fixture files are also
|
|
78
|
+
the sources for the ``czi_recompress`` tests, so that corpus now produces
|
|
79
|
+
files libCZI accepts.
|
|
80
|
+
- Documented why the ZSTDHDR zstd level is 3 and not a parameter. Measured end
|
|
81
|
+
to end on a 503 MB uncompressed CZI, against ZEISS czicompress at 0.87 s and
|
|
82
|
+
319.9 MB on the same file: level 3 gives 0.24 s and 318.3 MB, level 6 gives
|
|
83
|
+
0.92 s and 315.3 MB, level 9 gives 1.38 s and 314.4 MB. The encode cost
|
|
84
|
+
transfers almost 1:1 into wall clock because this path is encode-bound
|
|
85
|
+
rather than I/O-bound, so 32 workers hide none of it, and level 6 is already
|
|
86
|
+
slower than the tool it replaces. About 1% of size for 4 to 6x the time is
|
|
87
|
+
not a trade worth a default. The hi-lo byte-plane shuffle is where the gain
|
|
88
|
+
actually is, and it is already on: on real microscopy pixels it takes the
|
|
89
|
+
ratio from 0.537 to 0.485 while being faster than raw zstd, 923 against
|
|
90
|
+
199 MB/s.
|
|
91
|
+
|
|
13
92
|
0.7.0 (2026-10-03)
|
|
14
93
|
------------------
|
|
15
94
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencodecs
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)
|
|
5
5
|
Author: Kevin Cutler
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -39,6 +39,18 @@ Requires-Dist: nd2; extra == "reference"
|
|
|
39
39
|
Requires-Dist: readlif; extra == "reference"
|
|
40
40
|
Requires-Dist: oiffile; extra == "reference"
|
|
41
41
|
Requires-Dist: blosc2; extra == "reference"
|
|
42
|
+
Requires-Dist: zstandard; extra == "reference"
|
|
43
|
+
Requires-Dist: lz4; extra == "reference"
|
|
44
|
+
Requires-Dist: brotli; extra == "reference"
|
|
45
|
+
Requires-Dist: cramjam; extra == "reference"
|
|
46
|
+
Requires-Dist: lerc; extra == "reference"
|
|
47
|
+
Requires-Dist: zfpy; extra == "reference"
|
|
48
|
+
Requires-Dist: pcodec; extra == "reference"
|
|
49
|
+
Requires-Dist: ocf-blosc2; extra == "reference"
|
|
50
|
+
Requires-Dist: qoi; extra == "reference"
|
|
51
|
+
Requires-Dist: pillow-heif; extra == "reference"
|
|
52
|
+
Requires-Dist: pylibCZIrw; extra == "reference"
|
|
53
|
+
Requires-Dist: aicspylibczi; extra == "reference"
|
|
42
54
|
Provides-Extra: test
|
|
43
55
|
Requires-Dist: pytest>=8; extra == "test"
|
|
44
56
|
Requires-Dist: pytest-cov; extra == "test"
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "opencodecs"
|
|
10
|
-
version = "0.7.
|
|
10
|
+
version = "0.7.1"
|
|
11
11
|
description = "Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.10"
|
|
@@ -54,7 +54,21 @@ reference = [
|
|
|
54
54
|
"nd2", # ND2
|
|
55
55
|
"readlif", # LIF
|
|
56
56
|
"oiffile", # OIB
|
|
57
|
-
"blosc2", # MRCZ
|
|
57
|
+
"blosc2", # MRCZ, and Blosc2 / B2ND streams
|
|
58
|
+
# The libraries' own bindings, which test_foreign_readers.py decodes
|
|
59
|
+
# our streams with alongside imagecodecs.
|
|
60
|
+
"zstandard", # zstd
|
|
61
|
+
"lz4", # LZ4 frame
|
|
62
|
+
"brotli", # Brotli
|
|
63
|
+
"cramjam", # Snappy raw and framed
|
|
64
|
+
"lerc", # LERC
|
|
65
|
+
"zfpy", # ZFP
|
|
66
|
+
"pcodec", # pcodec
|
|
67
|
+
"ocf-blosc2", # Blosc2 in zarr (no zarr spec defines it)
|
|
68
|
+
"qoi", # QOI
|
|
69
|
+
"pillow-heif", # HEIF, libheif's decoder
|
|
70
|
+
"pylibCZIrw", # CZI through libCZI
|
|
71
|
+
"aicspylibczi", # CZI, the Allen Institute's libCZI binding
|
|
58
72
|
]
|
|
59
73
|
test = [
|
|
60
74
|
"pytest>=8",
|
|
@@ -813,6 +813,10 @@ class CziReader(Reader):
|
|
|
813
813
|
finally:
|
|
814
814
|
os.close(self._fd)
|
|
815
815
|
self._owns_fd = False
|
|
816
|
+
# As on the positional-read path: a closed reader has no
|
|
817
|
+
# buffer, so the next call raises the reader's own "closed"
|
|
818
|
+
# error, not the mapping's "mmap closed or invalid".
|
|
819
|
+
self._mmap = None
|
|
816
820
|
|
|
817
821
|
@classmethod
|
|
818
822
|
def from_http(
|
|
@@ -70,6 +70,39 @@ _CMP_NAME_TO_CODE = {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
#: libCZI refuses metadata that is not rooted at ``ImageDocument``, with
|
|
74
|
+
#: ``Root-node "ImageDocument" not found``. That means czicompress and ZEN
|
|
75
|
+
#: cannot open the file at all -- not a degraded read, no read. The writer's
|
|
76
|
+
#: old default was a bare ``<Metadata/>``, which opencodecs' own reader
|
|
77
|
+
#: accepts, so every round-trip test passed while the files were unreadable
|
|
78
|
+
#: by every other CZI tool. Found 2026-10-04 when czicompress rejected a file
|
|
79
|
+
#: this writer had just produced.
|
|
80
|
+
#:
|
|
81
|
+
#: So metadata lacking the root is WRAPPED in one, and a caller writing a NEW
|
|
82
|
+
#: file cannot produce an unopenable one by accident or on purpose.
|
|
83
|
+
#:
|
|
84
|
+
#: ``metadata_as_is=True`` opts out, and exactly one caller needs it:
|
|
85
|
+
#: ``czi_recompress``, which promises the source's metadata is carried across
|
|
86
|
+
#: UNCHANGED. Rewriting it there would break that promise, and silently
|
|
87
|
+
#: "fixing" a source that libCZI could not open either is a surprise, not a
|
|
88
|
+
#: service -- the output is then exactly as openable as the input, which is
|
|
89
|
+
#: what faithful means.
|
|
90
|
+
_DEFAULT_METADATA = b"<ImageDocument><Metadata/></ImageDocument>"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _ensure_image_document(xml: bytes) -> bytes:
|
|
94
|
+
"""Return ``xml`` rooted at ImageDocument, wrapping it if it is not."""
|
|
95
|
+
head = xml.lstrip()
|
|
96
|
+
decl = b""
|
|
97
|
+
if head.startswith(b"<?xml"):
|
|
98
|
+
end = head.find(b"?>")
|
|
99
|
+
if end != -1:
|
|
100
|
+
decl, head = head[:end + 2], head[end + 2:].lstrip()
|
|
101
|
+
if head.startswith(b"<ImageDocument>") or head.startswith(b"<ImageDocument "):
|
|
102
|
+
return xml
|
|
103
|
+
return decl + b"<ImageDocument>" + head + b"</ImageDocument>"
|
|
104
|
+
|
|
105
|
+
|
|
73
106
|
class CziWriterError(RuntimeError):
|
|
74
107
|
"""Raised on writer state-machine violations."""
|
|
75
108
|
|
|
@@ -263,6 +296,27 @@ def _zstdhdr_encode(pixel_bytes, itemsize: int, hilo: bool, *, scratch=None):
|
|
|
263
296
|
before this returns, and verification later reads the encoded payload
|
|
264
297
|
and the caller's own pixel snapshot rather than this buffer, so the
|
|
265
298
|
next frame may reuse it.
|
|
299
|
+
|
|
300
|
+
WHY LEVEL 3, AND WHY IT IS NOT A PARAMETER
|
|
301
|
+
Measured end to end through this pipeline on a 503 MB uncompressed CZI
|
|
302
|
+
(2026-10-04), against ZEISS czicompress at 0.87 s / 319.9 MB on the same
|
|
303
|
+
file:
|
|
304
|
+
|
|
305
|
+
level 3 0.24 s 318.3 MB beats czicompress on size AND speed
|
|
306
|
+
level 6 0.92 s 315.3 MB 0.93% smaller, 3.91x slower
|
|
307
|
+
level 9 1.38 s 314.4 MB 1.23% smaller, 5.83x slower
|
|
308
|
+
|
|
309
|
+
The encode cost transfers almost 1:1 into wall clock, because this path is
|
|
310
|
+
encode-bound rather than I/O-bound: there is no I/O to hide it behind, even
|
|
311
|
+
with 32 workers. So a higher level buys about 1% of size for 4-6x the time,
|
|
312
|
+
and level 6 is already slower than the tool we replaced. Level 3 is the
|
|
313
|
+
only setting that wins on both axes.
|
|
314
|
+
|
|
315
|
+
The hi-lo shuffle, by contrast, is free and large: on real microscopy
|
|
316
|
+
pixels it took the ratio from 0.537 to 0.485 (9.7% smaller) while being
|
|
317
|
+
FASTER than raw zstd, 923 MB/s against 199 MB/s, because shuffled byte
|
|
318
|
+
planes are far more compressible. That is where the gain is, and it is
|
|
319
|
+
already on by default.
|
|
266
320
|
"""
|
|
267
321
|
from .codecs._zstd import encode as zstd_encode
|
|
268
322
|
if hilo and itemsize > 1:
|
|
@@ -519,15 +573,22 @@ class _CziStreamWriter(Writer):
|
|
|
519
573
|
"""Shared sub-block sink with optional bounded pixel verification."""
|
|
520
574
|
|
|
521
575
|
def __init__(self, path: str | Path, *, compression: str = "none",
|
|
522
|
-
hilo: bool = True, metadata_xml: bytes | str =
|
|
523
|
-
verify: bool = False, background_encode: bool = False
|
|
576
|
+
hilo: bool = True, metadata_xml: bytes | str | None = None,
|
|
577
|
+
verify: bool = False, background_encode: bool = False,
|
|
578
|
+
metadata_as_is: bool = False):
|
|
524
579
|
if compression not in _CMP_NAME_TO_CODE:
|
|
525
580
|
raise CziWriterError(f"unknown compression {compression!r}")
|
|
526
581
|
self._path = Path(path)
|
|
527
582
|
self._cmp_code = _CMP_NAME_TO_CODE[compression]
|
|
528
583
|
self._hilo = bool(hilo)
|
|
529
|
-
|
|
530
|
-
|
|
584
|
+
if metadata_xml is None:
|
|
585
|
+
xml = _DEFAULT_METADATA
|
|
586
|
+
elif isinstance(metadata_xml, str):
|
|
587
|
+
xml = metadata_xml.encode("utf-8")
|
|
588
|
+
else:
|
|
589
|
+
xml = metadata_xml
|
|
590
|
+
self._metadata_segment = _build_metadata_segment(
|
|
591
|
+
xml if metadata_as_is else _ensure_image_document(xml))
|
|
531
592
|
self._header_size = (32 + 88 + 31) // 32 * 32
|
|
532
593
|
self._position = self._header_size + len(self._metadata_segment)
|
|
533
594
|
self._entries: list[dict] = []
|
|
@@ -1119,7 +1180,7 @@ def czi_recompress(src, dst, *, compression: str = "zstdhdr", hilo: bool = True,
|
|
|
1119
1180
|
n = len(reader.entries)
|
|
1120
1181
|
if not n:
|
|
1121
1182
|
raise CziWriterError(f"czi: {src} has no sub-blocks to recompress")
|
|
1122
|
-
metadata = reader.metadata_bytes or
|
|
1183
|
+
metadata = reader.metadata_bytes or _DEFAULT_METADATA
|
|
1123
1184
|
|
|
1124
1185
|
counts = {"recompressed": 0, "carried": 0}
|
|
1125
1186
|
|
|
@@ -1160,7 +1221,8 @@ def czi_recompress(src, dst, *, compression: str = "zstdhdr", hilo: bool = True,
|
|
|
1160
1221
|
try:
|
|
1161
1222
|
with CziWriter(str(dst), compression=compression, hilo=hilo,
|
|
1162
1223
|
metadata_xml=metadata, verify=verify,
|
|
1163
|
-
background_encode=background_encode
|
|
1224
|
+
background_encode=background_encode,
|
|
1225
|
+
metadata_as_is=True) as writer:
|
|
1164
1226
|
writer.write_many(frames(), workers=workers, copy_frames=False)
|
|
1165
1227
|
except BaseException:
|
|
1166
1228
|
# An interrupted rewrite must not leave a file behind. A CZI
|
|
@@ -139,6 +139,22 @@ class JpegXLReader(Reader):
|
|
|
139
139
|
self._inner.close()
|
|
140
140
|
|
|
141
141
|
|
|
142
|
+
def jxl_level_as_quality(opts: dict) -> dict:
|
|
143
|
+
"""imagecodecs.jpegxl_encode's ``level`` is a quality: 100 or below is
|
|
144
|
+
lossy at that quality, above 100 lossless. The encoder's ``quality``
|
|
145
|
+
has exactly that meaning, so ``level`` is passed as ``quality``.
|
|
146
|
+
Writers that take one compression level for every codec (TIFF,
|
|
147
|
+
NDTiff) reach JPEG XL through this too."""
|
|
148
|
+
if opts.get("level") is None:
|
|
149
|
+
opts.pop("level", None)
|
|
150
|
+
return opts
|
|
151
|
+
if opts.get("quality") is not None:
|
|
152
|
+
raise TypeError("jxl: pass level or quality, not both")
|
|
153
|
+
opts = dict(opts)
|
|
154
|
+
opts["quality"] = opts.pop("level")
|
|
155
|
+
return opts
|
|
156
|
+
|
|
157
|
+
|
|
142
158
|
class JpegXLCodec(Codec):
|
|
143
159
|
"""Native JPEG XL codec (libjxl 0.11).
|
|
144
160
|
|
|
@@ -176,7 +192,7 @@ class JpegXLCodec(Codec):
|
|
|
176
192
|
return _jxl_check_signature(head)
|
|
177
193
|
|
|
178
194
|
def encode(self, arr: np.ndarray, *, dest=None, **opts) -> bytes | None:
|
|
179
|
-
return _jxl_encode(arr, dest=dest, **opts)
|
|
195
|
+
return _jxl_encode(arr, dest=dest, **jxl_level_as_quality(opts))
|
|
180
196
|
|
|
181
197
|
def decode(self, src: Any, **opts) -> np.ndarray:
|
|
182
198
|
"""Decode a JXL codestream.
|
|
@@ -496,7 +496,10 @@ def _run_decoder(name: str, cfg: dict, data: bytes) -> bytes:
|
|
|
496
496
|
_DECODER_CACHE["blosc2"] = fn
|
|
497
497
|
if fn:
|
|
498
498
|
return fn(data)
|
|
499
|
-
if name
|
|
499
|
+
if name in ("blosc2", "numcodecs.blosc2"):
|
|
500
|
+
# "numcodecs.blosc2" is ocf-blosc2's v3 name, which our writer
|
|
501
|
+
# uses; plain "blosc2" is the v2 id, and what v3 stores this
|
|
502
|
+
# package wrote before 0.7.0 carry.
|
|
500
503
|
fn = _DECODER_CACHE.get("blosc2")
|
|
501
504
|
if not fn:
|
|
502
505
|
from .codecs._blosc2 import decode as _b_decode
|
|
@@ -53,7 +53,10 @@ _CODEC_NAME_MAP = {
|
|
|
53
53
|
"none": (None, None),
|
|
54
54
|
"raw": (None, None),
|
|
55
55
|
"zstd": ("zstd", "zstd"),
|
|
56
|
-
|
|
56
|
+
# No zarr specification defines Blosc2. These are the names ocf-blosc2,
|
|
57
|
+
# the one numcodecs plugin for it, registers: "blosc2" for v2, and
|
|
58
|
+
# "numcodecs.blosc2" for v3, which is how zarr names a numcodecs codec.
|
|
59
|
+
"blosc2": ("blosc2", "numcodecs.blosc2"),
|
|
57
60
|
"gzip": ("gzip", "gzip"),
|
|
58
61
|
"blosc": ("blosc", None), # v2-only (no v3 spec)
|
|
59
62
|
}
|
|
@@ -68,6 +71,16 @@ class OmeZarrWriterError(RuntimeError):
|
|
|
68
71
|
# ---------------------------------------------------------------------------
|
|
69
72
|
|
|
70
73
|
|
|
74
|
+
def _blosc2_config(level: int | None, v3: bool = False) -> dict:
|
|
75
|
+
"""The parameters ocf-blosc2's Blosc2(cname, clevel) takes, so zarr with
|
|
76
|
+
that plugin installed opens the store. cname names the inner codec;
|
|
77
|
+
_encode_chunk compresses with zstd, blosc2's default, and ocf-blosc2
|
|
78
|
+
reads "zstd" as that. (Before 0.7.0 the writer recorded "level", which
|
|
79
|
+
no reader but opencodecs accepted; those stores still read here.)"""
|
|
80
|
+
config = {"cname": "zstd", "clevel": level if level is not None else 5}
|
|
81
|
+
return config if v3 else {"id": "blosc2", **config}
|
|
82
|
+
|
|
83
|
+
|
|
71
84
|
def _encode_chunk(raw: bytes, codec: str, level: int | None) -> bytes:
|
|
72
85
|
"""Compress one chunk's bytes."""
|
|
73
86
|
if codec in (None, "none", "raw"):
|
|
@@ -309,8 +322,7 @@ def _write_v2(
|
|
|
309
322
|
metadata["compressor"] = {"id": "zstd",
|
|
310
323
|
"level": level if level is not None else 3}
|
|
311
324
|
elif compressor == "blosc2":
|
|
312
|
-
metadata["compressor"] =
|
|
313
|
-
"level": level if level is not None else 5}
|
|
325
|
+
metadata["compressor"] = _blosc2_config(level)
|
|
314
326
|
elif compressor == "gzip":
|
|
315
327
|
metadata["compressor"] = {"id": "gzip",
|
|
316
328
|
"level": level if level is not None else 6}
|
|
@@ -375,8 +387,8 @@ def _write_v3(
|
|
|
375
387
|
})
|
|
376
388
|
elif compressor == "blosc2":
|
|
377
389
|
codecs.append({
|
|
378
|
-
"name": "blosc2",
|
|
379
|
-
"configuration":
|
|
390
|
+
"name": "numcodecs.blosc2",
|
|
391
|
+
"configuration": _blosc2_config(level, v3=True),
|
|
380
392
|
})
|
|
381
393
|
elif compressor == "gzip":
|
|
382
394
|
codecs.append({
|
|
@@ -460,8 +472,8 @@ def _v3_codecs_for_shard_inner(
|
|
|
460
472
|
})
|
|
461
473
|
elif compressor == "blosc2":
|
|
462
474
|
codecs.append({
|
|
463
|
-
"name": "blosc2",
|
|
464
|
-
"configuration":
|
|
475
|
+
"name": "numcodecs.blosc2",
|
|
476
|
+
"configuration": _blosc2_config(level, v3=True),
|
|
465
477
|
})
|
|
466
478
|
elif compressor == "gzip":
|
|
467
479
|
codecs.append({
|
|
@@ -254,6 +254,9 @@ def encode_segment(data, codec: str | int, *, level: int | None = None,
|
|
|
254
254
|
kw["level"] = level
|
|
255
255
|
if code == JPEG2000:
|
|
256
256
|
kw.setdefault("planar", False)
|
|
257
|
+
if code == JXL:
|
|
258
|
+
from .._jxl_codec import jxl_level_as_quality
|
|
259
|
+
kw = jxl_level_as_quality(kw)
|
|
257
260
|
if code in (ZSTD, JPEG2000, JXL):
|
|
258
261
|
from .pipeline import native_workers, in_worker
|
|
259
262
|
# Zstandard counts background workers: zero means inline serial.
|
|
@@ -292,6 +295,9 @@ def bind_segment_encoder(codec: str | int, *, level=None, owned_output=False,
|
|
|
292
295
|
options.setdefault("level", level)
|
|
293
296
|
if code == JPEG2000:
|
|
294
297
|
options.setdefault("planar", False)
|
|
298
|
+
if code == JXL:
|
|
299
|
+
from .._jxl_codec import jxl_level_as_quality
|
|
300
|
+
options = jxl_level_as_quality(options)
|
|
295
301
|
eager = partial(fn, **options)
|
|
296
302
|
if code not in (ZSTD, JPEG2000, JXL):
|
|
297
303
|
return eager
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencodecs
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Streaming, network-aware image codecs for scientific imaging (prototype: JPEG XL)
|
|
5
5
|
Author: Kevin Cutler
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -39,6 +39,18 @@ Requires-Dist: nd2; extra == "reference"
|
|
|
39
39
|
Requires-Dist: readlif; extra == "reference"
|
|
40
40
|
Requires-Dist: oiffile; extra == "reference"
|
|
41
41
|
Requires-Dist: blosc2; extra == "reference"
|
|
42
|
+
Requires-Dist: zstandard; extra == "reference"
|
|
43
|
+
Requires-Dist: lz4; extra == "reference"
|
|
44
|
+
Requires-Dist: brotli; extra == "reference"
|
|
45
|
+
Requires-Dist: cramjam; extra == "reference"
|
|
46
|
+
Requires-Dist: lerc; extra == "reference"
|
|
47
|
+
Requires-Dist: zfpy; extra == "reference"
|
|
48
|
+
Requires-Dist: pcodec; extra == "reference"
|
|
49
|
+
Requires-Dist: ocf-blosc2; extra == "reference"
|
|
50
|
+
Requires-Dist: qoi; extra == "reference"
|
|
51
|
+
Requires-Dist: pillow-heif; extra == "reference"
|
|
52
|
+
Requires-Dist: pylibCZIrw; extra == "reference"
|
|
53
|
+
Requires-Dist: aicspylibczi; extra == "reference"
|
|
42
54
|
Provides-Extra: test
|
|
43
55
|
Requires-Dist: pytest>=8; extra == "test"
|
|
44
56
|
Requires-Dist: pytest-cov; extra == "test"
|
|
@@ -306,6 +306,7 @@ tests/test_coverage_push.py
|
|
|
306
306
|
tests/test_czi_bounded_iteration.py
|
|
307
307
|
tests/test_czi_direct_decode.py
|
|
308
308
|
tests/test_czi_fixture.py
|
|
309
|
+
tests/test_czi_foreign_readers.py
|
|
309
310
|
tests/test_czi_mixed_subblocks.py
|
|
310
311
|
tests/test_czi_parallel_write.py
|
|
311
312
|
tests/test_czi_pread_indices.py
|
|
@@ -347,6 +348,7 @@ tests/test_filter_compat.py
|
|
|
347
348
|
tests/test_filter_scratch.py
|
|
348
349
|
tests/test_fits.py
|
|
349
350
|
tests/test_fits_parallel.py
|
|
351
|
+
tests/test_foreign_readers.py
|
|
350
352
|
tests/test_gif.py
|
|
351
353
|
tests/test_gif_compositing.py
|
|
352
354
|
tests/test_gzip_presized.py
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Every CZI opencodecs writes must open in the OTHER readers, not just ours.
|
|
2
|
+
|
|
3
|
+
WHY THIS FILE EXISTS
|
|
4
|
+
--------------------
|
|
5
|
+
On 2026-10-04 czicompress refused a file this writer had just produced:
|
|
6
|
+
|
|
7
|
+
An unrecoverable runtime error occurred: Root-node "ImageDocument" not found.
|
|
8
|
+
|
|
9
|
+
The writer's default metadata was a bare ``<Metadata/>``. opencodecs' own
|
|
10
|
+
reader accepts that, so every round-trip test in the suite passed while the
|
|
11
|
+
files were unopenable by libCZI, czicompress and ZEN -- which is to say, by
|
|
12
|
+
everything a user would actually hand the file to. The bug was not in any
|
|
13
|
+
code path; it was in the shape of the testing. A suite that only reads its
|
|
14
|
+
own output cannot find it.
|
|
15
|
+
|
|
16
|
+
So these tests never use opencodecs to read. They write with opencodecs and
|
|
17
|
+
open with:
|
|
18
|
+
|
|
19
|
+
pylibCZIrw the libCZI binding, i.e. what ZEN and czicompress use
|
|
20
|
+
czifile Christoph Gohlke's independent pure-Python reader
|
|
21
|
+
aicspylibczi the Allen Institute's libCZI binding
|
|
22
|
+
|
|
23
|
+
Any reader that is not installed is skipped rather than silently passed over,
|
|
24
|
+
so a thinned environment shows up as skips instead of false confidence.
|
|
25
|
+
|
|
26
|
+
WHAT TO DO WHEN ONE OF THESE FAILS
|
|
27
|
+
----------------------------------
|
|
28
|
+
Assume the writer is wrong, not the reader. All three of these open files from
|
|
29
|
+
ZEN; we are the new participant in a format we did not define. A disagreement
|
|
30
|
+
between our reader and theirs means our reader is probably lenient in the same
|
|
31
|
+
way the writer is wrong, which is exactly how the metadata bug survived.
|
|
32
|
+
"""
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import pathlib
|
|
36
|
+
import sys
|
|
37
|
+
|
|
38
|
+
import numpy as np
|
|
39
|
+
import pytest
|
|
40
|
+
|
|
41
|
+
sys.path.insert(0, str(pathlib.Path(__file__).parent))
|
|
42
|
+
|
|
43
|
+
from opencodecs import CziWriter, czi_recompress # noqa: E402
|
|
44
|
+
|
|
45
|
+
CORPUS = pathlib.Path(__file__).resolve().parents[1] / ".test_data" / "czi"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _write(path, *, n=3, side=64, compression="none", **kw):
|
|
49
|
+
"""A small multi-channel CZI, written by opencodecs."""
|
|
50
|
+
rs = np.random.RandomState(5)
|
|
51
|
+
with CziWriter(str(path), compression=compression, **kw) as w:
|
|
52
|
+
for c in range(n):
|
|
53
|
+
w.write_frame(rs.randint(0, 4096, (side, side)).astype("uint16"),
|
|
54
|
+
dims=[("X", 0, side, side), ("Y", 0, side, side),
|
|
55
|
+
("C", c, 1, 1)])
|
|
56
|
+
return path
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# --------------------------------------------------------------- pylibCZIrw
|
|
60
|
+
# The one that matters most: it is libCZI, so it is what ZEN and czicompress
|
|
61
|
+
# see. A file this reader rejects is a file our users cannot open.
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize("compression", ["none", "zstd", "zstdhdr"])
|
|
64
|
+
def test_pylibczirw_opens_what_we_write(tmp_path, compression):
|
|
65
|
+
czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
|
|
66
|
+
src = _write(tmp_path / f"{compression}.czi", compression=compression)
|
|
67
|
+
with czirw.open_czi(str(src)) as r:
|
|
68
|
+
plane = r.read(plane={"C": 0})
|
|
69
|
+
assert plane is not None and plane.size > 0
|
|
70
|
+
assert r.total_bounding_rectangle[2:] == (64, 64)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_pylibczirw_reads_our_pixels_back_exactly(tmp_path):
|
|
74
|
+
"""Lossless must mean lossless to a FOREIGN decoder, not only to ours."""
|
|
75
|
+
czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
|
|
76
|
+
rs = np.random.RandomState(9)
|
|
77
|
+
frame = rs.randint(0, 4096, (64, 64)).astype("uint16")
|
|
78
|
+
out = tmp_path / "exact.czi"
|
|
79
|
+
with CziWriter(str(out), compression="zstdhdr") as w:
|
|
80
|
+
w.write_frame(frame, dims=[("X", 0, 64, 64), ("Y", 0, 64, 64),
|
|
81
|
+
("C", 0, 1, 1)])
|
|
82
|
+
with czirw.open_czi(str(out)) as r:
|
|
83
|
+
got = np.squeeze(r.read(plane={"C": 0}))
|
|
84
|
+
assert np.array_equal(got, frame), "a foreign reader sees different pixels"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_default_metadata_is_openable_by_libczi(tmp_path):
|
|
88
|
+
"""The exact bug this file was created for.
|
|
89
|
+
|
|
90
|
+
The old default produced files libCZI refused outright. Asserting through
|
|
91
|
+
our own reader could never have caught it.
|
|
92
|
+
"""
|
|
93
|
+
czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
|
|
94
|
+
src = _write(tmp_path / "default_meta.czi") # no metadata_xml passed
|
|
95
|
+
with czirw.open_czi(str(src)) as r:
|
|
96
|
+
assert r.total_bounding_rectangle is not None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_rootless_metadata_still_opens_because_it_is_wrapped(tmp_path):
|
|
100
|
+
"""Handing in the old broken value must not produce an unopenable file."""
|
|
101
|
+
czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
|
|
102
|
+
src = _write(tmp_path / "rootless.czi", metadata_xml=b"<Metadata/>")
|
|
103
|
+
with czirw.open_czi(str(src)) as r:
|
|
104
|
+
assert r.total_bounding_rectangle is not None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ------------------------------------------------------------------ czifile
|
|
108
|
+
# An independent implementation, not libCZI. It disagrees with libCZI in
|
|
109
|
+
# places, which is the point of having both.
|
|
110
|
+
|
|
111
|
+
def test_czifile_opens_what_we_write(tmp_path):
|
|
112
|
+
czifile = pytest.importorskip("czifile", reason="czifile missing")
|
|
113
|
+
src = _write(tmp_path / "cf.czi", compression="zstdhdr")
|
|
114
|
+
with czifile.CziFile(str(src)) as c:
|
|
115
|
+
# czifile exposes the directory and asarray(), not a .shape attribute.
|
|
116
|
+
assert len(c.filtered_subblock_directory) == 3, "sub-blocks not seen"
|
|
117
|
+
data = c.asarray()
|
|
118
|
+
assert data.size > 0 and data.dtype == np.uint16
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ------------------------------------------------------------- aicspylibczi
|
|
122
|
+
|
|
123
|
+
def test_aicspylibczi_opens_what_we_write(tmp_path):
|
|
124
|
+
aics = pytest.importorskip("aicspylibczi", reason="aicspylibczi missing")
|
|
125
|
+
src = _write(tmp_path / "aics.czi", compression="zstdhdr")
|
|
126
|
+
czi = aics.CziFile(str(src))
|
|
127
|
+
assert czi.get_dims_shape() is not None
|
|
128
|
+
img, _shape = czi.read_image(C=0)
|
|
129
|
+
assert img.size > 0
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ------------------------------------------- recompressed real files
|
|
133
|
+
# czi_recompress output is what the ingest pipeline actually produces, so it
|
|
134
|
+
# is the output that most needs to open elsewhere.
|
|
135
|
+
|
|
136
|
+
@pytest.mark.skipif(not (CORPUS / "idr0011_plate1_scene1.czi").is_file(),
|
|
137
|
+
reason="corpus CZI not present")
|
|
138
|
+
def test_recompressed_real_file_opens_in_libczi(tmp_path):
|
|
139
|
+
czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
|
|
140
|
+
dst = tmp_path / "re.czi"
|
|
141
|
+
czi_recompress(CORPUS / "idr0011_plate1_scene1.czi", dst,
|
|
142
|
+
compression="zstdhdr")
|
|
143
|
+
with czirw.open_czi(str(dst)) as r:
|
|
144
|
+
assert r.total_bounding_rectangle is not None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@pytest.mark.skipif(not (CORPUS / "ome_axioscan_pyramid.czi").is_file(),
|
|
148
|
+
reason="corpus slide scan not present")
|
|
149
|
+
def test_carried_jpegxr_file_opens_in_libczi(tmp_path):
|
|
150
|
+
"""Carrying lossy sub-blocks across must leave a file libCZI still reads.
|
|
151
|
+
|
|
152
|
+
This is the strongest interop case available: 481 JPEG XR sub-blocks
|
|
153
|
+
copied verbatim into a container our writer built. If the container is
|
|
154
|
+
wrong, libCZI will say so even though the payloads are untouched.
|
|
155
|
+
"""
|
|
156
|
+
czirw = pytest.importorskip("pylibCZIrw.czi", reason="pylibCZIrw missing")
|
|
157
|
+
dst = tmp_path / "carried.czi"
|
|
158
|
+
info = czi_recompress(CORPUS / "ome_axioscan_pyramid.czi", dst,
|
|
159
|
+
compression="zstdhdr",
|
|
160
|
+
recompress_when=lambda e: e.compression == 0)
|
|
161
|
+
assert info["carried"] == info["subblocks"]
|
|
162
|
+
with czirw.open_czi(str(dst)) as r:
|
|
163
|
+
assert r.total_bounding_rectangle is not None
|
|
@@ -73,7 +73,9 @@ def test_payload_buffers_are_reused(path):
|
|
|
73
73
|
def test_payload_location_is_cached(path):
|
|
74
74
|
with _open(path) as r:
|
|
75
75
|
r.read()
|
|
76
|
-
|
|
76
|
+
# Every source type computes and keeps its payload ranges, the
|
|
77
|
+
# mapping (no positional reads, e.g. Windows) as well.
|
|
78
|
+
assert len(r._payload_ranges) == len(r)
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
def test_concurrent_tile_reads_on_one_reader(path, frames):
|
|
@@ -231,4 +231,10 @@ def test_stored_stack_reads_on_more_than_one_worker(monkeypatch, tmp_path, n):
|
|
|
231
231
|
assert np.array_equal(out, np.stack(tiles, axis=0))
|
|
232
232
|
n_tasks, workers = seen[-1]
|
|
233
233
|
assert n_tasks == n
|
|
234
|
-
|
|
234
|
+
if cz._PAYLOAD_PREAD:
|
|
235
|
+
# Positional reads overlap their I/O across workers.
|
|
236
|
+
assert workers >= 2
|
|
237
|
+
else:
|
|
238
|
+
# Without positional reads (Windows) a stored stack is a copy out
|
|
239
|
+
# of the mapping, which did not scale across workers when measured.
|
|
240
|
+
assert workers == 1
|