From 7c761c908dc60b76b77f4a14becb6f70a61d45b9 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 29 Nov 2024 16:14:48 -0500 Subject: [PATCH 1/4] ENH: Support reading and validating OME-Zarr v0.1 --- ngff_zarr/from_ngff_zarr.py | 58 ++++++--- ngff_zarr/spec/0.1/copyright.include | 4 + ngff_zarr/spec/0.1/schemas/image.schema | 112 ++++++++++++++++++ ngff_zarr/spec/0.1/schemas/plate.schema | 112 ++++++++++++++++++ .../spec/0.1/schemas/strict_image.schema | 19 +++ ngff_zarr/spec/0.1/schemas/well.schema | 47 ++++++++ test/test_ngff_validation.py | 8 ++ 7 files changed, 341 insertions(+), 19 deletions(-) create mode 100644 ngff_zarr/spec/0.1/copyright.include create mode 100644 ngff_zarr/spec/0.1/schemas/image.schema create mode 100644 ngff_zarr/spec/0.1/schemas/plate.schema create mode 100644 ngff_zarr/spec/0.1/schemas/strict_image.schema create mode 100644 ngff_zarr/spec/0.1/schemas/well.schema diff --git a/ngff_zarr/from_ngff_zarr.py b/ngff_zarr/from_ngff_zarr.py index 3f1f79f9..5749a8c7 100644 --- a/ngff_zarr/from_ngff_zarr.py +++ b/ngff_zarr/from_ngff_zarr.py @@ -1,7 +1,7 @@ from collections.abc import MutableMapping from pathlib import Path from typing import Union, Optional -from packaging import version +import packaging.version import dask.array import zarr @@ -18,7 +18,7 @@ from .v04.zarr_metadata import Axis, Dataset, Scale, Translation from .validate import validate as validate_ngff -zarr_version = version.parse(zarr.__version__) +zarr_version = packaging.version.parse(zarr.__version__) zarr_version_major = zarr_version.major @@ -48,7 +48,11 @@ def from_ngff_zarr( format_kwargs = {} if version and zarr_version_major >= 3: - format_kwargs = {"zarr_format": 2} if version == "0.4" else {"zarr_format": 3} + format_kwargs = ( + {"zarr_format": 2} + if packaging.version.parse(version) < packaging.version.parse("0.5") + else {"zarr_format": 3} + ) root = zarr.open_group(store, mode="r", **format_kwargs) root_attrs = root.attrs.asdict() @@ -66,16 +70,22 @@ def from_ngff_zarr( else: metadata = root.attrs["multiscales"][0] - dims = [a["name"] for a in metadata["axes"]] + if "axes" not in metadata: + from .v04.zarr_metadata import supported_dims + + dims = list(reversed(supported_dims)) + else: + dims = [a["name"] for a in metadata["axes"]] name = "image" if name in metadata: name = metadata["name"] units = {d: None for d in dims} - for axis in metadata["axes"]: - if "unit" in axis: - units[axis["name"]] = axis["unit"] + if "axes" in metadata: + for axis in metadata["axes"]: + if "unit" in axis: + units[axis["name"]] = axis["unit"] images = [] datasets = [] @@ -85,17 +95,18 @@ def from_ngff_zarr( scale = {d: 1.0 for d in dims} translation = {d: 0.0 for d in dims} coordinateTransformations = [] - for transformation in dataset["coordinateTransformations"]: - if "scale" in transformation: - scale = transformation["scale"] - scale = dict(zip(dims, scale)) - coordinateTransformations.append(Scale(transformation["scale"])) - elif "translation" in transformation: - translation = transformation["translation"] - translation = dict(zip(dims, translation)) - coordinateTransformations.append( - Translation(transformation["translation"]) - ) + if "coordinateTransformations" in dataset: + for transformation in dataset["coordinateTransformations"]: + if "scale" in transformation: + scale = transformation["scale"] + scale = dict(zip(dims, scale)) + coordinateTransformations.append(Scale(transformation["scale"])) + elif "translation" in transformation: + translation = transformation["translation"] + translation = dict(zip(dims, translation)) + coordinateTransformations.append( + Translation(transformation["translation"]) + ) datasets.append( Dataset( path=dataset["path"], @@ -107,7 +118,16 @@ def from_ngff_zarr( images.append(ngff_image) metadata.pop("@type", None) - axes = [Axis(**axis) for axis in metadata["axes"]] + if "axes" in metadata: + axes = [Axis(**axis) for axis in metadata["axes"]] + else: + axes = [ + Axis(name="t", type="time"), + Axis(name="c", type="channel"), + Axis(name="z", type="space"), + Axis(name="y", type="space"), + Axis(name="x", type="space"), + ] coordinateTransformations = None if "coordinateTransformations" in metadata: coordinateTransformations = metadata["coordinateTransformations"] diff --git a/ngff_zarr/spec/0.1/copyright.include b/ngff_zarr/spec/0.1/copyright.include new file mode 100644 index 00000000..f0def708 --- /dev/null +++ b/ngff_zarr/spec/0.1/copyright.include @@ -0,0 +1,4 @@ +Copyright © 2020-[YEAR] +OME® +(U. Dundee). +OME trademark rules apply. diff --git a/ngff_zarr/spec/0.1/schemas/image.schema b/ngff_zarr/spec/0.1/schemas/image.schema new file mode 100644 index 00000000..5a97f3f2 --- /dev/null +++ b/ngff_zarr/spec/0.1/schemas/image.schema @@ -0,0 +1,112 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.1/schemas/image.schema", + "title": "NGFF Image", + "description": "JSON from OME-NGFF .zattrs", + "type": "object", + "properties": { + "multiscales": { + "description": "The multiscale datasets for this image", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "datasets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + }, + "required": ["path"] + } + }, + "version": { + "type": "string", + "enum": [ + "0.1" + ] + }, + "metadata": { + "type": "object", + "properties": { + "method": { + "type": "string" + }, + "version": { + "type": "string" + } + } + } + }, + "required": [ + "datasets" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "omero": { + "type": "object", + "properties": { + "channels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "window": { + "type": "object", + "properties": { + "end": { + "type": "number" + }, + "max": { + "type": "number" + }, + "min": { + "type": "number" + }, + "start": { + "type": "number" + } + }, + "required": [ + "start", + "min", + "end", + "max" + ] + }, + "label": { + "type": "string" + }, + "family": { + "type": "string" + }, + "color": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "window", + "color" + ] + } + } + }, + "required": [ + "channels" + ] + } + }, + "required": [ "multiscales" ] +} diff --git a/ngff_zarr/spec/0.1/schemas/plate.schema b/ngff_zarr/spec/0.1/schemas/plate.schema new file mode 100644 index 00000000..f67a135c --- /dev/null +++ b/ngff_zarr/spec/0.1/schemas/plate.schema @@ -0,0 +1,112 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.1/schemas/plate.schema", + "title": "OME-NGFF plate schema", + "description": "JSON from OME-NGFF Plate .zattrs", + "type": "object", + "properties": { + "plate": { + "type": "object", + "properties": { + "version": { + "type": "string", + "enum": [ + "0.1" + ] + }, + "name": { + "type": "string" + }, + "columns": { + "description": "Columns of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "rows": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "wells": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + }, + "required": [ + "path" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "field_count": { + "description": "Maximum number of fields per view across all wells." + }, + "acquisitions": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "maximumfieldcount": { + "type": "number" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "starttime": { + "type": "number" + } + }, + "required": [ + "id" + ] + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "version", "columns", "rows", "wells" + ] + } + }, + "required": [ + "plate" + ] +} diff --git a/ngff_zarr/spec/0.1/schemas/strict_image.schema b/ngff_zarr/spec/0.1/schemas/strict_image.schema new file mode 100644 index 00000000..497328bb --- /dev/null +++ b/ngff_zarr/spec/0.1/schemas/strict_image.schema @@ -0,0 +1,19 @@ +{ + "$id": "https://ngff.openmicroscopy.org/0.1/schemas/strict_image.schema", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.1/schemas/image.schema" + }, + { + "properties": { + "multiscales": { + "items": { + "required": [ + "version", "metadata", "type", "name" + ] + } + } + } + } + ] +} diff --git a/ngff_zarr/spec/0.1/schemas/well.schema b/ngff_zarr/spec/0.1/schemas/well.schema new file mode 100644 index 00000000..02475934 --- /dev/null +++ b/ngff_zarr/spec/0.1/schemas/well.schema @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.1/schemas/well.schema", + "title": "OME-NGFF well schema", + "description": "JSON from OME-NGFF .zattrs", + "type": "object", + "properties": { + "well": { + "type": "object", + "properties": { + "images": { + "description": "The fields of view for this well", + "type": "array", + "items": { + "type": "object", + "properties": { + "acquisition": { + "description": "A unique identifier within the context of the plate", + "type": "integer" + }, + "path": { + "description": "The path for this field of view subgroup", + "type": "string", + "pattern": "^[A-Za-z0-9]+$" + } + }, + "required": [ + "path" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "version": { + "description": "The version of the specification", + "type": "string", + "enum": [ + "0.1" + ] + } + }, + "required": [ + "images" + ] + } + } +} diff --git a/test/test_ngff_validation.py b/test/test_ngff_validation.py index d2656ae8..2c506c50 100644 --- a/test/test_ngff_validation.py +++ b/test/test_ngff_validation.py @@ -1,3 +1,5 @@ +from pathlib import Path + import numpy as np import zarr from ngff_zarr import ( @@ -36,6 +38,12 @@ def test_y_x_valid_ngff(): check_valid_ngff(multiscale) +def test_validate_0_1(): + test_store = Path(__file__).parent / "data" / "input" / "v01" / "6001251.zarr" + multiscales = from_ngff_zarr(test_store, validate=True, version="0.1") + print(multiscales) + + # def test_z_y_x_valid_ngff(): # array = np.random.random((32, 32, 16)) # image = to_spatial_image(array) From c44e03e39e39f1f8ec3a495d27e6e191137e5dee Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 29 Nov 2024 16:21:00 -0500 Subject: [PATCH 2/4] ENH: Support reading and validating OME-Zarr v0.2 --- ngff_zarr/spec/0.2/copyright.include | 4 + ngff_zarr/spec/0.2/schemas/image.schema | 101 ++++++++++++++++++++ ngff_zarr/spec/0.2/schemas/plate.schema | 119 ++++++++++++++++++++++++ ngff_zarr/spec/0.2/schemas/well.schema | 47 ++++++++++ test/test_ngff_validation.py | 16 ++++ 5 files changed, 287 insertions(+) create mode 100644 ngff_zarr/spec/0.2/copyright.include create mode 100644 ngff_zarr/spec/0.2/schemas/image.schema create mode 100644 ngff_zarr/spec/0.2/schemas/plate.schema create mode 100644 ngff_zarr/spec/0.2/schemas/well.schema diff --git a/ngff_zarr/spec/0.2/copyright.include b/ngff_zarr/spec/0.2/copyright.include new file mode 100644 index 00000000..f0def708 --- /dev/null +++ b/ngff_zarr/spec/0.2/copyright.include @@ -0,0 +1,4 @@ +Copyright © 2020-[YEAR] +OME® +(U. Dundee). +OME trademark rules apply. diff --git a/ngff_zarr/spec/0.2/schemas/image.schema b/ngff_zarr/spec/0.2/schemas/image.schema new file mode 100644 index 00000000..6eb26274 --- /dev/null +++ b/ngff_zarr/spec/0.2/schemas/image.schema @@ -0,0 +1,101 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "http://localhost:8000/image.schema", + "title": "NGFF Image", + "description": "JSON from OME-NGFF .zattrs", + "type": "object", + "properties": { + "multiscales": { + "description": "The multiscale datasets for this image", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "datasets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + }, + "required": ["path"] + } + }, + "version": { + "type": "string", + "enum": [ + "0.2" + ] + } + }, + "required": [ + "datasets" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "omero": { + "type": "object", + "properties": { + "channels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "window": { + "type": "object", + "properties": { + "end": { + "type": "number" + }, + "max": { + "type": "number" + }, + "min": { + "type": "number" + }, + "start": { + "type": "number" + } + }, + "required": [ + "start", + "min", + "end", + "max" + ] + }, + "label": { + "type": "string" + }, + "family": { + "type": "string" + }, + "color": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "window", + "color" + ] + } + } + }, + "required": [ + "channels" + ] + } + }, + "required": [ "multiscales" ] +} diff --git a/ngff_zarr/spec/0.2/schemas/plate.schema b/ngff_zarr/spec/0.2/schemas/plate.schema new file mode 100644 index 00000000..456ed3da --- /dev/null +++ b/ngff_zarr/spec/0.2/schemas/plate.schema @@ -0,0 +1,119 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.2/schemas/plate.schema", + "title": "OME-NGFF plate schema", + "description": "JSON from OME-NGFF Plate .zattrs", + "type": "object", + "properties": { + "plate": { + "type": "object", + "properties": { + "version": { + "type": "string", + "enum": [ + "0.2" + ] + }, + "name": { + "type": "string" + }, + "columns": { + "description": "Columns of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "rows": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "wells": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + }, + "required": [ + "path" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "field_count": { + "description": "Maximum number of fields per view across all wells." + }, + "acquisitions": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "maximumfieldcount": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "starttime": { + "description": "The start timestamp of the acquisition, expressed as epoch time i.e. the number seconds since the Epoch", + "type": "integer", + "minimum": 0 + }, + "endtime": { + "description": "The end timestamp of the acquisition, expressed as epoch time i.e. the number seconds since the Epoch", + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "id" + ] + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "version", "columns", "rows", "wells" + ] + } + }, + "required": [ + "plate" + ] +} diff --git a/ngff_zarr/spec/0.2/schemas/well.schema b/ngff_zarr/spec/0.2/schemas/well.schema new file mode 100644 index 00000000..8dca3290 --- /dev/null +++ b/ngff_zarr/spec/0.2/schemas/well.schema @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.2/schemas/well.schema", + "title": "OME-NGFF well schema", + "description": "JSON from OME-NGFF .zattrs", + "type": "object", + "properties": { + "well": { + "type": "object", + "properties": { + "images": { + "description": "The fields of view for this well", + "type": "array", + "items": { + "type": "object", + "properties": { + "acquisition": { + "description": "A unique identifier within the context of the plate", + "type": "integer" + }, + "path": { + "description": "The path for this field of view subgroup", + "type": "string", + "pattern": "^[A-Za-z0-9]+$" + } + }, + "required": [ + "path" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "version": { + "description": "The version of the specification", + "type": "string", + "enum": [ + "0.2" + ] + } + }, + "required": [ + "images" + ] + } + } +} diff --git a/test/test_ngff_validation.py b/test/test_ngff_validation.py index 2c506c50..eff64d77 100644 --- a/test/test_ngff_validation.py +++ b/test/test_ngff_validation.py @@ -44,6 +44,22 @@ def test_validate_0_1(): print(multiscales) +def test_validate_0_1_no_version(): + test_store = Path(__file__).parent / "data" / "input" / "v01" / "6001251.zarr" + from_ngff_zarr(test_store, validate=True, version="0.1") + + +def test_validate_0_2(): + test_store = Path(__file__).parent / "data" / "input" / "v02" / "6001240.zarr" + multiscales = from_ngff_zarr(test_store, validate=True, version="0.2") + print(multiscales) + + +def test_validate_0_2_no_version(): + test_store = Path(__file__).parent / "data" / "input" / "v02" / "6001240.zarr" + from_ngff_zarr(test_store, validate=True) + + # def test_z_y_x_valid_ngff(): # array = np.random.random((32, 32, 16)) # image = to_spatial_image(array) From 30e6d69f33636eabaf7a71859ec607fa575c7ead Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 29 Nov 2024 16:28:56 -0500 Subject: [PATCH 3/4] ENH: Support reading and validating OME-Zarr v0.3 --- ngff_zarr/from_ngff_zarr.py | 15 +- ngff_zarr/spec/0.3/copyright.include | 4 + ngff_zarr/spec/0.3/schemas/image.schema | 109 +++++++++ .../spec/0.3/schemas/jsonld/context.json | 6 + ngff_zarr/spec/0.3/schemas/jsonld/frame.json | 42 ++++ ngff_zarr/spec/0.3/schemas/jsonld/shacl.ttl | 210 ++++++++++++++++++ ngff_zarr/spec/0.3/schemas/plate.schema | 119 ++++++++++ .../spec/0.3/schemas/salad_schema/image.yml | 210 ++++++++++++++++++ .../spec/0.3/schemas/strict_image.schema | 19 ++ ngff_zarr/spec/0.3/schemas/well.schema | 47 ++++ test/test_ngff_validation.py | 11 + 11 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 ngff_zarr/spec/0.3/copyright.include create mode 100644 ngff_zarr/spec/0.3/schemas/image.schema create mode 100644 ngff_zarr/spec/0.3/schemas/jsonld/context.json create mode 100644 ngff_zarr/spec/0.3/schemas/jsonld/frame.json create mode 100644 ngff_zarr/spec/0.3/schemas/jsonld/shacl.ttl create mode 100644 ngff_zarr/spec/0.3/schemas/plate.schema create mode 100644 ngff_zarr/spec/0.3/schemas/salad_schema/image.yml create mode 100644 ngff_zarr/spec/0.3/schemas/strict_image.schema create mode 100644 ngff_zarr/spec/0.3/schemas/well.schema diff --git a/ngff_zarr/from_ngff_zarr.py b/ngff_zarr/from_ngff_zarr.py index 5749a8c7..a937fc12 100644 --- a/ngff_zarr/from_ngff_zarr.py +++ b/ngff_zarr/from_ngff_zarr.py @@ -75,7 +75,7 @@ def from_ngff_zarr( dims = list(reversed(supported_dims)) else: - dims = [a["name"] for a in metadata["axes"]] + dims = [a["name"] if "name" in a else a for a in metadata["axes"]] name = "image" if name in metadata: @@ -119,7 +119,18 @@ def from_ngff_zarr( metadata.pop("@type", None) if "axes" in metadata: - axes = [Axis(**axis) for axis in metadata["axes"]] + if "name" in metadata["axes"][0]: + axes = [Axis(**axis) for axis in metadata["axes"]] + else: + # v0.3 + type_dict = { + "t": "time", + "c": "channel", + "z": "space", + "y": "space", + "x": "space", + } + axes = [Axis(name=axis, type=type_dict[axis]) for axis in metadata["axes"]] else: axes = [ Axis(name="t", type="time"), diff --git a/ngff_zarr/spec/0.3/copyright.include b/ngff_zarr/spec/0.3/copyright.include new file mode 100644 index 00000000..f0def708 --- /dev/null +++ b/ngff_zarr/spec/0.3/copyright.include @@ -0,0 +1,4 @@ +Copyright © 2020-[YEAR] +OME® +(U. Dundee). +OME trademark rules apply. diff --git a/ngff_zarr/spec/0.3/schemas/image.schema b/ngff_zarr/spec/0.3/schemas/image.schema new file mode 100644 index 00000000..afc2bd16 --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/image.schema @@ -0,0 +1,109 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.3/schemas/image.schema", + "title": "NGFF Image", + "description": "JSON from OME-NGFF .zattrs", + "type": "object", + "properties": { + "multiscales": { + "description": "The multiscale datasets for this image", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "datasets": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + }, + "required": ["path"] + } + }, + "version": { + "type": "string", + "enum": [ + "0.3" + ] + }, + "axes": { + "type": "array", + "minItems": 2, + "items": { + "type": "string", + "pattern": "^[xyzct]$" + } + } + }, + "required": [ + "datasets", "axes" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "omero": { + "type": "object", + "properties": { + "channels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "window": { + "type": "object", + "properties": { + "end": { + "type": "number" + }, + "max": { + "type": "number" + }, + "min": { + "type": "number" + }, + "start": { + "type": "number" + } + }, + "required": [ + "start", + "min", + "end", + "max" + ] + }, + "label": { + "type": "string" + }, + "family": { + "type": "string" + }, + "color": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "window", + "color" + ] + } + } + }, + "required": [ + "channels" + ] + } + }, + "required": [ "multiscales" ] +} diff --git a/ngff_zarr/spec/0.3/schemas/jsonld/context.json b/ngff_zarr/spec/0.3/schemas/jsonld/context.json new file mode 100644 index 00000000..5cc48d43 --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/jsonld/context.json @@ -0,0 +1,6 @@ +{ + "@context": { + "@vocab": "http://localhost:8000/context.json#", + "ngff": "http://localhost:8000/context.json#" + } +} diff --git a/ngff_zarr/spec/0.3/schemas/jsonld/frame.json b/ngff_zarr/spec/0.3/schemas/jsonld/frame.json new file mode 100644 index 00000000..5deff19d --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/jsonld/frame.json @@ -0,0 +1,42 @@ +{ + "@context": { + "@vocab": "http://localhost:8000/context.json#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "ngff": "http://localhost:8000/context.json#", + "ngff:multiscales": { + "@container": "@set" + }, + "ngff:datasets": { + "@container": "@set" + }, + "ngff:end": { + "@type": "xsd:decimal" + } + }, + "@type": "Image", + "multiscales": { + "@type": { + "@default": "Multiscale" + }, + "datasets": { + "@type": { + "@default": "Dataset" + } + } + }, + "omero": { + "@type": { + "@default": "omero" + }, + "channels": { + "@type": { + "@default": "Channel" + }, + "window": { + "@type": { + "@default": "Window" + } + } + } + } +} diff --git a/ngff_zarr/spec/0.3/schemas/jsonld/shacl.ttl b/ngff_zarr/spec/0.3/schemas/jsonld/shacl.ttl new file mode 100644 index 00000000..02cb213b --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/jsonld/shacl.ttl @@ -0,0 +1,210 @@ +@prefix xsd: . +@prefix rdf: . +@prefix dash: . +@prefix sh: . +@prefix schema: . +@prefix ngff: . + +# Collection of wrapped Multiscale objects +schema:TopLevelImageShape a sh:NodeShape; + + sh:targetClass ngff:Image; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + + sh:property [ + sh:path ngff:multiscales; + sh:node dash:ListShape; + sh:property [ + sh:path ([sh:zeroOrMorePath rdf:rest] rdf:first); + sh:datatype schema:Multiscale_Shape; + ]; + sh:minCount 1; + ]; + + sh:property [ + sh:path ngff:omero; + sh:node schema:omero_Shape; + ]. + + +# +# Contents of the "multiscales" array (NOT YET USED) +# +schema:Multiscale_Wrapper a sh:NodeShape; + + sh:targetClass ngff:ListItem; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + + sh:property [ + sh:path ngff:item ; + ] ; + sh:property [ + sh:path ngff:position; + sh:datatype xsd:integer ; + ] . + + +# +# Contents of the "multiscales" array +# +schema:Multiscale_Shape a sh:NodeShape; + + sh:targetClass ngff:Multiscale; + + sh:property [ + sh:path ngff:name; + sh:datatype xsd:string; + ]; + + sh:property [ + sh:path ngff:version; + sh:datatype xsd:string; + sh:pattern "0.3"; + ]; + + sh:property [ + sh:path ngff:datasets; + sh:node dash:ListShape; + sh:property [ + sh:path ([sh:zeroOrMorePath rdf:rest] rdf:first); + sh:datatype schema:Dataset_Shape; + ]; + sh:minCount 1; + ]; + + sh:property [ + sh:path ngff:axes; + sh:node dash:ListShape; + sh:property [ + sh:path ([sh:zeroOrMorePath rdf:rest] rdf:first); + ]; + sh:minCount 2; + sh:pattern "^[xyzct]$"; + sh:datatype xsd:string; + ] ; + + sh:property [ + sh:path ngff:type; + sh:datatype xsd:string; + ] ; + + sh:property [ + sh:path ngff:metadata; + ] . + +schema:omero_Shape a sh:NodeShape; + + sh:targetClass ngff:omero; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + + sh:property [ + sh:path ngff:id; + sh:datatype xsd:integer; + ] ; + + sh:property [ + sh:path ngff:version; + sh:datatype xsd:string; + ] ; + + sh:property [ + sh:path ngff:channels; + sh:node dash:ListShape; + sh:property [ + sh:path ([sh:zeroOrMorePath rdf:rest] rdf:first); + sh:datatype schema:Channel_Shape; + ]; + sh:minCount 1; + ] ; + + sh:property [ + sh:path ngff:rdefs; + sh:node schema:RDef_Shape; + ]. + +schema:RDef_Shape a sh:NodeShape; + + sh:property [ + sh:path ngff:model; + sh:datatype xsd:string; + ] ; + + sh:property [ + sh:path ngff:defaultZ; + sh:datatype xsd:integer; + ] ; + + sh:property [ + sh:path ngff:defaultT; + sh:datatype xsd:integer; + ] . + +# +# Contents of the "datasets" array +# +schema:Dataset_Shape a sh:NodeShape; + + sh:targetClass ngff:Dataset; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + + sh:property [ + sh:path ngff:path; + sh:datatype xsd:string; + sh:minCount 1; + ]. + +schema:Channel_Shape a sh:NodeShape; + sh:targetClass ngff:Channel; + sh:property [ + sh:path ngff:window; + sh:node schema:window_Shape; + sh:minCount 1; + ]; + sh:property [ + sh:path ngff:label; + sh:datatype xsd:string; + ]; + sh:property [ + sh:path ngff:family; + sh:datatype xsd:string; + ]; + sh:property [ + sh:path ngff:color; + sh:datatype xsd:string; + sh:minCount 1; + ]; + sh:property [ + sh:path ngff:active; + sh:datatype xsd:boolean; + ]; + sh:property [ + sh:path ngff:inverted; + sh:datatype xsd:boolean; + ]. + +schema:window_Shape a sh:NodeShape; + sh:targetClass ngff:Window; + sh:property [ + sh:path ngff:end; + sh:datatype xsd:double; + sh:minCount 1; + ]; + sh:property [ + sh:path ngff:max; + sh:datatype xsd:double; + sh:minCount 1; + ]; + sh:property [ + sh:path ngff:min; + sh:datatype xsd:double; + sh:minCount 1; + ]; + sh:property [ + sh:path ngff:start; + sh:datatype xsd:double; + sh:minCount 1; + ]. diff --git a/ngff_zarr/spec/0.3/schemas/plate.schema b/ngff_zarr/spec/0.3/schemas/plate.schema new file mode 100644 index 00000000..4a3ddd9c --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/plate.schema @@ -0,0 +1,119 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.3/schemas/plate.schema", + "title": "NGFF Plate", + "description": "JSON from OME-NGFF Plate .zattrs", + "type": "object", + "properties": { + "plate": { + "type": "object", + "properties": { + "version": { + "type": "string", + "enum": [ + "0.3" + ] + }, + "name": { + "type": "string" + }, + "columns": { + "description": "Columns of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "rows": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "wells": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string" + } + }, + "required": [ + "path" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "field_count": { + "description": "Maximum number of fields per view across all wells." + }, + "acquisitions": { + "description": "Rows of the Plate grid", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "maximumfieldcount": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "starttime": { + "description": "The start timestamp of the acquisition, expressed as epoch time i.e. the number seconds since the Epoch", + "type": "integer", + "minimum": 0 + }, + "endtime": { + "description": "The end timestamp of the acquisition, expressed as epoch time i.e. the number seconds since the Epoch", + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "id" + ] + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "version", "columns", "rows", "wells" + ] + } + }, + "required": [ + "plate" + ] +} diff --git a/ngff_zarr/spec/0.3/schemas/salad_schema/image.yml b/ngff_zarr/spec/0.3/schemas/salad_schema/image.yml new file mode 100644 index 00000000..12741f54 --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/salad_schema/image.yml @@ -0,0 +1,210 @@ +$base: "https://ngff.openmicroscopy.org/latest/" + +$namespaces: + ngff: "https://ngff.openmicroscopy.org/latest/" + +$graph: + - name: DatasetMetadata + doc: | + Optional information about a Dataset. + type: record + fields: + version: + type: string + jsonldPredicate: "#version" + args: + type: + type: array + items: string + kwargs: + type: Any + method: + type: string + + - name: OmeroMetadata + doc: | + Optional rendering info for an Image. + type: record + fields: + version: + type: string + jsonldPredicate: "#version" + channels: + type: + type: array + items: ChannelMetadata + id: int + rdefs: + type: RdefMetadata + + - name: ChannelMetadata + doc: | + Rendering info for a Channel. + type: record + fields: + active: boolean? + coefficient: float? + color: string? + family: string? + inverted: boolean? + label: string? + window: WindowMetadata + + - name: WindowMetadata + doc: | + Rendering Window for a Channel. + type: record + fields: + start: float + end: float + min: float? + max: float? + + - name: RdefMetadata + doc: | + Rendering Rdef for an Image. + type: record + fields: + defaultT: int + defaultZ: int + model: string + + # This causes "SchemaException: Predicate collision on name" with Multiscale/name + # - name: CreatorMetadata + # doc: | + # Creator info for an Image. + # type: record + # fields: + # name: string + + - name: Dataset + doc: | + One array series in descending order of size. + type: record + fields: + path: + type: string + + - name: Multiscale + doc: | + Identifies pyramid-like collections of arrays. + type: record + abstract: true + fields: + - name: name + type: [string, "null"] + - name: datasets + type: + type: array + items: Dataset + - name: metadata + type: [DatasetMetadata, "null"] + - name: type + type: [string, "null"] + + - name: MultiscaleV2 + type: record + extends: [Multiscale] + fields: + - name: version + jsonldPredicate: "#version" + type: + type: enum + symbols: + - "0.2" + + - name: MultiscaleV3 + type: record + extends: [Multiscale] + fields: + - name: version + jsonldPredicate: "#version" + type: + type: enum + symbols: + - "0.3" + - name: axes + type: + type: array + items: + type: enum + symbols: + - x + - y + - z + - c + - t + + - name: Image + doc: | + OME:Image + type: record + documentRoot: true + fields: + multiscales: + type: + type: array + items: Multiscale + omero: + type: [OmeroMetadata, "null"] + # _creator: [CreatorMetadata, "null"] + + - name: Label + doc: | + OME:Label + type: record + extends: [Image] + documentRoot: true + fields: + image-label: LabelMetadata + + - name: LabelMetadata + doc: | + Metadata for a Label + type: record + fields: + version: + type: string + jsonldPredicate: "#version" + source: SourceMetadata + colors: + type: + type: array + items: LabelColor + properties: + type: + type: array + items: LabelValueMetadata + + - name: SourceMetadata + doc: | + Source Image for a Label + type: record + fields: + image: string + + - name: LabelValueMetadata + doc: | + Specifies metadata for a specified label-value + type: record + abstract: true + fields: + label-value: int + + - name: LabelColor + doc: | + Specifies a color for a specified label-value + type: record + extends: [LabelValueMetadata] + fields: + rgba: + type: + type: array + items: int + + - name: LabelProperties + doc: | + Somewhere to store custom key-value pairs + type: record + extends: [LabelValueMetadata] + # How to do custom fields? - (anything goes) diff --git a/ngff_zarr/spec/0.3/schemas/strict_image.schema b/ngff_zarr/spec/0.3/schemas/strict_image.schema new file mode 100644 index 00000000..e4b6bb79 --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/strict_image.schema @@ -0,0 +1,19 @@ +{ + "$id": "https://ngff.openmicroscopy.org/0.3/schemas/strict_image.schema", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.3/schemas/image.schema" + }, + { + "properties": { + "multiscales": { + "items": { + "required": [ + "version", "metadata", "type", "name" + ] + } + } + } + } + ] +} diff --git a/ngff_zarr/spec/0.3/schemas/well.schema b/ngff_zarr/spec/0.3/schemas/well.schema new file mode 100644 index 00000000..b88bc745 --- /dev/null +++ b/ngff_zarr/spec/0.3/schemas/well.schema @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.3/schemas/well.schema", + "title": "OME-NGFF well schema", + "description": "JSON from OME-NGFF .zattrs", + "type": "object", + "properties": { + "well": { + "type": "object", + "properties": { + "images": { + "description": "The fields of view for this well", + "type": "array", + "items": { + "type": "object", + "properties": { + "acquisition": { + "description": "A unique identifier within the context of the plate", + "type": "integer" + }, + "path": { + "description": "The path for this field of view subgroup", + "type": "string", + "pattern": "^[A-Za-z0-9]+$" + } + }, + "required": [ + "path" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "version": { + "description": "The version of the specification", + "type": "string", + "enum": [ + "0.3" + ] + } + }, + "required": [ + "images" + ] + } + } +} diff --git a/test/test_ngff_validation.py b/test/test_ngff_validation.py index eff64d77..29a5d1af 100644 --- a/test/test_ngff_validation.py +++ b/test/test_ngff_validation.py @@ -60,6 +60,17 @@ def test_validate_0_2_no_version(): from_ngff_zarr(test_store, validate=True) +def test_validate_0_3(): + test_store = Path(__file__).parent / "data" / "input" / "v03" / "9528933.zarr" + multiscales = from_ngff_zarr(test_store, validate=True, version="0.3") + print(multiscales) + + +def test_validate_0_3_no_version(): + test_store = Path(__file__).parent / "data" / "input" / "v03" / "9528933.zarr" + from_ngff_zarr(test_store, validate=True) + + # def test_z_y_x_valid_ngff(): # array = np.random.random((32, 32, 16)) # image = to_spatial_image(array) From 5dd8110bb59e02e012659fa67388b1af6048762a Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 29 Nov 2024 16:34:26 -0500 Subject: [PATCH 4/4] ENH: Update test data hashes Includes OME-Zarr v0.1-v0.3 input test datasets from the IDR: https://idr.github.io/ome-ngff-samples/ --- test/_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/_data.py b/test/_data.py index 0e640ec5..562dcb48 100644 --- a/test/_data.py +++ b/test/_data.py @@ -14,8 +14,8 @@ from zarr.storage import MemoryStore from deepdiff import DeepDiff -test_data_ipfs_cid = "bafybeif55lgkigwmejsgm3bto355ks4exydcqul75lblvsuu3hfctflnt4" -test_data_sha256 = "f791e80bd8ab7b264293810b4aa7253813e008ae6b051afdc2a852766d8a83a8" +test_data_ipfs_cid = "bafybeiccqqioflsdnpna3kewhskyjcitqdk6n3yrzhnhj5qzpjk5edc2be" +test_data_sha256 = "4921b0e38b09ea480d377a89f1b4074f8e783b5703a651c60ef1e495c877f716" test_dir = Path(__file__).resolve().parent extract_dir = "data"