diff --git a/changes/4553.bugfix.md b/changes/4553.bugfix.md new file mode 100644 index 0000000000..07a384c15a --- /dev/null +++ b/changes/4553.bugfix.md @@ -0,0 +1 @@ +Preserve the declared chunk shape after Numcodecs filters and decode transposed chunks in logical C order. diff --git a/src/zarr/codecs/numcodecs/_codecs.py b/src/zarr/codecs/numcodecs/_codecs.py index c980743166..bb2f0a3ab2 100644 --- a/src/zarr/codecs/numcodecs/_codecs.py +++ b/src/zarr/codecs/numcodecs/_codecs.py @@ -155,7 +155,7 @@ def __init__(self, **codec_config: JSON) -> None: super().__init__(**codec_config) def _decode_sync(self, chunk_data: NDBuffer, chunk_spec: ArraySpec) -> NDBuffer: - chunk_ndarray = chunk_data.as_ndarray_like() + chunk_ndarray = np.ascontiguousarray(chunk_data.as_ndarray_like()) out = self._codec.decode(chunk_ndarray) return chunk_spec.prototype.nd_buffer.from_ndarray_like(out.reshape(chunk_spec.shape)) @@ -164,6 +164,7 @@ def _encode_sync(self, chunk_data: NDBuffer, chunk_spec: ArraySpec) -> NDBuffer: # would be encoded in transposed element order (gh-3558) chunk_ndarray = np.ascontiguousarray(chunk_data.as_ndarray_like()) out = self._codec.encode(chunk_ndarray) + out = out.reshape(self.resolve_metadata(chunk_spec).shape) return chunk_spec.prototype.nd_buffer.from_ndarray_like(out) async def _encode_single(self, chunk_data: NDBuffer, chunk_spec: ArraySpec) -> NDBuffer: diff --git a/tests/test_codecs/test_numcodecs.py b/tests/test_codecs/test_numcodecs.py index d78b73f34c..93a03cedfc 100644 --- a/tests/test_codecs/test_numcodecs.py +++ b/tests/test_codecs/test_numcodecs.py @@ -10,6 +10,7 @@ from zarr import config, create_array, open_array from zarr.abc.numcodec import _is_numcodec, _is_numcodec_cls +from zarr.codecs import TransposeCodec from zarr.codecs import numcodecs as _numcodecs from zarr.errors import UnknownCodecError from zarr.registry import get_codec_class, get_numcodec @@ -135,9 +136,11 @@ def test_generic_compressor(codec_class: type[_numcodecs._NumcodecsBytesBytesCod "astype", ], ) +@pytest.mark.parametrize("transpose", [False, True]) def test_generic_filter( codec_class: type[_numcodecs._NumcodecsArrayArrayCodec], codec_config: dict[str, JSON], + transpose: bool, ) -> None: data = np.linspace(0, 10, 256, dtype="float32").reshape((16, 16)) @@ -149,6 +152,7 @@ def test_generic_filter( fill_value=0, filters=[ codec_class(**codec_config), + *([TransposeCodec(order=(1, 0))] if transpose else []), ], )