Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

utils.partial_volume

utils.partial_volume

Fractional occupancy of a high-resolution mask on a coarse (MRSI) voxel grid.

Answers one question: what fraction of this spectroscopic voxel is covered by that segmentation? -- when the two images differ in field of view, position, voxel size and obliquity, and are related only by their affines.

The mask is integrated over each target voxel by forward binning: every mask voxel is pushed through T = inv(target_affine) @ mask_affine and accumulated into the target voxel it lands in. Because the mask grid is typically far finer than the target grid (a 0.5-1 mm segmentation against 10-15 mm MRSI voxels), the mask grid is the sample grid -- forward binning reaches the dense-sampling limit at a cost that scales with the mask, not with a sampling parameter, and only ever moves mass between bins.

Occupancy here is nominal box overlap: the target voxel is the parallelepiped its affine defines. The MRSI spatial response function is deliberately not modelled -- see docs/diary/2026-08-24-mask-partial-volume.md.

Functions

NameDescription
mask_occupancyCompute what fraction of each target voxel is covered by mask.
target_halfwidthsHalf-extent of one mask voxel along each target axis, in target-index units.

mask_occupancy

utils.partial_volume.mask_occupancy(
    mask,
    target,
    *,
    mask_affine=None,
    target_affine=None,
    target_shape=None,
    labels=None,
    min_coverage=0.9,
    _force_general_path=False,
)

Compute what fraction of each target voxel is covered by mask.

Parameters

NameTypeDescriptionDefault
maskndarray, spatialimages.SpatialImage, or GESeries.NiiBaseThe high-resolution mask. A boolean or float array is treated as a binary mask or probability map and produces one occupancy map; an integer array holding more than one non-zero value is treated as a label volume and produces one map per label. If a plain array, mask_affine must be given.required
targetndarray, spatialimages.SpatialImage, or GESeries.NiiBaseThe coarse grid to map onto, typically MRSISeries.RAW_exp. Only its affine and shape are used; its data is never read.required
mask_affinendarrayThe mask’s affine. Required if mask is a plain array.None
target_affinendarrayThe target affine. Overrides the one carried by target.None
target_shapetuple[int, int, int]The target grid shape. Overrides the one carried by target.None
labelssequence[int] or mapping[int, str]Explicit labels to compute, overriding auto-detection. A mapping additionally names them: {1: "gm", 2: "wm"} yields a label coordinate of ["gm", "wm"].None
min_coveragefloatOccupancy is set to NaN in target voxels whose coverage falls below this fraction, since an occupancy computed over a sliver of a voxel is not comparable to one computed over a whole voxel. Defaults to 0.9; pass 0.0 to disable the masking.0.9
_force_general_pathboolInternal test hook: skip the axis-aligned fast path even when mask -> target is diagonal, so both code paths can be checked against each other. Not part of the public API.False

Returns

NameTypeDescription
xarray.Datasetoccupancy with dims (label, i, j, k) and coverage with dims (i, j, k), both in [0, 1]. Voxel-centre world coordinates are attached as 3-D non-dimension coordinates x, y, z; the affines and settings are in .attrs.

Notes

occupancy is masked by min_coverage but coverage never is, so a voxel that reads NaN can always be explained by looking at the same voxel in coverage. Without both, “no mask here” and “no anatomy here” are indistinguishable.

Examples

>>> pv = mask_occupancy(seg_nii, mrsi.RAW_exp)
>>> pv.occupancy.sel(label="mask")

target_halfwidths

utils.partial_volume.target_halfwidths(tgt_from_mask)

Half-extent of one mask voxel along each target axis, in target-index units.

A mask voxel spans +/-0.5 in each of its own index directions, so its image under the linear part of tgt_from_mask is a parallelepiped whose half-extent along target axis a is 0.5 * sum_b |T[a, b]|. For axis-aligned grids that reduces to half the voxel size ratio; the sum is what keeps it right when the grids are oblique.