ngff_zarr.resample_bounding_box¶
Find the region of a moving image needed to resample a fixed image grid.
Module Contents¶
Classes¶
The region of a moving image needed to resample a fixed image grid. |
Functions¶
|
|
Require a finite scale and translation for every spatial axis. |
|
Reject a pipeline result that cannot fill one bound per dimension. |
|
Reject a region that does not contain the points it was derived from. |
|
Translation of the sub-grid of |
|
Direction matrix from RFC-4 orientation, matching ngff_image_to_itk_image. |
|
Build an ITK-Wasm image carrying geometry only, with an empty buffer. |
|
Normalize a supported transform input into an ITK-Wasm transform list. |
|
One list entry’s displacement range, or |
|
Whether |
|
The range the displacement stages of |
|
Split a transform list into its linear part and a displacement range. |
|
The grid’s physical extent per component, direction included. |
|
The physical range as the two per-dimension mappings |
|
The region the pipeline reports for the identity, computed directly. |
|
The field a field transform names, its per-chunk range, and its window. |
|
A field transform’s linear part, which is the identity. |
|
Compute the moving-image region needed to resample a fixed image grid. |
Data¶
API¶
- ngff_zarr.resample_bounding_box._SPATIAL_DIMS¶
(‘x’, ‘y’, ‘z’)
- class ngff_zarr.resample_bounding_box.ResampleBoundingBox¶
The region of a moving image needed to resample a fixed image grid.
Every mapping is keyed by dimension name, so callers never have to track whether a given array is in Zarr or ITK axis order.
- slices(dims: collections.abc.Sequence[str] | None = None) tuple[slice, ...]¶
Slices selecting the region, clamped to the moving image bounds.
Negative start indices are clamped rather than passed through, since a negative bound would silently wrap around instead of raising.
- crop(
- moving: ngff_zarr.ngff_image.NgffImage,
Lazily crop
movingto this region, correcting its translation.The returned image indexes the same Dask graph – nothing is computed – so only this region’s chunks are read when it is finally materialized.
- ngff_zarr.resample_bounding_box._grown(
- region: ngff_zarr.resample_bounding_box.ResampleBoundingBox,
- bound: collections.abc.Mapping[str, tuple[float, float]],
- moving: ngff_zarr.ngff_image.NgffImage,
- index_bound: collections.abc.Mapping[str, tuple[float, float]] | None = None,
regionmoved and widened by a per-axis displacement range.The pipeline walks the boundary of the transformed grid, which reports where a linear map sends the grid exactly and misses whatever a displacement does strictly inside it. A point
pof the region reachesp + dwithdinbound, so the region’s image lies between the two ends of that range: a field that shifts every point the same way moves the region, and only what varies widens it.
- ngff_zarr.resample_bounding_box._spatial_dims(ngff_image: ngff_zarr.ngff_image.NgffImage) list[str]¶
- ngff_zarr.resample_bounding_box._check_geometry(
- label: str,
- image: ngff_zarr.ngff_image.NgffImage,
- spatial: collections.abc.Sequence[str],
Require a finite scale and translation for every spatial axis.
A missing or non-finite entry would otherwise reach the pipeline and come back as a plausible-looking region computed from the wrong geometry.
- ngff_zarr.resample_bounding_box._check_index_arrays(
- result: dict,
- itk_dims: collections.abc.Sequence[str],
Reject a pipeline result that cannot fill one bound per dimension.
The returned mappings are built from these two arrays alone, so a short or non-finite result would otherwise surface as an opaque failure further down, or as an undefined bound a caller slices with.
- ngff_zarr.resample_bounding_box._check_region_contains_corners(
- result: dict,
- itk_dims: collections.abc.Sequence[str],
Reject a region that does not contain the points it was derived from.
The pipeline computes the integer region in 32-bit index space while reporting the corners as doubles, so a grid whose extent exceeds that range comes back as a wrapped – and typically empty – region. Padding is non-negative, so a correct region always spans its own tight corners; checking that catches the wrap without assuming an axis direction.
- ngff_zarr.resample_bounding_box._shifted_translation(
- ngff_image: ngff_zarr.ngff_image.NgffImage,
- starts: dict,
Translation of the sub-grid of
ngff_imagethat starts atstarts.ITK places index
iatorigin + direction @ (i * spacing)and- Func:
ngff_image_to_itk_imageusestranslationas the origin, so the origin of a sub-grid is the translation moved by the index offset rotated through the RFC-4 direction matrix. Dimensions with no orientation, and non-spatial dimensions, move along their own axis.
- ngff_zarr.resample_bounding_box._itk_direction(
- ngff_image: ngff_zarr.ngff_image.NgffImage,
- itk_dims: collections.abc.Sequence[str],
Direction matrix from RFC-4 orientation, matching ngff_image_to_itk_image.
All-or-nothing: unless every spatial axis carries an orientation that maps onto an LPS axis, the direction falls back to identity.
- ngff_zarr.resample_bounding_box._metadata_only_itk_image(
- ngff_image: ngff_zarr.ngff_image.NgffImage,
- itk_dims: collections.abc.Sequence[str],
- direction: numpy.ndarray,
Build an ITK-Wasm image carrying geometry only, with an empty buffer.
ngff_image.datais never converted to an array – only itsshapeanddtypeare read, which are Dask metadata. This is what lets the region be computed against images whose pixels are remote or enormous.
- ngff_zarr.resample_bounding_box._NGFF_TRANSFORM_TYPES¶
‘frozenset(…)’
- ngff_zarr.resample_bounding_box._as_itk_transform_list(transform) list¶
Normalize a supported transform input into an ITK-Wasm transform list.
ITK applies the last entry of a transform list first, and so does an
itk.CompositeTransform, so the order carries over unchanged.
- ngff_zarr.resample_bounding_box._INTERVAL_PARAMETERIZATIONS¶
None
- ngff_zarr.resample_bounding_box._ORTHONORMAL_PARAMETERIZATIONS¶
‘frozenset(…)’
- ngff_zarr.resample_bounding_box._stage_interval(entry, dimension: int)¶
One list entry’s displacement range, or
Nonefor the rest.- Returns:
(low, high)per physical component, orNonewhen the entry is not one of the two interval parameterizations. Whether zero joins the range is the caller’s question: it depends on whether the grid can leave the stage’s domain, where ITK returns the point unchanged.
- ngff_zarr.resample_bounding_box._box_inside_field_domain(entry, dimension: int, grid_box) bool¶
Whether
grid_boxstays on the field’s own lattice.A
DisplacementField’s fixed parameters carry its grid: size, origin, spacing, then the direction, row-major, the layout- Func:
~ngff_zarr.ngff_displacement_field_to_itk_transformwrites and ITK reads. A point past the lattice is displaced by nothing, so a grid that leaves it takes zero into its range; one that stays inside keeps the values’ own range, which is what makes a constant field an exact shift. Anything not answerable exactly, a rotated field grid included, answersFalse, which only widens. ABSplinenever answersTrue: its control lattice reaches spline-order points beyond its domain of support, so lying inside the lattice proves nothing about staying on the domain.
- ngff_zarr.resample_bounding_box._accumulated_interval(entries, intervals, dimension: int, contained: bool)¶
The range the displacement stages of
entriescan add, orNone.ITK applies the last entry of a list first, so the walk runs from the last entry outward, carrying what the stages seen so far can add. A displacement stage adds its own range, since the linear list this widens replaced that stage by the identity; an affine stage maps the range through the signs of its matrix; an orthonormal stage bounds every component by the range’s Euclidean reach. Any other stage returns
None, and the caller keeps the boundary walk.Accumulating outward rather than folding each stage separately is what lets a displacement stage sit outside another one: the range reaching it is carried through, and its own range joins on top.
- Parameters:
intervals – One :func:
_stage_intervalper entry, so a field’s parameters are scanned once for the whole list.
- ngff_zarr.resample_bounding_box._nonlinear_interval(entries, dimension: int, grid_box=None)¶
Split a transform list into its linear part and a displacement range.
- Parameters:
grid_box –
(low, high)per physical component of the fixed grid, when the caller knows it. A loneDisplacementFieldwhose lattice contains the box keeps the values’ own range; in every other case zero joins the range, since ITK displaces a point beyond a stage’s domain by nothing.- Returns:
(affine_entries, (low, high))when every stage could be carried: the entries with the displacement stages replaced by the identity, whose boundary walk the pipeline reports exactly, and the range to widen its region by.(entries, None)otherwise, and the caller’s boundary walk stands, with the miss its docstring names.
- ngff_zarr.resample_bounding_box._grid_physical_box(grid: ngff_zarr.ngff_image.NgffImage, itk_dims)¶
The grid’s physical extent per component, direction included.
The directions RFC-4 orientations produce are signed permutations, so each physical component follows exactly one grid axis and the box is the per-axis pair of endpoint positions.
Nonefor any other direction.
- ngff_zarr.resample_bounding_box._direction_bounds(interval, itk_dims, direction)¶
The physical range as the two per-dimension mappings
_growntakes.The corners the region reports are physical positions keyed by dimension name, so component
jwidens the dimension nameditk_dims[j]. The start and size are indices, and an index moves through the direction’s transpose: with the signed permutations RFC-4 orientations produce, axiskfollows the one component its column names, sign included.
- ngff_zarr.resample_bounding_box._identity_region(
- grid: ngff_zarr.ngff_image.NgffImage,
- moving: ngff_zarr.ngff_image.NgffImage,
- padding: int,
The region the pipeline reports for the identity, computed directly.
A field transform’s linear part is the identity, so its ungrown region is plain arithmetic: the grid’s own physical extent, read off in the moving image’s index space, floored and ceiled, padded. Equality with the pipeline is pinned by
test_the_identity_region_is_the_pipelinesover randomized geometry. Only :func:~ngff_zarr.resample’s per-block loop uses it, where the pipeline round trip measured as three quarters of the graph build; :func:resample_bounding_boxitself stays on the pipeline.gridmust have at least one sample per spatial axis, which- Func:
resample_bounding_boxestablishes before reaching a transform.
- ngff_zarr.resample_bounding_box._field_stream(
- transform,
- fields: collections.abc.Mapping[str, object] | None,
- fixed: ngff_zarr.ngff_image.NgffImage,
- spatial: collections.abc.Sequence[str],
- extent: collections.abc.Mapping[str, int],
The field a field transform names, its per-chunk range, and its window.
A
displacementsorcoordinatestransform is the identity plus a displacement, so a region is the grid’s own image moved and widened by what the field can displace there. The range comes from the field’s values, read one chunk at a time and kept as two numbers per chunk, so a field larger than memory is bounded without being held.The field comes back carrying the axis type its component axis has, which a field read from a multiscales keeps in the metadata rather than on the image, so a crop of it is typed without consulting the transform again.
- ngff_zarr.resample_bounding_box._identity_transform_list(dims: collections.abc.Sequence[str]) list¶
A field transform’s linear part, which is the identity.
- ngff_zarr.resample_bounding_box.resample_bounding_box(
- transform,
- fixed: ngff_zarr.ngff_image.NgffImage,
- moving: ngff_zarr.ngff_image.NgffImage,
- padding: int = 1,
- *,
- fields: collections.abc.Mapping[str, object] | None = None,
Compute the moving-image region needed to resample a fixed image grid.
The region is derived from image geometry alone – the pixel buffers of
fixedandmovingare never read, and their Dask graphs are never computed. That is the point: describe two images and a transform with a few numbers, learn exactly which block of the moving image a resample will touch, and only then move pixels.Two kinds of transform are accepted, and they are interpreted in different coordinate spaces:
An RFC-5 coordinate transformation acts on the intrinsic coordinate system, where a point is
translation + scale * index. Its parameters are in Zarr axis order and no direction matrix applies. Itsinputandoutputidentifiers are not resolved: the transformation is applied from the fixed image’s intrinsic system to the moving image’s, whatever the identifiers name.An ITK transform (for example a
CompositeTransformreturned by Elastix) acts on ITK physical space, so the geometry is built the way- func:
ngff_zarr.ngff_image_to_itk_imagebuilds it, including the direction matrix derived from RFC-4 anatomical orientation.
An ITK transform need not be linear. An RFC-5 transformation is converted first, so it must be one this package can convert: a linear mapping –
identity,scale,translation,rotation,affine,mapAxis,byDimension,bijection, or asequenceof them – or adisplacementsorcoordinatestransformation whose field is passed infields.A field transform is the identity plus a displacement, and the region it reports is the grid’s own image moved and widened by the range that displacement takes. The range is read from the field one chunk at a time, so a field larger than memory is never held; a field that shifts every point the same way moves the region rather than widening it.
An ITK transform is measured by walking the boundary of the transformed grid, which reports a linear map exactly. A
DisplacementFieldorBSplinestage would be missed where it acts strictly inside the grid, so such a stage is bounded the way a field is: the walk measures the list with those stages replaced by the identity, and the range their values can add, carried through the stages applied after them, widens the result. A stage sitting outside another one carries the range reaching it and adds its own on top, so several of them compose. Other non-linear parameterizations, the velocity fields among them, still rely on the walk alone and can under-report a strictly interior excursion.In both cases the transform maps fixed points into moving space.
- Parameters:
transform – An RFC-5 coordinate transformation, an
itk.Transform, or an ITK-WasmTransform/TransformList.fixed (NgffImage) – The image whose grid is resampled. Geometry only.
moving (NgffImage) – The image to be sampled. Geometry only.
padding (int) – Pixels of padding added per side. The default of 1 covers linear interpolation, which reads one neighbor beyond the continuous index bound. Use 0 for the tight region, or more for wider kernels.
fields (Mapping[str, NgffImage | NgffMultiscales], optional) –
The field images an RFC-5
displacementsorcoordinatestransformation points at, keyed by itspath, as- func:
ngff_zarr.ngff_transform_to_itk_transformtakes them. Required for those two, ignored otherwise. A field carrying an anatomical orientation is refused here, since this branch works on the intrinsic systems where none applies: convert it with- func:
ngff_zarr.ngff_transform_to_itk_transform, passingfixedandmoving, and pass the ITK transform it returns.
- Returns:
The region, keyed by dimension name in Zarr order.
- Return type:
- Raises:
ValueError – If
fixedandmovingdo not share the same spatial dimensions, if there are not 2, 3 or 4 of them, if a scale or translation entry is missing, zero or non-finite, ifpaddingis negative, if the transform couples spatial and non-spatial axes, or if the region spans more than the index range the pipeline can represent.NotImplementedError – If the RFC-5 transformation is not linear.