Segmentation Base Class
SegmentAnatomyBase defines the shared chest-anatomy segmentation contract
used by PhysioTwin4D 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 physiotwin4d.SegmentAnatomyBase(log_level=20)[source]
Bases:
PhysioTwin4DBaseBase 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 inphysiotwin4d.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.- fast_mode
When True, subclasses may skip auxiliary model passes and use faster/less-accurate models to trade segmentation fidelity for speed (e.g. in automated tests). Defaults to False.
- Type:
- taxonomy
Group→organ mapping shared with
physiotwin4d.USDAnatomyTools.- Type:
- __init__(log_level=20)[source]
Initialize the SegmentAnatomyBase class.
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
physiotwin4d.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)
- postprocess_after_labelmap(input_image, labelmap_image)[source]
Hook for subclass-specific labelmap refinement before mask creation.
Called by
segment()afterpostprocess_labelmap(), and before the per-group masks are derived from the labelmap. The base implementation is a no-op; subclasses that offer optional features gated behind their own settings (e.g. TotalSegmentator’s contrast-enhanced-study detection) override this to apply them.- Parameters:
input_image (itk.image) – The original, unpreprocessed input image
labelmap_image (itk.image) – The postprocessed segmentation labelmap
- Returns:
The labelmap to use for mask creation
- Return type:
itk.image
- 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.
- segment(input_image)[source]
Perform complete anatomy segmentation.
This is the main segmentation method that coordinates preprocessing, segmentation, subclass-specific labelmap refinement, and anatomical group mask creation.
- Parameters:
input_image (itk.image) – The input 3D image to segment
- Returns:
- Dictionary containing:
”labelmap”: Detailed segmentation labelmap
one binary mask image per anatomy group, keyed by group name
- Return type:
Example
>>> result = segmenter.segment(image) >>> 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 physiotwin4d import SegmentChestTotalSegmentator
image = itk.imread("chest_ct.nrrd")
segmenter = SegmentChestTotalSegmentator()
masks = segmenter.segment(image)
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 physiotwin4d.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 physiotwin4d.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 keepsphysiotwin4d.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
physiotwin4d.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 anotherPhysioTwin4DBasesubclass 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. Real-data tests pull data through the session
fixtures (downloaded on first use); mark GPU- or Simpleware-bound tests with
requires_gpu / requires_simpleware so they fall into the right
opt-in bucket.
See Also
USD Anatomy Tools for the renderer side of the taxonomy