Scoring a Mesh-Stage Model Against Images
A mm error against the surfaces a registration produced says how well the
network reproduces that registration. WorkflowEvaluateMovement asks the
other question: how close are the size and shape of the inferred anatomy to the
anatomy that was actually imaged, structure by structure. See Tutorial 11 in
Tutorials.
For every gated time point it carries the reference frame’s labelmap into that time point with the network’s own deformation, and compares the result to the labelmap of the frame that was acquired: volume difference, Dice and surface RMSE per lung lobe or per heart chamber.
Per-structure scoring
- class physiotwin4d.WorkflowEvaluateMovement(movement_workflow, label_names, log_level=20)[source]
Bases:
PhysioTwin4DBaseScore inferred motion per anatomical structure against acquired frames.
- Parameters:
movement_workflow (
WorkflowInferMovement) – The displacement decoder whose predictions are scored.label_names (
dict[int,str]) – Structures to score,{label_id: name}. Ids the reference frame does not contain are dropped with a warning; ids a single acquired frame does not contain are skipped for that frame alone, since a structure can leave the field of view.log_level (
int|str) – Logging level. Default:logging.INFO.
- __init__(movement_workflow, label_names, log_level=20)[source]
Initialize the base class with logging configuration.
- Parameters:
class_name – Name for the class (used in log messages). If None, uses the class name. Default: None
log_level (
int|str) – Logging level. Can be an integer (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) or a string (‘DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’). Default: logging.INFOlog_to_file – Optional file path to write logs to in addition to console output. Default: None
- process(case_id, shape_parameters, reference_mesh, reference_labelmap, ground_truth_labelmaps, output_directory, smoothing_sigma_mm=10.0, evaluation_spacing_mm=1.0, include_dice=True)[source]
Score every gated time point of one case.
- Parameters:
case_id (
str) – Name of the case being scored, recorded in every output.shape_parameters (
Path) – JSON file with the case’s PCA coefficient vector.reference_mesh (
Path) – The case’s fitted reference-frame SSM surface. The predicted displacements are added to its points, and its extent defines the evaluation grid.reference_labelmap (
Image) – Labelmap of the reference frame, the anatomy carried into every other time point.ground_truth_labelmaps (
dict[float,Image]) – Acquired labelmap per stage, keyed by the normalized stage in[0, 1].output_directory (
Path) – Directory the report, the CSV and the per-stage geometry are written to.smoothing_sigma_mm (
float) – Gaussian sigma, in millimeters, that turns the network’s surface-shell deformation into a continuous field.evaluation_spacing_mm (
float) – Isotropic pitch every metric is measured on. It sets both the voxel volume the Dice and volume figures are quantized to and the resolution of the deformation fields, whose memory grows with its cube.include_dice (
bool) – Report the Dice overlap. Turn it off for a structure whose motion is small against its own size: Dice is an overlap fraction, so a lung lobe scores over 0.96 undeformed and the column says more about the organ’s bulk than about the motion. The volume and surface figures still resolve it.
- Return type:
- Returns:
Dict with
rows(every metric row),csv_file,report_file,volume_plot_file,predicted_surfacesandwarped_labelmaps.- Raises:
ValueError – If
ground_truth_labelmapsis empty, none of the requested labels are in the reference frame, or no label survives scoring because the acquired frames contain none of them.
- static dice(truth, predicted, label)[source]
Dice overlap of one label.
nanwhen neither volume contains it.- Return type:
Example
from physiotwin4d import (
WorkflowEvaluateMovement,
WorkflowInferMovement,
WorkflowInferPhysicsNeMo,
)
evaluate = WorkflowEvaluateMovement(
movement_workflow=WorkflowInferMovement(
WorkflowInferPhysicsNeMo(model_directory=model_dir)
),
label_names={28: "lung_upper_lobe_left", 29: "lung_lower_lobe_left"},
)
result = evaluate.process(
case_id="Case1Pack",
shape_parameters=pca_coefficients_file,
reference_mesh=ssm_surface_file,
reference_labelmap=reference_labelmap,
ground_truth_labelmaps={0.0: frame_00, 0.1: frame_10},
output_directory=out_dir,
)
print(result["report_file"], result["csv_file"])
Notes
Why labelmaps rather than the model’s surface. The lung shape model carries its five lobes as per-cell labels, but the heart model is a single structure — the whole heart minus its chamber cavities — so its chambers exist only in the acquired labelmaps. Warping those labelmaps scores every structure the acquisition contains, whether or not the shape model represents it separately.
The evaluation grid. Everything is measured on one isotropic grid built around the reference anatomy, so a case whose gated frames carry different slice pitches is still scored on a single, stated voxel volume. Its pitch sets both that voxel volume and the memory the per-stage deformation fields take, which grows with its cube.