-
Notifications
You must be signed in to change notification settings - Fork 12
Transformation spec proposal for NGFF
Right-handed coordinate system (x→, y↓, z↗), positive rotation angles rotate clock-wise (↻). Index according to axis index (x:0, y:1, z:2, ...).
Transformations are specified as forward transformations, i.e. a transformation transforms a vector from source into target space, a sequence of transformations is applied from first to last element. Rendering transformed images or volumes typically goes through the inverse of a transformation and some transformations cannot be inverted easily. If the sole purpose of a transformation is to be inverted (as in for rendering an image or volume), then its inverse can be explicitly defined as inverse_of
an otherwise undefined transformation.
Each transformation has a String:type
property from the below list and defines how other fields are interpreted. More transformations and their fields will be added to the list as they become available.
type | fields | description |
---|---|---|
identity |
identity transformation, is the default transformation and is typically not explicitly defined | |
sequence |
Transformation[]:transformations |
sequence of transformations, an empty sequence is the identity
|
translation |
one of:number[]:translation String :path
|
translation vector, stored either as a number[] (translation ) or as binary data at a location in this container (path ). If both are present, path is preferred. length of vector defines number of dimensions |
scale |
one of:number[]:scale String :path
|
scale vector, stored either as a number[] (scale ) or as binary data at a location in this container (path ). If both are present, path is preferred. length of vector defines number of dimensions |
affine |
one of:number[]:affine String :path
|
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 |
d_field |
String:url String:path(null) boolean:interleaved(true)
|
deformation / displacement field storing one offset for each grid coordinate, url points to the data container (e.g. "/field.hdf5" or "/field.tif" and path points to the dataset if the container can hold multiple datasets or is empty for single dataset containers |
p_field |
String:url String:path(null) boolean:interleaved(true)
|
position field storing one absolute position for each grid coordinate, url points to the data container (e.g. "/field.hdf5" or "/field.tif" and path points to the dataset if the container can hold multiple datasets or is empty for single dataset containers |
inverse_of |
Transformation:transformation |
inverse of a transformation |
translation
{
"type" : "translation",
"translation" : [-1, 2.2, 0]
}
inverse_of / scale
{
"type" : "inverse_of",
"transformation" : {
"type" : "scale",
"scale" : [1.1, 2.2, 3.3]
}
}
affine (2d)
{
"type" : "affine",
"affine" : [2.0, 0.1, 10, -0.1, 2.0, -10]
}
affine (3d)
{
"type" : "affine",
"affine" : [2.0, 0.1, -0.01, 10.0, -0.1, 2.0, 0.01, -10.0, 0.0, 0.0, 1.0, 5.0]
}
sequence
{
"type" : "sequence",
"transforms" : [
{
"type" : "translation",
"translation" : [ -1, 2.2, 0 ]
},
{
"type" : "scale",
"scale" : [ 1.2, 1, 2.2 ]
}
]
}
displacement / deformation field hdf5
{
"type" : "d_field",
"url" : "/path/to/my/JRC2018F_FCWB.h5",
"path" : "/0/dfield"
}
displacement / deformation field nrrd
{
"type" : "d_field",
"url" : "file:///path/to/my/JRC2018F_FCWB.nrrd,
}
This section is preliminary.
Axes may also be tagged with a type that describes the kind of domain (time, space, wavelength, amino-acid residue).
This should be stored in an axes
field in a datasets attributes. The value of this field must be an array of
length to the dataset's dimensionality. Each element of the array should be an axis object as described below.
See here for a longer list of specifications used for brainstorming.
"axes": {
"labels" : ["x", "y", "c", "z", "t" ],
"types" : ["space", "space", "channel", "space", "time" ],
"units" : [ "µm", "µm", "pixel", "µm", "ms" ]
}
See https://github.com/ome/ngff/issues/35
Example:
"axes": [
{
"label": "x",
"type": "space",
"unit": "µm"
},
{
"label": "y",
"type": "space",
"unit": "µm"
},
{
"label": "c",
"type": "channels",
"unit": null
}
{
"label": "z",
"type": "space",
"unit": "µm"
}
{
"label": "t",
"type": "time",
"unit": "ms"
}
Each axis object has the following fields:
-
label
:string
- an arbitrary string label / name for the axis
-
type
:string
- the type of domain of the axis. e.g. (
space
,time
,channel
)
- the type of domain of the axis. e.g. (
-
unit
:string
- the unit of the domain, or one of (
pixel
,px
, ornull
) if arbitrary or not applicable. e.g. (mm
,microns
,seconds
)
- the unit of the domain, or one of (
This proposal combines the descriptions of axes and transformations. A benefit of this is that it more clearly describes which dimensions can be "mixed" by transformations (see the alternative below). In proposals A and B, the assumption is that dimensions of the same type can be "mixed".
{ "axes": [
{
"type":"spatial",
"labels":["X","Y"],
"inputIndexes":[0,1],
"outputIndexes":[0,1],
"transform": {
"type":"affine",
"affine":[2.0, 0.0, 10.0,
0.0, 0.5, -5.0 ]
},
"unit":"nm"
},
{
"type":"channel",
"labels":["C"],
"inputIndexes":[2],
"outputIndexes":[2],
"transform": { "type":"identity" }
}
]
}
The type
, and label
fields are as above, though here the label
field is a String[]
, and gives a name to
the dimension in the output "world" coordinates.
-
transform
(Optional) : an object describing a transformation object- if not provided, assume the
identity
transformation.
- if not provided, assume the
-
inputIndexes
(Optional) :- if not provided, assume
[ M+1, M+2, ..., M+N-1]
, whereM
is the largest integer in theinputIndex
fields for all axes in the list prior to this one, andN
is the number of dimensions for this axes (length oflabels
). See the "Inferring indexes" section below for examples.
- if not provided, assume
-
outputIndexes
(Optional) :- if not provided, assume is identical to
inputIndexes
- if not provided, assume is identical to
Example 1
[
{
"type":"spatial",
"labels":["X","Y"],
"unit":"nm"
},
{
"type":"channel",
"labels":["C"],
}
]
implies
[
{
"type":"spatial",
"labels":["X","Y"],
"unit":"nm"
"inputIndexes":[0,1],
"outputIndexes":[0,1],
},
{
"type":"channel",
"labels":["C"],
"inputIndexes":[2],
"outputIndexes":[2],
}
]
Example 2
[
{
"type":"channel",
"labels":["C"],
},
{
"type":"spatial",
"labels":["X","Y"],
"unit":"nm"
}
]
implies
[
{
"type":"channel",
"labels":["C"],
"inputIndexes":[0],
"outputIndexes":[0],
},
{
"type":"spatial",
"labels":["X","Y"],
"unit":"nm"
"inputIndexes":[1,2],
"outputIndexes":[1,2],
}
]
units
are optional. If not provided, assume they are arbitrary "pixel" or discrete units.
Consider allowing types to be inferred from labels:
label | type |
---|---|
x , y , z , X , Y , Z
|
space |
c , C
|
channel |
t , T
|
time |
v , V
|
vector |
m , M
|
matrix |
theta , rho , phi
|
angle |
As a result, this axis specification:
"axes": {
"labels" : ["x", "y", "c" ],
}
implies:
"axes": {
"labels" : ["x", "y", "c", "z", "t" ],
"types" : ["space", "space", "channel" ],
"units" : [ "pixel", "pixel", "pixel" ]
}
What should assumptions be if no axes
are specified? Here are some ideas:
2D datasets imply:
"axes": {
"labels" : ["x", "y"],
"types" : ["space", "space" ],
"units" : [ "pixel", "pixel" ]
}
3D datasets imply:
"axes": {
"labels" : ["x", "y", "z"],
"types" : ["space", "space", "space" ],
"units" : [ "pixel", "pixel", "pixel" ]
}
4D datasets imply:
"axes": {
"labels" : ["x", "y", "z", "c" ],
"types" : ["space", "space", "space", "channel" ],
"units" : [ "pixel", "pixel", "pixel" ]
}
5D datasets imply:
"axes": {
"labels" : ["x", "y", "z", "c", "t" ],
"types" : ["space", "space", "space", "channel", "time" ],
"units" : [ "pixel", "pixel", "pixel", "pixel" ]
}