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¶
| Name | Description |
|---|---|
| mask_occupancy | Compute what fraction of each target voxel is covered by mask. |
| target_halfwidths | Half-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¶
| Name | Type | Description | Default |
|---|---|---|---|
| mask | ndarray, spatialimages.SpatialImage, or GESeries.NiiBase | The 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 |
| target | ndarray, spatialimages.SpatialImage, or GESeries.NiiBase | The coarse grid to map onto, typically MRSISeries.RAW_exp. Only its affine and shape are used; its data is never read. | required |
| mask_affine | ndarray | The mask’s affine. Required if mask is a plain array. | None |
| target_affine | ndarray | The target affine. Overrides the one carried by target. | None |
| target_shape | tuple[int, int, int] | The target grid shape. Overrides the one carried by target. | None |
| labels | sequence[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_coverage | float | Occupancy 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_path | bool | Internal 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¶
| Name | Type | Description |
|---|---|---|
| xarray.Dataset | occupancy 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.