Skip to main content

morphometry

Voxel geometry for en-face and volumetric biomarker measurement.

One home for the scale maths, so the conversion between cell counts and physical units cannot drift between biomarkers. Every function here is pure and takes its physical scale explicitly; nothing reads a config or a cache.

The en-face plane is (b-scan, column): the axial axis collapses under projection, so an area needs only slice_thickness and pixel_spacing_column, while a volume needs all three spacings.

Module

Functions

en_face_area_mm2

def en_face_area_mm2(cell_count: int, scale: VoxelScale)> float:

Convert an en-face cell count to mm².

The product is evaluated left to right and must not be parenthesised. Floating-point multiplication is not associative, and every other area site in this codebase groups left to right; grouping the two spacings first differs in the final bits and breaks the exact cross-check against the GA path. test_area_is_not_parenthesised fails on the parenthesised form, so this is enforced rather than merely requested.

Arguments

  • cell_count: Number of set cells on the (b-scan, column) plane.
  • scale: The cube's voxel dimensions.

Returns The area in mm².

en_face_distance_grid

def en_face_distance_grid(    num_bscans: int, num_cols: int, reference: tuple[float, float], scale: VoxelScale,)> numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]]:

Euclidean distance in mm from a reference point, per en-face cell.

Built the way the CST step builds its circular sampling region: mm coordinate grids on both en-face axes, then Euclidean distance. Working in mm rather than index space matters because the two axes are sampled about twenty times apart — an index-space radius would be a skewed ellipse.

Arguments

  • num_bscans: Size of the B-scan axis.
  • num_cols: Size of the column axis.
  • reference: (bscan, column) reference point, in index space.
  • scale: The cube's voxel dimensions.

Returns A (num_bscans, num_cols) array of distances in mm.

has_en_face_scale

def has_en_face_scale(scale: VoxelScale)> bool:

Whether the two en-face axes are usable, so an area can be measured.

Arguments

  • scale: The cube's voxel dimensions.

Returns True when slice_thickness_mm and pixel_spacing_column_mm are both finite and positive. The axial spacing is not consulted.

has_voxel_scale

def has_voxel_scale(scale: VoxelScale)> bool:

Whether all three axes are usable, so a volume can be measured.

Arguments

  • scale: The cube's voxel dimensions.

Returns True when every spacing is finite and positive.

label_en_face

def label_en_face(    mask: NDArray[Any],)> tuple[numpy.ndarray[typing.Any, numpy.dtype[numpy.int32]], int]:

Label 4-connected components on an en-face (b-scan, column) mask.

Matches the GA path's lesion identity, so a lesion count means the same thing in both.

Arguments

  • mask: A 2-D (num_bscans, num_cols) mask, non-zero where present.

Returns (labelled, count) — an int array of component ids (0 is background) and the number of components found.

label_volume

def label_volume(    mask: NDArray[Any],)> tuple[numpy.ndarray[typing.Any, numpy.dtype[numpy.int32]], int]:

Label 6-connected components on a 3-D (b-scan, row, column) mask.

For biomarkers where axial separation means separate lesions — two fluid cysts stacked in the same column are two cysts. Drusen sit in a thin band above Bruch's membrane and do not have that failure mode, which is why the en-face labeller is the default.

Arguments

  • mask: A 3-D mask, non-zero where present.

Returns (labelled, count).

volume_mm3

def volume_mm3(voxel_count: int, scale: VoxelScale)> float:

Convert a 3-D voxel count to mm³.

Left-to-right for the same reason as en_face_area_mm2.

Arguments

  • voxel_count: Number of set voxels.
  • scale: The cube's voxel dimensions.

Returns The volume in mm³.

volume_nl

def volume_nl(voxel_count: int, scale: VoxelScale)> float:

Convert a 3-D voxel count to nanolitres.

Arguments

  • voxel_count: Number of set voxels.
  • scale: The cube's voxel dimensions.

Returns The volume in nL, the unit fluid volumes are already reported in.

Classes

VoxelScale

class VoxelScale(    slice_thickness_mm: float,    pixel_spacing_row_mm: float,    pixel_spacing_column_mm: float,):

Physical voxel dimensions for one OCT cube, in millimetres.

Attributes

  • slice_thickness_mm: Gap between B-scans — the en-face vertical axis.
  • pixel_spacing_row_mm: Axial spacing. Collapses under en-face projection, so it is needed for volume but not for area.
  • pixel_spacing_column_mm: Lateral spacing — the en-face horizontal axis.

Variables

  • static pixel_spacing_column_mm : float
  • static pixel_spacing_row_mm : float
  • static slice_thickness_mm : float