Extend registration_pipelines and bench_registration with stock antspyx ANTs presets (SyNRA, SyN, Repro sr/br, QuickRepro sr/br) plus the legacy Rigid -> SyNOnly workflow, and update the deformable benchmark documentation with follow-up ANTs-variant runs and results.
456 lines
No EOL
31 KiB
Markdown
456 lines
No EOL
31 KiB
Markdown
# Registration Pipeline Benchmark — CT → T1c (M6) — Deformable
|
||
|
||
Companion to [REGISTRATION_BENCHMARK_RIGID.md](REGISTRATION_BENCHMARK_RIGID.md):
|
||
the **deformable** variant of the same six-pipeline benchmark. The rigid
|
||
benchmark asked "which toolkit aligns the head best with 6 DOF?"; this one
|
||
asks "which toolkit's native deformable workflow aligns the head (and the
|
||
tissue) best?" on the same 10 M6 CT→T1c test cases, the same unified
|
||
contract, and the same metrics.
|
||
|
||
Run date: 2026-10-02 (main run 05:43–07:52 local; serial `synthmorph`
|
||
re-run 09:58–10:07 local; accelerated `simpleitk` re-run 15:31–16:26
|
||
local). Follow-up ANTs-variant runs 2026-10-03 (08:44–09:12 local: five
|
||
stock antspyx presets — direct `SyNRA` / `SyN`, `Repro[sr|br]`,
|
||
`QuickRepro[sr|br]`; 11:00–11:26 local: the legacy two-call
|
||
`Rigid → SyNOnly` workflow; 12:44–12:49 local: re-run of the stock `SyN`
|
||
preset as `ants_syn`, replacing the initial `SyNOnly` row) on the same
|
||
10 cases (see the *ANTs variants* section). 10 M6 test cases, 3× NVIDIA A100 80GB PCIe, 96-core host, conda
|
||
env `longitudinal-313`. Main run: 6 workers (24 threads each, 2 workers
|
||
per GPU). `synthmorph` was re-run serially (1 worker, one container per
|
||
GPU at a time) because the concurrent run lost 6/9 cases to cuDNN
|
||
scratch-space allocation failures (see the SynthMorph note under
|
||
Interpretation); the re-run registered 9/9, matching the concurrent
|
||
run's scores to 4 decimals on the 3 cases it completed. `simpleitk`
|
||
was re-run after its spline grid was accelerated from 16 mm to 24 mm
|
||
(see the simpleitk row of the pipeline table; the original 16 mm results
|
||
are archived as `simpleitk_16mm_legacy.json`).
|
||
|
||
## Unified contract
|
||
|
||
Identical to the rigid benchmark: the returned transform `T` must satisfy
|
||
`sitk.Resample(ct, t1c, T, ...)` = CT on the T1c grid, i.e. **T maps T1c →
|
||
CT coordinates**. fixed = T1c, moving = CT, full volumes (no head box, no
|
||
mask, no crop, no pad), each toolkit's native automatic transform
|
||
initialization, CT clipped to [0, 80] HU only for the pipelines where the
|
||
rigid A/B showed clipping helps (niftyreg, synthmorph).
|
||
|
||
One deformable-specific change: every pipeline's output (elastix BSpline
|
||
combination, ANTs warp + affine composite, NiftyReg affine + F3D spline,
|
||
SimpleITK Euler + BSpline, SynthMorph joint field, IMPACT RecursiveBSpline)
|
||
is **rasterised to a SimpleITK `DisplacementFieldTransform` on the T1c
|
||
grid** before scoring, so all six pipelines return the same transform type
|
||
and the shared scoring / GTV warping / QA code is used verbatim.
|
||
|
||
## The six pipelines (deformable configurations)
|
||
|
||
Each pipeline runs its toolkit's **native deformable workflow** — a linear
|
||
init stage + a deformable stage, or a single deformable stage where that is
|
||
the toolkit's canonical setup:
|
||
|
||
| pipeline | deformable configuration | metric |
|
||
|---|---|---|
|
||
| **antspyx** | `type_of_transform='SyN'` (ANTs' native deformable): Affine stage (2100×1200×1200×0, shrink 4×2×2×1) + SyN[0.2,3,0] deformable stage (40×20×0, shrink 4×2×1), center-of-mass auto init | Mattes MI (32 bins), both stages |
|
||
| **elastix** | two-stage (the toolkit's canonical deformable workflow): EulerTransform rigid stage (2000 iters × 3 resolutions, CenterOfGravity auto init) → BSplineTransform stage (16 mm grid, spline order 3, 2000 iters × 3 resolutions) initialized from stage 1 via `-t0` | Mattes MI (32 bins), 5000 samples/level |
|
||
| **impact** | the ImpactReg `MR_CT_TS` **reference map in its native deformable form**: RecursiveBSplineTransform (14 mm final grid, order 3) driven by the MIND + TotalSegmentator static feature combination (4 per-level scales 400/300/200/200 iters, 2000 samples) with the reference's three-metric combination: IMPACT (w 1.0) + Mattes MI (w 0.4) + TransformBendingEnergyPenalty (w 10). Two M6 adaptations, same as the rigid benchmark: CenterOfGravity auto init (the reference's GeometricalCenter start leaves the heads ~100 mm apart and aborts) and RequiredRatioOfValidSamples 0.1 | IMPACT semantic (L1 + Dice) + Mattes MI + bending energy |
|
||
| **niftyreg** | reg_aladin rigid init (3-level pyramid, 15 TLS iters/level) → reg_f3d non-rigid (F3D B-spline Free-Form deformation, Modat et al. 2010; NMI metric, symmetric bending/linear regularisation, 3-level pyramid, 150 iters at the final level, 8 mm final grid) | NCC block matching (init) + NMI (F3D) |
|
||
| **simpleitk** | two-stage (the toolkit's canonical deformable demo): Euler3D stage (2000 iters/level, pyramid [4,2,1]) → BSplineTransform3D stage (24 mm grid, 50 iters/level on pyramid [4,2]), the BSpline's control points initialised to reproduce the Euler stage exactly. The 24 mm grid (5577 parameters) is the accelerated setting (the 2026-10-02 original used 16 mm / 13005 parameters, ~61 min/case under the harness): this build's spline per-iteration cost scales ~linearly with the control-point count (~2.8 s/iter at the shrink-4 level and ~20 s/iter at shrink-2 for 24 mm vs ~6.4 / ~45.8 s for 16 mm — independent of the metric sample count, which is fixed in this SimpleITK build; LBFGS2 was tested and fails to move the transform on this noisy MI), and the full re-run keeps the 16 mm quality within 0.003–0.029 MI per case (median 0.698 vs 0.705, ImpactLoss median unchanged) while retaining simpleitk's `m6_26TYQI3C` MI/ImpactLoss win — see the acceleration note under Interpretation. A full-resolution polish pass (10 iters at shrink-1) was tested and dropped the achieved MI (0.6891 at 32 mm), so the pyramid stays [4,2] | Mattes MI (32 bins), both stages |
|
||
| **synthmorph** | `joint` model (affine + deformable in one symmetric network pass — the toolkit's canonical deformable; the `deformable` model requires a pre-existing field and would bias the comparison) | anatomy-aware contrast-invariant network (pretrained) |
|
||
|
||
## Test cases
|
||
|
||
First 10 M6 labelled cases of `m6.discover()` (all with a GTV label):
|
||
`m6_22GD5VL2_20230724`, `m6_22IRRKU6_20240115`, `m6_23XIMO4M_20230222`,
|
||
`m6_24V62QQM_20230223`, `m6_24V62QQM_20231017`, `m6_24V62QQM_20240820`,
|
||
`m6_26TYQI3C_20221027`, `m6_2BBN5XA4_20250411`, `m6_2C4TKI6H_20240506`,
|
||
`m6_2CDNN7BH_20240102`.
|
||
|
||
(`m6_22GD5VL2_20230724` has an incomplete CT head — a known data issue,
|
||
not a pipeline failure; it is scored as ct_incomplete in every pipeline,
|
||
as in the rigid benchmark.)
|
||
|
||
(Ground-truth reference transforms are **not** part of this benchmark —
|
||
scoring is by achieved image similarity only, as in the rigid benchmark.)
|
||
|
||
## Metrics (identical for every pipeline)
|
||
|
||
- **Mattes MI** (nats) between the warped CT and the T1c over the MR head
|
||
(largest connected component of the T1c mask) — achieved alignment, higher
|
||
= better.
|
||
- **IMPACT loss** (vboussot/ImpactLoss; pretrained TotalSegmentator
|
||
`TS/M730` deep features — weighted L1 over the two deepest layers, full
|
||
volumes; the same scorer as the rigid benchmark) between the warped CT
|
||
and the T1c — semantic-anatomy similarity, lower = better.
|
||
- **Registration wall time** (seconds, excludes I/O + scoring).
|
||
|
||
Per-case JSONs additionally retain the production skull-ring QA (gap +
|
||
concentricity, and the acceptance verdict) for reference.
|
||
|
||
### Result
|
||
|
||
Alignment is scored by the achieved **Mattes MI** (nats, warped-CT ~ T1c
|
||
over the MR head; higher = better) **and** the **ImpactLoss** semantic score
|
||
(warped-CT ~ T1c over the full volumes; lower = better), on the same
|
||
unclipped scoring volumes as the rigid benchmark, so all rows are comparable
|
||
on the same scales and against the rigid numbers. Full volumes, no mask /
|
||
crop / pad, fixed = T1c / moving = CT (shared rule). `niftyreg` and
|
||
`synthmorph` clip the CT (the moving image) to [0, 80] HU *before
|
||
registration* (per the rigid benchmark's clipping A/B); `antspyx`,
|
||
`elastix`, `impact` and `simpleitk` register the CT as-is. All six
|
||
transforms are deformable (each toolkit's native workflow) and are
|
||
rasterised to T1c-grid displacement fields before scoring.
|
||
|
||
All six pipelines registered 9/9 cases (the 10th is the known
|
||
incomplete-CT data issue, unscored in every pipeline). **Bold** = best
|
||
for the column (MI higher is better, ImpactLoss lower is better).
|
||
|
||
| pipeline | toolkit | registered | s/reg (med) | ImpactLoss (med/mean) | MI (med/mean, nats) |
|
||
|---|---|---|---|---|---|
|
||
| **antspyx** | ANTsPy 0.6.3 | 9 | 31.7 | 0.158 / 0.156 | **0.723 / 0.729** |
|
||
| **elastix** | ITK-Elastix 0.25.4 | 9 | 45.8 | 0.164 / 0.164 | 0.622 / 0.627 |
|
||
| **impact** | Docker `vboussot/elastix_impact` (Elastix 5.2.0 + IMPACT, deformable MIND+TS reference map) | 9 | 273.2 | 0.159 / 0.159 | 0.553 / 0.547 |
|
||
| **niftyreg** | NiftyReg 2.1.1 (reg_aladin + reg_f3d, Docker + CUDA) | 9 | 38.1 | 0.161 / 0.161 | 0.666 / 0.686 |
|
||
| **simpleitk** | SimpleITK 2.5.6 | 9 | 1369.9 | 0.158 / 0.159 | 0.698 / 0.704 |
|
||
| **synthmorph** | Docker `freesurfer/synthmorph` (TensorFlow, pretrained) | 9 | 49.1 | **0.156 / 0.156** | 0.627 / 0.640 |
|
||
|
||
Per-case MI (nats, unclipped score; **bold** = best for the case). The
|
||
`synthmorph` column is from the serial re-run (see the SynthMorph note
|
||
under Interpretation):
|
||
|
||
| case | antspyx | elastix | impact | niftyreg | simpleitk | synthmorph |
|
||
|---|---|---|---|---|---|---|
|
||
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT |
|
||
| m6_22IRRKU6_20240115 | **0.679** | 0.580 | 0.548 | 0.637 | 0.654 | 0.620 |
|
||
| m6_23XIMO4M_20230222 | **0.723** | 0.622 | 0.573 | 0.711 | 0.693 | 0.618 |
|
||
| m6_24V62QQM_20230223 | **0.789** | 0.570 | 0.635 | 0.729 | 0.738 | 0.701 |
|
||
| m6_24V62QQM_20231017 | **0.720** | 0.622 | 0.580 | 0.665 | 0.692 | 0.628 |
|
||
| m6_24V62QQM_20240820 | **0.738** | 0.674 | 0.462 | 0.707 | 0.700 | 0.666 |
|
||
| m6_26TYQI3C_20221027 | 0.799 | 0.694 | 0.537 | 0.793 | **0.801** | 0.709 |
|
||
| m6_2BBN5XA4_20250411 | **0.666** | 0.585 | 0.436 | 0.612 | 0.641 | 0.569 |
|
||
| m6_2C4TKI6H_20240506 | **0.719** | 0.635 | 0.553 | 0.656 | 0.698 | 0.627 |
|
||
| m6_2CDNN7BH_20240102 | **0.726** | 0.660 | 0.602 | 0.666 | 0.718 | 0.624 |
|
||
|
||
Per-case ImpactLoss (semantic, unclipped score; **bold** = best (lowest)
|
||
for the case):
|
||
|
||
| case | antspyx | elastix | impact | niftyreg | simpleitk | synthmorph |
|
||
|---|---|---|---|---|---|---|
|
||
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT |
|
||
| m6_22IRRKU6_20240115 | 0.1541 | 0.1595 | 0.1532 | 0.1530 | 0.1572 | **0.1512** |
|
||
| m6_23XIMO4M_20230222 | 0.1581 | 0.1657 | **0.1567** | 0.1605 | 0.1603 | 0.1573 |
|
||
| m6_24V62QQM_20230223 | 0.1473 | 0.1634 | 0.1497 | 0.1541 | 0.1502 | **0.1470** |
|
||
| m6_24V62QQM_20231017 | **0.1518** | 0.1591 | 0.1547 | 0.1620 | 0.1548 | **0.1518** |
|
||
| m6_24V62QQM_20240820 | **0.1545** | 0.1646 | 0.1694 | 0.1706 | 0.1584 | 0.1562 |
|
||
| m6_26TYQI3C_20221027 | 0.1580 | 0.1638 | 0.1656 | 0.1672 | **0.1562** | 0.1602 |
|
||
| m6_2BBN5XA4_20250411 | 0.1624 | 0.1663 | 0.1669 | 0.1648 | 0.1631 | **0.1616** |
|
||
| m6_2C4TKI6H_20240506 | 0.1594 | 0.1640 | 0.1585 | **0.1552** | 0.1626 | 0.1564 |
|
||
| m6_2CDNN7BH_20240102 | 0.1590 | 0.1662 | 0.1597 | 0.1605 | 0.1633 | **0.1583** |
|
||
|
||
### Interpretation
|
||
|
||
- **`antspyx` (Affine + SyN) leads on achieved MI** (median 0.723; best on
|
||
8 of 9 cases and within 0.002 of best on the other —
|
||
`m6_26TYQI3C`, 0.799 vs `simpleitk`'s 0.801) at the best median
|
||
ImpactLoss of the gradient-based tools (0.158, within 0.002 of
|
||
`synthmorph`'s 0.156), and it is the fastest full-workflow pipeline
|
||
(31.7 s/case) — the best time/quality trade of the six.
|
||
- **`simpleitk` is second on MI** (0.698; it keeps its per-case win on
|
||
`m6_26TYQI3C` — 0.801, still the highest MI of the whole benchmark,
|
||
though now only 0.001 ahead of `antspyx` — and cedes its
|
||
`m6_2CDNN7BH` win to `antspyx`, 0.718 vs 0.726) at a cost of ~23
|
||
min/case under the harness — ~43× the fastest pipeline after the
|
||
spline-grid acceleration below (this build's BSpline per-iteration
|
||
cost scales ~linearly with the control-point count, independent of the
|
||
metric sample count — see the pipeline table).
|
||
- **`simpleitk` acceleration (2026-10-02):** the original setting (16 mm
|
||
spline grid, 13005 parameters) ran 3658.9 s/case (median) under the
|
||
harness. The spline stage's per-iteration cost in this build scales
|
||
~linearly with the control-point count (16 mm: ~6.4 s/iter at the
|
||
shrink-4 level, ~45.8 s at shrink-2; 24 mm: ~2.8 / ~20 s; 32 mm:
|
||
~1.2 / ~9.6 s), and the metric sample count is fixed in this SimpleITK
|
||
build (`SetMetricAsMattesMutualInformation` exposes only the histogram
|
||
bins); LBFGS2 was tested and its line search rejects every step on
|
||
this noisy MI (the transform does not move). Switching to the **24 mm
|
||
grid (5577 parameters) cut the wall to 1369.9 s median (2.7×)** at a
|
||
cost of −0.003…−0.029 MI on 8 of 9 cases (+0.017 on `m6_2C4TKI6H`;
|
||
median 0.698 vs 0.705), with the median ImpactLoss unchanged (0.158)
|
||
and `simpleitk`'s `m6_26TYQI3C` MI/ImpactLoss wins intact. A 32 mm
|
||
grid (a further ~1.9×) was rejected: it drops below `antspyx`'s 0.7994
|
||
on `m6_26TYQI3C` (0.7902, and 0.7963 even with 100 fine-level iters).
|
||
A full-resolution polish pass (10 iters at shrink-1) was also tested
|
||
and dropped the achieved MI (0.6891 at 32 mm). The original 16 mm
|
||
results are archived as `simpleitk_16mm_legacy.json`.
|
||
- **`niftyreg`'s `reg_f3d` stage delivers the largest deformable gain of
|
||
any pipeline** (0.292 → 0.666 median MI, +0.374): it recovers the two
|
||
gross-offset cases where the rigid block-matching locked onto local
|
||
minima (`m6_24V62QQM_20240820` 0.118 → 0.707, `m6_2BBN5XA4` 0.069 →
|
||
0.612), lifts the toolkit to third on MI, and it is the most
|
||
concentric of the six (median skull-ring gap 5.5 mm mid / 3.8 mm std,
|
||
6/9 accepted).
|
||
- **The deformable stage does not uniformly pay off.** Against the rigid
|
||
benchmark's per-case MI it improves `antspyx` (0.652 → 0.723 median),
|
||
`simpleitk` (0.647 → 0.698, accelerated 24 mm setting) and `niftyreg`
|
||
(0.292 → 0.666), but
|
||
**degrades** `elastix` (0.646 → 0.622; mixed per case, worst regression
|
||
`m6_24V62QQM_20230223` 0.698 → 0.570) and `impact` (0.652 → 0.553 —
|
||
lower than its own rigid init on **all 9 cases**). The two degradations
|
||
are the coarse-grid BSpline configurations (14–16 mm) with heavy
|
||
bending regularisation — for `impact` the reference map's
|
||
TransformBendingEnergyPenalty at weight 10 — so the deformable stage
|
||
smooths rather than refines, and (for `impact`) its IMPACT-dominant
|
||
objective is simply not the achieved-MI objective.
|
||
- **`impact`'s deformable reference map is the weakest on the intensity
|
||
scale** (MI 0.553) even though its *semantic* score is competitive
|
||
(ImpactLoss 0.159, within 0.003 of the best median) — a
|
||
metric-objective/achieved-MI divergence the rigid benchmark did not
|
||
show (there the two scores agreed, with `impact` in the top tier on
|
||
both). At 273 s/case it is the worst time/quality trade of the six.
|
||
- **`synthmorph` (serial re-run, 9/9) splits the leaderboards: best on
|
||
the semantic scale, mid-pack on the intensity scale.** Median
|
||
ImpactLoss 0.156 — the best of the six — with the per-case best on 4 of
|
||
9 (plus a 4-decimal tie with `antspyx` on `m6_24V62QQM_20231017`),
|
||
while its MI (0.627) is fourth, statistically tied with `elastix`
|
||
(0.622). The pretrained contrast-invariant network optimises learned
|
||
anatomy rather than raw CT/MR intensity synchrony, so it wins where
|
||
anatomy matters and loses where the intensity histogram matters. Fast
|
||
(49.1 s/case) and the read-back cross-check against the tool's own
|
||
resampling is NCC 1.0 on all 9 cases.
|
||
- **The two score families discriminate differently here than in the
|
||
rigid benchmark.** The ImpactLoss scale is nearly saturated (medians
|
||
0.156–0.164, ~5 % spread; per-case bests spread across five tools,
|
||
`elastix` with 0/9), whereas the MI scale spans 0.553–0.723
|
||
(0.17 nats absolute) and the per-case bests go 8/9 `antspyx`, 1/9
|
||
`simpleitk`, 0/9 the rest. The deformable stage's main effect is fine
|
||
intensity alignment on top of the gross anatomy the rigid stages
|
||
already place — exactly what MI measures and ImpactLoss is less
|
||
sensitive to. For ranking the deformable workflows, achieved MI is
|
||
the more discriminative score.
|
||
- **`synthmorph` infrastructure note.** In the main concurrent run (6
|
||
workers, 2 containers per GPU) 6 of 9 cases failed with `CUDNN failed
|
||
to allocate the scratch space ... [Op:Conv3D]` inside the joint model's
|
||
256-channel 128³ hyperconvolution. Each case is a fresh
|
||
`docker run --rm` container, so the failures are cuDNN scratch-space
|
||
contention when two SynthMorph containers share one A100, not a
|
||
pipeline fault: the serial re-run registered 9/9, reproduced the
|
||
concurrent run's MI/ImpactLoss to 4 decimals on its 3 completed cases
|
||
(deterministic model), and ran ~17 s/case faster without the
|
||
contention (49.1 s vs 65.7 s median). The concurrent-run JSON is archived as
|
||
`synthmorph_concurrent_run.json`.
|
||
- **Production skull-ring QA (reference only, kept in the per-case
|
||
JSONs):** acceptance 6/9 `niftyreg`, 5/9 `elastix` / `synthmorph`,
|
||
4/9 `antspyx` / `simpleitk`, 2/9 `impact`. Concentricity: `niftyreg`
|
||
best (median gap 5.5 mm mid / 3.8 mm std), `simpleitk` (19.1 mm std,
|
||
accelerated setting) and `impact` (18.9 mm) worst on the rotational
|
||
term. The same weak correlation with perceived alignment noted in the
|
||
rigid benchmark, so it is reported for reference only.
|
||
|
||
## ANTs variants: direct SyNRA / SyN and the Repro / QuickRepro workflows
|
||
|
||
The current `antspyx` row above is antspyx's stock **`SyN` preset** (Affine
|
||
2100×1200×1200×0 + SyN[0.2,3,0] 40×20×0, Mattes MI 32, center-of-mass init).
|
||
As a follow-up (2026-10-03, 6 workers as the main run) seven further
|
||
ANTs configurations were benchmarked on the same 10 cases and the same
|
||
contract — the question: is there a better stock ANTs deformable workflow
|
||
for CT→T1c than the `SyN` preset?
|
||
|
||
| pipeline | antspyx preset | stages (all COM init unless noted) |
|
||
|---|---|---|
|
||
| **ants_rigid_synonly** | legacy two-call (`Rigid` + `SyNOnly`) | Rigid[0.25] (Mattes 32, 500×500×200, 4×2×1, 3×2×0) → SyNOnly SyN[0.2,3,0] (Mattes 32, 100×100×0, 4×2×1, 2×1×0) initialized from the Rigid stage; `Composite.h5` read-back |
|
||
| **ants_synra** | `SyNRA` | Rigid[0.25] + Affine[0.25] (each Mattes 32, 2100×1200×1200×0, 4×2×2×1, 3×2×1×0) + SyN[0.2,3,0] (Mattes 32, 40×20×0, 4×2×1) |
|
||
| **ants_syn** | `SyN` | Affine[0.25] (Mattes 32, 2100×1200×1200×0, 4×2×2×1, 3×2×1×0) + SyN[0.2,3,0] (Mattes 32, 40×20×0, 4×2×1); COM init — the stock preset, identical configuration to the existing `antspyx` row |
|
||
| **ants_synreprosr** | `antsRegistrationSyNRepro[sr]` | Rigid[0.1] (GC 1/1/Regular/0.25, 1000×500×250×100, 8×4×2×1, 3×2×1×0vox) + SyN[0.2,3,0] (CC 1/2, 50×20, 2×1, 1×0vox) |
|
||
| **ants_synreprobr** | `antsRegistrationSyNRepro[br]` | the `Repro` Rigid[0.1] stage + BSplineSyN[0.2,26,0,3] (CC 1/2, 50×20, 2×1, 1×0vox) |
|
||
| **ants_synquickreprosr** | `antsRegistrationSyNQuickRepro[sr]` | Rigid[0.1] (GC, 1000×500×250×0, 8×4×2×1) + SyN[0.2,3,0] (CC 1/2, 50×0, 2×1) |
|
||
| **ants_synquickreprobr** | `antsRegistrationSyNQuickRepro[br]` | Rigid[0.1] (GC, 1000×500×250×0, 8×4×2×1) + BSplineSyN[0.2,26,0,3] (CC 1/2, 50×0, 2×1) |
|
||
|
||
`sr`/`br` select the deformable stage (SyN vs BSplineSyN, antspyx preset
|
||
naming — not a brain mask); these are the same stage configurations the
|
||
`antsRegistrationSyNRepro*.sh` / `antsRegistrationSyNQuickRepro*.sh`
|
||
workflows use. `ants_rigid_synonly` is the two-call workflow that an early
|
||
version of this benchmark used: a Rigid stage, then a
|
||
*deeper* SyNOnly stage (100 iterations at every level incl. full
|
||
resolution, vs the `SyN` preset's 40×20×0) initialized from the Rigid
|
||
stage's transform. Full volumes, no CT clip, each toolkit preset's own
|
||
metrics (GC/CC/Mattes as above). As for the other rows, the stage files
|
||
are rasterised to the T1c-grid displacement field and scored with the
|
||
shared code; the composite read-back was cross-checked per case against
|
||
ANTs' own resampling (**NCC 1.0 on all 63 scored cases**,
|
||
`own_warp_ncc_head` in the JSONs). One read-back subtlety: when ANTs runs Rigid + Affine as separate
|
||
linear stages (`SyNRA`), it writes the merged linear stage as a *binary*
|
||
`0GenericAffine.mat` that SimpleITK cannot parse; the pipeline recovers it
|
||
by point-probing the transform (exact for affine maps, verified to
|
||
2×10⁻⁴ mm).
|
||
|
||
All six variants registered 9/9 (the 10th is the known incomplete-CT data
|
||
issue). **Bold** = best for the column; `antspyx` is the existing row,
|
||
re-listed for reference.
|
||
|
||
| pipeline | registered | s/reg (med) | ImpactLoss (med/mean) | MI (med/mean, nats) |
|
||
|---|---|---|---|---|
|
||
| **ants_rigid_synonly** | 9 | 208.4 | 0.156 / 0.155 | **0.753 / 0.763** |
|
||
| **antspyx** (existing) | 9 | 31.7 | 0.158 / 0.156 | 0.723 / 0.729 |
|
||
| **ants_synra** | 9 | 43.6 | 0.158 / 0.156 | 0.722 / 0.729 |
|
||
| **ants_syn** | 9 | 33.8 | 0.158 / 0.156 | 0.722 / 0.729 |
|
||
| **ants_synreprosr** | 9 | 294.7 | **0.151 / 0.153** | 0.530 / 0.529 |
|
||
| **ants_synreprobr** | 9 | 370.3 | 0.156 / 0.157 | 0.541 / 0.537 |
|
||
| **ants_synquickreprosr** | 9 | 83.7 | 0.154 / 0.156 | 0.542 / 0.534 |
|
||
| **ants_synquickreprobr** | 9 | 96.0 | 0.158 / 0.158 | 0.549 / 0.538 |
|
||
|
||
Per-case MI (nats, unclipped score; **bold** = best for the case):
|
||
|
||
| case | antspyx | ants_rigid_synonly | ants_synra | ants_syn | ants_synreprosr | ants_synreprobr | ants_synquickreprosr | ants_synquickreprobr |
|
||
|---|---|---|---|---|---|---|---|---|
|
||
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT |
|
||
| m6_22IRRKU6_20240115 | 0.679 | **0.741** | 0.681 | 0.681 | 0.623 | 0.646 | 0.630 | 0.627 |
|
||
| m6_23XIMO4M_20230222 | 0.723 | **0.760** | 0.722 | 0.722 | 0.530 | 0.544 | 0.529 | 0.530 |
|
||
| m6_24V62QQM_20230223 | 0.789 | **0.824** | 0.789 | 0.787 | 0.520 | 0.528 | 0.542 | 0.549 |
|
||
| m6_24V62QQM_20231017 | **0.720** | 0.716 | **0.720** | **0.720** | 0.473 | 0.475 | 0.469 | 0.462 |
|
||
| m6_24V62QQM_20240820 | 0.738 | **0.753** | 0.738 | 0.738 | 0.524 | 0.525 | 0.517 | 0.524 |
|
||
| m6_26TYQI3C_20221027 | 0.799 | **0.857** | 0.798 | 0.799 | 0.557 | 0.541 | 0.599 | 0.632 |
|
||
| m6_2BBN5XA4_20250411 | 0.665 | **0.696** | 0.665 | 0.666 | 0.230 | 0.303 | 0.235 | 0.297 |
|
||
| m6_2C4TKI6H_20240506 | 0.719 | **0.751** | 0.719 | 0.719 | 0.672 | 0.649 | 0.654 | 0.621 |
|
||
| m6_2CDNN7BH_20240102 | 0.726 | **0.765** | 0.726 | 0.725 | 0.629 | 0.622 | 0.631 | 0.599 |
|
||
|
||
Per-case ImpactLoss (semantic, unclipped score; **bold** = best (lowest) for
|
||
the case):
|
||
|
||
| case | antspyx | ants_rigid_synonly | ants_synra | ants_syn | ants_synreprosr | ants_synreprobr | ants_synquickreprosr | ants_synquickreprobr |
|
||
|---|---|---|---|---|---|---|---|---|
|
||
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT |
|
||
| m6_22IRRKU6_20240115 | 0.1541 | 0.1530 | 0.1542 | 0.1540 | **0.1442** | 0.1482 | 0.1474 | 0.1493 |
|
||
| m6_23XIMO4M_20230222 | 0.1581 | 0.1565 | 0.1581 | 0.1581 | **0.1505** | 0.1549 | 0.1535 | 0.1585 |
|
||
| m6_24V62QQM_20230223 | 0.1473 | **0.1464** | 0.1472 | 0.1472 | 0.1515 | 0.1553 | 0.1535 | 0.1560 |
|
||
| m6_24V62QQM_20231017 | 0.1518 | **0.1512** | 0.1516 | 0.1519 | 0.1559 | 0.1575 | 0.1577 | 0.1581 |
|
||
| m6_24V62QQM_20240820 | **0.1545** | **0.1545** | 0.1547 | 0.1546 | 0.1553 | 0.1578 | 0.1586 | 0.1586 |
|
||
| m6_26TYQI3C_20221027 | 0.1580 | 0.1562 | 0.1578 | 0.1579 | **0.1561** | 0.1574 | 0.1602 | 0.1610 |
|
||
| m6_2BBN5XA4_20250411 | 0.1624 | **0.1611** | 0.1624 | 0.1624 | 0.1680 | 0.1695 | 0.1690 | 0.1707 |
|
||
| m6_2C4TKI6H_20240506 | 0.1594 | 0.1582 | 0.1594 | 0.1593 | **0.1490** | 0.1526 | 0.1517 | 0.1545 |
|
||
| m6_2CDNN7BH_20240102 | 0.1590 | 0.1582 | 0.1589 | 0.1589 | **0.1493** | 0.1560 | 0.1538 | 0.1580 |
|
||
|
||
### Interpretation (ANTs variants)
|
||
|
||
- **`ants_rigid_synonly` is the best ANTs configuration of the eight** —
|
||
the only one that beats the current `antspyx` row: median MI **0.753**
|
||
(per-case best on 8 of 9; its 0.857 on `m6_26TYQI3C` is the highest MI
|
||
of the whole benchmark, ahead of the previous 0.801) at ImpactLoss
|
||
0.156 (second-best median after `synthmorph`'s 0.156). The gain over
|
||
the `SyN` preset comes from the *deeper* SyN stage: 100 iterations at
|
||
every level **including full resolution** (100×100×0 vs 40×20×0)
|
||
refining the Rigid stage's pose. The cost is 208.4 s/case (~6.6× the
|
||
preset) — the full-resolution SyN iterations dominate.
|
||
- **`ants_synra` is indistinguishable from the current `antspyx` row** on
|
||
quality (median MI 0.722 vs 0.723, ImpactLoss 0.158 vs 0.158; per-case
|
||
deltas within 0.002 nats, ties on 4 of 9) at **+38 % wall time**
|
||
(43.6 s vs 31.7 s): the explicit Rigid stage ahead of the Affine stage
|
||
buys nothing — on these pairs the 12-DOF stage from the center-of-mass
|
||
init already finds the gross pose, so the extra 6-DOF stage just
|
||
re-solves it.
|
||
- **`ants_syn` reproduces the existing `antspyx` row** (median MI 0.722
|
||
vs 0.723, ImpactLoss 0.158, s/reg 33.8 vs 31.7; per-case deltas within
|
||
0.002 nats) — as expected, since it is the *same stock `SyN` preset*
|
||
(Affine + SyN[0.2,3,0], COM init) re-run in this follow-up batch: it
|
||
serves as a consistency check that the shared read-back / scoring path
|
||
is configuration-stable.
|
||
- **The Repro / QuickRepro family is semantically competitive but weaker on
|
||
intensity:** median MI 0.530–0.549 (vs 0.723 for the `SyN` preset),
|
||
driven by the GC-based Rigid stage and the short 2-level SyN/BSplineSyN
|
||
refinement (50×20 or 50×0 iters, 2×1 pyramid, CC 2-bin metric) — a
|
||
workflow tuned for intra-modality T1w-T1w. `ants_synreprosr` still takes
|
||
the **best ImpactLoss median of all thirteen pipelines here** (0.151;
|
||
per-case wins on 4 of 9), so the semantic/intensity split of the main
|
||
table carries over to the stock ANTs workflows.
|
||
- **Time tracks budget, not quality:** the full Repro Rigid stage
|
||
(1000×500×250×100, 8×4×2×1, GC) dominates — `ants_synreprobr` is the
|
||
slowest ANTs configuration (370 s/case) at the same (low) MI as the
|
||
84–96 s `QuickRepro` variants (which drop the 4th rigid level and the
|
||
2nd SyN level, changing the MI by < 0.02). Only `ants_rigid_synonly`
|
||
challenges the `SyN` preset's quality (better MI at 6.6× the time);
|
||
every other variant is worse for more or for the same.
|
||
- **Production skull-ring QA (reference only):** acceptance 4/9
|
||
`ants_syn` / `ants_synra` / `ants_rigid_synonly` (= baseline), 2/9
|
||
`ants_synreprobr`, 1/9 the rest; rotational dispersion worst in the
|
||
`Repro` / `QuickRepro` family (24–29 mm std) vs 14–15 mm for
|
||
`ants_syn` / `ants_synra` / `ants_rigid_synonly` — consistent with
|
||
their alignment.
|
||
|
||
## Transform conventions (deformable-specific)
|
||
|
||
Unlike the rigid benchmark (where the elastix-style toolkits came out in the
|
||
opposite direction 8/9 cases and needed the best-of-T vs T⁻¹ orientation
|
||
gate), every deformable output here is consumed in the documented
|
||
fixed→moving = T1c→CT direction; the gate is replaced by an analytic
|
||
read-back plus a skull-ring sanity check. Two non-obvious conventions were
|
||
established empirically (validated against each tool's own resampling, NCC
|
||
≥ 0.998 on the head):
|
||
|
||
- **NiftyReg and surfa (FreeSurfer's NIfTI stack) decode RAS+ NIfTI qforms
|
||
naively, i.e. they work in an x/y-mirrored world.** A displacement delta
|
||
(dx, dy, dz) stored by such a tool is (−dx, −dy, dz) in the true RAS
|
||
world; the field grid (file indices) is unaffected. The read-back flips
|
||
the x/y components of the dense field. (ANTs writes correct RAS fields —
|
||
no flip needed.)
|
||
- **NiftyReg reg_f3d writes only the control-point grid** (outputCPP.nii);
|
||
`reg_transform -disp` evaluates it to a dense field on the reference grid.
|
||
The reg_aladin affine is folded into the control points before
|
||
optimization (reg_f3d initialises the spline with the affine's
|
||
deformation field), so the dense field **is** the full transform — no
|
||
separate composition step. (The `-comp` subcommand was tested and does
|
||
not reproduce reg_f3d's internal transform; it is not used.)
|
||
- **SynthMorph joint**: driven as `register -m joint moving fixed`
|
||
(moving = clipped CT first, fixed = T1c second); the `-t` output is the
|
||
displacement field on the fixed (T1c) grid — the contract transform
|
||
directly. The tool's own `-o` resampling is written as a cross-check
|
||
(per-case `own_resample_ncc_head` in the JSONs).
|
||
- **ANTs SyN**: the two stage outputs (001Warp.nii.gz + 000GenericAffine.mat)
|
||
are composed as warp-then-affine (verified to reproduce ANTs' own
|
||
`warpedmovout` to < 0.001 MI) and rasterised.
|
||
- **Elastix / IMPACT**: the combination transform (auto-init folded in) is
|
||
rasterised — elastix in-process via `itk.TransformToDisplacementFieldFilter`
|
||
(exact, machine precision on affine fields), IMPACT by evaluating the
|
||
BSpline control-point file directly (SimpleITK cannot read elastix's
|
||
BSpline .txt format; scipy's order-3 B-spline = ITK's kernel).
|
||
|
||
## Reproducing
|
||
|
||
```bash
|
||
source /opt/conda/etc/profile.d/conda.sh && conda activate longitudinal-313
|
||
cd /mnt/b4/xfr/git26/longitudinal
|
||
|
||
# all six deformable pipelines (results go to results/bench_registration_deformable/)
|
||
# the 2026-10-02 main run used --workers 6 --threads 24 (~2 h 9 min wall,
|
||
# dominated by simpleitk's ~111 min phase)
|
||
python scripts/bench_registration.py --deformable --workers 6 --threads 24 --qa-images
|
||
|
||
# single pipeline / case subset
|
||
python scripts/bench_registration.py --deformable --pipelines simpleitk impact --n-cases 3
|
||
|
||
# the ANTs-variant follow-up (direct SyNRA / SyN + Repro / QuickRepro
|
||
# sr/br; results go to the same results/bench_registration_deformable/ dir,
|
||
# 2026-10-03 run, 6 workers, ~28 min wall; `ants_syn` is the re-run of the
|
||
# stock SyN preset that replaced the initial SyNOnly row)
|
||
python scripts/bench_registration.py --deformable --pipelines "ants_synra ants_syn ants_synreprosr ants_synreprobr ants_synquickreprosr ants_synquickreprobr" --workers 6 --threads 24 --qa-images
|
||
|
||
# the legacy two-call Rigid -> SyNOnly(100,100,100) workflow (2026-10-03,
|
||
# 6 workers, ~25 min wall; dominated by the full-resolution SyN iters)
|
||
python scripts/bench_registration.py --deformable --pipelines "ants_rigid_synonly" --workers 6 --threads 24 --qa-images
|
||
|
||
# serial SynthMorph re-run (the 2026-10-02 final numbers): one container per
|
||
# GPU at a time; two concurrent containers on one A100 hit cuDNN
|
||
# scratch-space OOM in the joint model's 256-ch 128^3 hyperconvolution
|
||
python scripts/bench_registration.py --deformable --pipelines synthmorph --workers 1 --threads 24 --qa-images
|
||
|
||
# combine the per-pipeline JSONs into summary.json / summary.md / plot
|
||
python scripts/bench_registration.py --deformable --merge
|
||
```
|
||
|
||
## Visual QA images
|
||
|
||
`--qa-images` writes a `checkreg`-style PNG per (pipeline, case) to
|
||
`results/bench_registration_deformable/qa/<pipeline>__<case>.png`
|
||
(9 per pipeline; the incomplete-CT case is skipped) — same format as the
|
||
rigid benchmark: a 3×2 grid of the mid-axial / mid-coronal / mid-sagittal
|
||
slices through the head center, **left column** checkerboard blend of the
|
||
warped CT vs the T1c (clean registration = seamless mosaic), **right
|
||
column** additive color overlay (CT red, T1c green; agreement = yellow,
|
||
misregistration = red/green fringing). CT is brain-windowed 0–80 HU, T1c
|
||
2–98 % head window; the title carries the pipeline, case key and the
|
||
achieved Mattes MI. |