Skip to content

Commit

Permalink
clarification on affine and rotation transformation
Browse files Browse the repository at this point in the history
* adds section on matrix transformations
  • Loading branch information
bogovicj committed Jul 29, 2024
1 parent 6ebcf5f commit 00b79f1
Showing 1 changed file with 45 additions and 11 deletions.
56 changes: 45 additions & 11 deletions latest/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -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).

<div class=example>

For matrix transformations, points in the coordinate system:

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 6, 2024

This looks great, but I would add an example for affine transformations to be more complete. In particular I would clarify that:

  1. the current example is only for "rotations"
  2. for affine matrices we need both the matrix and the point vector to have extra structure (homogenous coordinates). Explicitly: a. extra row of zeros; b. extra translation column for the matrix; c. extra "1" for the vector). Also, it should be clear that the last row (0, 0, 0, ..., 0, 1) needs to be omitted when representing the matrix in NGFF.
  3. I would finally add a quick comment saying that that if the affine matrix represented in homogeneous coordinates (including the last row), has shape n_rows x n_cols, then the input coordinate system has dimension n_cols - 1 and the output coordinate system has dimension n_rows - 1.

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 6, 2024

Update: actually 2 and 3 are already specified for the "affine" matrix (3 is formulated in an equivalent way). So I would just make the point 1 clearer here.


```
{ "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]
```

</div>


### Transformation types

Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. If the value
Expand Down Expand Up @@ -888,15 +925,15 @@ y = 2 * j

#### <a name="affine">affine</a>

`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).

<dl>
<dt><strong>path</strong></dt>
<dd> 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)`.</dd>
If 2D its shape must be `N x (M+1)`.</dd>
<dt><strong>affine</strong></dt>
<dd> 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`.</dd>

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 6, 2024

I see a problem for sequences here. When the matrix is stored as a 2D nested list, then we can apply the transformation without the need to specify the input and output axes, but if we have a 1D list we can't and we need to know which are the input/output axes to infer the shape of the matrix.

Dealing with both cases makes the implementation more complex because the same affine transformation requires may extra structure to be valid in a sequence. I can imagine this leading to problems when converting a transformation back and forth among tools.

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 6, 2024

Solution, I would require always the 2D representation.

This comment has been minimized.

Copy link
@bogovicj

bogovicj Aug 13, 2024

Author Contributor

I would require always the 2D representation.

I'm happy with this. Probably better to have fewer options for how to store stuff, and 2D is preferable to 1D for this reason.

I might also consider requiring that no transform in the sequence changes the dimensionality of its input.

This comment has been minimized.

Copy link
@bogovicj

bogovicj Aug 13, 2024

Author Contributor

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 14, 2024

Thanks for the update!

I might also consider requiring that no transform in the sequence changes the dimensionality of its input.

I think this would be too restrictive as such transformations would probably be needed for reusing transformations across elements with different axes (such as a transformation, originally created to align images, reused to align labels to the images). In spatialdata we use sequences often for this use case.

Expand Down Expand Up @@ -949,20 +986,17 @@ The matrix may be stored as a 2D array (inner arrays represent the rows of the m

#### <a name="rotation">rotation</a>

`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

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 6, 2024

typo, should be rotation not affine.

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.

<dl>
<dt><strong>path</strong></dt>
<dd> 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`,

This comment has been minimized.

Copy link
@LucaMarconato

LucaMarconato Aug 6, 2024

In this case both the 1D and 2D representation would not lead to problems in a sequence. But if affine ends up needing always to be 2D, then having a 1D list here may be ugly.

if 2D its size must be `N x N`.</dd>
if 2D its shape must be `N x N`.</dd>
<dt><strong>rotation</strong></dt>
<dd> 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`.</dd>
Expand Down

0 comments on commit 00b79f1

Please sign in to comment.