Mask-to-smooth-contour post-processing
This package converts an existing binary segmentation mask into a smooth, closed contour. It is independent of the model that produced the mask and is always an explicit post-processing step: segmentors continue to return their raw masks and dense contours unchanged.
from cardiac_toolkit.segmentation import (
LAX_EDITABLE_CONTOUR_V1,
mask_to_smooth_contour,
)
raw = segmentor.segment(frame).masks["LV_endo"]
result = mask_to_smooth_contour(
raw,
pixel_spacing=(row_mm, column_mm),
config=LAX_EDITABLE_CONTOUR_V1,
)
editable_xy = result.control_points
smooth_xy = result.smooth_contour
quality = result.metrics.as_dict()
All output coordinates are [x, y] pixel coordinates. Pixel spacing is
(row_mm, column_mm). Anchor selection, curve rendering, and distance metrics
are performed in physical millimetres before results are mapped back to the
input pixel grid.
mask_to_smooth_contour keeps only the largest external component of a filled
mask. When a model returns separate cavity and myocardium classes, use
cavity_myocardium_masks_to_smooth_contours; it keeps the cavity boundary as
the endocardium and uses the largest external boundary of their union as the
epicardium. For a mask that is already a true myocardium ring, use
myocardium_mask_to_smooth_contours.
The versioned presets are:
LAX_EDITABLE_CONTOUR_V1: 8–10 editable anchors, 1.5 mm tolerance, 4 mm minimum spacing, and 0.95 tension.SAX_MYOCARDIUM_BSPLINE_V1: the historical periodic B-spline with smoothing parameter 40 and 100 rendered points, followed by curvature-based selection of 6, 8, or 10 editable controls at the 0.985 IoU target.
The LAX preset is the recovered July standalone implementation expressed in physical units. The SAX preset preserves the January historical setting. Neither is silently retuned at runtime.
See ../examples/mask_to_smooth_contour/ for anonymized qualitative examples.
Those images have no manual ground-truth contours and should not be interpreted
as performance validation.