From f0da62ad36bc138c4392df0ef19eb14fbdbcf0cb Mon Sep 17 00:00:00 2001 From: bogovicj Date: Tue, 1 Feb 2022 13:50:56 -0500 Subject: [PATCH 001/113] start of named space and transform metadata --- latest/index.bs | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 55789a21..1e751202 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -212,9 +212,10 @@ 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: +"axes" describes the dimensions of a 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. +- 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' @@ -222,11 +223,47 @@ keys as specified below for discovering certain types of data, especially images 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. +"space" metadata {#space-md} +-------------------------- + +A "space" is a collection of dimensions / "axes" with a name. Each space dictionary: +- 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". +- SHOULD be such that the "type" of all "axes" in the list are identical. + +``` +{ + "name" : "volume_micrometers", + "axes" : [ + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} + ] +} +``` + +### "Array space" + +"Array space" is the unique space whose name is the empty string. The array data of every dataset is in "Array +space." Therefore, the dimensionality of "array space" varies according to its corresponding dataset. +In [[#trafo-md]], there SHOULD exist at least one transformation whose "input_space" is the empty string, i.e. +"array space." + + "transformations" metadata {#trafo-md} ------------------------------------- "transformations" describes a series of transformations, e.g. to map 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". +It is a list of dictionaries. Each transformation dictionary: + +- MUST contain the field "type". +- MUST contain any other fields required by the given "type" (see table below). +- MUST contain the field "name". +- SHOULD require the field "input_space". +- MUST require the field "output_space". +- the value of "output_space" SHOULD not be the empty string. + + 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`. @@ -236,9 +273,6 @@ Additional fields for the entry depend on "type" and are defined by the column ` | `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. | -In addition, the field "axisIndices" MAY be given to specify the subset of axes that the transformation is applied to, leaving other axes unchanged. If not given, the transformation is applied to all axes. The length of "axisIndices" MUST be equal to the dimensionality of the transformation. If "axisIndices" are not given, the dimensionality of the transformation MUST be equal to the number of dimensions of the space that the transformation is applied to. -If given, "axisIndices" MUST be given in increasing order. It uses zero-based indexing. - The transformations in the list are applied sequentally and in order. From 4331deba9e7fd48ab4c974b78ead2b639a3c5293 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Wed, 9 Feb 2022 09:06:58 -0500 Subject: [PATCH 002/113] fwd and inv coordinate transforms --- latest/index.bs | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 1e751202..e9409002 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -16,6 +16,7 @@ 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. @@ -256,11 +257,11 @@ In [[#trafo-md]], there SHOULD exist at least one transformation whose "input_sp "transformations" describes a series of transformations, e.g. to map discrete data space of an array to the corresponding physical space. It is a list of dictionaries. Each transformation dictionary: +- MUST contain the field "name". - MUST contain the field "type". - MUST contain any other fields required by the given "type" (see table below). -- MUST contain the field "name". -- SHOULD require the field "input_space". -- MUST require the field "output_space". +- SHOULD contain the field "input_space". +- MUST contain the field "output_space". - the value of "output_space" SHOULD not be the empty string. @@ -275,6 +276,44 @@ Additional fields for the entry depend on "type" and are defined by the column ` The transformations in the list are applied sequentally and in order. +### Forward and inverse coordate transformations + +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"] +} +``` + +represents the function: + +``` +x = 2 * i +y = 0.5 * j +``` + +When rendering images and interpolating, implementations may need the "inverse" transformation - from output to +input space. 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 should be specified using the `inverse` +transformation type, for example: + +```json +{ + "name": "nonlinear-inverse", + "type": "displacement_field", + "path": "/path/to/my/transform", + "input_axes" : ["i", "j"], + "output_axes" : ["x", "y"], + "inverse" : true +} +``` + "multiscales" metadata {#multiscale-md} --------------------------------------- From 180568bbcd8850f61c4b1ce406b38d99ef3ceb9d Mon Sep 17 00:00:00 2001 From: bogovicj Date: Wed, 9 Feb 2022 14:20:42 -0500 Subject: [PATCH 003/113] more transform details * define pixel center coordinate system * add input/output_axes * add transform inverse flag * add affine transform type --- latest/index.bs | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 87be4412..8dfa46fa 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -250,9 +250,20 @@ A "space" is a collection of dimensions / "axes" with a name. Each space dictio } ``` +### Pixel coordinate convention + +**The pixel center is the origin of the continuous coordinate system.** + +It is vital to consistently define relationship between the discrete/arrray and continuous/interpolated +coordinate systems. A pixel is the continuous region (rectangle) that corresponds to a single sample +in the discrete array, i.e., the area corresponding to nearest-neighbor interpolation of that sample. +The center of the pixel corresponding to the discrete origin `(0,0)` is the origin of the continuous space +`(0.0, 0.0)` when the transformation is the identity. See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. + + ### "Array space" -"Array space" is the unique space whose name is the empty string. The array data of every dataset is in "Array +"Array space" is the unique space whose name is the empty string (`""`) or `null`. The array data of every dataset is in "Array space." Therefore, the dimensionality of "array space" varies according to its corresponding dataset. In [[#trafo-md]], there SHOULD exist at least one transformation whose "input_space" is the empty string, i.e. "array space." @@ -265,17 +276,19 @@ In [[#trafo-md]], there SHOULD exist at least one transformation whose "input_sp 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 "name". +- MUST contain the field "name". The values MUST be unique across all "name" fields for coordinate transformations. - MUST contain the field "type". - MUST contain any other fields required by the given "type" (see table below). -- SHOULD contain the field "input_space". -- MUST contain the field "output_space". +- SHOULD contain exactly one of the fields "input_space" or "input_axes". +- MUST contain exactly one of the fields "output_space" or "output_axes". +- OPTIONAL contain the field "inverse". - the value of "output_space" SHOULD not be the empty string.
`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. | +
`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. +
`affine` one of:
`"affine":List[float]`,
`"path":str`
affine transformation matrix defined as list consisting of n sets of n + 1 scalar numbers, stored either as a number[] (affine) or as binary data at a location in this container (path). If both are present, path is preferred. n is number of dimensions
typefieldsdescription
@@ -997,6 +1010,20 @@ Version History {#history} "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" } } From 4f4836400103d4e1e59775be76532ac638a340af Mon Sep 17 00:00:00 2001 From: bogovicj Date: Wed, 9 Feb 2022 14:21:47 -0500 Subject: [PATCH 004/113] start page on details and recommendations --- latest/transform-details.bs | 106 ++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 latest/transform-details.bs diff --git a/latest/transform-details.bs b/latest/transform-details.bs new file mode 100644 index 00000000..b2b744c8 --- /dev/null +++ b/latest/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. + + From 98a40debe19c09e750168682e862e4cfff6158fd Mon Sep 17 00:00:00 2001 From: bogovicj Date: Thu, 5 May 2022 17:27:12 -0400 Subject: [PATCH 005/113] transformation progress * flesh out array space * define array indexing * add more transformation types * start transformation details section and examples * update example --- latest/index.bs | 149 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 116 insertions(+), 33 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 8dfa46fa..c953ec2d 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -234,39 +234,61 @@ If part of [[#multiscale-md]], the length of "axes" MUST be equal to the number "space" metadata {#space-md} -------------------------- -A "space" is a collection of dimensions / "axes" with a name. Each space dictionary: +A "space" is a collection of "axes" / dimensions with a name and defines a coordinate system. Each space: - 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". -- SHOULD be such that the "type" of all "axes" in the list are identical. ``` { "name" : "volume_micrometers", "axes" : [ - {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"}, {"name": "y", "type": "space", "unit": "micrometer"}, - {"name": "x", "type": "space", "unit": "micrometer"} + {"name": "z", "type": "space", "unit": "micrometer"} ] } ``` -### Pixel coordinate convention +Order of the `"axes"` list matters and defines the index of each dimension and coordinates for points in that +space. For the above example, the `"x"` dimension is the first dimension. -**The pixel center is the origin of the continuous coordinate system.** -It is vital to consistently define relationship between the discrete/arrray and continuous/interpolated -coordinate systems. A pixel is the continuous region (rectangle) that corresponds to a single sample -in the discrete array, i.e., the area corresponding to nearest-neighbor interpolation of that sample. -The center of the pixel corresponding to the discrete origin `(0,0)` is the origin of the continuous space -`(0.0, 0.0)` when the transformation is the identity. See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. +### "Array space" and indexing + +"Array space" is a default space for every array 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"`. For example, a 3D array space at path `/my/data/array` is: + +``` +{ + "name" : "/my/data/array", + "axes" : [ + {"name": "dim_0", "type": "array", "unit": ""}, + {"name": "dim_1", "type": "array", "unit": ""}, + {"name": "dim_2", "type": "array", "unit": ""} + ] +} +``` +though this object should not and need not explicitly appear in metadata. The dimensionality of each varies +according to its corresponding array. -### "Array space" +The axis with name `"dim_0"` is the first element of the `"axes"` list and corresponds to the +"first dimension" of the array, where "first dimension" is defined by the byte order. As described in the [zarr +array metadata](https://zarr.readthedocs.io/en/stable/spec/v2.html#arrays), the first dimension of an array "C" +("F") order vary slowest (fastest). -"Array space" is the unique space whose name is the empty string (`""`) or `null`. The array data of every dataset is in "Array -space." Therefore, the dimensionality of "array space" varies according to its corresponding dataset. -In [[#trafo-md]], there SHOULD exist at least one transformation whose "input_space" is the empty string, i.e. -"array space." + +### Coordinate convention + +**The pixel/voxel center is the origin of the continuous coordinate system.** + +It is vital to consistently define relationship between the discrete/arrray 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 space +`(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)`. See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. "transformations" metadata {#trafo-md} @@ -274,34 +296,93 @@ In [[#trafo-md]], there SHOULD exist at least one transformation whose "input_sp "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 +It is a list of objects. Each entry describes a single transformation, and -- MUST contain the field "name". The values MUST be unique across all "name" fields for coordinate transformations. - MUST contain the field "type". - MUST contain any other fields required by the given "type" (see table below). -- SHOULD contain exactly one of the fields "input_space" or "input_axes". - MUST contain exactly one of the fields "output_space" or "output_axes". -- OPTIONAL contain the field "inverse". -- the value of "output_space" SHOULD not be the empty string. +- SHOULD contain exactly one of the fields "input_space" or "input_axes". +- MAY contain the field "name". Its value MUST be unique across all "name" fields for coordinate transformations.
`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.
`affine` one of:
`"affine":List[float]`,
`"path":str`
affine transformation matrix defined as list consisting of n sets of n + 1 scalar numbers, stored either as a number[] (affine) or as binary data at a location in this container (path). If both are present, path is preferred. n is number of dimensions +
`sequence` + `"transformations":List[Transformation]` + A sequence of transformations, Applying the sequence applies the composition of all transforms in the + list, in order. An empty sequence is the identity transformation. | +
`displacement_field` + `"url":str`
`"path":str`
"interpolation":str +
Displacement field transformation in this container (path) or another container located at (url). +
`coordinate_field` + `"url":str`
`"path":str`
"interpolation":str +
Coordinate field transformation in this container (path) or another container located at (url). +
`inverse_of` + `"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.
typefieldsdescription
+ The transformations in the list are applied sequentally and in order. -### Forward and inverse -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"` +### Details + +#### Affine + +Affine transformations from N-dimensions to M-dimensions are represented at `(N)x(M+1)` matrices in homogeneous coordinates and stored as flat arrays +(row-major). + +A 2D-2D example: +```json +{ + "type": "affine", + "affine": [1, 2, 3, 4, 5, 6], + "input_axes" : ["i", "j"], + "output_axes" : ["x", "y"] +} +``` + +defines the function: + +``` +x = 1*i + 2*j + 3 +y = 4*i + 5*j + 6 +``` + +A 2D - 3D example: +```json +{ + "type": "affine", + "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], + "input_axes" : ["i", "j"], + "output_axes" : ["x", "y", "z"] +} +``` + +defines the function: + +``` +x = 1*i + 2*j + 3 +y = 4*i + 5*j + 6 +z = 7*i + 8*j + 9 +``` + + +### Points, coordinates, direction, and indexing + +Coordinate transformations are 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 transformation: ```json { - "name": "ij2xy", "type": "scale", "scale": [2, 0.5] "input_axes" : ["i", "j"] @@ -309,26 +390,28 @@ from *points* in the input space to *points* in the output space. For example, t } ``` -represents the function: +defines the function: ``` x = 2 * i y = 0.5 * j ``` -When rendering images and interpolating, implementations may need the "inverse" transformation - from output to +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 output to input space. 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 should be specified using the `inverse` transformation type, for example: ```json { - "name": "nonlinear-inverse", - "type": "displacement_field", - "path": "/path/to/my/transform", - "input_axes" : ["i", "j"], - "output_axes" : ["x", "y"], - "inverse" : true + "type": "inverse_of", + "transformation" : { + "name": "nonlinear-inverse", + "type": "displacement_field", + "path": "/path/to/my/transform", + } } ``` From dd7195359515825180d0f860495cf12d653c5e1b Mon Sep 17 00:00:00 2001 From: bogovicj Date: Mon, 9 May 2022 12:18:34 -0400 Subject: [PATCH 006/113] change fields to camelCase --- latest/index.bs | 86 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index c953ec2d..58045718 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -253,7 +253,8 @@ Order of the `"axes"` list matters and defines the index of each dimension and c space. For the above example, the `"x"` dimension is the first dimension. -### "Array space" and indexing +### Array space and indexing {#array-space} + "Array space" is a default space for every array 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 @@ -300,8 +301,8 @@ It is a list of objects. Each entry describes a single transformation, and - MUST contain the field "type". - MUST contain any other fields required by the given "type" (see table below). -- MUST contain exactly one of the fields "output_space" or "output_axes". -- SHOULD contain exactly one of the fields "input_space" or "input_axes". +- MUST contain exactly one of the fields "outputSpace" or "outputAxes". +- SHOULD contain exactly one of the fields "inputSpace" or "inputAxes". - MAY contain the field "name". Its value MUST be unique across all "name" fields for coordinate transformations. @@ -313,13 +314,13 @@ It is a list of objects. Each entry describes a single transformation, and
`"transformations":List[Transformation]` A sequence of transformations, Applying the sequence applies the composition of all transforms in the list, in order. An empty sequence is the identity transformation. | -
`displacement_field` +
`displacementField` `"url":str`
`"path":str`
"interpolation":str
Displacement field transformation in this container (path) or another container located at (url). -
`coordinate_field` +
`coordinateField` `"url":str`
`"path":str`
"interpolation":str
Coordinate field transformation in this container (path) or another container located at (url). -
`inverse_of` +
`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` @@ -331,6 +332,63 @@ It is a list of objects. Each entry describes a single transformation, and The transformations in the list are applied sequentally and in order. +### Spaces and axes + +Every coordinate transformation MUST specify its output coordinate system, either using `outputSpace` to +specify the name of an output space, or by using `outputAxes` to specify a list of axis names. Every +coordinate transformation SHOULD specify its input coordinate system, similarly either using `inputSpace` to +`inputAxes`. If neither `inputSpace` nor `inputAxes` are provided, the transformation's input +is in [array-space](#array-space). + +Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. +Which subspace should be defined using the `"inputAxes"` and `"outputAxes"` fields. An axis name MUST appear +in exactly one `"outputAxis"` lists. + +A simple, contrived example +```json +{ + "type" : "sequence", + "transformations" : [ + { + "type": "scale", + "scale" : [ 0.5, 0.5 ], + "inputAxes: ["dim_1", "dim_2"], + "outputAxes: ["x", "y"], + }, + { + "type": "scale", + "scale" : [ 2 ], + "inputAxes: ["dim_0"], + "outputAxes: ["t"], + } + ] +} +``` + +```json +{ + "type" : "sequence", + "transformations" : [ + { + "type": "inverseOf", + "transformation" : { + "name": "nonlinear-inverse", + "type": "displacementField", + "path": "/path/to/my/transform", + "inputAxes: ["i", "j"], + "outputAxes: ["x", "y"], + }, + }, + { + "type": "scale", + "scale" : [ 2 ] + "inputAxes: ["k"], + "outputAxes: ["z"], + } + ] +} +``` + ### Details @@ -344,8 +402,8 @@ A 2D-2D example: { "type": "affine", "affine": [1, 2, 3, 4, 5, 6], - "input_axes" : ["i", "j"], - "output_axes" : ["x", "y"] + "inputAxes" : ["i", "j"], + "outputAxes" : ["x", "y"] } ``` @@ -361,8 +419,8 @@ A 2D - 3D example: { "type": "affine", "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], - "input_axes" : ["i", "j"], - "output_axes" : ["x", "y", "z"] + "inputAxes" : ["i", "j"], + "outputAxes" : ["x", "y", "z"] } ``` @@ -385,8 +443,8 @@ The indexes of axis dimensions correspond to indexes into transformation paramet { "type": "scale", "scale": [2, 0.5] - "input_axes" : ["i", "j"] - "output_axes" : ["x", "y"] + "inputAxes" : ["i", "j"] + "outputAxes" : ["x", "y"] } ``` @@ -406,10 +464,10 @@ transformation type, for example: ```json { - "type": "inverse_of", + "type": "inverseOf", "transformation" : { "name": "nonlinear-inverse", - "type": "displacement_field", + "type": "displacementField", "path": "/path/to/my/transform", } } From 1b26a8e6beb79145155535073e2c95d1cf477da7 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Thu, 12 May 2022 16:51:06 -0400 Subject: [PATCH 007/113] add details for coordinate and displacement fields --- latest/index.bs | 93 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 58045718..f28a192a 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -222,7 +222,7 @@ keys as specified below for discovering certain types of data, especially images "axes" describes the dimensions of a 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 "type". It SHOULD be one of "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' @@ -315,10 +315,10 @@ It is a list of objects. Each entry describes a single transformation, and A sequence of transformations, Applying the sequence applies the composition of all transforms in the list, in order. An empty sequence is the identity transformation. |
`displacementField` - `"url":str`
`"path":str`
"interpolation":str +
`"url":str`
`"path":str`
`"interpolation":str`
Displacement field transformation in this container (path) or another container located at (url).
`coordinateField` - `"url":str`
`"path":str`
"interpolation":str +
`"url":str`
`"path":str`
`"interpolation":str`
Coordinate field transformation in this container (path) or another container located at (url).
`inverseOf` `"transform":Transform` @@ -392,9 +392,9 @@ A simple, contrived example ### Details -#### Affine +#### affine -Affine transformations from N-dimensions to M-dimensions are represented at `(N)x(M+1)` matrices in homogeneous coordinates and stored as flat arrays +`affine` transformations from N-dimensions to M-dimensions are represented at `(N)x(M+1)` matrices in homogeneous coordinates and stored as flat arrays (row-major). A 2D-2D example: @@ -432,6 +432,89 @@ y = 4*i + 5*j + 6 z = 7*i + 8*j + 9 ``` +#### coordinateField and displacementField + +`coordinateField` and `displacementField` transformations store coordinates or displacements in an array. Applying the transformation amounts +to looking up the appropriate locations in the array, and interpolating. Metadata for these coordinate +transforms have the following field: + +
+
path
+
The location of the coordinate array in this (or another) containter.
+
url
+
An optional URL to the container in which the coordinate field array is stored. If not provided + the provided `path` MUST exist in this container.
+
interpolation
+
The `interpolation` attributes MAY be provided. It's value indicates + the interpolation to use if transforing points not on the array's discrete grid. + Values could be: +
    +
  • linear (default)
  • +
  • nearest
  • +
  • cubic
  • +
+
+ +The array data at `path` MUST define space and coordinate transform metadata: + +* space metadata MUST have exactly one axis with `"type" : "coordinate"` +* Every axis in the `coordinateTransform`'s `inputSpace` MUST appear in the space +* The array dimension corresponding to the coordinate axis MUST have length greater than or equal to the dimensionality of the `coordinateTransform` `outputSpace` +* SHOULD have `name` equal to the `name` of the corresponding `coordinateTransform`. + +For a `coordinateField`: + +* If a `coordinateField`s input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(ni + 1)`. +* space metadata MUST have exactly one axis with `"type" : "coordinate"` + +For a `displacementField`: +* space metadata MUST have exactly one axis with `"type" : "displacement"` +* `inputSpace` and `outputSpace` + + +A simple 1D example, +``` +{ + "name" : "a coordinate field transform", + "type": "coordinateField", + "path" : "coordinates", + "inputAxes" : ["i"], + "outputAxes" : ["x"], + "interpolation" : "nearest" +} +``` + +if the arrray in `coordinates` contains the data: `[-9, 9, 0]`, then this metadata defines the function: + +``` +x = + if ( i < 0.5 ) -9 + else if ( i < 1.5 ) 9 + else 0 +``` + +Example metadata for the array data at path `coordinates` above: + +``` +{ + "spaces" : [ + { + "name" : "a coordinate field transform", + "axes" : [ + { "label": "i", "type": "space", "discrete": true }, + { "label": "c", "type": "coordinate", "discrete": true } + ] + } + ], + "transformations" : [ + { + "type" : "identity", + "outputSpace" : "my coordinate field transform" + } + ] +} +``` + ### Points, coordinates, direction, and indexing From cd9c43139cff2b854e79df8385062337d34d4d22 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 13 May 2022 10:51:11 -0400 Subject: [PATCH 008/113] add details on dimension order --- latest/index.bs | 54 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index f28a192a..2670d236 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -220,9 +220,9 @@ keys as specified below for discovering certain types of data, especially images "axes" metadata {#axes-md} -------------------------- -"axes" describes the dimensions of a coordinate space. It is a list of dictionaries, where each dictionary describes a dimension (axis) and: +"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 "space", "time", "channel", "coordinate", or "displacement" but MAY take other values for custom axis types that are not part of this specification yet. +- 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' @@ -253,31 +253,53 @@ Order of the `"axes"` list matters and defines the index of each dimension and c space. For the above example, the `"x"` dimension is the first dimension. -### Array space and indexing {#array-space} +### Array space and indexing - -"Array space" is a default space for every array whose parameters need not be explicitly defined. +Every array has a default coordinate system we call "array space" 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"`. For example, a 3D array space at path `/my/data/array` is: +default "name"s. The ith axis has `"name":"dim_i"`. For example, a 3D array space at path `/my/data/array` is: ``` { "name" : "/my/data/array", "axes" : [ - {"name": "dim_0", "type": "array", "unit": ""}, - {"name": "dim_1", "type": "array", "unit": ""}, - {"name": "dim_2", "type": "array", "unit": ""} + {"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 varies -according to its corresponding array. +though this object should not and need not explicitly appear in metadata. The dimensionality of each array space +equals the dimensionality of its corresponding zarr array. + +The axis with name `"dim_0"` is the first 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. + +and corresponds to the +"first dimension" of the array, where the "first dimension" corresponds to the first element of +the `shape` attribute in the `.zarray` zarr attributes, 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 +} +``` -The axis with name `"dim_0"` is the first element of the `"axes"` list and corresponds to the -"first dimension" of the array, where "first dimension" is defined by the byte order. As described in the [zarr -array metadata](https://zarr.readthedocs.io/en/stable/spec/v2.html#arrays), the first dimension of an array "C" -("F") order vary slowest (fastest). +Then `dim_0` has length 4, `dim_1` has length 3, and `dim_2` has length 5. ### Coordinate convention @@ -338,7 +360,7 @@ Every coordinate transformation MUST specify its output coordinate system, eithe specify the name of an output space, or by using `outputAxes` to specify a list of axis names. Every coordinate transformation SHOULD specify its input coordinate system, similarly either using `inputSpace` to `inputAxes`. If neither `inputSpace` nor `inputAxes` are provided, the transformation's input -is in [array-space](#array-space). +is in array-space. Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. Which subspace should be defined using the `"inputAxes"` and `"outputAxes"` fields. An axis name MUST appear From e210027312bc2ea5e30f1bd0125f0f07b16a771a Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 13 May 2022 10:56:43 -0400 Subject: [PATCH 009/113] clean up array indexing section --- latest/index.bs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 2670d236..9d2b26d5 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -271,18 +271,12 @@ default "name"s. The ith axis has `"name":"dim_i"`. For example, a 3D array spac ``` though this object should not and need not explicitly appear in metadata. The dimensionality of each array space -equals the dimensionality of its corresponding zarr array. - -The axis with name `"dim_0"` is the first 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. - -and corresponds to the -"first dimension" of the array, where the "first dimension" corresponds to the first element of -the `shape` attribute in the `.zarray` zarr attributes, 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. +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: From d8735410a52931cf85c28b903d4d335819ba4be5 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sun, 15 May 2022 11:20:35 -0400 Subject: [PATCH 010/113] add more details for scale, translation, displacement --- latest/index.bs | 147 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 7 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 9d2b26d5..296c8f4b 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -320,6 +320,7 @@ It is a list of objects. Each entry describes a single transformation, and - MUST contain exactly one of the fields "outputSpace" or "outputAxes". - SHOULD contain exactly one of the fields "inputSpace" or "inputAxes". - 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` identity transformation, is the default transformation and is typically not explicitly defined @@ -410,8 +411,21 @@ A simple, contrived example #### affine -`affine` transformations from N-dimensions to M-dimensions are represented at `(N)x(M+1)` matrices in homogeneous coordinates and stored as flat arrays -(row-major). +`affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` matrices in homogeneous coordinates. +The matrix may be stored as a 2D array or as a 1D array (row-major). + +
+
path
+
The path to an array containing the affine parameters. + The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. + If 2D, its shape MUST be `N x M+1`.
+
url
+
An optional URL to the container in which the affine array is stored. If not provided, + the provided `path` MUST exist in this container.
+
affine
+
The affine parameters stored in JSON. If stored as a flat list of numbers the list MUST be length `N*(M+1)`. + If stored as a list of lists, the outer list MUST be length `N`, and inner lists MUST be length `(M+1)`.
+
A 2D-2D example: ```json @@ -448,6 +462,82 @@ y = 4*i + 5*j + 6 z = 7*i + 8*j + 9 ``` +The same transformation may also be written as: +```json +{ + "type": "affine", + "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + "inputAxes" : ["i", "j"], + "outputAxes" : ["x", "y", "z"] +} +``` + +#### scale + +`scale` transformations are special cases of affine transformations. When possible, a +scale transformation should be preferred to its equivalent affine. + +
+
path
+
The path to an array containing the affine parameters. + The array at this path MUST be 1D, and its length MUST be `N`.
+
url
+
An optional URL to the container in which the affine array is stored. If not provided, + the provided `path` MUST exist in this container.
+
scale
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
+ +For example +```json +{ + "type": "scale", + "scale": [3, 2], + "inputAxes" : ["i", "j"], + "outputAxes" : ["x", "y"] +} +``` + +defines the function: + +``` +x = 3 * i +y = 2 * j +``` + +#### translation + +`translation` transformations are special cases of affine transformations. When possible, a +translation transformation should be preferred to its equivalent affine. + +
+
path
+
The path to an array containing the affine parameters. + The array at this path MUST be 1D, and its length MUST be `N`.
+
url
+
An optional URL to the container in which the affine array is stored. If not provided, + the provided `path` MUST exist in this container.
+
scale
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
+ +For example +```json +{ + "type": "translation", + "translation": [9, -1], + "inputAxes" : ["i", "j"], + "outputAxes" : ["x", "y"] +} +``` + +defines the function: + +``` +x = i + 9 +y = j - 1 +``` + #### coordinateField and displacementField `coordinateField` and `displacementField` transformations store coordinates or displacements in an array. Applying the transformation amounts @@ -458,7 +548,7 @@ transforms have the following field:
path
The location of the coordinate array in this (or another) containter.
url
-
An optional URL to the container in which the coordinate field array is stored. If not provided +
An optional URL to the container in which the coordinate field array is stored. If not provided, the provided `path` MUST exist in this container.
interpolation
The `interpolation` attributes MAY be provided. It's value indicates @@ -485,10 +575,9 @@ For a `coordinateField`: For a `displacementField`: * space metadata MUST have exactly one axis with `"type" : "displacement"` -* `inputSpace` and `outputSpace` +* `inputSpace` and `outputSpace` MUST have an equal number of dimensions. - -A simple 1D example, +For example, in 1D: ``` { "name" : "a coordinate field transform", @@ -525,12 +614,56 @@ Example metadata for the array data at path `coordinates` above: "transformations" : [ { "type" : "identity", - "outputSpace" : "my coordinate field transform" + "outputSpace" : "a coordinate field transform" + } + ] +} +``` + +A 1D example displacement field: +``` +{ + "name" : "a displacement field transform", + "type": "displacementField", + "path" : "displacements", + "inputAxes" : ["x"], + "outputAxes" : ["y"], + "interpolation" : "linear" +} +``` + +if the arrray in `displacements` contains the data: `[-1, 0, 1]`. + +Example metadata for the array data at path `displacements` above: + +``` +{ + "spaces" : [ + { + "name" : "a coordinate field transform", + "axes" : [ + { "label": "x", "type": "space", "unit" : "nanometer" }, + { "label": "d", "type": "displacement", "discrete": true } + ] + } + ], + "transformations" : [ + { + "type" : "scale", + "scale" : [2, 1], + "outputSpace" : "a displacement field transform" } ] } ``` +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`. + ### Points, coordinates, direction, and indexing From 63ed4ce126df8eb09e4a28b62ef0eec4f7626960 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Mon, 16 May 2022 09:04:55 -0400 Subject: [PATCH 011/113] merge and cleanup --- latest/index.bs | 108 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 7 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 296c8f4b..eb0b9408 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -238,7 +238,7 @@ A "space" is a collection of "axes" / dimensions with a name and defines a coord - 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". -``` +```json { "name" : "volume_micrometers", "axes" : [ @@ -259,7 +259,7 @@ Every array has a default coordinate system we call "array space" whose paramete 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"`. For example, a 3D array space at path `/my/data/array` is: -``` +```json { "name" : "/my/data/array", "axes" : [ @@ -323,10 +323,10 @@ It is a list of objects. Each entry describes a single transformation, and - Parameter values MUST be compatible with input and output space dimensionality (see details). -
`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. -
`affine` one of:
`"affine":List[float]`,
`"path":str`
affine transformation matrix defined as list consisting of n sets of n + 1 scalar numbers, stored either as a number[] (affine) or as binary data at a location in this container (path). If both are present, path is preferred. n is number of dimensions +
`identity` The identity transformation is the default transformation and is typically not explicitly defined +
`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`). The length of vector defines number of dimensions. | +
`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`). The length of vector defines number of dimensions. +
`affine` one of:
`"affine":List[number]`,
`"path":str`
affine transformation matrix defined as list consisting of n sets of n + 1 numbers, stored either as a number[] (affine) or as binary data at a location in this container (path). If both are present, path is preferred. n is number of dimensions
`sequence` `"transformations":List[Transformation]` A sequence of transformations, Applying the sequence applies the composition of all transforms in the @@ -343,6 +343,10 @@ It is a list of objects. Each entry describes a single transformation, and
`bijection` `"forward":Transform`
`"inverse":Transform`
Explicitly define an invertible transformation by providing a forward transformation and its inverse. +
`dimensionWise` + `"transformations":List[Transformation]` + Define a high dimensional transformation using lower dimensional transformations on subsets of + dimensions.
typefieldsdescription
@@ -361,7 +365,7 @@ Coordinate transformations may be defined as sequences of transformations, each Which subspace should be defined using the `"inputAxes"` and `"outputAxes"` fields. An axis name MUST appear in exactly one `"outputAxis"` lists. -A simple, contrived example +A simple example ```json { "type" : "sequence", @@ -665,6 +669,96 @@ The transformation specifies linear interpolation, which in this case yields input point, hence the output is `1.0 + (-0.5) = 0.5`. +#### dimensionWise + +`dimensionWise` 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 subset of input and output dimensions (axes). + Every transformation in this list MUST have the fields `inputAxes` and `outputAxes` fields. + Every axis name in `inputAxes` MUST appear in this parent objects `inputSpace` axes. + Every axis in the parent `dimensionWise`'s `outputSpace` MUST appear in exactly one + of its child transformations' `outputAxes`. +
+
+ +This is a valid `dimensionWise` transformation: + +```json +{ + "spaces" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + ], + "transforms" : [ + { + "name: "in2out", + "type: "dimensionWise", + "inputSpace" : "in", + "outputSpace" : "out", + "transformations" : [ + { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["x"]} + { "type" : "scale", "scale" : [2.0], "inputAxes" : ["j"], "outputAxes" : ["y"]} + ] + } + ] +} +``` + +This is also a valid transformation: + +```json +{ + "spaces" : [ + { "name" : "in", "axes" : [ {"name" : "0"}, {"name" : "1"}, {"name" : "2"}, {"name" : "3"}] }, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"}, {"name" : "z"} ] } + ], + "transforms" : [ + { + "name: "in2out", + "type: "dimensionWise", + "inputSpace" : "in", + "outputSpace" : "out", + "transformations" : [ + { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["0","3" ], "outputAxes" : ["y", "x"]} + { "type" : "scale", "scale" : [2.0], "inputAxes" : ["1"], "outputAxes" : ["z"]} + ] + } + ] +} +``` + + +This is an invalid `dimensionWise` transform: + +```json +{ + "spaces" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + ], + "transforms" : [ + { + "name: "in2out", + "type: "dimensionWise", + "inputSpace" : "in", + "outputSpace" : "out", + "transformations" : [ + { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["z"]} + { "type" : "scale", "scale" : [2.0], "inputAxes" : ["0"], "outputAxes" : ["y"]} + ] + } + ] +} +``` + +for two reasons. First because inputAxis "0" used by the scale transformation is not an axis of the `dimensionWise` transformation's +`inputSpace`. Second, the "x" axis of the `outputSpace` does not appear in the `outputAxes` of any child +transformation. + + ### Points, coordinates, direction, and indexing Coordinate transformations are are functions of *points* in the input space to *points* in the output space. We call this the "forward" direction. From 9058b4bc78da60cf9ee9a827413b20f07d198491 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Wed, 1 Jun 2022 18:05:44 -0400 Subject: [PATCH 012/113] clarify input and output dimensionality for transforms * use "input" and "output" rather than '*Space' and '*Axes' --- latest/index.bs | 108 ++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 5a0af5f8..0917fa18 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -263,7 +263,7 @@ A "space" is a collection of "axes" / dimensions with a name and defines a coord } ``` -Order of the `"axes"` list matters and defines the index of each dimension and coordinates for points in that +Order of the `"axes"` list matters and defines the index of each array dimension and coordinates for "points" in that space. For the above example, the `"x"` dimension is the first dimension. @@ -331,8 +331,8 @@ It is a list of objects. Each entry describes a single transformation, and - MUST contain the field "type". - MUST contain any other fields required by the given "type" (see table below). -- MUST contain exactly one of the fields "outputSpace" or "outputAxes". -- SHOULD contain exactly one of the fields "inputSpace" or "inputAxes". +- MUST contain the field "output". +- SHOULD contain the field "input". - 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). @@ -376,8 +376,8 @@ coordinate transformation SHOULD specify its input coordinate system, similarly is in array-space. Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. -Which subspace should be defined using the `"inputAxes"` and `"outputAxes"` fields. An axis name MUST appear -in exactly one `"outputAxis"` lists. +Which subspace should be defined by providing arrays of strings as the values for the `"input"` and `"output"` +fields. An axis name MUST appear in exactly one `"output"` lists. A simple example ```json @@ -387,14 +387,14 @@ A simple example { "type": "scale", "scale" : [ 0.5, 0.5 ], - "inputAxes: ["dim_1", "dim_2"], - "outputAxes: ["x", "y"], + "input" : ["dim_1", "dim_2"], + "output" : ["x", "y"], }, { "type": "scale", "scale" : [ 2 ], - "inputAxes: ["dim_0"], - "outputAxes: ["t"], + "input" : ["dim_0"], + "output" : ["t"], } ] } @@ -410,15 +410,15 @@ A simple example "name": "nonlinear-inverse", "type": "displacementField", "path": "/path/to/my/transform", - "inputAxes: ["i", "j"], - "outputAxes: ["x", "y"], + "input" : ["i", "j"], + "output" : ["x", "y"], }, }, { "type": "scale", "scale" : [ 2 ] - "inputAxes: ["k"], - "outputAxes: ["z"], + "input" : ["k"], + "output" : ["z"], } ] } @@ -427,6 +427,13 @@ A simple example ### Details +Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value +of "input" is an array, it's length gives the input dimension, otherwise the length of "axes" for the coordinate +system with the name of the "input" value gives the input dimension. If the value of "input" is an array, it's +length gives the input imension, otherwise it is given by the length of "axes" for the coordinate system with +the name of the "input". If the value of "output" is an array, it's length gives the output dimension, +otherwise it is given by the length of "axes" for the coordinate system with the name of the "output". + #### affine `affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` matrices in homogeneous coordinates. @@ -450,8 +457,8 @@ A 2D-2D example: { "type": "affine", "affine": [1, 2, 3, 4, 5, 6], - "inputAxes" : ["i", "j"], - "outputAxes" : ["x", "y"] + "input" : ["i", "j"], + "output" : ["x", "y"] } ``` @@ -467,8 +474,8 @@ A 2D - 3D example: { "type": "affine", "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], - "inputAxes" : ["i", "j"], - "outputAxes" : ["x", "y", "z"] + "input" : ["i", "j"], + "output" : ["x", "y", "z"] } ``` @@ -485,15 +492,16 @@ The same transformation may also be written as: { "type": "affine", "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - "inputAxes" : ["i", "j"], - "outputAxes" : ["x", "y", "z"] + "input" : ["i", "j"], + "output" : ["x", "y", "z"] } ``` #### scale `scale` transformations are special cases of affine transformations. When possible, a -scale transformation should be preferred to its equivalent affine. +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.
path
@@ -511,8 +519,8 @@ For example { "type": "scale", "scale": [3, 2], - "inputAxes" : ["i", "j"], - "outputAxes" : ["x", "y"] + "input" : ["i", "j"], + "output" : ["x", "y"] } ``` @@ -526,7 +534,8 @@ y = 2 * j #### translation `translation` transformations are special cases of affine transformations. When possible, a -translation transformation should be preferred to its equivalent affine. +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.
path
@@ -544,8 +553,8 @@ For example { "type": "translation", "translation": [9, -1], - "inputAxes" : ["i", "j"], - "outputAxes" : ["x", "y"] + "input" : ["i", "j"], + "output" : ["x", "y"] } ``` @@ -583,7 +592,7 @@ The array data at `path` MUST define space and coordinate transform metadata: * space metadata MUST have exactly one axis with `"type" : "coordinate"` * Every axis in the `coordinateTransform`'s `inputSpace` MUST appear in the space -* The array dimension corresponding to the coordinate axis MUST have length greater than or equal to the dimensionality of the `coordinateTransform` `outputSpace` +* The array dimension corresponding to the coordinate axis MUST have length greater than or equal to the dimensionality of the `coordinateTransform` `output` * SHOULD have `name` equal to the `name` of the corresponding `coordinateTransform`. For a `coordinateField`: @@ -593,7 +602,7 @@ For a `coordinateField`: For a `displacementField`: * space metadata MUST have exactly one axis with `"type" : "displacement"` -* `inputSpace` and `outputSpace` MUST have an equal number of dimensions. +* `input` and `output` MUST have an equal number of dimensions. For example, in 1D: ``` @@ -601,8 +610,8 @@ For example, in 1D: "name" : "a coordinate field transform", "type": "coordinateField", "path" : "coordinates", - "inputAxes" : ["i"], - "outputAxes" : ["x"], + "input" : ["i"], + "output" : ["x"], "interpolation" : "nearest" } ``` @@ -632,7 +641,7 @@ Example metadata for the array data at path `coordinates` above: "transformations" : [ { "type" : "identity", - "outputSpace" : "a coordinate field transform" + "output" : "a coordinate field transform" } ] } @@ -644,8 +653,8 @@ A 1D example displacement field: "name" : "a displacement field transform", "type": "displacementField", "path" : "displacements", - "inputAxes" : ["x"], - "outputAxes" : ["y"], + "input" : ["x"], + "output" : ["y"], "interpolation" : "linear" } ``` @@ -669,7 +678,7 @@ Example metadata for the array data at path `displacements` above: { "type" : "scale", "scale" : [2, 1], - "outputSpace" : "a displacement field transform" + "output" : "a displacement field transform" } ] } @@ -692,9 +701,9 @@ on subsets of dimensions.
transformations
A list of transformations, each of which applies to a subset of input and output dimensions (axes). Every transformation in this list MUST have the fields `inputAxes` and `outputAxes` fields. - Every axis name in `inputAxes` MUST appear in this parent objects `inputSpace` axes. + Every axis name in `input` MUST appear in this parent objects `input` axes. Every axis in the parent `dimensionWise`'s `outputSpace` MUST appear in exactly one - of its child transformations' `outputAxes`. + of its child transformations' `output`.
@@ -710,8 +719,8 @@ This is a valid `dimensionWise` transformation: { "name: "in2out", "type: "dimensionWise", - "inputSpace" : "in", - "outputSpace" : "out", + "input" : "in", + "output" : "out", "transformations" : [ { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["x"]} { "type" : "scale", "scale" : [2.0], "inputAxes" : ["j"], "outputAxes" : ["y"]} @@ -733,11 +742,11 @@ This is also a valid transformation: { "name: "in2out", "type: "dimensionWise", - "inputSpace" : "in", - "outputSpace" : "out", + "input" : "in", + "output" : "out", "transformations" : [ - { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["0","3" ], "outputAxes" : ["y", "x"]} - { "type" : "scale", "scale" : [2.0], "inputAxes" : ["1"], "outputAxes" : ["z"]} + { "type" : "coordinateField", "path" : "/coordinates", "input" : ["0","3" ], "output" : ["y", "x"]} + { "type" : "scale", "scale" : [2.0], "input" : ["1"], "output" : ["z"]} ] } ] @@ -757,20 +766,19 @@ This is an invalid `dimensionWise` transform: { "name: "in2out", "type: "dimensionWise", - "inputSpace" : "in", - "outputSpace" : "out", + "input" : "in", + "output" : "out", "transformations" : [ - { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["z"]} - { "type" : "scale", "scale" : [2.0], "inputAxes" : ["0"], "outputAxes" : ["y"]} + { "type" : "coordinateField", "path" : "/coordinates", "input" : ["i"], "output" : ["z"]} + { "type" : "scale", "scale" : [2.0], "input" : ["0"], "output" : ["y"]} ] } ] } ``` -for two reasons. First because inputAxis "0" used by the scale transformation is not an axis of the `dimensionWise` transformation's -`inputSpace`. Second, the "x" axis of the `outputSpace` does not appear in the `outputAxes` of any child -transformation. +for two reasons. First because input"0" used by the scale transformation is not an axis of the `dimensionWise` transformation's +`input`. Second, the "x" axis of the `output` does not appear in the `output` of any child transformation. ### Points, coordinates, direction, and indexing @@ -783,8 +791,8 @@ The indexes of axis dimensions correspond to indexes into transformation paramet { "type": "scale", "scale": [2, 0.5] - "inputAxes" : ["i", "j"] - "outputAxes" : ["x", "y"] + "input" : ["i", "j"] + "output" : ["x", "y"] } ``` From 38ac6ac16bf6eed1fcc67a4cbac2b933d0a56499 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 3 Jun 2022 08:03:53 -0400 Subject: [PATCH 013/113] update to transformations * reorder details * clean up table * add rotation * details for sequence * describe inverses * wrap examples --- latest/index.bs | 332 ++++++++++++++++++++++++++++++------------------ 1 file changed, 205 insertions(+), 127 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 0917fa18..19eb8c9b 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -322,29 +322,51 @@ The center of a 2d pixel corresponding to the origin `(0,0)` in the discrete arr half-open interval `[-0.5, 0.5) x [-0.5, 0.5)`. See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. -"transformations" metadata {#trafo-md} +"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 objects. Each entry describes a single transformation, and +Coordinate transforms are in the "forward" direction. They represent functions from *points* in the +input space to *points* in the output space. Implementations SHOULD be able to compute and apply the inverse +of some coordinate transformations (see the Details section below will note those transform types). + + - MUST contain the field "type". - MUST contain any other fields required by the given "type" (see table below). -- MUST contain the field "output". -- SHOULD contain the field "input". +- MUST contain the field "output", unless part of a `sequence` (see details). +- SHOULD contain the field "input", unless part of a `sequence` (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 -
`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`). The length of vector defines number of dimensions. | -
`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`). The length of vector defines number of dimensions. -
`affine` one of:
`"affine":List[number]`,
`"path":str`
affine transformation matrix defined as list consisting of n sets of n + 1 numbers, stored either as a number[] (affine) or as binary data at a location in this container (path). If both are present, path is preferred. n is number of dimensions +
`identity` + + The identity transformation is the default transformation and is typically not explicitly defined. +
`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, path is + preferred. +
`rotation` + one of:
`"rotation":List[number]`,
`"path":str` +
rotation transformation matrix stored as a flat array stored either with json uing the rotation field + or as binary data at a location in this container (path). If both are present, path is + preferred.
`sequence` `"transformations":List[Transformation]` A sequence of transformations, Applying the sequence applies the composition of all transforms in the - list, in order. An empty sequence is the identity transformation. | + list, in order. An empty sequence is the identity transformation.
`displacementField` `"url":str`
`"path":str`
`"interpolation":str`
Displacement field transformation in this container (path) or another container located at (url). @@ -365,64 +387,13 @@ It is a list of objects. Each entry describes a single transformation, and
typefieldsdescription
-The transformations in the list are applied sequentally and in order. - -### Spaces and axes -Every coordinate transformation MUST specify its output coordinate system, either using `outputSpace` to -specify the name of an output space, or by using `outputAxes` to specify a list of axis names. Every -coordinate transformation SHOULD specify its input coordinate system, similarly either using `inputSpace` to -`inputAxes`. If neither `inputSpace` nor `inputAxes` are provided, the transformation's input -is in array-space. +Conforming readers: +- SHOULD be able to apply transformations to points +- MAY enable applying transformations to points +- MAY enable applying transformations to images -Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. -Which subspace should be defined by providing arrays of strings as the values for the `"input"` and `"output"` -fields. An axis name MUST appear in exactly one `"output"` lists. -A simple example -```json -{ - "type" : "sequence", - "transformations" : [ - { - "type": "scale", - "scale" : [ 0.5, 0.5 ], - "input" : ["dim_1", "dim_2"], - "output" : ["x", "y"], - }, - { - "type": "scale", - "scale" : [ 2 ], - "input" : ["dim_0"], - "output" : ["t"], - } - ] -} -``` - -```json -{ - "type" : "sequence", - "transformations" : [ - { - "type": "inverseOf", - "transformation" : { - "name": "nonlinear-inverse", - "type": "displacementField", - "path": "/path/to/my/transform", - "input" : ["i", "j"], - "output" : ["x", "y"], - }, - }, - { - "type": "scale", - "scale" : [ 2 ] - "input" : ["k"], - "output" : ["z"], - } - ] -} -``` ### Details @@ -434,29 +405,34 @@ length gives the input imension, otherwise it is given by the length of "axes" f the name of the "input". If the value of "output" is an array, it's length gives the output dimension, otherwise it is given by the length of "axes" for the coordinate system with the name of the "output". -#### affine +#### identity -`affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` matrices in homogeneous coordinates. -The matrix may be stored as a 2D array or as a 1D array (row-major). +`identity` transformation maps input coordinates to output coordinates without modification. `identity` +transformations are invertible. + +#### 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. `translation` transformations are +invertible.
path
The path to an array containing the affine parameters. - The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. - If 2D, its shape MUST be `N x M+1`.
+ The array at this path MUST be 1D, and its length MUST be `N`.
url
An optional URL to the container in which the affine array is stored. If not provided, the provided `path` MUST exist in this container.
-
affine
-
The affine parameters stored in JSON. If stored as a flat list of numbers the list MUST be length `N*(M+1)`. - If stored as a list of lists, the outer list MUST be length `N`, and inner lists MUST be length `(M+1)`.
+
scale
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
-A 2D-2D example: +For example ```json { - "type": "affine", - "affine": [1, 2, 3, 4, 5, 6], + "type": "translation", + "translation": [9, -1], "input" : ["i", "j"], "output" : ["x", "y"] } @@ -465,43 +441,16 @@ A 2D-2D example: defines the function: ``` -x = 1*i + 2*j + 3 -y = 4*i + 5*j + 6 -``` - -A 2D - 3D example: -```json -{ - "type": "affine", - "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], - "input" : ["i", "j"], - "output" : ["x", "y", "z"] -} -``` - -defines the function: - -``` -x = 1*i + 2*j + 3 -y = 4*i + 5*j + 6 -z = 7*i + 8*j + 9 -``` - -The same transformation may also be written as: -```json -{ - "type": "affine", - "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - "input" : ["i", "j"], - "output" : ["x", "y", "z"] -} +x = i + 9 +y = j - 1 ``` #### 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. +`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. Values in the `scale` array SHOULD be non-zero; in that case, `scale` +transformations are invertible.
path
@@ -531,45 +480,115 @@ x = 3 * i y = 2 * j ``` -#### translation +#### affine -`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. +`affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` +matrices in homogeneous coordinates. This transformation type is invertible when `N` equals `M`. +The matrix may be stored as a 2D array or as a 1D array (row-major).
path
The path to an array containing the affine parameters. - The array at this path MUST be 1D, and its length MUST be `N`.
+ The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. + If 2D, its shape MUST be `N x M+1`.
url
An optional URL to the container in which the affine array is stored. If not provided, the provided `path` MUST exist in this container.
-
scale
-
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
affine
+
The affine parameters stored in JSON. If stored as a flat list of numbers the list MUST be length `N*(M+1)`. + If stored as a list of lists, the outer list MUST be length `N`, and inner lists MUST be length `(M+1)`.
-For example +
+ A 2D-2D example: + ```json + { + "type": "affine", + "affine": [1, 2, 3, 4, 5, 6], + "input" : ["i", "j"], + "output" : ["x", "y"] + } + ``` + + defines the function: + + ``` + x = 1*i + 2*j + 3 + y = 4*i + 5*j + 6 + ``` +
+ +
+ An example with two dimensional inputs and three dimensional outputs. + ```json + { + "type": "affine", + "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], + "input" : ["i", "j"], + "output" : ["x", "y", "z"] + } + ``` + + defines the function: + + ``` + x = 1*i + 2*j + 3 + y = 4*i + 5*j + 6 + z = 7*i + 8*j + 9 + ``` + + The same transformation may also be written as: + ```json + { + "type": "affine", + "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + "input" : ["i", "j"], + "output" : ["x", "y", "z"] + } + ``` +
+ + +#### sequence + +A `sequence` transformation consist of an ordered array of coordinate transformations. +`sequence` transformations are invertible if and only if every coordinate transform in the array +is invertible. + +The `input` and `output` fields MAY be omitted for transformations that are in the list +of transformations of a `sequence`. + +
+For example, this sequence: ```json { - "type": "translation", - "translation": [9, -1], + "type" : "sequence", + "transformations" : [ + { "type": "translation", "translation" : [0.1, 0.9] }, + { "type": "scale", "scale" : [2, 3] } + ] "input" : ["i", "j"], "output" : ["x", "y"] } ``` -defines the function: +describes the function ``` -x = i + 9 -y = j - 1 +x = (i + 0.1) * 2 +y = (j + 0.9) * 3 ``` +
+ + + #### coordinateField and displacementField -`coordinateField` and `displacementField` transformations store coordinates or displacements in an array. Applying the transformation amounts -to looking up the appropriate locations in the array, and interpolating. Metadata for these coordinate -transforms have the following field: +`coordinateField` and `displacementField` transformations store coordinates or displacements in an array. +Applying the transformation amounts to looking up the appropriate locations in the array, and interpolating. +`coordinateField` and `displacementField` transformations are note invertible in general, but implementations +MAY approximate their inverses. Metadata for these coordinate transforms have the following field:
path
@@ -591,7 +610,7 @@ transforms have the following field: The array data at `path` MUST define space and coordinate transform metadata: * space metadata MUST have exactly one axis with `"type" : "coordinate"` -* Every axis in the `coordinateTransform`'s `inputSpace` MUST appear in the space +* Every axis name in the `coordinateTransform`'s `input` MUST appear in the space * The array dimension corresponding to the coordinate axis MUST have length greater than or equal to the dimensionality of the `coordinateTransform` `output` * SHOULD have `name` equal to the `name` of the corresponding `coordinateTransform`. @@ -700,9 +719,9 @@ on subsets of dimensions.
transformations
A list of transformations, each of which applies to a subset of input and output dimensions (axes). - Every transformation in this list MUST have the fields `inputAxes` and `outputAxes` fields. - Every axis name in `input` MUST appear in this parent objects `input` axes. - Every axis in the parent `dimensionWise`'s `outputSpace` MUST appear in exactly one + The values of `input` and `output` fields MUST be an array of strings. + Every axis name in `input` MUST appear in this parent object's `input` axes. + Every axis name in the parent dimensionWise's output MUST appear in exactly one of its child transformations' `output`.
@@ -821,6 +840,65 @@ transformation type, for example: } ``` +### Spaces and axes + +Every coordinate transformation MUST specify its output coordinate system using `output` to with a string value +corresponding to the name of an output space or with an array of strings to provide a list of axis names. +Similarly, every coordinate transformation SHOULD specify its input coordinate system either using `input`. If +`input` is not provided the transformation's input is in array-space. + +Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. +Which subspace should be defined by providing arrays of strings as the values for the `input` and `output` +fields. An axis name MUST appear in exactly one `output` lists. + + +
+A simple example +```json +{ + "type" : "sequence", + "transformations" : [ + { + "type": "scale", + "scale" : [ 0.5, 0.5 ], + "input" : ["dim_1", "dim_2"], + "output" : ["x", "y"], + }, + { + "type": "scale", + "scale" : [ 2 ], + "input" : ["dim_0"], + "output" : ["t"], + } + ] +} +``` + +```json +{ + "type" : "sequence", + "transformations" : [ + { + "type": "inverseOf", + "transformation" : { + "name": "nonlinear-inverse", + "type": "displacementField", + "path": "/path/to/my/transform", + "input" : ["i", "j"], + "output" : ["x", "y"], + }, + }, + { + "type": "scale", + "scale" : [ 2 ] + "input" : ["k"], + "output" : ["z"], + } + ] +} +``` +
+ "multiscales" metadata {#multiscale-md} --------------------------------------- From 3a2aae1658166cec96c693f383fc0875b92c5531 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 3 Jun 2022 14:46:48 -0400 Subject: [PATCH 014/113] rotation transformation details * rephrase matrix storage --- latest/index.bs | 62 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 19eb8c9b..310d387f 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -356,13 +356,12 @@ of some coordinate transformations (see the Details section below will note thos
`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, path is - preferred. + 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 a flat array stored either with json uing the rotation field - or as binary data at a location in this container (path). If both are present, path is - preferred. + 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 @@ -448,7 +447,7 @@ y = j - 1 #### 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 +SHOULD be defined to its equivalent affine. Input and output dimensionality MUST be identical and MUST equal the the length of the "scale" array. Values in the `scale` array SHOULD be non-zero; in that case, `scale` transformations are invertible. @@ -495,8 +494,10 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
An optional URL to the container in which the affine array is stored. If not provided, the provided `path` MUST exist in this container.
affine
-
The affine parameters stored in JSON. If stored as a flat list of numbers the list MUST be length `N*(M+1)`. - If stored as a list of lists, the outer list MUST be length `N`, and inner lists MUST be length `(M+1)`.
+
The affine parameters stored in JSON. The matrix may be stored + as a row-major flat list of numbers the list MUST be length `N*(M+1)`. If stored as a list of + lists, the outer list MUST be length `N`, and all inner lists MUST be + length `(M+1)`. In this case, inner lists contain rows of the matrix.
@@ -549,6 +550,51 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
+#### rotation + +`rotation` transformations are special cases of affine transformations. +When possible, a rotation transformation SHOULD be defined rather than +its equivalent affine. Input and output dimensionality (N) MUST be +identical and greater than 1. Rotations are stored as `NxN` matrices, +see below, and MUST have determinant equal to one, with orthonormal rows +and columns. `rotation` transformations are invertible. + +
+
path
+
The path to an array containing the affine parameters. + The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*N`. + If 2D, its shape MUST be `N x N`.
+
url
+
An optional URL to the container in which the affine array is stored. If not provided, + the provided `path` MUST exist in this container.
+
rotation
+
The rotation parameters stored in JSON. The matrix may be stored + as a row-major flat list of numbers. Int that case, the list MUST be + length `N*N`. The matrix may bes stored a list of lists, the outer list MUST be + length `N`, and all inner lists MUST be length `N`. In this case, + inner lists contain rows of the matrix.
+
+ +
+ A 2D example + ```json + { + "type": "rotation", + "affine": [0, 1, 1, 0], + "input" : ["i", "j"], + "output" : ["x", "y"] + } + ``` + + defines the function: + + ``` + x = 0*i + 1*j + y = 1*i + 0*j + ``` +
+ + #### sequence A `sequence` transformation consist of an ordered array of coordinate transformations. From ed121921a11df881f52b5d3bd627585772675dae Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 3 Jun 2022 15:59:56 -0400 Subject: [PATCH 015/113] minor rephrase intro to coordinateTransformations section --- latest/index.bs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 310d387f..fa63722d 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -325,10 +325,8 @@ half-open interval `[-0.5, 0.5) x [-0.5, 0.5)`. See chapter 4 and figure 4.1 of "coordinateTransformations" metadata {#trafo-md} ------------------------------------- -"coordinateTransformations" describe a series of transformations that map between two coordinate spaces (defined by "axes"). +"coordinateTransformations" describe 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 objects. Each entry describes a single transformation, and - Coordinate transforms are in the "forward" direction. They represent functions from *points* in the input space to *points* in the output space. Implementations SHOULD be able to compute and apply the inverse of some coordinate transformations (see the Details section below will note those transform types). From 3ec8543e1d56771d6fdf1b8bbabb951e2af5752f Mon Sep 17 00:00:00 2001 From: bogovicj Date: Mon, 6 Jun 2022 13:41:19 -0400 Subject: [PATCH 016/113] add details for bijection ct * change to "coordinates", removing "Field" * change to "displacements", removing "Field" --- latest/index.bs | 80 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index fa63722d..63d2ccf1 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -364,10 +364,10 @@ of some coordinate transformations (see the Details section below will note thos
`"transformations":List[Transformation]` A sequence of transformations, Applying the sequence applies the composition of all transforms in the list, in order. An empty sequence is the identity transformation. -
`displacementField` +
`displacements` `"url":str`
`"path":str`
`"interpolation":str`
Displacement field transformation in this container (path) or another container located at (url). -
`coordinateField` +
`coordinates` `"url":str`
`"path":str`
`"interpolation":str`
Coordinate field transformation in this container (path) or another container located at (url).
`inverseOf` @@ -593,6 +593,10 @@ and columns. `rotation` transformations are invertible. +#### inverseOf + +A + #### sequence A `sequence` transformation consist of an ordered array of coordinate transformations. @@ -627,11 +631,11 @@ y = (j + 0.9) * 3 -#### coordinateField and displacementField +#### coordinates and displacements -`coordinateField` and `displacementField` transformations store coordinates or displacements in an array. +`coordinates` and `displacements` transformations store coordinates or displacements in an array. Applying the transformation amounts to looking up the appropriate locations in the array, and interpolating. -`coordinateField` and `displacementField` transformations are note invertible in general, but implementations +`coordinates` and `displacements` transformations are note invertible in general, but implementations MAY approximate their inverses. Metadata for these coordinate transforms have the following field:
@@ -658,12 +662,12 @@ The array data at `path` MUST define space and coordinate transform metadata: * The array dimension corresponding to the coordinate axis MUST have length greater than or equal to the dimensionality of the `coordinateTransform` `output` * SHOULD have `name` equal to the `name` of the corresponding `coordinateTransform`. -For a `coordinateField`: +For `coordinates`: -* If a `coordinateField`s input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(ni + 1)`. +* If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(ni + 1)`. * space metadata MUST have exactly one axis with `"type" : "coordinate"` -For a `displacementField`: +For `displacements`: * space metadata MUST have exactly one axis with `"type" : "displacement"` * `input` and `output` MUST have an equal number of dimensions. @@ -671,7 +675,7 @@ For example, in 1D: ``` { "name" : "a coordinate field transform", - "type": "coordinateField", + "type": "coordinates", "path" : "coordinates", "input" : ["i"], "output" : ["x"], @@ -714,7 +718,7 @@ A 1D example displacement field: ``` { "name" : "a displacement field transform", - "type": "displacementField", + "type": "displacements", "path" : "displacements", "input" : ["x"], "output" : ["y"], @@ -785,7 +789,7 @@ This is a valid `dimensionWise` transformation: "input" : "in", "output" : "out", "transformations" : [ - { "type" : "coordinateField", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["x"]} + { "type" : "coordinates", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["x"]} { "type" : "scale", "scale" : [2.0], "inputAxes" : ["j"], "outputAxes" : ["y"]} ] } @@ -808,7 +812,7 @@ This is also a valid transformation: "input" : "in", "output" : "out", "transformations" : [ - { "type" : "coordinateField", "path" : "/coordinates", "input" : ["0","3" ], "output" : ["y", "x"]} + { "type" : "coordinates", "path" : "/coordinates", "input" : ["0","3" ], "output" : ["y", "x"]} { "type" : "scale", "scale" : [2.0], "input" : ["1"], "output" : ["z"]} ] } @@ -832,7 +836,7 @@ This is an invalid `dimensionWise` transform: "input" : "in", "output" : "out", "transformations" : [ - { "type" : "coordinateField", "path" : "/coordinates", "input" : ["i"], "output" : ["z"]} + { "type" : "coordinates", "path" : "/coordinates", "input" : ["i"], "output" : ["z"]} { "type" : "scale", "scale" : [2.0], "input" : ["0"], "output" : ["y"]} ] } @@ -844,6 +848,52 @@ for two reasons. First because input"0" used by the scale transformation is not `input`. Second, the "x" axis of the `output` does not appear in the `output` of any child transformation. +#### 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 +approprite dimensionality. + +
+For example +```json +{ + "type" : "bijection", + "forward" :{ "type" : "coordinates", "path" : "/forward_coordinates" }, + "inverse" :{ "type" : "coordinates", "path" : "/inverse_coordinates" }, + "input" : "src", + "output" : "tgt" +} +``` + +the input and output of the `forward` and `inverse` transformations are understoood to be: + + +```json +{ + "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" +} +``` + +
+ + + ### Points, coordinates, direction, and indexing Coordinate transformations are are functions of *points* in the input space to *points* in the output space. We call this the "forward" direction. @@ -878,7 +928,7 @@ transformation type, for example: "type": "inverseOf", "transformation" : { "name": "nonlinear-inverse", - "type": "displacementField", + "type": "displacements", "path": "/path/to/my/transform", } } @@ -926,7 +976,7 @@ A simple example "type": "inverseOf", "transformation" : { "name": "nonlinear-inverse", - "type": "displacementField", + "type": "displacements", "path": "/path/to/my/transform", "input" : ["i", "j"], "output" : ["x", "y"], From 30cade5073324dd8a5acaec0fa71a592a0c4a3ff Mon Sep 17 00:00:00 2001 From: bogovicj Date: Thu, 9 Jun 2022 11:48:05 -0400 Subject: [PATCH 017/113] big changes to transformations spec * add details for transformation types * (identity, inverseOf, bijection) * describe inputAxes and outputAxes * add new examples --- latest/index.bs | 328 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 296 insertions(+), 32 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 63d2ccf1..c0dbf394 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -269,9 +269,12 @@ space. For the above example, the `"x"` dimension is the first dimension. ### Array space and indexing -Every array has a default coordinate system we call "array space" whose parameters need not be explicitly defined. +Every array has a default coordinate system called "array space" 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"`. For example, a 3D array space at path `/my/data/array` is: +default "name"s. The ith axis has `"name":"dim_i"`. + +
+For example, a 3D array space at path `/my/data/array` is: ```json { @@ -284,30 +287,35 @@ default "name"s. The ith axis has `"name":"dim_i"`. For example, a 3D array spac } ``` -though this object should not and need not explicitly appear in metadata. The dimensionality of each array space -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. +though this object should not and need not explicitly appear in metadata. +
-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 -} -``` +The dimensionality of each array space 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. -Then `dim_0` has length 4, `dim_1` has length 3, and `dim_2` has length 5. +
+ 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. +
### Coordinate convention @@ -328,15 +336,22 @@ half-open interval `[-0.5, 0.5) x [-0.5, 0.5)`. See chapter 4 and figure 4.1 of "coordinateTransformations" describe map between two coordinate spaces (defined by "axes"). For example, to map a discrete data space of an array to the corresponding physical space. Coordinate transforms are in the "forward" direction. They represent functions from *points* in the -input space to *points* in the output space. Implementations SHOULD be able to compute and apply the inverse -of some coordinate transformations (see the Details section below will note those transform types). +input space to *points* in the output space. + +Implementations SHOULD be able to compute and apply the inverse of some coordinate transformations whose +inverses are computable in closed-form (see the Details section below will note those transform types). If an +operation is requested that requires the inverse of a transformation that can not be inverted in closed-form, +implementations MAY estimate their inverses, or MAY output a warning that the requested operation is +unsupported. - 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` (see details). - SHOULD contain the field "input", unless part of a `sequence` (see details). +- MAY contain the fields "inputAxes" and / or "outputAxes" (see below). - MAY contain the field "name". Its value MUST be unique across all "name" fields for coordinate transformations. +- MAY contain the fields "inputAxes" and "outputAxes" (see below). - Parameter values MUST be compatible with input and output space dimensionality (see details). @@ -392,8 +407,7 @@ Conforming readers: - -### Details +### Transformation types Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value of "input" is an array, it's length gives the input dimension, otherwise the length of "axes" for the coordinate @@ -404,8 +418,40 @@ otherwise it is given by the length of "axes" for the coordinate system with the #### identity -`identity` transformation maps input coordinates to output coordinates without modification. `identity` -transformations are invertible. +`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. + +
+ +```json +{ + "coordinateSystems" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} + ], + "coordinateTransformations" : [ + { "type" : "identity", "input" : "in", "output" : "out" } + ] +} +``` + +defines the function: + +``` +x = i +y = j +``` + +
+ +`identity` transformations may be used to implement axis permutations by using the `inputAxes` and `outputAxes` +fields (see below). + + +#### axisPermutation + +(The same as identity, but give a different type to mark what it's doing) #### translation @@ -595,11 +641,42 @@ and columns. `rotation` transformations are invertible. #### inverseOf -A +An `inverseOf` transformation contain 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 should 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. +
+ + +
+For example + +```json +"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", + } +] +``` +
#### sequence -A `sequence` transformation consist of an ordered array of coordinate transformations. +A `sequence` transformation consists of an ordered array of coordinate transformations. `sequence` transformations are invertible if and only if every coordinate transform in the array is invertible. @@ -631,6 +708,77 @@ y = (j + 0.9) * 3 +This is a valid `sequence` transformation: + +```json +{ + "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"]} + ] + } + ] +} +``` + +This is also a valid transformation: + +```json +{ + "coordinateSystems" : [ + { "name" : "in", "axes" : [ {"name" : "0"}, {"name" : "1"}, {"name" : "2"}, {"name" : "3"}] }, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"}, {"name" : "z"} ] } + ], + "coordinateTransformations" : [ + { + "type: "sequence", + "input" : "in", + "output" : "out", + "transformations" : [ + { "type" : "coordinates", "path" : "/coordinates", "input" : ["0","3" ], "output" : ["y", "x"]} + { "type" : "scale", "scale" : [2.0], "input" : ["1"], "output" : ["z"]} + ] + } + ] +} +``` + + +This is an invalid `sequence` transform: + +```json +{ + "spaces" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + ], + "transforms" : [ + { + "type: "sequence", + "input" : "in", + "output" : "out", + "transformations" : [ + { "type" : "coordinates", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["z"]} + { "type" : "scale", "scale" : [2.0], "inputAxes" : ["0"], "outputAxes" : ["y"]} + ] + } + ] +} +``` + +for two reasons. First because input"0" used by the scale transformation is not an axis of the `sequence` transformation's +`input`. Second, the "x" axis of the `output` does not appear in the `output` of any child transformation. + + #### coordinates and displacements `coordinates` and `displacements` transformations store coordinates or displacements in an array. @@ -892,6 +1040,122 @@ the input and output of the `forward` and `inverse` transformations are understo +### `inputAxes` and `outputAxes` + +`inputAxes` and `outputAxes` are optional fields for transformations to express that it applies to a subset +or reording of the axes of the input or output coordinate system, respectively. If not present, the `inputAxes` +(`outputAxes`) are understood to be a list of the `input` (`output`) coordinate systems axis names, in the +order they appear in the coordinate system's `axes` list. If present: + +- the values for `inputAxes` and `outputAxes` MUST be a list of strings. +- every string in the `inputAxes` list MUST appear as a name for an axes of the transformations input coordinate system. +- every string in the `outputAxes` list MUST appear as a name for an axes of the transformations output coordinate system. +- every axis name in the output coordinate system MUST appear either as an axis name of the input coordinate system or in the list of `outputAxes` + +
+ +An `identity` may be used to permute axes by setting +the `inputAxes` and / or `outputAxes` fields appropriately (see below). + +```json +{ + "coordinateSystems" : [ + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} + ], + "coordinateTransformations" : [ + { + "type" : "identity", + "input" : "in", + "output" : "out", + "inputAxes" : ["i", "j"], + "outputAxes" : ["y", "x"] + } + ] +} +``` + +defines the function: + +``` +x = j +y = i +``` + +
+ + +
+ A valid example + + ```json + "coordinateSystems" : [ + { "name " : "in", "axes" : [ {"name" : "x", "name" : "y" }] } + { "name " : "out", "axes" : [ {"name" : "x", "name" : "y" }] } + ] + { + "type": "scale", + "scale" : [2], + "input" : "in" + "output" : "out", + "inputAxes" : ["y"], + "outputAxes" : ["y"] + } + ``` + scales the `y` axis only. + +
+ +
+ An invalid example + + ```json + "coordinateSystems" : [ + { "name " : "in", "axes" : [ {"name" : "i", "name" : "j" }] } + { "name " : "out", "axes" : [ {"name" : "x", "name" : "y" }] } + ] + "coordinateTransformations" : [ + { + "type": "scale", + "scale" : [2], + "input" : "in" + "output" : "out", + "inputAxes" : ["j"], + "outputAxes" : ["y"] + } + ] + ``` + + Is invalid because the axis name `x` of the output coordinate system does not appear in the list of `outputAxes`, + nor does it appear in the axis names of the coordinate system `in`. Instead, a `sequence` transformation can + be defined, such that every axis in the output space appears in at least one of the `outputAxes` of the + sequence's transformations. For example (asuming the same coordinate systems as above): + + ```json + { + "type": "sequence", + "transformations" : [ + { + "type": "scale", + "scale" : [2], + "inputAxes" : ["j"], + "outputAxes" : ["y"] + }, + { + "type": "identity", + "inputAxes" : ["i"], + "outputAxes" : ["x"] + } + ], + "input" : "in" + "output" : "out", + } + ``` + + is valid. + +
+ ### Points, coordinates, direction, and indexing @@ -938,7 +1202,7 @@ transformation type, for example: Every coordinate transformation MUST specify its output coordinate system using `output` to with a string value corresponding to the name of an output space or with an array of strings to provide a list of axis names. -Similarly, every coordinate transformation SHOULD specify its input coordinate system either using `input`. If +Similarly, every coordinate transformation SHOULD specify its input coordinate system using the `input`. If `input` is not provided the transformation's input is in array-space. Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. From df470d7a9d3ba6284f74dafc6419ad4df9b89a62 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Tue, 6 Sep 2022 11:32:55 -0400 Subject: [PATCH 018/113] Add "coordinateSystems" and "coordinateTransformations" fields for examples * some clean up --- latest/index.bs | 122 ++++++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 46 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index c0dbf394..9b27bc22 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -338,11 +338,6 @@ For example, to map a discrete data space of an array to the corresponding physi Coordinate transforms are in the "forward" direction. They represent functions from *points* in the input space to *points* in the output space. -Implementations SHOULD be able to compute and apply the inverse of some coordinate transformations whose -inverses are computable in closed-form (see the Details section below will note those transform types). If an -operation is requested that requires the inverse of a transformation that can not be inverted in closed-form, -implementations MAY estimate their inverses, or MAY output a warning that the requested operation is -unsupported. - MUST contain the field "type". @@ -350,8 +345,8 @@ unsupported. - MUST contain the field "output", unless part of a `sequence` (see details). - SHOULD contain the field "input", unless part of a `sequence` (see details). - MAY contain the fields "inputAxes" and / or "outputAxes" (see below). -- MAY contain the field "name". Its value MUST be unique across all "name" fields for coordinate transformations. - MAY contain the fields "inputAxes" and "outputAxes" (see below). +- 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).
@@ -405,6 +400,12 @@ Conforming readers: - MAY enable applying transformations to points - MAY enable applying transformations to images +### Transformation inverses + +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. ### Transformation types @@ -446,8 +447,7 @@ y = j `identity` transformations may be used to implement axis permutations by using the `inputAxes` and `outputAxes` -fields (see below). - +fields (see below [`inputAxes` and `outputAxes`](#inputaxes-and-outputaxes)). #### axisPermutation @@ -473,12 +473,18 @@ invertible. For example ```json -{ - "type": "translation", - "translation": [9, -1], - "input" : ["i", "j"], - "output" : ["x", "y"] -} +"coordinateSystems" : [ + { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } + { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } +], +"coordinateTransformations" : [ + { + "type": "translation", + "translation": [9, -1], + "input" : "ij", + "output" : "xy" + } +] ``` defines the function: @@ -508,12 +514,18 @@ transformations are invertible. For example ```json -{ - "type": "scale", - "scale": [3, 2], - "input" : ["i", "j"], - "output" : ["x", "y"] -} +"coordinateSystems" : [ + { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } + { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } +], +"coordinateTransformations" : [ + { + "type": "scale", + "scale": [3, 2], + "input" : "ij", + "output" : "xy" + } +] ``` defines the function: @@ -547,12 +559,18 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
A 2D-2D example: ```json - { - "type": "affine", - "affine": [1, 2, 3, 4, 5, 6], - "input" : ["i", "j"], - "output" : ["x", "y"] - } + "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" + } + ] ``` defines the function: @@ -566,12 +584,18 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
An example with two dimensional inputs and three dimensional outputs. ```json - { - "type": "affine", - "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], - "input" : ["i", "j"], - "output" : ["x", "y", "z"] - } + "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" + } + ] ``` defines the function: @@ -587,8 +611,8 @@ The matrix may be stored as a 2D array or as a 1D array (row-major). { "type": "affine", "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - "input" : ["i", "j"], - "output" : ["x", "y", "z"] + "input" : "ij", + "output" : "xyz" } ```
@@ -622,18 +646,24 @@ and columns. `rotation` transformations are invertible.
A 2D example ```json - { - "type": "rotation", - "affine": [0, 1, 1, 0], - "input" : ["i", "j"], - "output" : ["x", "y"] - } + "coordinateSystems" : [ + { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } + { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } + ], + "coordinateTransformations" : [ + { + "type": "rotation", + "affine": [0, -1, 1, 0], + "input" : "ij", + "output" : "xy" + } + ] ``` defines the function: ``` - x = 0*i + 1*j + x = 0*i - 1*j y = 1*i + 0*j ```
@@ -641,7 +671,7 @@ and columns. `rotation` transformations are invertible. #### inverseOf -An `inverseOf` transformation contain another transformation (often non-linear), and indicates that +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). @@ -1047,10 +1077,10 @@ or reording of the axes of the input or output coordinate system, respectively. (`outputAxes`) are understood to be a list of the `input` (`output`) coordinate systems axis names, in the order they appear in the coordinate system's `axes` list. If present: -- the values for `inputAxes` and `outputAxes` MUST be a list of strings. -- every string in the `inputAxes` list MUST appear as a name for an axes of the transformations input coordinate system. -- every string in the `outputAxes` list MUST appear as a name for an axes of the transformations output coordinate system. -- every axis name in the output coordinate system MUST appear either as an axis name of the input coordinate system or in the list of `outputAxes` +- Values for `inputAxes` and `outputAxes` MUST be a list of strings. +- Every string in the `inputAxes` list MUST be the name of an axis of the transformation's input coordinate system. +- Every string in the `outputAxes` list MUST be the name of an axis of the transformation's output coordinate system. +- Every axis name in the output coordinate system MUST in the list of `outputAxes`.
From 99f3a4e8254c1c6bc861c8c12e6c884262c8c831 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 7 Sep 2022 16:37:52 -0400 Subject: [PATCH 019/113] coordinate transformation updates: * add mapIndex, mapAXis * add examples * affine stored as flat array only --- .../examples/subspace/subspaceMultidim.json | 64 ++++ latest/examples/subspace/subspacePermute.json | 26 ++ .../examples/transformations/affine2d2d.json | 14 + .../examples/transformations/affine2d3d.json | 14 + latest/examples/transformations/identity.json | 9 + latest/examples/transformations/mapAxis1.json | 23 ++ latest/examples/transformations/mapAxis2.json | 23 ++ .../examples/transformations/mapIndex1.json | 23 ++ .../examples/transformations/mapIndex2.json | 23 ++ latest/examples/transformations/scale.json | 14 + .../examples/transformations/translation.json | 14 + latest/index.bs | 290 +++++++++++------- 12 files changed, 424 insertions(+), 113 deletions(-) create mode 100644 latest/examples/subspace/subspaceMultidim.json create mode 100644 latest/examples/subspace/subspacePermute.json create mode 100644 latest/examples/transformations/affine2d2d.json create mode 100644 latest/examples/transformations/affine2d3d.json create mode 100644 latest/examples/transformations/identity.json create mode 100644 latest/examples/transformations/mapAxis1.json create mode 100644 latest/examples/transformations/mapAxis2.json create mode 100644 latest/examples/transformations/mapIndex1.json create mode 100644 latest/examples/transformations/mapIndex2.json create mode 100644 latest/examples/transformations/scale.json create mode 100644 latest/examples/transformations/translation.json 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/affine2d2d.json b/latest/examples/transformations/affine2d2d.json new file mode 100644 index 00000000..cfbd8eab --- /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..457f93a4 --- /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/identity.json b/latest/examples/transformations/identity.json new file mode 100644 index 00000000..2b488b0d --- /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/mapAxis1.json b/latest/examples/transformations/mapAxis1.json new file mode 100644 index 00000000..6ce023f7 --- /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..7932ed5f --- /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..16f70697 --- /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..e28f8f4d --- /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/scale.json b/latest/examples/transformations/scale.json new file mode 100644 index 00000000..98c633a1 --- /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/translation.json b/latest/examples/transformations/translation.json new file mode 100644 index 00000000..8ea78a9c --- /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", + "translation": [9, -1.42], + "input" : "in", + "output" : "out" + } + ] +} diff --git a/latest/index.bs b/latest/index.bs index 9b27bc22..e72ed636 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -245,13 +245,15 @@ keys as specified below for discovering certain types of data, especially images 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. -"space" metadata {#space-md} +"coordinateSystem" metadata {#coord-sys-md} -------------------------- -A "space" is a collection of "axes" / dimensions with a name and defines a coordinate system. Each space: +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". +
+ ```json { "name" : "volume_micrometers", @@ -262,19 +264,19 @@ A "space" is a collection of "axes" / dimensions with a name and defines a coord ] } ``` +
-Order of the `"axes"` list matters and defines the index of each array dimension and coordinates for "points" in that -space. For the above example, the `"x"` dimension is the first dimension. +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. -### Array space and indexing +### Array coordinate systems and indexing -Every array has a default coordinate system called "array space" 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"`. +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"`.
-For example, a 3D array space at path `/my/data/array` is: +For example, a 3D array at path `/my/data/array` defines the coordinate system: ```json { @@ -291,7 +293,7 @@ though this object should not and need not explicitly appear in metadata.
-The dimensionality of each array space equals the dimensionality of its corresponding zarr array. The axis with +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), @@ -317,6 +319,22 @@ the last dimension of an array in "C" order (row-major) are stored contiguously. 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"`. + +```json +"arrayCoordinateSystem" : { + "name" : "myDataArray", + "axes" : [ + {"name": "i", "type": "array"}, + {"name": "j", "type": "array"}, + {"name": "k", "type": "array"} + ] +} +``` + ### Coordinate convention @@ -325,27 +343,25 @@ the last dimension of an array in "C" order (row-major) are stored contiguously. It is vital to consistently define relationship between the discrete/arrray 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 space -`(0.0, 0.0)` (when the transformation is the identity). The continuous rectangle of the pixel is given by the +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)`. See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. "coordinateTransformations" metadata {#trafo-md} ------------------------------------- -"coordinateTransformations" describe map between two coordinate spaces (defined by "axes"). -For example, to map a discrete data space of an array to the corresponding physical space. +"coordinateTransformations" describe map between two coordinate systems (defined by "axes"). +For example, to map an array's discrete coordinate system to the 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` (see details). - SHOULD contain the field "input", unless part of a `sequence` (see details). - MAY contain the fields "inputAxes" and / or "outputAxes" (see below). -- MAY contain the fields "inputAxes" and "outputAxes" (see below). - 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). @@ -353,6 +369,12 @@ input space to *points* in the output space.
`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 @@ -425,17 +447,10 @@ system. `identity` transformations are invertible.
-```json -{ - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} - ], - "coordinateTransformations" : [ - { "type" : "identity", "input" : "in", "output" : "out" } - ] -} -``` +
+path: examples/transformations/identity.json
+highlight: json
+
defines the function: @@ -446,12 +461,112 @@ y = j
-`identity` transformations may be used to implement axis permutations by using the `inputAxes` and `outputAxes` -fields (see below [`inputAxes` and `outputAxes`](#inputaxes-and-outputaxes)). +#### mapIndex + +`mapIndex` transformations describe axis permutations as a reordering of the input dimensions. +Transformations MUST include a `mapIndex` field whose value is an array of integers. If the ith element of the array is j, it +means that output dimension i comes from input dimension j. The length of the `mapIndex` array MUST be equal to the +dimensionality of the output coordinate system. If the input coordinate system has dimension N, then every integer in that array +MUST be less than N. -#### axisPermutation +
+ +
+path: examples/transformations/mapIndex1.json
+highlight: json
+
-(The same as identity, but give a different type to mark what it's doing) +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 +``` + +
+ +#### 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. + +
+ +
+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 @@ -471,28 +586,20 @@ invertible.
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
-For example -```json -"coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } - { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } -], -"coordinateTransformations" : [ - { - "type": "translation", - "translation": [9, -1], - "input" : "ij", - "output" : "xy" - } -] -``` +
+ +
+path: examples/transformations/translation.json
+highlight: json
+
defines the function: ``` x = i + 9 -y = j - 1 +y = j - 1.42 ``` +
#### scale @@ -512,28 +619,20 @@ transformations are invertible.
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
-For example -```json -"coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } - { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } -], -"coordinateTransformations" : [ - { - "type": "scale", - "scale": [3, 2], - "input" : "ij", - "output" : "xy" - } -] -``` +
+ +
+path: examples/transformations/scale.json
+highlight: json
+
defines the function: ``` x = 3 * i -y = 2 * j +y = 2.12 * j ``` +
#### affine @@ -544,34 +643,22 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
path
The path to an array containing the affine parameters. - The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. - If 2D, its shape MUST be `N x M+1`.
+ The array at this path MUST be 1D, its length MUST be `N*(M+1)`.
url
An optional URL to the container in which the affine array is stored. If not provided, the provided `path` MUST exist in this container.
affine
The affine parameters stored in JSON. The matrix may be stored - as a row-major flat list of numbers the list MUST be length `N*(M+1)`. If stored as a list of - lists, the outer list MUST be length `N`, and all inner lists MUST be - length `(M+1)`. In this case, inner lists contain rows of the matrix.
+ as a row-major flat list of numbers the list MUST be length `N*(M+1)`.
A 2D-2D example: - ```json - "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" - } - ] - ``` + +
+    path: examples/transformations/affine2d2d.json
+    highlight: json
+    
defines the function: @@ -583,20 +670,10 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
An example with two dimensional inputs and three dimensional outputs. - ```json - "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" - } - ] - ``` +
+    path: examples/transformations/affine2d3d.json
+    highlight: json
+    
defines the function: @@ -605,16 +682,6 @@ The matrix may be stored as a 2D array or as a 1D array (row-major). y = 4*i + 5*j + 6 z = 7*i + 8*j + 9 ``` - - The same transformation may also be written as: - ```json - { - "type": "affine", - "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - "input" : "ij", - "output" : "xyz" - } - ```
@@ -630,17 +697,13 @@ and columns. `rotation` transformations are invertible.
path
The path to an array containing the affine parameters. - The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*N`. - If 2D, its shape MUST be `N x N`.
+ The array at this path MUST be 1D. Its length MUST be `N*N`.
url
An optional URL to the container in which the affine array is stored. If not provided, the provided `path` MUST exist in this container.
rotation
-
The rotation parameters stored in JSON. The matrix may be stored - as a row-major flat list of numbers. Int that case, the list MUST be - length `N*N`. The matrix may bes stored a list of lists, the outer list MUST be - length `N`, and all inner lists MUST be length `N`. In this case, - inner lists contain rows of the matrix.
+
The rotation parameters stored in JSON. The matrix is stored + as a row-major flat list of numbers, its length MUST be `N*N`.
@@ -734,6 +797,7 @@ x = (i + 0.1) * 2 y = (j + 0.9) * 3 ``` +and is invertible.
From de73c6891f5192067a1d048ca0f88fddf160ab52 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sun, 11 Sep 2022 16:38:59 -0400 Subject: [PATCH 020/113] update multiscales_example --- .../multiscales_example.json | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) 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 +} From 163c8f4c82554d6dcdd332c9a8d6c3fa01991fe4 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sun, 11 Sep 2022 16:40:49 -0400 Subject: [PATCH 021/113] rename dimensionWise byDimension * sequence does not have by-dimension behavior --- latest/index.bs | 125 ++++++++++-------------------------------------- 1 file changed, 25 insertions(+), 100 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index e72ed636..e4c6f5df 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -394,8 +394,7 @@ input space to *points* in the output space. 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. An empty sequence is the identity 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). @@ -408,7 +407,7 @@ input space to *points* in the output space.
`bijection` `"forward":Transform`
`"inverse":Transform`
Explicitly define an invertible transformation by providing a forward transformation and its inverse. -
`dimensionWise` +
`byDimension` `"transformations":List[Transformation]` Define a high dimensional transformation using lower dimensional transformations on subsets of dimensions. @@ -769,26 +768,24 @@ For example #### sequence -A `sequence` transformation consists of an ordered array of coordinate transformations. -`sequence` transformations are invertible if and only if every coordinate transform in the array -is invertible. +A `sequence` transformation consists of an ordered array of coordinate transformations, and is invertible if and only if every +coordinate transform in the array is invertible. -The `input` and `output` fields MAY be omitted for transformations that are in the list -of transformations of a `sequence`. +
+
transformations
+
A non-empty array of transformations.
+
+ +The `input` and `output` fields MAY be omitted for transformations that are in the list of transformations of a `sequence`.
-For example, this sequence: -```json -{ - "type" : "sequence", - "transformations" : [ - { "type": "translation", "translation" : [0.1, 0.9] }, - { "type": "scale", "scale" : [2, 3] } - ] - "input" : ["i", "j"], - "output" : ["x", "y"] -} -``` + +This sequence: + +
+path: examples/transformations/sequence.json
+highlight: json
+
describes the function @@ -801,83 +798,11 @@ and is invertible.
- -This is a valid `sequence` transformation: - -```json -{ - "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"]} - ] - } - ] -} -``` - -This is also a valid transformation: - -```json -{ - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "0"}, {"name" : "1"}, {"name" : "2"}, {"name" : "3"}] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"}, {"name" : "z"} ] } - ], - "coordinateTransformations" : [ - { - "type: "sequence", - "input" : "in", - "output" : "out", - "transformations" : [ - { "type" : "coordinates", "path" : "/coordinates", "input" : ["0","3" ], "output" : ["y", "x"]} - { "type" : "scale", "scale" : [2.0], "input" : ["1"], "output" : ["z"]} - ] - } - ] -} -``` - - -This is an invalid `sequence` transform: - -```json -{ - "spaces" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } - ], - "transforms" : [ - { - "type: "sequence", - "input" : "in", - "output" : "out", - "transformations" : [ - { "type" : "coordinates", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["z"]} - { "type" : "scale", "scale" : [2.0], "inputAxes" : ["0"], "outputAxes" : ["y"]} - ] - } - ] -} -``` - -for two reasons. First because input"0" used by the scale transformation is not an axis of the `sequence` transformation's -`input`. Second, the "x" axis of the `output` does not appear in the `output` of any child transformation. - - #### coordinates and displacements -`coordinates` and `displacements` transformations store coordinates or displacements in an array. -Applying the transformation amounts to looking up the appropriate locations in the array, and interpolating. -`coordinates` and `displacements` transformations are note invertible in general, but implementations +`coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a transformation. +Applying the transformation amounts to looking up the appropriate locations in the array and interpolating. +`coordinates` and `displacements` transformations are not invertible in general, but implementations MAY approximate their inverses. Metadata for these coordinate transforms have the following field:
@@ -918,7 +843,7 @@ For example, in 1D: { "name" : "a coordinate field transform", "type": "coordinates", - "path" : "coordinates", + "path" : "i2xCoordinates", "input" : ["i"], "output" : ["x"], "interpolation" : "nearest" @@ -1001,9 +926,9 @@ The transformation specifies linear interpolation, which in this case yields input point, hence the output is `1.0 + (-0.5) = 0.5`. -#### dimensionWise +#### byDimension -`dimensionWise` transformations build a high dimensional transformation using lower dimensional transformations +`byDimension` transformations build a high dimensional transformation using lower dimensional transformations on subsets of dimensions.
@@ -1027,7 +952,7 @@ This is a valid `dimensionWise` transformation: "transforms" : [ { "name: "in2out", - "type: "dimensionWise", + "type: "byDimension", "input" : "in", "output" : "out", "transformations" : [ @@ -1050,7 +975,7 @@ This is also a valid transformation: "transforms" : [ { "name: "in2out", - "type: "dimensionWise", + "type: "byDimension", "input" : "in", "output" : "out", "transformations" : [ From 7b35c28f87dd9517b3d6e69cd0e82bc8ca422542 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sun, 11 Sep 2022 16:41:15 -0400 Subject: [PATCH 022/113] add more transformation examples --- .../examples/transformations/coordinates1d.json | 14 ++++++++++++++ .../transformations/displacement1d.json | 14 ++++++++++++++ latest/examples/transformations/sequence.json | 17 +++++++++++++++++ .../transformations/sequenceSubspace1.json | 17 +++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 latest/examples/transformations/coordinates1d.json create mode 100644 latest/examples/transformations/displacement1d.json create mode 100644 latest/examples/transformations/sequence.json create mode 100644 latest/examples/transformations/sequenceSubspace1.json diff --git a/latest/examples/transformations/coordinates1d.json b/latest/examples/transformations/coordinates1d.json new file mode 100644 index 00000000..99a87f7e --- /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..8af48611 --- /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/sequence.json b/latest/examples/transformations/sequence.json new file mode 100644 index 00000000..99eb6311 --- /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", + "transformations" : [ + { "type": "translation", "translation" : [0.1, 0.9] }, + { "type": "scale", "scale" : [2, 3] } + ], + "input" : "in", + "output" : "out" + } + ] +} 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"]} + ] + } + ] +} From 7baeed76f64f20c9fc11164f5263f3e70e2f4b35 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sun, 11 Sep 2022 19:21:13 -0400 Subject: [PATCH 023/113] byDimension examples, rm input/outputAxes fields --- .../transformations/byDimension1.json | 17 ++ .../transformations/byDimension2.json | 17 ++ .../transformations/byDimensionInvalid1.json | 17 ++ .../transformations/byDimensionInvalid2.json | 17 ++ latest/index.bs | 233 ++++-------------- 5 files changed, 113 insertions(+), 188 deletions(-) create mode 100644 latest/examples/transformations/byDimension1.json create mode 100644 latest/examples/transformations/byDimension2.json create mode 100644 latest/examples/transformations/byDimensionInvalid1.json create mode 100644 latest/examples/transformations/byDimensionInvalid2.json diff --git a/latest/examples/transformations/byDimension1.json b/latest/examples/transformations/byDimension1.json new file mode 100644 index 00000000..44334d50 --- /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..ebf89a1c --- /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..368ecd7b --- /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..f5f26f6b --- /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/index.bs b/latest/index.bs index e4c6f5df..e60d2462 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -361,7 +361,6 @@ input space to *points* in the output space. - MUST contain any other fields required by the given "type" (see table below). - MUST contain the field "output", unless part of a `sequence` (see details). - SHOULD contain the field "input", unless part of a `sequence` (see details). -- MAY contain the fields "inputAxes" and / or "outputAxes" (see below). - 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). @@ -776,7 +775,7 @@ coordinate transform in the array is invertible.
A non-empty array of transformations.
-The `input` and `output` fields MAY be omitted for transformations that are in the list of transformations of a `sequence`. +The `input` and `output` fields SHOULD be omitted for transformations that are in the list of transformations of a `sequence`.
@@ -933,86 +932,61 @@ on subsets of dimensions.
transformations
-
A list of transformations, each of which applies to a subset of input and output dimensions (axes). +
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 appear in this parent object's `input` axes. - Every axis name in the parent dimensionWise's output MUST appear in exactly one - of its child transformations' `output`. + 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`.
-This is a valid `dimensionWise` transformation: -```json -{ - "spaces" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } - ], - "transforms" : [ - { - "name: "in2out", - "type: "byDimension", - "input" : "in", - "output" : "out", - "transformations" : [ - { "type" : "coordinates", "path" : "/coordinates", "inputAxes" : ["i"], "outputAxes" : ["x"]} - { "type" : "scale", "scale" : [2.0], "inputAxes" : ["j"], "outputAxes" : ["y"]} - ] - } - ] -} -``` +
-This is also a valid transformation: +A valid `byDimension` transformation: -```json -{ - "spaces" : [ - { "name" : "in", "axes" : [ {"name" : "0"}, {"name" : "1"}, {"name" : "2"}, {"name" : "3"}] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"}, {"name" : "z"} ] } - ], - "transforms" : [ - { - "name: "in2out", - "type: "byDimension", - "input" : "in", - "output" : "out", - "transformations" : [ - { "type" : "coordinates", "path" : "/coordinates", "input" : ["0","3" ], "output" : ["y", "x"]} - { "type" : "scale", "scale" : [2.0], "input" : ["1"], "output" : ["z"]} - ] - } - ] -} -``` +
+path: examples/transformations/byDimension1.json
+highlight: json
+
+
-This is an invalid `dimensionWise` transform: +
-```json -{ - "spaces" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } - ], - "transforms" : [ - { - "name: "in2out", - "type: "dimensionWise", - "input" : "in", - "output" : "out", - "transformations" : [ - { "type" : "coordinates", "path" : "/coordinates", "input" : ["i"], "output" : ["z"]} - { "type" : "scale", "scale" : [2.0], "input" : ["0"], "output" : ["y"]} - ] - } - ] -} -``` +Another valid `byDimension` transformation: -for two reasons. First because input"0" used by the scale transformation is not an axis of the `dimensionWise` transformation's -`input`. Second, the "x" axis of the `output` does not appear in the `output` of any child 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 @@ -1059,123 +1033,6 @@ the input and output of the `forward` and `inverse` transformations are understo
-### `inputAxes` and `outputAxes` - -`inputAxes` and `outputAxes` are optional fields for transformations to express that it applies to a subset -or reording of the axes of the input or output coordinate system, respectively. If not present, the `inputAxes` -(`outputAxes`) are understood to be a list of the `input` (`output`) coordinate systems axis names, in the -order they appear in the coordinate system's `axes` list. If present: - -- Values for `inputAxes` and `outputAxes` MUST be a list of strings. -- Every string in the `inputAxes` list MUST be the name of an axis of the transformation's input coordinate system. -- Every string in the `outputAxes` list MUST be the name of an axis of the transformation's output coordinate system. -- Every axis name in the output coordinate system MUST in the list of `outputAxes`. - -
- -An `identity` may be used to permute axes by setting -the `inputAxes` and / or `outputAxes` fields appropriately (see below). - -```json -{ - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} - ], - "coordinateTransformations" : [ - { - "type" : "identity", - "input" : "in", - "output" : "out", - "inputAxes" : ["i", "j"], - "outputAxes" : ["y", "x"] - } - ] -} -``` - -defines the function: - -``` -x = j -y = i -``` - -
- - -
- A valid example - - ```json - "coordinateSystems" : [ - { "name " : "in", "axes" : [ {"name" : "x", "name" : "y" }] } - { "name " : "out", "axes" : [ {"name" : "x", "name" : "y" }] } - ] - { - "type": "scale", - "scale" : [2], - "input" : "in" - "output" : "out", - "inputAxes" : ["y"], - "outputAxes" : ["y"] - } - ``` - scales the `y` axis only. - -
- -
- An invalid example - - ```json - "coordinateSystems" : [ - { "name " : "in", "axes" : [ {"name" : "i", "name" : "j" }] } - { "name " : "out", "axes" : [ {"name" : "x", "name" : "y" }] } - ] - "coordinateTransformations" : [ - { - "type": "scale", - "scale" : [2], - "input" : "in" - "output" : "out", - "inputAxes" : ["j"], - "outputAxes" : ["y"] - } - ] - ``` - - Is invalid because the axis name `x` of the output coordinate system does not appear in the list of `outputAxes`, - nor does it appear in the axis names of the coordinate system `in`. Instead, a `sequence` transformation can - be defined, such that every axis in the output space appears in at least one of the `outputAxes` of the - sequence's transformations. For example (asuming the same coordinate systems as above): - - ```json - { - "type": "sequence", - "transformations" : [ - { - "type": "scale", - "scale" : [2], - "inputAxes" : ["j"], - "outputAxes" : ["y"] - }, - { - "type": "identity", - "inputAxes" : ["i"], - "outputAxes" : ["x"] - } - ], - "input" : "in" - "output" : "out", - } - ``` - - is valid. - -
- - ### Points, coordinates, direction, and indexing From 162a9e9587a7b2395a03940acafb48638c197aca Mon Sep 17 00:00:00 2001 From: bogovicj Date: Thu, 15 Sep 2022 14:45:27 -0400 Subject: [PATCH 024/113] reorder, simplify sections. * flesh out some examples --- latest/index.bs | 257 +++++++++++++++++++++++------------------------- 1 file changed, 125 insertions(+), 132 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index e60d2462..35430256 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -267,14 +267,15 @@ A "coordinate system" is a collection of "axes" / dimensions with a name. Every 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. +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). -### Array coordinate systems and indexing +### 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"`. - +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: @@ -301,22 +302,22 @@ the last dimension of an array in "C" order (row-major) are stored contiguously. (column-major) order, the elements of the first dimension are stored contiguously.
- For example, if `/my/data/array/.zarray` contains: +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 - } - ``` +```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. +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 @@ -324,16 +325,14 @@ the `.zattr` metadata of the array whose value is a coordinate system object. Th `axes` MUST be equal to the dimensionality. The value of `"type"` for each object in the axes array MUST equal `"array"`. -```json -"arrayCoordinateSystem" : { - "name" : "myDataArray", - "axes" : [ - {"name": "i", "type": "array"}, - {"name": "j", "type": "array"}, - {"name": "k", "type": "array"} - ] -} -``` +
+ +
+path: examples/coordSystems/arrayCoordSys.json
+highlight: json
+
+ +
### Coordinate convention @@ -417,10 +416,100 @@ input space to *points* in the output space. Conforming readers: - SHOULD be able to apply transformations to points -- MAY enable applying transformations to points -- MAY enable applying transformations to images +- SHOULD be able to apply transformations to images + +### 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). + +
+ +The sequence transformation's input corresponds to an array coordinate system at path "/my/array". + +```json +"coordinateSystems" : [ + { "name" : "in", "axes" : [{"name" : "i"}, {"name":"j"}] }, + { "name" : "outScale", "axes" : [{"name" : "x"}, {"name":"y"}] } + { "name" : "outSeq", "axes" : [{"name" : "x"}, {"name":"y"}] } + { "name" : "outInv", "axes" : [{"name" : "x"}, {"name":"y"}] } + { "name" : "outByDim", "axes" : [{"name" : "x"}, {"name":"y"}] } +], +"coordinateTransformations" : [ + { + "type": "scale", + "input" : "in", + "output" : "outSeq" + "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"]} + ] + } +] +``` -### Transformation inverses +
+ +Coordinate transformations are 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 output to +input space. 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 should be specified using the `inverse` +transformation type, for example: + +```json +{ + "type": "inverseOf", + "transformation" : { + "name": "nonlinear-inverse", + "type": "displacements", + "path": "/path/to/my/transform", + } +} +``` 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 @@ -428,6 +517,9 @@ operation is requested that requires the inverse of a transformation that can no implementations MAY estimate an inverse, or MAY output a warning that the requested operation is unsupported. + + + ### Transformation types Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value @@ -627,8 +719,8 @@ highlight: json defines the function: ``` -x = 3 * i -y = 2.12 * j +x = 3.12 * i +y = 2 * j ```
@@ -1034,105 +1126,6 @@ the input and output of the `forward` and `inverse` transformations are understo -### Points, coordinates, direction, and indexing - -Coordinate transformations are 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 transformation: - -```json -{ - "type": "scale", - "scale": [2, 0.5] - "input" : ["i", "j"] - "output" : ["x", "y"] -} -``` - -defines the function: - -``` -x = 2 * i -y = 0.5 * 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 output to -input space. 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 should be specified using the `inverse` -transformation type, for example: - -```json -{ - "type": "inverseOf", - "transformation" : { - "name": "nonlinear-inverse", - "type": "displacements", - "path": "/path/to/my/transform", - } -} -``` - -### Spaces and axes - -Every coordinate transformation MUST specify its output coordinate system using `output` to with a string value -corresponding to the name of an output space or with an array of strings to provide a list of axis names. -Similarly, every coordinate transformation SHOULD specify its input coordinate system using the `input`. If -`input` is not provided the transformation's input is in array-space. - -Coordinate transformations may be defined as sequences of transformations, each of which applies to a subspace. -Which subspace should be defined by providing arrays of strings as the values for the `input` and `output` -fields. An axis name MUST appear in exactly one `output` lists. - - -
-A simple example -```json -{ - "type" : "sequence", - "transformations" : [ - { - "type": "scale", - "scale" : [ 0.5, 0.5 ], - "input" : ["dim_1", "dim_2"], - "output" : ["x", "y"], - }, - { - "type": "scale", - "scale" : [ 2 ], - "input" : ["dim_0"], - "output" : ["t"], - } - ] -} -``` - -```json -{ - "type" : "sequence", - "transformations" : [ - { - "type": "inverseOf", - "transformation" : { - "name": "nonlinear-inverse", - "type": "displacements", - "path": "/path/to/my/transform", - "input" : ["i", "j"], - "output" : ["x", "y"], - }, - }, - { - "type": "scale", - "scale" : [ 2 ] - "input" : ["k"], - "output" : ["z"], - } - ] -} -``` -
- "multiscales" metadata {#multiscale-md} --------------------------------------- From a90662d5dfaa80f81be05057da0f91d092033b61 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Thu, 15 Sep 2022 15:44:47 -0400 Subject: [PATCH 025/113] clean up example formatting --- .../examples/transformations/affine2d2d.json | 12 ++++---- .../examples/transformations/affine2d3d.json | 16 +++++----- .../transformations/byDimension1.json | 20 ++++++------- .../transformations/byDimension2.json | 18 +++++------ .../transformations/byDimensionInvalid1.json | 20 ++++++------- .../transformations/byDimensionInvalid2.json | 20 ++++++------- .../transformations/coordinates1d.json | 18 +++++------ .../transformations/displacement1d.json | 18 +++++------ latest/examples/transformations/identity.json | 10 +++---- latest/examples/transformations/mapAxis1.json | 30 +++++++++---------- latest/examples/transformations/mapAxis2.json | 30 +++++++++---------- .../examples/transformations/mapIndex1.json | 30 +++++++++---------- .../examples/transformations/mapIndex2.json | 30 +++++++++---------- latest/examples/transformations/scale.json | 12 ++++---- latest/examples/transformations/sequence.json | 22 +++++++------- .../examples/transformations/translation.json | 12 ++++---- 16 files changed, 159 insertions(+), 159 deletions(-) diff --git a/latest/examples/transformations/affine2d2d.json b/latest/examples/transformations/affine2d2d.json index cfbd8eab..f85e3b36 100644 --- a/latest/examples/transformations/affine2d2d.json +++ b/latest/examples/transformations/affine2d2d.json @@ -1,14 +1,14 @@ { "coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name" : "j"}] }, - { "name" : "xy", "axes" : [{"name" : "x"}, {"name" : "y"}] } + { "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" + "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 index 457f93a4..be45fd1b 100644 --- a/latest/examples/transformations/affine2d3d.json +++ b/latest/examples/transformations/affine2d3d.json @@ -1,14 +1,14 @@ { - "coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name" : "j"}] }, - { "name" : "xyz", "axes" : [{"name" : "x"}, {"name" : "y"}, {"name" : "z"}] } + "coordinateSystems": [ + { "name": "ij", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "xyz", "axes": [{"name": "x"}, {"name": "y"}, {"name": "z"}] } ], - "coordinateTransformations" : [ + "coordinateTransformations": [ { - "type" : "affine", - "affine" : [1, 2, 3, 4, 5, 6, 7, 8, 9], - "input" : "ij", - "output" : "xyz" + "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 index 44334d50..a42ed37a 100644 --- a/latest/examples/transformations/byDimension1.json +++ b/latest/examples/transformations/byDimension1.json @@ -1,16 +1,16 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } ], - "coordinateTransformations" : [ + "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"]} + "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 index ebf89a1c..0568c9bd 100644 --- a/latest/examples/transformations/byDimension2.json +++ b/latest/examples/transformations/byDimension2.json @@ -1,16 +1,16 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"}, {"name" : "k"}, {"name" : "l"}] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"}, {"name" : "z"} ] } + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"}, {"name": "k"}, {"name": "l"}] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"}, {"name": "z"} ] } ], - "coordinateTransformations" : [ + "coordinateTransformations": [ { - "type" : "byDimension", - "input" : "in", - "output" : "out", + "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"]} + { "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 index 368ecd7b..8d3c9696 100644 --- a/latest/examples/transformations/byDimensionInvalid1.json +++ b/latest/examples/transformations/byDimensionInvalid1.json @@ -1,16 +1,16 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } ], - "coordinateTransformations" : [ + "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"]} + "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 index f5f26f6b..fdd3ac4b 100644 --- a/latest/examples/transformations/byDimensionInvalid2.json +++ b/latest/examples/transformations/byDimensionInvalid2.json @@ -1,16 +1,16 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ] }, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ] } + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } ], - "coordinateTransformations" : [ + "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"]} + "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 index 99a87f7e..314bc6fb 100644 --- a/latest/examples/transformations/coordinates1d.json +++ b/latest/examples/transformations/coordinates1d.json @@ -1,14 +1,14 @@ { - "coordinateSystems" : [ - { "name" : "i", "axes" : [{"name" : "i"}] }, - { "name" : "x", "axes" : [{"name" : "x"}] } + "coordinateSystems": [ + { "name": "i", "axes": [{"name": "i"}] }, + { "name": "x", "axes": [{"name": "x"}] } ], - "coordinateTransformations" : [{ - "name" : "a coordinate field transform", + "coordinateTransformations": [{ + "name": "a coordinate field transform", "type": "coordinates", - "path" : "i2xCoordinates", - "input" : "i", - "output" : "x", - "interpolation" : "nearest" + "path": "i2xCoordinates", + "input": "i", + "output": "x", + "interpolation": "nearest" }] } diff --git a/latest/examples/transformations/displacement1d.json b/latest/examples/transformations/displacement1d.json index 8af48611..5db76446 100644 --- a/latest/examples/transformations/displacement1d.json +++ b/latest/examples/transformations/displacement1d.json @@ -1,14 +1,14 @@ { - "coordinateSystems" : [ - { "name" : "i", "axes" : [{"name" : "i"}] }, - { "name" : "x", "axes" : [{"name" : "x"}] } + "coordinateSystems": [ + { "name": "i", "axes": [{"name": "i"}] }, + { "name": "x", "axes": [{"name": "x"}] } ], - "coordinateTransformations" : [{ - "name" : "a displacement field transform", + "coordinateTransformations": [{ + "name": "a displacement field transform", "type": "displacements", - "path" : "i2xCoordinates", - "input" : "i", - "output" : "x", - "interpolation" : "nearest" + "path": "i2xCoordinates", + "input": "i", + "output": "x", + "interpolation": "nearest" }] } diff --git a/latest/examples/transformations/identity.json b/latest/examples/transformations/identity.json index 2b488b0d..3ea1529a 100644 --- a/latest/examples/transformations/identity.json +++ b/latest/examples/transformations/identity.json @@ -1,9 +1,9 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} ], - "coordinateTransformations" : [ - { "type" : "identity", "input" : "in", "output" : "out" } + "coordinateTransformations": [ + { "type": "identity", "input": "in", "output": "out" } ] } diff --git a/latest/examples/transformations/mapAxis1.json b/latest/examples/transformations/mapAxis1.json index 6ce023f7..2b6b0b79 100644 --- a/latest/examples/transformations/mapAxis1.json +++ b/latest/examples/transformations/mapAxis1.json @@ -1,23 +1,23 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, - { "name" : "out1", "axes" : [ {"name" : "x"}, {"name" : "y"} ]}, - { "name" : "out2", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out1", "axes": [ {"name": "x"}, {"name": "y"} ]}, + { "name": "out2", "axes": [ {"name": "x"}, {"name": "y"} ]} ], - "coordinateTransformations" : [ + "coordinateTransformations": [ { - "name" : "equivalent to identity", - "type" : "mapAxis", - "mapAxis" : { "x":"i", "y":"j" }, - "input" : "in", - "output" : "out1" + "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" + "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 index 7932ed5f..c8336aef 100644 --- a/latest/examples/transformations/mapAxis2.json +++ b/latest/examples/transformations/mapAxis2.json @@ -1,23 +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"} ]} + "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" : [ + "coordinateTransformations": [ { - "name" : "projection down", - "type" : "mapAxis", - "mapAxis" : { "x" : "b" }, - "input" : "in", - "output" : "out_down" + "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" + "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 index 16f70697..db785459 100644 --- a/latest/examples/transformations/mapIndex1.json +++ b/latest/examples/transformations/mapIndex1.json @@ -1,23 +1,23 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, - { "name" : "out1", "axes" : [ {"name" : "x"}, {"name" : "y"} ]}, - { "name" : "out2", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out1", "axes": [ {"name": "x"}, {"name": "y"} ]}, + { "name": "out2", "axes": [ {"name": "x"}, {"name": "y"} ]} ], - "coordinateTransformations" : [ + "coordinateTransformations": [ { - "name" : "equivalent to identity", - "type" : "mapIndex", - "mapIndex" : [ 0, 1 ], - "input" : "in", - "output" : "out1" + "name": "equivalent to identity", + "type": "mapIndex", + "mapIndex": [0, 1], + "input": "in", + "output": "out1" }, { - "name" : "permutation", - "type" : "mapIndex", - "mapIndex" : [ 1, 0 ], - "input" : "in", - "output" : "out2" + "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 index e28f8f4d..411828de 100644 --- a/latest/examples/transformations/mapIndex2.json +++ b/latest/examples/transformations/mapIndex2.json @@ -1,23 +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"} ]} + "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" : [ + "coordinateTransformations": [ { - "name" : "projection down", - "type" : "mapIndex", - "mapIndex" : [ 1 ], - "input" : "in", - "output" : "out_down" + "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" + "name": "projection up", + "type": "mapIndex", + "mapIndex": [0, 1, 1], + "input": "in", + "output": "out_up" } ] } diff --git a/latest/examples/transformations/scale.json b/latest/examples/transformations/scale.json index 98c633a1..b5c83309 100644 --- a/latest/examples/transformations/scale.json +++ b/latest/examples/transformations/scale.json @@ -1,14 +1,14 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [{"name" : "i"}, {"name":"j"}] }, - { "name" : "out", "axes" : [{"name" : "x"}, {"name":"y"}] } + "coordinateSystems": [ + { "name": "in", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "out", "axes": [{"name": "x"}, {"name": "y"}] } ], - "coordinateTransformations" : [ + "coordinateTransformations": [ { "type": "scale", "scale": [3.12, 2], - "input" : "in", - "output" : "out" + "input": "in", + "output": "out" } ] } diff --git a/latest/examples/transformations/sequence.json b/latest/examples/transformations/sequence.json index 99eb6311..1d88b21d 100644 --- a/latest/examples/transformations/sequence.json +++ b/latest/examples/transformations/sequence.json @@ -1,17 +1,17 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j"} ]}, - { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y"} ]} + "coordinateSystems": [ + { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, + { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} ], - "coordinateTransformations" : [ + "coordinateTransformations": [ { - "type" : "sequence", - "transformations" : [ - { "type": "translation", "translation" : [0.1, 0.9] }, - { "type": "scale", "scale" : [2, 3] } - ], - "input" : "in", - "output" : "out" + "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/translation.json b/latest/examples/transformations/translation.json index 8ea78a9c..cbc32ec9 100644 --- a/latest/examples/transformations/translation.json +++ b/latest/examples/transformations/translation.json @@ -1,14 +1,14 @@ { - "coordinateSystems" : [ - { "name" : "in", "axes" : [{"name" : "i"}, {"name":"j"}] }, - { "name" : "out", "axes" : [{"name" : "x"}, {"name":"y"}] } + "coordinateSystems": [ + { "name": "in", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "out", "axes": [{"name": "x"}, {"name": "y"}] } ], "coordinateTransformations" : [ { "type": "translation", - "translation": [9, -1.42], - "input" : "in", - "output" : "out" + "input": "in", + "output": "out", + "translation": [9, -1.42] } ] } From 32656e557ed62e47e48e4eadda5f6285adf77b20 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 16 Sep 2022 15:25:50 -0400 Subject: [PATCH 026/113] add array coordinate system example --- latest/examples/coordSystems/arrayCoordSys.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 latest/examples/coordSystems/arrayCoordSys.json 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"} + ] + } +} From 0245ca95216b7802bfdeee766b2c2c361175f6c7 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Fri, 23 Sep 2022 09:11:40 -0400 Subject: [PATCH 027/113] rm duplicate editor names, typo fix --- latest/index.bs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 35430256..57352551 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -13,13 +13,10 @@ Local Boilerplate: header yes Local Boilerplate: copyright yes 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/ 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. @@ -483,7 +480,7 @@ The sequence transformation's input corresponds to an array coordinate system at -Coordinate transformations are are functions of *points* in the input space to *points* in the output space. We call this the "forward" direction. +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: From a3ff0b5aabbb73d75b1a5643ff5eaace44df10b5 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 23 Sep 2022 11:01:41 -0400 Subject: [PATCH 028/113] fix sequence example json. add inverseOf to input/output field exception --- latest/index.bs | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 57352551..d373c629 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -347,7 +347,7 @@ half-open interval `[-0.5, 0.5) x [-0.5, 0.5)`. See chapter 4 and figure 4.1 of "coordinateTransformations" metadata {#trafo-md} ------------------------------------- -"coordinateTransformations" describe map between two coordinate systems (defined by "axes"). +"coordinateTransformations" describe the mapping between two coordinate systems (defined by "axes"). For example, to map an array's discrete coordinate system to the 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. @@ -355,8 +355,8 @@ 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` (see details). -- SHOULD contain the field "input", unless part of a `sequence` (see details). +- 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). @@ -436,26 +436,26 @@ The sequence transformation's input corresponds to an array coordinate system at ```json "coordinateSystems" : [ { "name" : "in", "axes" : [{"name" : "i"}, {"name":"j"}] }, - { "name" : "outScale", "axes" : [{"name" : "x"}, {"name":"y"}] } - { "name" : "outSeq", "axes" : [{"name" : "x"}, {"name":"y"}] } - { "name" : "outInv", "axes" : [{"name" : "x"}, {"name":"y"}] } + { "name" : "outScale", "axes" : [{"name" : "x"}, {"name":"y"}] }, + { "name" : "outSeq", "axes" : [{"name" : "x"}, {"name":"y"}] }, + { "name" : "outInv", "axes" : [{"name" : "x"}, {"name":"y"}] }, { "name" : "outByDim", "axes" : [{"name" : "x"}, {"name":"y"}] } ], "coordinateTransformations" : [ { "type": "scale", "input" : "in", - "output" : "outSeq" - "scale" : [ 0.5, 1.2 ], + "output" : "outSeq", + "scale" : [ 0.5, 1.2 ] }, { "type" : "sequence", "input" : "/my/array", - "output" : "outSeq" + "output" : "outSeq", "transformations" : [ { "type": "scale", "scale" : [ 0.5, 0.6 ] }, { "type": "translation", "translation" : [ 2, 5 ] } - ], + ] }, { "type": "inverseOf", @@ -492,18 +492,17 @@ 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 output to -input space. 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 should be specified using the `inverse` +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 should be specified using the `inverseOf` transformation type, for example: ```json { "type": "inverseOf", "transformation" : { - "name": "nonlinear-inverse", "type": "displacements", - "path": "/path/to/my/transform", + "path": "/path/to/displacements", } } ``` @@ -514,9 +513,6 @@ operation is requested that requires the inverse of a transformation that can no implementations MAY estimate an inverse, or MAY output a warning that the requested operation is unsupported. - - - ### Transformation types Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value From 0df402d185120ce166ac5c318c35039b8a999fe2 Mon Sep 17 00:00:00 2001 From: bogovicj Date: Fri, 23 Sep 2022 11:38:16 -0400 Subject: [PATCH 029/113] be clearer re: half-open interval --- latest/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/latest/index.bs b/latest/index.bs index d373c629..2f7bb1a3 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -341,7 +341,7 @@ coordinate systems. A pixel/voxel is the continuous region (rectangle) that corr 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)`. See chapter 4 and figure 4.1 of the ITK Software Guide [[itk]]. +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} From f05d480941ebb0239e077977232046c743a8d2cc Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 3 Oct 2022 20:34:37 -0400 Subject: [PATCH 030/113] move axes section into coordinateSystems section --- latest/index.bs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 57352551..01f56e1e 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -228,26 +228,13 @@ 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 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' - -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. - -"coordinateSystem" metadata {#coord-sys-md} +"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". +- MUST contain the field "axes", whose value is a valid set of "axes" (see below).
@@ -267,6 +254,18 @@ The order of the `"axes"` list matters and defines the index of each array dimen 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' + +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. + ### Array coordinate systems From ed17ed5f36181acd2c2185a5da804aef1ff4cdc7 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 3 Oct 2022 20:50:37 -0400 Subject: [PATCH 031/113] typo fixes --- latest/index.bs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 01f56e1e..6ff965dd 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -335,7 +335,7 @@ highlight: json **The pixel/voxel center is the origin of the continuous coordinate system.** -It is vital to consistently define relationship between the discrete/arrray and continuous/interpolated +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 @@ -514,8 +514,6 @@ implementations MAY estimate an inverse, or MAY output a warning that the reques - - ### Transformation types Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value @@ -658,18 +656,18 @@ z = b `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. `translation` transformations are +identical and MUST equal the the length of the "translation" array (N). `translation` transformations are invertible.
path
-
The path to an array containing the affine parameters. +
The path to a zarr-array containing the translation parameters. The array at this path MUST be 1D, and its length MUST be `N`.
url
-
An optional URL to the container in which the affine array is stored. If not provided, +
An optional URL to the container in which the translation zarr-array is stored. If not provided, the provided `path` MUST exist in this container.
scale
-
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
@@ -691,18 +689,18 @@ y = j - 1.42 `scale` transformations are special cases of affine transformations. When possible, a scale transformation SHOULD be defined to its equivalent affine. Input and output dimensionality MUST be identical and MUST equal -the the length of the "scale" array. Values in the `scale` array SHOULD be non-zero; in that case, `scale` +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 an array containing the affine parameters. +
The path to a zarr-array containing the scale parameters. The array at this path MUST be 1D, and its length MUST be `N`.
url
-
An optional URL to the container in which the affine array is stored. If not provided, +
An optional URL to the container in which the scale zarr-array is stored. If not provided, the provided `path` MUST exist in this container.
scale
-
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
+
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
@@ -728,10 +726,10 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
path
-
The path to an array containing the affine parameters. +
The path to a zarr-array containing the affine parameters. The array at this path MUST be 1D, its length MUST be `N*(M+1)`.
url
-
An optional URL to the container in which the affine array is stored. If not provided, +
An optional URL to the container in which the affine zarr-array is stored. If not provided, the provided `path` MUST exist in this container.
affine
The affine parameters stored in JSON. The matrix may be stored From cd01ab7eb76e33dfaf603b67cba515b3802f5b49 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 3 Oct 2022 21:26:33 -0400 Subject: [PATCH 032/113] add two new coordinate transformation examples * multiscales dataset-level * xarray like coordinates / byDimension --- .../multiscales_example_relative.json | 61 +++++++++++++++++++ .../examples/transformations/xarrayLike.json | 17 ++++++ 2 files changed, 78 insertions(+) create mode 100644 latest/examples/multiscales_strict/multiscales_example_relative.json create mode 100644 latest/examples/transformations/xarrayLike.json 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/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"] } + ] + } + ] +} From 86672e655b881d9759c042a51c0414a71cb83477 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Tue, 4 Oct 2022 20:21:54 +0200 Subject: [PATCH 033/113] Allow tests to access all schemas --- latest/tests/test_validation.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/latest/tests/test_validation.py b/latest/tests/test_validation.py index 83f49911..fed62a13 100644 --- a/latest/tests/test_validation.py +++ b/latest/tests/test_validation.py @@ -44,6 +44,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"): @@ -54,7 +59,7 @@ def pytest_generate_tests(metafunc): schema = json.load(f) for test in suite["tests"]: ids.append("validate_" + str(test["formerly"]).split("/")[-1][0:-5]) - suites.append(Suite(schema, {schema["$id"]: schema}, test["data"], test["valid"])) + suites.append(Suite(schema, schema_store, test["data"], test["valid"])) # Examples for config_filename in glob.glob("examples/*/.config.json"): @@ -69,7 +74,7 @@ def pytest_generate_tests(metafunc): data = ''.join(line for line in f if not line.lstrip().startswith('//')) data = json.loads(data) ids.append("example_" + str(filename).split("/")[-1][0:-5]) - suites.append(Suite(schema, {schema["$id"]: schema}, data, True)) # Assume true + suites.append(Suite(schema, schema_store, data, True)) # Assume true metafunc.parametrize("suite", suites, ids=ids, indirect=True) From b34875dff77c91320e91b9d36f0ad49d8995a454 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Tue, 4 Oct 2022 21:22:43 +0200 Subject: [PATCH 034/113] Fix how schemas are matched to examples --- latest/examples/transformations/.config.json | 3 +++ latest/tests/test_validation.py | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 latest/examples/transformations/.config.json 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/tests/test_validation.py b/latest/tests/test_validation.py index fed62a13..2799f13b 100644 --- a/latest/tests/test_validation.py +++ b/latest/tests/test_validation.py @@ -1,5 +1,6 @@ import json import glob +from pathlib import Path from dataclasses import dataclass from typing import List @@ -62,13 +63,14 @@ def pytest_generate_tests(metafunc): suites.append(Suite(schema, schema_store, test["data"], test["valid"])) # Examples - for config_filename in glob.glob("examples/*/.config.json"): + for config_filename in Path(".").glob("examples/*/.config.json"): + print(config_filename) with open(config_filename) as o: data = json.load(o) schema = data["schema"] with open(schema) as f: schema = json.load(f) - for filename in glob.glob("examples/*/*.json"): + for filename in config_filename.parent.glob("*.json"): with open(filename) as f: # Strip comments data = ''.join(line for line in f if not line.lstrip().startswith('//')) From 35ea3e38568e9f6c685ac87f5606cd8760b1f742 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Tue, 4 Oct 2022 21:23:09 +0200 Subject: [PATCH 035/113] Add initial coordinate system schemas --- latest/schemas/axes.schema | 45 ++++++ latest/schemas/coordinate_transforms.schema | 144 ++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 latest/schemas/axes.schema create mode 100644 latest/schemas/coordinate_transforms.schema diff --git a/latest/schemas/axes.schema b/latest/schemas/axes.schema new file mode 100644 index 00000000..c5eed1b8 --- /dev/null +++ b/latest/schemas/axes.schema @@ -0,0 +1,45 @@ +{ + "$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, + "items": { + "$ref": "#/$defs/axis" + }, + "$defs": { + "axis": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the axis" + }, + "type": { + "type": "string", + "enum": [ + "array", + "space", + "time", + "channel", + "coordinate", + "displacement" + ], + "description": "Dimension of the axis" + }, + "discrete": { + "type": "boolean", + "description": "Whether the dimension is discrete" + }, + "units": { + "type": "string", + "description": "Units for the axis" + } + }, + "required": [ + "name" + ] + } + } +} diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema new file mode 100644 index 00000000..d4fe5aeb --- /dev/null +++ b/latest/schemas/coordinate_transforms.schema @@ -0,0 +1,144 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ngff.openmicroscopy.org/latest/schemas/coordinate_transforms.schema", + "title": "NGFF Coordinate Systems and Transforms", + "description": "Coordinate Systems and transforms for OME-NGFF", + "type": "object", + "properties": { + "coordinateSystems": { + "type": "array", + "uniqueItems": true, + "items": { + "$ref": "#/$defs/coordinateSystem" + } + }, + "coordinateTransformations": { + "type": "array", + "uniqueItems": true, + "items": { + "allOf": [ + { + "$ref": "#/$defs/coordinateTransformation" + }, + { + "type": "object", + "properties": { + "input": { + "type": "string" + }, + "output": { + "type": "string" + } + }, + "required": [ + "input", + "output" + ] + } + ] + } + } + }, + "$defs": { + "coordinateSystem": { + "description": "Coordinate Systems for OME-NGFF", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of coordinate space" + }, + "axes": { + "$ref": "axes.schema" + } + }, + "required": [ + "name", + "axes" + ] + }, + "coordinateTransformation": { + "description": "OME-NGFF coordinate transformation.", + "allOf": [ + { + "type": "object", + "properties": { + "name": {"type": "string"}, + "type": {"type": "string"} + }, + "required": [ + "type" + ] + }, + { + "oneOf": [ + { + "$ref": "#/$defs/identity" + }, + { + "$ref": "#/$defs/scale" + }, + { + "$ref": "#/$defs/translation" + } + ] + } + ] + }, + "identity": { + "type": "object", + "properties": { + "type": { + "const": "identity" + } + } + }, + "scale": { + "type": "object", + "properties": { + "type": { + "const": "scale" + }, + "oneOf": { + "path": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "scale": { + "type": "array", + "items": { + "type": "number", + "exclusiveMinimum": 0 + } + } + } + } + }, + "translation": { + "type": "object", + "properties": { + "type": { + "const": "translation" + }, + "oneOf": { + "path": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "translation": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + } + } +} From 3fcba533b2a784ceb4200f7881822b586a1bb335 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Tue, 4 Oct 2022 22:04:55 +0200 Subject: [PATCH 036/113] Add array coordinate schema --- latest/schemas/coordinate_transforms.schema | 38 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index d4fe5aeb..8192b773 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -37,6 +37,36 @@ } ] } + }, + "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" + ] } }, "$defs": { @@ -63,8 +93,12 @@ { "type": "object", "properties": { - "name": {"type": "string"}, - "type": {"type": "string"} + "name": { + "type": "string" + }, + "type": { + "type": "string" + } }, "required": [ "type" From 883ff1c315956f873b52bb5366388654d57e9af0 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Tue, 4 Oct 2022 22:07:16 +0200 Subject: [PATCH 037/113] Add test for array coordinate system --- latest/examples/coordSystems/.config.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 latest/examples/coordSystems/.config.json 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" +} From c2a18c44bfcfb4692e1fc4f0ff9f75639d76d176 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Tue, 4 Oct 2022 22:07:39 +0200 Subject: [PATCH 038/113] Stop adding cases for .config files --- latest/tests/test_validation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/latest/tests/test_validation.py b/latest/tests/test_validation.py index 2799f13b..91c6a65f 100644 --- a/latest/tests/test_validation.py +++ b/latest/tests/test_validation.py @@ -71,6 +71,8 @@ def pytest_generate_tests(metafunc): with open(schema) as f: schema = json.load(f) for filename in config_filename.parent.glob("*.json"): + if filename.name.startswith(".config"): + continue with open(filename) as f: # Strip comments data = ''.join(line for line in f if not line.lstrip().startswith('//')) From 027b55bdeff20f0e3634c903923faf900a33cb29 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 4 Oct 2022 20:54:30 -0400 Subject: [PATCH 039/113] describe where to store coordinateTransformations --- latest/index.bs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/latest/index.bs b/latest/index.bs index 649ed411..4786d9ce 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -347,7 +347,7 @@ half-open interval `[-0.5, 0.5) x [-0.5, 0.5)` (i.e., -0.5 is included, +0.5 is ------------------------------------- "coordinateTransformations" describe the mapping between two coordinate systems (defined by "axes"). -For example, to map an array's discrete coordinate system to the corresponding physical coordinates. +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. @@ -414,6 +414,37 @@ Conforming readers: - 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]]), +and MUST be duplicated in the atrributes of the zarr array. 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
+│
+├── .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 From 5bfe44245cd1d8e0f99d6ce2383fdcd809312f2b Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 5 Oct 2022 10:17:12 -0400 Subject: [PATCH 040/113] corrections for coordinates and displacements --- latest/index.bs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 4786d9ce..aeb9fe27 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -968,7 +968,7 @@ if the arrray in `coordinates` contains the data: `[-9, 9, 0]`, then this metada ``` x = - if ( i < 0.5 ) -9 + if ( i > -0.5 and i < 0.5 ) -9 else if ( i < 1.5 ) 9 else 0 ``` @@ -977,16 +977,16 @@ Example metadata for the array data at path `coordinates` above: ``` { - "spaces" : [ + "coordinateSystems" : [ { "name" : "a coordinate field transform", "axes" : [ - { "label": "i", "type": "space", "discrete": true }, - { "label": "c", "type": "coordinate", "discrete": true } + { "name": "i", "type": "space", "discrete": true }, + { "name": "c", "type": "coordinate", "discrete": true } ] } ], - "transformations" : [ + "coordinateTransformations" : [ { "type" : "identity", "output" : "a coordinate field transform" @@ -1013,16 +1013,16 @@ Example metadata for the array data at path `displacements` above: ``` { - "spaces" : [ + "coordinateSystems" : [ { "name" : "a coordinate field transform", "axes" : [ - { "label": "x", "type": "space", "unit" : "nanometer" }, - { "label": "d", "type": "displacement", "discrete": true } + { "name": "x", "type": "space", "unit" : "nanometer" }, + { "name": "d", "type": "displacement", "discrete": true } ] } ], - "transformations" : [ + "coordinateTransformations" : [ { "type" : "scale", "scale" : [2, 1], From aab746956f574d234c41c330da83ee622a570cdf Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Thu, 6 Oct 2022 11:26:07 -0400 Subject: [PATCH 041/113] add optional "longName" field for axes see #142 --- latest/index.bs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/latest/index.bs b/latest/index.bs index aeb9fe27..3b1943a8 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -263,9 +263,27 @@ is indicated by the length of its "axes" array. The "volume_micrometers" exampl - 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 From 098bfb8d5f931268eefac2af3ec13cb41b183dec Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 15:36:01 +0200 Subject: [PATCH 042/113] Add longName property to axes schema --- latest/schemas/axes.schema | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/latest/schemas/axes.schema b/latest/schemas/axes.schema index c5eed1b8..083ffcac 100644 --- a/latest/schemas/axes.schema +++ b/latest/schemas/axes.schema @@ -16,6 +16,10 @@ "type": "string", "description": "Name of the axis" }, + "longName": { + "type": "string", + "description": "Longer name or description of the axis." + }, "type": { "type": "string", "enum": [ From f04ebef79981835c9e0b6cef555da5022815c650 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:15:53 +0200 Subject: [PATCH 043/113] Add mapAxis and mapIndex schemas --- latest/schemas/coordinate_transforms.schema | 44 +++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 8192b773..1e7a5fb9 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -109,6 +109,12 @@ { "$ref": "#/$defs/identity" }, + { + "$ref": "#/$defs/mapIndex" + }, + { + "$ref": "#/$defs/mapAxis" + }, { "$ref": "#/$defs/scale" }, @@ -127,6 +133,44 @@ } } }, + "mapIndex": { + "type": "object", + "description": "Permute axes by position", + "properties": { + "type": { + "const": "mapIndex" + }, + "mapIndex": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "required": [ + "mapIndex" + ] + }, + "mapAxis": { + "type": "object", + "description": "Permute axes by name", + "properties": { + "type": { + "const": "mapAxis" + }, + "mapAxis": { + "type": "object", + "patternProperties": { + ".*": { + "type": "string" + } + } + }, + "required": [ + "mapAxis" + ] + } + }, "scale": { "type": "object", "properties": { From ead1f7be1828f48acd5f6d6a1816cd3e266f0852 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:37:49 +0200 Subject: [PATCH 044/113] Add affine transfrom, use reference for path_w_url --- latest/schemas/coordinate_transforms.schema | 107 +++++++++++++++----- 1 file changed, 79 insertions(+), 28 deletions(-) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 1e7a5fb9..a702bb6b 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -70,6 +70,22 @@ } }, "$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" + ] + }, "coordinateSystem": { "description": "Coordinate Systems for OME-NGFF", "type": "object", @@ -120,6 +136,9 @@ }, { "$ref": "#/$defs/translation" + }, + { + "$ref": "#/$defs/affine" } ] } @@ -176,47 +195,79 @@ "properties": { "type": { "const": "scale" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" }, - "oneOf": { - "path": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "scale": { - "type": "array", - "items": { - "type": "number", - "exclusiveMinimum": 0 + { + "properties": { + "scale": { + "type": "array", + "items": { + "type": "number", + "exclusiveMinimum": 0 + } } - } + }, + "required": [ + "scale" + ] } - } + ] }, "translation": { "type": "object", "properties": { "type": { "const": "translation" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" }, - "oneOf": { - "path": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "translation": { - "type": "array", - "items": { - "type": "number" + { + "properties": { + "translation": { + "type": "array", + "items": { + "type": "number" + } } + }, + "required": [ + "translation" + ] + } + ] + }, + "affine": { + "type": "object", + "properties": { + "type": { + "const": "affine" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" + }, + { + "properties": { + "affine": { + "type": "array", + "items": { + "type": "number" + } + }, + "required": [ + "affine" + ] } } - } + ] } } } From 281df32956be52800e316afb45891c43db1f4030 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:48:50 +0200 Subject: [PATCH 045/113] Move rotation example to example directory --- latest/examples/transformations/rotation.json | 14 ++++++++++++++ latest/index.bs | 19 +++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 latest/examples/transformations/rotation.json 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/index.bs b/latest/index.bs index 3b1943a8..37ff9fad 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -839,20 +839,11 @@ and columns. `rotation` transformations are invertible.
A 2D example - ```json - "coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } - { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } - ], - "coordinateTransformations" : [ - { - "type": "rotation", - "affine": [0, -1, 1, 0], - "input" : "ij", - "output" : "xy" - } - ] - ``` + +
+    path: examples/transformations/rotation.json
+    highlight: json
+    
defines the function: From 87d78e8c03331f5279be7839eb28cae383458c20 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:51:19 +0200 Subject: [PATCH 046/113] Add schema for rotation --- latest/schemas/coordinate_transforms.schema | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index a702bb6b..5b9373b8 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -139,6 +139,9 @@ }, { "$ref": "#/$defs/affine" + }, + { + "$ref": "#/$defs/rotation" } ] } @@ -268,6 +271,32 @@ } } ] + }, + "rotation": { + "type": "object", + "properties": { + "type": { + "const": "rotation" + } + }, + "oneOf": [ + { + "$ref": "#/$defs/path_w_url" + }, + { + "properties": { + "rotation": { + "type": "array", + "items": { + "type": "number" + } + }, + "required": [ + "rotation" + ] + } + } + ] } } } From 033d4bba282426bdba0cde07d1230534dd8419c7 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:59:06 +0200 Subject: [PATCH 047/113] Move inverseOf example to examples directory --- .../examples/transformations/inverseOf.json | 14 ++++++++++++++ latest/index.bs | 19 +++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 latest/examples/transformations/inverseOf.json 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/index.bs b/latest/index.bs index 37ff9fad..bfcf2c27 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -873,20 +873,11 @@ transformation (if it exists).
For example -```json -"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", - } -] -``` +
+    path: examples/transformations/inverseOf.json
+    highlight: json
+    
+
#### sequence From f78e4411cdf2e2e7c66a48653cc7bb8b4b32b772 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 17:00:58 +0200 Subject: [PATCH 048/113] Added schema for inverseOf Note that the test currently does not pass because displacements have not been defined yet. --- latest/schemas/coordinate_transforms.schema | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 5b9373b8..315c2248 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -142,6 +142,9 @@ }, { "$ref": "#/$defs/rotation" + }, + { + "$ref": "#/$defs/inverseOf" } ] } @@ -297,6 +300,20 @@ } } ] + }, + "inverseOf": { + "type": "object", + "properties": { + "type": { + "const": "inverseOf" + }, + "transformation": { + "$ref": "#/$defs/coordinateTransformation" + } + }, + "required": [ + "transformation" + ] } } } From 55e35bd966606ad117eab9188fd2877e2df686ff Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 17:48:03 +0200 Subject: [PATCH 049/113] Added byDimension transformation The tests mark invalid pass because we would need dynamic validation to check whether the axes were present in the coordinate spaces. --- latest/schemas/coordinate_transforms.schema | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 315c2248..04c6990e 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -145,11 +145,38 @@ }, { "$ref": "#/$defs/inverseOf" + }, + { + "$ref": "#/$defs/byDimension" } ] } ] }, + "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": { @@ -314,6 +341,18 @@ "required": [ "transformation" ] + }, + "byDimension": { + "type": "object", + "properties": { + "type": { "const": "byDimension" }, + "transformations": { + "type": "array", + "items": { + "$ref": "#/$defs/byDimensionTransformation" + } + } + } } } } From 0833395edb9fe456c38ea7f3cdb6a84e10e41c81 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 17:54:46 +0200 Subject: [PATCH 050/113] Sequence schemas --- latest/schemas/coordinate_transforms.schema | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 04c6990e..ddbbb5da 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -146,6 +146,9 @@ { "$ref": "#/$defs/inverseOf" }, + { + "$ref": "#/$defs/sequence" + }, { "$ref": "#/$defs/byDimension" } @@ -342,6 +345,19 @@ "transformation" ] }, + "sequence": { + "description": "A sequence of transformations", + "type": "object", + "properties": { + "type": { "const": "sequence" }, + "transformations": { + "type": "array", + "items": { + "$ref": "#/$defs/coordinateTransformation" + } + } + } + }, "byDimension": { "type": "object", "properties": { From 74466c54fdefce1e54a6ff347cff7d802c617efc Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:48:50 +0200 Subject: [PATCH 051/113] Move rotation example to example directory --- latest/examples/transformations/rotation.json | 14 ++++++++++++++ latest/index.bs | 19 +++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 latest/examples/transformations/rotation.json 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/index.bs b/latest/index.bs index a9f7b1f6..864fcf5f 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -929,20 +929,11 @@ and columns. `rotation` transformations are invertible.
A 2D example - ```json - "coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] } - { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } - ], - "coordinateTransformations" : [ - { - "type": "rotation", - "affine": [0, -1, 1, 0], - "input" : "ij", - "output" : "xy" - } - ] - ``` + +
+    path: examples/transformations/rotation.json
+    highlight: json
+    
defines the function: From ff2bc8677088bcc230e9ac11d524403ea02dcd48 Mon Sep 17 00:00:00 2001 From: Isaac Virshup Date: Sun, 9 Oct 2022 16:59:06 +0200 Subject: [PATCH 052/113] Move inverseOf example to examples directory --- .../examples/transformations/inverseOf.json | 14 ++++++++++++++ latest/index.bs | 19 +++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 latest/examples/transformations/inverseOf.json 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/index.bs b/latest/index.bs index 864fcf5f..161f0130 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -963,20 +963,11 @@ transformation (if it exists).
For example -```json -"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", - } -] -``` +
+    path: examples/transformations/inverseOf.json
+    highlight: json
+    
+
#### sequence From a68523fa949125d6570ba678e9294a90b78c30f3 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 12 Oct 2022 14:21:10 -0400 Subject: [PATCH 053/113] multiscales example relative typo fix --- .../multiscales_strict/multiscales_example_relative.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/latest/examples/multiscales_strict/multiscales_example_relative.json b/latest/examples/multiscales_strict/multiscales_example_relative.json index 04e52b2c..c0b10d5b 100644 --- a/latest/examples/multiscales_strict/multiscales_example_relative.json +++ b/latest/examples/multiscales_strict/multiscales_example_relative.json @@ -46,7 +46,7 @@ "type": "scale", "scale": [0.1, 1.0, 0.5, 0.5, 0.5], "input" : "/0", - "output" : "xampleCoordinateSystem" + "output" : "exampleCoordinateSystem" }], "type": "gaussian", "metadata": { From 3fe7aa269ff03cd6d8caef912f4bcadabd6fb50b Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 12 Oct 2022 15:45:28 -0400 Subject: [PATCH 054/113] corrections and clarifications to coordinates and displacements * move bijection examples to separate files * add axis types to xarrayLike example --- latest/examples/subspace/subspacePermute.json | 14 ++-- .../examples/transformations/bijection.json | 15 ++++ .../transformations/bijection_verbose.json | 7 ++ .../examples/transformations/xarrayLike.json | 4 +- latest/index.bs | 72 ++++++++----------- 5 files changed, 62 insertions(+), 50 deletions(-) create mode 100644 latest/examples/transformations/bijection.json create mode 100644 latest/examples/transformations/bijection_verbose.json diff --git a/latest/examples/subspace/subspacePermute.json b/latest/examples/subspace/subspacePermute.json index 88c6b4ea..26749751 100644 --- a/latest/examples/subspace/subspacePermute.json +++ b/latest/examples/subspace/subspacePermute.json @@ -1,24 +1,24 @@ { "coordinateSystems" : [ - { "name " : "in", "axes" : [ {"name" : "i", "name" : "j" }] }, - { "name " : "out", "axes" : [ {"name" : "x", "name" : "y" }] } + { "name" : "in", "axes" : [ {"name" : "i"}, {"name" : "j" } ]}, + { "name" : "out", "axes" : [ {"name" : "x"}, {"name" : "y" } ]} ], "coordinateTransformations" : [ { - "type" : "sequence", + "type" : "byDimension", "input" : "in", "output" : "out", "transformations" : [ { "type": "identity", - "inputAxes" : ["j"], - "outputAxes" : ["x"] + "input" : ["j"], + "output" : ["x"] }, { "type": "scale", "scale" : [2], - "inputAxes" : ["i"], - "outputAxes" : ["y"] + "input" : ["i"], + "output" : ["y"] } ] } diff --git a/latest/examples/transformations/bijection.json b/latest/examples/transformations/bijection.json new file mode 100644 index 00000000..bc74b986 --- /dev/null +++ b/latest/examples/transformations/bijection.json @@ -0,0 +1,15 @@ +{ + "coordinateSystems" : [ + { "name": "src", "axes": [{"name": "i"}, {"name": "j"}] }, + { "name": "tgt", "axes": [{"name": "x"}, {"name": "y"}] } + ], + "coordinateTransformations" : [ + { + "type": "bijection", + "forward": { "type" : "coordinates", "path" : "/forward_coordinates" }, + "inverse": { "type" : "coordinates", "path" : "/inverse_coordinates" }, + "input": "src", + "output": "tgt" + } + ] +} diff --git a/latest/examples/transformations/bijection_verbose.json b/latest/examples/transformations/bijection_verbose.json new file mode 100644 index 00000000..5eccc08e --- /dev/null +++ b/latest/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/latest/examples/transformations/xarrayLike.json b/latest/examples/transformations/xarrayLike.json index fdacf6e0..6a7c45b4 100644 --- a/latest/examples/transformations/xarrayLike.json +++ b/latest/examples/transformations/xarrayLike.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} + { "name": "in", "axes": [ {"name": "i", "type": "array"}, {"name": "j", "type": "array"} ]}, + { "name": "out", "axes": [ {"name": "x", "type": "space"}, {"name": "y", "type": "space"} ]} ], "coordinateTransformations": [ { diff --git a/latest/index.bs b/latest/index.bs index 161f0130..c0fd342f 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -1035,11 +1035,11 @@ The array data at `path` MUST define space and coordinate transform metadata: For `coordinates`: -* If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(ni + 1)`. -* space metadata MUST have exactly one axis with `"type" : "coordinate"` +* If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. +* coordinateSystem metadata MUST have exactly one axis with `"type" : "coordinate"` For `displacements`: -* space metadata MUST have exactly one axis with `"type" : "displacement"` +* coordinateSystem metadata MUST have exactly one axis with `"type" : "displacement"` * `input` and `output` MUST have an equal number of dimensions. For example, in 1D: @@ -1048,21 +1048,13 @@ For example, in 1D: "name" : "a coordinate field transform", "type": "coordinates", "path" : "i2xCoordinates", - "input" : ["i"], - "output" : ["x"], + "input" : "i", + "output" : "x", "interpolation" : "nearest" } ``` -if the arrray in `coordinates` contains the data: `[-9, 9, 0]`, then this metadata defines the function: - -``` -x = - if ( i > -0.5 and i < 0.5 ) -9 - else if ( i < 1.5 ) 9 - else 0 -``` - +where we assume input spaces "i" and "x" are defined elsewhere. Example metadata for the array data at path `coordinates` above: ``` @@ -1085,20 +1077,29 @@ Example metadata for the array data at path `coordinates` above: } ``` +If the arrray 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: ``` { "name" : "a displacement field transform", "type": "displacements", "path" : "displacements", - "input" : ["x"], - "output" : ["y"], + "input" : "i", + "output" : "x", "interpolation" : "linear" } ``` -if the arrray in `displacements` contains the data: `[-1, 0, 1]`. - +where we assume input spaces "i" and "x" are defined elsewhere. Example metadata for the array data at path `displacements` above: ``` @@ -1121,8 +1122,8 @@ Example metadata for the array data at path `displacements` above: ] } ``` - -This transformation maps the point `[1.0]` to the point `[0.5]`. A scale +If the arrray 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 @@ -1210,31 +1211,20 @@ to be correct / consistent for points that fall within those extents. It may not approprite dimensionality.
+ For example -```json -{ - "type" : "bijection", - "forward" :{ "type" : "coordinates", "path" : "/forward_coordinates" }, - "inverse" :{ "type" : "coordinates", "path" : "/inverse_coordinates" }, - "input" : "src", - "output" : "tgt" -} -``` -the input and output of the `forward` and `inverse` transformations are understoood to be: +
+path: examples/transformations/bijection.json
+highlight: json
+
+the input and output of the `forward` and `inverse` transformations are understoood to be: -```json -{ - "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" -} -``` +
+path: examples/transformations/bijection_verbose.json
+highlight: json
+
From f976a87e00f235d1fdd0f16e4cb10080473c0ee9 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 12 Oct 2022 15:45:41 -0400 Subject: [PATCH 055/113] transforms MUST have "input" --- latest/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/latest/index.bs b/latest/index.bs index c0fd342f..7abf6648 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -463,7 +463,7 @@ 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). +- 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). From 72b4f9cc5996b8d913ead5fff5d29f63b514d3ab Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 14 Mar 2023 14:50:54 -0400 Subject: [PATCH 056/113] cts: remove url parameters --- latest/index.bs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 7abf6648..868a8a69 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -498,11 +498,11 @@ input space to *points* in the output space.
`"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). + `"path":str`
`"interpolation":str` +
Displacement field transformation located at (path).
`coordinates` - `"url":str`
`"path":str`
`"interpolation":str` -
Coordinate field transformation in this container (path) or another container located at (url). + `"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. @@ -800,9 +800,6 @@ 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`.
-
url
-
An optional URL to the container in which the translation zarr-array is stored. If not provided, - the provided `path` MUST exist in this container.
scale
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
@@ -833,9 +830,6 @@ 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`.
-
url
-
An optional URL to the container in which the scale zarr-array is stored. If not provided, - the provided `path` MUST exist in this container.
scale
The scale parameters stored as a JSON list of numbers. The list MUST have length `N`.
@@ -865,9 +859,6 @@ The matrix may be stored as a 2D array or as a 1D array (row-major).
path
The path to a zarr-array containing the affine parameters. The array at this path MUST be 1D, its length MUST be `N*(M+1)`.
-
url
-
An optional URL to the container in which the affine zarr-array is stored. If not provided, - the provided `path` MUST exist in this container.
affine
The affine parameters stored in JSON. The matrix may be stored as a row-major flat list of numbers the list MUST be length `N*(M+1)`.
@@ -919,9 +910,6 @@ and columns. `rotation` transformations are invertible.
path
The path to an array containing the affine parameters. The array at this path MUST be 1D. Its length MUST be `N*N`.
-
url
-
An optional URL to the container in which the affine array is stored. If not provided, - the provided `path` MUST exist in this container.
rotation
The rotation parameters stored in JSON. The matrix is stored as a row-major flat list of numbers, its length MUST be `N*N`.
@@ -1012,9 +1000,6 @@ MAY approximate their inverses. Metadata for these coordinate transforms have th
path
The location of the coordinate array in this (or another) containter.
-
url
-
An optional URL to the container in which the coordinate field array is stored. If not provided, - the provided `path` MUST exist in this container.
interpolation
The `interpolation` attributes MAY be provided. It's value indicates the interpolation to use if transforing points not on the array's discrete grid. From be7d84982799bd9a99a66d3a75ea6abfd846af90 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 14 Mar 2023 14:51:48 -0400 Subject: [PATCH 057/113] ct: fix and clarify nested json representation for affine and rotation types --- .../transformations/affine2d3d_nested.json | 15 +++++++++++ latest/index.bs | 27 +++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 latest/examples/transformations/affine2d3d_nested.json diff --git a/latest/examples/transformations/affine2d3d_nested.json b/latest/examples/transformations/affine2d3d_nested.json new file mode 100644 index 00000000..6c0dce02 --- /dev/null +++ b/latest/examples/transformations/affine2d3d_nested.json @@ -0,0 +1,15 @@ +{ + "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/index.bs b/latest/index.bs index 868a8a69..eacfb189 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -853,15 +853,16 @@ y = 2 * j `affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` matrices in homogeneous coordinates. This transformation type is invertible when `N` equals `M`. -The matrix may be stored as a 2D array or as a 1D array (row-major). +The matrix may be stored as a 2D array (inner arrays represent the rows of the matrix) or as a 1D array (row-major).
path
The path to a zarr-array containing the affine parameters. - The array at this path MUST be 1D, its length MUST be `N*(M+1)`.
+ The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. + If 2D its size must be `N x (M+1)`.
affine
-
The affine parameters stored in JSON. The matrix may be stored - as a row-major flat list of numbers the list MUST be length `N*(M+1)`.
+
The affine parameters stored in JSON. The matrix may be stored as a row-major flat array of numbers that MUST be + length `N*(M+1)`, or as 2D nested array where the outer array MUST be length `N` and the inner arrays MUST be length `M+1`.
@@ -894,6 +895,13 @@ The matrix may be stored as a 2D array or as a 1D array (row-major). y = 4*i + 5*j + 6 z = 7*i + 8*j + 9 ``` + + Using a nested 2D JSON array, the same transformation can be written: +
+    path: examples/transformations/affine2d3d_nested.json
+    highlight: json
+    
+
@@ -904,15 +912,18 @@ When possible, a rotation transformation SHOULD be defined rather than its equivalent affine. Input and output dimensionality (N) MUST be identical and greater than 1. Rotations are stored as `NxN` matrices, see below, and MUST have determinant equal to one, with orthonormal rows -and columns. `rotation` transformations are invertible. +and columns. The matrix may be stored as a 2D array (inner arrays represent +the rows of the matrix) or as a 1D array (row-major). `rotation` transformations +are invertible.
path
The path to an array containing the affine parameters. - The array at this path MUST be 1D. Its length MUST be `N*N`.
+ The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*N`, + if 2D its size must be `N x N`.
rotation
-
The rotation parameters stored in JSON. The matrix is stored - as a row-major flat list of numbers, its length MUST be `N*N`.
+
The parameters stored in JSON. The matrix may be stored as a row-major flat array of numbers that MUST be + length `N*N`, or as 2D nested array where the outer array MUST be length `N` and the inner arrays MUST be length `N`.
From da94207a23017f1139a2f73a3e538add50d658de Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 14 Mar 2023 21:05:35 -0400 Subject: [PATCH 058/113] motivate coordinate systems * add an example --- latest/index.bs | 62 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index eacfb189..9badf39d 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -321,8 +321,8 @@ Conforming readers: -------------------------- 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). +- 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" (see below).
@@ -340,7 +340,63 @@ A "coordinate system" is a collection of "axes" / dimensions with a name. Every 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). +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. Task 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": "x", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "z", "type": "space", "unit": "micrometer"} + ] + }, + { + "name" : "sampleA-instrument2", + "axes" : [ + {"name": "x", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "z", "type": "space", "unit": "micrometer"} + ] + } +], +"coordinateTransformations": [ + { + "type": "affine": + "path": "../sampleA_instrument2-to-instrument1" + "input": "sampleA_instrument2", + "output": "sampleA_instrument1" + } +] +``` + +
+ + ### "axes" metadata {#axes-md} From b92f540dc95440f2d6b7012185b09c2b862aa744 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 14 Mar 2023 21:05:52 -0400 Subject: [PATCH 059/113] describe discrete axes, interpolation, etc --- latest/index.bs | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 9badf39d..fe9d3746 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -413,7 +413,7 @@ If part of [[#multiscale-md]], the length of "axes" MUST be equal to the number
-Examples of valid axes. +Examples of valid axes: ```json [ @@ -421,13 +421,46 @@ Examples of valid axes. {"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": "c", "type": "coordinate", "discrete" : true }, + {"name": "v", "type": "displacement", "discrete": true }, {"name": "freq", "type": "frequency", "unit": "megahertz"} ] ```
+Arrays are often thought of as containing discrete samples along the continuous variable. Axes representing space and time are +usually continuous, meaning they can be indexed by real-valued (floating point) numbers whereas discrete axes may be indexed +only by integers. Arrays are inherently discrete (see Array coordinate systems, below) , but values "in between" discrete +coordinates can be retreived using an *interpolation* method. If an axis is continuous (`"discrete" : false`), it indicates +that indexing with continuous values is meaningful, and that interpolation may be performed along that axis. Interpolation may +be performed jointly across axes with the same `type`, and interpolation should not be performed for discrete axes or jointly +across axes with differing `type`s. Other non-continuous variables and axis types are also usually discrete, such as +`channel`s, `coordinate`s, and `displacement`s. + +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": "c", "type": "channel", "discrete": true}, + {"name": "x", "type": "space"}, + {"name": "y", "type": "space"}, + {"name": "t", "type": "time"}, + ] +} +``` + +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. +
+ ### Array coordinate systems From 383aa87c4b15d6c4b265cf233a97e73ef2628aa4 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Tue, 19 Sep 2023 19:25:18 -0400 Subject: [PATCH 060/113] spec: remove 5D limits --- latest/index.bs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index fe9d3746..5c1d0b9b 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -134,7 +134,7 @@ 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. +See [[#multiscale-md]] for details. For this example we assume an image with 5 dimensions and axes called `t,c,z,y,x`.
@@ -155,8 +155,8 @@ For this example we assume an image with 5 dimensions and axes called `t,c,z,y,x
     ├── 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.
     │   │
-    │   ├── .zarray           # All image arrays must be up to 5-dimensional
-    │   │                     # with the axis of type time before type channel, before spatial axes.
+    │   ├── .zarray           
+    │   │                     
     │   │
     │   └─ t                  # Chunks are stored with the nested directory layout.
     │      └─ c               # All but the last chunk element are stored as directories.
@@ -169,7 +169,7 @@ For this example we assume an image with 5 dimensions and axes called `t,c,z,y,x
         ├── .zgroup           # The labels group is a container which holds a list of labels to make the objects easily discoverable
         │
         ├── .zattrs           # All labels will be listed in `.zattrs` e.g. `{ "labels": [ "original/0" ] }`
-        │                     # Each dimension of the label `(t, c, z, y, x)` should be either the same as the
+        │                     # 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.
         │
@@ -1318,21 +1318,21 @@ highlight: json
 "multiscales" metadata {#multiscale-md}
 ---------------------------------------
 
-Metadata about an image can be found under the "multiscales" key in the group-level 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.
+Metadata about an image can be found under the "multiscales" key in the group-level metadata. Here, "image" refers to a multidimensional array with two or more dimensions. 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 "axes", see [[#axes-md]].
-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".
+The length of "axes" must be greater than 1 and MUST equal the dimensionality of the zarr arrays storing the image data (see "datasets:path").
+The "axes" field SHOULD contain 2 or 3 entries with "type:space".
+The order of the entries MUST correspond to the order of dimensions of the zarr arrays. In addition, the entries SHOULD 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 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 contains the path to the array for this resolution relative
 to the current zarr group. The "path"s MUST be ordered from largest (i.e. highest resolution) to smallest.
 
-Each "datasets" dictionary 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 "datasets" dictionary MUST have the same number of dimensions. The number of dimensions and order MUST correspond to number and order of "axes".
 Each dictionary in "datasets" MUST contain the field "coordinateTransformations", which contains a list of transformations that map the data coordinates to the physical coordinates (as specified by "axes") for this resolution level.
 The transformations are defined according to [[#trafo-md]]. The transformation MUST only be of type `translation` or `scale`.
 They MUST contain exactly one `scale` transformation that 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.

From 1849133e3ea3424d7f2bf361cc436b3ff9d0aa74 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 13:18:27 -0400
Subject: [PATCH 061/113] tforms: more details to conforming readers

---
 latest/index.bs | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index fe9d3746..c72606c6 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -536,7 +536,6 @@ in the discrete array, i.e., the area corresponding to nearest-neighbor (NN) int
 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]].
-=======
 
 
 
@@ -608,8 +607,10 @@ input space to *points* in the output space.
 
 
 Conforming readers:
-- SHOULD be able to apply transformations to points
-- SHOULD be able to apply transformations to images
+- 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]]),
 and MUST be duplicated in the atrributes of the zarr array. Transformations between different images MUST be stored in the

From a2e7687c1fb2e4bbdb9d0b72411db5d14a5af4a9 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 13:18:43 -0400
Subject: [PATCH 062/113] tforms: more sensible output name for sequence
 transform example

---
 latest/index.bs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/latest/index.bs b/latest/index.bs
index c72606c6..d308e642 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -673,7 +673,7 @@ The sequence transformation's input corresponds to an array coordinate system at
     {
         "type": "scale",
         "input" : "in",
-        "output" : "outSeq",
+        "output" : "outScale",
         "scale" : [ 0.5, 1.2 ]
     },
     {

From 5ce238af299d52b010d82f2efef062fd95d95c53 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 13:18:59 -0400
Subject: [PATCH 063/113] tforms: typo fix

---
 latest/index.bs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/latest/index.bs b/latest/index.bs
index d308e642..48e32b78 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -653,7 +653,7 @@ Exceptions are if the the coordinate transformation appears in the `transformati
 `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
+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).
 

From 676aebae7b607a5488160021fc21ebe1723cdd7d Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 13:19:35 -0400
Subject: [PATCH 064/113] tforms: fix name of zgroup in example hierarchy
 comment

---
 latest/index.bs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index 48e32b78..08d790de 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -624,8 +624,8 @@ store.zarr                      # Root folder of the zarr store
 │                               # 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
+├── coordinateTransformations   # transformations that use array storage go in a "coordinateTransformations" zarr group.
+│   └── displacements           # for example, a zarr array containing a displacement field
 │       ├── .zattrs
 │       └── .zarray
 │

From 578dd72339317c4187dd947a8403d707ad55f339 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 13:49:52 -0400
Subject: [PATCH 065/113] fix header and link for axes metadata

---
 latest/index.bs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index ec28e0e8..85d93559 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -398,7 +398,7 @@ image.
 
 
 
-### "axes" metadata {#axes-md}
+### "axes" metadata
 
 "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.
@@ -1323,7 +1323,7 @@ Metadata about an image can be found under the "multiscales" key in the group-le
 
 "multiscales" contains a list of dictionaries where each entry describes a multiscale image.
 
-Each "multiscales" dictionary MUST contain the field "axes", see [[#axes-md]].
+Each "multiscales" dictionary MUST contain the field "axes", see the [axes section](#axes-metadata).
 The length of "axes" must be greater than 1 and MUST equal the dimensionality of the zarr arrays storing the image data (see "datasets:path").
 The "axes" field SHOULD contain 2 or 3 entries with "type:space".
 The order of the entries MUST correspond to the order of dimensions of the zarr arrays. In addition, the entries SHOULD 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".

From c33287d9aa68e9dcc287dae92169ca9ee94ad798 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 13:59:09 -0400
Subject: [PATCH 066/113] example coordinate systems follow recommendations

---
 latest/index.bs | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index 85d93559..3c0705f4 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -330,9 +330,9 @@ A "coordinate system" is a collection of "axes" / dimensions with a name. Every
 {
     "name" : "volume_micrometers",
     "axes" : [
-        {"name": "x", "type": "space", "unit": "micrometer"},
+        {"name": "z", "type": "space", "unit": "micrometer"},
         {"name": "y", "type": "space", "unit": "micrometer"},
-        {"name": "z", "type": "space", "unit": "micrometer"}
+        {"name": "x", "type": "space", "unit": "micrometer"}
     ]
 }
 ```
@@ -370,17 +370,17 @@ image.
     {
         "name" : "sampleA-instrument1",
         "axes" : [
-            {"name": "x", "type": "space", "unit": "micrometer"},
+            {"name": "z", "type": "space", "unit": "micrometer"},
             {"name": "y", "type": "space", "unit": "micrometer"},
-            {"name": "z", "type": "space", "unit": "micrometer"}
+            {"name": "x", "type": "space", "unit": "micrometer"}
         ]
     },
     {
         "name" : "sampleA-instrument2",
         "axes" : [
-            {"name": "x", "type": "space", "unit": "micrometer"},
+            {"name": "z", "type": "space", "unit": "micrometer"},
             {"name": "y", "type": "space", "unit": "micrometer"},
-            {"name": "z", "type": "space", "unit": "micrometer"}
+            {"name": "x", "type": "space", "unit": "micrometer"}
         ]
     }
 ],
@@ -449,10 +449,10 @@ For the coordinate system:
 {
     "name" : "index and interpolation",
     "axes" : [
+        {"name": "t", "type": "time"},
         {"name": "c", "type": "channel", "discrete": true},
-        {"name": "x", "type": "space"},
         {"name": "y", "type": "space"},
-        {"name": "t", "type": "time"},
+        {"name": "x", "type": "space"}
     ]
 }
 ```

From 6da38f7c0aef4cb3ceefd55cc1f3a69e943dea22 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Tue, 10 Oct 2023 14:19:11 -0400
Subject: [PATCH 067/113] rm mapIndex transformation

* prefer using mapAxis
---
 .../examples/transformations/mapIndex1.json   | 23 --------
 .../examples/transformations/mapIndex2.json   | 23 --------
 latest/index.bs                               | 56 -------------------
 3 files changed, 102 deletions(-)
 delete mode 100644 latest/examples/transformations/mapIndex1.json
 delete mode 100644 latest/examples/transformations/mapIndex2.json

diff --git a/latest/examples/transformations/mapIndex1.json b/latest/examples/transformations/mapIndex1.json
deleted file mode 100644
index db785459..00000000
--- a/latest/examples/transformations/mapIndex1.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "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
deleted file mode 100644
index 411828de..00000000
--- a/latest/examples/transformations/mapIndex2.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "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/index.bs b/latest/index.bs
index 3c0705f4..ce8d37ba 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -559,9 +559,6 @@ input space to *points* in the output space.
   
`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. @@ -772,59 +769,6 @@ y = j -#### mapIndex - -`mapIndex` transformations describe axis permutations as a reordering of the input dimensions. -Transformations MUST include a `mapIndex` field whose value is an array of integers. If the ith element of the array is j, it -means that output dimension i comes from input dimension j. The length of the `mapIndex` array MUST be equal to the -dimensionality of the output coordinate system. If the input coordinate system has dimension N, then every integer in that array -MUST be less than N. - -
- -
-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 -``` - -
#### mapAxis From 1451a47bd383fdb9087c899dc7f35bad42018df5 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 11 Oct 2023 15:06:05 -0400 Subject: [PATCH 068/113] start of coordinatTransformations schema --- .../multiscales_example_relative.json | 9 +- .../multiscales_transformations.json | 25 +- .../schemas/coordinateTransformations.schema | 296 ++++++ latest/schemas/image.schema | 91 +- latest/tests/image_suite.json | 892 ++++++++++-------- latest/tests/strict_image_suite.json | 253 ++--- 6 files changed, 997 insertions(+), 569 deletions(-) create mode 100644 latest/schemas/coordinateTransformations.schema diff --git a/latest/examples/multiscales_strict/multiscales_example_relative.json b/latest/examples/multiscales_strict/multiscales_example_relative.json index c0b10d5b..206ad8c7 100644 --- a/latest/examples/multiscales_strict/multiscales_example_relative.json +++ b/latest/examples/multiscales_strict/multiscales_example_relative.json @@ -17,8 +17,15 @@ ], "datasets": [ { - "path": "0" + "path": "0", // the transformation of other arrays are defined relative to this, the highest resolution, array + "coordinateTransformations": [{ + // the second scale level (downscaled by a factor of 2 relative to "0" in zyx) + "type": "scale", + "scale": [1, 1, 1, 1, 1], + "input" : "/0`", + "output" : "exampleCoordinateSystem" + }] }, { "path": "1", diff --git a/latest/examples/multiscales_strict/multiscales_transformations.json b/latest/examples/multiscales_strict/multiscales_transformations.json index c99040c2..ace5f791 100644 --- a/latest/examples/multiscales_strict/multiscales_transformations.json +++ b/latest/examples/multiscales_strict/multiscales_transformations.json @@ -1,16 +1,21 @@ { "multiscales": [ { - "axes": [ + "coordinateSystems" : [ { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" + "name" : "name", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } ], "datasets": [ @@ -44,4 +49,4 @@ } } ] -} \ No newline at end of file +} diff --git a/latest/schemas/coordinateTransformations.schema b/latest/schemas/coordinateTransformations.schema new file mode 100644 index 00000000..8bd35f4f --- /dev/null +++ b/latest/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/latest/schemas/image.schema b/latest/schemas/image.schema index 3926c4f4..61c1dce5 100644 --- a/latest/schemas/image.schema +++ b/latest/schemas/image.schema @@ -36,15 +36,15 @@ "0.5-dev" ] }, - "axes": { - "$ref": "#/$defs/axes" + "coordinateSystems": { + "$ref": "#/$defs/coordinateSystems" }, "coordinateTransformations": { "$ref": "#/$defs/coordinateTransformations" - } + } }, "required": [ - "datasets", "axes" + "datasets", "coordinateSystems" ] }, "minItems": 1, @@ -107,8 +107,25 @@ } }, "required": [ "multiscales" ], - "$defs": { + "coordinateSystems" : { + "type": "array", + "uniqueItems": true, + "contains": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "axes" : { + "$ref" : "#/$defs/axes" + } + }, + "required": [ + "name", "axes" + ] + } + }, "axes": { "type": "array", "uniqueItems": true, @@ -165,69 +182,7 @@ } }, "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"] - } - ] - } + "$ref": "coordinateTransformations.schema" } } } diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index 39cb73d5..3b6a55b4 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -10,23 +10,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time", - "units": "micrometer" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -41,7 +24,29 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time", + "units": "micrometer" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -53,21 +58,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "angle" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -83,7 +73,27 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "angle" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -111,16 +121,21 @@ ] } ], - "axes": [ + "coordinateSystems": [ { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } ] } @@ -134,18 +149,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micron" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -160,7 +163,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micron" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -200,16 +220,21 @@ "multichannel": true } }, - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } ] } @@ -223,22 +248,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "angle", - "type": "custom" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -254,7 +263,28 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "angle", + "type": "custom" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -266,18 +296,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "x", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -292,7 +310,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "x", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -304,16 +339,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time" - }, - { - "name": "c", - "type": "channel" - } - ], "datasets": [ { "path": "0", @@ -328,7 +353,22 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + } + ] + } + ] } ] }, @@ -340,18 +380,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -366,7 +394,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -378,18 +423,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -404,7 +437,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -416,32 +466,6 @@ "data": { "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", @@ -460,7 +484,38 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "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" + } + ] + } + ] } ] }, @@ -472,18 +527,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -498,22 +541,39 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ], "omero": { "channels": [ { "active": true, - "coefficient": 1.0, + "coefficient": 1, "color": 255, "family": "linear", "label": "1234", "window": { - "end": 1765.0, - "max": 2555.0, - "min": 5.0, - "start": 0.0 + "end": 1765, + "max": 2555, + "min": 5, + "start": 0 } } ] @@ -527,16 +587,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "type": "space", - "units": "micron" - }, - { - "type": "space", - "units": "micron" - } - ], "datasets": [ { "path": "0", @@ -551,7 +601,22 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "type": "space", + "units": "micron" + }, + { + "type": "space", + "units": "micron" + } + ] + } + ] } ] }, @@ -563,13 +628,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -584,7 +642,19 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -596,20 +666,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time" - }, - { - "name": "c", - "type": "channel" - }, - { - "name": "x", - "type": "space" - } - ], "datasets": [ { "path": "0", @@ -625,7 +681,26 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "x", + "type": "space" + } + ] + } + ] } ] }, @@ -637,18 +712,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": 0, @@ -663,7 +726,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -675,18 +755,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -709,7 +777,24 @@ "type": "scale" } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -721,24 +806,29 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0" } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -750,20 +840,25 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "datasets": [], + "version": "0.5-dev", + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } - ], - "datasets": [], - "version": "0.5-dev" + ] } ] }, @@ -775,19 +870,24 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "version": "0.5-dev", + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } - ], - "version": "0.5-dev" + ] } ] }, @@ -825,18 +925,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -851,7 +939,24 @@ ] } ], - "version": "0.3" + "version": "0.3", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -863,18 +968,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "invalid", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -889,7 +982,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "invalid", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -901,18 +1011,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -934,7 +1032,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -946,7 +1061,6 @@ "data": { "multiscales": [ { - "axes": [], "datasets": [ { "path": "0", @@ -961,7 +1075,13 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [] + } + ] } ] }, @@ -973,24 +1093,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "X", - "type": "space" - }, - { - "name": "z", - "type": "space" - }, - { - "name": "y", - "type": "space" - }, - { - "name": "x", - "type": "space" - } - ], "datasets": [ { "path": "0", @@ -1007,7 +1109,30 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "X", + "type": "space" + }, + { + "name": "z", + "type": "space" + }, + { + "name": "y", + "type": "space" + }, + { + "name": "x", + "type": "space" + } + ] + } + ] } ] }, @@ -1028,18 +1153,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -1054,22 +1167,39 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ], "omero": { "channels": [ { "active": true, - "coefficient": 1.0, + "coefficient": 1, "color": "ff0000", "family": "linear", "label": "1234", "window": { "end": "100", - "max": 2555.0, - "min": 5.0, - "start": 0.0 + "max": 2555, + "min": 5, + "start": 0 } } ] @@ -1083,25 +1213,30 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", "coordinateTransformations": [] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -1113,18 +1248,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "coordinateTransformations": [ @@ -1138,7 +1261,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, diff --git a/latest/tests/strict_image_suite.json b/latest/tests/strict_image_suite.json index b8aeebe1..31db7b3c 100644 --- a/latest/tests/strict_image_suite.json +++ b/latest/tests/strict_image_suite.json @@ -12,32 +12,6 @@ { "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" - } - ], "datasets": [ { "path": "0", @@ -45,8 +19,8 @@ { "type": "scale", "scale": [ - 1.0, - 1.0, + 1, + 1, 0.5, 0.5, 0.5 @@ -60,11 +34,11 @@ { "type": "scale", "scale": [ - 1.0, - 1.0, - 1.0, - 1.0, - 1.0 + 1, + 1, + 1, + 1, + 1 ] } ] @@ -75,11 +49,11 @@ { "type": "scale", "scale": [ - 1.0, - 1.0, - 2.0, - 2.0, - 2.0 + 1, + 1, + 2, + 2, + 2 ] } ] @@ -90,10 +64,10 @@ "type": "scale", "scale": [ 0.1, - 1.0, - 1.0, - 1.0, - 1.0 + 1, + 1, + 1, + 1 ] } ], @@ -106,7 +80,38 @@ "kwargs": { "multichannel": true } - } + }, + "coordinateSystems": [ + { + "name": "tmpname", + "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" + } + ] + } + ] } ] }, @@ -118,18 +123,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -158,7 +151,24 @@ "type": "foo", "metadata": { "key": "value" - } + }, + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -201,16 +211,21 @@ "multichannel": true } }, - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } ] } @@ -224,18 +239,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -255,7 +258,24 @@ "type": "foo", "metadata": { "key": "value" - } + }, + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -267,31 +287,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time" - }, - { - "name": "c", - "type": "channel" - }, - { - "name": "z", - "type": "space", - "units": "micrometer" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -339,37 +334,67 @@ "type": "foo", "metadata": { "key": "value" - } + }, + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "z", + "type": "space", + "units": "micrometer" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ], "omero": { "channels": [ { "active": true, - "coefficient": 1.0, + "coefficient": 1, "color": "00FF00", "family": "linear", "inverted": false, "label": "FITC", "window": { - "end": 813.0, - "max": 870.0, - "min": 102.0, - "start": 82.0 + "end": 813, + "max": 870, + "min": 102, + "start": 82 } }, { "active": true, - "coefficient": 1.0, + "coefficient": 1, "color": "FF0000", "family": "linear", "inverted": false, "label": "RD-TR-PE", "window": { - "end": 815.0, - "max": 441.0, - "min": 129.0, - "start": 78.0 + "end": 815, + "max": 441, + "min": 129, + "start": 78 } } ], From 1602832e5cfe0bc7c0b4d06c98f214e3cd667bad Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Thu, 12 Oct 2023 14:23:12 -0400 Subject: [PATCH 069/113] missing_scale and duplicate_scale are valid --- latest/tests/image_suite.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index 3b6a55b4..882b39cc 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -418,7 +418,7 @@ "valid": false }, { - "formerly": "invalid/missing_scale.json", + "formerly": "invalid/translation_only.json", "description": "TBD", "data": { "multiscales": [ @@ -458,7 +458,7 @@ } ] }, - "valid": false + "valid": true }, { "formerly": "invalid/too_many_axes.json", @@ -1006,7 +1006,7 @@ "valid": false }, { - "formerly": "invalid/duplicate_scale.json", + "formerly": "valid/two_scales.json", "description": "TBD", "data": { "multiscales": [ @@ -1053,7 +1053,7 @@ } ] }, - "valid": false + "valid": true }, { "formerly": "invalid/no_axes.json", From 3c86a3b3c7cfb7ac01b7fee94a7537c2f8cd176b Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 4 Dec 2023 16:50:44 -0500 Subject: [PATCH 070/113] rm mapIndex examples * mapIndex no longer part of the spec --- .../examples/transformations/mapIndex1.json | 23 ------------------- .../examples/transformations/mapIndex2.json | 23 ------------------- 2 files changed, 46 deletions(-) delete mode 100644 latest/examples/transformations/mapIndex1.json delete mode 100644 latest/examples/transformations/mapIndex2.json diff --git a/latest/examples/transformations/mapIndex1.json b/latest/examples/transformations/mapIndex1.json deleted file mode 100644 index db785459..00000000 --- a/latest/examples/transformations/mapIndex1.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "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 deleted file mode 100644 index 411828de..00000000 --- a/latest/examples/transformations/mapIndex2.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "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" - } - ] -} From 9021ae08c9b6164a0fc4a79d30680a0355892e6a Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 4 Dec 2023 16:52:00 -0500 Subject: [PATCH 071/113] affine parameters can be nested arrays --- latest/schemas/coordinate_transforms.schema | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index ddbbb5da..0b19b4b3 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -279,6 +279,20 @@ } ] }, + "mtxFlatOrNested" : { + "type": "array", + "oneOf": [ + { + "items": { "type": "number" } + }, + { + "items": { + "type": "array", + "items" : { "type" : "number" } + } + } + ] + }, "affine": { "type": "object", "properties": { @@ -293,10 +307,7 @@ { "properties": { "affine": { - "type": "array", - "items": { - "type": "number" - } + "$ref": "#/$defs/mtxFlatOrNested" }, "required": [ "affine" From 841e0aaab9a921a42237cabe2d2a8352871fcacd Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 4 Dec 2023 16:52:29 -0500 Subject: [PATCH 072/113] rm mapIndex from transform schema --- latest/schemas/coordinate_transforms.schema | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 0b19b4b3..23bf9e71 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -125,9 +125,6 @@ { "$ref": "#/$defs/identity" }, - { - "$ref": "#/$defs/mapIndex" - }, { "$ref": "#/$defs/mapAxis" }, @@ -188,24 +185,6 @@ } } }, - "mapIndex": { - "type": "object", - "description": "Permute axes by position", - "properties": { - "type": { - "const": "mapIndex" - }, - "mapIndex": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - "required": [ - "mapIndex" - ] - }, "mapAxis": { "type": "object", "description": "Permute axes by name", From 704173cb69639c76a1e6a0849d0c7644e04a98ca Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 4 Dec 2023 16:52:52 -0500 Subject: [PATCH 073/113] add bijection transformation to schema --- latest/schemas/coordinate_transforms.schema | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 23bf9e71..989920e8 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -143,6 +143,9 @@ { "$ref": "#/$defs/inverseOf" }, + { + "$ref": "#/$defs/bijection" + }, { "$ref": "#/$defs/sequence" }, @@ -335,6 +338,23 @@ "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", From 03fe664acfd660f3bd6c45bdd0a4a99fd8cc888a Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 4 Dec 2023 16:53:27 -0500 Subject: [PATCH 074/113] add displacements and coordinates transformations to schema * preliminary --- latest/schemas/coordinate_transforms.schema | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema index 989920e8..1219563a 100644 --- a/latest/schemas/coordinate_transforms.schema +++ b/latest/schemas/coordinate_transforms.schema @@ -151,6 +151,12 @@ }, { "$ref": "#/$defs/byDimension" + }, + { + "$ref": "#/$defs/displacements" + }, + { + "$ref": "#/$defs/coordinates" } ] } @@ -379,6 +385,18 @@ } } } + }, + "displacements": { + "type": "object", + "properties": { + "type": { "const": "displacements" } + } + }, + "coordinates": { + "type": "object", + "properties": { + "type": { "const": "coordinates" } + } } } } From 34e41d31d61c671ce70478ea892a23b530a013a6 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 5 Dec 2023 15:06:09 -0500 Subject: [PATCH 075/113] update tests with coordinateSystems --- .../multiscales_transformations.json | 17 +- latest/tests/image_suite.json | 916 ++++++++++-------- latest/tests/strict_image_suite.json | 162 ++-- 3 files changed, 626 insertions(+), 469 deletions(-) diff --git a/latest/examples/multiscales_strict/multiscales_transformations.json b/latest/examples/multiscales_strict/multiscales_transformations.json index c99040c2..2767ce4c 100644 --- a/latest/examples/multiscales_strict/multiscales_transformations.json +++ b/latest/examples/multiscales_strict/multiscales_transformations.json @@ -1,16 +1,13 @@ { "multiscales": [ { - "axes": [ + "coordinateSystems" : [ { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "multiscales_transformations", + "axes": [ + { "name": "y", "type": "space", "units": "micrometer" }, + { "name": "x", "type": "space", "units": "micrometer" } + ] } ], "datasets": [ @@ -44,4 +41,4 @@ } } ] -} \ No newline at end of file +} diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index 39cb73d5..ef28b379 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -10,23 +10,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time", - "units": "micrometer" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -41,7 +24,29 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time", + "units": "micrometer" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -53,21 +58,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "angle" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -83,7 +73,27 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "angle" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -111,16 +121,21 @@ ] } ], - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } ] } @@ -134,18 +149,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micron" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -160,7 +163,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micron" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -200,16 +220,21 @@ "multichannel": true } }, - "axes": [ + "coordinateSystems": [ { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } ] } @@ -223,22 +248,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "angle", - "type": "custom" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -254,7 +263,28 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "angle", + "type": "custom" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -266,18 +296,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "x", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -292,28 +310,35 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "x", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, "valid": false }, { - "formerly": "invalid/missing_space_axes.json", + "formerly": "valid/no_space_axes.json", "description": "TBD", "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time" - }, - { - "name": "c", - "type": "channel" - } - ], "datasets": [ { "path": "0", @@ -328,11 +353,26 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + } + ] + } + ] } ] }, - "valid": false + "valid": true }, { "formerly": "invalid/invalid_transformation_type.json", @@ -340,18 +380,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -366,30 +394,35 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, "valid": false }, { - "formerly": "invalid/missing_scale.json", + "formerly": "invalid/translation_only.json", "description": "TBD", "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -404,44 +437,35 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, - "valid": false + "valid": true }, { - "formerly": "invalid/too_many_axes.json", + "formerly": "valid/many_axes.json", "description": "TBD", "data": { "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", @@ -460,11 +484,42 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "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" + } + ] + } + ] } ] }, - "valid": false + "valid": true }, { "formerly": "invalid/invalid_channels_color.json", @@ -472,18 +527,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -498,22 +541,39 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ], "omero": { "channels": [ { "active": true, - "coefficient": 1.0, + "coefficient": 1, "color": 255, "family": "linear", "label": "1234", "window": { - "end": 1765.0, - "max": 2555.0, - "min": 5.0, - "start": 0.0 + "end": 1765, + "max": 2555, + "min": 5, + "start": 0 } } ] @@ -527,16 +587,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "type": "space", - "units": "micron" - }, - { - "type": "space", - "units": "micron" - } - ], "datasets": [ { "path": "0", @@ -551,7 +601,22 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "type": "space", + "units": "micron" + }, + { + "type": "space", + "units": "micron" + } + ] + } + ] } ] }, @@ -563,13 +628,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -584,32 +642,30 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, "valid": false }, { - "formerly": "invalid/one_space_axes.json", + "formerly": "valid/one_space_axes.json", "description": "TBD", "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time" - }, - { - "name": "c", - "type": "channel" - }, - { - "name": "x", - "type": "space" - } - ], "datasets": [ { "path": "0", @@ -625,11 +681,30 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "x", + "type": "space" + } + ] + } + ] } ] }, - "valid": false + "valid": true }, { "formerly": "invalid/invalid_path.json", @@ -637,18 +712,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": 0, @@ -663,7 +726,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -675,18 +755,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -709,7 +777,24 @@ "type": "scale" } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -721,24 +806,29 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0" } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -750,20 +840,25 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "datasets": [], + "version": "0.5-dev", + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } - ], - "datasets": [], - "version": "0.5-dev" + ] } ] }, @@ -775,19 +870,24 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, + "version": "0.5-dev", + "coordinateSystems": [ { - "name": "x", - "type": "space", - "units": "micrometer" + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] } - ], - "version": "0.5-dev" + ] } ] }, @@ -825,18 +925,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -851,7 +939,24 @@ ] } ], - "version": "0.3" + "version": "0.3", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -863,18 +968,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "invalid", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -889,30 +982,35 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "invalid", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, - "valid": false + "valid": true }, { - "formerly": "invalid/duplicate_scale.json", + "formerly": "valid/two_scales.json", "description": "TBD", "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -934,19 +1032,35 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, - "valid": false + "valid": true }, { - "formerly": "invalid/no_axes.json", + "formerly": "invalid/no_coordinateSystems.json", "description": "TBD", "data": { "multiscales": [ { - "axes": [], "datasets": [ { "path": "0", @@ -961,7 +1075,13 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [] + } + ] } ] }, @@ -973,24 +1093,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "X", - "type": "space" - }, - { - "name": "z", - "type": "space" - }, - { - "name": "y", - "type": "space" - }, - { - "name": "x", - "type": "space" - } - ], "datasets": [ { "path": "0", @@ -1007,7 +1109,30 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "X", + "type": "space" + }, + { + "name": "z", + "type": "space" + }, + { + "name": "y", + "type": "space" + }, + { + "name": "x", + "type": "space" + } + ] + } + ] } ] }, @@ -1028,18 +1153,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", @@ -1054,22 +1167,39 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ], "omero": { "channels": [ { "active": true, - "coefficient": 1.0, + "coefficient": 1, "color": "ff0000", "family": "linear", "label": "1234", "window": { "end": "100", - "max": 2555.0, - "min": 5.0, - "start": 0.0 + "max": 2555, + "min": 5, + "start": 0 } } ] @@ -1083,25 +1213,30 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "path": "0", "coordinateTransformations": [] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, @@ -1113,18 +1248,6 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } - ], "datasets": [ { "coordinateTransformations": [ @@ -1138,7 +1261,24 @@ ] } ], - "version": "0.5-dev" + "version": "0.5-dev", + "coordinateSystems": [ + { + "name": "tmpname", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } + ] } ] }, diff --git a/latest/tests/strict_image_suite.json b/latest/tests/strict_image_suite.json index b8aeebe1..049eb93b 100644 --- a/latest/tests/strict_image_suite.json +++ b/latest/tests/strict_image_suite.json @@ -12,31 +12,36 @@ { "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": "multiscales_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": [ { @@ -201,17 +206,22 @@ "multichannel": true } }, - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } + "coordinateSystems": [ + { + "name": "image_metadata", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } ] } ] @@ -224,17 +234,22 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } + "coordinateSystems": [ + { + "name": "image", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } ], "datasets": [ { @@ -267,30 +282,35 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "t", - "type": "time" - }, - { - "name": "c", - "type": "channel" - }, - { - "name": "z", - "type": "space", - "units": "micrometer" - }, - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } + "coordinateSystems": [ + { + "name": "image_omero", + "axes": [ + { + "name": "t", + "type": "time" + }, + { + "name": "c", + "type": "channel" + }, + { + "name": "z", + "type": "space", + "units": "micrometer" + }, + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } ], "datasets": [ { From a24aefdb75ae32974be20c6b2574051102adefc0 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 5 Dec 2023 15:07:21 -0500 Subject: [PATCH 076/113] update image schema with coordinate systems --- latest/schemas/coordinate_systems.schema | 30 ++++++++++++ latest/schemas/image.schema | 62 ++---------------------- 2 files changed, 33 insertions(+), 59 deletions(-) create mode 100644 latest/schemas/coordinate_systems.schema diff --git a/latest/schemas/coordinate_systems.schema b/latest/schemas/coordinate_systems.schema new file mode 100644 index 00000000..583d9941 --- /dev/null +++ b/latest/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/latest/schemas/image.schema b/latest/schemas/image.schema index 3926c4f4..f7d1390c 100644 --- a/latest/schemas/image.schema +++ b/latest/schemas/image.schema @@ -36,15 +36,15 @@ "0.5-dev" ] }, - "axes": { - "$ref": "#/$defs/axes" + "coordinateSystems": { + "$ref": "coordinate_systems.schema" }, "coordinateTransformations": { "$ref": "#/$defs/coordinateTransformations" } }, "required": [ - "datasets", "axes" + "datasets", "coordinateSystems" ] }, "minItems": 1, @@ -107,63 +107,7 @@ } }, "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, From d22b6631b520830aa45489d9337054ac3229c50e Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 5 Dec 2023 15:07:38 -0500 Subject: [PATCH 077/113] axis schema: don't enforce types --- latest/schemas/axes.schema | 8 -------- 1 file changed, 8 deletions(-) diff --git a/latest/schemas/axes.schema b/latest/schemas/axes.schema index 083ffcac..51387e3c 100644 --- a/latest/schemas/axes.schema +++ b/latest/schemas/axes.schema @@ -22,14 +22,6 @@ }, "type": { "type": "string", - "enum": [ - "array", - "space", - "time", - "channel", - "coordinate", - "displacement" - ], "description": "Dimension of the axis" }, "discrete": { From 7add8795a890b84044e4989d32944f5e1d343c11 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 5 Dec 2023 15:44:45 -0500 Subject: [PATCH 078/113] test: strict_image add coordinateSystems --- latest/tests/strict_image_suite.json | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/latest/tests/strict_image_suite.json b/latest/tests/strict_image_suite.json index 049eb93b..1fd6129a 100644 --- a/latest/tests/strict_image_suite.json +++ b/latest/tests/strict_image_suite.json @@ -123,17 +123,22 @@ "data": { "multiscales": [ { - "axes": [ - { - "name": "y", - "type": "space", - "units": "micrometer" - }, - { - "name": "x", - "type": "space", - "units": "micrometer" - } + "coordinateSystems": [ + { + "name": "multiscales_transformations", + "axes": [ + { + "name": "y", + "type": "space", + "units": "micrometer" + }, + { + "name": "x", + "type": "space", + "units": "micrometer" + } + ] + } ], "datasets": [ { From e5b351c0e9b542076ca9d8370d7db2dac66cc038 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 5 Dec 2023 16:52:13 -0500 Subject: [PATCH 079/113] test image, strict_image add input/output to transforms --- latest/tests/image_suite.json | 134 +++++++++++++++++++-------- latest/tests/strict_image_suite.json | 79 ++++++++++------ 2 files changed, 146 insertions(+), 67 deletions(-) diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index ef28b379..f623b451 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -19,7 +19,9 @@ 0.13, 0.13 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "mismatch_axes_units" } ] } @@ -27,7 +29,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "mismatch_axes_units", "axes": [ { "name": "t", @@ -68,7 +70,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "untyped_axes" } ] } @@ -76,7 +80,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "untyped_axes", "axes": [ { "name": "angle" @@ -116,14 +120,16 @@ "scale": [ 1, 1 - ] + ], + "input": "/0", + "output": "missing_version" } ] } ], "coordinateSystems": [ { - "name": "tmpname", + "name": "missing_version", "axes": [ { "name": "y", @@ -158,7 +164,9 @@ 0.13, 0.13 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "invalid_axis_units" } ] } @@ -166,7 +174,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "invalid_axis_units", "axes": [ { "name": "y", @@ -203,7 +211,9 @@ "scale": [ 1, 1 - ] + ], + "input": "/0", + "output": "invalid_axis_units" } ] } @@ -258,7 +268,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "custom_type_axes" } ] } @@ -266,7 +278,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "custom_type_axes", "axes": [ { "name": "angle", @@ -305,7 +317,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "custom_type_axes" } ] } @@ -313,7 +327,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "duplicate_axes", "axes": [ { "name": "x", @@ -348,7 +362,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "no_space_axes" } ] } @@ -356,7 +372,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "no_space_axes", "axes": [ { "name": "t", @@ -389,7 +405,9 @@ 1, 1 ], - "type": "translation" + "type": "translation", + "input": "/0", + "output": "transformation_type" } ] } @@ -397,7 +415,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "transformation_type", "axes": [ { "name": "y", @@ -432,7 +450,9 @@ 1, 1 ], - "type": "translation" + "type": "translation", + "input": "/0", + "output": "transformation_type" } ] } @@ -440,7 +460,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "transformation_type", "axes": [ { "name": "y", @@ -479,7 +499,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "many_axes" } ] } @@ -487,7 +509,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "many_axes", "axes": [ { "name": "angle", @@ -536,7 +558,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "invalid_channels_color" } ] } @@ -544,7 +568,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "invalie_channels_color", "axes": [ { "name": "y", @@ -596,7 +620,9 @@ 0.13, 0.13 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "missing_axes_name" } ] } @@ -604,7 +630,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "missing_axes_name", "axes": [ { "type": "space", @@ -637,7 +663,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "invalid_axes_count" } ] } @@ -645,7 +673,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "tmpname", + "name": "invalid_axes_count", "axes": [ { "name": "y", @@ -676,7 +704,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -721,7 +751,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -764,7 +796,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -774,7 +808,9 @@ "scale": [ "invalid" ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ], "version": "0.5-dev", @@ -908,7 +944,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -934,7 +972,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -977,7 +1017,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -1020,14 +1062,18 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" }, { "scale": [ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -1070,7 +1116,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -1104,7 +1152,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -1162,7 +1212,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "out" } ] } @@ -1256,7 +1308,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "missing_path" } ] } diff --git a/latest/tests/strict_image_suite.json b/latest/tests/strict_image_suite.json index 1fd6129a..3c3d2ae2 100644 --- a/latest/tests/strict_image_suite.json +++ b/latest/tests/strict_image_suite.json @@ -55,7 +55,9 @@ 0.5, 0.5, 0.5 - ] + ], + "input": "/0", + "output": "out" } ] }, @@ -70,7 +72,9 @@ 1.0, 1.0, 1.0 - ] + ], + "input": "/1", + "output": "out" } ] }, @@ -85,7 +89,9 @@ 2.0, 2.0, 2.0 - ] + ], + "input": "/2", + "output": "out" } ] } @@ -99,7 +105,9 @@ 1.0, 1.0, 1.0 - ] + ], + "input": "out", + "output": "example" } ], "type": "gaussian", @@ -149,7 +157,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "example" } ] } @@ -160,7 +170,9 @@ 10, 10 ], - "type": "scale" + "type": "scale", + "input": "out", + "output": "multiscales_transformations" } ], "version": "0.5-dev", @@ -194,7 +206,9 @@ "scale": [ 1, 1 - ] + ], + "input": "/path/to/0", + "output": "image_metadata" } ] } @@ -265,7 +279,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "image" } ] } @@ -322,24 +338,31 @@ "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" + "type": "sequence", + "transformations" : [ + { + "scale": [ + 1, + 1, + 0.5, + 0.13, + 0.13 + ], + "type": "scale" + }, + { + "translation": [ + 0, + 9, + 0.5, + 25.74, + 21.58 + ], + "type": "translation" + } + ], + "input": "/0", + "output": "image_omero" } ] }, @@ -354,7 +377,9 @@ 0.26, 0.26 ], - "type": "scale" + "type": "scale", + "input": "/1", + "output": "image_omero" } ] } From 9abfa1e9db30afa489d78abf67e0e2d13c0e3c70 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 5 Dec 2023 16:53:14 -0500 Subject: [PATCH 080/113] start to modularize coordinate systems and transforms schemas * reference in image schema --- latest/examples/coordSystems/.config.json | 2 +- latest/examples/transformations/.config.json | 2 +- .../coordinate_systems_and_transforms.schema | 45 +++ .../schemas/coordinate_transformation.schema | 343 ++++++++++++++++++ latest/schemas/image.schema | 75 +--- 5 files changed, 394 insertions(+), 73 deletions(-) create mode 100644 latest/schemas/coordinate_systems_and_transforms.schema create mode 100644 latest/schemas/coordinate_transformation.schema diff --git a/latest/examples/coordSystems/.config.json b/latest/examples/coordSystems/.config.json index 56e6f885..04f988a8 100644 --- a/latest/examples/coordSystems/.config.json +++ b/latest/examples/coordSystems/.config.json @@ -1,3 +1,3 @@ { - "schema": "schemas/coordinate_transforms.schema" + "schema": "schemas/coordinate_systems_and_transforms.schema" } diff --git a/latest/examples/transformations/.config.json b/latest/examples/transformations/.config.json index 56e6f885..04f988a8 100644 --- a/latest/examples/transformations/.config.json +++ b/latest/examples/transformations/.config.json @@ -1,3 +1,3 @@ { - "schema": "schemas/coordinate_transforms.schema" + "schema": "schemas/coordinate_systems_and_transforms.schema" } diff --git a/latest/schemas/coordinate_systems_and_transforms.schema b/latest/schemas/coordinate_systems_and_transforms.schema new file mode 100644 index 00000000..403f78a5 --- /dev/null +++ b/latest/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/latest/schemas/coordinate_transformation.schema b/latest/schemas/coordinate_transformation.schema new file mode 100644 index 00000000..545d92d8 --- /dev/null +++ b/latest/schemas/coordinate_transformation.schema @@ -0,0 +1,343 @@ +{ + "$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, + "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/latest/schemas/image.schema b/latest/schemas/image.schema index f7d1390c..4dd3954e 100644 --- a/latest/schemas/image.schema +++ b/latest/schemas/image.schema @@ -24,7 +24,7 @@ "type": "string" }, "coordinateTransformations": { - "$ref": "#/$defs/coordinateTransformations" + "$ref": "coordinate_transformation.schema" } }, "required": ["path", "coordinateTransformations"] @@ -40,8 +40,8 @@ "$ref": "coordinate_systems.schema" }, "coordinateTransformations": { - "$ref": "#/$defs/coordinateTransformations" - } + "$ref": "coordinate_transformation.schema" + } }, "required": [ "datasets", "coordinateSystems" @@ -106,72 +106,5 @@ ] } }, - "required": [ "multiscales" ], - "$defs": { - "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"] - } - ] - } - } - } + "required": [ "multiscales" ] } From b7c28f97bdd90f76c7ef0daedc5f040d67bebb56 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 12:07:41 -0500 Subject: [PATCH 081/113] axes schema: allow at most three space axes --- latest/schemas/axes.schema | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/latest/schemas/axes.schema b/latest/schemas/axes.schema index 51387e3c..00b87369 100644 --- a/latest/schemas/axes.schema +++ b/latest/schemas/axes.schema @@ -5,9 +5,30 @@ "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", From 5818a1f1ad2f03630533049f7e2f9396a1d1ca8f Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:45:48 -0500 Subject: [PATCH 082/113] image_suite - one axis is valid --- latest/tests/image_suite.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index f623b451..afef37c5 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -649,7 +649,7 @@ "valid": false }, { - "formerly": "invalid/invalid_axes_count.json", + "formerly": "valid/one_axis.json", "description": "TBD", "data": { "multiscales": [ @@ -665,7 +665,7 @@ ], "type": "scale", "input": "/0", - "output": "invalid_axes_count" + "output": "one_axis" } ] } @@ -673,7 +673,7 @@ "version": "0.5-dev", "coordinateSystems": [ { - "name": "invalid_axes_count", + "name": "one_axis", "axes": [ { "name": "y", @@ -686,7 +686,7 @@ } ] }, - "valid": false + "valid": true }, { "formerly": "valid/one_space_axes.json", From 3896ab4790b6142c69b832f4cf63ce256c47d5fb Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:46:11 -0500 Subject: [PATCH 083/113] image_suite - two scales are valid, but not recommended --- latest/tests/image_suite.json | 37 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index afef37c5..b446abc9 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -1049,7 +1049,7 @@ }, { "formerly": "valid/two_scales.json", - "description": "TBD", + "description": "A sequence of two scales. Valid, but not recommended.", "data": { "multiscales": [ { @@ -1058,22 +1058,25 @@ "path": "0", "coordinateTransformations": [ { - "scale": [ - 1, - 1 - ], - "type": "scale", - "input": "/0", - "output": "out" - }, - { - "scale": [ - 1, - 1 - ], - "type": "scale", - "input": "/0", - "output": "out" + "type": "sequence", + "input": "/0", + "output": "out", + "transformations": [ + { + "scale": [ + 1, + 1 + ], + "type": "scale" + }, + { + "scale": [ + 1, + 1 + ], + "type": "scale" + } + ] } ] } From 2f7c413eedf62700ec57f507482139a57bb4d208 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:51:56 -0500 Subject: [PATCH 084/113] edit subspace example --- .../examples/subspace/subspaceMultidim.json | 100 +++++++----------- 1 file changed, 40 insertions(+), 60 deletions(-) diff --git a/latest/examples/subspace/subspaceMultidim.json b/latest/examples/subspace/subspaceMultidim.json index cfbe5dae..566a4069 100644 --- a/latest/examples/subspace/subspaceMultidim.json +++ b/latest/examples/subspace/subspaceMultidim.json @@ -1,64 +1,44 @@ { - "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"] - } - ] + "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" : "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"] - } - ] + { + "type": "scale", + "scale": [ 2 ], + "input": [ "1" ], + "output": [ "y" ] } - ] + ] + } + ] } From 67cb6d2b69d13c1f79db7212eb669d407ce0fbf1 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:52:40 -0500 Subject: [PATCH 085/113] coordinate_transformation arrays may not be empty --- latest/schemas/coordinate_transformation.schema | 1 + 1 file changed, 1 insertion(+) diff --git a/latest/schemas/coordinate_transformation.schema b/latest/schemas/coordinate_transformation.schema index 545d92d8..e5156da0 100644 --- a/latest/schemas/coordinate_transformation.schema +++ b/latest/schemas/coordinate_transformation.schema @@ -5,6 +5,7 @@ "description": "Coordinate Systems and transforms for OME-NGFF", "type": "array", "uniqueItems": true, + "minItems": 1, "items": { "allOf": [ { From fb88485ab1ec983980fd58e9daed2ded85b966b9 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:52:58 -0500 Subject: [PATCH 086/113] edit example byDimension2 --- .../transformations/byDimension2.json | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/latest/examples/transformations/byDimension2.json b/latest/examples/transformations/byDimension2.json index 0568c9bd..91ea2746 100644 --- a/latest/examples/transformations/byDimension2.json +++ b/latest/examples/transformations/byDimension2.json @@ -1,17 +1,42 @@ { - "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"}, {"name": "k"}, {"name": "l"}] }, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"}, {"name": "z"} ] } - ], - "coordinateTransformations": [ + "coordinateSystems": [ + { + "name": "in", + "axes": [ + { "name": "i", "type": "array" }, + { "name": "j", "type": "array" }, + { "name": "k", "type": "array" }, + { "name": "l", "type": "array" } + ] + }, + { + "name": "out", + "axes": [ + { "name": "x", "type": "array" }, + { "name": "y", "type": "array" }, + { "name": "z", "type": "array" } + ] + } + ], + "coordinateTransformations": [ + { + "type": "byDimension", + "input": "in", + "output": "out", + "transformations": [ { - "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"]} - ] + "type": "translation", + "translation": [ 1, 3 ], + "input": [ "i", "k" ], + "output": [ "y", "x" ] + }, + { + "type": "scale", + "scale": [ 2 ], + "input": [ "j" ], + "output": [ "z" ] } - ] + ] + } + ] } From 85437f29238c843b2d09990bad61e7bae9260e9d Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:54:38 -0500 Subject: [PATCH 087/113] edit multiscales examples --- .../multiscales_example_relative.json | 25 +++++++++++++++---- .../multiscales_transformations.json | 15 +++++++++-- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/latest/examples/multiscales_strict/multiscales_example_relative.json b/latest/examples/multiscales_strict/multiscales_example_relative.json index c0b10d5b..1d1864e0 100644 --- a/latest/examples/multiscales_strict/multiscales_example_relative.json +++ b/latest/examples/multiscales_strict/multiscales_example_relative.json @@ -13,12 +13,27 @@ {"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" + "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", @@ -26,8 +41,8 @@ // 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" + "input" : "/1", + "output" : "array_0" }] }, { @@ -37,7 +52,7 @@ "type": "scale", "scale": [1, 1, 4, 4, 4], "input" : "/2", - "output" : "/0" + "output" : "array_0" }] } ], @@ -45,7 +60,7 @@ // 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", + "input" : "array_0", "output" : "exampleCoordinateSystem" }], "type": "gaussian", diff --git a/latest/examples/multiscales_strict/multiscales_transformations.json b/latest/examples/multiscales_strict/multiscales_transformations.json index 2767ce4c..7fd7c9aa 100644 --- a/latest/examples/multiscales_strict/multiscales_transformations.json +++ b/latest/examples/multiscales_strict/multiscales_transformations.json @@ -2,6 +2,13 @@ "multiscales": [ { "coordinateSystems" : [ + { + "name": "multiscales_transformations_intermediate", + "axes": [ + { "name": "y", "type": "space", "units": "micrometer" }, + { "name": "x", "type": "space", "units": "micrometer" } + ] + }, { "name": "multiscales_transformations", "axes": [ @@ -19,7 +26,9 @@ 1, 1 ], - "type": "scale" + "type": "scale", + "input": "/0", + "output": "multiscales_transformations_intermediate" } ] } @@ -30,7 +39,9 @@ 10, 10 ], - "type": "scale" + "type": "scale", + "input": "multiscales_transformations_intermediate", + "output": "multiscales_transformations" } ], "version": "0.5-dev", From a9fadf30a29e417744994d0158d9b36bdc979f20 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 14:56:15 -0500 Subject: [PATCH 088/113] add schema config for subspace examples --- latest/examples/subspace/.config.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 latest/examples/subspace/.config.json diff --git a/latest/examples/subspace/.config.json b/latest/examples/subspace/.config.json new file mode 100644 index 00000000..04f988a8 --- /dev/null +++ b/latest/examples/subspace/.config.json @@ -0,0 +1,3 @@ +{ + "schema": "schemas/coordinate_systems_and_transforms.schema" +} From a0cd017fbd9ca400696f4b2d507f3efb13b3537b Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 6 Dec 2023 15:00:58 -0500 Subject: [PATCH 089/113] rm coordinate_transforms.schema * refactored into coordinate_transformation.schema and coordinate_systems_and_transforms.schema --- latest/schemas/coordinate_transforms.schema | 402 -------------------- 1 file changed, 402 deletions(-) delete mode 100644 latest/schemas/coordinate_transforms.schema diff --git a/latest/schemas/coordinate_transforms.schema b/latest/schemas/coordinate_transforms.schema deleted file mode 100644 index 1219563a..00000000 --- a/latest/schemas/coordinate_transforms.schema +++ /dev/null @@ -1,402 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://ngff.openmicroscopy.org/latest/schemas/coordinate_transforms.schema", - "title": "NGFF Coordinate Systems and Transforms", - "description": "Coordinate Systems and transforms for OME-NGFF", - "type": "object", - "properties": { - "coordinateSystems": { - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/$defs/coordinateSystem" - } - }, - "coordinateTransformations": { - "type": "array", - "uniqueItems": true, - "items": { - "allOf": [ - { - "$ref": "#/$defs/coordinateTransformation" - }, - { - "type": "object", - "properties": { - "input": { - "type": "string" - }, - "output": { - "type": "string" - } - }, - "required": [ - "input", - "output" - ] - } - ] - } - }, - "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" - ] - } - }, - "$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" - ] - }, - "coordinateSystem": { - "description": "Coordinate Systems for OME-NGFF", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of coordinate space" - }, - "axes": { - "$ref": "axes.schema" - } - }, - "required": [ - "name", - "axes" - ] - }, - "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" } - } - } - } -} From 150f1f84f4ffabae0584618a91fbc9786c32ea80 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Thu, 7 Dec 2023 15:14:51 -0500 Subject: [PATCH 090/113] multiscales-strict, re-order coordinate systems * minor formatting --- .../multiscales_transformations.json | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/latest/examples/multiscales_strict/multiscales_transformations.json b/latest/examples/multiscales_strict/multiscales_transformations.json index 7fd7c9aa..8dc6ca25 100644 --- a/latest/examples/multiscales_strict/multiscales_transformations.json +++ b/latest/examples/multiscales_strict/multiscales_transformations.json @@ -3,14 +3,14 @@ { "coordinateSystems" : [ { - "name": "multiscales_transformations_intermediate", + "name": "multiscales_transformations", "axes": [ { "name": "y", "type": "space", "units": "micrometer" }, { "name": "x", "type": "space", "units": "micrometer" } ] }, { - "name": "multiscales_transformations", + "name": "multiscales_transformations_intermediate", "axes": [ { "name": "y", "type": "space", "units": "micrometer" }, { "name": "x", "type": "space", "units": "micrometer" } @@ -22,10 +22,7 @@ "path": "0", "coordinateTransformations": [ { - "scale": [ - 1, - 1 - ], + "scale": [ 1, 1 ], "type": "scale", "input": "/0", "output": "multiscales_transformations_intermediate" @@ -35,10 +32,7 @@ ], "coordinateTransformations": [ { - "scale": [ - 10, - 10 - ], + "scale": [ 10, 10 ], "type": "scale", "input": "multiscales_transformations_intermediate", "output": "multiscales_transformations" From 61eb70aafa5f128f0dc32b7ade256e587ed39e19 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Thu, 7 Dec 2023 15:15:40 -0500 Subject: [PATCH 091/113] schema: add strict_axes, and strict_coordinate_systems * update strict_image accordingly * add an invalid strict_multiscales example (custom axis) --- latest/schemas/strict_axes.schema | 30 ++++++ .../schemas/strict_coordinate_systems.schema | 18 ++++ latest/schemas/strict_image.schema | 7 +- latest/tests/strict_image_suite.json | 101 +++++++++++++++++- 4 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 latest/schemas/strict_axes.schema create mode 100644 latest/schemas/strict_coordinate_systems.schema diff --git a/latest/schemas/strict_axes.schema b/latest/schemas/strict_axes.schema new file mode 100644 index 00000000..522602d2 --- /dev/null +++ b/latest/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/latest/schemas/strict_coordinate_systems.schema b/latest/schemas/strict_coordinate_systems.schema new file mode 100644 index 00000000..425ccbe5 --- /dev/null +++ b/latest/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/latest/schemas/strict_image.schema b/latest/schemas/strict_image.schema index bcecc003..4f631ba2 100644 --- a/latest/schemas/strict_image.schema +++ b/latest/schemas/strict_image.schema @@ -8,6 +8,11 @@ "properties": { "multiscales": { "items": { + "properties" : { + "coordinateSystems": { + "$ref": "strict_coordinate_systems.schema" + } + }, "required": [ "version", "metadata", "type", "name" ] @@ -16,4 +21,4 @@ } } ] -} \ No newline at end of file +} diff --git a/latest/tests/strict_image_suite.json b/latest/tests/strict_image_suite.json index 3c3d2ae2..b9b799cc 100644 --- a/latest/tests/strict_image_suite.json +++ b/latest/tests/strict_image_suite.json @@ -14,7 +14,36 @@ "name": "example", "coordinateSystems": [ { - "name": "multiscales_example", + "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" + } + ] + }, + { + "name": "tmp", "axes": [ { "name": "t", @@ -57,7 +86,7 @@ 0.5 ], "input": "/0", - "output": "out" + "output": "tmp" } ] }, @@ -74,7 +103,7 @@ 1.0 ], "input": "/1", - "output": "out" + "output": "tmp" } ] }, @@ -91,7 +120,7 @@ 2.0 ], "input": "/2", - "output": "out" + "output": "tmp" } ] } @@ -106,7 +135,7 @@ 1.0, 1.0 ], - "input": "out", + "input": "tmp", "output": "example" } ], @@ -125,6 +154,68 @@ }, "valid": true }, + { + "formerly": "invalid_strict/multiscales_nonstandard_axis_type.json", + "description": "TBD", + "data": { + "multiscales": [ + { + "version": "0.5-dev", + "name": "example", + "coordinateSystems": [ + { + "name": "nonstandard_axis", + "axes": [ + { + "name": "c", + "type": "custom", + "unit": "" + }, + { + "name": "y", + "type": "space", + "unit": "micrometer" + }, + { + "name": "x", + "type": "space", + "unit": "micrometer" + } + ] + } + ], + "datasets": [ + { + "path": "0", + "coordinateTransformations": [ + { + "type": "scale", + "scale": [ + 1.0, + 1.0, + 1.0 + ], + "input": "/0", + "output": "nonstandard_axis" + } + ] + } + ], + "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": false + }, { "formerly": "valid_strict/multiscales_transformations.json", "description": "TBD", From aa0542db45fe3838856387b1d83522b5f67da1c7 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Fri, 8 Dec 2023 09:28:59 -0500 Subject: [PATCH 092/113] correct byDimensionXarray transform example * explicitly add array coordinate system * add input field to transform --- .../transformations/byDimensionXarray.json | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 latest/examples/transformations/byDimensionXarray.json diff --git a/latest/examples/transformations/byDimensionXarray.json b/latest/examples/transformations/byDimensionXarray.json new file mode 100644 index 00000000..82531b0e --- /dev/null +++ b/latest/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" ] + } + ] + } + ] +} From bc4f395193b63ed671d4e4bdc0d7477c58abada8 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Fri, 8 Dec 2023 12:05:58 -0500 Subject: [PATCH 093/113] "units" -> "unit" --- .../multiscales_transformations.json | 8 +- latest/schemas/axes.schema | 4 +- latest/tests/image_suite.json | 92 +++++++++---------- latest/tests/strict_image_suite.json | 32 +++---- 4 files changed, 68 insertions(+), 68 deletions(-) diff --git a/latest/examples/multiscales_strict/multiscales_transformations.json b/latest/examples/multiscales_strict/multiscales_transformations.json index 8dc6ca25..192cae17 100644 --- a/latest/examples/multiscales_strict/multiscales_transformations.json +++ b/latest/examples/multiscales_strict/multiscales_transformations.json @@ -5,15 +5,15 @@ { "name": "multiscales_transformations", "axes": [ - { "name": "y", "type": "space", "units": "micrometer" }, - { "name": "x", "type": "space", "units": "micrometer" } + { "name": "y", "type": "space", "unit": "micrometer" }, + { "name": "x", "type": "space", "unit": "micrometer" } ] }, { "name": "multiscales_transformations_intermediate", "axes": [ - { "name": "y", "type": "space", "units": "micrometer" }, - { "name": "x", "type": "space", "units": "micrometer" } + { "name": "y", "type": "space", "unit": "micrometer" }, + { "name": "x", "type": "space", "unit": "micrometer" } ] } ], diff --git a/latest/schemas/axes.schema b/latest/schemas/axes.schema index 00b87369..56b447f2 100644 --- a/latest/schemas/axes.schema +++ b/latest/schemas/axes.schema @@ -49,9 +49,9 @@ "type": "boolean", "description": "Whether the dimension is discrete" }, - "units": { + "unit": { "type": "string", - "description": "Units for the axis" + "description": "Unit for the axis" } }, "required": [ diff --git a/latest/tests/image_suite.json b/latest/tests/image_suite.json index b446abc9..e017dac0 100644 --- a/latest/tests/image_suite.json +++ b/latest/tests/image_suite.json @@ -34,17 +34,17 @@ { "name": "t", "type": "time", - "units": "micrometer" + "unit": "micrometer" }, { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -88,12 +88,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -134,12 +134,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -179,12 +179,12 @@ { "name": "y", "type": "space", - "units": "micron" + "unit": "micron" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -237,12 +237,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -287,12 +287,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -332,12 +332,12 @@ { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -420,12 +420,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -465,12 +465,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -573,12 +573,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -634,11 +634,11 @@ "axes": [ { "type": "space", - "units": "micron" + "unit": "micron" }, { "type": "space", - "units": "micron" + "unit": "micron" } ] } @@ -678,7 +678,7 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -766,12 +766,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -821,12 +821,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -855,12 +855,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -885,12 +885,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -914,12 +914,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -987,12 +987,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -1032,12 +1032,12 @@ { "name": "y", "type": "invalid", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -1089,12 +1089,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -1230,12 +1230,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -1282,12 +1282,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -1326,12 +1326,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } diff --git a/latest/tests/strict_image_suite.json b/latest/tests/strict_image_suite.json index f41be352..d55069fb 100644 --- a/latest/tests/strict_image_suite.json +++ b/latest/tests/strict_image_suite.json @@ -260,12 +260,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -310,12 +310,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -371,12 +371,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -399,12 +399,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -438,12 +438,12 @@ { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -474,17 +474,17 @@ { "name": "z", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } @@ -561,17 +561,17 @@ { "name": "z", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "y", "type": "space", - "units": "micrometer" + "unit": "micrometer" }, { "name": "x", "type": "space", - "units": "micrometer" + "unit": "micrometer" } ] } From 802c94fbd5d61ecb15d2432c0ea243520b651a48 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Thu, 25 Jul 2024 14:22:24 -0400 Subject: [PATCH 094/113] toward clarity on axis order * changed example axis names to follow current convention * TODO coordinate/displacement field vector order * thanks @thewtex --- .../examples/coordSystems/arrayCoordSys.json | 4 +-- .../examples/transformations/affine2d2d.json | 8 ++--- .../transformations/affine2d3d_nested.json | 8 ++--- .../examples/transformations/bijection.json | 4 +-- .../transformations/byDimension1.json | 4 +-- .../transformations/byDimension2.json | 8 ++--- .../transformations/byDimensionInvalid1.json | 4 +-- .../transformations/byDimensionInvalid2.json | 4 +-- latest/examples/transformations/identity.json | 4 +-- .../examples/transformations/inverseOf.json | 6 ++-- latest/examples/transformations/mapAxis1.json | 8 ++--- latest/examples/transformations/mapAxis2.json | 4 +-- latest/examples/transformations/rotation.json | 10 +++--- latest/examples/transformations/scale.json | 4 +-- latest/examples/transformations/sequence.json | 4 +-- .../examples/transformations/translation.json | 4 +-- latest/index.bs | 35 ++++++++++++------- 17 files changed, 67 insertions(+), 56 deletions(-) diff --git a/latest/examples/coordSystems/arrayCoordSys.json b/latest/examples/coordSystems/arrayCoordSys.json index 9c7e1229..812356f8 100644 --- a/latest/examples/coordSystems/arrayCoordSys.json +++ b/latest/examples/coordSystems/arrayCoordSys.json @@ -2,9 +2,9 @@ "arrayCoordinateSystem" : { "name" : "myDataArray", "axes" : [ - {"name": "i", "type": "array"}, + {"name": "k", "type": "array"}, {"name": "j", "type": "array"}, - {"name": "k", "type": "array"} + {"name": "i", "type": "array"} ] } } diff --git a/latest/examples/transformations/affine2d2d.json b/latest/examples/transformations/affine2d2d.json index f85e3b36..c4322481 100644 --- a/latest/examples/transformations/affine2d2d.json +++ b/latest/examples/transformations/affine2d2d.json @@ -1,14 +1,14 @@ { "coordinateSystems" : [ - { "name": "ij", "axes": [{"name": "i"}, {"name": "j"}] }, - { "name": "xy", "axes": [{"name": "x"}, {"name": "y"}] } + { "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": "ij", - "output": "xy" + "input": "ji", + "output": "yx" } ] } diff --git a/latest/examples/transformations/affine2d3d_nested.json b/latest/examples/transformations/affine2d3d_nested.json index 6c0dce02..db904248 100644 --- a/latest/examples/transformations/affine2d3d_nested.json +++ b/latest/examples/transformations/affine2d3d_nested.json @@ -1,15 +1,15 @@ { "coordinateSystems" : [ - { "name": "ij", "axes": [{"name": "i"}, {"name": "j"}] }, - { "name": "xy", "axes": [{"name": "x"}, {"name": "y"}] } + { "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": "ij", - "output": "xy" + "input": "ji", + "output": "yx" } ] } diff --git a/latest/examples/transformations/bijection.json b/latest/examples/transformations/bijection.json index bc74b986..f988ad87 100644 --- a/latest/examples/transformations/bijection.json +++ b/latest/examples/transformations/bijection.json @@ -1,7 +1,7 @@ { "coordinateSystems" : [ - { "name": "src", "axes": [{"name": "i"}, {"name": "j"}] }, - { "name": "tgt", "axes": [{"name": "x"}, {"name": "y"}] } + { "name": "src", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "tgt", "axes": [{"name": "y"}, {"name": "x"}] } ], "coordinateTransformations" : [ { diff --git a/latest/examples/transformations/byDimension1.json b/latest/examples/transformations/byDimension1.json index a42ed37a..f4f9c42e 100644 --- a/latest/examples/transformations/byDimension1.json +++ b/latest/examples/transformations/byDimension1.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ] }, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ] } ], "coordinateTransformations": [ { diff --git a/latest/examples/transformations/byDimension2.json b/latest/examples/transformations/byDimension2.json index 91ea2746..ef2e29b4 100644 --- a/latest/examples/transformations/byDimension2.json +++ b/latest/examples/transformations/byDimension2.json @@ -3,18 +3,18 @@ { "name": "in", "axes": [ - { "name": "i", "type": "array" }, + { "name": "l", "type": "array" }, { "name": "j", "type": "array" }, { "name": "k", "type": "array" }, - { "name": "l", "type": "array" } + { "name": "i", "type": "array" } ] }, { "name": "out", "axes": [ - { "name": "x", "type": "array" }, + { "name": "z", "type": "array" }, { "name": "y", "type": "array" }, - { "name": "z", "type": "array" } + { "name": "x", "type": "array" } ] } ], diff --git a/latest/examples/transformations/byDimensionInvalid1.json b/latest/examples/transformations/byDimensionInvalid1.json index 8d3c9696..4054fc19 100644 --- a/latest/examples/transformations/byDimensionInvalid1.json +++ b/latest/examples/transformations/byDimensionInvalid1.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ] }, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ] } ], "coordinateTransformations": [ { diff --git a/latest/examples/transformations/byDimensionInvalid2.json b/latest/examples/transformations/byDimensionInvalid2.json index fdd3ac4b..810f0c04 100644 --- a/latest/examples/transformations/byDimensionInvalid2.json +++ b/latest/examples/transformations/byDimensionInvalid2.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ] }, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ] } + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ] }, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ] } ], "coordinateTransformations": [ { diff --git a/latest/examples/transformations/identity.json b/latest/examples/transformations/identity.json index 3ea1529a..8de6d9d5 100644 --- a/latest/examples/transformations/identity.json +++ b/latest/examples/transformations/identity.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ]}, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ]} ], "coordinateTransformations": [ { "type": "identity", "input": "in", "output": "out" } diff --git a/latest/examples/transformations/inverseOf.json b/latest/examples/transformations/inverseOf.json index 3e2966c5..ef12a2fe 100644 --- a/latest/examples/transformations/inverseOf.json +++ b/latest/examples/transformations/inverseOf.json @@ -1,7 +1,7 @@ { "coordinateSystems" : [ - { "name" : "moving", "axes" : [{"name" : "x-moving"}, {"name":"y-moving"}] }, - { "name" : "fixed", "axes" : [{"name" : "x-fixed"}, {"name":"y-fixed"}] } + { "name" : "moving", "axes" : [{"name" : "y-moving"}, {"name":"x-moving"}] }, + { "name" : "fixed", "axes" : [{"name" : "y-fixed"}, {"name":"x-fixed"}] } ], "coordinateTransformations" : [ { @@ -11,4 +11,4 @@ "output" : "fixed" } ] -} \ No newline at end of file +} diff --git a/latest/examples/transformations/mapAxis1.json b/latest/examples/transformations/mapAxis1.json index 2b6b0b79..37d3c73c 100644 --- a/latest/examples/transformations/mapAxis1.json +++ b/latest/examples/transformations/mapAxis1.json @@ -1,8 +1,8 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, - { "name": "out1", "axes": [ {"name": "x"}, {"name": "y"} ]}, - { "name": "out2", "axes": [ {"name": "x"}, {"name": "y"} ]} + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ]}, + { "name": "out1", "axes": [ {"name": "y"}, {"name": "x"} ]}, + { "name": "out2", "axes": [ {"name": "y"}, {"name": "x"} ]} ], "coordinateTransformations": [ { @@ -15,7 +15,7 @@ { "name": "permutation", "type": "mapAxis", - "mapAxis": { "x":"j", "y":"i" }, + "mapAxis": { "y":"i", "x":"j" }, "input": "in", "output": "out2" } diff --git a/latest/examples/transformations/mapAxis2.json b/latest/examples/transformations/mapAxis2.json index c8336aef..4b50d1f5 100644 --- a/latest/examples/transformations/mapAxis2.json +++ b/latest/examples/transformations/mapAxis2.json @@ -2,7 +2,7 @@ "coordinateSystems": [ { "name": "in", "axes": [ {"name": "a"}, {"name": "b"}]}, { "name": "out_down", "axes": [ {"name": "x"}]}, - { "name": "out_up", "axes": [ {"name": "x"}, {"name": "y"}, {"name": "z"} ]} + { "name": "out_up", "axes": [ {"name": "z"}, {"name": "y"}, {"name": "x"} ]} ], "coordinateTransformations": [ { @@ -15,7 +15,7 @@ { "name": "projection up", "type": "mapAxis", - "mapAxis": { "x": "a", "y": "b", "z": "b" }, + "mapAxis": { "z": "b", "y": "b", "x": "a" }, "input": "in", "output": "out_up" } diff --git a/latest/examples/transformations/rotation.json b/latest/examples/transformations/rotation.json index 5e73ff33..5c0af6aa 100644 --- a/latest/examples/transformations/rotation.json +++ b/latest/examples/transformations/rotation.json @@ -1,14 +1,14 @@ { "coordinateSystems" : [ - { "name" : "ij", "axes" : [{"name" : "i"}, {"name":"j"}] }, - { "name" : "xy", "axes" : [{"name" : "x"}, {"name":"y"}] } + { "name" : "ji", "axes" : [{"name" : "j"}, {"name":"i"}] }, + { "name" : "yx", "axes" : [{"name" : "y"}, {"name":"x"}] } ], "coordinateTransformations" : [ { "type": "rotation", "rotation": [0, -1, 1, 0], - "input" : "ij", - "output" : "xy" + "input" : "ji", + "output" : "yx" } ] -} \ No newline at end of file +} diff --git a/latest/examples/transformations/scale.json b/latest/examples/transformations/scale.json index b5c83309..56d348a5 100644 --- a/latest/examples/transformations/scale.json +++ b/latest/examples/transformations/scale.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [{"name": "i"}, {"name": "j"}] }, - { "name": "out", "axes": [{"name": "x"}, {"name": "y"}] } + { "name": "in", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "out", "axes": [{"name": "y"}, {"name": "x"}] } ], "coordinateTransformations": [ { diff --git a/latest/examples/transformations/sequence.json b/latest/examples/transformations/sequence.json index 1d88b21d..e781c108 100644 --- a/latest/examples/transformations/sequence.json +++ b/latest/examples/transformations/sequence.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [ {"name": "i"}, {"name": "j"} ]}, - { "name": "out", "axes": [ {"name": "x"}, {"name": "y"} ]} + { "name": "in", "axes": [ {"name": "j"}, {"name": "i"} ]}, + { "name": "out", "axes": [ {"name": "y"}, {"name": "x"} ]} ], "coordinateTransformations": [ { diff --git a/latest/examples/transformations/translation.json b/latest/examples/transformations/translation.json index cbc32ec9..5e28be7a 100644 --- a/latest/examples/transformations/translation.json +++ b/latest/examples/transformations/translation.json @@ -1,7 +1,7 @@ { "coordinateSystems": [ - { "name": "in", "axes": [{"name": "i"}, {"name": "j"}] }, - { "name": "out", "axes": [{"name": "x"}, {"name": "y"}] } + { "name": "in", "axes": [{"name": "j"}, {"name": "i"}] }, + { "name": "out", "axes": [{"name": "y"}, {"name": "x"}] } ], "coordinateTransformations" : [ { diff --git a/latest/index.bs b/latest/index.bs index ce8d37ba..09ab3012 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -489,8 +489,7 @@ The dimensionality of each array coordinate system equals the dimensionality of 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. +the last dimension of an array in "C" order are stored contiguously on disk or in-memory when directly loaded.
For example, if `/my/data/array/.zarray` contains: @@ -523,9 +522,12 @@ path: examples/coordSystems/arrayCoordSys.json highlight: json +Note that dimension `i` is contiguous in memory. +
+ ### Coordinate convention **The pixel/voxel center is the origin of the continuous coordinate system.** @@ -660,11 +662,11 @@ The sequence transformation's input corresponds to an array coordinate system at ```json "coordinateSystems" : [ - { "name" : "in", "axes" : [{"name" : "i"}, {"name":"j"}] }, - { "name" : "outScale", "axes" : [{"name" : "x"}, {"name":"y"}] }, - { "name" : "outSeq", "axes" : [{"name" : "x"}, {"name":"y"}] }, - { "name" : "outInv", "axes" : [{"name" : "x"}, {"name":"y"}] }, - { "name" : "outByDim", "axes" : [{"name" : "x"}, {"name":"y"}] } + { "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" : [ { @@ -776,7 +778,9 @@ y = j 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. +coordinate system with that name. Note that the order of the keys could be reversed. + +
@@ -917,6 +921,11 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m
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
@@ -1037,10 +1046,12 @@ and is invertible.
 
 #### coordinates and displacements
 
-`coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a transformation.
-Applying the transformation amounts to looking up the appropriate locations in the array and interpolating.
-`coordinates` and `displacements` transformations are not invertible in general, but implementations
-MAY approximate their inverses. Metadata for these coordinate transforms have the following field: 
+TODO define on-disk and in-memory layout of displacement fields.
+
+`coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a
+transformation. Applying the transformation amounts to looking up the appropriate locations in the array and interpolating.
+`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
From 51417a6cda565d0df0022b167555009ea05ad6ed Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Thu, 25 Jul 2024 14:26:16 -0400 Subject: [PATCH 095/113] typo fix --- latest/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/latest/index.bs b/latest/index.bs index 09ab3012..e3570bd6 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -1260,7 +1260,7 @@ path: examples/transformations/bijection.json highlight: json
-the input and output of the `forward` and `inverse` transformations are understoood to be: +the input and output of the `forward` and `inverse` transformations are understood to be:
 path: examples/transformations/bijection_verbose.json

From cd92ec1d4d8b6948a27f257c6c2c4a5478b91124 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Fri, 26 Jul 2024 15:44:23 -0400
Subject: [PATCH 096/113] rm spurious / in 

* fixes rendering using new bikeshed
---
 latest/index.bs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index e3570bd6..7d0ce624 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -1562,8 +1562,7 @@ Projects which support reading and/or writing OME-NGFF data include:
 
 
 
-Diagram of related projects
+Diagram of related projects
 
 All implementations prevent an equivalent representation of a dataset which can be downloaded or uploaded freely. An interactive
 version of this diagram is available from the [OME2020 Workshop](https://downloads.openmicroscopy.org/presentations/2020/Dundee/Workshops/NGFF/zarr_diagram/).

From 701867eee5e01bf6f428602962f957e4e5ef0b1d Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Sat, 27 Jul 2024 16:47:37 -0400
Subject: [PATCH 097/113] clarify axis order for coordinate and displacement
 fields

* including on-disk representation
* add an example
---
 latest/index.bs | 86 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 71 insertions(+), 15 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index 7d0ce624..e19d607c 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -924,7 +924,7 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m
 
     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).
+    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
@@ -1046,19 +1046,72 @@ and is invertible.
 
 #### coordinates and displacements
 
-TODO define on-disk and in-memory layout of displacement fields.
-
 `coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a
-transformation. Applying the transformation amounts to looking up the appropriate locations in the array and interpolating.
+transformation. Applying the transformation amounts to looking up the appropriate locations in the array and interpolating
+if necessary.
+
+These transformation types refer to an array at location specified by the `"path"` parameter whose coordinate sytem MUST contain
+an axis with type `coordinate` or `displacement` respectively for transformations of type `coordinates` or `displacements`. This
+axis MUST be of length equal to the number of axes of the output coordinate system and SHOULD be the last axis (contiguous on
+disk when c-order). The coordinate system MUST also contain an axis identical to every axis of its input coordinate system in
+the same order. As a consequence, the array will have one dimension more than its input coordinate system.
+
+The `i`th value of the array along the `coordinate` or `displacement` axis refers to the displacement
+or coordinate of `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`, the other two dimensions MUST be axes that are identical to +the axes of the `"in"` coordinate system. + +``` +"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 array at location should have a coordinate system such as: + +``` +"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) containter.
+
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 transforing points not on the array's discrete grid. + the interpolation to use if transforming points not on the array's discrete grid. Values could be:
  • linear (default)
  • @@ -1067,20 +1120,23 @@ inverses. Metadata for these coordinate transforms have the following field:
-The array data at `path` MUST define space and coordinate transform metadata: +For both `coordinates` and `displacements`, the array data at referred to by `path` MUST define space and coordinate transform metadata: -* space metadata MUST have exactly one axis with `"type" : "coordinate"` -* Every axis name in the `coordinateTransform`'s `input` MUST appear in the space -* The array dimension corresponding to the coordinate axis MUST have length greater than or equal to the dimensionality of the `coordinateTransform` `output` -* SHOULD have `name` equal to the `name` of the corresponding `coordinateTransform`. +* 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 dimensionality of the `coordinateTransform` `output` +* SHOULD have a `name` identical to the `name` of the corresponding `coordinateTransform`. For `coordinates`: -* If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. * coordinateSystem metadata MUST have exactly one axis with `"type" : "coordinate"` +* If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. +* the size of the array along the "coordinate" axis must be exactly `N` For `displacements`: + * coordinateSystem metadata MUST have exactly one axis with `"type" : "displacement"` +* If a `displacements`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. +* the size 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: @@ -1118,7 +1174,7 @@ Example metadata for the array data at path `coordinates` above: } ``` -If the arrray in `coordinates` contains the data: `[-9, 9, 0]`, then this metadata defines the function: +If the array in `coordinates` contains the data: `[-9, 9, 0]`, then this metadata defines the function: ``` x = @@ -1163,7 +1219,7 @@ Example metadata for the array data at path `displacements` above: ] } ``` -If the arrray in `displacements` contains the data: `[-1, 0, 1]`, +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. @@ -1249,7 +1305,7 @@ transformation's `input` (`output`) matches the bijection's `output` (`input`), 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 -approprite dimensionality. +appropriate dimensionality.
From 81cc4a5113f01ae9da6696ac663562d3460289a3 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sat, 27 Jul 2024 16:47:52 -0400 Subject: [PATCH 098/113] minor rephrasing in inverseOf --- latest/index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index e19d607c..ea99753a 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -721,7 +721,7 @@ i.e., the mapping from the first input axis to the first output axis is determin 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 should be specified using the `inverseOf` +forward transformation. Inverse transformations used for image rendering may be specified using the `inverseOf` transformation type, for example: ```json @@ -996,7 +996,7 @@ 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 should be enclosed in an `inverseOf` + 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.
From 7dec4fe362d343b8d2397bf5bc3868ad7c9e2a78 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Sun, 28 Jul 2024 15:28:58 -0400 Subject: [PATCH 099/113] toward resolving ambiguity in "sequence" transformations * thanks @LucaMarconato --- latest/index.bs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index ea99753a..106ae2ed 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -1015,15 +1015,30 @@ For example #### sequence A `sequence` transformation consists of an ordered array of coordinate transformations, and is invertible if and only if every -coordinate transform in the array is invertible. +coordinate transform in the array is invertible. 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. + +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` (see the note + below), and under all other conditions. +
transformations
A non-empty array of transformations.
-The `input` and `output` fields SHOULD be omitted for transformations that are in the list of transformations of a `sequence`. -
This sequence: From 6ebcf5fd952e206c28d680fa55968eac202b223d Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 29 Jul 2024 15:20:23 -0400 Subject: [PATCH 100/113] add anchors for transformation types --- latest/index.bs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 106ae2ed..0f23e9ce 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -749,7 +749,7 @@ length gives the input imension, otherwise it is given by the length of "axes" f the name of the "input". If the value of "output" is an array, it's length gives the output dimension, otherwise it is given by the length of "axes" for the coordinate system with the name of the "output". -#### identity +#### 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 @@ -772,7 +772,7 @@ y = j
-#### mapAxis +#### 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 @@ -781,7 +781,6 @@ system, the `mapAxis` MUST have a corresponding field. For every value of the ob coordinate system with that name. Note that the order of the keys could be reversed. -
@@ -827,7 +826,7 @@ z = b
 ```
 
-#### translation +#### 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 @@ -887,7 +886,7 @@ y = 2 * j ```
-#### affine +#### affine `affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` matrices in homogeneous coordinates. This transformation type is invertible when `N` equals `M`. @@ -948,7 +947,7 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m
-#### rotation +#### rotation `rotation` transformations are special cases of affine transformations. When possible, a rotation transformation SHOULD be defined rather than @@ -986,7 +985,7 @@ are invertible.
-#### inverseOf +#### 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. @@ -1012,7 +1011,7 @@ For example -#### sequence +#### sequence A `sequence` transformation consists of an ordered array of coordinate transformations, and is invertible if and only if every coordinate transform in the array is invertible. To apply a sequence transformation to a point in the input coordinate system, @@ -1059,7 +1058,7 @@ and is invertible. -#### coordinates and displacements +#### coordinates and displacements `coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a transformation. Applying the transformation amounts to looking up the appropriate locations in the array and interpolating @@ -1243,7 +1242,7 @@ The transformation specifies linear interpolation, which in this case yields input point, hence the output is `1.0 + (-0.5) = 0.5`. -#### byDimension +#### byDimension {#trafo-byDimension} `byDimension` transformations build a high dimensional transformation using lower dimensional transformations on subsets of dimensions. @@ -1307,7 +1306,7 @@ This transformation is invalid because the output axis `x` appears in more than -#### bijection +#### 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. From 00b79f12994cd5c991b62f1aba6adbcc930b9a16 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 29 Jul 2024 15:21:06 -0400 Subject: [PATCH 101/113] clarification on affine and rotation transformation * adds section on matrix transformations --- latest/index.bs | 56 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 0f23e9ce..736d5fa6 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -740,6 +740,43 @@ operation is requested that requires the inverse of a transformation that can no 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 may be stored either as row-major flat (one-dimensional) arrays or two-dimensional +arrays, either in json or stored 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 3 rows and 2 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 Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value @@ -888,7 +925,7 @@ y = 2 * j #### affine -`affine` transformations from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` +`affine`s are [matrix transformations](#matrix-transformations) from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` matrices in homogeneous coordinates. This transformation type is invertible when `N` equals `M`. The matrix may be stored as a 2D array (inner arrays represent the rows of the matrix) or as a 1D array (row-major). @@ -896,7 +933,7 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m
path
The path to a zarr-array containing the affine parameters. The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. - If 2D its size must be `N x (M+1)`.
+ If 2D its shape must be `N x (M+1)`.
affine
The affine parameters stored in JSON. The matrix may be stored as a row-major flat array of numbers that MUST be length `N*(M+1)`, or as 2D nested array where the outer array MUST be length `N` and the inner arrays MUST be length `M+1`.
@@ -949,20 +986,17 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m #### rotation -`rotation` transformations are special cases of affine transformations. -When possible, a rotation transformation SHOULD be defined rather than -its equivalent affine. Input and output dimensionality (N) MUST be -identical and greater than 1. Rotations are stored as `NxN` matrices, -see below, and MUST have determinant equal to one, with orthonormal rows -and columns. The matrix may be stored as a 2D array (inner arrays represent -the rows of the matrix) or as a 1D array (row-major). `rotation` transformations -are invertible. +`affine`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 and greater +than 1. Rotations are stored as `NxN` matrices, see below, and MUST have determinant equal to one, with orthonormal rows and +columns. The matrix may be stored as a 2D array (inner arrays represent the rows of the matrix) or as a 1D array (row-major). +`rotation` transformations are invertible.
path
The path to an array containing the affine parameters. The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*N`, - if 2D its size must be `N x N`.
+ if 2D its shape must be `N x N`.
rotation
The parameters stored in JSON. The matrix may be stored as a row-major flat array of numbers that MUST be length `N*N`, or as 2D nested array where the outer array MUST be length `N` and the inner arrays MUST be length `N`.
From 88b5f16fc55a4d440904bc4a5a37a03983e2b441 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 29 Jul 2024 15:21:59 -0400 Subject: [PATCH 102/113] minor rephrasing * prefer "shape" to "size" for zar arrays --- latest/index.bs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 736d5fa6..bfc12a00 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -527,7 +527,6 @@ Note that dimension `i` is contiguous in memory. - ### Coordinate convention **The pixel/voxel center is the origin of the continuous coordinate system.** @@ -540,7 +539,6 @@ system `(0.0, 0.0)` (when the transformation is the identity). The continuous re 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} ------------------------------------------------ @@ -896,7 +894,7 @@ y = j - 1.42 #### scale `scale` transformations are special cases of affine transformations. When possible, a scale transformation -SHOULD be defined to its equivalent affine. Input and output dimensionality MUST be identical and MUST equal +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. @@ -1178,13 +1176,13 @@ For `coordinates`: * coordinateSystem metadata MUST have exactly one axis with `"type" : "coordinate"` * If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. -* the size of the array along the "coordinate" axis must be exactly `N` +* 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"` * If a `displacements`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. -* the size of the array along the "displacement" axis must be exactly `N` +* 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: From cc6ade0d2571f892d02a20bec03760556eea0438 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 13 Aug 2024 10:59:52 -0400 Subject: [PATCH 103/113] remove reference to non-existent note --- latest/index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index bfc12a00..c9cd562a 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -1061,8 +1061,8 @@ outlined below: 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` (see the note - below), and under all other conditions. +- The `input` and `output` fields MUST be included for transformations of type: `mapAxis`, and `byDimension`, and + under all other conditions.
From 11729de7043e5490948daa888fac804b3b6b68d1 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 13 Aug 2024 16:42:53 -0400 Subject: [PATCH 104/113] matrix transformations MUST be stored as 2D (json or zarr) arrays --- .../examples/transformations/affine2d2d.json | 2 +- .../examples/transformations/affine2d3d.json | 2 +- .../transformations/affine2d3d_nested.json | 15 ------- latest/examples/transformations/rotation.json | 2 +- latest/index.bs | 42 +++++++------------ 5 files changed, 18 insertions(+), 45 deletions(-) delete mode 100644 latest/examples/transformations/affine2d3d_nested.json diff --git a/latest/examples/transformations/affine2d2d.json b/latest/examples/transformations/affine2d2d.json index c4322481..a4955d43 100644 --- a/latest/examples/transformations/affine2d2d.json +++ b/latest/examples/transformations/affine2d2d.json @@ -6,7 +6,7 @@ "coordinateTransformations" : [ { "type": "affine", - "affine": [1, 2, 3, 4, 5, 6], + "affine": [[1, 2, 3], [4, 5, 6]], "input": "ji", "output": "yx" } diff --git a/latest/examples/transformations/affine2d3d.json b/latest/examples/transformations/affine2d3d.json index be45fd1b..588bdec5 100644 --- a/latest/examples/transformations/affine2d3d.json +++ b/latest/examples/transformations/affine2d3d.json @@ -6,7 +6,7 @@ "coordinateTransformations": [ { "type": "affine", - "affine": [1, 2, 3, 4, 5, 6, 7, 8, 9], + "affine": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], "input": "ij", "output": "xyz" } diff --git a/latest/examples/transformations/affine2d3d_nested.json b/latest/examples/transformations/affine2d3d_nested.json deleted file mode 100644 index db904248..00000000 --- a/latest/examples/transformations/affine2d3d_nested.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "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/latest/examples/transformations/rotation.json b/latest/examples/transformations/rotation.json index 5c0af6aa..942dcf39 100644 --- a/latest/examples/transformations/rotation.json +++ b/latest/examples/transformations/rotation.json @@ -6,7 +6,7 @@ "coordinateTransformations" : [ { "type": "rotation", - "rotation": [0, -1, 1, 0], + "rotation": [[0, -1], [1, 0]], "input" : "ji", "output" : "yx" } diff --git a/latest/index.bs b/latest/index.bs index c9cd562a..d0ebadaf 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -742,10 +742,10 @@ implementations MAY estimate an inverse, or MAY output a warning that the reques 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 may be stored either as row-major flat (one-dimensional) arrays or two-dimensional -arrays, either in json or stored 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 3 rows and 2 columns). +(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).
@@ -924,18 +924,16 @@ y = 2 * j #### affine `affine`s are [matrix transformations](#matrix-transformations) from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` -matrices in homogeneous coordinates. This transformation type is invertible when `N` equals `M`. -The matrix may be stored as a 2D array (inner arrays represent the rows of the matrix) or as a 1D array (row-major). +matrices in homogeneous coordinates. 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 in a zarr array.
path
The path to a zarr-array containing the affine parameters. - The array at this path MUST be 1D or 2D. If 1D, its length MUST be `N*(M+1)`. - If 2D its shape must be `N x (M+1)`.
+ The array at this path MUST be 2D whose shape MUST be `N x (M+1)`.
affine
-
The affine parameters stored in JSON. The matrix may be stored as a row-major flat array of numbers that MUST be - length `N*(M+1)`, or as 2D nested array where the outer array MUST be length `N` and the inner arrays MUST be length `M+1`.
-
+
The affine parameters stored in JSON. The matrix MUST be stored as 2D nested array where the outer array MUST be length + `N` and the inner arrays MUST be length `M+1`.
A 2D-2D example: @@ -972,33 +970,23 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m y = 4*i + 5*j + 6 z = 7*i + 8*j + 9 ``` - - Using a nested 2D JSON array, the same transformation can be written: -
-    path: examples/transformations/affine2d3d_nested.json
-    highlight: json
-    
-
#### rotation `affine`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 and greater -than 1. Rotations are stored as `NxN` matrices, see below, and MUST have determinant equal to one, with orthonormal rows and -columns. The matrix may be stored as a 2D array (inner arrays represent the rows of the matrix) or as a 1D array (row-major). -`rotation` transformations are invertible. +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 1D or 2D. If 1D, its length MUST be `N*N`, - if 2D its shape must be `N x N`.
+ The array at this path MUST be 2D whose shape MUST be `N x N`.
rotation
-
The parameters stored in JSON. The matrix may be stored as a row-major flat array of numbers that MUST be - length `N*N`, or as 2D nested array where the outer array MUST be length `N` and the inner arrays MUST be length `N`.
-
+
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 From 8e9bdb6edbff4c83ae91e74964937b338981bf88 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Tue, 13 Aug 2024 16:44:19 -0400 Subject: [PATCH 105/113] typo affine -> rotation --- latest/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/latest/index.bs b/latest/index.bs index d0ebadaf..590a23ec 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -975,7 +975,7 @@ The matrix MUST be stored as a 2D array either as json or in a zarr array. #### rotation -`affine`s are [matrix transformations](#matrix-transformations) that are special cases of affine transformations. When possible, a 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. From d31184854ea34938a84803e91787698d57315368 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 28 Aug 2024 15:21:10 -0400 Subject: [PATCH 106/113] merge edits from rfc thanks @d-v-b --- latest/index.bs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index 590a23ec..e73812f8 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -345,8 +345,8 @@ is indicated by the length of its "axes" array. The "volume_micrometers" example 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. Task 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 +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.
@@ -402,7 +402,8 @@ image. "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. +- 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. - 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' @@ -428,14 +429,12 @@ Examples of valid axes: ```
-Arrays are often thought of as containing discrete samples along the continuous variable. Axes representing space and time are -usually continuous, meaning they can be indexed by real-valued (floating point) numbers whereas discrete axes may be indexed -only by integers. Arrays are inherently discrete (see Array coordinate systems, below) , but values "in between" discrete -coordinates can be retreived using an *interpolation* method. If an axis is continuous (`"discrete" : false`), it indicates -that indexing with continuous values is meaningful, and that interpolation may be performed along that axis. Interpolation may -be performed jointly across axes with the same `type`, and interpolation should not be performed for discrete axes or jointly -across axes with differing `type`s. Other non-continuous variables and axis types are also usually discrete, such as -`channel`s, `coordinate`s, and `displacement`s. +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 @@ -511,7 +510,7 @@ 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 +the user-defined attributes 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"`. @@ -610,7 +609,7 @@ Conforming readers: - SHOULD be able to apply transformations to images; Coordinate transformations from array to physical coordinates MUST be stored in multiscales ([[#multiscale-md]]), -and MUST be duplicated in the atrributes of the zarr array. Transformations between different images MUST be stored in the +and MUST be duplicated in the attributes of the zarr array. 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"`. From b5ed81a887008725a07eb3a195820252209e3280 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 28 Aug 2024 15:41:02 -0400 Subject: [PATCH 107/113] change examples to relative paths thanks @normanrz --- .../examples/transformations/bijection.json | 4 +-- .../transformations/bijection_verbose.json | 4 +-- .../examples/transformations/inverseOf.json | 5 ++- latest/index.bs | 31 +++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/latest/examples/transformations/bijection.json b/latest/examples/transformations/bijection.json index f988ad87..7ee77062 100644 --- a/latest/examples/transformations/bijection.json +++ b/latest/examples/transformations/bijection.json @@ -6,8 +6,8 @@ "coordinateTransformations" : [ { "type": "bijection", - "forward": { "type" : "coordinates", "path" : "/forward_coordinates" }, - "inverse": { "type" : "coordinates", "path" : "/inverse_coordinates" }, + "forward": { "type" : "coordinates", "path" : "forward_coordinates" }, + "inverse": { "type" : "coordinates", "path" : "inverse_coordinates" }, "input": "src", "output": "tgt" } diff --git a/latest/examples/transformations/bijection_verbose.json b/latest/examples/transformations/bijection_verbose.json index 5eccc08e..56547fdc 100644 --- a/latest/examples/transformations/bijection_verbose.json +++ b/latest/examples/transformations/bijection_verbose.json @@ -1,7 +1,7 @@ { "type": "bijection", - "forward": { "type" : "coordinates", "path" : "/forward_coordinates", "input" : "src", "output" : "tgt" }, - "inverse": { "type" : "coordinates", "path" : "/inverse_coordinates", "input" : "tgt", "output" : "src" }, + "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/latest/examples/transformations/inverseOf.json b/latest/examples/transformations/inverseOf.json index ef12a2fe..6a94274c 100644 --- a/latest/examples/transformations/inverseOf.json +++ b/latest/examples/transformations/inverseOf.json @@ -6,7 +6,10 @@ "coordinateTransformations" : [ { "type": "inverseOf", - "transformation" : { "type": "displacements", "path": "/path/to/displacements" }, + "transformation" : { + "type": "displacements", + "path": "path/to/displacements" + }, "input" : "moving", "output" : "fixed" } diff --git a/latest/index.bs b/latest/index.bs index e73812f8..e069ddd8 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -467,11 +467,11 @@ Every array has a default coordinate system whose parameters need not be explici 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: +For example, a 3D array at path `my/data/array` defines the coordinate system: ```json { - "name" : "/my/data/array", + "name" : "my/data/array", "axes" : [ {"name": "dim_0", "type": "array"}, {"name": "dim_1", "type": "array"}, @@ -491,7 +491,7 @@ chunks. As described in the [zarr array metadata](https://zarr.readthedocs.io/en the last dimension of an array in "C" order are stored contiguously on disk or in-memory when directly loaded.
-For example, if `/my/data/array/.zarray` contains: +For example, if `my/data/array/.zarray` contains: ```json { @@ -655,7 +655,7 @@ details).
-The sequence transformation's input corresponds to an array coordinate system at path "/my/array". +The sequence transformation's input corresponds to an array coordinate system at path "my/array". ```json "coordinateSystems" : [ @@ -674,7 +674,7 @@ The sequence transformation's input corresponds to an array coordinate system at }, { "type" : "sequence", - "input" : "/my/array", + "input" : "my/array", "output" : "outSeq", "transformations" : [ { "type": "scale", "scale" : [ 0.5, 0.6 ] }, @@ -687,7 +687,7 @@ The sequence transformation's input corresponds to an array coordinate system at "output" : "outInv", "transformation" : { "type": "displacements", - "path": "/path/to/displacements" + "path": "path/to/displacements" } }, { @@ -726,7 +726,7 @@ transformation type, for example: "type": "inverseOf", "transformation" : { "type": "displacements", - "path": "/path/to/displacements", + "path": "path/to/displacements", } } ``` @@ -1021,7 +1021,6 @@ transformation (if it exists).
-For example
     path: examples/transformations/inverseOf.json
@@ -1098,7 +1097,7 @@ In this example, the array located at `"displacementField"` MUST have three dime
 correspond to an axis with `type : displacement`, 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"}] }
@@ -1115,7 +1114,7 @@ the axes of the `"in"` coordinate system.
 
 The array at location should have a coordinate system such as:
 
-```
+```json
 "coordinateSystems" : [
     { "name" : "in", "axes" : [
         {"name":"y"}, {"name":"x"},
@@ -1173,7 +1172,7 @@ For `displacements`:
 * `input` and `output` MUST have an equal number of dimensions.
 
 For example, in 1D:
-```
+```json
 {
     "name" : "a coordinate field transform",
     "type": "coordinates",
@@ -1187,7 +1186,7 @@ For example, in 1D:
 where we assume input spaces "i" and "x" are defined elsewhere.
 Example metadata for the array data at path `coordinates` above:
 
-```
+```json
 {
     "coordinateSystems" : [
         {
@@ -1218,7 +1217,7 @@ x =
 
 
 A 1D example displacement field:
-```
+```json
 {
     "name" : "a displacement field transform",
     "type": "displacements",
@@ -1232,11 +1231,11 @@ A 1D example displacement field:
 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 coordinate field transform",
+            "name" : "a displacement field transform",
             "axes" : [
                 { "name": "x", "type": "space", "unit" : "nanometer" },
                 { "name": "d", "type": "displacement", "discrete": true }
@@ -1342,8 +1341,6 @@ appropriate dimensionality.
 
 
-For example -
 path: examples/transformations/bijection.json
 highlight: json

From 12a533871b1d1466ec00f51c7c06fea5630206d7 Mon Sep 17 00:00:00 2001
From: John Bogovic 
Date: Fri, 20 Sep 2024 12:02:33 -0400
Subject: [PATCH 108/113] typo and other minor fixes

thanks @LucaMarconato
---
 latest/index.bs | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/latest/index.bs b/latest/index.bs
index e069ddd8..caffc634 100644
--- a/latest/index.bs
+++ b/latest/index.bs
@@ -589,7 +589,7 @@ input space to *points* in the output space.
     
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. + 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. @@ -779,8 +779,8 @@ results in the point [2,-1,3] because it is computed with the matrix-vector mult Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value of "input" is an array, it's length gives the input dimension, otherwise the length of "axes" for the coordinate system with the name of the "input" value gives the input dimension. If the value of "input" is an array, it's -length gives the input imension, otherwise it is given by the length of "axes" for the coordinate system with -the name of the "input". If the value of "output" is an array, it's length gives the output dimension, +length gives the input dimension, otherwise it is given by the length of "axes" for the coordinate system with +the name of the "input". If the value of "output" is an array, its length gives the output dimension, otherwise it is given by the length of "axes" for the coordinate system with the name of the "output". #### identity @@ -1160,14 +1160,14 @@ For both `coordinates` and `displacements`, the array data at referred to by `pa For `coordinates`: -* coordinateSystem metadata MUST have exactly one axis with `"type" : "coordinate"` -* If a `coordinates`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. +* `coordinateSystem` metadata MUST have exactly one axis with `"type" : "coordinate"` +* If the input coordinate system for a `coordinates` transformation has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. * 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"` -* If a `displacements`' input space has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. +* `coordinateSystem` metadata MUST have exactly one axis with `"type" : "displacement"` +* If the input coordinate system of a `displacements` transformation has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. * the shape of the array along the "displacement" axis must be exactly `N` * `input` and `output` MUST have an equal number of dimensions. From 5376db5f31e2142a165f61724879106159e5c274 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 23 Sep 2024 11:47:23 -0400 Subject: [PATCH 109/113] fix affine matrix size, and other small fixes thanks @LucaMarconato --- latest/index.bs | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index caffc634..3c98c7cc 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -570,7 +570,7 @@ input space to *points* in the output space. 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` +
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` @@ -922,17 +922,18 @@ y = 2 * j #### affine -`affine`s are [matrix transformations](#matrix-transformations) from N-dimensional inputs to M-dimensional outputs are represented at `(N)x(M+1)` -matrices in homogeneous coordinates. 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 in a zarr array. +`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 `N x (M+1)`.
+ 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 - `N` and the inner arrays MUST be length `M+1`.
+ `M` and the inner arrays MUST be length `N+1`.
A 2D-2D example: @@ -948,6 +949,17 @@ The matrix MUST be stored as a 2D array either as json or in a zarr array. 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. +
@@ -968,7 +980,17 @@ The matrix MUST be stored as a 2D array either as json or in a zarr array. 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.
@@ -1036,6 +1058,19 @@ coordinate transform in the array is invertible. To apply a sequence transformat 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: From 50e7759c6cc9f7543d07cd2ee56ec267ff652b00 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 23 Sep 2024 12:06:06 -0400 Subject: [PATCH 110/113] correctly close code block --- latest/index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/latest/index.bs b/latest/index.bs index 3c98c7cc..acdb1e6d 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -980,6 +980,7 @@ invertible when `N` equals `M`. The matrix MUST be stored as a 2D array either 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: From e8bc1db3b5a78be25330644e825dbc0a24b8dc43 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Mon, 23 Sep 2024 13:21:49 -0400 Subject: [PATCH 111/113] clarify invertibility of sequence transformation --- latest/index.bs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index acdb1e6d..d8d4c954 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -717,8 +717,8 @@ 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` +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 @@ -739,7 +739,7 @@ implementations MAY estimate an inverse, or MAY output a warning that the reques #### Matrix transformations -Two transformation types ([affine](#affine) and [rotation](#rotation)) are parametrized by matrices. Matrices are applied to +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 @@ -924,8 +924,8 @@ y = 2 * j `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. +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
@@ -1054,10 +1054,11 @@ transformation (if it exists). #### sequence -A `sequence` transformation consists of an ordered array of coordinate transformations, and is invertible if and only if every -coordinate transform in the array is invertible. 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. +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.
From 7d711276b2a9a74e6e404c9bb79ba217d2c01ed1 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 25 Sep 2024 13:44:49 -0400 Subject: [PATCH 112/113] fix byDimension header link --- latest/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/latest/index.bs b/latest/index.bs index d8d4c954..f06b0763 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -1297,7 +1297,7 @@ The transformation specifies linear interpolation, which in this case yields input point, hence the output is `1.0 + (-0.5) = 0.5`. -#### byDimension {#trafo-byDimension} +#### byDimension `byDimension` transformations build a high dimensional transformation using lower dimensional transformations on subsets of dimensions. From 3a7e76fff2941a3287873a5848bca3add83d1fd3 Mon Sep 17 00:00:00 2001 From: John Bogovic Date: Wed, 25 Sep 2024 13:45:06 -0400 Subject: [PATCH 113/113] flesh out description for coordinates and displacements --- latest/index.bs | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/latest/index.bs b/latest/index.bs index f06b0763..3989418a 100644 --- a/latest/index.bs +++ b/latest/index.bs @@ -1115,24 +1115,30 @@ and is invertible. #### coordinates and displacements -`coordinates` and `displacements` transformations store coordinates or displacements in an array and interpret them as a -transformation. Applying the transformation amounts to looking up the appropriate locations in the array and interpolating -if necessary. +`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 whose coordinate sytem MUST contain -an axis with type `coordinate` or `displacement` respectively for transformations of type `coordinates` or `displacements`. This -axis MUST be of length equal to the number of axes of the output coordinate system and SHOULD be the last axis (contiguous on -disk when c-order). The coordinate system MUST also contain an axis identical to every axis of its input coordinate system in -the same order. As a consequence, the array will have one dimension more than its input coordinate system. +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"). -The `i`th value of the array along the `coordinate` or `displacement` axis refers to the displacement -or coordinate of `i`th output axis. See the example below. +* 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`, the other two dimensions MUST be axes that are identical to -the axes of the `"in"` coordinate system. +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" : [ @@ -1149,7 +1155,7 @@ the axes of the `"in"` coordinate system. ] ``` -The array at location should have a coordinate system such as: +The metadata at location `"displacementField"` should have a coordinate system such as: ```json "coordinateSystems" : [ @@ -1189,22 +1195,21 @@ inverses. Metadata for these coordinate transforms have the following field:
-For both `coordinates` and `displacements`, the array data at referred to by `path` MUST define space and coordinate transform metadata: +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 dimensionality of the `coordinateTransform` `output` +* 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"` -* If the input coordinate system for a `coordinates` transformation has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. * 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"` -* If the input coordinate system of a `displacements` transformation has dimensionality `N`, then the array data at `path` MUST have dimensionality equal to `(N + 1)`. * the shape of the array along the "displacement" axis must be exactly `N` * `input` and `output` MUST have an equal number of dimensions.