ngff_zarr.displacement_field_transform¶
Bridge ITK displacement fields and RFC-5 displacements transformations.
A displacement field is a transformation and an array at once. ITK keeps the
array inside the transform, as a vector image with its own grid; RFC-5 keeps it
in the store, as a multiscale image the displacements entry points at by
path. So the two conversions here are the only ones in this package whose
result has two parts, and they stay pure: nothing is read from or written to a
store. The caller writes the field next to its image, and loads it back, with
- func:
~ngff_zarr.to_ome_zarrand :func:~ngff_zarr.from_ome_zarr.
The same two conventions as the affine conversion apply, plus one.
Axis order
ITK orders a vector’s components fastest-axis-first, x then y then z, and
lays the field out as [z][y][x][component]. RFC-5 says the i-th
component of the field refers to the i-th axis of the output
coordinate system, so components follow dims; the same permutation as
for an affine’s matrix is applied to the component axis.
Frames
An ITK vector is a difference of two physical points. An RFC-5
displacement is a difference of two intrinsic points, d = q' - q, so
the two differ by more than a rotation as soon as the images sit in
different frames. Both directions follow from one identity,
phi_out(q + d(q)) = phi_in(q) + v(q), worked out in
:func:_frame_terms on top of the affine conversion’s change of frame.
The field’s own grid follows phi_in^-1, so its origin moves and its
spacing does not.
Grid direction
RFC-5 maps the field’s array coordinates to the input system with the
field’s own coordinateTransformations, which this package writes as a
scale and a translation. That can only express a grid oriented like the
input image, so a field whose direction matrix differs from the fixed
image’s (the identity when no frames are given) is refused rather than
written with a mapping a reader would misread. Registrations sample the
field on the fixed grid, so the common case is exact.
Module Contents¶
Classes¶
The range of displacement each chunk of a field can produce. |
Functions¶
Validate the spatial-only |
|
The two images’ directions and origins in ITK order, or |
|
The frame pair of two images with no orientation and no translation. |
|
The per-point terms relating ITK vectors and RFC-5 displacements. |
|
|
|
The per-voxel value conversion both field directions share. |
|
Read any supported field input into ITK conventions. |
|
The field an ITK-Wasm |
|
The field a vector image dictionary holds. |
|
Convert an ITK displacement field to an RFC-5 |
|
The field image a field transform names, checked against |
|
Convert one block of a field between ITK’s convention and RFC-5’s. |
|
Refuse a field whose grid cannot be placed without the two images. |
|
Smallest and largest displacement per component over one block. |
|
Bound the displacement of every chunk of |
|
The field indices a grid of |
|
Convert an RFC-5 |
|
The field |
Data¶
API¶
- ngff_zarr.displacement_field_transform._COMPONENT_DIM¶
‘c’
- ngff_zarr.displacement_field_transform._check_dims(dims: collections.abc.Sequence[str]) tuple[str, ...]¶
Validate the spatial-only
dimsa displacement field is defined on.
- ngff_zarr.displacement_field_transform._frames(fixed, moving, dims)¶
The two images’ directions and origins in ITK order, or
None.
- ngff_zarr.displacement_field_transform._unoriented_frames(
- dimension: int,
The frame pair of two images with no orientation and no translation.
With it the terms below all vanish, so the conversion that changes frames and the one that does not are the same arithmetic rather than two branches.
- ngff_zarr.displacement_field_transform._frame_terms(frames: ngff_zarr.itk_transform_to_ngff_transform._FrameGeometry)¶
The per-point terms relating ITK vectors and RFC-5 displacements.
An ITK vector is a difference of two physical points; an RFC-5 displacement is a difference of two intrinsic points. Writing
phi_out^-1 . phi_inasq -> M q + b– the change of frame the affine conversion applies, given the identity as its mapping – the defining identityphi_out(q + d(q)) = phi_in(q) + v(q)rearranges tod(q) = D_out^-1 v + (M - I) q + b v(q) = D_out (d - (M - I) q - b)
so one derivation serves both directions. The two
qterms vanish when the images share a frame, leavingd = D_out^-1 v.- Returns:
(D_out, M - I, b).
- ngff_zarr.displacement_field_transform._grid_shift(shape, origin, spacing, matrix, vector)¶
matrix q + vectorat every grid point, orNonewhen it is zero.shapeis the field’s[z][y][x]layout, so the index arrays come out slowest-axis-first and are reversed into ITK component order. The result is(*shape, N), ready to add to or subtract from the field.
- ngff_zarr.displacement_field_transform._convert_field_values(
- vectors: numpy.ndarray,
- origin: numpy.ndarray,
- spacing: numpy.ndarray,
- frames: ngff_zarr.itk_transform_to_ngff_transform._FrameGeometry,
- *,
- absolute: bool = False,
- inverse: bool = False,
The per-voxel value conversion both field directions share.
vectorsis(*grid, N)with ITK-ordered components;originandspacingare the GRID the values sit on, in ITK order – for a window of a larger field, the window’s own origin, which is what makes the conversion per-block: every term is a function of the voxel’s position and the frames alone.Forward (
inverse=False) turns ITK vectors into RFC-5 values:v @ inverse_out.Tthen the positional frame term added. Inverse takes the frame term off first and rotates back, so the two are exact inverses.shift_matrixisM - I; an absolute (coordinates) field holdsq + drather thand, soabsolutefolds the grid point itself into the positional term, in one pass over the grid.
- ngff_zarr.displacement_field_transform._decode_field(transform)¶
Read any supported field input into ITK conventions.
Returns
(vectors, origin, spacing, direction): the field as a[z][y][x][component]array with components in ITK order, and its grid in ITK component order.
- ngff_zarr.displacement_field_transform._decode_itkwasm_transform(transform)¶
The field an ITK-Wasm
DisplacementFieldentry packs.
- ngff_zarr.displacement_field_transform._decode_image_dict(image)¶
The field a vector image dictionary holds.
- ngff_zarr.displacement_field_transform.itk_displacement_field_to_ngff_transform(
- transform,
- dims: collections.abc.Sequence[str],
- *,
- path: str,
- fixed: ngff_zarr.ngff_image.NgffImage | None = None,
- moving: ngff_zarr.ngff_image.NgffImage | None = None,
Convert an ITK displacement field to an RFC-5
displacementstransform.The result has two parts, because the field is an array: the transform metadata, which names
path, and the field itself as an image to be written at that path. Write the field first, into a subgroup of the store the image goes to, then the image::transform, field = itk_displacement_field_to_ngff_transform( itk_transform, ["z", "y", "x"], path="displacement_field" ) to_ome_zarr(f"{store}/displacement_field", to_multiscales(field)) multiscales.metadata.coordinateTransformations = [transform] to_ome_zarr(store, multiscales, overwrite=False)- Parameters:
transform – An
itk.DisplacementFieldTransform, a vectoritk.Imageoritkwasm.Imageholding the field directly (the form a warp comes in from most registration tools), an ITK-WasmDisplacementFieldtransform, or a one-entry list of it. The field maps fixed points into moving space.dims (Sequence[str]) – The spatial axis names of the input coordinate system, in RFC-5 (Zarr) order. The field must be defined on these axes and no others.
path (str) – Where the field will be written, relative to the image’s group. Recorded on the returned transform.
fixed (NgffImage, optional) – The fixed and moving images the field relates. Passing both re-expresses the vectors on the images’ intrinsic coordinate systems, including the direction matrix derived from RFC-4 anatomical orientation, and gives the field the fixed image’s orientation and units. Omitting them is exact only when neither image carries an anatomical orientation.
moving (NgffImage, optional) – See
fixed. Pass both or neither.
- Returns:
The
displacementstransform, withinterpolationset tolinear(ITK’s interpolator for a field), and the field as anNgffImagewhose first axis carries the components,type: "displacement", followed bydims.- Return type:
- Raises:
ValueError – If the field’s grid is not oriented like the fixed image (the identity without frames), which the field’s scale and translation could not express; resample the field onto the fixed grid first. Also for a field whose dimensionality does not match
dims.
- ngff_zarr.displacement_field_transform.field_image(
- transform: ngff_zarr.v06.zarr_metadata.Displacements | ngff_zarr.v06.zarr_metadata.Coordinates,
- field,
- dims: collections.abc.Sequence[str],
The field image a field transform names, checked against
dims.Takes the
NgffImageor theNgffMultiscalesa caller passes infields, and returns the single-scale image, after checking that its axes are the component axis followed bydimsin order and that it holds one component per input axis.- Parameters:
transform (Displacements | Coordinates) – The
displacementsorcoordinatestransform.field – The field image or multiscales, as
fieldsholds it.dims (Sequence[str]) – The spatial axis names of the input coordinate system, in RFC-5 (Zarr) order.
- Returns:
The field as a single-scale image; the finest level of a multiscales.
- Return type:
- Raises:
ValueError – If the field has no single component axis of the type the transform calls for, if its axes are not that axis followed by
dims, or if it holds a number of components other thanlen(dims).
- ngff_zarr.displacement_field_transform.convert_itk_field_block(
- values: numpy.ndarray,
- dims: collections.abc.Sequence[str],
- *,
- translation: collections.abc.Sequence[float],
- spacing: collections.abc.Sequence[float],
- fixed: ngff_zarr.ngff_image.NgffImage | None = None,
- moving: ngff_zarr.ngff_image.NgffImage | None = None,
- transform_type: str = 'displacements',
- inverse: bool = False,
Convert one block of a field between ITK’s convention and RFC-5’s.
The block-level face of the two whole-field converters, for a field that is never assembled: a producer computing an ITK-convention field region by region converts each block on its way into a store created with
metadata_only=True, and a consumer reads a window back the same way. Every term of the conversion is a function of the voxel’s position and the frames alone, so converting a block with the BLOCK’s own origin equals cutting that block from the converted whole – which is what the tests pin, against the whole-field converters themselves.- Parameters:
values (np.ndarray) – The block, channel-first
(N, *block)with the spatial axes indimsorder. Forward, the components are ITK’s (a vector’s x, then y, then z); withinverse=Truethey followdims, as the store holds them.dims (Sequence[str]) – The spatial axis names of the input coordinate system, in RFC-5 (Zarr) order.
translation (Sequence[float]) – Where the block starts on the field’s grid, per axis in
dimsorder: the field’s owntranslationadvanced by the block’s voxel offset timesspacing. This is the RFC-5 value, in the input coordinate system’s frame – withfixed/movingit is NOT the block’s ITK physical origin, which the fixed image’s direction moves away from it, and the whole-field converters take this same value.spacing (Sequence[float]) – The field’s scale per axis, in
dimsorder.fixed (NgffImage, optional) –
The fixed and moving images the field relates; see
- func:
itk_displacement_field_to_ngff_transform. Pass both or neither; with neither the frames are one, and the conversion is the component permutation alone.
moving (NgffImage, optional) – See
fixed.transform_type (str, optional) –
displacements(offsets) orcoordinates(absolute output positions).inverse (bool, optional) – Convert the store’s values back to ITK’s convention.
- Returns:
The converted block, channel-first, contiguous, components in
dimsorder (ITK’s withinverse=True).- Return type:
np.ndarray
- Raises:
ValueError – If the block’s component count does not match
dims, or for an unknowntransform_type.
- ngff_zarr.displacement_field_transform.check_unoriented_field(
- field: ngff_zarr.ngff_image.NgffImage,
- dims: collections.abc.Sequence[str],
Refuse a field whose grid cannot be placed without the two images.
An ITK transform lives in physical space, and an anatomical orientation only says where a grid sits once the images it relates are known.
- Parameters:
- Raises:
ValueError – If the field carries an anatomical orientation.
- class ngff_zarr.displacement_field_transform.FieldBound¶
The range of displacement each chunk of a field can produce.
A field is read through a kernel that is non-negative and sums to one, so a displacement anywhere in a chunk is a convex combination of that chunk’s values and lies between their smallest and largest. That makes the range a bound on every interpolated displacement, not a sample of one: walking the boundary of a region instead misses a bump the region encloses.
The range is kept rather than its magnitude, so a field that shifts every point the same way moves the region it bounds instead of widening it.
The granularity is the field’s own chunking, since that is what a read costs. A field stored in one chunk therefore reports one range for the whole volume, which is also the case where the field fits in memory.
- low: numpy.ndarray¶
None
- high: numpy.ndarray¶
None
- over(
- window: collections.abc.Sequence[tuple[int, int]],
- outside: bool = False,
The displacement range over the chunks
windowtouches.- Parameters:
window (Sequence[tuple[int, int]]) –
(start, stop)per spatial axis, in field index space and indimsorder.outside (bool) – Whether the grid the window came from also has points beyond the field. ITK displaces those by nothing, so zero belongs in the range as much as the values do; leaving it out lets a field that displaces every point it covers one way carry the region away from the points it does not cover.
- Returns:
{dim: (low, high)}per component, keyed by dimension. Zero for an empty window, where the field displaces nothing.- Return type:
- ngff_zarr.displacement_field_transform._block_extrema(
- block,
- block_info=None,
- *,
- origin=None,
- spacing=None,
- offset=None,
Smallest and largest displacement per component over one block.
Comes back as one array of twice the components, the minima before the maxima, so a single pass over the field answers both.
offsetis where the array this block came from starts in the field, since the block is located against its own array rather than the field.
- ngff_zarr.displacement_field_transform.field_displacement_bound(
- transform: ngff_zarr.v06.zarr_metadata.Displacements | ngff_zarr.v06.zarr_metadata.Coordinates,
- field: ngff_zarr.ngff_image.NgffImage,
- dims: collections.abc.Sequence[str],
- window: collections.abc.Sequence[tuple[int, int]] | None = None,
Bound the displacement of every chunk of
field, one chunk at a time.One pass over the field, reading a chunk and keeping two numbers per component. The values are not held: what comes back is a few floats per chunk, which is what lets a caller size its reads against a field that does not fit in memory.
- Parameters:
transform (Displacements | Coordinates) – The
displacementsorcoordinatestransform the field belongs to.field (NgffImage) – The field image, as :func:
field_imagereturns it.dims (Sequence[str]) – The spatial axis names of the input coordinate system, in RFC-5 (Zarr) order.
window (Sequence[tuple[int, int]] | None) –
The field indices a caller will ask about, as
- func:
field_windowreturns them. The pass then covers the chunks that window touches and no others, which is what a grid smaller than the field it is defined on saves. Defaults to the whole field.
- Returns:
The per-chunk range, keyed by the field’s own indices.
- Return type:
- ngff_zarr.displacement_field_transform.field_window(
- field: ngff_zarr.ngff_image.NgffImage,
- dims: collections.abc.Sequence[str],
- translation: collections.abc.Mapping[str, float],
- scale: collections.abc.Mapping[str, float],
- shape: collections.abc.Sequence[int],
- margin: int = 1,
The field indices a grid of
shapeattranslationreads.The field is evaluated at the grid’s own points, so the window is that grid’s extent expressed in field indices, whatever the displacement is: what a displacement sizes is the moving read, which
- Class:
FieldBoundanswers.- Parameters:
field (NgffImage) – The field image.
dims (Sequence[str]) – The spatial axis names, in RFC-5 order.
translation (Mapping[str, float]) – The grid’s translation, keyed by dimension.
scale (Mapping[str, float]) – The grid’s scale, keyed by dimension.
shape (Sequence[int]) – The grid’s extent, in
dimsorder.margin (int) – Lattice points kept beyond the bracketing pair, so that linear interpolation at a point on the boundary reads the same values it reads from the whole field.
- Returns:
(start, stop)per axis, indimsorder, clamped to the field, and whether the grid also has points beyond the field, which ITK displaces by nothing. :meth:FieldBound.overtakes the second as itsoutside.- Return type:
- ngff_zarr.displacement_field_transform.ngff_displacement_field_to_itk_transform(
- transform: ngff_zarr.v06.zarr_metadata.Displacements | ngff_zarr.v06.zarr_metadata.Coordinates,
- field,
- dims: collections.abc.Sequence[str],
- *,
- fixed: ngff_zarr.ngff_image.NgffImage | None = None,
- moving: ngff_zarr.ngff_image.NgffImage | None = None,
Convert an RFC-5
displacementsorcoordinatestransform to ITK.The counterpart of :func:
itk_displacement_field_to_ngff_transform, and what :func:~ngff_zarr.ngff_transform_to_itk_transformcalls when handed a field transform with its field. The field is the image stored attransform.path; load it withfrom_ome_zarr(f"{store}/{transform.path}").A
coordinatesfield holds the absolute output position of each grid point where adisplacementsfield holds the offset from it. The two differ by the position of the grid point itself, so both reach ITK as oneDisplacementField: ITK has no absolute-coordinate transform.- Parameters:
transform (Displacements | Coordinates) – The
displacementsorcoordinatestransform.field – The field image: an
NgffImagewhose component axis is the one withaxes_typesdisplacement(coordinatefor acoordinatestransform), followed bydimsin order; or anNgffMultiscales, whose finest level is used.dims (Sequence[str]) – The spatial axis names of the input coordinate system, in RFC-5 (Zarr) order.
fixed (NgffImage, optional) –
The fixed and moving images the field relates; see
- func:
itk_displacement_field_to_ngff_transform. The field’s own orientation, if any, must be the fixed image’s.
moving (NgffImage, optional) – See
fixed. Pass both or neither.
- Returns:
A single-entry ITK-Wasm
TransformListof parameterizationDisplacementField.itk.transform_from_dictturns it into a nativeitk.DisplacementFieldTransform.- Return type:
list[itkwasm.Transform]
- Raises:
ValueError – If the field’s axes are not the component axis followed by
dims, or if its orientation is not the fixed image’s.
- ngff_zarr.displacement_field_transform._fields_entry(
- transform: ngff_zarr.v06.zarr_metadata.Displacements | ngff_zarr.v06.zarr_metadata.Coordinates,
- fields: collections.abc.Mapping[str, object] | None,
The field
fieldsholds fortransform, with a message otherwise.