Segmentation Base Class
SegmentAnatomyBase defines the shared chest-anatomy segmentation contract
used by PhysioMotion4D segmentation implementations. It owns an
AnatomyTaxonomy instance that subclasses populate to declare which
anatomy groups (and which organ labels within each group) they produce.
Class Reference
- class physiomotion4d.SegmentAnatomyBase(log_level=20)[source]
Bases:
PhysioMotion4DBaseBase class for anatomy segmentation that provides common functionality for segmenting anatomy in CT images.
This class implements preprocessing, postprocessing, and mask creation methods that are shared across different anatomy segmentation implementations. It owns an
AnatomyTaxonomyinstance that captures the group→organ structure (e.g.heartcontainsatrial_appendage_leftat id 61); subclasses populate it viaself.taxonomy.add_organ(...)and call_finalize_other_group()once they’re done.Extensibility
Each segmenter is free to define its own group names — the taxonomy does not hard-code a fixed set. A new subclass adds groups by calling
self.taxonomy.add_organ(group_name, label_id, organ_name)for each organ; the group is created lazily on first use. To assign a custom OmniSurface look to a new group, register it inphysiomotion4d.usd_anatomy_tools.DEFAULT_RENDER_PARAMS(see that module’s docstring). Groups without a registered look fall back to the"other"entry, so they still render.- taxonomy
Group→organ mapping shared with
physiomotion4d.USDAnatomyTools.- Type:
- __init__(log_level=20)[source]
Initialize the SegmentAnatomyBase class.
Sets up default parameters for image preprocessing and seeds the anatomy taxonomy with the two base-class default organs (contrast and soft_tissue). Subclasses should:
Add their organ groups via
self.taxonomy.add_organ(...).Call
_finalize_other_group()to fill in unclaimed ids.
- label_to_type(label_name)[source]
Return the anatomy group (‘heart’, ‘lung’, etc.) for a label name.
Used by
physiomotion4d.ConvertVTKToUSDto group label-mode mesh prims under per-type Xforms (e.g./World/{basename}/heart/{label_name}). Delegates to the taxonomy.
- set_target_spacing(target_spacing)[source]
Set the target isotropic spacing for image resampling.
- Parameters:
target_spacing (float) – Target spacing in millimeters for all three spatial dimensions. Set to 0.0 to disable resampling.
- Return type:
Example
>>> segmenter.set_target_spacing(1.0) # 1mm isotropic spacing
- preprocess_input(input_image)[source]
Preprocess the input image for segmentation.
Performs image preprocessing including resampling to isotropic spacing and optional intensity rescaling. The preprocessing ensures consistent image characteristics for reliable segmentation.
- Parameters:
input_image (itk.image) – The input 3D CT image to preprocess
- Returns:
- The preprocessed image with isotropic spacing and
optionally rescaled intensities
- Return type:
itk.image
- Raises:
AssertionError – If the input image is not 3D
ValueError – If intensity rescaling parameters are invalid
Example
>>> preprocessed = segmenter.preprocess_input(ct_image)
- postprocess_labelmap(labelmap_image, input_image)[source]
Resample the labelmap to match the input image spacing.
Ensures the segmentation labelmap has the same spatial properties as the original input image by resampling using label-specific interpolation that preserves discrete label values.
- Parameters:
labelmap_image (itk.image) – The segmentation labelmap to resample
input_image (itk.image) – The original input image providing target spacing and geometry
- Returns:
The resampled labelmap matching input image properties
- Return type:
itk.image
Example
>>> final_labels = segmenter.postprocess_labelmap(labels, original_image)
- segment_connected_component(preprocessed_image, labelmap_image, lower_threshold, upper_threshold, labelmap_ids=None, mask_id=0, use_mid_slice=True, hole_fill=2)[source]
Segment connected components based on intensity thresholding.
Identifies connected regions within intensity thresholds and existing anatomical masks, then selects the largest component. This is useful for segmenting structures like contrast-enhanced blood or specific tissue types.
- Parameters:
preprocessed_image (itk.image) – The preprocessed input image
labelmap_image (itk.image) – Existing labelmap to constrain search
lower_threshold (int) – Lower intensity threshold
upper_threshold (int) – Upper intensity threshold
labelmap_ids (None | list[int]) – List of label IDs to search within. If None, searches within all existing labels
mask_id (int) – ID to assign to the segmented component
use_mid_slice (bool) – If True, find largest component in middle slice only; if False, use entire 3D volume
hole_fill (int) – Number of pixels to dilate/erode for hole filling
- Returns:
Updated labelmap with new component labeled as mask_id
- Return type:
itk.image
Example
>>> # Segment contrast-enhanced blood >>> updated_labels = segmenter.segment_connected_component( ... preprocessed_image, labels, 700, 4000, mask_id=135 ... )
- segment_contrast_agent(preprocessed_image, labelmap_image)[source]
Include contrast-enhanced blood in the labelmap.
Segments high-intensity regions corresponding to contrast-enhanced blood vessels and cardiac chambers. Uses connected component analysis focused on the middle slice where the heart is typically located.
- Parameters:
preprocessed_image (itk.image) – The preprocessed CT image
labelmap_image (itk.image) – Existing segmentation labelmap
- Returns:
Updated labelmap with contrast-enhanced regions labeled
- Return type:
itk.image
Note
Assumes the mid-z slice of the data contains the heart.
Example
>>> contrast_labels = segmenter.segment_contrast_agent(preprocessed_image, base_labels)
- create_anatomy_group_masks(labelmap_image)[source]
Create binary masks for different anatomical groups from the labelmap.
Generates separate binary masks for major anatomical systems by grouping related anatomical structures from the detailed labelmap. This is useful for motion analysis and visualization.
- Parameters:
labelmap_image (itk.image) – The detailed segmentation labelmap
- Returns:
- Dictionary of binary masks keyed by group
name. Exactly one entry per group registered in
taxonomy(plus"other"). The returned key set is segmenter-specific — callers that need a particular group should check membership ("lung" in masks) rather than assume a fixed schema.
- Return type:
Example
>>> masks = segmenter.create_anatomy_group_masks(labelmap) >>> if "lung" in masks: ... lung_mask = masks["lung"]
- segmentation_method(preprocessed_image)[source]
Abstract method for image segmentation - must be implemented by subclasses.
This method should contain the core segmentation algorithm specific to each implementation (e.g., TotalSegmentator).
- Parameters:
preprocessed_image (itk.image) – The preprocessed input image
- Returns:
The segmentation labelmap
- Return type:
itk.image
- Raises:
NotImplementedError – If called on the base class
Note
This method must be implemented by subclasses to provide the specific segmentation algorithm.
- dilate_mask(mask, dilation)[source]
Dilate a binary mask using morphological operations.
Expands the mask regions by the specified number of pixels to create larger regions of interest. Useful for creating candidate regions or ensuring complete coverage of anatomical structures.
- Parameters:
mask (itk.image) – The binary mask to dilate
dilation (int) – Number of pixels to dilate in each direction
- Returns:
The dilated binary mask
- Return type:
itk.image
Example
>>> dilated_heart = segmenter.dilate_mask(heart_mask, 5)
- segment(input_image, contrast_enhanced_study=False)[source]
Perform complete chest CT segmentation.
This is the main segmentation method that coordinates preprocessing, segmentation, contrast agent detection (if applicable), postprocessing, and anatomical group mask creation.
- Parameters:
input_image (itk.image) – The input 3D CT image to segment
contrast_enhanced_study (bool) – Whether the study uses contrast enhancement. If True, performs additional contrast agent segmentation to identify enhanced blood vessels
- Returns:
- Dictionary containing:
”labelmap”: Detailed segmentation labelmap
”lung”: Binary mask of pulmonary structures
”heart”: Binary mask of cardiac structures
”major_vessels”: Binary mask of major blood vessels
”bone”: Binary mask of skeletal structures
”soft_tissue”: Binary mask of soft tissue organs
”other”: Binary mask of remaining structures
”contrast”: Binary mask of contrast-enhanced regions
- Return type:
Example
>>> result = segmenter.segment(ct_image, contrast_enhanced_study=True) >>> labelmap = result['labelmap'] >>> heart_mask = result['heart']
Segmentation Contract
Concrete segmenters accept an ITK image and return a dictionary of ITK images:
import itk
from physiomotion4d import SegmentChestTotalSegmentator
image = itk.imread("chest_ct.nrrd")
segmenter = SegmentChestTotalSegmentator()
masks = segmenter.segment(image, contrast_enhanced_study=True)
labelmap = masks["labelmap"]
if "heart" in masks:
heart = masks["heart"]
The returned dictionary always contains "labelmap" plus one entry per
anatomy group the segmenter registered in its taxonomy (and "other" for
unclassified labels). The exact key set is segmenter-specific — callers
must check membership ("lung" in masks) rather than assume a fixed
schema. For example, SegmentChestTotalSegmentator returns the full
heart, lung, bone, major_vessels, soft_tissue, contrast, other set,
while SegmentHeartSimpleware returns only the groups its
ASCardio module actually populates (heart, major_vessels,
soft_tissue, contrast, other).
Anatomy Taxonomy
The group-to-organ mapping is held by AnatomyTaxonomy, a small
data class shared between the segmenter and downstream renderers
(USDAnatomyTools, ConvertVTKToUSD). It is independent of
ITK and OpenUSD so segmentation code can be reasoned about without pulling
in the rendering stack.
- class physiomotion4d.AnatomyGroup(name, organs=<factory>)[source]
One named anatomy group together with the organ labels it contains.
- name
Group name (e.g.
"heart","lung").
- organs
Maps integer label id to organ name within this group.
- __init__(name, organs=<factory>)
- class physiomotion4d.AnatomyTaxonomy[source]
Mapping of anatomical groups to the organs each group contains.
Groups are added in insertion order, which determines the order returned by
group_names()and the iteration order ofall_labels().Example
>>> tax = AnatomyTaxonomy() >>> tax.add_organ("heart", 51, "heart") >>> tax.add_organ("heart", 61, "atrial_appendage_left") >>> tax.add_organ("lung", 10, "lung_upper_lobe_left") >>> tax.group_for_label("atrial_appendage_left") 'heart' >>> tax.labels_in_group("lung") {10: 'lung_upper_lobe_left'}
- OTHER_GROUP = 'other'
Sentinel group name used by
fill_other_group()for unclaimed ids.
- add_group(name)[source]
Ensure a group exists and return it.
- Parameters:
name (
str) – Group name.- Return type:
- Returns:
The (new or existing)
AnatomyGroup.
- add_organ(group, label_id, organ_name)[source]
Add one organ label to the named group.
Creates the group if it does not yet exist. Reassigning the same label id within the same group is silently allowed (last write wins). If the same id is already registered in a different group, a warning is logged and the new assignment is dropped, so the first registration wins and
group_for_id()remains deterministic.
- group_for_label(label_name)[source]
Return the group containing label_name.
Falls back to
OTHER_GROUPif no group contains the name; this keepsphysiomotion4d.ConvertVTKToUSDhappy when it encounters labels the segmenter did not classify.- Return type:
- group_for_id(label_id)[source]
Return the group containing label_id;
OTHER_GROUPif absent.- Return type:
- fill_other_group(id_range=range(1, 256), name_template='other_{id}')[source]
Populate the
othergroup with any ids not already claimed.Called by
physiomotion4d.SegmentAnatomyBasesubclasses at the end of__init__to mark every id in the segmenter’s class index space that no specific group claimed.
Typical usage from a subclass __init__:
class SegmentMySite(SegmentAnatomyBase):
def __init__(self):
super().__init__()
self.taxonomy.add_organ("heart", 51, "myocardium")
self.taxonomy.add_organ("heart", 61, "atrial_appendage_left")
self.taxonomy.add_organ("lung", 10, "lung_upper_lobe_left")
# ...
self._finalize_other_group()
Downstream callers can introspect what a segmenter produces without running it:
tax = segmenter.taxonomy
print(tax.group_names()) # ['heart', 'lung', ...]
print(tax.labels_in_group("heart")) # {51: 'myocardium', ...}
print(tax.group_for_label("myocardium")) # 'heart'
print(tax.all_labels()) # full id -> name dict
Extending Segmentation
New runtime segmentation classes should:
Inherit from
SegmentAnatomyBase(or anotherPhysioMotion4DBasesubclass if no anatomy taxonomy is needed).Populate
self.taxonomywithadd_organcalls in__init__.Call
self._finalize_other_group()once all groups have been registered.Use
log_info()/log_debug()instead ofprint.Document the key set the segmenter produces; downstream callers should check membership rather than assume a fixed schema.
Keep synthetic tests small and mark real-data tests with requires_data.
See Also
USD Anatomy Tools for the renderer side of the taxonomy