diff --git a/py/ngff_zarr/to_multiscales.py b/py/ngff_zarr/to_multiscales.py index 7af29947..9f8bbaad 100644 --- a/py/ngff_zarr/to_multiscales.py +++ b/py/ngff_zarr/to_multiscales.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC # SPDX-License-Identifier: MIT import atexit +import copy import shutil import signal import threading @@ -502,7 +503,13 @@ def to_multiscales( input image orders them differently. :rtype : NgffMultiscales """ - ngff_image = data if isinstance(data, NgffImage) else to_ngff_image(data) + # Shallow copy, with its own computed_callbacks: the rechunk and dask + # conversion below must not reach the caller's image (gh-issue-627). + if isinstance(data, NgffImage): + ngff_image = copy.copy(data) + ngff_image.computed_callbacks = list(data.computed_callbacks) + else: + ngff_image = to_ngff_image(data) # IPFS and visualization friendly default chunks default_chunk_size = 128 if "z" in ngff_image.dims else 256 diff --git a/py/ngff_zarr/to_ngff_zarr.py b/py/ngff_zarr/to_ngff_zarr.py index 2203de2a..7760dcee 100644 --- a/py/ngff_zarr/to_ngff_zarr.py +++ b/py/ngff_zarr/to_ngff_zarr.py @@ -1,5 +1,6 @@ # SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC # SPDX-License-Identifier: MIT +import copy import sys import tempfile import warnings @@ -68,6 +69,10 @@ def _numcodecs_to_zarr_v3_codec(compressor): codec_id = getattr(compressor, "codec_id", None) if codec_id is None: + # Already a native Zarr v3 codec: pass it through unchanged. The + # sharding path otherwise swaps it for the zstd default. + if hasattr(compressor, "to_dict"): + return compressor return None try: @@ -210,9 +215,16 @@ def _write_with_tensorstore( full_array_shape=None, create_dataset=True, compressor=None, + compression_chain=None, **kwargs, ) -> None: - """Write array using tensorstore backend""" + """Write array using tensorstore backend. + + ``compressor`` carries a single legacy codec (zarr format 2 metadata). + ``compression_chain`` carries the ordered zarr v3 bytes-to-bytes codec + chain: ``None`` requests the default compression, an empty sequence + requests no compression. + """ import tensorstore as ts # Use full array shape if provided, otherwise use the region array shape @@ -269,6 +281,35 @@ def create_compression_codec(compressor): if compressor is None: return None + # Native Zarr v3 codecs carry no codec_id but serialise into + # exactly the form TensorStore expects. + if hasattr(compressor, "to_dict") and not hasattr( + compressor, "codec_id" + ): + codec = compressor.to_dict() + # zarr-python records constructor-defaulted blosc attrs in + # _tunable_attrs and evolves them from the dtype at write + # time; mirror that so both backends land on the same + # configuration. Explicitly chosen values are kept. + config = codec.get("configuration") + if codec.get("name") == "blosc" and isinstance(config, dict): + config = dict(config) + itemsize = array.dtype.itemsize + tunable = getattr(compressor, "_tunable_attrs", None) + if tunable is None: + # Fallback for zarr without _tunable_attrs. + if config.get("typesize") in (None, 1): + config["typesize"] = itemsize + else: + if "typesize" in tunable: + config["typesize"] = itemsize + if "shuffle" in tunable: + config["shuffle"] = ( + "bitshuffle" if itemsize == 1 else "shuffle" + ) + codec = {**codec, "configuration": config} + return codec + if hasattr(compressor, "codec_id"): # numcodecs compressor object codec_id = compressor.codec_id @@ -305,33 +346,45 @@ def create_compression_codec(compressor): return compressor return None + # Mirror the zarr-python codec chain. Left unset, TensorStore + # applies its own defaults: sharded and uncompressed. + bytes_codec = {"name": "bytes", "configuration": {"endian": "little"}} + default_compression = { + "name": "zstd", + "configuration": {"level": 0, "checksum": False}, + } + if compression_chain is None: + fallback = ( + create_compression_codec(compressor) + if compressor is not None + else None + ) + compression_codecs = [fallback or default_compression] + else: + # An explicit chain is preserved in order; an explicit empty + # chain means no compression, matching zarr-python. + compression_codecs = [ + codec + for codec in map(create_compression_codec, compression_chain) + if codec is not None + ] + inner_codecs = [bytes_codec, *compression_codecs] + # Add sharding codec with inner codecs if needed if internal_chunk_shape: - sharding_config = {"chunk_shape": internal_chunk_shape} - - # If compression is specified, add it as inner codec for sharding - if compressor is not None: - compression_codec = create_compression_codec(compressor) - if compression_codec: - # For sharding, compression goes in the inner codecs - sharding_config["codecs"] = [compression_codec] - codecs.append( { "name": "sharding_indexed", - "configuration": sharding_config, + "configuration": { + "chunk_shape": internal_chunk_shape, + "codecs": inner_codecs, + }, } ) else: - # No sharding, add compression codec directly if specified - if compressor is not None: - compression_codec = create_compression_codec(compressor) - if compression_codec: - codecs.append(compression_codec) - - # Set codecs if any were added - if codecs: - spec["metadata"]["codecs"] = codecs + codecs.extend(inner_codecs) + + spec["metadata"]["codecs"] = codecs else: raise ValueError(f"Unsupported zarr format: {zarr_format}") @@ -567,6 +620,18 @@ def _configure_sharding( return sharding_kwargs, internal_chunk_shape, arr +def _is_bytes_codec(codec) -> bool: + """Is this the zarr v3 array-to-bytes codec rather than a compressor?""" + if isinstance(codec, str): + return codec == "bytes" + name = getattr(codec, "name", None) + if name is None and hasattr(codec, "to_dict"): + name = codec.to_dict().get("name") + if name is None and isinstance(codec, dict): + name = codec.get("name") + return name == "bytes" + + def _write_array_with_tensorstore( store_path: str, path: str, @@ -584,6 +649,25 @@ def _write_array_with_tensorstore( """Write an array using the TensorStore backend.""" # Extract compressor and other conflicting parameters from kwargs to avoid conflicts compressor = kwargs.pop("compressor", None) + # The public API takes ``compressors`` (plural); without this a supplied + # codec is dropped and TensorStore silently writes its default. The full + # chain is preserved in order; only the array-to-bytes ("bytes") codec is + # dropped since the writer always leads with one. ``None`` — absent or + # explicit — selects no compression only when explicitly passed, matching + # zarr-python, so the unset case is detected with a sentinel. + _unset = object() + compressors = kwargs.pop("compressors", _unset) + if compressors is _unset: + compression_chain = [compressor] if compressor is not None else None + elif compressors is None: + compression_chain = [] + elif isinstance(compressors, (list, tuple)): + compression_chain = [c for c in compressors if not _is_bytes_codec(c)] + else: + compression_chain = [compressors] + if compressor is None and compression_chain: + # zarr format 2 metadata carries a single compressor. + compressor = compression_chain[0] kwargs.pop("chunks", None) # Remove chunks from kwargs since it's a positional arg scale_path = f"{store_path}/{path}" @@ -598,6 +682,7 @@ def _write_array_with_tensorstore( full_array_shape=full_array_shape, create_dataset=create_dataset, compressor=compressor, + compression_chain=compression_chain, **kwargs, ) else: # Sharding @@ -612,6 +697,7 @@ def _write_array_with_tensorstore( full_array_shape=full_array_shape, create_dataset=create_dataset, compressor=compressor, + compression_chain=compression_chain, **kwargs, ) @@ -1302,10 +1388,13 @@ def _prepare_next_scale( """Prepare the next scale for processing if needed. :param scale_strategy: Strategy for handling non-power-of-2 scale factors. - "pad" (default) always uses incremental downsampling from the previous level, - which may produce slightly different sizes due to floor-division rounding. - "exact" uses pre-computed images from the initial to_multiscales() call when - incremental downsampling cannot achieve the exact target size. + "pad" (default) always downsamples incrementally from the previous + level, which is memory-efficient but can miss the target sizes; the + datasets metadata still describes the exact targets, so the store then + misdescribes its own geometry. "exact" uses pre-computed images from + the initial to_multiscales() call when incremental downsampling cannot + achieve the exact target size, so the written arrays match the + coordinate metadata. """ # No next scale if we're at the last one if index >= nscales - 1: @@ -1442,11 +1531,14 @@ def to_ome_zarr( :param scale_strategy: Strategy for handling non-power-of-2 scale factors during - multiscale writing. "pad" (default) always uses incremental downsampling from - the previous level, which is memory-efficient but may produce slightly different - sizes due to floor-division rounding. "exact" uses pre-computed images from - the initial to_multiscales() call when incremental downsampling cannot achieve - the exact target size (original_size // scale_factor). + multiscale writing. "pad" (default) always downsamples incrementally from + the previous level, which is memory-efficient but can miss the target + sizes; the datasets metadata still describes the exact targets, so the + store then misdescribes its own geometry. "exact" uses pre-computed + images from the initial to_multiscales() call when incremental + downsampling cannot achieve the exact target size + (original_size // scale_factor), so the written arrays match the + coordinate metadata. :type scale_strategy: "pad" or "exact", optional :param **kwargs: Passed to the zarr.create_array() or zarr.creation.create() function, e.g., compression options. @@ -1565,6 +1657,14 @@ def _to_ngff_zarr_impl( """ Internal implementation of to_ngff_zarr without .ozx handling. """ + # Shallow copy, with per-image computed_callbacks: the write loop swaps + # image data for on-disk views and regenerates scales, and none of that + # may reach the caller's multiscales (gh-issue-627). + multiscales = copy.copy(multiscales) + multiscales.images = [copy.copy(image) for image in multiscales.images] + for image in multiscales.images: + image.computed_callbacks = list(image.computed_callbacks) + # Setup and validation store_path = str(store) if isinstance(store, (str, Path)) else None diff --git a/py/pixi.lock b/py/pixi.lock index a4e29d93..b5fc7590 100644 --- a/py/pixi.lock +++ b/py/pixi.lock @@ -352,7 +352,6 @@ environments: - pypi: ./ - pypi: https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1d/df/5ba47e12638ac537a8166a454c497032af83f2d5eb826bca81024ac8f78c/itkwasm-1.0b195-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl @@ -364,8 +363,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl @@ -515,7 +515,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0b/00/787ea5f237b8ea7bc67140c99155f9c00b5baf11c49afc5f3bfefa298f95/numcodecs-0.16.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1c/e8/b33784d6fc77fb5062a8a7854e43e1e618b87d5ddf610a88025e4de6226e/google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl - pypi: https://files.pythonhosted.org/packages/1d/df/5ba47e12638ac537a8166a454c497032af83f2d5eb826bca81024ac8f78c/itkwasm-1.0b195-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/42/56/ed5f492bd553a31c8e28d621f8256f2c7b1a133b28f73525d96ca355891a/wasmtime-45.0.0-py3-none-manylinux2014_aarch64.whl @@ -528,8 +527,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl @@ -606,7 +606,6 @@ environments: - pypi: ./ - pypi: https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1d/df/5ba47e12638ac537a8166a454c497032af83f2d5eb826bca81024ac8f78c/itkwasm-1.0b195-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/36/c6/f7ff6c11f5ca215d9f43d3629163727a272eabc356e5c9b2853df2bfe965/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl - pypi: https://files.pythonhosted.org/packages/3c/9e/38e7ca8184c958b51f45d56a4aeceb1134ecde2d8bd157efadc98502cc42/numcodecs-0.16.5-cp314-cp314-macosx_10_13_x86_64.whl @@ -619,8 +618,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl @@ -697,7 +697,6 @@ environments: - pypi: ./ - pypi: https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1d/df/5ba47e12638ac537a8166a454c497032af83f2d5eb826bca81024ac8f78c/itkwasm-1.0b195-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/21/88/8ecf3c2b864a490b9e7010c84fd203ec8cf3b280651106a3a74dd1b0ca72/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl @@ -710,9 +709,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a1/37/260fa42e7b2b08e6e00ad632f8dd620961a60a459426c26cea390f8c68d0/numcodecs-0.16.5-cp314-cp314-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl @@ -819,7 +819,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/08/a5/7b059810934a09fb3ccb657e0843813c1fee1183d3bc2c8041800374aa2c/google_crc32c-1.8.0-cp314-cp314-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/0b/35/1cceccc5fcb50fa2ed53e2aa278cd032f3902682a73e763fb1ac3be8e6fa/rich_argparse-1.8.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1d/df/5ba47e12638ac537a8166a454c497032af83f2d5eb826bca81024ac8f78c/itkwasm-1.0b195-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/71/8e31ea472ceb934e7261ac59a786e82cd82b4d4dcb7c870d498aa9c3c21e/wasmtime-45.0.0-py3-none-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/31/a5/a0425af36c20d55a3ea884db4b4efca25a43bea9214ba69ca7932dd997b4/numcodecs-0.16.5-cp314-cp314-win_amd64.whl @@ -831,9 +830,10 @@ environments: - pypi: https://files.pythonhosted.org/packages/88/0a/469e2bd01be1490336e6c8707386845655d59261543315778a3ccc7e8019/zarr-3.2.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d5/0c/043d5e551459da400957a1395e0febbf771446ff34291afcbe3d8be2a279/fsspec-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/bc/83e112abc66cd466c6b83f99118035867cecd41802f8d044638aa78a106e/locket-1.0.0-py2.py3-none-any.whl @@ -1587,7 +1587,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/37/3922951a55a3d0f0340e884929087ce08e333cbb16a86002535c095960fc/gcsfs-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -1643,15 +1642,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/8f/29/93ea9cbab7f57b4e60480c51fc51d8e138e399d11797c981d5f6e79f9832/imagecodecs-2026.3.6-cp311-abi3-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/18/938441025db6769a3464596b2410af3afde0b21eb2f204c6f766f68af4bd/aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/aa/57/0a3499479cb19d7e4b7fc38b2ba15c0ea20e1e88cb2b634b75dd3e9ff8b8/itk_core-5.4.6-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b9/77/2ff7aefc09cf1306a81cd7a46af34f80ebefef81a2e8329b94b58ad813ae/distributed-2026.3.0-py3-none-any.whl @@ -1840,7 +1840,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/37/3922951a55a3d0f0340e884929087ce08e333cbb16a86002535c095960fc/gcsfs-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl @@ -1897,15 +1896,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/b0/d5/d68e30b29098f63beab6fe501100fe82674ff142b32c672532da86a99b3a/grpcio-1.81.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b9/77/2ff7aefc09cf1306a81cd7a46af34f80ebefef81a2e8329b94b58ad813ae/distributed-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -2021,7 +2021,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/37/3922951a55a3d0f0340e884929087ce08e333cbb16a86002535c095960fc/gcsfs-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -2079,13 +2078,14 @@ environments: - pypi: https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b8/8b/72c0e80aad08e09867ce14a621bce689a733552f20cdf2ef96d4b052da10/cachebox-5.2.3-cp314-cp314-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b9/77/2ff7aefc09cf1306a81cd7a46af34f80ebefef81a2e8329b94b58ad813ae/distributed-2026.3.0-py3-none-any.whl @@ -2202,7 +2202,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/37/3922951a55a3d0f0340e884929087ce08e333cbb16a86002535c095960fc/gcsfs-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/9f/38ff9e57a2eade7bf9dfee5eae17f39fc0e998658050279cbb14d97d36d9/msgpack-1.1.2-cp314-cp314-macosx_11_0_arm64.whl @@ -2260,14 +2259,15 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a1/37/260fa42e7b2b08e6e00ad632f8dd620961a60a459426c26cea390f8c68d0/numcodecs-0.16.5-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b9/77/2ff7aefc09cf1306a81cd7a46af34f80ebefef81a2e8329b94b58ad813ae/distributed-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl @@ -2415,7 +2415,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/37/3922951a55a3d0f0340e884929087ce08e333cbb16a86002535c095960fc/gcsfs-2026.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -2472,13 +2471,14 @@ environments: - pypi: https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b9/77/2ff7aefc09cf1306a81cd7a46af34f80ebefef81a2e8329b94b58ad813ae/distributed-2026.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -2658,7 +2658,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/b1/8938e8830b0ee2e167fc75a094dea766a1152bde46752cd9bfc57ee78a82/ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/11/2f/5abff74666f8388d2c9516c265f99c33484c827f7fcb3cd703c2f3cbb17e/cachebox-5.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/16/e4/b9ec2f4dfc34ecf724bc1beb96a9f6fa9b91801645688ffadacd485089da/numcodecs-0.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl @@ -2729,16 +2728,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/93/16/85c4959c5e011cd76b71f8805b96c4fdc0e96048eebda323855ad420ec8a/itk_numerics-5.4.6-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl @@ -2912,7 +2912,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/19/8e/b7b329905006f1b3627e1f531de8ab36bd544fa3d6136576c19f9d90de84/imagecodecs-2025.3.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl @@ -2982,16 +2981,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/db/cf51a71bab2009517d1a7f0ee07657e3bd446c4d69f67e6966cf17bcf956/propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/bc/02/8ae1b63dbdebb2ebf600523f48b54e9bfb10db5a28551c3432346f49e1dd/cachebox-5.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl @@ -3092,7 +3092,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/9e/88193fcb7a2a43fe8ed9d9888374d43fa5c7176aa802651e68b28f1aee4a/cachebox-5.2.3-cp310-cp310-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/c0/6d72cde772bcec196b7188731d41282993b2958440f77fdf0db216f722da/numcodecs-0.13.1-cp310-cp310-macosx_10_9_x86_64.whl @@ -3161,16 +3160,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/93/d7/516d984057745a6cd96575eea814fe1edd6646ee6efd552fb7b0921dec83/cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -3272,7 +3272,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/d8/d5446401846b14c44c9f6dbe66353e14aad0b0004485c4b1f593333f7fbb/itk_numerics-5.4.6-cp310-cp310-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl @@ -3343,18 +3342,19 @@ environments: - pypi: https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/98/8d/e0b13d9bfd43f295cce7824ebaac1970f818a7027c16f290de404934cafe/cachebox-5.2.3-cp310-cp310-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/8f/7b3ec212f1ea0683f55f978e3246bc313c38818664edfc97a9f349a4901e/yarl-1.24.2-cp310-cp310-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -3485,7 +3485,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/43/a70196a357d4b43751129019b5672c311120bd81c61cbeb625a081a45d2f/itk_numerics-5.4.6-cp310-cp310-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -3549,9 +3548,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/82/9cd69a1af288fbdedf01a10e3c8a0b6890b08c7f3f96d36a213699dbcd94/wrapt-2.2.2-cp310-cp310-win_amd64.whl @@ -3559,7 +3560,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/ba/93/89976c696fb0224662239d952c47b4d1661b34d79a332ef5584facaa8579/msgpack-1.2.1-cp310-cp310-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -3752,7 +3752,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/11/c5/65e7dfc4108451f5317aca47a7c339954d0b7601ed4db4481a2f80ee2da1/tensorstore-0.1.84-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl @@ -3815,9 +3814,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/80/19189ea605017473660e43762dc853d2797984b3c7bf30ce656099add30c/ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/aa/57/0a3499479cb19d7e4b7fc38b2ba15c0ea20e1e88cb2b634b75dd3e9ff8b8/itk_core-5.4.6-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -3826,7 +3827,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/50/8e4d59b3e344405d8393d6cc5cc92754d3cc1d81134041ebffd3f5ab73e6/cachebox-5.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b6/ec/08f671f69a444d704aeecebf92af659b67b97a869942411d0a578b08c334/yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz @@ -4005,7 +4005,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl @@ -4073,16 +4072,17 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/ec/40aed2330e7f02ecf74386ffcfef9ccb7108c6a430f15b6a252b663b1bed/wrapt-2.2.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl - pypi: https://files.pythonhosted.org/packages/bb/9b/8da38af731e3832e9f987548e4bfb610d7f3054019e12c44a94ba9272b37/cachebox-5.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl @@ -4197,7 +4197,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/4a/3dfd5f7850cbf0d06dc84ba9aa00db766b52ca38d8b86e3a38314d52498c/cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1a/e4/e804505f87627cd8cdae9c010c47c4485fd8c1ce31a7dd0ab7fcc4707377/tifffile-2026.3.3-py3-none-any.whl @@ -4257,9 +4256,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b5/7f10929c45e2d0d0dc78bead98458c271af3e028e66b16441de88829a8b7/charset_normalizer-3.4.8-cp311-cp311-macosx_10_9_universal2.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl @@ -4267,7 +4268,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -4391,7 +4391,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl @@ -4458,15 +4457,16 @@ environments: - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b5/7f10929c45e2d0d0dc78bead98458c271af3e028e66b16441de88829a8b7/charset_normalizer-3.4.8-cp311-cp311-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/bd/e4/d5c2dea8ee845bffe8c3342f05a21578d5e8c4c8f886f7f98a992086ccef/itk_numerics-5.4.6-cp311-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -4603,7 +4603,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0f/5b/af02c417954f46e5c7bd5163ac251f535877d909fce54861c99ae197f6f6/numcodecs-0.16.5-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/17/ca/69274c51dcd6e8947d77b2806cf47a4a15f2c846e2cbeb1882547d3da283/aiohttp-3.14.1-cp311-cp311-win_amd64.whl @@ -4666,9 +4665,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ae/8f/dc5531155e7070361eb1b7e4c1a9d896d0cb21c49f807a6c03fd63fc877e/cffi-2.0.0-cp311-cp311-win_amd64.whl @@ -4677,7 +4678,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/24/70bd59276883fdd91600ca20040b41efd4902a923283c4d6edcb1de128d2/ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -4861,7 +4861,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl @@ -4928,17 +4927,18 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/aa/57/0a3499479cb19d7e4b7fc38b2ba15c0ea20e1e88cb2b634b75dd3e9ff8b8/itk_core-5.4.6-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -5112,7 +5112,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -5178,17 +5177,18 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/97/1e/98aaddf272552d9fef1f0296a9939d1487914a239e98678f6b20f8b0a5c8/numcodecs-0.16.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/57/a9474c3aeaa337c8a330c0dc5df266527d56da3b189c029529f6b08af2a4/charset_normalizer-3.4.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -5302,7 +5302,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -5363,17 +5362,18 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/bc/dd/9e8cbd8f5582ca4b590336f2b91ee5662f6a6ca562b565abaf696a0f81ff/msgpack-1.2.1-cp312-cp312-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -5493,7 +5493,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -5561,10 +5560,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/96/72/1e6442a00cd2924d361aa1b642ab6373ec35c6fabf311a760be9f76e0f13/scipy-1.18.0-cp312-cp312-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a8/b8/3c70881695e056f8a32f8b941126cf78775d9a4d7feba8abcb52cb7b04f2/ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl @@ -5573,7 +5574,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/bd/e4/d5c2dea8ee845bffe8c3342f05a21578d5e8c4c8f886f7f98a992086ccef/itk_numerics-5.4.6-cp311-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -5704,7 +5704,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -5764,10 +5763,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9b/82/eb8b72f184b1e4986dd9daec15d7f6d9285a6728d2b07b7f04656829f473/charset_normalizer-3.4.8-cp312-cp312-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a7/25/722e3b93bd687009afb2d59a35e13d30ddd8f80571445bb0c4e4ce26ec66/yarl-1.24.2-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl @@ -5776,7 +5777,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -5959,7 +5959,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/13/95/450765b971a3bed9d7cf003c3833c1976482eb83b0241b6dbb840a25b43b/cachebox-5.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl @@ -6022,10 +6021,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/9d/6e/dbf1d0625dc711fb2851f4f3c3055c39ed58bae92082d8c627dbe6013736/aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/aa/57/0a3499479cb19d7e4b7fc38b2ba15c0ea20e1e88cb2b634b75dd3e9ff8b8/itk_core-5.4.6-cp311-abi3-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl @@ -6034,7 +6035,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b6/11/9d6ce94465d6a7f92c413430b3e77f0879b39427a217ffb3d23585df4bb3/grpcio-1.82.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz @@ -6212,7 +6212,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl @@ -6274,11 +6273,13 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl - pypi: https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl @@ -6286,7 +6287,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b0/d8/00c6854ac1512bb9eaf13bd3f8f28222f7674947fc510a4ff7616f2efc80/google_resumable_media-2.10.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -6404,7 +6404,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/49/fe5a8572a70cd9cba79f80af9388ac8c5c914ed4459b956f940244e499a5/charset_normalizer-3.4.8-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -6462,10 +6461,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/90/5f/85535dfb3cfd6442d66d1df1694062c5d6df02f895329e7e120b2a3d2b8b/aiobotocore-3.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/90/aa/ceeb81d9dc886d445416310f0eff5c3978f20224b4143e96e36b7e85b011/dask-2026.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ad/f3/fe7e1f57f170ed6cae20eecc0dea62e9de862e68adede80a6cd54a9b4dfa/grpcio_status-1.82.0-py3-none-any.whl @@ -6474,7 +6475,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b2/f4/c4227aacfacc5cb0cc2d119b65301d177912a6842cd64e120c47af76064f/aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/4b/afc1fef8a43bafb139f57f73bbd70df82807af5934321e8112ae50668827/botocore-1.43.0-py3-none-any.whl @@ -6596,7 +6596,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/11/49/fe5a8572a70cd9cba79f80af9388ac8c5c914ed4459b956f940244e499a5/charset_normalizer-3.4.8-cp313-cp313-macosx_10_13_universal2.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -6657,10 +6656,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ab/01/a2d5f96cd4e74424864d30bc0a7e44d0a12dacdcfa91b5b2d1bd3dca6bf3/aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl @@ -6669,7 +6670,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/bd/e4/d5c2dea8ee845bffe8c3342f05a21578d5e8c4c8f886f7f98a992086ccef/itk_numerics-5.4.6-cp311-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -6803,7 +6803,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/0c/d5/c5db1ea3394c6e1732fb3286b3bd878b59507a8f77d32a2cebda7d7b7cd4/donfig-0.8.1.post1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/a1/510b0a7fadc6f43a6ce50152e69dbd86415240835868bb0bd9b5b88b1e06/aioitertools-0.13.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/12/27/fb8d7338b4d551900fa3e580acbe7a0cf655d940e164cb5c00ec31961094/orderly_set-5.5.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/1b/bd/a89eaebd2f9db5f92ddcc8e4f23c266be1dbd11058bb83451d8dd029f34c/google_cloud_storage-3.12.0-py3-none-any.whl @@ -6864,10 +6863,12 @@ environments: - pypi: https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/a6/18221b2b39e60968b335dbc078e4d36302036ad6fe93d42e7fe443f10697/itkwasm_image_io_wasi-1.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a2/5c/1abfe6871eb7868c485b83eb9b3587f6996f26a15af849882da2912a9bee/liffile-2026.4.11-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/0b/f68a968b49876eae0f2a515387093cebb2eb9451380a96741cc20efac0d0/s3fs-2026.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a5/12/97d8ad183e3130e168f2feb860edd68f1b72e57f29268d980f3b70e34cd0/tensorstore-0.1.84-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/a7/5c/c7b9a48efe973883f5707a311f87772a4c307a22ec80d6f3c851846a0a02/grpcio-1.82.0-cp313-cp313-win_amd64.whl @@ -6877,7 +6878,6 @@ environments: - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl - - pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/b7/d2/ae881ee28d014f38e0ccbfd974a06a919ba9af34f1f74bf42b5301891d63/scipy-1.18.0-cp313-cp313-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/b8/02/5bf3639f5b77e9b183011c08541c5039ba3d04f5316c70312b48a8e003a9/pims-0.7.tar.gz - pypi: https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl @@ -20748,7 +20748,7 @@ packages: requires_dist: - dask[array]>=2026.1.2 - importlib-resources - - itkwasm-downsample>=1.8.0 + - itkwasm-downsample>=2.0.0 - itkwasm>=1.0b183 - numpy - platformdirs @@ -21437,15 +21437,6 @@ packages: requires_dist: - pycparser ; implementation_name != 'PyPy' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/12/c6/906a1c94a9e0d12b6cbc8a36d6fe284d06298d3d3eda14934d21609845ab/itkwasm_downsample-1.8.1-py3-none-any.whl - name: itkwasm-downsample - version: 1.8.1 - sha256: f1315d32a1d80a610c682c3a32ab41aa46b584f91f17a839a7c0b6e3796e60c7 - requires_dist: - - itkwasm-downsample-emscripten ; sys_platform == 'emscripten' - - itkwasm-downsample-wasi ; sys_platform != 'emscripten' - - itkwasm>=1.0b145 - requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/13/95/450765b971a3bed9d7cf003c3833c1976482eb83b0241b6dbb840a25b43b/cachebox-5.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: cachebox version: 5.2.3 @@ -25378,6 +25369,15 @@ packages: - sphinx-book-theme ; extra == 'rtd' - sphinx-examples ; extra == 'rtd' requires_python: '>=3.7' +- pypi: https://files.pythonhosted.org/packages/9e/4a/16c7026b190df319983c4d98f7f41d76f2b6e4c5c36f36cddbf297ae2df3/itkwasm_downsample-2.0.0-py3-none-any.whl + name: itkwasm-downsample + version: 2.0.0 + sha256: 8b87a0acb9d4202a11ee040d6b2dc6badf80210d82e6665d27cc505fa35eb894 + requires_dist: + - itkwasm-downsample-emscripten ; sys_platform == 'emscripten' + - itkwasm-downsample-wasi ; sys_platform != 'emscripten' + - itkwasm>=1.0b185 + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/9e/84/ad6a0b408daa859246f57c03efd28e5dd1b33c21737c2db84cae8c237aa5/cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl name: cffi version: 2.0.0 @@ -25524,6 +25524,14 @@ packages: - typing-extensions>=4.0 ; python_full_version < '3.11' - cryptography>=3.4.0 ; extra == 'crypto' requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/a4/2d/1867f6d45a9fcd863e2d1611ac8e2df40f2744c05da4f701406455d3084e/itkwasm_downsample_wasi-2.0.0-py3-none-any.whl + name: itkwasm-downsample-wasi + version: 2.0.0 + sha256: ec95c6f5ea5c0b7c03a82387fc0ad2c80a15316447093c04fcb7e2af2372c7e8 + requires_dist: + - importlib-resources + - itkwasm>=1.0b185 + requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl name: rpds-py version: 2026.6.3 @@ -25998,14 +26006,6 @@ packages: version: 0.5.2 sha256: fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/b5/2b/2cca4261bab978eb3d9f1aa32fe7dcd9021e193bdc88b3dcf0bb366e9f91/itkwasm_downsample_wasi-1.8.1-py3-none-any.whl - name: itkwasm-downsample-wasi - version: 1.8.1 - sha256: a3807838a0e2354c7fcc2ec83ba281884fc80b792a1baacc4215a26a80db6535 - requires_dist: - - importlib-resources - - itkwasm>=1.0b145 - requires_python: '>=3.8' - pypi: https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl name: psutil version: 7.2.2 diff --git a/py/pyproject.toml b/py/pyproject.toml index 588b9698..6463b59a 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "dask[array]>=2026.1.2", "importlib_resources", "itkwasm >= 1.0b183", - "itkwasm-downsample >= 1.8.0", + "itkwasm-downsample >= 2.0.0", "numpy", "platformdirs", "psutil; sys_platform != \"emscripten\"", diff --git a/py/test/_data.py b/py/test/_data.py index ff176b03..d0a9aae4 100644 --- a/py/test/_data.py +++ b/py/test/_data.py @@ -17,7 +17,7 @@ from zarr.storage import MemoryStore test_data_ipfs_cid = "bafybeifqibhcomn4u42aqrgvttyfteysbspvzez5sbezcqj5yylzzafpma" -test_data_sha256 = "323a8f030980171c20dc064cd977d831cfd7402bd9b7ffa7c1d6d6750ea8fe99" +test_data_sha256 = "525dfae8fe52df4a18dc19de97f018e667e161bc83dc0584144c16d872349705" test_dir = Path(__file__).resolve().parent extract_dir = "data" @@ -95,7 +95,7 @@ def input_images(): pooch.retrieve( fname="data.tar.gz", path=test_dir, - url="https://github.com/fideus-labs/ngff-zarr/releases/download/testing-data/ngff-zarr-testing-data-v0.20.1.tar.gz", + url="https://github.com/fideus-labs/ngff-zarr/releases/download/testing-data/ngff-zarr-testing-data-v0.21.0.tar.gz", # url=f"https://itk.mypinata.cloud/ipfs/{test_data_ipfs_cid}", # url=f"https://{test_data_ipfs_cid}.ipfs.w3s.link/ipfs/{test_data_ipfs_cid}/data.tar.gz", known_hash=f"sha256:{test_data_sha256}", @@ -164,58 +164,152 @@ def store_contents(store, keys): return contents +_METADATA_SUFFIXES = (".zarray", ".zattrs", ".zgroup", ".zmetadata", "zarr.json") + + +def _is_metadata_key(key): + return key.endswith(_METADATA_SUFFIXES) + + +def _drop_codecs(node): + """Drop the codec in use, which zarr picks and changes between releases. + + zstd until 3.2, blosc/lz4 from 3.3. ngff-zarr does not choose it, so + comparing it compares zarr rather than this library. + """ + if isinstance(node, list): + return [_drop_codecs(item) for item in node] + if not isinstance(node, dict): + return node + return { + key: _drop_codecs(value) + for key, value in node.items() + if key not in ("compressor", "compressors", "codecs") + } + + +def _array_paths(keys, contents): + """Group paths that hold an array, for both zarr formats.""" + paths = set() + for key in keys: + if key.endswith(".zarray"): + paths.add(key[: -len("/.zarray")] if "/" in key else "") + elif key.endswith("zarr.json"): + try: + meta = json.loads(contents[key].decode("utf-8")) + except (KeyError, UnicodeDecodeError, json.JSONDecodeError): + continue + if meta.get("node_type") == "array": + paths.add(key[: -len("/zarr.json")] if "/" in key else "") + return paths + + +def _arrays_equal(baseline_store, test_store, path): + """Compare decompressed values, so the codec in use does not matter.""" + import numpy as np + + kwargs = {"mode": "r"} + if path: + kwargs["path"] = path + baseline = zarr.open_array(store=baseline_store, **kwargs) + test = zarr.open_array(store=test_store, **kwargs) + + if baseline.shape != test.shape: + sys.stderr.write(f"shape differs at {path or '/'}: ") + sys.stderr.write(f"{baseline.shape} != {test.shape}\n") + return False + if baseline.dtype != test.dtype: + sys.stderr.write(f"dtype differs at {path or '/'}: ") + sys.stderr.write(f"{baseline.dtype} != {test.dtype}\n") + return False + if not np.array_equal(np.asarray(baseline[...]), np.asarray(test[...])): + sys.stderr.write(f"values differ at {path or '/'}\n") + return False + return True + + def store_equals(baseline_store, test_store): + """Compare two stores by value rather than by stored bytes. + + Chunks are compressed, so identical data encoded by different codecs, or + by different releases of the same codec, yields different bytes. Compare + decompressed arrays, and parse the metadata rather than diffing its bytes. + """ baseline_keys = store_keys(baseline_store) test_keys = store_keys(test_store) - json_keys = {".zmetadata", ".zattrs", ".zgroup", "zarr.json"} + + # Key sets must match in both directions: a missing key hides data, an + # extra one is data the baseline never blessed. + if baseline_keys != test_keys: + missing = sorted(baseline_keys - test_keys) + extra = sorted(test_keys - baseline_keys) + if missing: + sys.stderr.write(f"baseline keys not in test store: {missing}\n") + if extra: + sys.stderr.write(f"test keys not in baseline: {extra}\n") + return False + baseline_contents = store_contents(baseline_store, baseline_keys) test_contents = store_contents(test_store, test_keys) - for k in baseline_keys: - if k in json_keys: - baseline_metadata = json.loads(baseline_contents[k].decode("utf-8")) - test_metadata = json.loads(test_contents[k].decode("utf-8")) + for k in sorted(baseline_keys): + if not _is_metadata_key(k): + continue - diff = DeepDiff(baseline_metadata, test_metadata, ignore_order=True) - if diff: - sys.stderr.write("Metadata in {k} files do not match\n") - sys.stderr.write(f"Differences: {diff}\n") - return False - else: - if k not in test_keys: - sys.stderr.write(f"baseline key {k} not in test keys\n") - sys.stderr.write(f"test keys: {test_keys}\n") - return False - if ( - baseline_contents.get(k) != test_contents.get(k) - and ".zattrs" not in k - and ".zgroup" not in k - and "zarr.json" not in k - ): - sys.stderr.write(f"test value != baseline value for key {k}\n") - sys.stderr.write(f"baseline: {baseline_contents[k]}, \n") - sys.stderr.write(f"test: {test_contents[k]}, \n") - return False + baseline_metadata = _drop_codecs( + json.loads(baseline_contents[k].decode("utf-8")) + ) + test_metadata = _drop_codecs(json.loads(test_contents[k].decode("utf-8"))) + diff = DeepDiff(baseline_metadata, test_metadata, ignore_order=True) + if diff: + sys.stderr.write(f"Metadata in {k} does not match\n") + sys.stderr.write(f"Differences: {diff}\n") + return False + + for path in sorted(_array_paths(baseline_keys, baseline_contents)): + if not _arrays_equal(baseline_store, test_store, path): + return False return True -def verify_against_baseline(dataset_name, baseline_name, multiscales, version="0.4"): +def verify_against_baseline( + dataset_name, baseline_name, multiscales, version="0.4", scale_strategy="pad" +): + baseline_path = ( + test_data_dir + / f"baseline/zarr{zarr_version_major}/v{version}/{dataset_name}/{baseline_name}" + ) + + # store_equals walks the baseline's keys, so an absent one compares equal + # to anything. Skip instead: a missing baseline is a gap, not a pass. + if not baseline_path.is_dir(): + pytest.skip( + f"No baseline at {baseline_path.relative_to(test_data_dir)} " + f"(zarr {zarr.__version__}); nothing to compare against." + ) + try: from zarr.storage import DirectoryStore - baseline_store = DirectoryStore( - test_data_dir / f"baseline/v{version}/{dataset_name}/{baseline_name}", - **zarr_kwargs, - ) + baseline_store = DirectoryStore(baseline_path, **zarr_kwargs) except ImportError: from zarr.storage import LocalStore - baseline_store = LocalStore( - test_data_dir / f"baseline/v{version}/{dataset_name}/{baseline_name}" - ) + baseline_store = LocalStore(baseline_path) + + # A directory that exists but holds no arrays (a truncated extraction, + # say) would also compare vacuously. Demand at least one array. + baseline_keys = store_keys(baseline_store) + assert _array_paths(baseline_keys, store_contents(baseline_store, baseline_keys)), ( + f"baseline at {baseline_path.relative_to(test_data_dir)} holds no arrays; " + "the comparison would pass vacuously" + ) test_store = MemoryStore() - to_ngff_zarr(test_store, multiscales, version=version) + to_ngff_zarr( + test_store, multiscales, version=version, scale_strategy=scale_strategy + ) + assert store_equals(baseline_store, test_store) diff --git a/py/test/test_baseline_guard.py b/py/test/test_baseline_guard.py new file mode 100644 index 00000000..9b78e7e6 --- /dev/null +++ b/py/test/test_baseline_guard.py @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC +# SPDX-License-Identifier: MIT +"""A missing baseline must never read as a passing comparison.""" + +import pytest +from ngff_zarr import Methods, to_multiscales, to_ngff_image + +from ._data import store_equals, verify_against_baseline + + +def _tiny_multiscales(): + import numpy as np + + image = to_ngff_image(np.zeros((32, 32), dtype=np.uint8), dims=("y", "x")) + return to_multiscales(image, [2], method=Methods.ITKWASM_GAUSSIAN) + + +def test_missing_baseline_skips_rather_than_passes(): + with pytest.raises(BaseException) as excinfo: + verify_against_baseline( + "does-not-exist", "no/such/baseline.zarr", _tiny_multiscales() + ) + assert excinfo.typename == "Skipped", ( + f"a missing baseline must skip, got {excinfo.typename}" + ) + + +def test_store_equals_requires_identical_key_sets(): + """An empty baseline, or an extra key on either side, must not pass.""" + import zarr + from ngff_zarr import to_ngff_zarr + from packaging import version + from zarr.storage import MemoryStore + + multiscales = _tiny_multiscales() + populated = MemoryStore() + to_ngff_zarr(populated, multiscales, version="0.4") + + assert not store_equals(MemoryStore(), populated), ( + "an empty baseline compared equal to a populated store" + ) + + test_store = MemoryStore() + to_ngff_zarr(test_store, multiscales, version="0.4") + assert store_equals(populated, test_store), ( + "two identical writes should compare equal" + ) + + # zarr 2 has no zarr_format kwarg and writes format 2 anyway. + extra_kwargs = ( + {"zarr_format": 2} + if version.parse(zarr.__version__) >= version.parse("3.0.0b1") + else {} + ) + zarr.create(shape=(1,), store=test_store, path="extra", **extra_kwargs) + assert not store_equals(populated, test_store), ( + "an extra array in the test store went unnoticed" + ) diff --git a/py/test/test_non_power_of_2_scale_factors.py b/py/test/test_non_power_of_2_scale_factors.py index 3bf59a99..6a2821a0 100644 --- a/py/test/test_non_power_of_2_scale_factors.py +++ b/py/test/test_non_power_of_2_scale_factors.py @@ -156,10 +156,12 @@ def test_non_power_of_2_with_dict_scale_factors(): ), f"Expected (40, 10) but got {result.images[2].data.shape}" -def test_scale_strategy_pad_default(): +def test_scale_strategy_defaults_to_pad(): """ - Test that scale_strategy defaults to "pad" and produces shapes from - incremental downsampling (which may differ from exact division). + The default write downsamples incrementally from the previous level, so a + target that is not reachable by an integer factor is missed: from level 1 + (60, 60) a factor-3 target of (40, 40) is out of reach, pad lands on + (30, 30), and factor 4 then has nothing left to do. """ data = np.random.randint(0, 256, size=(120, 120), dtype=np.uint8) image = nz.to_ngff_image(data, scale={"y": 1.0, "x": 1.0}) @@ -168,33 +170,38 @@ def test_scale_strategy_pad_default(): with tempfile.TemporaryDirectory() as tmpdir: zarr_path = f"{tmpdir}/test.ome.zarr" - # No scale_strategy arg → defaults to "pad" + # No scale_strategy arg -> defaults to "pad" nz.to_ngff_zarr(zarr_path, multiscales) result = nz.from_ngff_zarr(zarr_path) - # Level 0 is always exact - assert result.images[0].data.shape == (120, 120) - # Level 1 (factor 2): 120/2 = 60 — power-of-2, same either way - assert result.images[1].data.shape == (60, 60) - # Level 2 (factor 3): pad mode does incremental from level 1 (60) - # 60 // (3/2) = 60 // 1.5 → floor(60/1.5) = 40 if exact, but - # incremental factor = 3/2 = 1.5 → int(1.5) = 1 → 60/1 = 60? - # Actually, _dim_scale_factors computes: next_abs / prev_abs = 3/2 = 1.5 - # Then to_multiscales uses this as a dict factor. - # The gaussian method downsamples by floor division, so: - # 60 // 1 = 60 is wrong. Let me not assert specific pad shapes here. - # Instead, just verify all levels exist and have reasonable sizes. - assert len(result.images) == 4 - for i in range(4): - shape = result.images[i].data.shape - assert len(shape) == 2 - assert shape[0] > 0 - assert shape[1] > 0 - # Each level should be smaller or equal to the previous - if i > 0: - prev_shape = result.images[i - 1].data.shape - assert shape[0] <= prev_shape[0] - assert shape[1] <= prev_shape[1] + expected_shapes = [(120, 120), (60, 60), (30, 30), (30, 30)] + for i, expected in enumerate(expected_shapes): + actual = result.images[i].data.shape + assert actual == expected, f"Level {i}: expected {expected}, got {actual}" + + +def test_scale_strategy_pad_opt_in(): + """ + Test that scale_strategy="pad" downsamples incrementally from the + previous level, missing targets that are not reachable by an integer + factor: from level 1 (60, 60) a factor-3 target of (40, 40) is not + reachable, so pad lands on (30, 30) and factor 4 then has nothing + left to do. + """ + data = np.random.randint(0, 256, size=(120, 120), dtype=np.uint8) + image = nz.to_ngff_image(data, scale={"y": 1.0, "x": 1.0}) + + multiscales = nz.to_multiscales(image, scale_factors=[2, 3, 4]) + + with tempfile.TemporaryDirectory() as tmpdir: + zarr_path = f"{tmpdir}/test.ome.zarr" + nz.to_ngff_zarr(zarr_path, multiscales, scale_strategy="pad") + result = nz.from_ngff_zarr(zarr_path) + + expected_shapes = [(120, 120), (60, 60), (30, 30), (30, 30)] + for i, expected in enumerate(expected_shapes): + actual = result.images[i].data.shape + assert actual == expected, f"Level {i}: expected {expected}, got {actual}" def test_scale_strategy_exact(): diff --git a/py/test/test_to_ngff_zarr_dask_image.py b/py/test/test_to_ngff_zarr_dask_image.py index 56db57b2..c314847e 100644 --- a/py/test/test_to_ngff_zarr_dask_image.py +++ b/py/test/test_to_ngff_zarr_dask_image.py @@ -1,11 +1,21 @@ # SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC # SPDX-License-Identifier: MIT +import platform + import dask.array as da +import pytest from ngff_zarr import Methods, config, to_multiscales, to_ngff_image from ._data import verify_against_baseline +_on_x86 = platform.machine().lower() in ("x86_64", "amd64") + +@pytest.mark.skipif( + not _on_x86, + reason="baselines are generated on x86_64; scipy Gaussian " + "floating point differs on other architectures", +) def test_gaussian_isotropic_scale_factors(input_images): dataset_name = "cthead1" image = input_images[dataset_name] @@ -20,6 +30,11 @@ def test_gaussian_isotropic_scale_factors(input_images): verify_against_baseline(dataset_name, baseline_name, multiscales) +@pytest.mark.skipif( + not _on_x86, + reason="baselines are generated on x86_64; scipy Gaussian " + "floating point differs on other architectures", +) def test_gaussian_isotropic_scale_factors_two_components(input_images): dataset_name = "brain_two_components" image = input_images[dataset_name] diff --git a/py/test/test_to_ngff_zarr_itk.py b/py/test/test_to_ngff_zarr_itk.py index 8b37248a..9b3b663b 100644 --- a/py/test/test_to_ngff_zarr_itk.py +++ b/py/test/test_to_ngff_zarr_itk.py @@ -1,9 +1,14 @@ # SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC # SPDX-License-Identifier: MIT +import platform + +import pytest from ngff_zarr import Methods, to_multiscales, to_ngff_image from ._data import verify_against_baseline +_on_x86 = platform.machine().lower() in ("x86_64", "amd64") + def test_bin_shrink_isotropic_scale_factors(input_images): dataset_name = "cthead1" @@ -54,6 +59,11 @@ def test_bin_shrink_tzyxc(): pass +@pytest.mark.skipif( + not _on_x86, + reason="baselines are generated on x86_64; native ITK Gaussian " + "floating point differs on other architectures", +) def test_gaussian_isotropic_scale_factors(input_images): dataset_name = "cthead1" image = input_images[dataset_name] diff --git a/py/test/test_to_ngff_zarr_itkwasm.py b/py/test/test_to_ngff_zarr_itkwasm.py index f9889390..ff230d2d 100644 --- a/py/test/test_to_ngff_zarr_itkwasm.py +++ b/py/test/test_to_ngff_zarr_itkwasm.py @@ -202,7 +202,11 @@ def test_gaussian_isotropic_scale_factors(input_images): baseline_name = "2_3/ITKWASM_GAUSSIAN.zarr" multiscales = to_multiscales(image, [2, 3], method=Methods.ITKWASM_GAUSSIAN) # store_new_multiscales(dataset_name, baseline_name, multiscales) - verify_against_baseline(dataset_name, baseline_name, multiscales) + # The baselines hold the exact geometry. The default "pad" strategy trades + # it for speed and is covered in test_non_power_of_2_scale_factors.py. + verify_against_baseline( + dataset_name, baseline_name, multiscales, scale_strategy="exact" + ) dataset_name = "MR-head" image = input_images[dataset_name] @@ -213,7 +217,9 @@ def test_gaussian_isotropic_scale_factors(input_images): multiscales = to_multiscales(image, [2, 3, 4], method=Methods.ITKWASM_GAUSSIAN) # from ._data import store_new_multiscales # store_new_multiscales(dataset_name, baseline_name, multiscales) - verify_against_baseline(dataset_name, baseline_name, multiscales) + verify_against_baseline( + dataset_name, baseline_name, multiscales, scale_strategy="exact" + ) # def test_gaussian_anisotropic_scale_factors(input_images): @@ -250,7 +256,13 @@ def test_label_image_isotropic_scale_factors(input_images): baseline_name = "2_3/ITKWASM_LABEL_IMAGE.zarr" multiscales = to_multiscales(image, [2, 3], method=Methods.ITKWASM_LABEL_IMAGE) # store_new_multiscales(dataset_name, baseline_name, multiscales) - verify_against_baseline(dataset_name, baseline_name, multiscales, version=version) + verify_against_baseline( + dataset_name, + baseline_name, + multiscales, + version=version, + scale_strategy="exact", + ) # def test_label_image_anisotropic_scale_factors(input_images): diff --git a/py/test/test_to_ngff_zarr_sharding.py b/py/test/test_to_ngff_zarr_sharding.py index c540f373..3ab50e2c 100644 --- a/py/test/test_to_ngff_zarr_sharding.py +++ b/py/test/test_to_ngff_zarr_sharding.py @@ -24,7 +24,7 @@ def test_zarr_python_sharding(input_images): dataset_name = "cthead1" image = input_images[dataset_name] - baseline_name = "2_4/RFC3_GAUSSIAN.zarr" + baseline_name = "2_4_chunks64/RFC3_GAUSSIAN.zarr" chunks = (64, 64) multiscales = to_multiscales( image, [2, 4], chunks=chunks, method=Methods.ITKWASM_GAUSSIAN @@ -110,7 +110,7 @@ def test_tensorstore_sharding(input_images): dataset_name = "cthead1" image = input_images[dataset_name] - baseline_name = "2_4/RFC3_GAUSSIAN.zarr" + baseline_name = "2_4_chunks64/RFC3_GAUSSIAN.zarr" chunks = (64, 64) multiscales = to_multiscales( image, [2, 4], chunks=chunks, method=Methods.ITKWASM_GAUSSIAN diff --git a/py/test/test_to_ngff_zarr_tensorstore.py b/py/test/test_to_ngff_zarr_tensorstore.py index f951045e..e97ef3e0 100644 --- a/py/test/test_to_ngff_zarr_tensorstore.py +++ b/py/test/test_to_ngff_zarr_tensorstore.py @@ -53,18 +53,25 @@ def test_gaussian_isotropic_scale_factors(input_images): baseline_name = "2_3/ITKWASM_GAUSSIAN.zarr" multiscales = to_multiscales(image, [2, 3], method=Methods.ITKWASM_GAUSSIAN) with tempfile.TemporaryDirectory() as tmpdir: - to_ngff_zarr(tmpdir, multiscales, use_tensorstore=True) + # The baselines hold the exact geometry. The default "pad" strategy + # trades it for speed and is covered in + # test_non_power_of_2_scale_factors.py. + to_ngff_zarr(tmpdir, multiscales, use_tensorstore=True, scale_strategy="exact") multiscales = from_ngff_zarr(tmpdir) - verify_against_baseline(dataset_name, baseline_name, multiscales) + verify_against_baseline( + dataset_name, baseline_name, multiscales, scale_strategy="exact" + ) dataset_name = "MR-head" image = input_images[dataset_name] baseline_name = "2_3_4/ITKWASM_GAUSSIAN.zarr" multiscales = to_multiscales(image, [2, 3, 4], method=Methods.ITKWASM_GAUSSIAN) with tempfile.TemporaryDirectory() as tmpdir: - to_ngff_zarr(tmpdir, multiscales, use_tensorstore=True) + to_ngff_zarr(tmpdir, multiscales, use_tensorstore=True, scale_strategy="exact") multiscales = from_ngff_zarr(tmpdir) - verify_against_baseline(dataset_name, baseline_name, multiscales) + verify_against_baseline( + dataset_name, baseline_name, multiscales, scale_strategy="exact" + ) @pytest.mark.skipif( @@ -232,6 +239,80 @@ def test_tensorstore_chunk_shape_consistency_with_sharding(): ) +def _write_and_read_codecs(tmpdir, **to_ngff_zarr_kwargs): + import json + + data = np.arange(64 * 64, dtype=np.uint16).reshape(64, 64) + image = to_ngff_image(data, dims=("y", "x"), scale={"y": 1.0, "x": 1.0}) + multiscales = to_multiscales(image, [2], method=Methods.DASK_IMAGE_GAUSSIAN) + to_ngff_zarr( + tmpdir, + multiscales, + use_tensorstore=True, + version="0.5", + **to_ngff_zarr_kwargs, + ) + array_metadata = json.loads( + (pathlib.Path(tmpdir) / "scale0" / "image" / "zarr.json").read_text() + ) + return array_metadata["codecs"] + + +@pytest.mark.skipif( + zarr_version < version.parse("3.0.8"), reason="zarr version < 3.0.0b1" +) +def test_tensorstore_codec_chain_preserved(): + """An explicit codec chain is written in order, not reduced to one codec.""" + pytest.importorskip("tensorstore") + from zarr.codecs import BytesCodec, GzipCodec, ZstdCodec + + with tempfile.TemporaryDirectory() as tmpdir: + codecs = _write_and_read_codecs( + tmpdir, + compressors=[BytesCodec(), ZstdCodec(level=3), GzipCodec(level=2)], + ) + assert [codec["name"] for codec in codecs] == ["bytes", "zstd", "gzip"] + assert codecs[1]["configuration"]["level"] == 3 + assert codecs[2]["configuration"]["level"] == 2 + + +@pytest.mark.skipif( + zarr_version < version.parse("3.0.8"), reason="zarr version < 3.0.0b1" +) +def test_tensorstore_codec_chain_bytes_only(): + """A chain holding only the bytes codec writes uncompressed data.""" + pytest.importorskip("tensorstore") + from zarr.codecs import BytesCodec + + with tempfile.TemporaryDirectory() as tmpdir: + codecs = _write_and_read_codecs(tmpdir, compressors=[BytesCodec()]) + assert [codec["name"] for codec in codecs] == ["bytes"] + + +@pytest.mark.skipif( + zarr_version < version.parse("3.0.8"), reason="zarr version < 3.0.0b1" +) +def test_tensorstore_codec_chain_empty(): + """An explicit empty chain writes uncompressed data.""" + pytest.importorskip("tensorstore") + + with tempfile.TemporaryDirectory() as tmpdir: + codecs = _write_and_read_codecs(tmpdir, compressors=()) + assert [codec["name"] for codec in codecs] == ["bytes"] + + +@pytest.mark.skipif( + zarr_version < version.parse("3.0.8"), reason="zarr version < 3.0.0b1" +) +def test_tensorstore_codec_chain_default(): + """Without a codec option the default zstd compression is applied.""" + pytest.importorskip("tensorstore") + + with tempfile.TemporaryDirectory() as tmpdir: + codecs = _write_and_read_codecs(tmpdir) + assert [codec["name"] for codec in codecs] == ["bytes", "zstd"] + + @pytest.mark.skipif( zarr_version < version.parse("3.0.8"), reason="zarr version < 3.0.0b1" ) diff --git a/py/test/test_to_ngff_zarr_v3_compression.py b/py/test/test_to_ngff_zarr_v3_compression.py index 38b186c7..24e53b7d 100644 --- a/py/test/test_to_ngff_zarr_v3_compression.py +++ b/py/test/test_to_ngff_zarr_v3_compression.py @@ -22,7 +22,7 @@ def test_zarr_v3_compression(input_images): dataset_name = "cthead1" image = input_images[dataset_name] - baseline_name = "2_4/RFC3_GAUSSIAN.zarr" + baseline_name = "2_4_chunks64/RFC3_GAUSSIAN.zarr" chunks = (64, 64) multiscales = to_multiscales( image, [2, 4], chunks=chunks, method=Methods.ITKWASM_GAUSSIAN @@ -72,7 +72,7 @@ def test_zarr_v3_compression_with_sharding(input_images): """Test Zarr v3 compression combined with sharding functionality""" dataset_name = "cthead1" image = input_images[dataset_name] - baseline_name = "2_4/RFC3_GAUSSIAN.zarr" + baseline_name = "2_4_chunks64/RFC3_GAUSSIAN.zarr" chunks = (64, 64) multiscales = to_multiscales( image, [2, 4], chunks=chunks, method=Methods.ITKWASM_GAUSSIAN diff --git a/py/test/test_writer_isolation.py b/py/test/test_writer_isolation.py new file mode 100644 index 00000000..2e73f8de --- /dev/null +++ b/py/test/test_writer_isolation.py @@ -0,0 +1,150 @@ +# SPDX-FileCopyrightText: Copyright (c) Fideus Labs LLC +# SPDX-License-Identifier: MIT +"""ngff-zarr should not inherit backend defaults, nor leak into its caller. + +The baseline comparison works on decompressed values, so neither of these +shows up there. They need their own tests. +""" + +import json +import tempfile +from pathlib import Path + +import numpy as np +import pytest +import zarr +from ngff_zarr import config, to_multiscales, to_ngff_image, to_ngff_zarr +from packaging import version + +zarr_version = version.parse(zarr.__version__) + +pytestmark = pytest.mark.skipif( + zarr_version < version.parse("3.0.0b1"), reason="zarr version < 3.0.0b1" +) + + +def _multiscales(shape=(128, 256, 256), dtype=np.uint16): + image = to_ngff_image(np.zeros(shape, dtype=dtype), dims=("z", "y", "x")) + return to_multiscales(image, [2]) + + +def _scale0_metadata(store_path): + return json.loads((Path(store_path) / "scale0" / "image" / "zarr.json").read_text()) + + +def test_tensorstore_matches_zarr_python_by_default(): + """Left unset, TensorStore applies its own defaults: sharded, uncompressed.""" + pytest.importorskip("tensorstore") + ms = _multiscales() + + written = {} + for use_tensorstore in (False, True): + with tempfile.TemporaryDirectory() as tmpdir: + to_ngff_zarr(tmpdir, ms, version="0.5", use_tensorstore=use_tensorstore) + meta = _scale0_metadata(tmpdir) + written[use_tensorstore] = ( + meta["chunk_grid"]["configuration"]["chunk_shape"], + [codec["name"] for codec in meta["codecs"]], + ) + + assert written[True] == written[False], ( + f"backends disagree: zarr-python wrote {written[False]}, " + f"tensorstore wrote {written[True]}" + ) + assert "sharding_indexed" not in written[True][1], ( + "sharding was never requested via chunks_per_shard" + ) + + +@pytest.mark.parametrize( + # No shuffle/typesize given: zarr-python evolves them from the dtype, + # and the TensorStore path must land on the same configuration. + "explicit_shuffle", + [True, False], + ids=["explicit-shuffle", "evolved-from-dtype"], +) +def test_tensorstore_honours_requested_compressor(explicit_shuffle): + """A supplied codec must reach the store with the same configuration + zarr-python would write, not just the same name.""" + pytest.importorskip("tensorstore") + # Import from the submodule: recent zarr no longer re-exports the codec + # classes from zarr.codecs. + from zarr.codecs.blosc import BloscCodec, BloscShuffle + + kwargs = {"cname": "zlib", "clevel": 5} + if explicit_shuffle: + kwargs["shuffle"] = BloscShuffle.shuffle + compressors = BloscCodec(**kwargs) + ms = _multiscales() + + written = {} + for use_tensorstore in (False, True): + with tempfile.TemporaryDirectory() as tmpdir: + to_ngff_zarr( + tmpdir, + ms, + version="0.5", + use_tensorstore=use_tensorstore, + compressors=compressors, + ) + codecs = _scale0_metadata(tmpdir)["codecs"] + written[use_tensorstore] = next( + (codec for codec in codecs if codec["name"] == "blosc"), None + ) + + assert written[False] is not None, "zarr-python did not write blosc" + assert written[True] == written[False], ( + f"backends disagree: zarr-python wrote {written[False]}, " + f"tensorstore wrote {written[True]}" + ) + + +def test_to_multiscales_leaves_its_input_alone(): + """Callers reuse images across calls; results must not depend on history.""" + image = to_ngff_image(np.zeros((512, 512), dtype=np.uint8), dims=("y", "x")) + data_before = image.data + callbacks_before = list(image.computed_callbacks) + + to_multiscales(image, [2, 4], chunks=(64, 64)) + + assert image.data is data_before, "to_multiscales replaced the caller's data" + assert image.computed_callbacks == callbacks_before, ( + "to_multiscales appended to the caller's computed_callbacks" + ) + + +def test_to_multiscales_does_not_leak_cache_cleanup(): + """The large-image path registers a cleanup callback; keep it local.""" + original_target = config.memory_target + config.memory_target = int(1e5) + try: + image = to_ngff_image(np.zeros((512, 512), dtype=np.uint8), dims=("y", "x")) + before = len(image.computed_callbacks) + to_multiscales(image, [2]) + assert len(image.computed_callbacks) == before, ( + "cache cleanup piled up on the caller's image" + ) + finally: + config.memory_target = original_target + + +def test_to_ngff_zarr_leaves_multiscales_alone(): + """The write loop swaps data for on-disk views and regenerates scales; + none of that may reach the caller's multiscales.""" + image = to_ngff_image( + np.zeros((120, 120), dtype=np.uint8), dims=("y", "x"), scale={"y": 1, "x": 1} + ) + ms = to_multiscales(image, [2, 3, 4]) + data_before = [img.data for img in ms.images] + shapes_before = [img.data.shape for img in ms.images] + + with tempfile.TemporaryDirectory() as tmpdir: + to_ngff_zarr(tmpdir, ms, version="0.5") + + for i, img in enumerate(ms.images): + assert img.data is data_before[i], ( + f"to_ngff_zarr replaced the caller's data at scale {i}" + ) + assert img.data.shape == shapes_before[i], ( + f"to_ngff_zarr reshaped the caller's scale {i}" + ) diff --git a/ts/deno.json b/ts/deno.json index 6f50689f..44323459 100644 --- a/ts/deno.json +++ b/ts/deno.json @@ -28,7 +28,7 @@ }, "imports": { "@itk-wasm/compare-images": "npm:@itk-wasm/compare-images@^5.4.1", - "@itk-wasm/downsample": "npm:@itk-wasm/downsample@^1.8.1", + "@itk-wasm/downsample": "npm:@itk-wasm/downsample@^2.0.0", "@itk-wasm/image-io": "npm:@itk-wasm/image-io@^1.6.0", "@std/assert": "jsr:@std/assert@^1.0.18", "@std/cli": "jsr:@std/cli@^1.0.27", diff --git a/ts/deno.lock b/ts/deno.lock index 6ceb5f73..3f10e1df 100644 --- a/ts/deno.lock +++ b/ts/deno.lock @@ -24,11 +24,11 @@ "npm:@fideus-labs/fizarrita@^1.3.0": "1.3.0_zarrita@0.6.1", "npm:@fideus-labs/worker-pool@1": "1.0.0", "npm:@itk-wasm/compare-images@^5.4.1": "5.4.1", - "npm:@itk-wasm/downsample@^1.8.1": "1.8.1", + "npm:@itk-wasm/downsample@2": "2.0.0", "npm:@itk-wasm/image-io@^1.6.0": "1.6.0", "npm:@playwright/test@^1.58.1": "1.58.2", "npm:fflate@~0.8.2": "0.8.2", - "npm:itk-wasm@^1.0.0-b.196": "1.0.0-b.196", + "npm:itk-wasm@^1.0.0-b.196": "1.0.0-b.199", "npm:microdiff@^1.5.0": "1.5.0", "npm:numcodecs@~0.3.2": "0.3.2", "npm:reference-spec-reader@0.2": "0.2.0", @@ -200,8 +200,8 @@ ], "bin": true }, - "@itk-wasm/downsample@1.8.1": { - "integrity": "sha512-8j29GkeIECmMgS70DyLHnLMT81Ua5HZ28xSifhshEK2nERq6x4V8nm+CjJDpnKpNE3ZFe3cIY/MES5lr4iF/FQ==", + "@itk-wasm/downsample@2.0.0": { + "integrity": "sha512-3EIeESnFE17i818xFSBO47tyJxgtDLhOkSJlsFixysO9jM2guBYuFD7u1rifAbacPeXb4zVhkyVDVO/78FzVwg==", "dependencies": [ "itk-wasm" ] @@ -317,6 +317,12 @@ "actor@2.3.1": { "integrity": "sha512-ST/3wnvcP2tKDXnum7nLCLXm+/rsf8vPocXH2Fre6D8FQwNkGDd4JEitBlXj007VQJfiGYRQvXqwOBZVi+JtRg==" }, + "agent-base@6.0.2": { + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": [ + "debug" + ] + }, "asynckit@0.4.0": { "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, @@ -326,11 +332,12 @@ "possible-typed-array-names" ] }, - "axios@1.13.5": { - "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "axios@1.19.0": { + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", "dependencies": [ "follow-redirects", "form-data", + "https-proxy-agent", "proxy-from-env" ] }, @@ -433,6 +440,12 @@ "core-util-is@1.0.3": { "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "debug@4.4.3": { + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": [ + "ms" + ] + }, "decompress-tar@4.1.1": { "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dependencies": [ @@ -554,8 +567,8 @@ "graceful-fs" ] }, - "follow-redirects@1.15.11": { - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" + "follow-redirects@1.16.0": { + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==" }, "for-each@0.3.5": { "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", @@ -563,8 +576,8 @@ "is-callable" ] }, - "form-data@4.0.5": { - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "form-data@4.0.6": { + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dependencies": [ "asynckit", "combined-stream", @@ -669,12 +682,19 @@ "has-symbols" ] }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "hasown@2.0.4": { + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dependencies": [ "function-bind" ] }, + "https-proxy-agent@5.0.1": { + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": [ + "agent-base", + "debug" + ] + }, "ieee754@1.2.1": { "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, @@ -820,8 +840,8 @@ "it-stream-types@2.0.2": { "integrity": "sha512-Rz/DEZ6Byn/r9+/SBCuJhpPATDF9D+dz5pbgSUyBsCDtza6wtNATrz/jz1gDyNanC3XdLboriHnOC925bZRBww==" }, - "itk-wasm@1.0.0-b.196": { - "integrity": "sha512-LJoGEFQLgE2lKERTEOq9XFtMTnPUwR0TBWxTA/hI1QPqvXfVNHQGv+vzHTrRCq2GAM+vtWZuVt/XecTBBkN6Hg==", + "itk-wasm@1.0.0-b.199": { + "integrity": "sha512-+02eJud1aRTlSrwInhsx8eWXqiIxWZYLeMy7pBegPY17YbSKBwr9lfMbY/XbnpyaleRdjq3r81yauciEvYZc5A==", "dependencies": [ "@emnapi/wasi-threads", "@itk-wasm/dam", @@ -907,6 +927,9 @@ "mri@1.2.0": { "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "multiformats@13.4.2": { "integrity": "sha512-eh6eHCrRi1+POZ3dA+Dq1C6jhP1GNtr9CRINMb67OKzqW9I5DUuZM/3jLPlzhgpGeiNUlEGEbkCYChXMCc/8DQ==" }, @@ -1008,8 +1031,8 @@ "uint8arrays" ] }, - "proxy-from-env@1.1.0": { - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "proxy-from-env@2.1.0": { + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==" }, "rabin-rs@2.1.0": { "integrity": "sha512-5y72gAXPzIBsAMHcpxZP8eMDuDT98qMP1BqSDHRbHkJJXEgWIN1lA47LxUqzsK6jknOJtgfkQr9v+7qMlFDm6g==" @@ -1224,7 +1247,7 @@ "npm:@fideus-labs/fizarrita@^1.3.0", "npm:@fideus-labs/worker-pool@1", "npm:@itk-wasm/compare-images@^5.4.1", - "npm:@itk-wasm/downsample@^1.8.1", + "npm:@itk-wasm/downsample@2", "npm:@itk-wasm/image-io@^1.6.0", "npm:@playwright/test@^1.58.1", "npm:fflate@~0.8.2", diff --git a/ts/src/methods/itkwasm-browser.ts b/ts/src/methods/itkwasm-browser.ts index 1636e824..8417561b 100644 --- a/ts/src/methods/itkwasm-browser.ts +++ b/ts/src/methods/itkwasm-browser.ts @@ -18,8 +18,11 @@ import type { ZarrCodec } from "../utils/codecs.ts"; import { defaultCodecs } from "../utils/codecs.ts"; import { zarrGet, zarrSet } from "../utils/worker_pool.ts"; import { + castImageToFloat32, + castImageToIntegerType, type DimFactors, dimScaleFactors, + isIntegerComponentType, itkImageToZarr, MAX_VECTOR_COMPONENTS, nextScaleMetadata, @@ -324,11 +327,22 @@ async function downsampleGaussian( // Use all zeros for cropRadius const cropRadius = new Array(shrinkFactors.length).fill(0); + // The Gaussian filter loses precision on integer inputs; cast to + // float32 and round back afterwards, mirroring the Python port. + const originalComponentType = itkImage.imageType.componentType; + const needsFloatWorkaround = isIntegerComponentType(originalComponentType); + const inputImage = needsFloatWorkaround + ? castImageToFloat32(itkImage) + : itkImage; + // Perform downsampling using browser-compatible function - const { downsampled } = await downsample(itkImage, { + const { downsampled: downsampledRaw } = await downsample(inputImage, { shrinkFactors, cropRadius: cropRadius, }); + const downsampled = needsFloatWorkaround + ? castImageToIntegerType(downsampledRaw, originalComponentType) + : downsampledRaw; // Compute new metadata const [translation, scale] = nextScaleMetadata( diff --git a/ts/src/methods/itkwasm-node.ts b/ts/src/methods/itkwasm-node.ts index a97b020d..0b496548 100644 --- a/ts/src/methods/itkwasm-node.ts +++ b/ts/src/methods/itkwasm-node.ts @@ -23,8 +23,11 @@ import type { ZarrCodec } from "../utils/codecs.ts"; import { defaultCodecs } from "../utils/codecs.ts"; import { zarrGet, zarrSet } from "../utils/worker_pool.ts"; import { + castImageToFloat32, + castImageToIntegerType, type DimFactors, dimScaleFactors, + isIntegerComponentType, itkImageToZarr, MAX_VECTOR_COMPONENTS, nextScaleMetadata, @@ -329,11 +332,22 @@ async function downsampleGaussian( // Use all zeros for cropRadius const cropRadius = new Array(shrinkFactors.length).fill(0); + // The Gaussian filter loses precision on integer inputs; cast to + // float32 and round back afterwards, mirroring the Python port. + const originalComponentType = itkImage.imageType.componentType; + const needsFloatWorkaround = isIntegerComponentType(originalComponentType); + const inputImage = needsFloatWorkaround + ? castImageToFloat32(itkImage) + : itkImage; + // Perform downsampling using Node-compatible function - const { downsampled } = await downsample(itkImage, { + const { downsampled: downsampledRaw } = await downsample(inputImage, { shrinkFactors, cropRadius: cropRadius, }); + const downsampled = needsFloatWorkaround + ? castImageToIntegerType(downsampledRaw, originalComponentType) + : downsampledRaw; // Compute new metadata const [translation, scale] = nextScaleMetadata( diff --git a/ts/src/methods/itkwasm-shared.ts b/ts/src/methods/itkwasm-shared.ts index c7029d87..ce941c97 100644 --- a/ts/src/methods/itkwasm-shared.ts +++ b/ts/src/methods/itkwasm-shared.ts @@ -6,6 +6,7 @@ * Used by both browser and Node implementations */ +import { castImage } from "itk-wasm"; import type { Image } from "itk-wasm"; import * as zarr from "zarrita"; @@ -254,6 +255,102 @@ export function getItkComponentType( return "float32"; } +/** + * Integer component types eligible for the Gaussian float32 workaround + */ +export type IntegerComponentType = + | "uint8" + | "int8" + | "uint16" + | "int16" + | "uint32" + | "int32"; + +const INTEGER_COMPONENT_RANGES: Record< + IntegerComponentType, + [number, number] +> = { + uint8: [0, 255], + int8: [-128, 127], + uint16: [0, 65535], + int16: [-32768, 32767], + uint32: [0, 4294967295], + int32: [-2147483648, 2147483647], +}; + +export function isIntegerComponentType( + componentType: unknown, +): componentType is IntegerComponentType { + return typeof componentType === "string" && + Object.hasOwn(INTEGER_COMPONENT_RANGES, componentType); +} + +/** + * Round half to even, matching NumPy's rint used by the Python port. + */ +export function rintHalfToEven(value: number): number { + const floor = Math.floor(value); + const frac = value - floor; + if (frac < 0.5) return floor; + if (frac > 0.5) return floor + 1; + return floor % 2 === 0 ? floor : floor + 1; +} + +/** + * Cast an ITK-Wasm image to float32. + * + * itkwasm-downsample's Gaussian filter performs internal arithmetic that + * loses precision on integer inputs (e.g. a uint16 input of 1 produces 0). + * The Python port casts integer inputs to float32 before the Gaussian + * downsample, then rounds and casts back; this helper and + * castImageToIntegerType mirror that so both ports produce identical + * pixel values. + */ +export function castImageToFloat32(image: Image): Image { + if (image.data === null) { + throw new Error("Image data is null"); + } + return castImage(image, { componentType: "float32" }); +} + +/** + * Round a float image back to the given integer type, clamping to the + * type's range. Matches np.clip(np.rint(data), min, max) in the Python + * port's float workaround. + * + * castImage performs the type conversion; it truncates raw floats, so the + * values are rounded and clamped first. The intermediate is float64 because + * float32 cannot represent every uint32/int32 value exactly. + */ +export function castImageToIntegerType( + image: Image, + componentType: IntegerComponentType, +): Image { + const src = image.data; + if (src === null) { + throw new Error("Image data is null"); + } + const [min, max] = INTEGER_COMPONENT_RANGES[componentType]; + const rounded = new Float64Array(src.length); + for (let i = 0; i < src.length; i++) { + let value = rintHalfToEven(src[i] as number); + if (value < min) { + value = min; + } else if (value > max) { + value = max; + } + rounded[i] = value; + } + return castImage( + { + ...image, + imageType: { ...image.imageType, componentType: "float64" }, + data: rounded, + }, + { componentType }, + ); +} + /** * Create identity matrix for ITK direction */ diff --git a/ts/test/baseline_comparison_test.ts b/ts/test/baseline_comparison_test.ts index 709417d2..98c32ead 100644 --- a/ts/test/baseline_comparison_test.ts +++ b/ts/test/baseline_comparison_test.ts @@ -14,7 +14,6 @@ import { assertEquals, assertExists } from "@std/assert"; import { join } from "@std/path"; import { readImageNode, writeImageNode } from "@itk-wasm/image-io"; -import { compareImagesNode } from "@itk-wasm/compare-images"; import type { Image as ItkWasmImage } from "itk-wasm"; import { Methods } from "../src/types/methods.ts"; @@ -70,60 +69,72 @@ async function writeScaleImage( } /** - * Helper to compare two ITK-Wasm images with tolerance + * Compare two ITK-Wasm images by value: geometry and pixel data must be + * identical. Mirrors the Python port's store_equals, which compares + * decompressed arrays element-wise, so both ports assert the same thing. + * + * @itk-wasm/compare-images is deliberately not used: the comparison wanted + * here is exact — zero threshold, zero pixels tolerance — which is plain + * element-wise equality, so a Wasm round trip would buy nothing. (It also + * threw on 3D images with a non-identity direction, fixed upstream in + * InsightSoftwareConsortium/ITK-Wasm#1582.) */ -async function compareImages( +function compareImages( testImage: ItkWasmImage, baselineImage: ItkWasmImage, testName: string, -): Promise { - try { - const result = await compareImagesNode(testImage, { - baselineImages: [baselineImage], - differenceThreshold: 0.0, - radiusTolerance: 0, - numberOfPixelsTolerance: 0, - ignoreBoundaryPixels: false, - }); +): void { + assertEquals(testImage.size, baselineImage.size, `${testName}: size`); + assertEquals( + testImage.spacing, + baselineImage.spacing, + `${testName}: spacing`, + ); + assertEquals(testImage.origin, baselineImage.origin, `${testName}: origin`); + assertEquals( + Array.from(testImage.direction as ArrayLike), + Array.from(baselineImage.direction as ArrayLike), + `${testName}: direction`, + ); + assertEquals( + testImage.imageType.componentType, + baselineImage.imageType.componentType, + `${testName}: componentType`, + ); - // Check if images match - const metrics = result.metrics as { - almostEqual?: boolean; - numberOfPixelsWithDifferences?: number; - }; + const testData = testImage.data; + const baselineData = baselineImage.data; + assertExists(testData, `${testName}: test image data`); + assertExists(baselineData, `${testName}: baseline image data`); + assertEquals( + testData.length, + baselineData.length, + `${testName}: data length`, + ); - if (!metrics.almostEqual) { - console.error( - `❌ ${testName} failed: ${metrics.numberOfPixelsWithDifferences} pixels differ`, - ); + let diffCount = 0; + let diffSum = 0; + let firstDiffIndex = -1; + for (let i = 0; i < testData.length; i++) { + // Compare raw values (works for bigint arrays too); Number() only for + // the diagnostics. + if (testData[i] !== baselineData[i]) { + diffCount++; + diffSum += Number(testData[i]) - Number(baselineData[i]); + if (firstDiffIndex < 0) firstDiffIndex = i; } - - assertEquals( - metrics.almostEqual, - true, - `Images should match for ${testName}`, - ); - assertEquals( - metrics.numberOfPixelsWithDifferences, - 0, - `No pixels should differ for ${testName}`, + } + if (diffCount > 0) { + const meanDiff = (diffSum / diffCount).toFixed(4); + console.error( + `❌ ${testName} failed: ${diffCount}/${testData.length} pixels differ, ` + + `mean diff ${meanDiff}, first at index ${firstDiffIndex} ` + + `(test=${testData[firstDiffIndex]}, baseline=${ + baselineData[firstDiffIndex] + })`, ); - } catch (error) { - console.error(`Error comparing images for ${testName}:`, error); - console.error("Test image info:", { - size: testImage.size, - spacing: testImage.spacing, - origin: testImage.origin, - dataLength: testImage.data?.length, - }); - console.error("Baseline image info:", { - size: baselineImage.size, - spacing: baselineImage.spacing, - origin: baselineImage.origin, - dataLength: baselineImage.data?.length, - }); - throw error; } + assertEquals(diffCount, 0, `No pixels should differ for ${testName}`); } Deno.test("cthead1 - ITKWASM_GAUSSIAN scale factors [2, 4]", async () => { @@ -320,13 +331,10 @@ Deno.test("MR-head - ITKWASM_GAUSSIAN scale factors [2, 3, 4]", async () => { const inputPath = join(INPUT_DIR, "MR-head.nrrd"); const itkImage = await readImageNode(inputPath); assertExists(itkImage); - // The baseline store predates automatic anatomical-orientation writing and - // carries no orientation, so its ITK direction is identity. Disable - // orientation here to compare downsampled pixel output apples-to-apples; the - // orientation round-trip itself is covered by rfc4_integration_test.ts. - const ngffImage = await itkImageToNgffImage(itkImage, { - addAnatomicalOrientation: false, - }); + // The baseline store carries RFC-4 anatomical orientation, so keep it on + // the test side too; both are converted back to an ITK direction by + // ngffImageToItkImage before comparison. + const ngffImage = await itkImageToNgffImage(itkImage); // Generate multiscales const multiscales = await toMultiscales(ngffImage, {