ngff_zarr.itk_transform_to_ngff_transform¶
Convert ITK transforms to RFC-5 coordinate transformations.
The inverse of :mod:ngff_zarr.ngff_transform_to_itk_transform. Its purpose
is persistence: a registration produces an ITK transform, and RFC-5 is where
that result belongs once it is written next to the image.
The same two conventions apply in reverse – the spatial block and the offset
are permuted from ITK’s fastest-axis-first order back to dims order, and ITK’s
center of rotation is folded into the offset, since an RFC-5 affine has no
center:
ITK: y = A (x - c) + t + c
RFC-5: y = A x + b with b = t + c - A c
Module Contents¶
Classes¶
The direction matrices and origins of an image pair, in ITK order. |
Functions¶
Exact parameter count a directly decoded parameterization must carry. |
|
Refuse a transform that ITK itself reports as non-linear. |
|
Guess whether the transform computes in single or double precision. |
|
The magnitude the transform’s own numbers work at. |
|
Refuse an affine whose own numbers are not usable. |
|
Refuse a transform the recovered affine does not actually describe. |
|
Recover |
|
|
|
|
|
Reduce a native |
|
The parameterization as a plain name, e.g. |
|
Decode an ITK-Wasm transform, falling back to |
|
Reduce any supported ITK transform input to a single matrix and offset. |
|
The spatial dims in ITK component order: x first, then y, then z. |
|
The matrix sending an ITK-order vector to the |
|
The inverse of an RFC-4 direction matrix. |
|
The geometry |
|
Re-express |
|
Validate the fixed/moving pair handed to a converter. |
|
Convert an ITK transform to a matrix and offset in RFC-5 axis order. |
|
Convert an ITK transform to an RFC-5 coordinate transformation. |
|
Return a less expressive equivalent transformation, or |
Data¶
API¶
- ngff_zarr.itk_transform_to_ngff_transform._SPATIAL_DIMS¶
(‘x’, ‘y’, ‘z’)
- ngff_zarr.itk_transform_to_ngff_transform._NON_LINEAR_PARAMETERIZATIONS¶
‘frozenset(…)’
- ngff_zarr.itk_transform_to_ngff_transform._direct_parameter_count(parameterization: str, dimension: int) int | None¶
Exact parameter count a directly decoded parameterization must carry.
What ITK’s
MatrixOffsetTransformBaseand friends pack, and therefore the ground truth for the transform’s dimensionality.Nonefor the parameterizations this module does not decode arithmetically.
- ngff_zarr.itk_transform_to_ngff_transform._reject_non_linear(itk_transform) None¶
Refuse a transform that ITK itself reports as non-linear.
- ngff_zarr.itk_transform_to_ngff_transform._AFFINE_CHECK_RTOL¶
None
- ngff_zarr.itk_transform_to_ngff_transform._working_precision(*samples: numpy.ndarray) str¶
Guess whether the transform computes in single or double precision.
itk.AffineTransform[itk.F, 3]answersTransformPointwith about seven digits, so holding it to a double-precision tolerance rejects every valid single-precision transform. Rather than read the ITK type name, which is a private spelling, look at whether the values coming back are exactly representable infloat32: they are when the transform computes infloat32, and a double-precision result generally is not. Guessingfloat32for a double-precision transform that happens to return exact values only loosens the check on values that carry no round-off anyway.
- ngff_zarr.itk_transform_to_ngff_transform._working_scale(offset: numpy.ndarray) float¶
The magnitude the transform’s own numbers work at.
Both the probing step and the point the recovered affine is checked at follow it, because either one fixed near the origin stops meaning anything once the offset dominates: with a step of 1 and an offset of 1e15,
T(h e_j) - T(0)keeps no significant digit and probing reports a zero matrix that a check near the origin cannot tell from the truth. An affine map is exact for any step, so there is no truncation error to trade against and the usual “small step” rule is exactly backwards here.
- ngff_zarr.itk_transform_to_ngff_transform._reject_non_finite(itk_transform, matrix, offset) None¶
Refuse an affine whose own numbers are not usable.
- ngff_zarr.itk_transform_to_ngff_transform._check_affine_model(itk_transform, matrix, offset, samples) None¶
Refuse a transform the recovered affine does not actually describe.
Neither reading a transform’s matrix nor probing it proves the transform is affine. Probing a deformation succeeds and returns a plausible affine that is simply wrong away from the probed points, and
AzimuthElevationToCartesianTransforminherits an affine’s matrix and offset, overridesTransformPoint, and still reports itself linear (InsightSoftwareConsortium/ITK#6791). So the recovered model is confronted with one more evaluation, at a point no probe reached.
- ngff_zarr.itk_transform_to_ngff_transform._matrix_offset_by_probing(itk_transform, dimension: int)¶
Recover
(matrix, offset)by evaluating the transform.The fallback for a transform that carries no matrix and offset of its own:
offset = T(0)andmatrix[:, j] = (T(h e_j) - T(0)) / h. Exact for any linear transform whatever parameterization it stores, but it pays for that in cancellation, which is why the step follows the transform’s own scale (see :func:_working_scale). The limit it leaves: a matrix coefficient whose contribution at the probe scale falls below the rounding of the offset itself is unrecoverable by evaluation and folds into the offset. Reading the matrix directly has no such limit, so this runs only where that is impossible.
- ngff_zarr.itk_transform_to_ngff_transform._homogeneous(matrix: numpy.ndarray, offset: numpy.ndarray) numpy.ndarray¶
y = A x + bas a square matrix acting on homogeneous coordinates.
- ngff_zarr.itk_transform_to_ngff_transform._matrix_offset_direct(itk_transform, dimension: int)¶
(matrix, offset)read from the transform’s own numbers, orNone.Every
MatrixOffsetTransformBase– an affine, and the rigid, rotation and similarity types built on it – evaluatesy = GetMatrix() x + GetOffset()with the center of rotation already folded into the offset, whatever it stores its parameters as. Reading that pair beats recomposing it from three evaluations, which is a difference of nearly equal points: a transform combining a large offset with a small matrix coefficient, as nanometre coordinates produce, loses the coefficient to cancellation and would be persisted as a singular mapping.Nonefor a transform that carries no such pair, which then falls back to probing.
- ngff_zarr.itk_transform_to_ngff_transform._matrix_offset_from_itk(itk_transform, dimension: int)¶
Reduce a native
itk.Transformto a single matrix and offset.
- ngff_zarr.itk_transform_to_ngff_transform._parameterization_name(transform_type) str¶
The parameterization as a plain name, e.g.
"Affine".itkwasm.TransformParameterizationsmixes instr, and since Python 3.11str()on such a member returns"TransformParameterizations.Affine"rather than its value. Reading.valuekeeps a member and a plain string on the same footing.
- ngff_zarr.itk_transform_to_ngff_transform._matrix_offset_from_itkwasm(entry, dimension: int)¶
Decode an ITK-Wasm transform, falling back to
itkwhen needed.
- ngff_zarr.itk_transform_to_ngff_transform._itk_matrix_offset(transform, dimension: int)¶
Reduce any supported ITK transform input to a single matrix and offset.
- ngff_zarr.itk_transform_to_ngff_transform._itk_axis_order(spatial)¶
The spatial dims in ITK component order: x first, then y, then z.
ITK orders points fastest-axis-first by name, not by reversing however the caller spelled
dims: reversing is only right for the canonical (z, y, x).
- ngff_zarr.itk_transform_to_ngff_transform._permutation_from_itk(spatial) numpy.ndarray¶
The matrix sending an ITK-order vector to the
dims-order vector.
- ngff_zarr.itk_transform_to_ngff_transform._inverse_direction(direction: numpy.ndarray) numpy.ndarray¶
The inverse of an RFC-4 direction matrix.
Every column names one LPS axis with a sign, so the matrix is a signed permutation: orthogonal, and its transpose is its exact inverse. Going through
np.linalg.invwould round where the transpose does not, and would answer a direction that somehow came through singular with aLinAlgErrorrather than with geometry.
- class ngff_zarr.itk_transform_to_ngff_transform._FrameGeometry¶
Bases:
typing.NamedTupleThe direction matrices and origins of an image pair, in ITK order.
A tuple, so it still unpacks straight into :func:
_change_of_frame, whose arguments it names in order.- direction_in: numpy.ndarray¶
None
- direction_out: numpy.ndarray¶
None
- origin_in: numpy.ndarray¶
None
- origin_out: numpy.ndarray¶
None
- ngff_zarr.itk_transform_to_ngff_transform._frame_geometry(
- fixed,
- moving,
- itk_dims,
The geometry
ngff_image_to_itk_imagegives the two images.
- ngff_zarr.itk_transform_to_ngff_transform._change_of_frame(
- matrix,
- offset,
- direction_in,
- direction_out,
- origin_in,
- origin_out,
Re-express
y = A x + tthrough a change of frame on each side.ngff_image_to_itk_imagebuilds each image withorigin = translationand the RFC-4 direction matrixD, so an intrinsic pointpsits at the physical pointphi(p) = D (p - o) + o. Given a mappingTbetween two frames, this returnsphi_out^-1 . T . phi_infor the directions and origins supplied:M = D_out^-1 A D_in b = D_out^-1 (A (I - D_in) o_in + t - o_out) + o_out
phi^-1has the same shape asphiwithD^-1in place ofD, so the same formula serves both conversions: ITK to RFC-5 passes the images’ directions, RFC-5 to ITK passes their inverses. The scales cancel on both sides; the translations do not, which is why orientations alone would not be enough. With no anatomical orientation every direction is the identity and the mapping comes back unchanged.
- ngff_zarr.itk_transform_to_ngff_transform._check_frame_images(fixed, moving, spatial)¶
Validate the fixed/moving pair handed to a converter.
- ngff_zarr.itk_transform_to_ngff_transform.itk_transform_to_ngff_matrix(
- transform,
- dims: collections.abc.Sequence[str],
- *,
- fixed=None,
- moving=None,
Convert an ITK transform to a matrix and offset in RFC-5 axis order.
- Parameters:
transform – An
itk.Transform(including aCompositeTransform), an ITK-WasmTransform, or an ITK-WasmTransformList.dims (Sequence[str]) – The axis names of the coordinate system the result should be expressed on, in RFC-5 (Zarr) order, e.g.
("z", "y", "x"). Only the spatial axes take part in the transform.
- Returns:
(matrix, offset)over the spatial axes, in Zarr order.- Return type:
tuple[numpy.ndarray, numpy.ndarray]
- Raises:
NotImplementedError – If the transform is not linear.
- ngff_zarr.itk_transform_to_ngff_transform.itk_transform_to_ngff_transform(
- transform,
- dims: collections.abc.Sequence[str],
- simplify: bool = True,
- *,
- fixed=None,
- moving=None,
Convert an ITK transform to an RFC-5 coordinate transformation.
This is what lets a registration result be written into an OME-Zarr store: run the registration, convert the transform, and attach it to the multiscales metadata.
- Parameters:
transform – An
itk.Transform(including theCompositeTransforman Elastix registration returns), an ITK-WasmTransform, or an ITK-WasmTransformList.dims (Sequence[str]) – The axis names of the coordinate system the transformation is expressed on, in RFC-5 (Zarr) order. Non-spatial axes (
t,c) are left untransformed.simplify (bool) – Return the least expressive transformation that represents the mapping exactly –
identity,translation,scale, or asequenceof scale and translation – falling back toaffine. RFC-5 recommends this. A mirror never simplifies to ascale, since RFC-5 requires strictly positive scale factors. Note thatmultiscales > datasetsaccepts only a singlescale, a singleidentity, or a two-elementsequenceof scale and translation, so a baretranslationor anaffinebelongs in the multiscales-levelcoordinateTransformationsinstead. Set toFalseto always get anaffine.fixed (NgffImage, optional) – The fixed and moving images the ITK transform was produced on. An ITK transform acts on ITK physical space, which includes the direction matrix derived from RFC-4 anatomical orientation; an RFC-5 transformation acts on the intrinsic coordinate systems. Passing both images lets the conversion change frames exactly. Omitting them is exact only when neither image carries an anatomical orientation.
moving (NgffImage, optional) – See
fixed. Pass both or neither.
- Returns:
An RFC-5 coordinate transformation over
dims.- Return type:
Transform
- Raises:
NotImplementedError – If the transform is not linear. A non-linear registration has no affine equivalent.