Skip to content

API Reference: Label Diagnostic and Remapping Tools

Overview

The label tools help users whose segmentations have different label values than library schematics expect. This commonly occurs when: - Slicer resets labels to sequential 1-N after manual corrections - Different clinical sites use different labeling conventions - Converting between segmentation tools with different standards

The tools provide: 1. Diagnostic checking - Compare your image labels to schematic expectations 2. Auto-suggestion - Detect sequential patterns and suggest mappings 3. Custom scaffolding - Generate configs with your actual label values


Quick Start

from pycemrg_image_analysis.utilities.label_tools import check_labels

# One-line diagnostic
report = check_labels(Path("my_seg.nrrd"), "lpv1_ring")

# If issues found, create custom mapping
from pycemrg_image_analysis import ImageAnalysisScaffolder

my_mapping = {
    "LPV1_label": 1,        # Your actual value
    "LA_myo_label": 5,      # Your actual value
    "LPV1_ring_label": 50,  # Target output value
}

scaffolder = ImageAnalysisScaffolder()
scaffolder.scaffold_components_with_mapping(
    output_dir=Path("config/"),
    component_names=["lpv1_ring"],
    label_mapping=my_mapping,
)

Classes

LabelDiagnostic

Check if image labels match schematic expectations.

check_image_against_schematic(image_path, schematic_name) -> DiagnosticReport

Parameters: - image_path: Path to segmentation image - schematic_name: Schematic to check (e.g., "lpv1_ring", "biventricular_basic")

Returns: DiagnosticReport with detailed mismatch information

Example:

from pycemrg_image_analysis.utilities.label_tools import LabelDiagnostic

diagnostic = LabelDiagnostic()
report = diagnostic.check_image_against_schematic(
    Path("seg_from_slicer.nrrd"),
    "lpv1_ring"
)

if report.has_issues:
    print(f"Missing: {len(report.missing_labels)} labels")
    for m in report.missing_labels:
        print(f"  {m.label_name}: expected {m.expected_value}")

Print human-readable diagnostic report.

Example:

diagnostic.print_report(report)

Output:

======================================================================
Label Diagnostic Report
======================================================================
Image: seg_from_slicer.nrrd
Schematic: lpv1_ring

Image contains 2 unique labels (excluding 0):
  [1, 5]

Schematic expects 3 labels:

❌ ISSUES FOUND:

Missing labels (2):
  • LPV1_label: expected 8, NOT FOUND in image
  • LA_myo_label: expected 104, NOT FOUND in image

💡 SUGGESTION:
   Your image has different label values than the schematic expects.
   Use LabelRemapper to create a custom mapping...


DiagnosticReport

Results of diagnostic check.

Attributes: - image_path: Path to checked image - schematic_name: Schematic checked against - image_labels: Set of labels found in image - expected_labels: Dict of expected {label_name: value} - mismatches: List of LabelMismatch objects

Properties: - has_issues: True if any labels missing or conflicted - missing_labels: Labels expected but not found - ok_labels: Labels that match expectations

Example:

if report.has_issues:
    for mismatch in report.missing_labels:
        print(f"{mismatch.label_name} missing")


LabelRemapper

Create custom label mappings for non-standard label schemes.

create_mapping_from_dict(label_mapping) -> Dict[str, int]

Validate and return a label mapping.

Parameters: - label_mapping: Dict mapping label names to integers

Returns: Validated mapping dictionary

Raises: ValueError if values are not integers or negative

Example:

from pycemrg_image_analysis.utilities.label_tools import LabelRemapper

remapper = LabelRemapper()
mapping = remapper.create_mapping_from_dict({
    "LPV1_label": 1,
    "LA_myo_label": 5,
    "LPV1_ring_label": 50,
})

suggest_mapping_from_report(report) -> Optional[Dict[str, int]]

Auto-suggest mapping for sequential labels (Slicer reset pattern).

Parameters: - report: DiagnosticReport from check

Returns: Suggested mapping dict, or None if no clear pattern

Example:

suggestion = remapper.suggest_mapping_from_report(report)

if suggestion:
    print("Auto-detected mapping:")
    for name, value in suggestion.items():
        print(f"  {name}: {value}")
else:
    print("Cannot auto-detect - manual mapping needed")

Auto-detection criteria: - Image labels must be sequential (1, 2, 3, ...) - Count must match expected label count - If both conditions met, maps sequentially to schematic label names


Scaffolder Extension

ImageAnalysisScaffolder.scaffold_components_with_mapping()

Generate configs using custom label values instead of template defaults.

Parameters: - output_dir: Where to save configs - component_names: List of schematics (e.g., ["lpv1_ring"]) - label_mapping: Dict of {label_name: your_integer} - overwrite: Overwrite existing files (default False)

Example:

from pycemrg_image_analysis import ImageAnalysisScaffolder

# After Slicer reset labels to 1, 2, 3...
my_labels = {
    "LPV1_label": 1,
    "LA_myo_label": 2,
    "LPV1_ring_label": 50,
}

scaffolder = ImageAnalysisScaffolder()
scaffolder.scaffold_components_with_mapping(
    output_dir=Path("config/"),
    component_names=["lpv1_ring"],
    label_mapping=my_labels,
)

Result: - config/labels.yaml contains 1, 2, 50 (not template values 8, 104, 208) - config/semantic_maps/lpv1_ring.json unchanged (uses label names, not values) - config/parameters.json unchanged

Fallback behavior: If a label name is NOT in label_mapping, uses schematic default value.


Convenience Functions

check_labels(image_path, schematic_name) -> DiagnosticReport

One-line diagnostic that prints report and returns result.

from pycemrg_image_analysis.utilities.label_tools import check_labels

report = check_labels(Path("seg.nrrd"), "lpv1_ring")
# Automatically prints formatted report

if report.has_issues:
    # Handle mismatches...

list_available_schematics() -> None

Print all schematic names organized by category.

from pycemrg_image_analysis.utilities.label_tools import list_available_schematics

list_available_schematics()

Output:

Available Schematics:
==================================================

Myocardium:
  • aortic_wall
  • la_myocardium
  • lv_outflow
  ...

Valves:
  • aortic_valve
  • mitral_valve
  ...

Rings:
  • lpv1_ring
  • lpv2_ring
  ...


Runtime Validation Functions

get_present_labels(image) -> set[int]

Get all unique non-zero labels present in an image.

Pure stateless function that extracts label values from in-memory image. Useful for runtime validation before executing workflow steps.

Parameters: - image: SimpleITK.Image to inspect

Returns: Set of integer label values present (excluding 0/background)

Example:

from pycemrg_image_analysis.utilities.label_tools import get_present_labels

# Check what's in working image
present = get_present_labels(working_image)
# {1, 2, 3, 5}

# Orchestrator validates before step
required = {1, 4}  # Step needs LV_BP and LA_BP
if not required.issubset(present):
    missing = required - present
    logger.warning(f"Missing labels {missing}, skipping step")
    continue


check_required_labels(image, required_label_values) -> tuple[bool, set[int]]

Check if image contains all required labels.

Parameters: - image: SimpleITK.Image to check - required_label_values: Set of integer labels that must be present

Returns: Tuple of (all_present: bool, missing: set[int])

Example:

from pycemrg_image_analysis.utilities.label_tools import check_required_labels

# Get required label values from LabelManager
required = {
    label_manager.get_value("LV_BP_label"),
    label_manager.get_value("LA_BP_label")
}

# Check presence
all_present, missing = check_required_labels(working_image, required)

if not all_present:
    missing_names = [label_manager.get_name(val) for val in missing]
    logger.warning(f"Skipping mitral_valve: missing {missing_names}")
    continue  # Skip to next step

# Safe to proceed
contract = build_valve_contract(...)
result = valve_logic.create_from_rule(contract)

Use case: These functions enable graceful degradation in orchestrators - skip workflow steps when required source labels are missing, rather than silently failing or producing garbage outputs.

Key differences from LabelDiagnostic: - Operates on in-memory sitk.Image, not file paths - Returns machine-readable data (set[int], not printed reports) - No schematic dependency - works with any label set - Designed for runtime validation, not pre-flight diagnostics


Workflows

Workflow 1: Quick Check Before Using Standard Scaffolding

from pycemrg_image_analysis.utilities.label_tools import check_labels
from pycemrg_image_analysis import ImageAnalysisScaffolder

# Check compatibility
report = check_labels(Path("seg.nrrd"), "biventricular_basic")

if not report.has_issues:
    # Labels match - use standard scaffolding
    scaffolder = ImageAnalysisScaffolder()
    scaffolder.scaffold_components(
        output_dir=Path("config/"),
        component_names=["biventricular_basic"]
    )
else:
    print("Labels don't match - need custom mapping")

Workflow 2: Auto-Detect and Apply Sequential Mapping

from pycemrg_image_analysis.utilities.label_tools import (
    LabelDiagnostic, LabelRemapper
)
from pycemrg_image_analysis import ImageAnalysisScaffolder

# Diagnose
diagnostic = LabelDiagnostic()
report = diagnostic.check_image_against_schematic(
    Path("seg_from_slicer.nrrd"),
    "lpv1_ring"
)

# Try auto-suggestion
remapper = LabelRemapper()
suggestion = remapper.suggest_mapping_from_report(report)

if suggestion:
    # Use auto-detected mapping
    scaffolder = ImageAnalysisScaffolder()
    scaffolder.scaffold_components_with_mapping(
        output_dir=Path("config/"),
        component_names=["lpv1_ring"],
        label_mapping=suggestion,
    )
    print("✅ Config generated with auto-detected mapping")
else:
    print("Manual mapping required")

Workflow 3: Manual Mapping for Non-Sequential Labels

from pycemrg_image_analysis.utilities.label_tools import check_labels
from pycemrg_image_analysis import ImageAnalysisScaffolder

# Check what we have
report = check_labels(Path("seg.nrrd"), "lpv1_ring")

# Create manual mapping based on report
# (User looks at report and determines which image label = which role)
manual_mapping = {
    "LPV1_label": 5,        # Image has 5 for LPV1
    "LA_myo_label": 10,     # Image has 10 for LA myocardium
    "LPV1_ring_label": 100, # Choose 100 for ring output
}

# Generate configs
scaffolder = ImageAnalysisScaffolder()
scaffolder.scaffold_components_with_mapping(
    output_dir=Path("config/"),
    component_names=["lpv1_ring"],
    label_mapping=manual_mapping,
)

Common Scenarios

Scenario 1: Slicer Reset to 1-N

Problem: After manual corrections in Slicer, labels reset to 1, 2, 3, 4...

Solution:

# Auto-suggestion should work
diagnostic = LabelDiagnostic()
report = diagnostic.check_image_against_schematic(image, schematic)

remapper = LabelRemapper()
mapping = remapper.suggest_mapping_from_report(report)
# mapping = {"LPV1_label": 1, "LA_myo_label": 2, ...}

scaffolder.scaffold_components_with_mapping(..., label_mapping=mapping)

Scenario 2: Site-Specific Labeling Convention

Problem: Clinical site uses custom label scheme (e.g., 100-199 for chambers, 200-299 for vessels)

Solution:

# Manual mapping
site_mapping = {
    "LV_BP_label": 101,
    "RV_BP_label": 102,
    "LA_BP_label": 103,
    "RA_BP_label": 104,
    # ...
}

scaffolder.scaffold_components_with_mapping(..., label_mapping=site_mapping)

Scenario 3: Partial Mismatch

Problem: Some labels match, some don't

Solution:

# Only specify mismatched labels in mapping
# Unspecified labels use schematic defaults
partial_mapping = {
    "LPV1_label": 99,  # Only this one is different
    # Rest use template values
}

scaffolder.scaffold_components_with_mapping(..., label_mapping=partial_mapping)


Error Handling

from pycemrg_image_analysis.utilities.label_tools import LabelDiagnostic

diagnostic = LabelDiagnostic()

try:
    report = diagnostic.check_image_against_schematic(
        Path("missing.nrrd"),
        "invalid_schematic"
    )
except FileNotFoundError:
    print("Image file not found")
except KeyError as e:
    print(f"Invalid schematic: {e}")

See Also

  • Examples: examples/label_remapping_workflow.py
  • Tests: tests/unit/test_label_tools.py
  • Schematics: src/pycemrg_image_analysis/schematics/