diff --git a/0.5/tests/test_validation.py b/0.5/tests/test_validation.py index b9c7c929..c2844f1a 100644 --- a/0.5/tests/test_validation.py +++ b/0.5/tests/test_validation.py @@ -60,6 +60,11 @@ def pytest_generate_tests(metafunc): if "suite" in metafunc.fixturenames: suites: List[Schema] = [] ids: List[str] = [] + schema_store = {} + for filename in glob.glob("schemas/*.schema"): + with open(filename) as o: + schema = json.load(o) + schema_store[schema["$id"]] = schema # Validation for filename in glob.glob("tests/*.json"): diff --git a/0.6-dev/copyright.include b/0.6-dev/copyright.include new file mode 100644 index 00000000..f0def708 --- /dev/null +++ b/0.6-dev/copyright.include @@ -0,0 +1,4 @@ +Copyright © 2020-[YEAR] +OME® +(U. Dundee). +OME trademark rules apply. diff --git a/0.6-dev/examples/bf2raw/.config.json b/0.6-dev/examples/bf2raw/.config.json new file mode 100644 index 00000000..2525328c --- /dev/null +++ b/0.6-dev/examples/bf2raw/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/bf2raw.schema" +} diff --git a/0.6-dev/examples/bf2raw/image.json b/0.6-dev/examples/bf2raw/image.json new file mode 100644 index 00000000..8fda1fd2 --- /dev/null +++ b/0.6-dev/examples/bf2raw/image.json @@ -0,0 +1,10 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "bioformats2raw.layout": 3 + } + } +} diff --git a/0.6-dev/examples/bf2raw/plate.json b/0.6-dev/examples/bf2raw/plate.json new file mode 100644 index 00000000..ef1ac0a7 --- /dev/null +++ b/0.6-dev/examples/bf2raw/plate.json @@ -0,0 +1,36 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "bioformats2raw.layout": 3, + "plate": { + "columns": [ + { + "name": "1" + } + ], + "name": "Plate Name 0", + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ], + "field_count": 1, + "rows": [ + { + "name": "A" + } + ], + "acquisitions": [ + { + "id": 0 + } + ] + } + } + } +} diff --git a/0.6-dev/examples/coordSystems/.config.json b/0.6-dev/examples/coordSystems/.config.json new file mode 100644 index 00000000..04f988a8 --- /dev/null +++ b/0.6-dev/examples/coordSystems/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/coordinate_systems_and_transforms.schema" +} diff --git a/0.6-dev/examples/label_strict/.config.json b/0.6-dev/examples/label_strict/.config.json new file mode 100644 index 00000000..e7329dc9 --- /dev/null +++ b/0.6-dev/examples/label_strict/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/strict_label.schema" +} diff --git a/0.6-dev/examples/label_strict/colors_properties.json b/0.6-dev/examples/label_strict/colors_properties.json new file mode 100644 index 00000000..fa1f4cd6 --- /dev/null +++ b/0.6-dev/examples/label_strict/colors_properties.json @@ -0,0 +1,37 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "label-value": 0, + "rgba": [0, 0, 128, 128] + }, + { + "label-value": 1, + "rgba": [0, 128, 0, 128] + } + ], + "properties": [ + { + "label-value": 0, + "area (pixels)": 1200, + "class": "intercellular space" + }, + { + "label-value": 1, + "area (pixels)": 1650, + "class": "cell", + "cell type": "neuron" + } + ], + "source": { + "image": "../../" + } + } + } + } +} diff --git a/0.6-dev/examples/multiscales_strict/.config.json b/0.6-dev/examples/multiscales_strict/.config.json new file mode 100644 index 00000000..b0469538 --- /dev/null +++ b/0.6-dev/examples/multiscales_strict/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/strict_image.schema" +} diff --git a/0.6-dev/examples/multiscales_strict/multiscales_example.json b/0.6-dev/examples/multiscales_strict/multiscales_example.json new file mode 100644 index 00000000..063e3175 --- /dev/null +++ b/0.6-dev/examples/multiscales_strict/multiscales_example.json @@ -0,0 +1,69 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "name": "example", + "coordinateSystems": [ + { + "name": "example", + "axes": [ + { "name": "t", "type": "time", "unit": "millisecond" }, + { "name": "c", "type": "channel" }, + { "name": "z", "type": "space", "unit": "micrometer" }, + { "name": "y", "type": "space", "unit": "micrometer" }, + { "name": "x", "type": "space", "unit": "micrometer" } + ] + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformation": { + // the voxel size for the first scale level (0.5 micrometer) + // and the time unit (0.1 milliseconds), which is the same for each scale level + "type": "scale", + "scale": [0.1, 1.0, 0.5, 0.5, 0.5], + "input": "0", + "output": "example" + } + }, + { + "path": "1", + "coordinateTransformation": { + // the voxel size for the second scale level (downscaled by a factor of 2 -> 1 micrometer) + // and the time unit (0.1 milliseconds), which is the same for each scale level + "type": "scale", + "scale": [0.1, 1.0, 1.0, 1.0, 1.0], + "input": "1", + "output": "example" + } + }, + { + "path": "2", + "coordinateTransformation": { + // the voxel size for the third scale level (downscaled by a factor of 4 -> 2 micrometer) + // and the time unit (0.1 milliseconds), which is the same for each scale level + "type": "scale", + "scale": [0.1, 1.0, 2.0, 2.0, 2.0], + "input": "2", + "output": "example" + } + } + ], + "type": "gaussian", + "metadata": { + "description": "the fields in metadata depend on the downscaling implementation. Here, the parameters passed to the skimage function are given", + "method": "skimage.transform.pyramid_gaussian", + "version": "0.16.1", + "args": "[true]", + "kwargs": { "multichannel": true } + } + } + ] + } + } +} diff --git a/0.6-dev/examples/multiscales_strict/multiscales_example_relative.json b/0.6-dev/examples/multiscales_strict/multiscales_example_relative.json new file mode 100644 index 00000000..1d1864e0 --- /dev/null +++ b/0.6-dev/examples/multiscales_strict/multiscales_example_relative.json @@ -0,0 +1,76 @@ +{ + "multiscales": [ + { + "version": "0.5-dev", + "name": "example", + "coordinateSystems" : [ + { + "name" : "exampleCoordinateSystem", + "axes": [ + {"name": "t", "type": "time", "unit": "millisecond"}, + {"name": "c", "type": "channel"}, + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} + ] + }, + { + "name" : "array_0", + "axes": [ + {"name": "t", "type": "time", "unit": "millisecond"}, + {"name": "c", "type": "channel"}, + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} + ] + } + ], + "datasets": [ + { + "path": "0", + // the transformation of other arrays are defined relative to this, the highest resolution, array + "coordinateTransformations": [{ + "type": "identity", + "input": "/0", + "output": "array_0" + }] + }, + { + "path": "1", + "coordinateTransformations": [{ + // the second scale level (downscaled by a factor of 2 relative to "0" in zyx) + "type": "scale", + "scale": [1, 1, 2, 2, 2], + "input" : "/1", + "output" : "array_0" + }] + }, + { + "path": "2", + "coordinateTransformations": [{ + // the third scale level (downscaled by a factor of 4 relative to "0" in zyx) + "type": "scale", + "scale": [1, 1, 4, 4, 4], + "input" : "/2", + "output" : "array_0" + }] + } + ], + "coordinateTransformations": [{ + // the time unit (0.1 milliseconds), the voxel size for all spatial axes of "0" (0.5 micrometers) + "type": "scale", + "scale": [0.1, 1.0, 0.5, 0.5, 0.5], + "input" : "array_0", + "output" : "exampleCoordinateSystem" + }], + "type": "gaussian", + "metadata": { + "description": "the fields in metadata depend on the downscaling implementation. Here, the parameters passed to the skimage function are given", + "method": "skimage.transform.pyramid_gaussian", + "version": "0.16.1", + "args": "[true]", + "kwargs": {"multichannel": true} + } + } + ] +} diff --git a/0.6-dev/examples/multiscales_strict/multiscales_example_twoCoordSystems.json b/0.6-dev/examples/multiscales_strict/multiscales_example_twoCoordSystems.json new file mode 100644 index 00000000..9afe6141 --- /dev/null +++ b/0.6-dev/examples/multiscales_strict/multiscales_example_twoCoordSystems.json @@ -0,0 +1,85 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "name": "example", + "coordinateSystems": [ + { + "name": "default", + "axes": [ + { "name": "t", "type": "time", "unit": "millisecond" }, + { "name": "c", "type": "channel" }, + { "name": "z", "type": "space", "unit": "micrometer" }, + { "name": "y", "type": "space", "unit": "micrometer" }, + { "name": "x", "type": "space", "unit": "micrometer" } + ] + }, + { + "name": "example", + "axes": [ + { "name": "t", "type": "time", "unit": "millisecond" }, + { "name": "c", "type": "channel" }, + { "name": "z", "type": "space", "unit": "micrometer" }, + { "name": "y", "type": "space", "unit": "micrometer" }, + { "name": "x", "type": "space", "unit": "micrometer" } + ] + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformation": { + // the voxel size for the first scale level (0.5 micrometer) + "type": "scale", + "scale": [1.0, 1.0, 0.5, 0.5, 0.5], + "input": "0", + "output": "default" + } + }, + { + "path": "1", + "coordinateTransformation": { + // the voxel size for the second scale level (downscaled by a factor of 2 -> 1 micrometer) + "type": "scale", + "scale": [1.0, 1.0, 1.0, 1.0, 1.0], + "input": "1", + "output": "default" + } + }, + { + "path": "2", + "coordinateTransformation": { + // the voxel size for the third scale level (downscaled by a factor of 4 -> 2 micrometer) + "type": "scale", + "scale": [1.0, 1.0, 2.0, 2.0, 2.0], + "input": "2", + "output": "default" + } + } + ], + "coordinateTransformations": [ + { + // the time unit (0.1 milliseconds), which is the same for each scale level + "type": "scale", + "scale": [0.1, 1.0, 1.0, 1.0, 1.0], + "input": "default", + "output": "example" + } + ], + "type": "gaussian", + "metadata": { + "description": "the fields in metadata depend on the downscaling implementation. Here, the parameters passed to the skimage function are given", + "method": "skimage.transform.pyramid_gaussian", + "version": "0.16.1", + "args": "[true]", + "kwargs": { "multichannel": true } + } + } + ] + } + } +} diff --git a/0.6-dev/examples/multiscales_strict/multiscales_transformations.json b/0.6-dev/examples/multiscales_strict/multiscales_transformations.json new file mode 100644 index 00000000..e4eb742a --- /dev/null +++ b/0.6-dev/examples/multiscales_strict/multiscales_transformations.json @@ -0,0 +1,47 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ], + "coordinateTransformations": [ + { + "scale": [10, 10], + "type": "scale" + } + ], + "name": "image_with_coordinateTransformations", + "type": "foo", + "metadata": { + "key": "value" + } + } + ] + } + } +} diff --git a/0.6-dev/examples/ome/.config.json b/0.6-dev/examples/ome/.config.json new file mode 100644 index 00000000..8a611ccf --- /dev/null +++ b/0.6-dev/examples/ome/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/ome.schema" +} diff --git a/0.6-dev/examples/ome/series-2.json b/0.6-dev/examples/ome/series-2.json new file mode 100644 index 00000000..ad4c25e8 --- /dev/null +++ b/0.6-dev/examples/ome/series-2.json @@ -0,0 +1,10 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "series": ["0", "1"] + } + } +} diff --git a/0.6-dev/examples/plate_strict/.config.json b/0.6-dev/examples/plate_strict/.config.json new file mode 100644 index 00000000..a49b1743 --- /dev/null +++ b/0.6-dev/examples/plate_strict/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/strict_plate.schema" +} diff --git a/0.6-dev/examples/plate_strict/plate_2wells.json b/0.6-dev/examples/plate_strict/plate_2wells.json new file mode 100644 index 00000000..e8961001 --- /dev/null +++ b/0.6-dev/examples/plate_strict/plate_2wells.json @@ -0,0 +1,97 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 1, + "maximumfieldcount": 1, + "name": "single acquisition", + "starttime": 1343731272000 + } + ], + "columns": [ + { + "name": "1" + }, + { + "name": "2" + }, + { + "name": "3" + }, + { + "name": "4" + }, + { + "name": "5" + }, + { + "name": "6" + }, + { + "name": "7" + }, + { + "name": "8" + }, + { + "name": "9" + }, + { + "name": "10" + }, + { + "name": "11" + }, + { + "name": "12" + } + ], + "field_count": 1, + "name": "sparse test", + "rows": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + }, + { + "name": "D" + }, + { + "name": "E" + }, + { + "name": "F" + }, + { + "name": "G" + }, + { + "name": "H" + } + ], + "wells": [ + { + "path": "C/5", + "rowIndex": 2, + "columnIndex": 4 + }, + { + "path": "D/7", + "rowIndex": 3, + "columnIndex": 6 + } + ] + } + } + } +} diff --git a/0.6-dev/examples/plate_strict/plate_6wells.json b/0.6-dev/examples/plate_strict/plate_6wells.json new file mode 100644 index 00000000..bf1b8afe --- /dev/null +++ b/0.6-dev/examples/plate_strict/plate_6wells.json @@ -0,0 +1,78 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 1, + "maximumfieldcount": 2, + "name": "Meas_01(2012-07-31_10-41-12)", + "starttime": 1343731272000 + }, + { + "id": 2, + "maximumfieldcount": 2, + "name": "Meas_02(201207-31_11-56-41)", + "starttime": 1343735801000 + } + ], + "columns": [ + { + "name": "1" + }, + { + "name": "2" + }, + { + "name": "3" + } + ], + "field_count": 4, + "name": "test", + "rows": [ + { + "name": "A" + }, + { + "name": "B" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + }, + { + "path": "A/2", + "rowIndex": 0, + "columnIndex": 1 + }, + { + "path": "A/3", + "rowIndex": 0, + "columnIndex": 2 + }, + { + "path": "B/1", + "rowIndex": 1, + "columnIndex": 0 + }, + { + "path": "B/2", + "rowIndex": 1, + "columnIndex": 1 + }, + { + "path": "B/3", + "rowIndex": 1, + "columnIndex": 2 + } + ] + } + } + } +} diff --git a/0.6-dev/examples/subspace/.config.json b/0.6-dev/examples/subspace/.config.json new file mode 100644 index 00000000..04f988a8 --- /dev/null +++ b/0.6-dev/examples/subspace/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/coordinate_systems_and_transforms.schema" +} diff --git a/0.6-dev/examples/subspace/subspaceMultidim.json b/0.6-dev/examples/subspace/subspaceMultidim.json new file mode 100644 index 00000000..566a4069 --- /dev/null +++ b/0.6-dev/examples/subspace/subspaceMultidim.json @@ -0,0 +1,44 @@ +{ + "coordinateSystems": [ + { + "name": "in", + "axes": [ + { "name": "0", "type": "array" }, + { "name": "1", "type": "array" }, + { "name": "2", "type": "array" }, + { "name": "3", "type": "array" }, + { "name": "4", "type": "array" } + ] + }, + { + "name": "out", + "axes": [ + { "name": "x", "type": "space" }, + { "name": "y", "type": "space" }, + { "name": "z", "type": "space" } + ] + } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "name": "5D-to-3D-not-contiguous", + "input": "in", + "output": "out", + "transformations": [ + { + "type": "mapAxis", + "mapAxis": { "0": "x", "2": "z" }, + "input": [ "0", "2" ], + "output": [ "x", "z" ] + }, + { + "type": "scale", + "scale": [ 2 ], + "input": [ "1" ], + "output": [ "y" ] + } + ] + } + ] +} diff --git a/0.6-dev/examples/subspace/subspacePermute.json b/0.6-dev/examples/subspace/subspacePermute.json new file mode 100644 index 00000000..26749751 --- /dev/null +++ b/0.6-dev/examples/subspace/subspacePermute.json @@ -0,0 +1,26 @@ +{ + "coordinateSystems" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j" } ]}, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y" } ]} + ], + "coordinateTransformations" : [ + { + "type" : "byDimension", + "input" : "in", + "output" : "out", + "transformations" : [ + { + "type": "identity", + "input" : ["j"], + "output" : ["x"] + }, + { + "type": "scale", + "scale" : [2], + "input" : ["i"], + "output" : ["y"] + } + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/.config.json b/0.6-dev/examples/transformations/.config.json new file mode 100644 index 00000000..04f988a8 --- /dev/null +++ b/0.6-dev/examples/transformations/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/coordinate_systems_and_transforms.schema" +} diff --git a/0.6-dev/examples/transformations/affine2d2d.json b/0.6-dev/examples/transformations/affine2d2d.json new file mode 100644 index 00000000..a4955d43 --- /dev/null +++ b/0.6-dev/examples/transformations/affine2d2d.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems" : [ + { "name": "ji", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "yx", "axes": [{"name": "y"}, {"name": "x"}] } + ], + "coordinateTransformations" : [ + { + "type": "affine", + "affine": [[1, 2, 3], [4, 5, 6]], + "input": "ji", + "output": "yx" + } + ] +} diff --git a/0.6-dev/examples/transformations/affine2d3d.json b/0.6-dev/examples/transformations/affine2d3d.json new file mode 100644 index 00000000..588bdec5 --- /dev/null +++ b/0.6-dev/examples/transformations/affine2d3d.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "ij", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "xyz", "axes": [{"name": "x"}, {"name": "y"}, {"name": "z"}] } + ], + "coordinateTransformations": [ + { + "type": "affine", + "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + "input": "ij", + "output": "xyz" + } + ] +} diff --git a/0.6-dev/examples/transformations/bijection.json b/0.6-dev/examples/transformations/bijection.json new file mode 100644 index 00000000..7ee77062 --- /dev/null +++ b/0.6-dev/examples/transformations/bijection.json @@ -0,0 +1,15 @@ +{ + "coordinateSystems" : [ + { "name": "src", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "tgt", "axes": [{"name": "y"}, {"name": "x"}] } + ], + "coordinateTransformations" : [ + { + "type": "bijection", + "forward": { "type" : "coordinates", "path" : "forward_coordinates" }, + "inverse": { "type" : "coordinates", "path" : "inverse_coordinates" }, + "input": "src", + "output": "tgt" + } + ] +} diff --git a/0.6-dev/examples/transformations/bijection_verbose.json b/0.6-dev/examples/transformations/bijection_verbose.json new file mode 100644 index 00000000..56547fdc --- /dev/null +++ b/0.6-dev/examples/transformations/bijection_verbose.json @@ -0,0 +1,7 @@ +{ + "type": "bijection", + "forward": { "type" : "coordinates", "path" : "forward_coordinates", "input" : "src", "output" : "tgt" }, + "inverse": { "type" : "coordinates", "path" : "inverse_coordinates", "input" : "tgt", "output" : "src" }, + "input": "src", + "output": "tgt" +} diff --git a/0.6-dev/examples/transformations/byDimension1.json b/0.6-dev/examples/transformations/byDimension1.json new file mode 100644 index 00000000..f4f9c42e --- /dev/null +++ b/0.6-dev/examples/transformations/byDimension1.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ] }, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ] } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations": [ + { "type": "translation", "translation": [-1.0], "input": ["i"], "output": ["x"]}, + { "type": "scale", "scale": [2.0], "input": ["j"], "output": ["y"]} + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/byDimension2.json b/0.6-dev/examples/transformations/byDimension2.json new file mode 100644 index 00000000..ef2e29b4 --- /dev/null +++ b/0.6-dev/examples/transformations/byDimension2.json @@ -0,0 +1,42 @@ +{ + "coordinateSystems": [ + { + "name": "in", + "axes": [ + { "name": "l", "type": "array" }, + { "name": "j", "type": "array" }, + { "name": "k", "type": "array" }, + { "name": "i", "type": "array" } + ] + }, + { + "name": "out", + "axes": [ + { "name": "z", "type": "array" }, + { "name": "y", "type": "array" }, + { "name": "x", "type": "array" } + ] + } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations": [ + { + "type": "translation", + "translation": [ 1, 3 ], + "input": [ "i", "k" ], + "output": [ "y", "x" ] + }, + { + "type": "scale", + "scale": [ 2 ], + "input": [ "j" ], + "output": [ "z" ] + } + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/byDimensionInvalid1.json b/0.6-dev/examples/transformations/byDimensionInvalid1.json new file mode 100644 index 00000000..4054fc19 --- /dev/null +++ b/0.6-dev/examples/transformations/byDimensionInvalid1.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ] }, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ] } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations": [ + { "type": "translation", "translation": [-1.0], "input": ["i"], "output": ["z"]}, + { "type": "scale", "scale": [2.0], "input": ["0"], "output": ["y"]} + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/byDimensionInvalid2.json b/0.6-dev/examples/transformations/byDimensionInvalid2.json new file mode 100644 index 00000000..810f0c04 --- /dev/null +++ b/0.6-dev/examples/transformations/byDimensionInvalid2.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ] }, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ] } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations": [ + { "type": "translation", "translation": [-1.0], "input": ["i"], "output": ["x"]}, + { "type": "scale", "scale": [2.0], "input": ["i"], "output": ["x"]} + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/byDimensionXarray.json b/0.6-dev/examples/transformations/byDimensionXarray.json new file mode 100644 index 00000000..82531b0e --- /dev/null +++ b/0.6-dev/examples/transformations/byDimensionXarray.json @@ -0,0 +1,39 @@ +{ + "coordinateSystems": [ + { + "name": "physical", + "axes": [ + { "name": "x", "type": "space", "unit": "micrometer" }, + { "name": "y", "type": "space", "unit": "micrometer" } + ] + }, + { + "name": "array", + "axes": [ + { "name": "dim_0", "type": "array" }, + { "name": "dim_1", "type": "array" } + ] + } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "array", + "output": "physical", + "transformations": [ + { + "type": "coordinates", + "path": "xCoordinates", + "input": [ "dim_0" ], + "output": [ "x" ] + }, + { + "type": "coordinates", + "path": "yCoordinates", + "input": [ "dim_1" ], + "output": [ "y" ] + } + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/coordinates1d.json b/0.6-dev/examples/transformations/coordinates1d.json new file mode 100644 index 00000000..314bc6fb --- /dev/null +++ b/0.6-dev/examples/transformations/coordinates1d.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "i", "axes": [{"name": "i"}] }, + { "name": "x", "axes": [{"name": "x"}] } + ], + "coordinateTransformations": [{ + "name": "a coordinate field transform", + "type": "coordinates", + "path": "i2xCoordinates", + "input": "i", + "output": "x", + "interpolation": "nearest" + }] +} diff --git a/0.6-dev/examples/transformations/displacement1d.json b/0.6-dev/examples/transformations/displacement1d.json new file mode 100644 index 00000000..5db76446 --- /dev/null +++ b/0.6-dev/examples/transformations/displacement1d.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "i", "axes": [{"name": "i"}] }, + { "name": "x", "axes": [{"name": "x"}] } + ], + "coordinateTransformations": [{ + "name": "a displacement field transform", + "type": "displacements", + "path": "i2xCoordinates", + "input": "i", + "output": "x", + "interpolation": "nearest" + }] +} diff --git a/0.6-dev/examples/transformations/identity.json b/0.6-dev/examples/transformations/identity.json new file mode 100644 index 00000000..8de6d9d5 --- /dev/null +++ b/0.6-dev/examples/transformations/identity.json @@ -0,0 +1,9 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ]}, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ]} + ], + "coordinateTransformations": [ + { "type": "identity", "input": "in", "output": "out" } + ] +} diff --git a/0.6-dev/examples/transformations/inverseOf.json b/0.6-dev/examples/transformations/inverseOf.json new file mode 100644 index 00000000..6a94274c --- /dev/null +++ b/0.6-dev/examples/transformations/inverseOf.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems" : [ + { "name" : "moving", "axes" : [{"name" : "y-moving"}, {"name":"x-moving"}] }, + { "name" : "fixed", "axes" : [{"name" : "y-fixed"}, {"name":"x-fixed"}] } + ], + "coordinateTransformations" : [ + { + "type": "inverseOf", + "transformation" : { + "type": "displacements", + "path": "path/to/displacements" + }, + "input" : "moving", + "output" : "fixed" + } + ] +} diff --git a/0.6-dev/examples/transformations/mapAxis1.json b/0.6-dev/examples/transformations/mapAxis1.json new file mode 100644 index 00000000..37d3c73c --- /dev/null +++ b/0.6-dev/examples/transformations/mapAxis1.json @@ -0,0 +1,23 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ]}, + { "name": "out1", "axes": [ {"name": "y"}, {"name": "x"} ]}, + { "name": "out2", "axes": [ {"name": "y"}, {"name": "x"} ]} + ], + "coordinateTransformations": [ + { + "name": "equivalent to identity", + "type": "mapAxis", + "mapAxis": { "x":"i", "y":"j" }, + "input": "in", + "output": "out1" + }, + { + "name": "permutation", + "type": "mapAxis", + "mapAxis": { "y":"i", "x":"j" }, + "input": "in", + "output": "out2" + } + ] +} diff --git a/0.6-dev/examples/transformations/mapAxis2.json b/0.6-dev/examples/transformations/mapAxis2.json new file mode 100644 index 00000000..4b50d1f5 --- /dev/null +++ b/0.6-dev/examples/transformations/mapAxis2.json @@ -0,0 +1,23 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "a"}, {"name": "b"}]}, + { "name": "out_down", "axes": [ {"name": "x"}]}, + { "name": "out_up", "axes": [ {"name": "z"}, {"name": "y"}, {"name": "x"} ]} + ], + "coordinateTransformations": [ + { + "name": "projection down", + "type": "mapAxis", + "mapAxis": { "x": "b" }, + "input": "in", + "output": "out_down" + }, + { + "name": "projection up", + "type": "mapAxis", + "mapAxis": { "z": "b", "y": "b", "x": "a" }, + "input": "in", + "output": "out_up" + } + ] +} diff --git a/0.6-dev/examples/transformations/rotation.json b/0.6-dev/examples/transformations/rotation.json new file mode 100644 index 00000000..942dcf39 --- /dev/null +++ b/0.6-dev/examples/transformations/rotation.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems" : [ + { "name" : "ji", "axes" : [{"name" : "j"}, {"name":"i"}] }, + { "name" : "yx", "axes" : [{"name" : "y"}, {"name":"x"}] } + ], + "coordinateTransformations" : [ + { + "type": "rotation", + "rotation": [[0, -1], [1, 0]], + "input" : "ji", + "output" : "yx" + } + ] +} diff --git a/0.6-dev/examples/transformations/scale.json b/0.6-dev/examples/transformations/scale.json new file mode 100644 index 00000000..56d348a5 --- /dev/null +++ b/0.6-dev/examples/transformations/scale.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "out", "axes": [{"name": "y"}, {"name": "x"}] } + ], + "coordinateTransformations": [ + { + "type": "scale", + "scale": [3.12, 2], + "input": "in", + "output": "out" + } + ] +} diff --git a/0.6-dev/examples/transformations/sequence.json b/0.6-dev/examples/transformations/sequence.json new file mode 100644 index 00000000..e781c108 --- /dev/null +++ b/0.6-dev/examples/transformations/sequence.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ]}, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ]} + ], + "coordinateTransformations": [ + { + "type": "sequence", + "input": "in", + "output": "out", + "transformations": [ + { "type": "translation", "translation": [0.1, 0.9] }, + { "type": "scale", "scale": [2, 3] } + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/sequenceSubspace1.json b/0.6-dev/examples/transformations/sequenceSubspace1.json new file mode 100644 index 00000000..e8a5bd35 --- /dev/null +++ b/0.6-dev/examples/transformations/sequenceSubspace1.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + ], + "coordinateTransformations" : [ + { + "type" : "sequence", + "input" : "in", + "output" : "out", + "transformations" : [ + { "type" : "coordinates", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["x"]}, + { "type" : "scale", "scale" : [2.0], "inputAxes" : ["j"], "outputAxes" : ["y"]} + ] + } + ] +} diff --git a/0.6-dev/examples/transformations/translation.json b/0.6-dev/examples/transformations/translation.json new file mode 100644 index 00000000..5e28be7a --- /dev/null +++ b/0.6-dev/examples/transformations/translation.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "out", "axes": [{"name": "y"}, {"name": "x"}] } + ], + "coordinateTransformations" : [ + { + "type": "translation", + "input": "in", + "output": "out", + "translation": [9, -1.42] + } + ] +} diff --git a/0.6-dev/examples/transformations/xarrayLike.json b/0.6-dev/examples/transformations/xarrayLike.json new file mode 100644 index 00000000..6a7c45b4 --- /dev/null +++ b/0.6-dev/examples/transformations/xarrayLike.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i", "type": "array"}, {"name": "j", "type": "array"} ]}, + { "name": "out", "axes": [ {"name": "x", "type": "space"}, {"name": "y", "type": "space"} ]} + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations": [ + { "type": "coordinates", "path": "/xCoordinates", "input" : ["i"], "output" : ["x"] }, + { "type": "coordinates", "path": "/yCoordinates", "input" : ["j"], "output" : ["y"] } + ] + } + ] +} diff --git a/0.6-dev/examples/well_strict/.config.json b/0.6-dev/examples/well_strict/.config.json new file mode 100644 index 00000000..129ac69c --- /dev/null +++ b/0.6-dev/examples/well_strict/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/strict_well.schema" +} diff --git a/0.6-dev/examples/well_strict/well_2fields.json b/0.6-dev/examples/well_strict/well_2fields.json new file mode 100644 index 00000000..987fe5e4 --- /dev/null +++ b/0.6-dev/examples/well_strict/well_2fields.json @@ -0,0 +1,21 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "well": { + "images": [ + { + "acquisition": 0, + "path": "0" + }, + { + "acquisition": 3, + "path": "1" + } + ] + } + } + } +} diff --git a/0.6-dev/examples/well_strict/well_4fields.json b/0.6-dev/examples/well_strict/well_4fields.json new file mode 100644 index 00000000..581ec66b --- /dev/null +++ b/0.6-dev/examples/well_strict/well_4fields.json @@ -0,0 +1,29 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "ome": { + "version": "0.5", + "well": { + "images": [ + { + "acquisition": 1, + "path": "0" + }, + { + "acquisition": 1, + "path": "1" + }, + { + "acquisition": 2, + "path": "2" + }, + { + "acquisition": 2, + "path": "3" + } + ] + } + } + } +} diff --git a/0.6-dev/header.include b/0.6-dev/header.include new file mode 100644 index 00000000..7bd9e500 --- /dev/null +++ b/0.6-dev/header.include @@ -0,0 +1,31 @@ + + + + + + [TITLE] + + + +
+ + OME logo (6 circles in a hexagon) + +

[TITLE]

+

[LONGSTATUS], +

+
+
+ +
+
+ +
+ +

Status of this document

+
+
+ + +
diff --git a/0.6-dev/index.bs b/0.6-dev/index.bs new file mode 100644 index 00000000..855c873f --- /dev/null +++ b/0.6-dev/index.bs @@ -0,0 +1,1784 @@ +
+Title: OME-Zarr specification
+Shortname: ome-zarr
+Level: 1
+Status: w3c/CG-FINAL
+TR: https://ngff.openmicroscopy.org/0.5/
+URL: https://ngff.openmicroscopy.org/0.6-dev/
+Repository: https://github.com/ome/ngff
+Issue Tracking: Forums https://forum.image.sc/tag/ome-ngff
+Logo: http://www.openmicroscopy.org/img/logos/ome-logomark.svg
+Local Boilerplate: header yes, copyright yes
+Boilerplate: style-darkmode off
+Markup Shorthands: markdown yes
+Editor: Josh Moore, German BioImaging e.V., https://gerbi-gmb.de/, https://orcid.org/0000-0003-4028-811X
+Text Macro: NGFFVERSION 0.5
+Abstract: This document contains next-generation file format (NGFF)
+Abstract: specifications for storing bioimaging data in the cloud.
+Abstract: All specifications are submitted to the https://image.sc community for review.
+Status Text: The current released version of this specification is 0.5. Migration scripts
+Status Text: will be provided between numbered versions. Data written with these latest changes
+Status Text: (an "editor's draft") will not necessarily be supported.
+
+ +OME-Zarr specification {#ome-zarr} +---------------------------------- + +The conventions and specifications defined in this document are designed to +enable next-generation file formats to represent the same bioimaging data +that can be represented in \[OME-TIFF](http://www.openmicroscopy.org/ome-files/) +and beyond. + +Document conventions +-------------------- + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, +“RECOMMENDED”, “MAY”, and “OPTIONAL” are to be interpreted as described in +[RFC 2119](https://tools.ietf.org/html/rfc2119). + +

+Transitional metadata is added to the specification with the +intention of removing it in the future. Implementations may be expected (MUST) or +encouraged (SHOULD) to support the reading of the data, but writing will usually +be optional (MAY). Examples of transitional metadata include custom additions by +implementations that are later submitted as a formal specification. (See [[#bf2raw]]) +

+ +Some of the JSON examples in this document include comments. However, these are only for +clarity purposes and comments MUST NOT be included in JSON objects. + +Storage format {#storage-format} +================================ + +OME-Zarr is implemented using the Zarr format as defined by the +[version 3 of the Zarr specification](https://zarr-specs.readthedocs.io/en/latest/v3/core/v3.0.html). +All features of the Zarr format including codecs, chunk grids, chunk +key encodings, data types and storage transformers may be used with +OME-Zarr unless explicitly disallowed in this specification. + +An overview of the layout of an OME-Zarr fileset should make +understanding the following metadata sections easier. The hierarchy +is represented here as it would appear locally but could equally +be stored on a web server to be accessed via HTTP or in object storage +like S3 or GCS. + +Images {#image-layout} +---------------------- + +The following layout describes the expected Zarr hierarchy for images with +multiple levels of resolutions and optionally associated labels. +Note that the number of dimensions is variable between 2 and 5 and that axis names are arbitrary, see [[#multiscale-md]] for details. + +
+├── 123.zarr                  # One OME-Zarr image (id=123).
+│   ...
+│
+└── 456.zarr                  # Another OME-Zarr image (id=456).
+    │
+    ├── zarr.json             # Each image is a Zarr group of other groups and arrays.
+    │                         # Group level attributes are stored in the `zarr.json` file and include
+    │                         # "multiscales" and "omero" (see below).
+    │
+    ├── 0                     # Each multiscale level is stored as a separate Zarr array,
+    │   ...                   # which is a folder containing chunk files which compose the array.
+    ├── n                     # The name of the array is arbitrary with the ordering defined by
+    │   │                     # by the "multiscales" metadata, but is often a sequence starting at 0.
+    │   │
+    │   ├── zarr.json         # All image arrays must be up to 5-dimensional
+    │   │                     # with the axis of type time before type channel, before spatial axes.
+    │   │
+    │   └─ ...                # Chunks are stored conforming to the Zarr array specification and 
+    │                         # metadata as specified in the array's `zarr.json`.
+    │
+    └── labels
+        │
+        ├── zarr.json         # The labels group is a container which holds a list of labels to make the objects easily discoverable
+        │                     # All labels will be listed in `zarr.json` e.g. `{ "labels": [ "original/0" ] }`
+        │                     # Each dimension of the label should be either the same as the
+        │                     # corresponding dimension of the image, or `1` if that dimension of the label
+        │                     # is irrelevant.
+        │
+        └── original          # Intermediate folders are permitted but not necessary and currently contain no extra metadata.
+            │
+            └── 0             # Multiscale, labeled image. The name is unimportant but is registered in the "labels" group above.
+                ├── zarr.json # Zarr Group which is both a multiscaled image as well as a labeled image.
+                │             # Metadata of the related image and as well as display information under the "image-label" key.
+                │
+                ├── 0         # Each multiscale level is stored as a separate Zarr array, as above, but only integer values
+                └── ...       # are supported.
+                
+
+ + + +High-content screening {#hcs-layout} +------------------------------------ + +The following specification defines the hierarchy for a high-content screening +dataset. Three groups MUST be defined above the images: + +- the group above the images defines the well and MUST implement the + [well specification](#well-md). All images contained in a well are fields + of view of the same well +- the group above the well defines a row of wells +- the group above the well row defines an entire plate i.e. a two-dimensional + collection of wells organized in rows and columns. It MUST implement the + [plate specification](#plate-md) + +A well row group SHOULD NOT be present if there are no images in the well row. +A well group SHOULD NOT be present if there are no images in the well. + + +
+.
+│
+└── 5966.zarr                 # One OME-Zarr plate (id=5966)
+    ├── zarr.json             # Implements "plate" specification
+    ├── A                     # First row of the plate
+    │   ├── zarr.json
+    │   │
+    │   ├── 1                 # First column of row A
+    │   │   ├── zarr.json     # Implements "well" specification
+    │   │   │
+    │   │   ├── 0             # First field of view of well A1
+    │   │   │   │
+    │   │   │   ├── zarr.json # Implements "multiscales", "omero"
+    │   │   │   ├── 0         # Resolution levels          
+    │   │   │   ├── ...
+    │   │   │   └── labels    # Labels (optional)
+    │   │   └── ...           # Other fields of view
+    │   └── ...               # Other columns
+    └── ...                   # Other rows
+
+ +OME-Zarr Metadata {#metadata} +============================= + +The "OME-Zarr Metadata" contains metadata keys as specified below +for discovering certain types of data, especially images. + +The OME-Zarr Metadata is stored in the various `zarr.json` files throughout the above array +hierarchy. In this file, the metadata is stored under the namespaced key +`ome` in `attributes`. +The version of the OME-Zarr Metadata is denoted as a string in the `version` attribute within the `ome` namespace. + +The OME-Zarr Metadata version MUST be consistent within a hierarchy. + +```json +{ + ... + "attributes": { + "ome": { + "version": "0.5", + ... + } + } +} +``` + +"axes" metadata {#axes-md} +-------------------------- + +"axes" describes the dimensions of a coordinate systems and adds an interpretation to the data along that dimension. A named collection +of axes forms a coordinate system ([[#coord-sys-md]]) (see below). +It is a list of dictionaries, where each dictionary describes a dimension (axis) and: +- MUST contain the field "name" that gives the name for this dimension. The values MUST be unique across all "name" fields. +- SHOULD contain the field "type". It SHOULD be one of the strings "array", "space", "time", "channel", "coordinate", or + "displacement" but MAY take other string values for custom axis types that are not part of this specification yet. +- SHOULD contain the field "unit" to specify the physical unit of this dimension. The value SHOULD be one of the following strings, which are valid units according to UDUNITS-2. + - Units for "space" axes: 'angstrom', 'attometer', 'centimeter', 'decimeter', 'exameter', 'femtometer', 'foot', 'gigameter', 'hectometer', 'inch', 'kilometer', 'megameter', 'meter', 'micrometer', 'mile', 'millimeter', 'nanometer', 'parsec', 'petameter', 'picometer', 'terameter', 'yard', 'yoctometer', 'yottameter', 'zeptometer', 'zettameter' + - Units for "time" axes: 'attosecond', 'centisecond', 'day', 'decisecond', 'exasecond', 'femtosecond', 'gigasecond', 'hectosecond', 'hour', 'kilosecond', 'megasecond', 'microsecond', 'millisecond', 'minute', 'nanosecond', 'petasecond', 'picosecond', 'second', 'terasecond', 'yoctosecond', 'yottasecond', 'zeptosecond', 'zettasecond' +- MAY contain the field "longName". The value MUST be a string, and can provide a longer name or description of an axis and its properties. + +The "axes" are used as part of [[#multiscale-md]]. The length of "axes" MUST be equal to the number of dimensions of the arrays that contain the image data. + +
+ +Examples of valid axes: + +```json +[ + {"name": "x", "type": "space", "unit": "micrometer"}, + {"name": "t", "type": "time", "unit": "second", "longName": "Unix Epoch time"}, + {"name": "c", "type": "channel", "discrete": true}, + {"name": "i0", "type": "array"}, + {"name": "c", "type": "coordinate", "discrete" : true }, + {"name": "v", "type": "displacement", "discrete": true }, + {"name": "freq", "type": "frequency", "unit": "megahertz"} +] +``` +
+ +Arrays are inherently discrete (see Array coordinate systems, below) but are often used to store discrete samples of a +continuous variable. The continuous values "in between" discrete samples can be retrieved using an *interpolation* method. If an +axis is continuous (`"discrete" : false`), it indicates that interpolation is well-defined. Axes representing `space` and `time` +are usually continuous. Similarly, joint interpolation across axes is well-defined only for axes of the same `type`. In +contrast, discrete axes (`"discrete" : true`) may be indexed only by integers. Axes of representing a `channel`, `coordinate`, +or `displacement` are usually discrete. + +Note: The most common methods for interpolation are "nearest neighbor", "linear", "cubic", and "windowed sinc". Here, we refer +to any method that obtains values at real valued coordinates using discrete samples as an "interpolator". As such, label images +may be interpolated using "nearest neighbor" to obtain labels at points along the continuum. + +
+ +For the coordinate system: + +```json +{ + "name" : "index and interpolation", + "axes" : [ + {"name": "t", "type": "time"}, + {"name": "c", "type": "channel", "discrete": true}, + {"name": "y", "type": "space"}, + {"name": "x", "type": "space"} + ] +} +``` + +Indexing an image at the point `(0.1, 0.2, 0.3, 0.4)` is not valid, because the value of the first coordinate (`0.1`) refers +to the discrete axis `"c"`. Indexing an image at the point `(1, 0.2, 0.3, 0.4)` is valid. +
+ + +"coordinateSystems" metadata {#coord-sys-md} +-------------------------- + +A "coordinate system" is a collection of "axes" / dimensions with a name. Every coordinate system: +- MUST contain the field "name". The value MUST be a non-empty string that is unique among `coordinateSystem`s. +- MUST contain the field "axes", whose value is an array of valid "axes". + +
+ +```json +{ + "name" : "volume_micrometers", + "axes" : [ + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} + ] +} +``` +
+ +The order of the `"axes"` list matters and defines the index of each array dimension and coordinates for points in that +coordinate system. For the above example, the `"x"` dimension is the last dimension. The "dimensionality" of a coordinate system +is indicated by the length of its "axes" array. The "volume_micrometers" example coordinate system above is three dimensional (3D). + +The axes of a coordinate system (see below) give information about the types, units, and other properties of the coordinate +system's dimensions. Axis `name`s may contain semantically meaningful information, but can be arbitrary. As a result, two +coordinate systems that have identical axes in the same order may not be "the same" in the sense that measurements at the same +point refer to different physical entities and therefore should not be analyzed jointly. Tasks that require images, annotations, +regions of interest, etc., SHOULD ensure that they are in the same coordinate system (same name, with identical axes) or can be +transformed to the same coordinate system before doing analysis. See the example below. + +
+ +Two instruments simultaneously image the same sample from two different angles, and the 3D data from both instruments are +calibrated to "micrometer" units. Two samples are collected ("sampleA" and "sampleB"). An analysis of sample A requires +measurements from both instruments' images at certain points in space. Suppose a region of interest (ROI) is determined from the +image obtained from instrument 2, but quantification from that region is needed for instrument 1. Since measurements were +collected at different angles, a measurement by instrument 1 at the point with coordinates (x,y,z) may not correspond to the +measurement at the same point in instrument 2 (i.e., it may not be the same physical location in the sample). To analyze both +images together, they must be in the same coordinate system. + +The set of coordinate transformations ([[#trafo-md]]) encodes relationships between coordinate systems, specifically, how to +convert points and images to different coordinate systems. Implementations can apply the coordinate transform to images or +points in coordinate system "sampleA_instrument2" to bring them into the "sampleA_instrument1" coordinate system. In this case, +the ROI should be transformed to the "sampleA_image1" coordinate system, then used for quantification with the instrument 1 +image. + +```json +"coordinateSystems" : [ + { + "name" : "sampleA-instrument1", + "axes" : [ + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} + ] + }, + { + "name" : "sampleA-instrument2", + "axes" : [ + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} + ] + } +], +"coordinateTransformations": [ + { + "type": "affine": + "path": "../sampleA_instrument2-to-instrument1" + "input": "sampleA_instrument2", + "output": "sampleA_instrument1" + } +] +``` + +
+ + +### Array coordinate systems + +The dimensions of an array do not have an interpretation until they are associated with a coordinate system via a coordinate +transformation. Nevertheless, it can be useful to refer to the "raw" coordinates of the array. Some applications might prefer to +define points or regions-of-interest in "pixel coordinates" rather than "physical coordinates," for example. Indicating that +choice explicitly will be important for interoperability. This is possible by using **array coordinate systems**. + +Every array has a default coordinate system whose parameters need not be explicitly defined. The dimensionality of each array +coordinate system equals the dimensionality of its corresponding zarr array. Its name is the path to the array in the +container, its axes have `"type":"array"`, are unitless, and have "name"s corresponding to the `dimension_names` in zarr. +The `dimension_names` must be unique and non-null. + +
+ +For example, a 3D array at path `0` whose zarr.json contains: + +```json +"dimension_names": ["dim_0", "dim_1", "dim_2"] +``` + +implcitly defines the coordinate system: + +```json +{ + "name" : "0", + "axes" : [ + {"name": "dim_0", "type": "array"}, + {"name": "dim_1", "type": "array"}, + {"name": "dim_2", "type": "array"} + ] +} +``` + +though this object should not and need not explicitly appear in metadata. +
+ + +### Coordinate convention + +**The pixel/voxel center is the origin of the continuous coordinate system.** + +It is vital to consistently define relationship between the discrete/array and continuous/interpolated +coordinate systems. A pixel/voxel is the continuous region (rectangle) that corresponds to a single sample +in the discrete array, i.e., the area corresponding to nearest-neighbor (NN) interpolation of that sample. +The center of a 2d pixel corresponding to the origin `(0,0)` in the discrete array is the origin of the continuous coordinate +system `(0.0, 0.0)` (when the transformation is the identity). The continuous rectangle of the pixel is given by the +half-open interval `[-0.5, 0.5) x [-0.5, 0.5)` (i.e., -0.5 is included, +0.5 is excluded). See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. + + + +"bioformats2raw.layout" (transitional) {#bf2raw} +------------------------------------------------ + +[=Transitional=] "bioformats2raw.layout" metadata identifies a group which implicitly describes a series of images. +The need for the collection stems from the common "multi-image file" scenario in microscopy. Parsers like Bio-Formats +define a strict, stable ordering of the images in a single container that can be used to refer to them by other tools. + +In order to capture that information within an OME-Zarr dataset, `bioformats2raw` internally introduced a wrapping layer. +The bioformats2raw layout has been added to v0.4 as a transitional specification to specify filesets that already exist +in the wild. An upcoming NGFF specification will replace this layout with explicit metadata. + +

Layout

+ +Typical Zarr layout produced by running `bioformats2raw` on a fileset that contains more than one image (series > 1): + +
+series.ome.zarr               # One converted fileset from bioformats2raw
+    ├── zarr.json             # Contains "bioformats2raw.layout" metadata
+    ├── OME                   # Special group for containing OME metadata
+    │   ├── zarr.json         # Contains "series" metadata
+    │   └── METADATA.ome.xml  # OME-XML file stored within the Zarr fileset
+    ├── 0                     # First image in the collection
+    ├── 1                     # Second image in the collection
+    └── ...
+
+ +

Attributes

+ +The OME-Zarr Metadata in the top-level `zarr.json` file must contain the `bioformats2raw.layout` key: +
+path: examples/bf2raw/image.json
+highlight: json
+
+ +If the top-level group represents a plate, the `bioformats2raw.layout` metadata will be present but +the "plate" key MUST also be present, takes precedence and parsing of such datasets should follow [[#plate-md]]. It is not +possible to mix collections of images with plates at present. + +
+path: examples/bf2raw/plate.json
+highlight: json
+
+ +The OME-Zarr Metadata in the `zarr.json` file within the OME group may contain the "series" key: + +
+path: examples/ome/series-2.json
+highlight: json
+
+ +

Details

+ +Conforming groups: + +- MUST have the value "3" for the "bioformats2raw.layout" key in their OME-Zarr Metadata in the `zarr.json` at the top of the hierarchy; +- SHOULD have OME metadata representing the entire collection of images in a file named "OME/METADATA.ome.xml" which: + - MUST adhere to the OME-XML specification but + - MUST use `` elements as opposed to ``, `` or ``; + - MAY make use of the [minimum specification](https://docs.openmicroscopy.org/ome-model/6.2.2/specifications/minimum.html). + +Additionally, the logic for finding the Zarr group for each image follows the following logic: + +- If "plate" metadata is present, images MUST be located at the defined location. + - Matching "series" metadata (as described next) SHOULD be provided for tools that are unaware of the "plate" specification. +- If the "OME" Zarr group exists, it: + - MAY contain a "series" attribute. If so: + - "series" MUST be a list of string objects, each of which is a path to an image group. + - The order of the paths MUST match the order of the "Image" elements in "OME/METADATA.ome.xml" if provided. +- If the "series" attribute does not exist and no "plate" is present: + - separate "multiscales" images MUST be stored in consecutively numbered groups starting from 0 (i.e. "0/", "1/", "2/", "3/", ...). +- Every "multiscales" group MUST represent exactly one OME-XML "Image" in the same order as either the series index or the group numbers. + +Conforming readers: +- SHOULD make users aware of the presence of more than one image (i.e. SHOULD NOT default to only opening the first image); +- MAY use the "series" attribute in the "OME" group to determine a list of valid groups to display; +- MAY choose to show all images within the collection or offer the user a choice of images, as with HCS plates; +- MAY ignore other groups or arrays under the root of the hierarchy. + + +"coordinateTransformations" metadata {#trafo-md} +------------------------------------------------ + +"coordinateTransformations" describe the mapping between two coordinate systems (defined by "axes"). +For example, to map an array's discrete coordinate system to its corresponding physical coordinates. +Coordinate transforms are in the "forward" direction. They represent functions from *points* in the +input space to *points* in the output space. + + +- MUST contain the field "type". +- MUST contain any other fields required by the given "type" (see table below). +- MUST contain the field "output", unless part of a `sequence` or `inverseOf` (see details). +- MUST contain the field "input", unless part of a `sequence` or `inverseOf` (see details). +- MAY contain the field "name". Its value MUST be unique across all "name" fields for coordinate transformations. +- Parameter values MUST be compatible with input and output space dimensionality (see details). + + + +
`identity` + + The identity transformation is the default transformation and is typically not explicitly defined. +
`mapAxis` + `"mapAxis":Dict[String:String]` + A `maxAxis` transformation specifies an axis permutation as a map between axis names. +
`translation` + one of:
`"translation":List[number]`,
`"path":str` +
translation vector, stored either as a list of numbers (`"translation"`) or as binary data at a location + in this container (`path`). +
`scale` + one of:
`"scale":List[number]`,
`"path":str` +
scale vector, stored either as a list of numbers (`scale`) or as binary data at a location in this + container (`path`). +
`affine` + one of:
`"affine":List[List[number]]`,
`"path":str` +
affine transformation matrix stored as a flat array stored either with json uing the affine field + or as binary data at a location in this container (path). If both are present, the binary values at path should be used. +
`rotation` + one of:
`"rotation":List[List[number]]`,
`"path":str` +
rotation transformation matrix stored as an array stored either + with json or as binary data at a location in this container (path). + If both are present, the binary parameters at path are used. +
`sequence` + `"transformations":List[Transformation]` + A sequence of transformations, Applying the sequence applies the composition of all transforms in the list, in order. +
`displacements` + `"path":str`
`"interpolation":str` +
Displacement field transformation located at (path). +
`coordinates` + `"path":str`
`"interpolation":str` +
Coordinate field transformation located at (path). +
`inverseOf` + `"transform":Transform` + The inverse of a transformation. Useful if a transform is not closed-form invertible. See Forward and inverse for details and examples. +
`bijection` + `"forward":Transform`
`"inverse":Transform` +
Explicitly define an invertible transformation by providing a forward transformation and its inverse. +
`byDimension` + `"transformations":List[Transformation]` + Define a high dimensional transformation using lower dimensional transformations on subsets of + dimensions. +
typefieldsdescription +
+ + +Conforming readers: +- MUST parse `identity`, `scale`, `translation` transformations; +- SHOULD parse `mapAxis`, `affine` transformations; +- SHOULD be able to apply transformations to points; +- SHOULD be able to apply transformations to images; + +Coordinate transformations from array to physical coordinates MUST be stored in multiscales ([[#multiscale-md]]). +Transformations between different images MUST be stored in the attributes of a parent zarr group. For transformations that store +data or parameters in a zarr array, those zarr arrays SHOULD be stored in a zarr group `"coordinateTransformations"`. + +
+store.zarr                      # Root folder of the zarr store
+│
+├── zarr.json                   # coordinate transformations describing the relationship between two image coordinate systems
+│                               # are stored in the attributes of their parent group.
+│                               # transformations between 'volume' and 'crop' coordinate systems are stored here.
+│
+├── coordinateTransformations   # transformations that use array storage go in a "coordinateTransformations" zarr group.
+│   └── displacements           # for example, a zarr array containing a displacement field
+│       └── zarr.json
+│
+├── volume
+│   ├── zarr.json               # group level attributes (multiscales)
+│   └── 0                       # a group containing the 0th scale
+│       └── image               # a zarr array
+│           └── zarr.json       # physical coordinate system and transformations here
+│                               # the array attributes
+└── crop
+    ├── .zattrs                 # group level attributes (multiscales)
+    └── 0                       # a group containing the 0th scale
+        └── image               # a zarr array
+            └── zarr.json       # physical coordinate system and transformations here
+                                # the array attributes
+
+ +### Additional details + +Input and output dimensionality of a transformation may be determined by the value of the "input" and "output" fields, +respectively. If the value of "input" ("output") is an array, its length gives the number of input (output) dimensions, +otherwise the length of "axes" for the coordinate system with the name of the "input" ("output") value gives the number of input +(output) dimensions. + +Most coordinate transformations MUST specify their input and output coordinate systems using `input` and `output` with a string value +corresponding to the name of a coordinate system. The coordinate system's name may be the path to an array, and therefore may +not appear in the list of coordinate systems. + +Exceptions are if the the coordinate transformation appears in the `transformations` list of a `sequence` or is the +`transformation` of an `inverseOf` transformation. In these two cases input and output SHOULD be omitted (see below for +details). + +Transformations in the `transformations` list of a `byDimensions` transformation MUST provide `input` and `output` as arrays +of strings corresponding to axis names of the parent transformation's input and output coordinate systems (see below for +details). + +
+ +The sequence transformation's input corresponds to an array coordinate system at path "my/array". + +```json +"coordinateSystems" : [ + { "name" : "in", "axes" : [{"name" : "j"}, {"name":"i"}] }, + { "name" : "outScale", "axes" : [{"name" : "y"}, {"name":"x"}] }, + { "name" : "outSeq", "axes" : [{"name" : "y"}, {"name":"x"}] }, + { "name" : "outInv", "axes" : [{"name" : "y"}, {"name":"x"}] }, + { "name" : "outByDim", "axes" : [{"name" : "y"}, {"name":"x"}] } +], +"coordinateTransformations" : [ + { + "type": "scale", + "input" : "in", + "output" : "outScale", + "scale" : [ 0.5, 1.2 ] + }, + { + "type" : "sequence", + "input" : "my/array", + "output" : "outSeq", + "transformations" : [ + { "type": "scale", "scale" : [ 0.5, 0.6 ] }, + { "type": "translation", "translation" : [ 2, 5 ] } + ] + }, + { + "type": "inverseOf", + "input" : "in", + "output" : "outInv", + "transformation" : { + "type": "displacements", + "path": "path/to/displacements" + } + }, + { + "type": "byDimension", + "input" : "in", + "output" : "outDim", + "transformations" : [ + { "type" : "translation", "translation" : [1], "input" : ["i"], "output" : ["x"]}, + { "type" : "scale", "scale" : [2.0], "input" : ["j"], "output" : ["y"]} + ] + } +] +``` + +
+ +Coordinate transformations are functions of *points* in the input space to *points* in the output space. We call this the "forward" direction. +Points are ordered lists of coordinates, where a coordinate is the location/value of that point along its corresponding axis. +The indexes of axis dimensions correspond to indexes into transformation parameter arrays. For example, the scale transformation above +defines the function: + +``` +x = 0.5 * i +y = 1.2 * j +``` + +i.e., the mapping from the first input axis to the first output axis is determined by the first scale parameter. + +When rendering transformed images and interpolating, implementations may need the "inverse" transformation - from the output to +the input coordinate system. Inverse transformations will not be explicitly specified when they can be computed in closed form +from the forward transformation. Inverse transformations used for image rendering may be specified using the `inverseOf` +transformation type, for example: + +```json +{ + "type": "inverseOf", + "transformation" : { + "type": "displacements", + "path": "path/to/displacements", + } +} +``` + +Implementations SHOULD be able to compute and apply the inverse of some coordinate transformations when they +are computable in closed-form (as the [Transformation types](#transformation-types) section below indicates). If an +operation is requested that requires the inverse of a transformation that can not be inverted in closed-form, +implementations MAY estimate an inverse, or MAY output a warning that the requested operation is unsupported. + + +#### Matrix transformations + +Two transformation types ([affine](#affine) and [rotation](#rotation)) are parametrized by matrices. Matrices are applied to +column vectors that represent points in the input coordinate system. The first (last) axis in a coordinate system is the top +(bottom) entry in the column vector. Matrices are stored as two-dimensional arrays, either as json or in a zarr array. When +stored as a 2D zarr array, the first dimension indexes rows and the second dimension indexes columns (e.g., an array of +`"shape":[3,4]` has 3 rows and 4 columns). When stored as a 2D json array, the inner array contains rows (e.g. `[[1,2,3], +[4,5,6]]` has 2 rows and 3 columns). + +
+ +For matrix transformations, points in the coordinate system: + +``` +{ "name" : "in", "axes" : [{"name" : "z"}, {"name" : "y"}, {"name":"x"}] }, +``` + +are represented as column vectors: + +``` +[z] +[y] +[x] +``` + +As a result, transforming the point `[z,y,x]=[1,2,3]` with the matrix `[[0,1,0],[-1,0,0],[0,0,-1]]` +results in the point [2,-1,3] because it is computed with the matrix-vector multiplication: + +``` +[ 0 1 0] [1] [ 2] +[-1 0 0] [2] = [-1] +[ 0 0 -1] [3] [-3] +``` + +
+ + +### Transformation types + +#### identity + +`identity` transformations map input coordinates to output coordinates without modification. The position of +the ith axis of the output coordinate system is set to the position of the ith axis of the input coordinate +system. `identity` transformations are invertible. + +
+ +
+path: examples/transformations/identity.json
+highlight: json
+
+ +defines the function: + +``` +x = i +y = j +``` + +
+ + +#### mapAxis + +`mapAxis` transformations describe axis permutations as a mapping of axis names. Transformations MUST include a `mapAxis` field +whose value is an object, all of whose values are strings. If the object contains `"x":"i"`, then the transform sets the value +of the output coordinate for axis "x" to the value of the coordinate of input axis "i" (think `x = i`). For every axis in its output coordinate +system, the `mapAxis` MUST have a corresponding field. For every value of the object there MUST be an axis of the input +coordinate system with that name. Note that the order of the keys could be reversed. + + +
+ +
+path: examples/transformations/mapAxis1.json
+highlight: json
+
+ +The "equivalent to identity" transformation defines the function: + +``` +x = i +y = j +``` + +and the "permutation" transformation defines the function + +``` +x = j +y = i +``` + +
+ +
+ +
+path: examples/transformations/mapAxis2.json
+highlight: json
+
+ +The "projection_down" transformation defines the function: + +``` +x = b +``` + +and the "projection_up" transformation defines the function: + +``` +x = a +y = b +z = b +``` +
+ +#### translation + +`translation` transformations are special cases of affine transformations. When possible, a +translation transformation should be preferred to its equivalent affine. Input and output dimensionality MUST be +identical and MUST equal the the length of the "translation" array (N). `translation` transformations are +invertible. + +
+
path
+
The path to a zarr-array containing the translation parameters. + The array at this path MUST be 1D, and its length MUST be `N`.
+
scale
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
+ +
+ +
+path: examples/transformations/translation.json
+highlight: json
+
+ +defines the function: + +``` +x = i + 9 +y = j - 1.42 +``` +
+ +#### scale + +`scale` transformations are special cases of affine transformations. When possible, a scale transformation +SHOULD be preferred to its equivalent affine. Input and output dimensionality MUST be identical and MUST equal +the the length of the "scale" array (N). Values in the `scale` array SHOULD be non-zero; in that case, `scale` +transformations are invertible. + +
+
path
+
The path to a zarr-array containing the scale parameters. + The array at this path MUST be 1D, and its length MUST be `N`.
+
scale
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
+ +
+ +
+path: examples/transformations/scale.json
+highlight: json
+
+ +defines the function: + +``` +x = 3.12 * i +y = 2 * j +``` +
+ +#### affine + +`affine`s are [matrix transformations](#matrix-transformations) from N-dimensional inputs to M-dimensional outputs are +represented as the upper `(M)x(N+1)` sub-matrix of a `(M+1)x(N+1)` matrix in [homogeneous +coordinates](https://en.wikipedia.org/wiki/Homogeneous_coordinates) (see examples). This transformation type may be (but is not +necessarily) invertible when `N` equals `M`. The matrix MUST be stored as a 2D array either as json or as a zarr array. + +
+
path
+
The path to a zarr-array containing the affine parameters. + The array at this path MUST be 2D whose shape MUST be `M x (N+1)`.
+
affine
+
The affine parameters stored in JSON. The matrix MUST be stored as 2D nested array where the outer array MUST be length + `M` and the inner arrays MUST be length `N+1`.
+ +
+ A 2D-2D example: + +
+    path: examples/transformations/affine2d2d.json
+    highlight: json
+    
+ + defines the function: + + ``` + x = 1*i + 2*j + 3 + y = 4*i + 5*j + 6 + ``` + + it is equivalent to this matrix-vector multiplication in homogeneous coordinates: + + ``` + [ 1 2 3 ][ i ] [ x ] + [ 4 5 6 ][ j ] = [ y ] + [ 0 0 1 ][ 1 ] [ 1 ] + ``` + + where the last row `[0 0 1]` is omitted in the JSON representation. + +
+ +
+ An example with two dimensional inputs and three dimensional outputs. + + Note that the order of the axes can in general be determined by the application or user. + These axes relate to the memory or on-disk order insofar as the last dimension is contiguous + when the zarr array is c-order (the default for zarr version 2, and the only option for zarr version 3). + +
+    path: examples/transformations/affine2d3d.json
+    highlight: json
+    
+ + defines the function: + + ``` + x = 1*i + 2*j + 3 + y = 4*i + 5*j + 6 + z = 7*i + 8*j + 9 + ``` + + it is equivalent to this matrix-vector multiplication in homogeneous coordinates: + + ``` + [ 1 2 3 ][ i ] [ x ] + [ 4 5 6 ][ j ] = [ y ] + [ 7 8 9 ][ 1 ] [ z ] + [ 0 0 1 ] [ 1 ] + ``` + + where the last row `[0 0 1]` is omitted in the JSON representation. +
+ + +#### rotation + +`rotation`s are [matrix transformations](#matrix-transformations) that are special cases of affine transformations. When possible, a rotation +transformation SHOULD be preferred to its equivalent affine. Input and output dimensionality (N) MUST be identical. Rotations +are stored as `NxN` matrices, see below, and MUST have determinant equal to one, with orthonormal rows and columns. The matrix +MUST be stored as a 2D array either as json or in a zarr array. `rotation` transformations are invertible. + +
+
path
+
The path to an array containing the affine parameters. + The array at this path MUST be 2D whose shape MUST be `N x N`.
+
rotation
+
The parameters stored in JSON. The matrix MUST be stored as a 2D nested array where the outer array MUST be length `N` + and the inner arrays MUST be length `N`.
+ +
+ A 2D example + +
+    path: examples/transformations/rotation.json
+    highlight: json
+    
+ + defines the function: + + ``` + x = 0*i - 1*j + y = 1*i + 0*j + ``` +
+ + +#### inverseOf + +An `inverseOf` transformation contains another transformation (often non-linear), and indicates that +transforming points from output to input coordinate systems is possible using the contained transformation. +Transforming points from the input to the output coordinate systems requires the inverse of the contained +transformation (if it exists). + +
+ Software libraries that perform image registration often return the transformation from fixed image + coordinates to moving image coordinates, because this "inverse" transformation is most often required + when rendering the transformed moving image. Results such as this may be enclosed in an `inverseOf` + transformation. This enables the "outer" coordinate transformation to specify the moving image coordinates + as `input` and fixed image coordinates as `output`, a choice that many users and developers find intuitive. +
+ + +
+ +
+    path: examples/transformations/inverseOf.json
+    highlight: json
+    
+ +
+ +#### sequence + +A `sequence` transformation consists of an ordered array of coordinate transformations, and is invertible if every coordinate +transform in the array is invertible (though could be invertible in other cases as well). To apply a sequence transformation +to a point in the input coordinate system, apply the first transformation in the list of transformations. Next, apply the second +transformation to the result. Repeat until every transformation has been applied. The output of the last transformation is the +result of the sequence. + +
+ +Considering transformations as functions of points, if the list contains transformations `[f0, f1, f2]` in that order, applying +this sequence to point `x` is equivalent to: + +``` +f2(f1(f0(x))) +``` + +`f0` is applied first, `f1` is applied second, and `f2` is applied last. + +
+ +The transformations included in the `transformations` array may omit their `input` and `output` fields under the conditions +outlined below: + +- The `input` and `output` fields MAY be omitted for the following transformation types: + - `identity`, `scale`, `translation`, `rotation`, `affine`, `displacements`, `coordinates` +- The `input` and `output` fields MAY be omitted for `inverseOf` transformations if those fields may be omitted for the + transformation it wraps +- The `input` and `output` fields MAY be omitted for `bijection` transformations if the fields may be omitted for + both its `forward` and `inverse` transformations +- The `input` and `output` fields MAY be omitted for `sequence` transformations if the fields may be omitted for + all transformations in the sequence after flattening the nested sequence lists. +- The `input` and `output` fields MUST be included for transformations of type: `mapAxis`, and `byDimension`, and + under all other conditions. + + +
+
transformations
+
A non-empty array of transformations.
+
+ +
+ +This sequence: + +
+path: examples/transformations/sequence.json
+highlight: json
+
+ +describes the function + +``` +x = (i + 0.1) * 2 +y = (j + 0.9) * 3 +``` + +and is invertible. +
+ + +#### coordinates and displacements + +`coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a vector +field that defines a transformation. The arrays must have a dimension corresponding to every axis of the input coordinate +system and one additional dimension to hold components of the vector. Applying the transformation amounts to looking up the +appropriate vector in the array, interpolating if necessary, and treating it either as a position directly (`coordinates`) or a +displacement of the input point (`displacements`). + +These transformation types refer to an array at location specified by the `"path"` parameter. The input and output coordinate +systems for these transformations ("input / output coordinate systems") constrain the array size and the coordinate system +metadata for the array ("field coordinate system"). + +* If the input coordinate system has `N` axes, the array at location path MUST have `N+1` dimensions +* The field coordinate system MUST contain an axis identical to every axis of its input coordinate system in the same order. +* The field coordinate system MUST contain an axis with type `coordinate` or `displacement` respectively for transformations of type `coordinates` or `displacements`. + * This SHOULD be the last axis (contiguous on disk when c-order). +* If the output coordinate system has `M` axes, the length of the array along the `coordinate`/`displacement` dimension MUST be of length `M`. + +The `i`th value of the array along the `coordinate` or `displacement` axis refers to the coordinate or displacement +of the `i`th output axis. See the example below. + +
+ +In this example, the array located at `"displacementField"` MUST have three dimensions. One dimension MUST +correspond to an axis with `type : displacement` (in this example, the last dimension), the other two dimensions MUST be axes +that are identical to the axes of the `"in"` coordinate system. + +```json +"coordinateSystems" : [ + { "name" : "in", "axes" : [{"name" : "y"}, {"name":"x"}] }, + { "name" : "out", "axes" : [{"name" : "y"}, {"name":"x"}] } +], +"coordinateTransformations" : [ + { + "type": "displacements", + "input" : "in", + "output" : "out", + "path" : "displacementField" + } +] +``` + +The metadata at location `"displacementField"` should have a coordinate system such as: + +```json +"coordinateSystems" : [ + { "name" : "in", "axes" : [ + {"name":"y"}, {"name":"x"}, + {"name":"d", "type":"displacement", "discrete":true} ] + } +] +``` + +Indexing into this array using c-order, for spatial positions `y` and `x`, the y- and x-displacements would be given by: + +``` +y_displacement = displacementField[y][x][0] +x_displacement = displacementField[y][x][1] +``` + +I.e. the y-displacement is first, because the y-axis is the first element of the input and output coordinate systems. + +
+ + +`coordinates` and `displacements` transformations are not invertible in general, but implementations MAY approximate their +inverses. Metadata for these coordinate transforms have the following field: + +
+
path
+
The location of the coordinate array in this (or another) container.
+
interpolation
+
The `interpolation` attributes MAY be provided. It's value indicates + the interpolation to use if transforming points not on the array's discrete grid. + Values could be: +
    +
  • linear (default)
  • +
  • nearest
  • +
  • cubic
  • +
+
+ +For both `coordinates` and `displacements`, the array data at referred to by `path` MUST define coordinate system and coordinate transform metadata: + +* Every axis name in the `coordinateTransform`'s `input` MUST appear in the coordinate system +* The array dimension corresponding to the `coordinate` or `displacement` axis MUST have length equal to the number of dimensions of the `coordinateTransform` `output` +* If the input coordinate system `N` axes, then the array data at `path` MUST have `(N + 1)` dimensions. +* SHOULD have a `name` identical to the `name` of the corresponding `coordinateTransform`. + +For `coordinates`: + +* `coordinateSystem` metadata MUST have exactly one axis with `"type" : "coordinate"` +* the shape of the array along the "coordinate" axis must be exactly `N` + +For `displacements`: + +* `coordinateSystem` metadata MUST have exactly one axis with `"type" : "displacement"` +* the shape of the array along the "displacement" axis must be exactly `N` +* `input` and `output` MUST have an equal number of dimensions. + +For example, in 1D: +```json +{ + "name" : "a coordinate field transform", + "type": "coordinates", + "path" : "i2xCoordinates", + "input" : "i", + "output" : "x", + "interpolation" : "nearest" +} +``` + +where we assume input spaces "i" and "x" are defined elsewhere. +Example metadata for the array data at path `coordinates` above: + +```json +{ + "coordinateSystems" : [ + { + "name" : "a coordinate field transform", + "axes" : [ + { "name": "i", "type": "space", "discrete": true }, + { "name": "c", "type": "coordinate", "discrete": true } + ] + } + ], + "coordinateTransformations" : [ + { + "type" : "identity", + "output" : "a coordinate field transform" + } + ] +} +``` + +If the array in `coordinates` contains the data: `[-9, 9, 0]`, then this metadata defines the function: + +``` +x = + if ( i < 0.5 ) -9 + else if ( i >= 0.5 and i < 1.5 ) 9 + else if ( i >= 1.5 ) 0 +``` + + +A 1D example displacement field: +```json +{ + "name" : "a displacement field transform", + "type": "displacements", + "path" : "displacements", + "input" : "i", + "output" : "x", + "interpolation" : "linear" +} +``` + +where we assume input spaces "i" and "x" are defined elsewhere. +Example metadata for the array data at path `displacements` above: + +```json +{ + "coordinateSystems" : [ + { + "name" : "a displacement field transform", + "axes" : [ + { "name": "x", "type": "space", "unit" : "nanometer" }, + { "name": "d", "type": "displacement", "discrete": true } + ] + } + ], + "coordinateTransformations" : [ + { + "type" : "scale", + "scale" : [2, 1], + "output" : "a displacement field transform" + } + ] +} +``` +If the array in `displacements` contains the data: `[-1, 0, 1]`, +this transformation maps the point `[1.0]` to the point `[0.5]`. A scale +transformation maps the array coordinates to the "x" axis. Using the inverse +of the scale transform, we see that we need the position `0.5` in array coordinates. +The transformation specifies linear interpolation, which in this case yields +`(0.5 * -1) + (0.5 * 0) = -0.5`. That value gives us the displacement of the +input point, hence the output is `1.0 + (-0.5) = 0.5`. + + +#### byDimension + +`byDimension` transformations build a high dimensional transformation using lower dimensional transformations +on subsets of dimensions. + +
+
transformations
+
A list of transformations, each of which applies to a (non-strict) subset of input and output dimensions (axes). + The values of `input` and `output` fields MUST be an array of strings. + Every axis name in `input` MUST correspond to a name of some axis in this parent object's `input` coordinate system. + Every axis name in the parent byDimension's `output` MUST appear in exactly one of its child transformations' `output`. +
+
+ + +
+ +A valid `byDimension` transformation: + +
+path: examples/transformations/byDimension1.json
+highlight: json
+
+ +
+ +
+ +Another valid `byDimension` transformation: + +
+path: examples/transformations/byDimension2.json
+highlight: json
+
+ +
+ +
+ +This is an **invalid** `byDimension` transform: + +
+path: examples/transformations/byDimensionInvalid1.json
+highlight: json
+
+ +It is invalid for two reasons. First because input `0` used by the scale transformation is not an axis of the `byDimension` transformation's `input`. Second, the `x` axis of the `output` does not appear in the `output` of any child transformation. + +
+ +
+ +Another **invalid** `byDimension` transform: + +
+path: examples/transformations/byDimensionInvalid2.json
+highlight: json
+
+ +This transformation is invalid because the output axis `x` appears in more than one transformation in the `transformations` list. + +
+ + +#### bijection + +A bijection transformation is an invertible transformation in which both the `forward` and `inverse` transformations +are explicitly defined. Each direction SHOULD be a transformation type that is not closed-form invertible. +Its' input and output spaces MUST have equal dimension. The input and output dimensions for the both the forward +and inverse transformations MUST match bijection's input and output space dimensions. + +`input` and `output` fields MAY be omitted for the `forward` and `inverse` transformations, in which case +the `forward` transformation's `input` and `output` are understood to match the bijection's, and the `inverse` +transformation's `input` (`output`) matches the bijection's `output` (`input`), see the example below. + +Practically, non-invertible transformations have finite extents, so bijection transforms should only be expected +to be correct / consistent for points that fall within those extents. It may not be correct for any point of +appropriate dimensionality. + +
+ +
+path: examples/transformations/bijection.json
+highlight: json
+
+ +the input and output of the `forward` and `inverse` transformations are understood to be: + +
+path: examples/transformations/bijection_verbose.json
+highlight: json
+
+ +
+ + +"multiscales" metadata {#multiscale-md} +--------------------------------------- + +Metadata about an image can be found under the "multiscales" key in the group-level OME-Zarr Metadata. +Here, "image" refers to 2 to 5 dimensional data representing image or volumetric data with optional time or channel axes. +It is stored in a multiple resolution representation. + +"multiscales" contains a list of dictionaries where each entry describes a multiscale image. + +Each "multiscales" dictionary MUST contain the field "coordinateSystems", whose value is an array containing valid coordinate +system metadata (see [[#coord-sys-md]]). The first entry of this array is the "default" coordinate system, and should be +used for viewing and processing unless a use case dictates otherwise. It will generally be a representation of the image +in its native physical coordinate system. + +The following MUST hold for all coordinate systems. +The length of "axes" must be between 2 and 5 and MUST be equal to the dimensionality of the Zarr arrays storing the image data (see "datasets:path"). +The "axes" MUST contain 2 or 3 entries of "type:space" and MAY contain one additional entry of "type:time" and MAY contain one +additional entry of "type:channel" or a null / custom type. The order of the entries MUST correspond to the order of dimensions +of the Zarr arrays. In addition, the entries MUST be ordered by "type" where the "time" axis must come first (if present), +followed by the "channel" or custom axis (if present) and the axes of type "space". If there are three spatial axes where two +correspond to the image plane ("yx") and images are stacked along the other (anisotropic) axis ("z"), the spatial axes SHOULD be +ordered as "zyx". + +Each "multiscales" dictionary MUST contain the field "datasets", which is a list of dictionaries describing the arrays storing +the individual resolution levels. Each dictionary in "datasets" MUST contain the field "path", whose value is a string +containing the path to the Zarr array for this resolution relative to the current Zarr group. The "path"s MUST be ordered from +largest (i.e. highest resolution) to smallest. Every Zarr array referred to by a "path" MUST have the same number of dimensions +and MUST NOT have more than 5 dimensions. The number of dimensions and order MUST correspond to number and order of "axes". + +Each dictionary in "datasets" MUST contain the field "coordinateTransformation", whose value is a dictionary that defines a +transformation that maps Zarr array coordinates for this resolution level to the default coordinate system (the first entry of +the "coordinateSystems" array). The transformation is defined according to [[#trafo-md]]. The transformation MUST take as input +points in the array coordinate system corresponding to the Zarr array at location "path." The value of "input" SHOULD equal the +value of "path", but implementations should always treat the value of "input" as if it were equal to the value of "path". The +value of the transformation’s "output" MUST be the name of the default coordinate system. + +This transformation SHOULD be one of the following: + +* A single scale transformation +* A sequence transformation containing one scale and one translation transformation. + +In these cases, the scale transformation specifies the pixel size in physical units or time duration. If scaling information is +not available or applicable for one of the axes, the value MUST express the scaling factor between the current resolution and +the first resolution for the given axis, defaulting to 1.0 if there is no downsampling along the axis. This is strongly +recommended so that the the "default" coordinate system of the image avoids more complex transformations. + +If applications require additional transformations, each "multiscales" dictionary MAY contain the field +"coordinateTransformations", describing transformations that are applied to all resolution levels in the same manner, after +being transformed to the array at location "path." The value of "input" SHOULD equal the value of "path", but implementations +should always treat the value of "input" as if it were equal to the value of "path". + +Each "multiscales" dictionary SHOULD contain the field "name". + +Each "multiscales" dictionary SHOULD contain the field "type", which gives the type of downscaling method used to generate the multiscale image pyramid. +It SHOULD contain the field "metadata", which contains a dictionary with additional information about the downscaling method. + +
+path: examples/multiscales_strict/multiscales_example.json
+highlight: json
+
+ + +If only one multiscale is provided, use it. Otherwise, the user can choose by +name, using the first multiscale as a fallback: + +```python +datasets = [] +for named in multiscales: + if named["name"] == "3D": + datasets = [x["path"] for x in named["datasets"]] + break +if not datasets: + # Use the first by default. Or perhaps choose based on chunk size. + datasets = [x["path"] for x in multiscales[0]["datasets"]] +``` + +"omero" metadata (transitional) {#omero-md} +------------------------------------------- + +[=Transitional=] information specific to the channels of an image and how to render it +can be found under the "omero" key in the group-level metadata: + +```json +"id": 1, # ID in OMERO +"name": "example.tif", # Name as shown in the UI +"channels": [ # Array matching the c dimension size + { + "active": true, + "coefficient": 1, + "color": "0000FF", + "family": "linear", + "inverted": false, + "label": "LaminB1", + "window": { + "end": 1500, + "max": 65535, + "min": 0, + "start": 0 + } + } +], +"rdefs": { + "defaultT": 0, # First timepoint to show the user + "defaultZ": 118, # First Z section to show the user + "model": "color" # "color" or "greyscale" +} +``` + +See the [OMERO WebGateway documentation](https://omero.readthedocs.io/en/stable/developers/Web/WebGateway.html#imgdata) +for more information. + +The "omero" metadata is optional, but if present it MUST contain the field "channels", which is an array of dictionaries describing the channels of the image. +Each dictionary in "channels" MUST contain the field "color", which is a string of 6 hexadecimal digits specifying the color of the channel in RGB format. +Each dictionary in "channels" MUST contain the field "window", which is a dictionary describing the windowing of the channel. +The field "window" MUST contain the fields "min" and "max", which are the minimum and maximum values of the window, respectively. +It MUST also contain the fields "start" and "end", which are the start and end values of the window, respectively. + +"labels" metadata {#labels-md} +------------------------------ + +In OME-Zarr, Zarr arrays representing pixel-annotation data are stored in a group called "labels". Some applications--notably image segmentation--produce +a new image that is in the same coordinate system as a corresponding multiscale image (usually having the same dimensions and coordinate transformations). +This new image is composed of integer values corresponding to certain labels with custom meanings. For example, pixels take the value 1 or 0 +if the corresponding pixel in the original image represents cellular space or intercellular space, respectively. +Such an image is referred to in this specification as a 'label image'. + +The "labels" group is nested within an image group, at the same level of the Zarr hierarchy as the resolution levels for the original image. +The "labels" group is not itself an image; it contains images. The pixels of the label images MUST be integer data types, i.e. one of +[`uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`]. Intermediate groups between "labels" and the images within it are allowed, +but these MUST NOT contain metadata. Names of the images in the "labels" group are arbitrary. + +The OME-Zarr Metadata in the `zarr.json` file associated with the "labels" group MUST contain a JSON object with the key `labels`, whose value is a JSON array of paths to the +labeled multiscale image(s). All label images SHOULD be listed within this metadata file. For example: + +```json +{ + ... + "attributes": { + "ome": { + "version": "0.5", + "labels": [ + "cell_space_segmentation" + ] + } + } +} +``` + +The `zarr.json` file for the label image MUST implement the multiscales specification. Within the `multiscales` object, the JSON array +associated with the `datasets` key MUST have the same number of entries (scale levels) as the original unlabeled image. + +In addition to the `multiscales` key, the OME-Zarr Metadata in this image-level `zarr.json` file SHOULD contain another key, `image-label`, +whose value is also a JSON object. The `image-label` object stores information about the display colors, source image, and optionally, +further arbitrary properties of the label image. That `image-label` object SHOULD contain the following keys: first, a `colors` key, +whose value MUST be a JSON array describing color information for the unique label values. Second, a `version` key, whose value MUST be a +string specifying the version of the OME-Zarr `image-label` schema. + +Conforming readers SHOULD display labels using the colors specified by the `colors` JSON array, as follows. This array contains one +JSON object for each unique custom label. Each of these objects MUST contain the `label-value` key, whose value MUST be the integer +corresponding to a particular label. In addition to the `label-value` key, the objects in this array MAY contain an `rgba` key whose +value MUST be an array of four integers between 0 and 255, inclusive. These integers represent the `uint8` values of red, green, and +blue that comprise the final color to be displayed at the pixels with this label. The fourth integer in the `rgba` array represents alpha, +or the opacity of the color. Additional keys under `colors` are allowed. + +Next, the `image-label` object MAY contain the following keys: a `properties` key, and a `source` key. + +Like the `colors` key, the value of the `properties` key MUST be an array of JSON objects describing the set of unique possible pixel values. +Each object in the `properties` array MUST contain the `label-value` key, whose value again MUST be an integer specifying the pixel value for that label. +Additionally, an arbitrary number of key-value pairs MAY be present for each label value, denoting arbitrary metadata associated with that label. +Label-value objects within the `properties` array do not need to have the same keys. + +The value of the `source` key MUST be a JSON object containing information about the original image from which the label image derives. +This object MAY include a key `image`, whose value MUST be a string specifying the relative path to a Zarr image group. +The default value is `../../` since most labeled images are stored in a "labels" group that is nested within the original image group. + +Here is an example of a simple `image-label` object for a label image in which 0s and 1s represent intercellular and cellular space, respectively: + +
+path: examples/label_strict/colors_properties.json
+highlight: json
+
+ +In this case, the pixels consisting of a 0 in the Zarr array will be displayed as 50% blue and 50% opacity. Pixels with a 1 in the Zarr array, +which correspond to cellular space, will be displayed as 50% green and 50% opacity. + +"plate" metadata {#plate-md} +---------------------------- + +For high-content screening datasets, the plate layout can be found under the +custom attributes of the plate group under the `plate` key in the group-level metadata. + +The `plate` dictionary MAY contain an `acquisitions` key whose value MUST be a list of +JSON objects defining the acquisitions for a given plate to which wells can refer to. Each +acquisition object MUST contain an `id` key whose value MUST be an unique integer identifier +greater than or equal to 0 within the context of the plate to which fields of view can refer +to (see #well-md). +Each acquisition object SHOULD contain a `name` key whose value MUST be a string identifying +the name of the acquisition. Each acquisition object SHOULD contain a `maximumfieldcount` +key whose value MUST be a positive integer indicating the maximum number of fields of view for the +acquisition. Each acquisition object MAY contain a `description` key whose value MUST be a +string specifying a description for the acquisition. Each acquisition object MAY contain +a `starttime` and/or `endtime` key whose values MUST be integer epoch timestamps specifying +the start and/or end timestamp of the acquisition. + +The `plate` dictionary MUST contain a `columns` key whose value MUST be a list of JSON objects +defining the columns of the plate. Each column object defines the properties of +the column at the index of the object in the list. Each column in the physical plate +MUST be defined, even if no wells in the column are defined. Each column object MUST +contain a `name` key whose value is a string specifying the column name. The `name` MUST +contain only alphanumeric characters, MUST be case-sensitive, and MUST NOT be a duplicate of any +other `name` in the `columns` list. Care SHOULD be taken to avoid collisions on +case-insensitive filesystems (e.g. avoid using both `Aa` and `aA`). + +The `plate` dictionary SHOULD contain a `field_count` key whose value MUST be a positive integer +defining the maximum number of fields per view across all wells. + +The `plate` dictionary SHOULD contain a `name` key whose value MUST be a string defining the +name of the plate. + +The `plate` dictionary MUST contain a `rows` key whose value MUST be a list of JSON objects +defining the rows of the plate. Each row object defines the properties of +the row at the index of the object in the list. Each row in the physical plate +MUST be defined, even if no wells in the row are defined. Each defined row MUST +contain a `name` key whose value MUST be a string defining the row name. The `name` MUST +contain only alphanumeric characters, MUST be case-sensitive, and MUST NOT be a duplicate of any +other `name` in the `rows` list. Care SHOULD be taken to avoid collisions on +case-insensitive filesystems (e.g. avoid using both `Aa` and `aA`). + +The `plate` dictionary MUST contain a `version` key whose value MUST be a string specifying the +version of the plate specification. + +The `plate` dictionary MUST contain a `wells` key whose value MUST be a list of JSON objects +defining the wells of the plate. Each well object MUST contain a `path` key whose value MUST +be a string specifying the path to the well subgroup. The `path` MUST consist of a `name` in +the `rows` list, a file separator (`/`), and a `name` from the `columns` list, in that order. +The `path` MUST NOT contain additional leading or trailing directories. +Each well object MUST contain both a `rowIndex` key whose value MUST be an integer identifying +the index into the `rows` list and a `columnIndex` key whose value MUST be an integer identifying +the index into the `columns` list. `rowIndex` and `columnIndex` MUST be 0-based. The +`rowIndex`, `columnIndex`, and `path` MUST all refer to the same row/column pair. + +For example the following JSON object defines a plate with two acquisitions and +6 wells (2 rows and 3 columns), containing up to 2 fields of view per acquisition. + +
+path: examples/plate_strict/plate_6wells.json
+highlight: json
+
+ +The following JSON object defines a sparse plate with one acquisition and +2 wells in a 96 well plate, containing one field of view per acquisition. + +
+path: examples/plate_strict/plate_2wells.json
+highlight: json
+
+ +"well" metadata {#well-md} +-------------------------- + +For high-content screening datasets, the metadata about all fields of views +under a given well can be found under the "well" key in the attributes of the +well group. + +The `well` dictionary MUST contain an `images` key whose value MUST be a list of JSON objects +specifying all fields of views for a given well. Each image object MUST contain a +`path` key whose value MUST be a string specifying the path to the field of view. The `path` +MUST contain only alphanumeric characters, MUST be case-sensitive, and MUST NOT be a duplicate +of any other `path` in the `images` list. If multiple acquisitions were performed in the plate, +it MUST contain an `acquisition` key whose value MUST be an integer identifying the acquisition +which MUST match one of the acquisition JSON objects defined in the plate metadata (see #plate-md). + +The `well` dictionary SHOULD contain a `version` key whose value MUST be a string specifying the +version of the well specification. + +For example the following JSON object defines a well with four fields of +view. The first two fields of view were part of the first acquisition while +the last two fields of view were part of the second acquisition. + +
+path: examples/well_strict/well_4fields.json
+highlight: json
+
+ +The following JSON object defines a well with two fields of view in a plate with +four acquisitions. The first field is part of the first acquisition, and the second +field is part of the last acquisition. + +
+path: examples/well_strict/well_2fields.json
+highlight: json
+
+ +Specification naming style {#naming-style} +========================================== + +Multi-word keys in this specification should use the `camelCase` style. +NB: some parts of the specification don't obey this convention as they +were added before this was adopted, but they should be updated in due course. + +Implementations {#implementations} +================================== + +See [Tools](https://ngff.openmicroscopy.org/tools/index.html). + +Citing {#citing} +================ + +[Next-generation file format (NGFF) specifications for storing bioimaging data in the cloud.](https://ngff.openmicroscopy.org/0.4) +J. Moore, *et al*. Open Microscopy Environment Consortium, 8 February 2022. +This edition of the specification is [https://ngff.openmicroscopy.org/0.5/](https://ngff.openmicroscopy.org/0.5/]). +The latest edition is available at [https://ngff.openmicroscopy.org/latest/](https://ngff.openmicroscopy.org/latest/). +[(doi:10.5281/zenodo.4282107)](https://doi.org/10.5281/zenodo.4282107) + +Version History {#history} +========================== + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RevisionDateDescription
0.5.22025-01-10Clarify that the dimension_names field in axes MUST be included.
0.5.12025-01-10Re-add the improved omero description in PR-191.
0.5.02024-11-21use Zarr v3 in OME-Zarr, see RFC-2.
0.4.12023-02-09expand on "labels" description
0.4.12022-09-26transitional metadata for image collections ("bioformats2raw.layout")
0.4.02022-02-08multiscales: add axes type, units and coordinateTransformations
0.4.02022-02-08plate: add rowIndex/columnIndex
0.3.02021-08-24Add axes field to multiscale metadata
0.2.02021-03-29Change chunk dimension separator to "/"
0.1.42020-11-26Add HCS specification
0.1.32020-09-14Add labels specification
0.1.2 2020-05-07Add description of "omero" metadata
0.1.1 2020-05-06Add info on the ordering of resolutions
0.1.0 2020-04-20First version for internal demo
+ + +
+{
+  "blogNov2020": {
+    "href": "https://blog.openmicroscopy.org/file-formats/community/2020/11/04/zarr-data/",
+    "title": "Public OME-Zarr data (Nov. 2020)",
+    "authors": [
+      "OME Team"
+    ],
+    "status": "Informational",
+    "publisher": "OME",
+    "id": "blogNov2020",
+    "date": "04 November 2020"
+  },
+  "imagesc26952": {
+    "href": "https://forum.image.sc/t/ome-s-position-regarding-file-formats/26952",
+    "title": "OME’s position regarding file formats",
+    "authors": [
+      "OME Team"
+    ],
+    "status": "Informational",
+    "publisher": "OME",
+    "id": "imagesc26952",
+    "date": "19 June 2020"
+  },
+  "n5": {
+    "id": "n5",
+    "href": "https://github.com/saalfeldlab/n5/issues/62",
+    "title": "N5---a scalable Java API for hierarchies of chunked n-dimensional tensors and structured meta-data",
+    "status": "Informational",
+    "authors": [
+      "John A. Bogovic",
+      "Igor Pisarev",
+      "Philipp Hanslovsky",
+      "Neil Thistlethwaite",
+      "Stephan Saalfeld"
+    ],
+    "date": "2020"
+  },
+  "ome-zarr-py": {
+    "id": "ome-zarr-py",
+    "href": "https://doi.org/10.5281/zenodo.4113931",
+    "title": "ome-zarr-py: Experimental implementation of next-generation file format (NGFF) specifications for storing bioimaging data in the cloud.",
+    "status": "Informational",
+    "publisher": "Zenodo",
+    "authors": [
+      "OME",
+      "et al"
+    ],
+    "date": "06 October 2020"
+  },
+  "zarr": {
+    "id": "zarr",
+    "href": "https://doi.org/10.5281/zenodo.4069231",
+    "title": "Zarr: An implementation of chunked, compressed, N-dimensional arrays for Python.",
+    "status": "Informational",
+    "publisher": "Zenodo",
+    "authors": [
+      "Alistair Miles",
+      "et al"
+    ],
+    "date": "06 October 2020"
+  },
+  "itk":{
+    "id": "itk-book",
+    "href": "https://itk.org/ItkSoftwareGuide.pdf",
+    "title": "The ITK Software Guide",
+    "status": "Informational",
+    "publisher": "ITK",
+    "authors": [
+      "Hans J. Johnson",
+      "Matthew M. McCormick",
+      "Luis Ibanez",
+      "Insight Software Consortium"
+    ],
+    "date": "16 April 2021"
+  }
+}
+
diff --git a/0.6-dev/schemas/_version.schema b/0.6-dev/schemas/_version.schema new file mode 100644 index 00000000..1da06a6d --- /dev/null +++ b/0.6-dev/schemas/_version.schema @@ -0,0 +1,10 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema", + "title": "OME-Zarr Metadata version", + "description": "The version of the OME-Zarr Metadata", + "type": "string", + "enum": [ + "0.5" + ] +} diff --git a/0.6-dev/schemas/axes.schema b/0.6-dev/schemas/axes.schema new file mode 100644 index 00000000..56b447f2 --- /dev/null +++ b/0.6-dev/schemas/axes.schema @@ -0,0 +1,62 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/axes.schema", + "title": "NGFF Axes", + "description": "JSON from OME-NGFF .zattrs", + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "$ref": "#/$defs/axis" + }, + "$comment": "Ensure that there exist at most three space axes", + "contains": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "space" + ] + }, + "unit": { + "type": "string" + } + } + }, + "minContains": 0, + "maxContains": 3, + "$defs": { + "axis": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the axis" + }, + "longName": { + "type": "string", + "description": "Longer name or description of the axis." + }, + "type": { + "type": "string", + "description": "Dimension of the axis" + }, + "discrete": { + "type": "boolean", + "description": "Whether the dimension is discrete" + }, + "unit": { + "type": "string", + "description": "Unit for the axis" + } + }, + "required": [ + "name" + ] + } + } +} diff --git a/0.6-dev/schemas/bf2raw.schema b/0.6-dev/schemas/bf2raw.schema new file mode 100644 index 00000000..d3c87253 --- /dev/null +++ b/0.6-dev/schemas/bf2raw.schema @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/bf2raw.schema", + "title": "OME-Zarr container produced by bioformats2raw", + "description": "The zarr.json attributes key", + "type": "object", + "properties": { + "ome": { + "description": "The versioned OME-Zarr Metadata namespace", + "type": "object", + "properties": { + "bioformats2raw.layout": { + "description": "The top-level identifier metadata added by bioformats2raw", + "type": "number", + "enum": [ + 3 + ] + }, + "version": { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema" + } + }, + "required": [ + "bioformats2raw.layout", + "version" + ] + } + }, + "required": [ + "ome" + ] +} diff --git a/0.6-dev/schemas/coordinateTransformations.schema b/0.6-dev/schemas/coordinateTransformations.schema new file mode 100644 index 00000000..8bd35f4f --- /dev/null +++ b/0.6-dev/schemas/coordinateTransformations.schema @@ -0,0 +1,296 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/coordinateTransformations.schema", + "title": "NGFF Coordinate Transformations", + "description": "JSON from OME-NGFF .zattrs", + "type": "array", + "minItems": 1, + "contains": { + "type": "object" + }, + "maxContains": 1, + "items": { + "$ref": "#/$defs/transformation" + }, + "$defs": { + "transformation" : { + "oneOf": [ + { + "$ref": "#/$defs/scaleTransformation" + }, + { + "$ref": "#/$defs/translationTransformation" + }, + { + "$ref": "#/$defs/affineTransformation" + }, + { + "$ref": "#/$defs/rotationTransformation" + }, + { + "$ref": "#/$defs/inverseOfTransformation" + }, + { + "$ref": "#/$defs/sequenceTransformation" + }, + { + "$ref": "#/$defs/coordinatesTransformation" + }, + { + "$ref": "#/$defs/displacementsTransformation" + }, + { + "$ref": "#/$defs/byDimensionTransformation" + }, + { + "$ref": "#/$defs/bijectionTransformation" + } + ] + }, + "scaleTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "scale" + ] + }, + "scale": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "path": { + "type": "string" + } + }, + "required": [ + "type" + ], + "oneOf": [ + { + "required": ["scale"] + }, + { + "required": ["path"] + } + ] + }, + "translationTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "translation" + ] + }, + "translation": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "path": { + "type": "string" + } + }, + "required": [ + "type" + ], + "oneOf": [ + { + "required": ["translation"] + }, + { + "required": ["path"] + } + ] + }, + "affineTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "affine" + ] + }, + "affine": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "path": { + "type": "string" + } + }, + "required": [ + "type" + ], + "oneOf": [ + { + "required": ["affine"] + }, + { + "required": ["path"] + } + ] + }, + "rotationTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rotation" + ] + }, + "rotation": { + "type": "object", + "minItems": 2, + "items": { + "type": "number" + } + }, + "path": { + "type": "string" + } + }, + "required": [ + "type" + ], + "oneOf": [ + { + "required": ["rotation"] + }, + { + "required": ["path"] + } + ] + }, + "inverseOfTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "inverseOf" + ] + }, + "transformation": { + "type": "object" + } + }, + "required": [ + "type", "transformation" + ] + }, + "sequenceTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "sequence" + ] + }, + "transformations": { + "type": "array" + } + }, + "required": [ + "type", "transformations" + ] + }, + "coordinatesTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "coordinates" + ] + }, + "path": { + "type": "string" + }, + "interpolation": { + "type": "string", + "enum": [ + "nearest", "linear", "cubic" + ] + } + }, + "required": [ + "type", "path", "interpolation" + ] + }, + "displacementsTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "displacements" + ] + }, + "path": { + "type": "string" + }, + "interpolation": { + "type": "string", + "enum": [ + "nearest", "linear", "cubic" + ] + } + }, + "required": [ + "type", "path", "interpolation" + ] + }, + "byDimensionTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "byDimension" + ] + }, + "transformations": { + "type": "array" + } + }, + "required": [ + "type", "transformations" + ] + }, + "bijectionTransformation": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "bijection" + ] + }, + "forward": { + "type": "object" + }, + "inverse": { + "type": "object" + } + }, + "required": [ + "type", "forward", "inverse" + ] + } + } +} diff --git a/0.6-dev/schemas/coordinate_systems.schema b/0.6-dev/schemas/coordinate_systems.schema new file mode 100644 index 00000000..583d9941 --- /dev/null +++ b/0.6-dev/schemas/coordinate_systems.schema @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/coordinate_systems.schema", + "title": "NGFF CoordinateSystem", + "description": "JSON from OME-NGFF .zattrs", + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/coordinateSystem" + }, + "$defs": { + "coordinateSystem": { + "description": "Coordinate Systems for OME-NGFF", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of coordinate system" + }, + "axes": { + "$ref": "axes.schema" + } + }, + "required": [ + "name", + "axes" + ] + } + } +} diff --git a/0.6-dev/schemas/coordinate_systems_and_transforms.schema b/0.6-dev/schemas/coordinate_systems_and_transforms.schema new file mode 100644 index 00000000..403f78a5 --- /dev/null +++ b/0.6-dev/schemas/coordinate_systems_and_transforms.schema @@ -0,0 +1,45 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/coordinate_systems_and_transforms.schema", + "title": "NGFF Coordinate Systems and Transforms", + "description": "Coordinate Systems and transforms for OME-NGFF", + "type": "object", + "properties": { + "coordinateSystems": { + "$ref": "coordinate_systems.schema" + }, + "coordinateTransformations": { + "$ref": "coordinate_transformation.schema" + }, + "arrayCoordinateSystem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of coordinate space" + }, + "axes": { + "allOf": [ + { + "$ref": "axes.schema" + }, + { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "const": "array" + } + } + } + } + ] + } + }, + "required": [ + "axes" + ] + } + } +} diff --git a/0.6-dev/schemas/coordinate_transformation.schema b/0.6-dev/schemas/coordinate_transformation.schema new file mode 100644 index 00000000..e5156da0 --- /dev/null +++ b/0.6-dev/schemas/coordinate_transformation.schema @@ -0,0 +1,344 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/coordinate_transformation.schema", + "title": "NGFF Coordinate Systems and Transforms", + "description": "Coordinate Systems and transforms for OME-NGFF", + "type": "array", + "uniqueItems": true, + "minItems": 1, + "items": { + "allOf": [ + { + "$ref": "#/$defs/coordinateTransformation" + }, + { + "type": "object", + "properties": { + "input": { + "type": "string" + }, + "output": { + "type": "string" + } + }, + "required": [ + "input", + "output" + ] + } + ] + }, + "$defs": { + "path_w_url": { + "description": "Path specification. Schema local solution until https://github.com/ome/ngff/issues/144 is resolved.", + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "path" + ] + }, + "coordinateTransformation": { + "description": "OME-NGFF coordinate transformation.", + "allOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "oneOf": [ + { + "$ref": "#/$defs/identity" + }, + { + "$ref": "#/$defs/mapAxis" + }, + { + "$ref": "#/$defs/scale" + }, + { + "$ref": "#/$defs/translation" + }, + { + "$ref": "#/$defs/affine" + }, + { + "$ref": "#/$defs/rotation" + }, + { + "$ref": "#/$defs/inverseOf" + }, + { + "$ref": "#/$defs/bijection" + }, + { + "$ref": "#/$defs/sequence" + }, + { + "$ref": "#/$defs/byDimension" + }, + { + "$ref": "#/$defs/displacements" + }, + { + "$ref": "#/$defs/coordinates" + } + ] + } + ] + }, + "byDimensionTransformation": { + "type": "object", + "description": "Transformation used inside a byDimension transformation", + "allOf": [ + { "$ref": "#/$defs/coordinateTransformation" }, + { + "properties": { + "input": { + "type": "array", + "items": { + "type": "string" + } + }, + "output": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + ], + "required": ["input", "output"] + }, + "identity": { + "type": "object", + "properties": { + "type": { + "const": "identity" + } + } + }, + "mapAxis": { + "type": "object", + "description": "Permute axes by name", + "properties": { + "type": { + "const": "mapAxis" + }, + "mapAxis": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + }, + "required": [ + "mapAxis" + ] + } + }, + "scale": { + "type": "object", + "properties": { + "type": { + "const": "scale" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" + }, + { + "properties": { + "scale": { + "type": "array", + "items": { + "type": "number", + "exclusiveMinimum": 0 + } + } + }, + "required": [ + "scale" + ] + } + ] + }, + "translation": { + "type": "object", + "properties": { + "type": { + "const": "translation" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" + }, + { + "properties": { + "translation": { + "type": "array", + "items": { + "type": "number" + } + } + }, + "required": [ + "translation" + ] + } + ] + }, + "mtxFlatOrNested" : { + "type": "array", + "oneOf": [ + { + "items": { "type": "number" } + }, + { + "items": { + "type": "array", + "items" : { "type" : "number" } + } + } + ] + }, + "affine": { + "type": "object", + "properties": { + "type": { + "const": "affine" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" + }, + { + "properties": { + "affine": { + "$ref": "#/$defs/mtxFlatOrNested" + }, + "required": [ + "affine" + ] + } + } + ] + }, + "rotation": { + "type": "object", + "properties": { + "type": { + "const": "rotation" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" + }, + { + "properties": { + "rotation": { + "type": "array", + "items": { + "type": "number" + } + }, + "required": [ + "rotation" + ] + } + } + ] + }, + "inverseOf": { + "type": "object", + "properties": { + "type": { + "const": "inverseOf" + }, + "transformation": { + "$ref": "#/$defs/coordinateTransformation" + } + }, + "required": [ + "transformation" + ] + }, + "bijection": { + "type": "object", + "properties": { + "type": { + "const": "bijection" + }, + "forward": { + "$ref": "#/$defs/coordinateTransformation" + }, + "inverse": { + "$ref": "#/$defs/coordinateTransformation" + } + }, + "required": [ + "forward", "inverse" + ] + }, + "sequence": { + "description": "A sequence of transformations", + "type": "object", + "properties": { + "type": { "const": "sequence" }, + "transformations": { + "type": "array", + "items": { + "$ref": "#/$defs/coordinateTransformation" + } + } + } + }, + "byDimension": { + "type": "object", + "properties": { + "type": { "const": "byDimension" }, + "transformations": { + "type": "array", + "items": { + "$ref": "#/$defs/byDimensionTransformation" + } + } + } + }, + "displacements": { + "type": "object", + "properties": { + "type": { "const": "displacements" } + } + }, + "coordinates": { + "type": "object", + "properties": { + "type": { "const": "coordinates" } + } + } + } +} diff --git a/0.6-dev/schemas/image.schema b/0.6-dev/schemas/image.schema new file mode 100644 index 00000000..835c88a3 --- /dev/null +++ b/0.6-dev/schemas/image.schema @@ -0,0 +1,435 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/image.schema", + "title": "OME-Zarr Image", + "description": "The zarr.json attributes key", + "type": "object", + "properties": { + "ome": { + "description": "The versioned OME-Zarr Metadata namespace", + "type": "object", + "properties": { + "multiscales": { + "$ref": "#/$defs/multiscales" + }, + "omero": { + "$ref": "#/$defs/omero" + }, + "version": { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema" + } + }, + "required": [ + "multiscales", + "version" + ] + } + }, + "required": [ + "ome" + ], + "$defs": { + "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" + }, + "coordinateTransformations": { + "$ref": "coordinate_transformation.schema" + } + }, + "required": [ + "path", + "coordinateTransformations" + ] + } + }, +<<<<<<< HEAD:latest/schemas/image.schema + "version": { + "type": "string", + "enum": [ + "0.5-dev" + ] + }, + "coordinateSystems": { + "$ref": "coordinate_systems.schema" +||||||| 449fbca:latest/schemas/image.schema + "version": { + "type": "string", + "enum": [ + "0.5-dev" + ] + }, + "axes": { + "$ref": "#/$defs/axes" +======= + "axes": { + "$ref": "#/$defs/axes" +>>>>>>> upstream/main:0.5/schemas/image.schema + }, + "coordinateTransformations": { +<<<<<<< HEAD:latest/schemas/image.schema + "$ref": "coordinate_transformation.schema" + } +||||||| 449fbca:latest/schemas/image.schema + "$ref": "#/$defs/coordinateTransformations" + } +======= + "$ref": "#/$defs/coordinateTransformations" + } +>>>>>>> upstream/main:0.5/schemas/image.schema + }, + "required": [ +<<<<<<< HEAD:latest/schemas/image.schema + "datasets", "coordinateSystems" +||||||| 449fbca:latest/schemas/image.schema + "datasets", "axes" +======= + "datasets", + "axes" +>>>>>>> upstream/main:0.5/schemas/image.schema + ] + }, + "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": [ + "channels" + ] +<<<<<<< HEAD:latest/schemas/image.schema + } + }, + "required": [ "multiscales" ] +||||||| 449fbca:latest/schemas/image.schema + } + }, + "required": [ "multiscales" ], + + "$defs": { + "axes": { + "type": "array", + "uniqueItems": true, + "minItems": 2, + "maxItems": 5, + "contains": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["space"] + }, + "units": { + "type": "string" + } + } + }, + "minContains": 2, + "maxContains": 3, + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["channel", "time", "space"] + } + }, + "required": ["name", "type"] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "not": { + "enum": ["space", "time", "channel"] + } + } + }, + "required": ["name"] + } + ] + } + }, + "coordinateTransformations": { + "type": "array", + "minItems": 1, + "contains": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "scale" + ] + }, + "scale": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "maxContains": 1, + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "scale" + ] + }, + "scale": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "required": ["type", "scale"] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "translation" + ] + }, + "translation": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "required": ["type", "translation"] + } + ] + } + } + } +======= + }, + "axes": { + "type": "array", + "uniqueItems": true, + "minItems": 2, + "maxItems": 5, + "contains": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "space" + ] + }, + "unit": { + "type": "string" + } + } + }, + "minContains": 2, + "maxContains": 3, + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "channel", + "time", + "space" + ] + } + }, + "required": [ + "name", + "type" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "not": { + "enum": [ + "space", + "time", + "channel" + ] + } + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "coordinateTransformations": { + "type": "array", + "minItems": 1, + "contains": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "scale" + ] + }, + "scale": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "maxContains": 1, + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "scale" + ] + }, + "scale": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "required": [ + "type", + "scale" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "translation" + ] + }, + "translation": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "required": [ + "type", + "translation" + ] + } + ] + } + } + } +>>>>>>> upstream/main:0.5/schemas/image.schema +} diff --git a/0.6-dev/schemas/label.schema b/0.6-dev/schemas/label.schema new file mode 100644 index 00000000..dd5cb6d6 --- /dev/null +++ b/0.6-dev/schemas/label.schema @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/label.schema", + "title": "OME-Zarr labelled image schema", + "description": "The zarr.json attributes key", + "type": "object", + "properties": { + "ome": { + "description": "The versioned OME-Zarr Metadata namespace", + "type": "object", + "properties": { + "image-label": { + "$ref": "#/$defs/image-label" + }, + "version": { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema" + } + }, + "required": [ + "image-label", + "version" + ] + } + }, + "required": [ + "ome" + ], + "$defs": { + "image-label": { + "type": "object", + "properties": { + "colors": { + "description": "The colors for this label image", + "type": "array", + "items": { + "type": "object", + "properties": { + "label-value": { + "description": "The value of the label", + "type": "number" + }, + "rgba": { + "description": "The RGBA color stored as an array of four integers between 0 and 255", + "type": "array", + "items": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "minItems": 4, + "maxItems": 4 + } + }, + "required": [ + "label-value" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "properties": { + "description": "The properties for this label image", + "type": "array", + "items": { + "type": "object", + "properties": { + "label-value": { + "description": "The pixel value for this label", + "type": "integer" + } + }, + "required": [ + "label-value" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "source": { + "description": "The source of this label image", + "type": "object", + "properties": { + "image": { + "type": "string" + } + } + } + } + } + } +} diff --git a/0.6-dev/schemas/ome.schema b/0.6-dev/schemas/ome.schema new file mode 100644 index 00000000..36569280 --- /dev/null +++ b/0.6-dev/schemas/ome.schema @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/ome.schema", + "title": "OME-Zarr group produced by bioformats2raw to contain OME metadata", + "description": "The zarr.json attributes key", + "type": "object", + "properties": { + "ome": { + "description": "The versioned OME-Zarr Metadata namespace", + "type": "object", + "properties": { + "series": { + "description": "An array of the same length and the same order as the images defined in the OME-XML", + "type": "array", + "items": { + "type": "string" + }, + "minContains": 1 + }, + "version": { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema" + } + }, + "required": [ + "series", + "version" + ] + } + }, + "required": [ + "ome" + ] +} diff --git a/0.6-dev/schemas/ome_zarr.schema b/0.6-dev/schemas/ome_zarr.schema new file mode 100644 index 00000000..9b22bba6 --- /dev/null +++ b/0.6-dev/schemas/ome_zarr.schema @@ -0,0 +1,24 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/ome_zarr.schema", + "anyOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/bf2raw.schema" + }, + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/image.schema" + }, + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/label.schema" + }, + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/ome.schema" + }, + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/plate.schema" + }, + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/well.schema" + } + ] +} diff --git a/0.6-dev/schemas/plate.schema b/0.6-dev/schemas/plate.schema new file mode 100644 index 00000000..3c8a1b97 --- /dev/null +++ b/0.6-dev/schemas/plate.schema @@ -0,0 +1,153 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/plate.schema", + "title": "OME-Zarr plate schema", + "description": "The zarr.json attributes key", + "type": "object", + "properties": { + "ome": { + "description": "The versioned OME-Zarr Metadata namespace", + "type": "object", + "properties": { + "plate": { + "type": "object", + "properties": { + "acquisitions": { + "description": "The acquisitions for this plate", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "description": "A unique identifier within the context of the plate", + "type": "integer", + "minimum": 0 + }, + "maximumfieldcount": { + "description": "The maximum number of fields of view for the acquisition", + "type": "integer", + "exclusiveMinimum": 0 + }, + "name": { + "description": "The name of the acquisition", + "type": "string" + }, + "description": { + "description": "The description of the acquisition", + "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" + ] + } + }, + "field_count": { + "description": "The maximum number of fields per view across all wells", + "type": "integer", + "exclusiveMinimum": 0 + }, + "name": { + "description": "The name of the plate", + "type": "string" + }, + "columns": { + "description": "The columns of the plate", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "The column name", + "type": "string", + "pattern": "^[A-Za-z0-9]+$" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "rows": { + "description": "The rows of the plate", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "The row name", + "type": "string", + "pattern": "^[A-Za-z0-9]+$" + } + }, + "required": [ + "name" + ] + }, + "minItems": 1, + "uniqueItems": true + }, + "wells": { + "description": "The wells of the plate", + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "description": "The path to the well subgroup", + "type": "string", + "pattern": "^[A-Za-z0-9]+/[A-Za-z0-9]+$" + }, + "rowIndex": { + "description": "The index of the well in the rows list", + "type": "integer", + "minimum": 0 + }, + "columnIndex": { + "description": "The index of the well in the columns list", + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "path", + "rowIndex", + "columnIndex" + ] + }, + "minItems": 1, + "uniqueItems": true + } + }, + "required": [ + "columns", + "rows", + "wells" + ] + }, + "version": { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema" + } + }, + "required": [ + "plate", + "version" + ] + } + }, + "required": [ + "ome" + ] +} diff --git a/0.6-dev/schemas/strict_axes.schema b/0.6-dev/schemas/strict_axes.schema new file mode 100644 index 00000000..522602d2 --- /dev/null +++ b/0.6-dev/schemas/strict_axes.schema @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/strict_axes.schema", + "title": "NGFF Strict Axes", + "description": "JSON from OME-NGFF .zattrs", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/latest/schemas/axes.schema" + }, + { + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "array", + "channel", + "time", + "space", + "displacement", + "coordinate", + "frequency" + ] + } + } + } + } + ] +} diff --git a/0.6-dev/schemas/strict_coordinate_systems.schema b/0.6-dev/schemas/strict_coordinate_systems.schema new file mode 100644 index 00000000..425ccbe5 --- /dev/null +++ b/0.6-dev/schemas/strict_coordinate_systems.schema @@ -0,0 +1,18 @@ +{ + "$id": "https://ngff.openmicroscopy.org/latest/schemas/strict_coordinate_systems.schema", + "allOf" : [ + { + "$ref": "coordinate_systems.schema" + }, + { + "items": { + "type": "object", + "properties": { + "axes": { + "$ref": "strict_axes.schema" + } + } + } + } + ] +} diff --git a/0.6-dev/schemas/strict_image.schema b/0.6-dev/schemas/strict_image.schema new file mode 100644 index 00000000..042790e7 --- /dev/null +++ b/0.6-dev/schemas/strict_image.schema @@ -0,0 +1,25 @@ +{ + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/strict_image.schema", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/image.schema" + }, + { + "properties": { + "ome": { + "properties": { + "multiscales": { + "items": { + "required": [ + "metadata", + "type", + "name" + ] + } + } + } + } + } + } + ] +} diff --git a/0.6-dev/schemas/strict_label.schema b/0.6-dev/schemas/strict_label.schema new file mode 100644 index 00000000..f1ec4db5 --- /dev/null +++ b/0.6-dev/schemas/strict_label.schema @@ -0,0 +1,21 @@ +{ + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/strict_label.schema", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/label.schema" + }, + { + "properties": { + "ome": { + "properties": { + "image-label": { + "required": [ + "colors" + ] + } + } + } + } + } + ] +} diff --git a/0.6-dev/schemas/strict_plate.schema b/0.6-dev/schemas/strict_plate.schema new file mode 100644 index 00000000..ce678ce8 --- /dev/null +++ b/0.6-dev/schemas/strict_plate.schema @@ -0,0 +1,31 @@ +{ + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/strict_plate.schema", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/plate.schema" + }, + { + "properties": { + "ome": { + "properties": { + "plate": { + "properties": { + "acquisitions": { + "items": { + "required": [ + "name", + "maximumfieldcount" + ] + } + } + }, + "required": [ + "name" + ] + } + } + } + } + } + ] +} diff --git a/0.6-dev/schemas/strict_well.schema b/0.6-dev/schemas/strict_well.schema new file mode 100644 index 00000000..43cb58cb --- /dev/null +++ b/0.6-dev/schemas/strict_well.schema @@ -0,0 +1,8 @@ +{ + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/strict_well.schema", + "allOf": [ + { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/well.schema" + } + ] +} diff --git a/0.6-dev/schemas/well.schema b/0.6-dev/schemas/well.schema new file mode 100644 index 00000000..447f51df --- /dev/null +++ b/0.6-dev/schemas/well.schema @@ -0,0 +1,56 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/0.5/schemas/well.schema", + "title": "OME-Zarr well schema", + "description": "JSON from OME-Zarr zarr.json", + "type": "object", + "properties": { + "ome": { + "description": "The versioned OME-Zarr Metadata namespace", + "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 + } + }, + "required": [ + "images" + ] + }, + "version": { + "$ref": "https://ngff.openmicroscopy.org/0.5/schemas/_version.schema" + } + }, + "required": [ + "well", + "version" + ] + } + }, + "required": [ + "ome" + ] +} diff --git a/0.6-dev/tests/image_suite.json b/0.6-dev/tests/image_suite.json new file mode 100644 index 00000000..df359ce4 --- /dev/null +++ b/0.6-dev/tests/image_suite.json @@ -0,0 +1,1040 @@ +{ + "description": "TBD", + "schema": { + "id": "schemas/image.schema" + }, + "tests": [ + { + "formerly": "valid/mismatch_axes_units.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "t", + "type": "time", + "unit": "micrometer" + }, + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [0.13, 0.13], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid/untyped_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "angle" + }, + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid/invalid_axis_units.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micron" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [0.13, 0.13], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid/missing_name.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "datasets": [ + { + "path": "path/to/0", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [1, 1] + } + ] + } + ], + "type": "gaussian", + "metadata": { + "method": "skimage.transform.pyramid_gaussian", + "version": "0.16.1", + "args": ["true", "false"], + "kwargs": { + "multichannel": true + } + }, + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ] + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid/custom_type_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "angle", + "type": "custom" + }, + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": true + }, + { + "formerly": "invalid/duplicate_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "x", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_space_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_transformation_type.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "translation" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_scale.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "translation": [1, 1], + "type": "translation" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/too_many_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "angle", + "type": "custom" + }, + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "z", + "type": "space" + }, + { + "name": "y", + "type": "space" + }, + { + "name": "x", + "type": "space" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1, 1, 1, 1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_channels_color.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ], + "omero": { + "channels": [ + { + "active": true, + "coefficient": 1.0, + "color": 255, + "family": "linear", + "label": "1234", + "window": { + "end": 1765.0, + "max": 2555.0, + "min": 5.0, + "start": 0.0 + } + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_axes_name.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "type": "space", + "unit": "micron" + }, + { + "type": "space", + "unit": "micron" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [0.13, 0.13], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_axes_count.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/one_space_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "x", + "type": "space" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_path.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": 0, + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_multiscales_transformations.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ], + "coordinateTransformations": [ + { + "scale": ["invalid"], + "type": "scale" + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_transformations.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0" + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/no_datasets.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_datasets.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_axis_type.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "invalid", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/duplicate_scale.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + }, + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/no_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/too_many_space_axes.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "X", + "type": "space" + }, + { + "name": "z", + "type": "space" + }, + { + "name": "y", + "type": "space" + }, + { + "name": "x", + "type": "space" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1, 1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/no_multiscales.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [] + } + }, + "valid": false + }, + { + "formerly": "invalid/invalid_channels_window.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ], + "omero": { + "channels": [ + { + "active": true, + "coefficient": 1.0, + "color": "ff0000", + "family": "linear", + "label": "1234", + "window": { + "end": "100", + "max": 2555.0, + "min": 5.0, + "start": 0.0 + } + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "invalid/empty_transformations.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [] + } + ] + } + ] + } + }, + "valid": false + }, + { + "formerly": "invalid/missing_path.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ] + } + ] + } + }, + "valid": false + } + ] +} diff --git a/0.6-dev/tests/label_suite.json b/0.6-dev/tests/label_suite.json new file mode 100644 index 00000000..66a2d35d --- /dev/null +++ b/0.6-dev/tests/label_suite.json @@ -0,0 +1,158 @@ +{ + "description": "Tests for the image-label JSON schema", + "schema": { + "id": "schemas/label.schema" + }, + "tests": [ + { + "formerly": "image-label/minimal", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "label-value": 1, + "rgba": [0, 0, 0, 0] + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "image-label/minimal_properties", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "label-value": 1, + "rgba": [0, 0, 0, 0] + } + ], + "properties": [ + { + "label-value": 1 + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "image-label/empty_colors", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [] + } + } + }, + "valid": false + }, + { + "formerly": "image-label/empty_properties", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "properties": [] + } + } + }, + "valid": false + }, + { + "formerly": "image-label/colors_no_label_value", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "rgba": [0, 0, 0, 0] + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "image-label/properties_no_label_value", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "properties": [ + { + "value": "foo" + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "image-label/colors_rgba_length", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "label-value": 1, + "rgba": [0, 0, 0] + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "image-label/colors_rgba_type", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "label-value": 1, + "rgba": [0, 0, 0, 500] + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "image-label/colors_duplicate", + "data": { + "ome": { + "version": "0.5", + "image-label": { + "colors": [ + { + "label-value": 1, + "rgba": [0, 0, 0, 0] + }, + { + "label-value": 1, + "rgba": [0, 0, 0, 0] + } + ] + } + } + }, + "valid": false + } + ] +} diff --git a/0.6-dev/tests/plate_suite.json b/0.6-dev/tests/plate_suite.json new file mode 100644 index 00000000..3db70de7 --- /dev/null +++ b/0.6-dev/tests/plate_suite.json @@ -0,0 +1,883 @@ +{ + "description": "Tests for the plate JSON schema", + "schema": { + "id": "schemas/plate.schema" + }, + "tests": [ + { + "formerly": "plate/minimal_no_acquisitions", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "plate/minimal_acquisitions", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0 + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "plate/missing_rows", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/empty_rows", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/duplicate_rows", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + }, + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_columns", + "data": { + "ome": { + "version": "0.5", + "plate": { + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/empty_columns", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/duplicate_columns", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + }, + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_wells", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/empty_wells", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": {} + } + } + }, + "valid": false + }, + { + "formerly": "plate/duplicate_rows", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + }, + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + }, + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_column_name", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "concentration": 10 + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_row_name", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "concentration": 10 + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_well_path", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_well_rowIndex", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_well_columnIndex", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/well_1group", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A1", + "rowIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/well_3groups", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "plate/A/1", + "rowIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/non_alphanumeric_column", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A-1" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A-1/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/non_alphanumeric_row", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "A1" + } + ], + "wells": [ + { + "path": "A/A1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "plate/missing_acquisition_id", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "maximumfieldcount": 1 + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/non_integer_acquisition_id", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": "0" + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/negative_acquisition_id", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": -1 + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/non_integer_acquisition_maximumfieldcount", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "maximumfieldcount": "0" + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/acquisition_zero_maximumfieldcount", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "maximumfieldcount": 0 + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/acquisition_noninteger_starttime", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "starttime": "2022-05-13T13:48:06+00:00" + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/acquisition_negative_starttime", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "starttime": -1 + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/acquisition_noninteger_endtime", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "endtime": "2022-05-13T13:48:06+00:00" + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/negative_endtime", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "endtime": -1 + } + ], + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/zero_field_count", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "field_count": 0, + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + } + ] +} diff --git a/0.6-dev/tests/strict_image_suite.json b/0.6-dev/tests/strict_image_suite.json new file mode 100644 index 00000000..b52fd2d3 --- /dev/null +++ b/0.6-dev/tests/strict_image_suite.json @@ -0,0 +1,341 @@ +{ + "description": "TBD", + "schema": { + "id": "schemas/strict_image.schema" + }, + "tests": [ + { + "formerly": "valid_strict/multiscales_example.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "name": "example", + "axes": [ + { + "name": "t", + "type": "time", + "unit": "millisecond" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "z", + "type": "space", + "unit": "micrometer" + }, + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [1.0, 1.0, 0.5, 0.5, 0.5] + } + ] + }, + { + "path": "1", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [1.0, 1.0, 1.0, 1.0, 1.0] + } + ] + }, + { + "path": "2", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [1.0, 1.0, 2.0, 2.0, 2.0] + } + ] + } + ], + "coordinateTransformations": [ + { + "type": "scale", + "scale": [0.1, 1.0, 1.0, 1.0, 1.0] + } + ], + "type": "gaussian", + "metadata": { + "description": "the fields in metadata depend on the downscaling implementation. Here, the parameters passed to the skimage function are given", + "method": "skimage.transform.pyramid_gaussian", + "version": "0.16.1", + "args": "[true]", + "kwargs": { + "multichannel": true + } + } + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid_strict/multiscales_transformations.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ], + "coordinateTransformations": [ + { + "scale": [10, 10], + "type": "scale" + } + ], + "name": "image_with_coordinateTransformations", + "type": "foo", + "metadata": { + "key": "value" + } + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid_strict/image_metadata.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "@id": "top", + "@type": "ngff:Image", + "multiscales": [ + { + "@id": "inner", + "name": "example", + "datasets": [ + { + "path": "path/to/0", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [1, 1] + } + ] + } + ], + "type": "gaussian", + "metadata": { + "method": "skimage.transform.pyramid_gaussian", + "version": "0.16.1", + "args": ["true", "false"], + "kwargs": { + "multichannel": true + } + }, + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ] + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid_strict/image.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1], + "type": "scale" + } + ] + } + ], + "name": "simple_image", + "type": "foo", + "metadata": { + "key": "value" + } + } + ] + } + }, + "valid": true + }, + { + "formerly": "valid_strict/image_omero.json", + "description": "TBD", + "data": { + "ome": { + "version": "0.5", + "multiscales": [ + { + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "z", + "type": "space", + "unit": "micrometer" + }, + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "scale": [1, 1, 0.5, 0.13, 0.13], + "type": "scale" + }, + { + "translation": [0, 9, 0.5, 25.74, 21.58], + "type": "translation" + } + ] + }, + { + "path": "1", + "coordinateTransformations": [ + { + "scale": [1, 1, 1, 0.26, 0.26], + "type": "scale" + } + ] + } + ], + "name": "image_with_omero_metadata", + "type": "foo", + "metadata": { + "key": "value" + } + } + ], + "omero": { + "channels": [ + { + "active": true, + "coefficient": 1.0, + "color": "00FF00", + "family": "linear", + "inverted": false, + "label": "FITC", + "window": { + "end": 813.0, + "max": 870.0, + "min": 102.0, + "start": 82.0 + } + }, + { + "active": true, + "coefficient": 1.0, + "color": "FF0000", + "family": "linear", + "inverted": false, + "label": "RD-TR-PE", + "window": { + "end": 815.0, + "max": 441.0, + "min": 129.0, + "start": 78.0 + } + } + ], + "id": 1, + "rdefs": { + "defaultT": 0, + "defaultZ": 2, + "model": "color" + }, + "version": "0.5-dev" + } + } + }, + "valid": true + } + ] +} diff --git a/0.6-dev/tests/strict_label_suite.json b/0.6-dev/tests/strict_label_suite.json new file mode 100644 index 00000000..1b3535c2 --- /dev/null +++ b/0.6-dev/tests/strict_label_suite.json @@ -0,0 +1,18 @@ +{ + "description": "Tests for the strict image-label JSON schema", + "schema": { + "id": "schemas/strict_label.schema" + }, + "tests": [ + { + "formerly": "image-label/no_colors", + "data": { + "ome": { + "version": "0.5", + "image-label": {} + } + }, + "valid": false + } + ] +} diff --git a/0.6-dev/tests/strict_plate_suite.json b/0.6-dev/tests/strict_plate_suite.json new file mode 100644 index 00000000..7cea0747 --- /dev/null +++ b/0.6-dev/tests/strict_plate_suite.json @@ -0,0 +1,171 @@ +{ + "description": "Tests for the strict plate JSON schema", + "schema": { + "id": "schemas/strict_plate.schema" + }, + "tests": [ + { + "formerly": "plate/strict_no_acquisitions", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "name": "test plate", + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "plate/missing_name", + "data": { + "ome": { + "version": "0.5", + "plate": { + "columns": [ + { + "name": "A" + } + ], + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/strict_acquisitions", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "name": "0", + "maximumfieldcount": 1 + } + ], + "columns": [ + { + "name": "A" + } + ], + "name": "test plate", + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "plate/missing_acquisition_name", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "maximumfieldcount": 1 + } + ], + "columns": [ + { + "name": "A" + } + ], + "name": "test plate", + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + }, + { + "formerly": "plate/missing_acquisition_maximumfieldcount", + "data": { + "ome": { + "version": "0.5", + "plate": { + "acquisitions": [ + { + "id": 0, + "name": "0" + } + ], + "columns": [ + { + "name": "A" + } + ], + "name": "test plate", + "rows": [ + { + "name": "1" + } + ], + "wells": [ + { + "path": "A/1", + "rowIndex": 0, + "columnIndex": 0 + } + ] + } + } + }, + "valid": false + } + ] +} diff --git a/0.6-dev/tests/strict_well_suite.json b/0.6-dev/tests/strict_well_suite.json new file mode 100644 index 00000000..f56b6c24 --- /dev/null +++ b/0.6-dev/tests/strict_well_suite.json @@ -0,0 +1,41 @@ +{ + "description": "Tests for the strict well JSON schema", + "schema": { + "id": "schemas/strict_well.schema" + }, + "tests": [ + { + "formerly": "well/strict_no_acquisitions", + "data": { + "ome": { + "version": "0.5", + "well": { + "images": [ + { + "path": "0" + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "plate/strict_acquisitions", + "data": { + "ome": { + "version": "0.5", + "well": { + "images": [ + { + "acquisition": 0, + "path": "0" + } + ] + } + } + }, + "valid": true + } + ] +} diff --git a/0.6-dev/tests/test_validation.py b/0.6-dev/tests/test_validation.py new file mode 100644 index 00000000..c2844f1a --- /dev/null +++ b/0.6-dev/tests/test_validation.py @@ -0,0 +1,130 @@ +import json +import glob +import os + +from dataclasses import dataclass +from typing import List + +import pytest + +from jsonschema import RefResolver, Draft202012Validator as Validator +from jsonschema.exceptions import ValidationError + +schema_store = {} +for schema_filename in glob.glob("schemas/*"): + with open(schema_filename) as f: + schema = json.load(f) + schema_store[schema["$id"]] = schema + +GENERIC_SCHEMA = schema_store[ + "https://ngff.openmicroscopy.org/0.5/schemas/ome_zarr.schema" +] + +print(schema_store) + + +@dataclass +class Suite: + schema: dict + data: dict + valid: bool = True + + def validate(self, validator) -> None: + if not self.valid: + with pytest.raises(ValidationError): + validator.validate(self.data) + else: + validator.validate(self.data) + + def maybe_validate(self, validator) -> None: + if self.valid: + validator.validate(self.data) + + +def pytest_generate_tests(metafunc): + """ + Generates tests for the examples/ as well as tests/ subdirectories. + + Examples: + These tests evaluate all of the files under the examples/ directory + using the configuration in the provided config file in order detect + what should be run. It is assumed that all files are valid and complete + so that they can be wholly included into the specification. The + .config.json file in each directory defines which schema will be used. + + Validation: + These test consumes https://github.com/json-schema-org/JSON-Schema-Test-Suite#structure-of-a-test + styled JSON tests. Metadata in each test defines which schema is used + and whether or not the block is considered valid. + """ + if "suite" in metafunc.fixturenames: + suites: List[Schema] = [] + ids: List[str] = [] + schema_store = {} + for filename in glob.glob("schemas/*.schema"): + with open(filename) as o: + schema = json.load(o) + schema_store[schema["$id"]] = schema + + # Validation + for filename in glob.glob("tests/*.json"): + with open(filename) as o: + suite = json.load(o) + schema = suite["schema"] + with open(schema["id"]) as f: + schema = json.load(f) + for test in suite["tests"]: + ids.append("validate_" + str(test["formerly"]).split("/")[-1][0:-5]) + suites.append(Suite(schema, test["data"], test["valid"])) + + # Examples + for config_filename in glob.glob("examples/*/.config.json"): + with open(config_filename) as o: + data = json.load(o) + schema = data["schema"] + with open(schema) as f: + schema = json.load(f) + example_folder = os.path.dirname(config_filename) + for filename in glob.glob(f"{example_folder}/*.json"): + with open(filename) as f: + # Strip comments + data = "".join( + line for line in f if not line.lstrip().startswith("//") + ) + data = json.loads(data) + data = data["attributes"] # Only validate the attributes object + ids.append("example_" + str(filename).split("/")[-1][0:-5]) + suites.append(Suite(schema, data, True)) # Assume true + + metafunc.parametrize("suite", suites, ids=ids, indirect=True) + + +@pytest.fixture +def suite(request): + return request.param + + +def test_run(suite): + resolver = RefResolver.from_schema(suite.schema, store=schema_store) + validator = Validator(suite.schema, resolver=resolver) + suite.validate(validator) + + +def test_generic_run(suite): + resolver = RefResolver.from_schema(GENERIC_SCHEMA, store=schema_store) + validator = Validator(GENERIC_SCHEMA, resolver=resolver) + suite.maybe_validate(validator) + + +def test_example_configs(): + """ + Test that all example folders have a config file + """ + missing = [] + for subdir in os.walk("examples"): + has_examples = glob.glob(f"{subdir[0]}/*.json") + has_config = glob.glob(f"{subdir[0]}/.config.json") + if has_examples and not has_config: + missing.append(subdir[0]) + if missing: + raise Exception(f"Directories missing configs: {missing}") diff --git a/0.6-dev/tests/well_suite.json b/0.6-dev/tests/well_suite.json new file mode 100644 index 00000000..7fbf8ca2 --- /dev/null +++ b/0.6-dev/tests/well_suite.json @@ -0,0 +1,80 @@ +{ + "description": "Tests for the well JSON schema", + "schema": { + "id": "schemas/well.schema" + }, + "tests": [ + { + "formerly": "well/minimal_no_acquisition", + "data": { + "ome": { + "version": "0.5", + "well": { + "images": [ + { + "path": "0" + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "well/minimal_acquisitions", + "data": { + "ome": { + "version": "0.5", + "well": { + "images": [ + { + "acquisition": 1, + "path": "0" + } + ] + } + } + }, + "valid": true + }, + { + "formerly": "well/empty_images", + "data": { + "well": { + "images": [] + } + }, + "valid": false + }, + { + "formerly": "well/duplicate_images", + "data": { + "well": { + "images": [ + { + "path": "0" + }, + { + "path": "0" + } + ] + } + }, + "valid": false + }, + { + "formerly": "well/non_integer_acquisition_id", + "data": { + "well": { + "images": [ + { + "acquisition": "0", + "path": "0" + } + ] + } + }, + "valid": false + } + ] +} diff --git a/0.6-dev/tox.ini b/0.6-dev/tox.ini new file mode 100644 index 00000000..632ca64e --- /dev/null +++ b/0.6-dev/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = v04 +skipsdist = True + +[testenv] +deps = + pytest + jsonschema +commands = + pytest tests --color=yes --basetemp={envtmpdir} {posargs:-v} \ No newline at end of file diff --git a/0.6-dev/transform-details.bs b/0.6-dev/transform-details.bs new file mode 100644 index 00000000..b2b744c8 --- /dev/null +++ b/0.6-dev/transform-details.bs @@ -0,0 +1,106 @@ + + +Coordinates and Axes {#coords-axes} +===================== + +OME-NGFF datasets are arrays that hold values. The arrays may be indexed by discrete (integer) +coordinates in order to obtain a corresponding value. If values are desired at continuous (real-valued) +coordinates, then interpolation is required. + +Interpolation {#interp} +--------------------- + +Interpolation is the process of producing values at continuous coordinates from data sampled at discrete +coordinates. "Nearest-neighbor" and "N-Linear" are the two most commonly used interpolation methods. + + +Pixel coordinates {#pix-coords} +--------------------- + +**The pixel center is the origin of the continuous coordinate system.** + +### Top-left convention + +A common alternative convention is for the origin in the continuous space is at the "top-left" of the pixel. +This is not recommended, but can be acheived by explicitly adding a half-pixel translation, for example: + +```json +{ + "name": "center_to_top-left", + "type": "translation", + "translation" : [0.5, 0.5], + "output_space" : "top-left-space" +} +``` + +Coordinate Transformations {#coord-tforms} +===================== + +This document describes background and motivation that is outside the NGFF specification. + + +Direction {#direction} +--------------------- + +Specified coordinate transforms are in the "forward" direction. They represent functions +from *points* in the input space to *points* in the output space. For example, the transformation `"ij2xy"` + + +```json +{ + "name": "ij2xy", + "type": "scale", + "scale": [2, 0.5] + "input_axes" : ["i", "j"] + "output_axes" : ["x", "y"] +} +``` + +representes the function + +``` +x = 2 * i +y = 0.5 * j +``` + + +Recommendations {#recommendations} +===================== + + +"Native" physical space +--------------------- + +Datasets SHOULD define a transformation from array space to their "native physical space." +This transformation SHOULD describe physical pixel spacing and origin only, and therefore SHOULD consist of +`scale` and/or `translation` types only. + +Subsequent reorientation / registration transformations SHOULD use this native space as their `input_space`, +i.e., transformations should be defined in physical coordinates. + +