diff --git a/latest/examples/coordSystems/.config.json b/latest/examples/coordSystems/.config.json new file mode 100644 index 00000000..56e6f885 --- /dev/null +++ b/latest/examples/coordSystems/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/coordinate_transforms.schema" +} diff --git a/latest/examples/coordSystems/arrayCoordSys.json b/latest/examples/coordSystems/arrayCoordSys.json new file mode 100644 index 00000000..9c7e1229 --- /dev/null +++ b/latest/examples/coordSystems/arrayCoordSys.json @@ -0,0 +1,10 @@ +{ + "arrayCoordinateSystem" : { + "name" : "myDataArray", + "axes" : [ + {"name": "i", "type": "array"}, + {"name": "j", "type": "array"}, + {"name": "k", "type": "array"} + ] + } +} diff --git a/latest/examples/multiscales_strict/multiscales_example.json b/latest/examples/multiscales_strict/multiscales_example.json index 73e5286c..beed1cd7 100644 --- a/latest/examples/multiscales_strict/multiscales_example.json +++ b/latest/examples/multiscales_strict/multiscales_example.json @@ -3,44 +3,50 @@ { "version": "0.5-dev", "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"} + "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", "coordinateTransformations": [{ - // the voxel size for the first scale level (0.5 micrometer) + // the voxel size for the first scale level (0.5 micrometer), time unit (0.1 milliseconds) "type": "scale", - "scale": [1.0, 1.0, 0.5, 0.5, 0.5] + "scale": [0.1, 1.0, 0.5, 0.5, 0.5], + "input" : "/0", + "output" : "example" }] }, { "path": "1", "coordinateTransformations": [{ - // the voxel size for the second scale level (downscaled by a factor of 2 -> 1 micrometer) + // the voxel size for the second scale level (downscaled by a factor of 2 -> 1 micrometer), time unit (0.1 milliseconds) "type": "scale", - "scale": [1.0, 1.0, 1.0, 1.0, 1.0] + "scale": [0.1, 1.0, 1.0, 1.0, 1.0], + "input" : "/1`", + "output" : "example" }] }, { "path": "2", "coordinateTransformations": [{ - // the voxel size for the third scale level (downscaled by a factor of 4 -> 2 micrometer) + // the voxel size for the third scale level (downscaled by a factor of 4 -> 2 micrometer), time unit (0.1 milliseconds) "type": "scale", - "scale": [1.0, 1.0, 2.0, 2.0, 2.0] + "scale": [0.1, 1.0, 2.0, 2.0, 2.0], + "input" : "/2", + "output" : "example" }] } ], - "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] - }], "type": "gaussian", "metadata": { "description": "the fields in metadata depend on the downscaling implementation. Here, the parameters passed to the skimage function are given", @@ -51,4 +57,4 @@ } } ] -} \ No newline at end of file +} diff --git a/latest/examples/multiscales_strict/multiscales_example_relative.json b/latest/examples/multiscales_strict/multiscales_example_relative.json new file mode 100644 index 00000000..04e52b2c --- /dev/null +++ b/latest/examples/multiscales_strict/multiscales_example_relative.json @@ -0,0 +1,61 @@ +{ + "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"} + ] + } + ], + "datasets": [ + { + "path": "0" + // the transformation of other arrays are defined relative to this, the highest resolution, array + }, + { + "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" : "/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" : "/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" : "/0", + "output" : "xampleCoordinateSystem" + }], + "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/latest/examples/subspace/subspaceMultidim.json b/latest/examples/subspace/subspaceMultidim.json new file mode 100644 index 00000000..cfbe5dae --- /dev/null +++ b/latest/examples/subspace/subspaceMultidim.json @@ -0,0 +1,64 @@ +{ + "coordinateSystems" : [ + { "name " : "in", "axes" : [ {"name" : "0", "name" : "1", "name": "2", "name": "3", "name": "4" }] }, + { "name " : "out", "axes" : [ {"name" : "x", "name" : "y", "name" : "z" }] } + ], + "coordinateTransformations" : [ + { + "type" : "sequence", + "name" : "5D-to-3D", + "input" : "in", + "output" : "out", + "transformations" : [ + { + "type": "mapIndex", + "inputAxes" : ["0", "1"], + "outputAxes" : ["x", "y"] + }, + { + "type": "scale", + "scale" : [2], + "inputAxes" : ["3"], + "outputAxes" : ["z"] + } + ] + }, + { + "type" : "sequence", + "name" : "5D-to-3D-not-contiguous", + "input" : "in", + "output" : "out", + "transformations" : [ + { + "type": "mapIndex", + "inputAxes" : ["0", "2"], + "outputAxes" : ["x", "z"] + }, + { + "type": "scale", + "scale" : [2], + "inputAxes" : ["1"], + "outputAxes" : ["y"] + } + ] + }, + { + "type" : "sequence", + "name" : "5D-to-3D-not-contiguous", + "input" : "in", + "output" : "out", + "transformations" : [ + { + "type": "mapAxes", + "map" : {"0":"x", "2":"z"} + }, + { + "type": "scale", + "scale" : [2], + "inputAxes" : ["1"], + "outputAxes" : ["y"] + } + ] + } + ] +} diff --git a/latest/examples/subspace/subspacePermute.json b/latest/examples/subspace/subspacePermute.json new file mode 100644 index 00000000..88c6b4ea --- /dev/null +++ b/latest/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" : "sequence", + "input" : "in", + "output" : "out", + "transformations" : [ + { + "type": "identity", + "inputAxes" : ["j"], + "outputAxes" : ["x"] + }, + { + "type": "scale", + "scale" : [2], + "inputAxes" : ["i"], + "outputAxes" : ["y"] + } + ] + } + ] +} diff --git a/latest/examples/transformations/.config.json b/latest/examples/transformations/.config.json new file mode 100644 index 00000000..56e6f885 --- /dev/null +++ b/latest/examples/transformations/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/coordinate_transforms.schema" +} diff --git a/latest/examples/transformations/affine2d2d.json b/latest/examples/transformations/affine2d2d.json new file mode 100644 index 00000000..f85e3b36 --- /dev/null +++ b/latest/examples/transformations/affine2d2d.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems" : [ + { "name": "ij", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "xy", "axes": [{"name": "x"}, {"name": "y"}] } + ], + "coordinateTransformations" : [ + { + "type": "affine", + "affine": [1, 2, 3, 4, 5, 6], + "input": "ij", + "output": "xy" + } + ] +} diff --git a/latest/examples/transformations/affine2d3d.json b/latest/examples/transformations/affine2d3d.json new file mode 100644 index 00000000..be45fd1b --- /dev/null +++ b/latest/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/latest/examples/transformations/byDimension1.json b/latest/examples/transformations/byDimension1.json new file mode 100644 index 00000000..a42ed37a --- /dev/null +++ b/latest/examples/transformations/byDimension1.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } + ], + "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/latest/examples/transformations/byDimension2.json b/latest/examples/transformations/byDimension2.json new file mode 100644 index 00000000..0568c9bd --- /dev/null +++ b/latest/examples/transformations/byDimension2.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"}, {"name": "k"}, {"name": "l"}] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"}, {"name": "z"} ] } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations" : [ + { "type": "translation", "translation": [1, 3], "input": ["i", "k" ], "output": ["y", "x"]}, + { "type": "scale", "scale": [2.0], "input": ["j"], "output": ["z"]} + ] + } + ] +} diff --git a/latest/examples/transformations/byDimensionInvalid1.json b/latest/examples/transformations/byDimensionInvalid1.json new file mode 100644 index 00000000..8d3c9696 --- /dev/null +++ b/latest/examples/transformations/byDimensionInvalid1.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } + ], + "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/latest/examples/transformations/byDimensionInvalid2.json b/latest/examples/transformations/byDimensionInvalid2.json new file mode 100644 index 00000000..fdd3ac4b --- /dev/null +++ b/latest/examples/transformations/byDimensionInvalid2.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } + ], + "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/latest/examples/transformations/coordinates1d.json b/latest/examples/transformations/coordinates1d.json new file mode 100644 index 00000000..314bc6fb --- /dev/null +++ b/latest/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/latest/examples/transformations/displacement1d.json b/latest/examples/transformations/displacement1d.json new file mode 100644 index 00000000..5db76446 --- /dev/null +++ b/latest/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/latest/examples/transformations/identity.json b/latest/examples/transformations/identity.json new file mode 100644 index 00000000..3ea1529a --- /dev/null +++ b/latest/examples/transformations/identity.json @@ -0,0 +1,9 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} + ], + "coordinateTransformations": [ + { "type": "identity", "input": "in", "output": "out" } + ] +} diff --git a/latest/examples/transformations/inverseOf.json b/latest/examples/transformations/inverseOf.json new file mode 100644 index 00000000..3e2966c5 --- /dev/null +++ b/latest/examples/transformations/inverseOf.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems" : [ + { "name" : "moving", "axes" : [{"name" : "x-moving"}, {"name":"y-moving"}] }, + { "name" : "fixed", "axes" : [{"name" : "x-fixed"}, {"name":"y-fixed"}] } + ], + "coordinateTransformations" : [ + { + "type": "inverseOf", + "transformation" : { "type": "displacements", "path": "/path/to/displacements" }, + "input" : "moving", + "output" : "fixed" + } + ] +} \ No newline at end of file diff --git a/latest/examples/transformations/mapAxis1.json b/latest/examples/transformations/mapAxis1.json new file mode 100644 index 00000000..2b6b0b79 --- /dev/null +++ b/latest/examples/transformations/mapAxis1.json @@ -0,0 +1,23 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out1", "axes": [ {"name": "x"}, {"name": "y"} ]}, + { "name": "out2", "axes": [ {"name": "x"}, {"name": "y"} ]} + ], + "coordinateTransformations": [ + { + "name": "equivalent to identity", + "type": "mapAxis", + "mapAxis": { "x":"i", "y":"j" }, + "input": "in", + "output": "out1" + }, + { + "name": "permutation", + "type": "mapAxis", + "mapAxis": { "x":"j", "y":"i" }, + "input": "in", + "output": "out2" + } + ] +} diff --git a/latest/examples/transformations/mapAxis2.json b/latest/examples/transformations/mapAxis2.json new file mode 100644 index 00000000..c8336aef --- /dev/null +++ b/latest/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": "x"}, {"name": "y"}, {"name": "z"} ]} + ], + "coordinateTransformations": [ + { + "name": "projection down", + "type": "mapAxis", + "mapAxis": { "x": "b" }, + "input": "in", + "output": "out_down" + }, + { + "name": "projection up", + "type": "mapAxis", + "mapAxis": { "x": "a", "y": "b", "z": "b" }, + "input": "in", + "output": "out_up" + } + ] +} diff --git a/latest/examples/transformations/mapIndex1.json b/latest/examples/transformations/mapIndex1.json new file mode 100644 index 00000000..db785459 --- /dev/null +++ b/latest/examples/transformations/mapIndex1.json @@ -0,0 +1,23 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out1", "axes": [ {"name": "x"}, {"name": "y"} ]}, + { "name": "out2", "axes": [ {"name": "x"}, {"name": "y"} ]} + ], + "coordinateTransformations": [ + { + "name": "equivalent to identity", + "type": "mapIndex", + "mapIndex": [0, 1], + "input": "in", + "output": "out1" + }, + { + "name": "permutation", + "type": "mapIndex", + "mapIndex": [1, 0], + "input": "in", + "output": "out2" + } + ] +} diff --git a/latest/examples/transformations/mapIndex2.json b/latest/examples/transformations/mapIndex2.json new file mode 100644 index 00000000..411828de --- /dev/null +++ b/latest/examples/transformations/mapIndex2.json @@ -0,0 +1,23 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "a"}, {"name": "b"}]}, + { "name": "out_down", "axes": [ {"name": "x"}]}, + { "name": "out_up", "axes": [ {"name": "x"}, {"name": "y"}, {"name": "z"} ]} + ], + "coordinateTransformations": [ + { + "name": "projection down", + "type": "mapIndex", + "mapIndex": [1], + "input": "in", + "output": "out_down" + }, + { + "name": "projection up", + "type": "mapIndex", + "mapIndex": [0, 1, 1], + "input": "in", + "output": "out_up" + } + ] +} diff --git a/latest/examples/transformations/rotation.json b/latest/examples/transformations/rotation.json new file mode 100644 index 00000000..5e73ff33 --- /dev/null +++ b/latest/examples/transformations/rotation.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems" : [ + { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] }, + { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } + ], + "coordinateTransformations" : [ + { + "type": "rotation", + "rotation": [0, -1, 1, 0], + "input" : "ij", + "output" : "xy" + } + ] +} \ No newline at end of file diff --git a/latest/examples/transformations/scale.json b/latest/examples/transformations/scale.json new file mode 100644 index 00000000..b5c83309 --- /dev/null +++ b/latest/examples/transformations/scale.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "out", "axes": [{"name": "x"}, {"name": "y"}] } + ], + "coordinateTransformations": [ + { + "type": "scale", + "scale": [3.12, 2], + "input": "in", + "output": "out" + } + ] +} diff --git a/latest/examples/transformations/sequence.json b/latest/examples/transformations/sequence.json new file mode 100644 index 00000000..1d88b21d --- /dev/null +++ b/latest/examples/transformations/sequence.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": "translation", "translation": [0.1, 0.9] }, + { "type": "scale", "scale": [2, 3] } + ] + } + ] +} diff --git a/latest/examples/transformations/sequenceSubspace1.json b/latest/examples/transformations/sequenceSubspace1.json new file mode 100644 index 00000000..e8a5bd35 --- /dev/null +++ b/latest/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/latest/examples/transformations/translation.json b/latest/examples/transformations/translation.json new file mode 100644 index 00000000..cbc32ec9 --- /dev/null +++ b/latest/examples/transformations/translation.json @@ -0,0 +1,14 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "out", "axes": [{"name": "x"}, {"name": "y"}] } + ], + "coordinateTransformations" : [ + { + "type": "translation", + "input": "in", + "output": "out", + "translation": [9, -1.42] + } + ] +} diff --git a/latest/examples/transformations/xarrayLike.json b/latest/examples/transformations/xarrayLike.json new file mode 100644 index 00000000..fdacf6e0 --- /dev/null +++ b/latest/examples/transformations/xarrayLike.json @@ -0,0 +1,17 @@ +{ + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} + ], + "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/latest/index.bs b/latest/index.bs index bb935c48..161f0130 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -16,6 +16,7 @@ Markup Shorthands: markdown yes Editor: Josh Moore, University of Dundee (UoD) https://www.dundee.ac.uk, https://orcid.org/0000-0003-4028-811X Editor: Sébastien Besson, University of Dundee (UoD) https://www.dundee.ac.uk, https://orcid.org/0000-0001-8783-1429 Editor: Constantin Pape, European Molecular Biology Laboratory (EMBL) https://www.embl.org/sites/heidelberg/, https://orcid.org/0000-0001-6562-7187 +Editor: John Bogovic, Hughes Medical Institute Janelia (HHMI) https://www.janelia.org/, https://orcid.org/0000-0002-4829-9457 Text Macro: NGFFVERSION 0.5-dev Abstract: This document contains next-generation file format (NGFF) Abstract: specifications for storing bioimaging data in the cloud. @@ -189,7 +190,7 @@ 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: +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 @@ -199,9 +200,6 @@ dataset. Three groups MUST be defined above the images: 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. -
. # Root folder, potentially in S3, @@ -238,18 +236,6 @@ Metadata {#metadata} The various `.zattrs` files throughout the above array hierarchy may contain metadata keys as specified below for discovering certain types of data, especially images. -"axes" metadata {#axes-md} --------------------------- - -"axes" describes the dimensions of a physical coordinate space. 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 "space", "time" or "channel", but MAY take other 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' - -If part of [[#multiscale-md]], the length of "axes" MUST be equal to the number of dimensions of the arrays that contain the image data. - "bioformats2raw.layout" (transitional) {#bf2raw} ------------------------------------------------ @@ -331,24 +317,927 @@ Conforming readers: - MAY ignore other groups or arrays under the root of the hierarchy. +"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", that gives the space name. The values MUST be non-empty and unique. +- MUST contain the field "axes", whose value is a valid set of "axes" (see below). + ++ +```json +{ + "name" : "volume_micrometers", + "axes" : [ + {"name": "x", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "z", "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 first dimension. The "dimensionality" of a coordinate system +is indicated by the length of its "axes" array. The "volume_micrometers" example above is three dimensional (3D). + +### "axes" metadata {#axes-md} + +"axes" describes the dimensions of a coordinate systems. 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 "array", "space", "time", "channel", "coordinate", or "displacement" but MAY take other values for custom axis types that are not part of this specification yet. +- MAY contain the field "discrete". The value MUST be a boolean, and is `true` if the axis represents a discrete dimension. +- 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. + +If 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", "unit": "parsec"}, + {"name": "v", "type": "displacement", "unit": "nanometer"}, + {"name": "freq", "type": "frequency", "unit": "megahertz"} +] +``` ++ + +### Array coordinate systems + +Every array has a default coordinate system whose parameters need not be explicitly defined. Its name is the path to the array +in the container, its axes have `"type":"array"`, are unitless, and have default "name"s. The ith axis has `"name":"dim_i"` +(these are the same default names used by [xarray](https://docs.xarray.dev/en/stable/user-guide/terminology.html)). ++For example, a 3D array at path `/my/data/array` defines the coordinate system: + +```json +{ + "name" : "/my/data/array", + "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. ++ + +The dimensionality of each array coordinate system equals the dimensionality of its corresponding zarr array. The axis with +name `"dim_i"` is the ith element of the `"axes"` list. The axes and their order align with the `shape` +attribute in the zarr array attributes (in `.zarray`), and whose data depends on the byte order used to store +chunks. As described in the [zarr array metadata](https://zarr.readthedocs.io/en/stable/spec/v2.html#arrays), +the last dimension of an array in "C" order (row-major) are stored contiguously. For an array in "F" +(column-major) order, the elements of the first dimension are stored contiguously. + ++For example, if `/my/data/array/.zarray` contains: + +```json +{ + "chunks": [ 4, 3, 5 ], + "compressor": null, + "dtype": "|u1", + "fill_value": 0, + "filters": null, + "order": "C", + "shape": [ 4, 3, 5 ], + "zarr_format": 2 +} +``` + +Then `dim_0` has length 4, `dim_1` has length 3, and `dim_2` has length 5. ++ +The name and axes names MAY be customized by including a `arrayCoordinateSystem` field in +the `.zattr` metadata of the array whose value is a coordinate system object. The length of +`axes` MUST be equal to the dimensionality. The value of `"type"` for each object in the +axes array MUST equal `"array"`. + ++ ++ + +### 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]]. +======= + + + "coordinateTransformations" metadata {#trafo-md} ------------------------------------------------ -"coordinateTransformations" describe a series of transformations that map between two coordinate spaces (defined by "axes"). -For example, to map a discrete data space of an array to the corresponding physical space. -It is a list of dictionaries. Each entry describes a single transformation and MUST contain the field "type". -The value of "type" MUST be one of the elements of the `type` column in the table below. -Additional fields for the entry depend on "type" and are defined by the column `fields`. +"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). +- SHOULD 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).+path: examples/coordSystems/arrayCoordSys.json +highlight: json ++ +
`identity` | identity transformation, is the default transformation and is typically not explicitly defined - | ||||
---|---|---|---|---|---|
`translation` | one of: `"translation":List[float]`, `"path":str` | translation vector, stored either as a list of floats (`"translation"`) or as binary data at a location in this container (`path`). The length of vector defines number of dimensions. | - | |||
`scale` | one of: `"scale":List[float]`, `"path":str` | scale vector, stored either as a list of floats (`scale`) or as binary data at a location in this container (`path`). The length of vector defines number of dimensions. | + | |||
`identity` + | + | The identity transformation is the default transformation and is typically not explicitly defined. + | |||
`mapIndex` + | `"mapIndex":List[number]` + | A `mapIndex` transformation specifies an axis permutation by reordering the input axes. + | |||
`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[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[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` + | `"url":str` `"path":str` `"interpolation":str` + | Displacement field transformation in this container (path) or another container located at (url). + | |||
`coordinates` + | `"url":str` `"path":str` `"interpolation":str` + | Coordinate field transformation in this container (path) or another container located at (url). + | |||
`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.
type | fields | description
| |
+store.zarr # Root folder of the zarr store +│ +├── .zattrs # 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 "transformations" zarr group. +│ └── displacements # for example, a zarray containing a displacement field +│ ├── .zattrs +│ └── .zarray +│ +├── volume +│ ├── .zattrs # group level attributes (multiscales) +│ └── 0 # a group containing the 0th scale +│ └── image # a zarr array +│ ├── .zattrs # physical coordinate system and transformations here +│ └── .zarray # the array attributes +└── crop + ├── .zattrs # group level attributes (multiscales) + └── 0 # a group containing the 0th scale + └── image # a zarr array + ├── .zattrs # physical coordinate system and transformations here + └── .zarray # the array attributes ++ +### Additional details + +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` transforemation 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). + +
+path: examples/transformations/identity.json +highlight: json ++ +defines the function: + +``` +x = i +y = j +``` + +
+path: examples/transformations/mapIndex1.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/mapIndex2.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 +``` + +
+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 +``` +
+path: examples/transformations/translation.json +highlight: json ++ +defines the function: + +``` +x = i + 9 +y = j - 1.42 +``` +
+path: examples/transformations/scale.json +highlight: json ++ +defines the function: + +``` +x = 3.12 * i +y = 2 * j +``` +
+ path: examples/transformations/affine2d2d.json + highlight: json ++ + defines the function: + + ``` + x = 1*i + 2*j + 3 + y = 4*i + 5*j + 6 + ``` +
+ 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 + ``` +
+ path: examples/transformations/rotation.json + highlight: json ++ + defines the function: + + ``` + x = 0*i - 1*j + y = 1*i + 0*j + ``` +
+ path: examples/transformations/inverseOf.json + highlight: json ++ +
+path: examples/transformations/sequence.json +highlight: json ++ +describes the function + +``` +x = (i + 0.1) * 2 +y = (j + 0.9) * 3 +``` + +and is invertible. +
linear
(default)nearest
cubic
+path: examples/transformations/byDimension1.json +highlight: json ++ +
+path: examples/transformations/byDimension2.json +highlight: json ++ +
+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. + +
+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. + +
+Title: Coordinates and Transformations +Shortname: ome-ngff-transformations +Level: 1 +Status: LS-COMMIT +Status: w3c/ED +Group: ome +URL: https://ngff.openmicroscopy.org/latest/ +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 no +Local Boilerplate: copyright no +Boilerplate: style-darkmode off +Markup Shorthands: markdown yes +Editor: Josh Moore, Open Microscopy Environment (OME) https://www.openmicroscopy.org +Editor: Sébastien Besson, Open Microscopy Environment (OME) https://www.openmicroscopy.org +Editor: Constantin Pape, European Molecular Biology Laboratory (EMBL) https://www.embl.org/sites/heidelberg/ +Editor: John Bogovic, Hughes Medical Institute Janelia (HHMI) https://www.janelia.org/ +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 +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. ++ +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. + +