Skip to content

Add correspondence-quality command with Python API (#2612)#2613

Open
akenmorris wants to merge 4 commits into
masterfrom
amorris/correspondence_measure
Open

Add correspondence-quality command with Python API (#2612)#2613
akenmorris wants to merge 4 commits into
masterfrom
amorris/correspondence_measure

Conversation

@akenmorris

@akenmorris akenmorris commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Closes #2612.

Adds a shapeworks correspondence-quality subcommand and matching Python API.

For each subject, reconstructs the surface from its local particles via a biharmonic mesh warp from the cohort medoid template (matching ParticleShapeStatistics::compute_median_shape, so it's the same subject Studio picks as median), and measures point-to-cell distance to the groomed mesh. Distances are also normalized by each subject's bounding-box diagonal for scale-invariant "good/bad" judgement across anatomies. R

CLI

shapeworks correspondence-quality --name <project.swproj|xlsx>
    [--output <csv>] [--output_meshes <dir>]
    [--method point-to-cell|point-to-point] [--worst N]
  • Handles both mesh and distance-transform groomed inputs (DT → mesh via Image::toMesh(0.0)).
  • Handles multi-domain projects (single global L1-medoid template across all domains, mirroring Studio).
  • Aggregates (mean/median/p95/max) exclude the template's own near-zero row so small cohorts aren't skewed by it.
  • Optional --output_meshes writes each reconstruction as .vtk with an embedded per-vertex distance field for inspection in Studio/ParaView.
  • CSV columns: subject, domain, is_template, mean_dist, max_dist, bbox_diag, norm_mean, norm_max.

Python binding (sw.CorrespondenceEvaluation)

project = sw.Project(); project.load("model.swproj")
report = sw.CorrespondenceEvaluation.evaluate(project)
print(report.template_subject, report.agg_norm.mean)
for r in report.rows:
    print(r.subject, r.norm_mean, r.is_template)

@akenmorris
akenmorris force-pushed the amorris/correspondence_measure branch from f7e56f5 to 4bcb2ae Compare July 23, 2026 18:37
New `shapeworks correspondence-quality` subcommand evaluates per-subject
correspondence by reconstructing each shape from its local particles via
biharmonic mesh warp from the cohort L1-medoid template (matching Studio's
median selection) and measuring distance to the groomed mesh.

- Per-subject mean/max point-to-cell (or point-to-point) distance, plus
  bbox-diagonal-normalized values for scale-invariant good/bad judgement.
- Aggregates (mean/median/p95/max) exclude the template row so small
  cohorts aren't skewed by its near-identity reconstruction.
- Handles both mesh and distance-transform groomed inputs.
- Optional per-subject CSV and --output_meshes dump with embedded
  per-vertex distance field for visual inspection.
Move the correspondence-quality algorithm out of the shapeworks CLI
handler into a reusable class in Libs/Particles, alongside
ShapeEvaluation, and add pybind11 bindings so it can be called from
Python without shelling out.

- New Libs/Particles/CorrespondenceEvaluation.{h,cpp} with the
  CorrespondenceEvaluation class and CorrespondenceQualityReport /
  Row / Stats result structs.
- shapeworks correspondence-quality command is now a thin CLI wrapper
  around the class; output is byte-identical to before.
- Python: sw.CorrespondenceEvaluation.evaluate(project, method,
  output_meshes_dir) returns a CorrespondenceQualityReport with per-
  subject rows and aggregate stats (template row excluded).
- Libs/Particles now depends on Project.
@akenmorris
akenmorris force-pushed the amorris/correspondence_measure branch from 4bcb2ae to 1369b55 Compare July 23, 2026 20:04
Project is only referenced inside CorrespondenceEvaluation.cpp, not in any
header. Linking it PUBLIC to Particles was propagating it as a transitive
dep of every consumer (Optimize, shapeworks_py, tests) and perturbing VTK
static-initialization / factory-registration order in the shared-library
graph, which broke .vtp reads and mesh-domain tests on Linux and Mac Arm64
in CI while working locally.
Linking Project into Particles hoisted libxlnt (which bundles expat and
exports the public XML_* symbols) ahead of VTK's/ITK's static expat
archives, so xlnt's expat interposed VTK's and crashed on the first .vtp
read (OptimizeTests, shapeworks_py). Drop the Particles->Project link
edge: CorrespondenceEvaluation.o is only pulled from libParticles.a by
the CLI and Python module, which already link Project. Order Particles
before Project in shapeworks_py so its symbols still resolve on Linux.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add correspondence quality measure based on reconstruction error

1 participant