Replace the shared CT head-box resampling and manual centroid pre-shift with each toolkit's native automatic transform initialization (elastix/impact use CenterOfGravity, antspyx uses center-of-mass, simpleitk uses identity). All pipelines now register on the full volumes instead of a cropped head box. Drop the _ct_head_box_mm helper and update the benchmark table to document per-toolkit init methods in a dedicated "init" column.
26 KiB
Registration Pipeline Benchmark — CT → T1c (M6)
Benchmarks five rigid, cross-modality registration pipelines that align a
head-and-neck CT to a tightly-cropped T1c MRI so the GTV can be warped onto the
T1c (MR) native grid. This is the registration step of
scripts/02_build_m6_dataset.py. The pairs are hard: different modality (CT
Hounsfield vs MR intensity) and the patient is repositioned between the CT and
MR sessions, so the inter-session head tilt can reach ~30°.
- Pipelines:
src/registration_pipelines.py - Benchmark harness:
scripts/bench_registration.py - Results:
results/bench_registration/(per-pipeline JSON,summary.json,summary.md,bench_registration.png)
Unified contract
Every pipeline returns a SimpleITK transform T such that
sitk.Resample(ct, t1c, T, sitk.sitkLinear) # CT onto the MR grid
sitk.Resample(tv, t1c, T, sitk.sitkNearestNeighbor, 0) # GTV onto the MR grid
places the CT / GTV concentric with the MR head. In Resample's
destination→source convention this T maps T1c → CT. All pipelines
register on the full CT / T1c volumes (no head box) and use each toolkit's
native automatic transform initialization (no manual centroid offset):
elastix / impact use AutomaticTransformInitialization (CenterOfGravity),
antspyx its default center-of-mass alignment, simpleitk starts from identity,
synthmorph needs none. All use fixed = CT, moving = T1c except impact,
which keeps fixed = T1c, moving = CT: the CT FOV (~300×300×251 mm) is much
larger than the T1c crop (~220×220×150 mm), so with CT as the fixed image too
many CT samples map outside the T1c buffer and the IMPACT metric aborts
("too many samples map outside moving image buffer"). The comparison is on
each toolkit's native optimizer / metric / global-search capability.
The five pipelines
Listed alphabetically. All register on the full volumes (no head box) and
use each toolkit's native automatic transform initialization (no manual
centroid offset): elastix / impact use elastix's AutomaticTransformInitialization
(CenterOfGravity), antspyx uses its default center-of-mass alignment, simpleitk
starts from identity, and synthmorph needs none (global deep method). All use
fixed = CT, moving = T1c except impact, which keeps fixed = T1c, moving
= CT (swapping breaks its TotalSegmentator metric — see below).
| # | pipeline | fixed / moving | init | metric | transform | status in this env |
|---|---|---|---|---|---|---|
| 1 | antspyx |
CT / T1c | auto COM | Mattes MI (multi-res) | ANTs Rigid |
✅ ran (main env; stochastic MI subsampling) |
| 2 | elastix |
CT / T1c | auto CenterOfGravity | Mattes MI (10 bins) | EulerTransform rigid (5.x), ASGD, 3 resolutions |
✅ ran (main env, in-process python API — no CLI) |
| 3 | impact |
T1c / CT | auto CenterOfGravity | IMPACT semantic (TotalSegmentator TS/M730.pt, L2, Jacobian/GPU) |
EulerTransform rigid, ASGD, 4 scales |
✅ ran (main env, Docker + GPU) |
| 4 | simpleitk |
CT / T1c | identity | Mattes MI (32 bins) | Euler3D rigid, pyramid [4,2,1] |
✅ ran |
| 5 | synthmorph |
CT / T1c | none (global) | SynthMorph anatomy-aware contrast-invariant (deep, pretrained) | 6-DOF rigid matrix | ✅ ran (main env, Docker + GPU) |
simpleitk has two budgets: --simpleitk-mode benchmark (single pyramid,
centered start — the default, comparable to the other tools) and
--simpleitk-mode production (the full multi-start tilt grid from
02_build_m6_dataset.py, ~20× slower, the most robust but not used for the
head-to-head).
Test cases
First 10 labeled M6 cases (--n-cases 10 --sample first), reproducible. 9 have
a complete CT; 1 (m6_22GD5VL2_20230724) has an incomplete CT head and is
reported as incomplete-CT for every pipeline (a data issue, not a pipeline
failure).
m6_22GD5VL2_20230724 (incomplete CT)
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
Metrics (identical for every pipeline)
Production QA, crop-independent:
skull_ring_gap_mm— march rays from the MR head center; the gap between the MR head silhouette boundary and the warped-CT skull ring (p97 of positive CT) at mid and low levels, plus the angular std. Aligned ⇔0 ≤ mid ≤ 20 mmandstd ≤ 12 mm.midmeasures translational concentricity;stdmeasures rotational concentricity.tv_in_ct_frac— fraction of the dilated warped GTV inside the warped-CT head tissue (the slab must straddle the lesion). Acceptance needs≥ 0.5.tv_nvox— warped GTV volume.accepted—tv_nvox > 0 and tv_in_ct_frac ≥ 0.5 and aligned(the production acceptance rule).mi_ct_mr— mean Mattes MI (nats) between the CT inverse-transformed into MR coordinates (the warped CT on the T1c grid) and the original MR, restricted to the MR head and each image percentile-binned independently (SimpleITK Mattes-MI style). This is the registration's achieved metric read off as a standalone alignment score: higher = the warped-CT and MR intensities are better in-sync = better aligned; it collapses toward 0 when the CT is rotated/translated away from the MR.reg_wall_s— wall-clock registration time (excludes I/O + scoring).
Results (10 cases)
| pipeline | toolkit | registered | accepted | gap mid (med/mean) | gap std (med/mean) | s/reg (med) | tv-in-CT | MI (med/mean, nats) |
|---|---|---|---|---|---|---|---|---|
| antspyx | ANTsPy 0.6.3 | 9 | 4/10 (0.40) | 12.8 / 15.6 | 15.6 / 16.5 | 36.7 | 0.93 | 0.652 / 0.644 |
| elastix | ITK-Elastix 0.25.4 | 9 | 3/10 (0.30) | 13.6 / 15.7 | 14.8 / 17.0 | 2.6 | 0.93 | 0.641 / 0.640 |
| impact | Docker Elastix 5.2 + IMPACT | 9 | 4/10 (0.40) | 11.6 / 11.2 | 16.2 / 17.3 | 833.5 | 0.93 | 0.511 / 0.493 |
| simpleitk | SimpleITK 2.5.6 | 9 | 4/10 (0.40) | 14.4 / 16.6 | 17.9 / 17.5 | 9.7 | 0.93 | 0.651 / 0.641 |
| synthmorph | Docker SynthMorph (TensorFlow) | 9 | 2/10 (0.20) | 16.7 / 21.8 | 21.8 / 21.5 | 25.0 | 0.93 | 0.238 / 0.308 |
(antspyx and impact are stochastic (random metric/patch subsampling), so their accepted counts are one draw each — see Interpretation. impact's s/reg is ~14 min/case on GPU (full volumes), orders of magnitude above the intensity-metric tools. elastix, simpleitk and synthmorph are deterministic at fixed threads.)
Per-case (mid / std, OK = accepted):
| case | antspyx | elastix | impact | simpleitk | synthmorph |
|---|---|---|---|---|---|
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT |
| m6_22IRRKU6_20240115 | 24.6 / 28.6 | 24.0 / 28.3 | 22.7 / 27.0 | 26.3 / 29.6 | 10.4 / 23.7 |
| m6_23XIMO4M_20230222 | 22.2 / 23.9 | 22.3 / 23.5 | 9.1 / 16.2 | 21.9 / 23.2 | 20.3 / 23.3 |
| m6_24V62QQM_20230223 | 9.5 / 15.6 | 11.6 / 14.8 | 8.6 / 7.2 OK | 14.4 / 23.2 | 11.1 / 21.8 |
| m6_24V62QQM_20231017 | 11.7 / 7.3 OK | 12.2 / 7.4 OK | 4.6 / 6.6 OK | 11.7 / 7.5 OK | 11.5 / 8.4 OK |
| m6_24V62QQM_20240820 | 10.8 / 9.1 OK | 11.4 / 9.0 OK | 11.6 / 4.9 OK | 11.5 / 9.1 OK | 43.4 / 15.8 |
| m6_26TYQI3C_20221027 | 12.3 / 4.4 OK | 10.6 / 3.1 OK | 11.9 / 4.5 OK | 11.3 / 4.6 OK | 11.8 / 2.9 OK |
| m6_2BBN5XA4_20250411 | 19.1 / 7.7 OK | 18.2 / 14.1 | −1.7 / 29.3 | 18.8 / 7.5 OK | 52.4 / 53.0 |
| m6_2C4TKI6H_20240506 | 12.8 / 17.2 | 13.6 / 20.0 | 15.4 / 23.2 | 14.0 / 17.9 | 16.7 / 28.2 |
| m6_2CDNN7BH_20240102 | 17.5 / 34.6 | 17.6 / 32.8 | 18.9 / 37.1 | 19.3 / 35.0 | 18.9 / 16.8 |
Interpretation
- Translation is recovered well by all five (mid gaps mostly 4–16 mm,
within the
≤ 20 mmband, except the grossly-drifted cases).tv-in-CTis identical (~0.93): the warped GTV lands inside the CT head for every pipeline, so the target is in the right place. - The binding failure is rotation, and removing the head box makes it
harder. On the repositioned cases
std > 12 mm— the ~30° inter-session tilt produces local minima a single auto-start locks onto, and without the head-box crop the full-volume metric sees more neck/air confounders. Notably, the hardest tilt casem6_22IRRKU6is now rejected by all five (std 23–30 mm), which the head-box config got through with IMPACT. This is exactly why production02_build_m6_dataset.pyuses a multi-start tilt grid. - The easy cases are now accepted by nearly all pipelines.
m6_24V62QQM_20231017,m6_24V62QQM_20240820andm6_26TYQI3Care accepted by ANTs/Elastix/IMPACT/SimpleITK (SynthMorph accepts the two cleanest). The native automatic inits (center-of-gravity / center-of-mass) place the heads close enough that the optimizers converge on the well-positioned cases. - SimpleITK, ANTs and IMPACT tie at 4/10; Elastix 3/10; SynthMorph 2/10. ANTs is no longer the weakest — on the full volumes its native center-of-mass init works well (median std 15.6, up from the head-box+manual-pre-shift 31.7). It is stochastic (random MI subsampling), so the count varies by draw.
- IMPACT (semantic metric) is 4/10 but the slowest (~14 min/case on full
volumes) and lost its standout on the hardest tilt (
m6_22IRRKU6now mid 22.7 / std 27.0 vs mid 8.3 / std 13.2 with the head box). Matching anatomical semantics still gives it the best median mid (11.6) and a competitive MI (0.511), but the full-volume registration is a larger problem for the local ASGD. It also drifts grossly onm6_2BBN5XA4(mid −1.7, the transform flips to the far side). - Speed (median wall, 4 cases in parallel, 24 threads): Elastix ~2.6 s
(warm workers), SimpleITK ~9.7 s, ANTs ~36.7 s (CPU-bound, full volumes),
SynthMorph ~25 s (GPU: a single pretrained-model forward pass per case),
IMPACT ~834 s (GPU-bound: every ASGD iteration runs the TotalSegmentator
extractor on the sampled patches with backprop).
- Mattes MI (warped-CT ~ MR) is an independent, intensity-based confirmation: medians 0.651 (SimpleITK) / 0.641 (Elastix) / 0.511 (IMPACT) / 0.652 (ANTs) / 0.238 (SynthMorph). The intensity-metric tools cluster at ~0.64 (a genuine alignment signal), while SynthMorph's 0.238 reflects its tilt failures. A high MI means the CT skull/tissue intensities fall on the matching MR structures after the inverse transform.
- SynthMorph (deep, pretrained) is near-weakest of the five on these
cross-modality CT pairs. Its rigid model is trained on brain MRI
(T1/T2, anatomy-aware but intensity-domain MRI), so it recovers gross
translation well (mid 10–20 mm on most cases) but is repeatedly defeated by
the ~30° inter-session tilt: 7 of 9 cases have
std > 15 mm(rotation error) and two drift grossly (m6_2BBN5XA4mid 52 / std 53,m6_24V62QQM_20240820mid 43). It accepts the cleanm6_26TYQI3C(mid 11.8 / std 2.9, MI 0.60) andm6_24V62QQM_20231017(mid 11.5 / std 8.4) — where it is competitive with the intensity tools. Notably it needs no centroid init (it is a global deep method that recovers the gross offset on its own). Its low median MI (0.238) reflects the tilt failures. On same-modality MRI it would likely rank much higher; this CT benchmark is an out-of-distribution stress test for it. It is deterministic (one fixed pretrained model, no random subsampling), so unlike ANTs/IMPACT the per-case numbers are stable run to run. - Stochasticity: ANTs' Mattes-MI subsamples the image at random, so its per-case numbers carry run-to-run variance (this table is one draw); SimpleITK's MI (and registration) is deterministic at fixed thread count (RSGD wall time is thread-sensitive, convergence is not); the in-process Elastix ASGD is deterministic given its fixed seed. IMPACT is the most stochastic: its Jacobian mode draws random patches every iteration, so the final pose — and even which cases get accepted — changes run to run (two independent 10-case draws both accepted 3/10 but on different cases). Treat its per-case numbers as one sample from a distribution.
Reproducing
# main env — SimpleITK + Elastix + ANTsPy (all pip-installed here)
source /opt/conda/etc/profile.d/conda.sh && conda activate longitudinal-313
python scripts/bench_registration.py --pipelines simpleitk elastix antspyx \
--n-cases 10 --workers 4 --threads 24 --simpleitk-mode benchmark
# Docker pipelines (need the pulled image; see the SynthMorph / IMPACT
# sections below for the one-time setup)
python scripts/bench_registration.py --pipelines synthmorph --n-cases 10 \
--workers 4 --threads 24
python scripts/bench_registration.py --pipelines impact --n-cases 10 \
--workers 4 --threads 24
# combine the per-pipeline JSONs accumulated across envs into one table + plot
python scripts/bench_registration.py --merge
Other flags: --sample random --seed 0 (reproducible random 10),
--keys k1,k2,... (explicit cases), --n-cases N, --workers W,
--threads T, --simpleitk-mode {benchmark,production},
--synthmorph-model {rigid,affine}, --synthmorph-gpu <int|-1>,
--synthmorph-extent <int>, --impact-model <rel|abs>,
--impact-gpu <int|-1>, --impact-iters N, --impact-samples N,
--out-dir <dir>, --qa-images / --qa-out <dir> (visual QA, below).
Visual QA images (checkerboard + color overlay)
--qa-images writes a checkreg-style PNG per (pipeline, case) to
<out-dir>/qa/<pipeline>__<case>.png for eyeballing the alignment. Each image
is a 3×2 grid: rows are the three orthogonal mid-slices through the head
center (mid-axial, mid-coronal, mid-sagittal), and columns are:
- left — checkerboard blend: alternating blocks of the CT inverse-transformed into MR coordinates and the original MR; a clean registration gives a seamless mosaic, a misregistration shows hard edges at the block boundaries.
- right — additive color overlay: CT in red, MR in green. Structures that coincide blend to yellow; rotation/translation error appears as red/green fringing at the skull and brain edges.
The CT is shown in a brain window (L40/W80 = 0–80 HU) so the brain parenchyma is visible and comparable to the MR (the skull, ~800+ HU, clips to white); the MR uses a 2–98 % head window. The coronal and sagittal views are oriented superior-up to match the axial view.
The title carries the pipeline, case key, Mattes MI, and the skull-ring gap (mid/std), so a glance tells you both the achieved-metric score and whether the visuals agree. Generate for the available pipelines with:
source /opt/conda/etc/profile.d/conda.sh && conda activate longitudinal-313
python scripts/bench_registration.py \
--pipelines simpleitk elastix antspyx --n-cases 10 --qa-images
python scripts/bench_registration.py \
--pipelines synthmorph --n-cases 10 --qa-images
python scripts/bench_registration.py \
--pipelines impact --n-cases 10 --qa-images
(9 images each; the incomplete-CT case is skipped.)
SynthMorph pipeline (Docker) — one-time setup
The synthmorph pipeline runs the freesurfer/synthmorph
image: SynthMorph (Hoffmann et al., IEEE TMI 2022), an
anatomy-aware, acquisition-agnostic deep registration (TensorFlow
VxmAffineFeatureDetector with the pretrained synthmorph.rigid.1.h5 weights).
No skull-stripping or intensity preprocessing is needed: each volume is
normalized to an isotropic 1-mm LIA network space internally. It is the
second Docker-based pipeline and the only deep-learning one.
Prerequisites (check with RP.availability(); a missing piece raises
PipelineUnavailable with the exact command):
# 1. Docker (with NVIDIA container toolkit so --gpus works)
docker --version
# 2. Pull the image (~11 GB, one-time)
docker pull freesurfer/synthmorph
No extra model assets: the rigid weights ship inside the image
(/freesurfer/models).
How the adapter drives the image
- Rigid matrix model, no init —
register -m rigidon GPU when one is visible (auto-spread over the host GPUs by pid;--synthmorph-gpu -1forces CPU). SynthMorph is a global deep method, so it recovers the gross inter-session offset on its own and needs no initial transform. - Read-back — the
-TLTA (aLINEAR_RAS_TO_RAS4×4 in shared world space; with fixed=CT/moving=T1c the-Tmatrix is the T1c→CT contract transform) is parsed into a SimpleITKAffineTransformand run through the usualfix_orientationgate. - GPU sharing — the container is run as the host uid (clean scratch
removal, TF still reaches the GPU) with
TF_FORCE_GPU_ALLOW_GROWTHset so concurrent benchmark workers can share a GPU.
IMPACT pipeline (Docker) — one-time setup
The impact pipeline runs the vboussot/elastix_impact
image: Elastix 5.2.0 with the IMPACT
semantic similarity metric compiled in as a native Elastix metric, plus a
TorchScript TotalSegmentator feature extractor. It is GPU-based and
Docker-based (alongside synthmorph). It is also the one pipeline that keeps
fixed = T1c, moving = CT (the reverse of the others): the CT FOV
(~300×300×251 mm) is much larger than the T1c crop (~220×220×150 mm), so with
CT as the fixed image too many CT samples map outside the T1c buffer and the
IMPACT metric aborts ("too many samples map outside moving image buffer").
Prerequisites (check with RP.availability(); a missing piece raises
PipelineUnavailable with the exact command):
# 1. Docker (with NVIDIA container toolkit so --gpus works)
docker --version
# 2. Pull the image (~23.5 GB, one-time)
docker pull vboussot/elastix_impact
# 3. Fetch the TorchScript model (~123 MB, one-time) into the repo's model
# cache (data/impact_models/). M730.pt is the recommended TotalSegmentator.
mkdir -p /mnt/t24/Public/xfr/longitudinal/impact_models/TS
curl -L -o /mnt/t24/Public/xfr/longitudinal/impact_models/TS/M730.pt \
https://huggingface.co/VBoussot/impact-torchscript-models/resolve/main/TS/M730.pt
Other models (M258/M291/M850, MIND, SAM2.1, …) are selectable with
--impact-model <HF-relative-path-or-abs>; see the
impact-torchscript-models
repo for the full list and FOV/layer tables.
How the adapter drives the image
The image's CMD.sh is a generic launcher, so the adapter calls the in-image
binary directly (/lib/elastix-install/bin/elastix):
- Full volumes — fixed=T1c and moving=CT are used as-is (no head box). T1c must be the fixed image: the CT FOV is much larger, so with CT fixed too many CT samples map outside the T1c buffer and the metric aborts.
- Auto init (no
-t0) — the parameter map setsAutomaticTransformInitialization "true"with methodCenterOfGravity, so elastix aligns the intensity centroids;AutomaticScalesEstimationsets the optimizer scales. - Parameter map — rigid
EulerTransform(6-DOF),Impactmetric in Jacobian mode (differentiable, per the recommended config), 4 semantic scalesImpactVoxelSize6/3/1.5/1 mm (scaled to the T1c base spacing) with no image resampling (all-1 pyramid schedules, as the IMPACT docs require), ASGD,Randomsampler. GPU auto-spread across the host GPUs by pid (--impact-gpu -1forces CPU). - Read-back — the result
TransformParameters.0.itk.txtis anEuler3DTransformin the world physical frame (theAutomaticTransformInitializationis folded in by elastix); the adapter runs it through the usualfix_orientationgate.
Notes / caveats
- The benchmark compares each toolkit in its native configuration: all
register on the full volumes (no head box) and use each toolkit's native
automatic transform initialization (no manual
centroid_offset) — elastix / impactAutomaticTransformInitialization(CenterOfGravity), antspyx's center-of-mass alignment, simpleitk identity, synthmorph none. Production uses--simpleitk-mode production's multi-start tilt grid, which raises SimpleITK's acceptance at ~20× the cost; that is out of scope for the head-to-head but is the config the dataset actually ships with. synthmorphis deterministic (one fixed pretrained model, no random subsampling), so its per-case numbers are stable run to run — unlike ANTs / IMPACT. Its low acceptance (2/10) is a genuine cross-modality / tilt weakness, not variance.fix_orientation(elastix/ants/synthmorph/impact) chooses between a transform and its inverse by skull-ring concentricity — a correctness gate that makes the result independent of each toolkit's transform-direction convention. With the fixed=CT/moving=T1c swap,elastix's combination transform is now the inverse of the contract, so it reportsauto_inverted: True(the gate corrects it); antspyx/synthmorph/impact return the contract direction directly (auto_inverted: False).- SynthMorph specifics (Docker
freesurfer/synthmorph, TensorFlow, GPU):- Cross-modality / tilt weakness. The
rigidmodel is trained on brain MRI, so on these CT↔T1c pairs it recovers translation but is repeatedly defeated by the ~30° inter-session tilt (7/9 cases havestd > 15 mm). It is a legitimate data point for how far a pretrained MRI deep model generalizes out-of-domain to CT — not a bug. - No centroid init needed. Being a global deep method it recovers the
gross offset on its own; the
-iinitial-transform path is not used (which also sidesteps this image's float64/float32 bug on the-icode path). - GPU sharing. The container runs as the host uid with
TF_FORCE_GPU_ALLOW_GROWTHso concurrent benchmark workers share a GPU. - ~25 s/case (GPU: one pretrained forward pass), between SimpleITK (~10 s) / ANTs (~37 s) and IMPACT (~834 s).
- Cross-modality / tilt weakness. The
- Elastix specifics (ITK-Elastix 0.25.4, elastix 5.3.1, in-process python
API — no CLI binary involved):
- Elastix 5.x renamed components vs the classic parameter files: the rigid
3D transform is
EulerTransform(6-DOF,Rigid3DTransformis gone from the component set), the metric key isMetric(notImageMetric), the random sampler isRandom(notRandomSampler). The adapter uses the 5.x names and is not compatible with elastix ≤ 4.x parameter files. AutomaticTransformInitialization "true"(CenterOfGravity) provides the start (no manualcentroid_offset);AutomaticScalesEstimationsets the optimizer scales. An output directory is still set (SetOutputDirectory) for the C++ filter to run; the adapter uses a scratch dir underdata/m6reg/elastix/itkelastix/.WriteResultImagemust be the string"false"(PythonFalse); the default (absent) makes the filter raise "Could not read result image" after a successful registration.- With fixed=CT/moving=T1c the combination transform is CT→T1c (the inverse
of the contract), so
fix_orientationreportsauto_inverted: Trueand inverts it. The result is recovered by point-mapping (GetCombinationTransform().TransformPoint) into an affine — independent of elastix's Euler-angle parameterization — and wrapped in a SimpleITKAffineTransform(3). - Registration wall time is ~2.4 s/case median at full budget (ASGD 500/500/250 over 3 resolutions).
- Elastix 5.x renamed components vs the classic parameter files: the rigid
3D transform is
- IMPACT specifics (Docker
vboussot/elastix_impact, Elastix 5.2.0, GPU, Jacobian mode):- Slow, by design. Every ASGD iteration runs the TotalSegmentator
extractor on the sampled patches with backpropagation, so the recommended
500-iter / 2000-sample budget is ~14 min/case on full volumes (vs 2–37 s
for the other tools).
--impact-iters/--impact-samplestrade accuracy for speed. - Keeps fixed=T1c/moving=CT. IMPACT is the one pipeline that does NOT follow the shared fixed=CT/moving=T1c convention: the CT FOV (~300×300×251 mm) is much larger than the T1c crop (~220×220×150 mm), so with CT as the fixed image too many CT samples map outside the T1c buffer and the metric aborts ("too many samples map outside moving image buffer"). The returned transform still satisfies the contract (T1c→CT).
- Auto init.
AutomaticTransformInitialization(CenterOfGravity) aligns the intensity centroids; no manual-t0translation is used. - Rugged single-init convergence. The semantic landscape has more local
minima than Mattes-MI: on full volumes it no longer nails the hardest
repositioned case (
m6_22IRRKU6, now mid 22.7 / std 27.0 — the head box used to get it to mid 8.3 / std 13.2). A multi-start or higher-iteration budget would likely raise its acceptance; that is out of scope for the single-init head-to-head. - Stochastic. Jacobian mode resamples patches each iteration, so results vary run to run (see Stochasticity).
- Elastix 5.x differences vs the in-process
elastixpipeline: the image bundles 5.2.0, so the in-file multi-Transformchain andUseScalingFactorsare gone. The result is read back as anEuler3DTransform(.itk.txt). The image's ownCMD.shis a generic launcher, so the adapter invokes/lib/elastix-install/bin/elastixdirectly. - Models are not in the image. The TorchScript model(s) must be present
under the mounted
/Data/Models(the repo caches them indata/impact_models/, bind-mounted read-only). - CPU fallback:
--impact-gpu -1runs without--gpusand withImpactGPU -1/ mixed precision off (much slower).
- Slow, by design. Every ASGD iteration runs the TotalSegmentator
extractor on the sampled patches with backpropagation, so the recommended
500-iter / 2000-sample budget is ~14 min/case on full volumes (vs 2–37 s
for the other tools).