Skip to content

Extensions for N5 support #46

Description

@clbarnes

I am hacking on N5 support for zarrs and it can pretty much all be decomposed into extensions.

May be of interest to @bogovicj @mkitti @d-v-b .

There's a MVP which would allow metadata-only conversions from N5 to Zarr - i.e. owners of N5 data would just stick a zarr.json file next to every attributes.json.
The stretch goal of needing no on-disk changes would require a storage transformer, which there's not a lot of prior art for, and I'm not sure it's viable in any case (see below).

Chunk key encoding

Needs to turn the request for chunk (z,y,x) into the path "x/y/z".

Chunk grid

N5 blocks each encode their own shape, with the idea being that blocks at the end of an array where the array shape isn't an integer multiple of the block shape can encode that they are effectively truncated.
Assuming that this is the only case where this truncation happens, we could define a chunk grid which encodes this expectation.
Zarrs already does this with the regular_bounded chunk grid - I'm not sure if there are equivalents elsewhere in the ecosystem.

Note that in zarr-python v2's N5 support (and tensorstore), the trailing chunks were padded rather than truncated.
Such data would probably cause the regular_bounded chunk grid to fail.

Codec

N5 blocks comprise a header followed by encoded data, which means the N5 codec must be an array-to-bytes codec which has compression configuration inside it, rather than as part of the outermost codec chain.

N5 block data is always see comment below C order (I think; not entirely clear), despite the blocks themselves being in F order (I think) , and always bigendian. So config like

{
  "node_type": "array",
  ...
  "codecs": [
    {
      "name": "n5",
      "configuration": {
        // N5's compression is effectively an optional bytes-to-bytes codec
        "bytes_to_bytes_codec": {
          "name": "gzip",
          "configuration": { "level": 6 }
        }
      }
    }
  ]
}

would result in a Zarr codec which, on read

  1. reads the header
    • check for default/varlen/object modes
    • determine the size of the block (should be the chunk size, or smaller if it's on the end)
  2. strips the header and applies a zarr codec chain for the described decompression, then bigendian byte ordering
  3. possibly an array-to-array codec for padding the (truncated N5) end block shape to the (padded zarr) end chunk shape, as an alternative to the regular_bounded chunk grid above

This should be the only codec.

Storage transformer?

My current implementation is a store which wraps any other zarr store and redirects zarr.json requests to attributes.json, parses the N5 metadata response and re-encodes it as Zarr metadata.
This seems like a job for a storage transformer.

However, we can only read a storage transformer configuration from a zarr.json, and if the target data has a zarr.json, then we don't need the storage transformer (as the N5 codec and chunk key encoding would both be in that Zarr metadata).
So it's not clear how useful this would actually be.
Instead, we could just describe the steps an N5StoreWrapper would need to take and leave it up to the implementors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions