confusius.multipose¶
multipose ¶
Multi-pose data processing utilities.
This module provides functions for processing multi-pose fUSI data, including consolidating multiple poses into a single volume, slice timing correction, and other multi-pose specific operations.
Modules:
-
consolidate–Multi-pose volume consolidation.
-
slice_timing–Slice timing correction for multi-pose fUSI data.
Functions:
-
consolidate_poses–Merge
poseandsweep_dimdimensions into a single axis ordered by position. -
correct_slice_timings–Resample each sweep position to the volume's reference time.
consolidate_poses ¶
consolidate_poses(
da: DataArray,
affines_key: str = "physical_to_lab",
sweep_dim: str = "z",
rtol: float = 0.01,
) -> DataArray
Merge pose and sweep_dim dimensions into a single axis ordered by position.
For each (pose, sweep_dim) voxel, the position is computed using the sweep-dim
column and translation of the per-pose affine (other spatial dims are zero at voxel
centres along the sweep):
where sweep_col is the column index of sweep_dim in the spatial dim ordering
(z, y, x) → columns (0, 1, 2).
The primary sweep direction is found via singular value decomposition of all positions. Each voxel is projected onto that axis, the positions are checked for regularity, then the data is reindexed in ascending order along the consolidated sweep axis.
This function is primarily intended for consolidating multi-pose fUSI volumes
acquired with an Iconeus system using a purely translational probe sweep. In that
workflow, each pose corresponds to one probe position along the elevation axis
(z), and the DataArray is produced by load_scan:
scan_3d = load_scan("recording.scan") # dims: (pose, z, y, x)
volume = consolidate_poses(scan_3d) # dims: (z, y, x)
scan_4d = load_scan("recording_4d.scan") # dims: (time, pose, z, y, x)
volume = consolidate_poses(scan_4d) # dims: (time, z, y, x)
The function also works on any DataArray that carries a (npose, 4, 4) affine stack
in da.attrs["affines"][affines_key] with columns ordered as (z, y, x,
translation). The sweep_dim parameter selects which spatial dimension is being
swept across poses. For example, a stack of NIfTI DataArrays concatenated along a
new pose dimension with their physical_to_qform affines stacked accordingly.
Parameters:
-
(da¶DataArray) –DataArray with a
posedimension and a(npose, 4, 4)affine stack stored inda.attrs["affines"][affines_key]. Typically produced byload_scanfor3Dscanor4Dscanfiles. -
(affines_key¶str, default:"physical_to_lab") –Key into
da.attrs["affines"]that holds the(npose, 4, 4)affine stack. Column order must be(z, y, x, translation). -
(sweep_dim¶str, default:"z") –Name of the spatial dimension being swept across poses. Must be one of the spatial dimensions in
da.dims. The column index in the affine is determined by the order of spatial dimensions in the DataArray (e.g., if spatial dims are["z", "y", "x"], then"z"→ column 0,"y"→ column 1,"x"→ column 2). -
(rtol¶float, default:0.01) –Relative tolerance for the regularity check (fraction of mean spacing).
Returns:
-
DataArray–DataArray with
posemerged intosweep_dim, sorted by physical position. The consolidatedsweep_dimcoordinate holds the projection of each voxel's physical position onto the sweep axis, expressed in the same units as the inputsweep_dimcoordinate. For inputs that carry apose_timecoordinate, a consolidatedslice_timewith dims("time", sweep_dim)is included: each slice inherits the timestamp of the pose it came from.
Raises:
-
ValueError–If
dahas noposedimension, ifsweep_dimis not one of the spatial dimensions inda.dims, if the rotation block of the affine is not constant across poses (non-translation sweep), or if the consolidated positions are not regularly spaced withinrtol.
Warns:
-
UserWarning–If the sweep is not purely 1D (secondary/primary singular value ratio > 0.01).
correct_slice_timings ¶
correct_slice_timings(
da: DataArray,
method: Literal[
"linear",
"nearest",
"nearest-up",
"zero",
"slinear",
"quadratic",
"cubic",
"previous",
"next",
] = "linear",
fill_value: float
| tuple[float, float]
| Literal["extrapolate", "nan"] = "extrapolate",
) -> DataArray
Resample each sweep position to the volume's reference time.
In multi-pose fUSI acquisitions, each sweep position is acquired at a different time
within the volume period. This function resamples each position's time series so
that all positions appear to have been acquired at the time stored in the time
coordinate.
This function works on both:
- Consolidated data: dims
(time, <sweep_dim>, ...)with aslice_timecoordinate with dims(time, <sweep_dim>), typically produced byconsolidate_poses. - Unconsolidated data: dims
(time, pose, ...)with apose_timecoordinate with dims(time, pose).
The sweep dimension is inferred from the second dim of whichever timing coordinate is present.
If the input is Dask-backed, the function stays lazy: computation is deferred until
.compute() is called. The time dimension must not be chunked; spatial dimensions
may be freely chunked.
Parameters:
-
(da¶DataArray) –DataArray with a
slice_timeorpose_timecoordinate whose dims are(time, <sweep_dim>). -
(method¶(linear, nearest, nearest - up, zero, slinear, quadratic, cubic, previous, next), default:"linear") –Interpolation method passed to
scipy.interpolate.interp1d:"linear": linear interpolation."nearest": nearest-neighbour interpolation; rounds down at half-integers."nearest-up": nearest-neighbour interpolation; rounds up at half-integers."zero": zeroth-order spline (step function)."slinear": first-order spline."quadratic": second-order spline."cubic": third-order spline."previous": use previous point's value."next": use next point's value.
-
(fill_value¶float or tuple[float, float] or {extrapolate, nan}, default:"extrapolate") –How to handle target times that fall outside the range of a position's acquisition times.
"extrapolate"allows linear extrapolation."nan"inserts NaNs out of bounds. Use a float for a constant fill value, or a tuple(left, right)for different values on each side.
Returns:
-
DataArray–New DataArray with the same dims and coordinates as the input, but with each position's time series resampled to
da.coords["time"].values. The timing coordinate (slice_timeorpose_time) is dropped to avoid accidental double-correction.
Raises:
-
ValueError–If
dahas notimedimension or only one time point, if neitherslice_timenorpose_timecoordinate is present, if the timing coordinate does not have dims(time, <sweep_dim>), or if thetimedimension is chunked.
Warns:
-
UserWarning–If a spline method fails due to too few points and falls back to
"linear".