Skip to content

Commit aa41d67

Browse files
committed
Merge remote-tracking branch 'upstream/main' into unify-input-output
2 parents cffaeb8 + 4fc7355 commit aa41d67

3 files changed

Lines changed: 184 additions & 91 deletions

File tree

.codespellrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[codespell]
2+
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
3+
skip = .git,.codespellrc,references.bib
4+
check-hidden = true
5+
# ignore-regex =
6+
ignore-words-list = commend,Fuchsia

index.md

Lines changed: 167 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,9 @@ In contrast, discrete axes (`"discrete" : true`) may be indexed only by integers
275275
Axes representing a channel, coordinate, or displacement are usually discrete.
276276

277277
```{note}
278-
The most common methods for interpolation are "nearest neighbor", "linear", "cubic", and "windowed sinc".
278+
The most common methods for interpolation are "nearest", "linear", "bspline-cubic", and "windowed sinc" (see {cite:t}`thevenaz2000image`, section 8).
279279
Here, we refer to any method that obtains values at real-valued coordinates using discrete samples as an "interpolator".
280-
As such, label images may be interpolated using "nearest neighbor" to obtain labels at points along the continuum.
280+
As such, label images may be interpolated using "nearest" to obtain labels at points along the continuum.
281281
```
282282

283283
#### Coordinate convention
@@ -422,8 +422,8 @@ The following transformations are supported:
422422
| [`affine`](#affine-md) | one of:<br>`"affine":List[List[number]]`,<br>`"path":str` | 2D affine transformation matrix stored either with JSON (`affine`) or as a Zarr array at a location in this container (`path`). |
423423
| [`rotation`](#rotation-md) | one of:<br>`"rotation":List[List[number]]`,<br>`"path":str` | 2D rotation transformation matrix stored as an array stored either with json (`rotation`) or as a Zarr array at a location in this container (`path`).|
424424
| [`sequence`](#sequence-md) | `"transformations":List[Transformation]` | sequence of transformations. Applying the sequence applies the composition of all transforms in the list, in order. |
425-
| [`displacements`](#coordinates-displacements-md) | `"path":str` | Displacement field transformation located at `path`. |
426-
| [`coordinates`](#coordinates-displacements-md) | `"path":str` | Coordinate field transformation located at `path`. |
425+
| [`displacements`](#coordinates-displacements-md) | `"path":str` <br> `"interpolation":str` | Displacement field transformation located at `path`. |
426+
| [`coordinates`](#coordinates-displacements-md) | `"path":str` <br> `"interpolation":str` | Coordinate field transformation located at `path`. |
427427
| [`bijection`](#bijection-md) | `"forward":Transformation`<br>`"inverse":Transformation` | An invertible transformation providing an explicit forward transformation and its inverse. |
428428
| [`byDimension`](#bydimension-md) | `"transformations":List[Transformation]`.<br>Transformations in the array MUST have<br>`"input_axes": List[number]`, <br> and `"output_axes": List[number]` | A high dimensional transformation using lower dimensional transformations on subsets of dimensions. |
429429

@@ -464,7 +464,7 @@ i.e., the mapping from the first input axis to the first output axis is determin
464464
Conforming readers:
465465
- MUST parse `identity`, `scale`, `translation` transformations;
466466
- SHOULD parse `mapAxis`, `affine`, `rotation` transformations;
467-
- SHOULD display an informative warning if encountering transformations that cannot be parsed or displayed by a viewer;
467+
- SHOULD display an informative warning if encountering transformations that cannot be parsed or displayed by a consumer;
468468
- SHOULD be able to apply transformations to points;
469469
- SHOULD be able to apply transformations to images;
470470

@@ -579,6 +579,12 @@ to do so by estimating the transformations' inverse if they choose to.
579579
```
580580
:::
581581

582+
```{note}
583+
Exact reproducibility of pixel values for images transformed and resampled by
584+
the transformation types here may differ across implementation and is therefore
585+
out of the scope of this specification.
586+
```
587+
582588
**Transformations in array coordinate units**:
583589
Some applications might prefer to define points, regions-of-interest or transformation parameters
584590
in array coordinates (also referred to as pixel coordinates) rather than physical units.
@@ -1026,65 +1032,104 @@ and is invertible.
10261032
##### coordinates and displacements
10271033
(coordinates-displacements-md)=
10281034

1029-
`coordinates` and `displacements` transformations store coordinates or displacements in an array
1030-
and interpret them as a vector field that defines a transformation.
1031-
The arrays must have a dimension corresponding to every axis of the input coordinate system
1032-
and one additional dimension to hold components of the vector.
1033-
Applying the transformation amounts to looking up the appropriate vector in the array,
1034-
interpolating if necessary,
1035-
and treating it either as a position directly (`coordinates`)
1036-
or a displacement of the input point (`displacements`).
1037-
1038-
These transformation types refer to an array at location specified by the `path` parameter.
1039-
The input and output coordinate systems for these transformations (`input` / `output` coordinate systems)
1040-
constrain the array size and the coordinate system metadata for the array (field `coordinateSystem`).
1041-
1042-
* If the input coordinate system has `N` axes,
1043-
the array at location path MUST have `N+1` dimensions
1044-
* The field coordinate system MUST contain an axis identical to every axis
1045-
of its input coordinate system in the same order.
1046-
* The field coordinate system MUST contain an axis with type `coordinate` or `displacement`, respectively,
1047-
for transformations of type `coordinates` or `displacements`.
1048-
* This SHOULD be the last axis (contiguous on disk when c-order).
1049-
* If the output coordinate system has `M` axes,
1050-
the length of the array along the `coordinate`/`displacement` dimension MUST be of length `M`.
1051-
1052-
The `i`th value of the array along the `coordinate` or `displacement` axis refers to the coordinate or displacement
1053-
of the `i`th output axis. See the example below.
1054-
1055-
`coordinates` and `displacements` transformations are not invertible in general,
1035+
`coordinates` and `displacements` transformations store a vector field of arbitrary sampling density in an array,
1036+
defining a mapping from an input coordinate system to an output coordinate system.
1037+
The array contains either coordinates (absolute positions)
1038+
or displacements (relative shifts) for each point in the input space.
1039+
1040+
```{hint}
1041+
The `coordinates` and `displacements` transformations are not invertible in general,
10561042
but implementations MAY approximate their inverses.
1043+
```
1044+
1045+
**Array Structure**
1046+
1047+
The array containing the coordinates or displacements MUST:
1048+
- be a regular grid of vectors.
1049+
The vectors are stored in an array, the coordinates of which can be mapped
1050+
to the corresponding coordinates in the input coordinate system via a coordinate transformation (see details below).
1051+
- have one dimension corresponding to every axis of the input coordinate system
1052+
- have one additional dimension to hold components of the vector (either coordinates or displacements)
1053+
- only be used to represent transformations between coordinate systems that are defined in smooth, regularly sampled coordinate arrays.
1054+
10571055
Metadata for these coordinate transforms have the following fields:
10581056

10591057
**path**
10601058
: The location of the coordinate array in this (or another) container.
10611059

1060+
**interpolation**
1061+
: The interpolation attributes MAY be provided.
1062+
Its value indicates the interpolation to use if transforming points not on the array's discrete grid.
1063+
1064+
The interpolation methods listed in this specification document refer to the methods described in {cite:t}`thevenaz2000image` and are not exhaustive.
1065+
- `nearest` for nearest neighbor interpolation (see {cite:t}`thevenaz2000image`, section 8.1),
1066+
- `linear` for linear interpolation (default, see {cite:t}`thevenaz2000image`, section 8.2),
1067+
- `bspline-cubic` for cubic interpolation (see {cite:t}`thevenaz2000image`, section 8.3 on "cubic B-splines).
1068+
1069+
Consumers SHOULD clearly communicate to users if a different interpolation method is used.
10621070

1063-
For both `coordinates` and `displacements`,
1064-
the array data at referred to by `path` MUST define coordinate system
1065-
and coordinate transform metadata:
1071+
```{hint}
1072+
The `interpolation` field refers to the method that is used to interpolate the `coordinate` or `displacement` array,
1073+
*not* the method used to interpolate the image when applying the transformation to an image.
1074+
The `interpolation` field, if provided, is not normative in the sense that usage of a different method is invalid under the spec.
1075+
Implementations may prefer to use faster methods for rendering (i.e., `linear` or `nearest`) but this may lead to pathological cases:
1076+
- If `nearest` interpolation is used for a `coordinates` transformation,
1077+
the transformed image collapses into a single point at the nearest coordinate in the coordinate field.
1078+
- If `nearest` interpolation is used for a `displacements` transformation,
1079+
the transformed image is piecewise constant with discontinuities at the boundaries between nearest neighbor regions.
10661080
1067-
* Every axis name in the `coordinateTransform`'s `input`
1068-
MUST appear in the coordinate system.
1069-
* The array dimension corresponding to the `coordinate` or `displacement` axis
1070-
MUST have length equal to the number of dimensions of the `coordinateTransform` `output`
1071-
* If the input coordinate system `N` axes,
1072-
then the array data at `path` MUST have `(N + 1)` dimensions.
1073-
* SHOULD have a `name` identical to the `name` of the corresponding `coordinateTransform`.
1081+
While choosing the specified interpolation methods can help to avoid these pathologies,
1082+
implementations of the specified interpolation methods may still differ in their results.
1083+
An exact reproducibility of pixel values for images transformed and resampled by this transformation is therefore out of the scope of this specification.
1084+
```
10741085

1075-
For `coordinates`:
1086+
**Array metadata**
10761087

1077-
* `coordinateSystem` metadata MUST have exactly one axis with `"type" : "coordinate"`
1078-
* the shape of the array along the "coordinate" axis must be exactly `N`
1088+
For both `coordinates` and `displacements`,
1089+
the array data referred to by `path` MUST define the following metadata fields:
1090+
1091+
* `coordinateSystems`: MUST contain a [coordinate system](#coordinatesystems-metadata) with the following properties:
1092+
- Include all axes of the input coordinate system (in the same order).
1093+
- Include one additional axis of `"type": "coordinate"` (for coordinates transformations) or `"type": "displacement"` (for displacements transformations).
1094+
- The additional axis should be the last axis (for contiguous memory layout in C-order).
1095+
- The `name` of this coordinate system SHOULD be the same as the `name` of the corresponding coordinate transformation.
1096+
1097+
* `coordinateTransformations`: Defines how to map from the coordinate system of the array into a physical coordinate system
1098+
(e.g. the resolution at which the vector field is sampled). MUST contain a single transformation with the following properties:
1099+
- `type`: The type of the transformation; MUST be one of [`identity`](#identity-md), [`scale`](#scale-md)
1100+
or a [`sequence`](#sequence-md) of a [scale](#scale-md) followed by a [translation](#translation-md).
1101+
- `output`: The name of the coordinate system defined in the `coordinateSystems` field of the array metadata.
1102+
1103+
*Note*: The `input` field is omitted, as it is implicitly the pixel coordinate system of the array
1104+
(defined by the first `N` axes of the array's `coordinateSystem`).
10791105

1080-
For `displacements`:
1106+
**Constraints**
10811107

1082-
* `coordinateSystem` metadata MUST have exactly one axis with `"type" : "displacement"`
1083-
* the shape of the array along the "displacement" axis must be exactly `N`
1084-
* input and output coordinate systems MUST have an equal number of dimensions.
1108+
The array at `path` MUST satisfy:
10851109

1086-
:::{dropdown} Example 1
1087-
For example, in 1D:
1110+
- **Dimensionality**: If the input coordinate system has `N` axes, the array at location `path` MUST have `N+1` dimensions.
1111+
- **Vector dimension length**:
1112+
- For `coordinates` transformations, the length of the array along the `coordinate` dimension (last axis) MUST equal `M`,
1113+
the number of axes in the output coordinate system.
1114+
- For `displacements` transformations, the length of the array along the `displacement` dimension (last axis) MUST equal `N`,
1115+
the number of axes in the input (and output) coordinate system. `displacements` require `M=N`.
1116+
- **Vector component mapping**: The `i`th value of the array along the `coordinate` or `displacement` axis refers to the `i`th output axis.
1117+
1118+
```{hint}
1119+
Applying the transformation to a point `x` in the input coordinate system amounts to following the following steps:
1120+
1. Use the inverse of the transformation found in the vector field's metadata under `coordinateTransformations`
1121+
to map the input point `x` into the corresponding array coordinate `a`.
1122+
2. Look up the vector in the array corresponding to that point's coordinates in the array's coordinate system.
1123+
3. If the point (`a`) does not correspond to a discrete point in the `coordinate` or `displacement` array,
1124+
interpolate the vector field to obtain a vector for the input point.
1125+
4. Treat the result either as
1126+
- an absolute position (`coordinates`) or
1127+
- a displacement to add to the input point `x` (`displacements`).
1128+
```
1129+
1130+
:::{dropdown} Example 1: 1D coordinate transformation
1131+
For example, in 1D, a coordinate field transformation mapping from an input coordinate system `input`
1132+
to an output coordinate system `output` would have metadata such as:
10881133
```json
10891134
{
10901135
"name" : "a coordinate field transform",
@@ -1096,30 +1141,46 @@ For example, in 1D:
10961141
}
10971142
```
10981143

1099-
where we assume input spaces `i` and `x` are defined elsewhere.
1100-
Example metadata for the array data at path `coordinates` above:
1144+
where we assume input coordinate systems `input` and `output` are defined elsewhere.
1145+
Example metadata under the attributes of the zarr array at path `coordinateTransformations/i2xCoordinates` above:
11011146

11021147
```json
11031148
{
1104-
"coordinateSystems" : [
1105-
{
1106-
"name" : "a coordinate field transform",
1107-
"axes" : [
1108-
{ "name": "i", "type": "space", "discrete": true },
1109-
{ "name": "c", "type": "coordinate", "discrete": true }
1110-
]
1111-
}
1112-
],
1113-
"coordinateTransformations" : [
1114-
{
1115-
"type" : "identity",
1116-
"output" : "a coordinate field transform"
1117-
}
1118-
]
1149+
"ome": {
1150+
"coordinateSystems" : [
1151+
{
1152+
"name" : "a coordinate field transform",
1153+
"axes" : [
1154+
{ "name": "i", "type": "space", "discrete": true },
1155+
{ "name": "c", "type": "coordinate", "discrete": true }
1156+
]
1157+
}
1158+
],
1159+
"coordinateTransformations" : [
1160+
{
1161+
"type" : "identity",
1162+
"output" : "a coordinate field transform"
1163+
}
1164+
]
1165+
}
11191166
}
11201167
```
11211168

1122-
If the array in `coordinates` contains the data: `[-9, 9, 0]`, then this metadata defines the function:
1169+
Here, the axis `i` refers to the input positions along the `i`-axis,
1170+
which equal array indices in this case as indicated by the `identity` transformation.
1171+
The `c` axis holds the corresponding output coordinates.
1172+
1173+
If the array in `coordinates` contains the data:
1174+
```
1175+
[
1176+
[-9], // Output coordinate for index=0, i=0
1177+
[9], // Output coordinate for index=1, i=1
1178+
[0] // Output coordinate for index=2, i=2
1179+
]
1180+
```
1181+
1182+
then this metadata defines the following function to map
1183+
the `i` axis (`input` coordinate system) to the `x` axis (`output` coordinate system):
11231184

11241185
```
11251186
x =
@@ -1129,7 +1190,7 @@ x =
11291190
```
11301191
:::
11311192

1132-
:::{dropdown} Example 2
1193+
:::{dropdown} Example 2: 1D displacement transformation
11331194
A 1D example displacement field:
11341195
```json
11351196
{
@@ -1142,31 +1203,45 @@ A 1D example displacement field:
11421203
}
11431204
```
11441205

1145-
where we assume input spaces `i` and `x` are defined elsewhere.
1146-
Example metadata for the array data at path `displacements` above:
1206+
where we assume input coordinate systems `input` and `output` are defined elsewhere.
1207+
Example metadata under the attributes of the zarr array at path `displacements` above:
11471208

11481209
```json
11491210
{
1150-
"coordinateSystems" : [
1151-
{
1152-
"name" : "a displacement field transform",
1153-
"axes" : [
1154-
{ "name": "x", "type": "space", "unit" : "nanometer" },
1155-
{ "name": "d", "type": "displacement", "discrete": true }
1156-
]
1157-
}
1158-
],
1159-
"coordinateTransformations" : [
1160-
{
1161-
"type" : "scale",
1162-
"scale" : [2, 1],
1163-
"output" : "a displacement field transform"
1164-
}
1165-
]
1211+
"ome": {
1212+
"coordinateSystems" : [
1213+
{
1214+
"name" : "a displacement field transform",
1215+
"axes" : [
1216+
{ "name": "x", "type": "space", "unit" : "nanometer" },
1217+
{ "name": "d", "type": "displacement", "discrete": true }
1218+
]
1219+
}
1220+
],
1221+
"coordinateTransformations" : [
1222+
{
1223+
"type" : "scale",
1224+
"scale" : [2, 1],
1225+
"output" : "a displacement field transform"
1226+
}
1227+
]
1228+
}
11661229
}
11671230
```
11681231

1169-
If the array in `displacements` contains the data: `[-1, 0, 1]`,
1232+
Since the input and output coordinate system may be defined in physical units,
1233+
a scale transformation is needed to map the displacement vectors into the same physical units as the input point.
1234+
1235+
If the array in `displacements` contains the data:
1236+
1237+
```
1238+
[
1239+
[-1], // Displacement for index=0, x=0
1240+
[0], // Displacement for index=1, x=2
1241+
[1] // Displacement for index=2, x=4
1242+
]
1243+
```
1244+
11701245
this transformation maps the point `[1.0]` to the point `[0.5]`.
11711246
A scale transformation maps the array coordinates to the `x` axis.
11721247
Using the inverse of the scale transform, we see that we need the position `0.5` in array coordinates.
@@ -1176,7 +1251,7 @@ That value gives us the displacement of the input point,
11761251
hence the output is `1.0 + (-0.5) = 0.5`.
11771252
:::
11781253

1179-
:::{dropdown} Example 3
1254+
:::{dropdown} Example 3: 2D displacement transformation
11801255

11811256
In this example, the array located at `displacementField` MUST have three dimensions.
11821257
One dimension MUST correspond to an axis with `type : displacement` (in this example, the last dimension),
@@ -1202,7 +1277,8 @@ The metadata at location `displacementField` should have a coordinate system suc
12021277
```json
12031278
"coordinateSystems" : [
12041279
{ "name" : "in", "axes" : [
1205-
{"name":"y"}, {"name":"x"},
1280+
{"name":"y"},
1281+
{"name":"x"},
12061282
{"name":"d", "type":"displacement", "discrete":true} ]
12071283
}
12081284
]

references.bib

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,14 @@ @software{ngff_spec_latest
2222
doi = {10.5281/zenodo.4282106},
2323
url = {https://ngff.openmicroscopy.org/latest/},
2424
}
25+
26+
@article{thevenaz2000image,
27+
title={Image interpolation and resampling},
28+
author={Th{\'e}venaz, Philippe and Blu, Thierry and Unser, Michael},
29+
journal={Handbook of medical imaging, processing and analysis},
30+
volume={1},
31+
number={1},
32+
pages={393--420},
33+
year={2000},
34+
url={https://bigwww.epfl.ch/publications/thevenaz9901.pdf}
35+
}

0 commit comments

Comments
 (0)