feat(docs): update README and benchmark documentation for IMPACT pipeline details
This commit is contained in:
parent
7ac9979c62
commit
6b85e950e5
4 changed files with 175 additions and 67 deletions
|
|
@ -44,6 +44,8 @@ data/
|
|||
pseudo/roundK/ in-house pseudo-labels (rows.jsonl, masks, summary.json)
|
||||
pseudo_nnu/roundK/ nnU-Net pseudo-labels (rows.jsonl, masks, summary.json)
|
||||
pseudo_monai/roundK/ MONAI pseudo-labels (rows.jsonl, masks, summary.json)
|
||||
impact_models/ IMPACT TorchScript feature-extractor cache (registration
|
||||
`impact` pipeline, see REGISTRATION_BENCHMARK.md)
|
||||
src/ U-Net, dataset, losses, training/eval helpers
|
||||
scripts/ Pipeline A (in-house 3D U-Net)
|
||||
scripts_nnu/ Pipeline B (nnU-Net)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Registration Pipeline Benchmark — CT → T1c (M6)
|
||||
|
||||
Benchmarks **four rigid, cross-modality registration pipelines** that align a
|
||||
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
|
||||
|
|
@ -34,8 +34,9 @@ handling — not on who finds the ~30–60 mm gross offset first.
|
|||
|---|----------|---------|--------|-----------|--------------------|
|
||||
| 1 | `elastix` | ITK-Elastix 0.25.4 (pip `itk` + `itk-elastix`, elastix 5.3.1) | Mattes MI (10 bins) | `EulerTransform` rigid (5.x replacement for `Rigid3DTransform`), ASGD, 3 resolutions | ✅ ran (main env, in-process python API — no CLI) |
|
||||
| 2 | `simpleitk` | SimpleITK 2.5.6 | Mattes MI (32 bins) | `Euler3D` rigid, pyramid `[4,2,1]` | ✅ ran |
|
||||
| 3 | `antspyx` | ANTsPy / ANTs 0.6.3 | Mattes MI (multi-res) | ANTs `Rigid` | ✅ ran (isolated py3.12 env) |
|
||||
| 3 | `antspyx` | ANTsPy / ANTs 0.6.3 | Mattes MI (multi-res) | ANTs `Rigid` | ✅ ran (main env; stochastic MI subsampling) |
|
||||
| 4 | `deepreg` | Oxford DeepReg (CycleGAN) | CycleGAN + reg. loss (pretrained) | 3D displacement field | **not installed** (see below) |
|
||||
| 5 | `impact` | Docker `vboussot/elastix_impact` (Elastix 5.2.0 + IMPACT) | **IMPACT semantic** (TotalSegmentator `TS/M730.pt` features, L2, Jacobian/GPU) | `EulerTransform` rigid, ASGD, 4 scales (no image resampling) | ✅ ran (main env, Docker + GPU) |
|
||||
|
||||
`simpleitk` has two budgets: `--simpleitk-mode benchmark` (single pyramid,
|
||||
centered start — the default, comparable to the other tools) and
|
||||
|
|
@ -85,75 +86,97 @@ Production QA, crop-independent:
|
|||
| pipeline | toolkit | registered | accepted | gap mid (med/mean) | gap std (med/mean) | s/reg (med) | tv-in-CT | MI (med/mean, nats) |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| **simpleitk** | SimpleITK 2.5.6 | 9 | **4/10 (0.40)** | 12.6 / 16.1 | 15.4 / 16.0 | 8.6 | 0.93 | **0.643 / 0.630** |
|
||||
| **antspyx** | ANTsPy 0.6.3 | 9 | **1/10 (0.10)** | 14.2 / 17.3 | 24.8 / 22.4 | 12.7 | 0.93 | **0.600 / 0.523** |
|
||||
| **antspyx** | ANTsPy 0.6.3 | 9 | **0/10 (0.00)** | 14.9 / 16.5 | 18.4 / 22.7 | 14.5 | 0.93 | **0.598 / 0.529** |
|
||||
| **elastix** | ITK-Elastix 0.25.4 | 9 | **5/10 (0.50)** | 15.3 / 16.1 | 9.9 / 13.8 | 3.2 | 0.93 | **0.562 / 0.549** |
|
||||
| **impact** | Docker Elastix 5.2 + IMPACT | 9 | **3/10 (0.30)** | 12.4 / 19.0 | 15.1 / 16.7 | **562.3** | 0.93 | **0.475 / 0.448** |
|
||||
| deepreg | — | — | — | — | — | — | — | — |
|
||||
|
||||
*(antspyx and impact are stochastic (random metric/patch subsampling), so their
|
||||
accepted counts are one draw each — see Interpretation. impact's s/reg is ~9.4
|
||||
min/case on GPU, orders of magnitude above the intensity-metric tools.)*
|
||||
|
||||
Per-case (mid / std, `OK` = accepted):
|
||||
|
||||
| case | simpleitk | antspyx | elastix |
|
||||
|---|---|---|---|
|
||||
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT |
|
||||
| m6_22IRRKU6_20240115 | 25.7 / 29.3 | 26.3 / 29.2 | 26.4 / 30.3 |
|
||||
| m6_23XIMO4M_20230222 | 22.2 / 23.9 | 23.8 / 25.0 | 21.1 / 23.3 |
|
||||
| m6_24V62QQM_20230223 | 10.7 / 16.9 | 9.4 / 18.2 | 10.6 / 8.2 **OK** |
|
||||
| m6_24V62QQM_20231017 | 11.5 / 7.3 **OK** | 12.5 / 12.8 | 11.0 / 6.1 **OK** |
|
||||
| m6_24V62QQM_20240820 | 11.6 / 9.6 **OK** | 14.8 / 11.7 **OK** | 15.3 / 14.0 |
|
||||
| m6_26TYQI3C_20221027 | 12.2 / 3.7 **OK** | 6.1 / 37.2 | 11.4 / 3.2 **OK** |
|
||||
| m6_2BBN5XA4_20250411 | 18.6 / 8.0 **OK** | 14.3 / 12.7 | 18.1 / 9.9 **OK** |
|
||||
| m6_2C4TKI6H_20240506 | 12.6 / 15.4 | 13.6 / 14.7 | 12.0 / 6.8 **OK** |
|
||||
| m6_2CDNN7BH_20240102 | 19.4 / 30.2 | 18.2 / 33.6 | 18.9 / 22.7 |
|
||||
| case | simpleitk | antspyx | elastix | impact |
|
||||
|---|---|---|---|---|
|
||||
| m6_22GD5VL2_20230724 | incomplete-CT | incomplete-CT | incomplete-CT | incomplete-CT |
|
||||
| m6_22IRRKU6_20240115 | 25.7 / 29.3 | 24.4 / 27.7 | 26.4 / 30.3 | **8.4 / 12.6** |
|
||||
| m6_23XIMO4M_20230222 | 22.2 / 23.9 | 23.4 / 24.8 | 21.1 / 23.3 | 21.4 / 24.1 |
|
||||
| m6_24V62QQM_20230223 | 10.7 / 16.9 | 9.5 / 18.4 | 10.6 / 8.2 **OK** | 10.0 / 8.3 **OK** |
|
||||
| m6_24V62QQM_20231017 | 11.5 / 7.3 **OK** | 14.9 / 15.5 | 11.0 / 6.1 **OK** | 11.1 / 8.8 **OK** |
|
||||
| m6_24V62QQM_20240820 | 11.6 / 9.6 **OK** | 14.3 / 17.5 | 15.3 / 14.0 | 54.4 / 15.1 |
|
||||
| m6_26TYQI3C_20221027 | 12.2 / 3.7 **OK** | 17.7 / 37.6 | 11.4 / 3.2 **OK** | 23.9 / 22.6 |
|
||||
| m6_2BBN5XA4_20250411 | 18.6 / 8.0 **OK** | 14.3 / 12.7 | 18.1 / 9.9 **OK** | 10.1 / 5.9 **OK** |
|
||||
| m6_2C4TKI6H_20240506 | 12.6 / 15.4 | 13.1 / 14.3 | 12.0 / 6.8 **OK** | 12.4 / 22.3 |
|
||||
| m6_2CDNN7BH_20240102 | 19.4 / 30.2 | 17.2 / 35.4 | 18.9 / 22.7 | 19.6 / 30.3 |
|
||||
|
||||
### Interpretation
|
||||
|
||||
- **Translation is recovered well by all three** (mid gaps mostly 6–20 mm,
|
||||
- **Translation is recovered well by all four** (mid gaps mostly 6–20 mm,
|
||||
within the `≤ 20 mm` band). `tv-in-CT` is 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** — `std > 12 mm` on the repositioned cases
|
||||
(the ~30° inter-session tilt produces MI local minima that a single centered
|
||||
(the ~30° inter-session tilt produces local minima that a single centered
|
||||
start can lock onto). This is exactly why production `02_build_m6_dataset.py`
|
||||
uses a **multi-start tilt grid**.
|
||||
- **Elastix is the most accurate of the three standard single-init configs**
|
||||
(5/10 accepted, lowest median std 9.9 vs SimpleITK 15.4 and ANTs 24.8) and
|
||||
- **Elastix is the most accurate of the standard single-init configs**
|
||||
(5/10 accepted, lowest median std 9.9 vs SimpleITK 15.4 and ANTs 18.4) and
|
||||
the fastest (3.2 s/case median wall, ~1.8 s pure registration vs 8.6 s
|
||||
SimpleITK / 12.7 s ANTs): ASGD's adaptive step sizes climb out of small
|
||||
SimpleITK / 14.5 s ANTs): ASGD's adaptive step sizes climb out of small
|
||||
misalignments better than RSGD/ANTs on these boxes. It accepts `m6_24V62QQM_20230223`
|
||||
(std 8.2) and `m6_2C4TKI6H` (std 6.8), which the other two tools fail on
|
||||
(std 16.9 / 15.4), and misses `m6_24V62QQM_20240820` (std 14.0) that both
|
||||
others accept — different optimizers settle in different local minima on the
|
||||
hard tilted cases.
|
||||
- **ANTs' Rigid is the weakest** (1/10 accepted): it frequently locks onto the
|
||||
tilted local minimum. Note `m6_26TYQI3C`: ANTs nailed the position (mid 6.1)
|
||||
but missed the orientation (std 37.2) — a textbook tilted local minimum.
|
||||
- **ANTs' Rigid is the weakest of the intensity-metric tools** (0/10 in this
|
||||
draw; it is stochastic, so a prior draw gave 1/10): it frequently locks onto
|
||||
the tilted local minimum. Note `m6_26TYQI3C`: ANTs nailed the position (mid
|
||||
17.7) but missed the orientation (std 37.6) — a textbook tilted local minimum.
|
||||
- **IMPACT (semantic metric) is the standout on the hardest repositioned case**
|
||||
and the least reliable overall. On `m6_22IRRKU6` (the ~30° tilt every
|
||||
intensity tool fails, std 28–30 mm) it gets **mid 8.4 / std 12.6** — the best
|
||||
translation of any pipeline and within 0.6 mm of the acceptance threshold —
|
||||
because matching *anatomical semantics* (TotalSegmentator features) is far
|
||||
more discriminating for gross pose than raw cross-modality intensity. But the
|
||||
semantic landscape is rugged: single-init ASGD lands in bad local minima on
|
||||
4 of 9 cases (std 15–30 mm), and one (`m6_24V62QQM_20240820`) even drifts the
|
||||
translation (mid 54 mm). Net 3/10 accepted — between ANTs and SimpleITK — at
|
||||
~9.4 min/case, two orders of magnitude slower than the intensity tools.
|
||||
- **Speed** (median wall, 4 cases in parallel, 24 threads): Elastix ~3.2 s
|
||||
(warm workers; a worker's first case pays a ~10 s warm-up), SimpleITK ~8.6 s,
|
||||
ANTs ~12.7 s (includes the pre-shift resample + its affine-MI init stages).
|
||||
All CPU-bound.
|
||||
ANTs ~14.5 s (CPU-bound), **IMPACT ~562 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.643 (SimpleITK) / 0.600 (ANTs) / 0.562 (Elastix), and it diverges
|
||||
sharply on the tilted cases — ANTs `m6_26TYQI3C` drops to 0.094 nats
|
||||
(rotation ~37 mm off) while SimpleITK holds 0.670. A high MI means the CT
|
||||
skull/tissue intensities fall on the matching MR structures after the inverse
|
||||
transform.
|
||||
medians 0.643 (SimpleITK) / 0.598 (ANTs) / 0.562 (Elastix) / 0.475 (IMPACT),
|
||||
and it diverges sharply on the tilted cases — ANTs `m6_26TYQI3C` drops to
|
||||
~0.1 nats (rotation ~38 mm off) while SimpleITK holds 0.670. A high MI means
|
||||
the CT skull/tissue intensities fall on the matching MR structures after the
|
||||
inverse transform. IMPACT's lower median MI is consistent with its less
|
||||
reliable single-init convergence.
|
||||
- **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); Elastix's ASGD is
|
||||
deterministic given its fixed seed.
|
||||
(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
|
||||
|
||||
```bash
|
||||
# main env — SimpleITK + Elastix (itk-elastix is pip-installed here;
|
||||
# antspyx/deepreg report unavailable and are recorded as such)
|
||||
# main env — SimpleITK + Elastix + ANTsPy (all pip-installed here); deepreg
|
||||
# reports unavailable and is recorded as such
|
||||
source /opt/conda/etc/profile.d/conda.sh && conda activate longitudinal-313
|
||||
python scripts/bench_registration.py --n-cases 10 \
|
||||
--workers 4 --threads 24 --simpleitk-mode benchmark
|
||||
|
||||
# isolated py3.12 env with antspyx — ANTs (+ optional SimpleITK cross-check)
|
||||
/home/xfr/.conda/envs/ants312/bin/python scripts/bench_registration.py \
|
||||
--pipelines antspyx --n-cases 10 --workers 4 --threads 24
|
||||
# IMPACT (needs Docker + the pulled image + a model; see the Impact section
|
||||
# below for the one-time setup)
|
||||
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
|
||||
|
|
@ -162,8 +185,9 @@ 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}`,
|
||||
`--deepreg-ckpt <.ckpt>` (DeepReg), `--out-dir <dir>`,
|
||||
`--qa-images` / `--qa-out <dir>` (visual QA, below).
|
||||
`--deepreg-ckpt <.ckpt>` (DeepReg), `--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)
|
||||
|
||||
|
|
@ -189,10 +213,11 @@ The title carries the pipeline, case key, Mattes MI, and the skull-ring gap
|
|||
visuals agree. Generate for the available pipelines with:
|
||||
|
||||
```bash
|
||||
/home/xfr/.conda/envs/longitudinal-313/bin/python scripts/bench_registration.py \
|
||||
--pipelines simpleitk elastix --n-cases 10 --qa-images
|
||||
/home/xfr/.conda/envs/ants312/bin/python scripts/bench_registration.py \
|
||||
--pipelines antspyx --n-cases 10 --qa-images
|
||||
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 impact --n-cases 10 --qa-images
|
||||
```
|
||||
|
||||
(9 images each; the incomplete-CT case is skipped.)
|
||||
|
|
@ -213,6 +238,61 @@ python scripts/bench_registration.py --pipelines deepreg --n-cases 10 \
|
|||
The adapter runs `deepreg test` to predict a voxel displacement field, converts
|
||||
it to a SimpleITK `DisplacementFieldTransform`, and scores it like the others.
|
||||
|
||||
## IMPACT pipeline (Docker) — one-time setup
|
||||
|
||||
The `impact` pipeline runs the [`vboussot/elastix_impact`](https://hub.docker.com/r/vboussot/elastix_impact)
|
||||
image: **Elastix 5.2.0** with the [IMPACT](https://arxiv.org/abs/2503.24121)
|
||||
*semantic* similarity metric compiled in as a native Elastix metric, plus a
|
||||
TorchScript **TotalSegmentator** feature extractor. It is the only pipeline that
|
||||
is GPU-based and the only one that is Docker-based.
|
||||
|
||||
Prerequisites (check with `RP.availability()`; a missing piece raises
|
||||
`PipelineUnavailable` with the exact command):
|
||||
|
||||
```bash
|
||||
# 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](https://huggingface.co/VBoussot/impact-torchscript-models)
|
||||
repo for the full list and FOV/layer tables.
|
||||
|
||||
### How the adapter drives the image
|
||||
|
||||
The image's `CMD.sh` only runs `elastix -f -m -p -out` (no `-t0`), so the
|
||||
adapter calls the in-image binary directly (`/lib/elastix-install/bin/elastix`)
|
||||
to add the shared centered start:
|
||||
|
||||
1. **Head box** — resamples fixed=T1c and moving=CT into the same CT-space head
|
||||
box (physical frame) the other pipelines use, so MI/IMPACT is computed on
|
||||
head tissue, not neck/air.
|
||||
2. **Shared init via `-t0`** — the `centroid_offset` translation is written to a
|
||||
`TranslationTransform` parameter file and passed with `-t0`; with
|
||||
`HowToCombineTransforms "Compose"` the final transform is
|
||||
`T = T_euler(T_init(p))`. (Elastix 5.x removed in-file multi-`Transform`
|
||||
chains and `UseScalingFactors`, so the initial transform must be a separate
|
||||
`-t0` file.)
|
||||
3. **Parameter map** — rigid `EulerTransform` (6-DOF), `Impact` metric in
|
||||
**Jacobian** mode (differentiable, per the recommended config), 4 semantic
|
||||
scales `ImpactVoxelSize` 6/3/1.5/1 mm (scaled to the box's base spacing) with
|
||||
**no image resampling** (all-1 pyramid schedules, as the IMPACT docs require),
|
||||
ASGD, `Random` sampler. GPU auto-spread across the host GPUs by pid
|
||||
(`--impact-gpu -1` forces CPU).
|
||||
4. **Read-back** — the result `TransformParameters.0.itk.txt` is an
|
||||
`Euler3DTransform` in the box/world physical frame; the adapter composes it
|
||||
with the `-t0` translation and runs the usual `fix_orientation` gate.
|
||||
|
||||
## Notes / caveats
|
||||
|
||||
- The benchmark compares **standard single-init** configurations (centered
|
||||
|
|
@ -221,11 +301,11 @@ it to a SimpleITK `DisplacementFieldTransform`, and scores it like the others.
|
|||
the head-to-head but is the config the dataset actually ships with.
|
||||
- `deepreg` could not be executed here, so its row is "not installed", not
|
||||
measured; its code path is implemented and unit-verified for import/convention.
|
||||
- `fix_orientation` (elastix/ants/deepreg) 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. In practice it
|
||||
never fired (all `auto_inverted: False`): elastix/ants returned the T1c→CT
|
||||
direction as expected.
|
||||
- `fix_orientation` (elastix/ants/deepreg/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. In
|
||||
practice it never fired (all `auto_inverted: False`): elastix/ants/impact
|
||||
returned the T1c→CT direction as expected.
|
||||
- **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
|
||||
|
|
@ -247,4 +327,28 @@ it to a SimpleITK `DisplacementFieldTransform`, and scores it like the others.
|
|||
`AffineTransform(3)`.
|
||||
- Registration wall time is ~1.8 s/case at full budget (ASGD 500/500/250
|
||||
over 3 resolutions); each worker's first case pays a ~10 s warm-up, so the
|
||||
median wall is 3.2 s and the mean is inflated by warm-up.
|
||||
median wall is 3.2 s and the mean is inflated by warm-up.
|
||||
- **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 ~9 min/case (vs 3–15 s for the intensity
|
||||
tools). `--impact-iters` / `--impact-samples` trade accuracy for speed.
|
||||
- **Rugged single-init convergence.** The semantic landscape has more local
|
||||
minima than Mattes-MI: the pipeline nails the hardest repositioned case
|
||||
(`m6_22IRRKU6`, mid 8.4 mm — best of all) but lands in bad minima on some
|
||||
others. 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 `elastix` pipeline:** the image
|
||||
bundles 5.2.0, so the in-file multi-`Transform` chain and `UseScalingFactors`
|
||||
are gone — the shared centered start is a separate `-t0` translation file,
|
||||
and the result is read back as an `Euler3DTransform` (`.itk.txt`) and
|
||||
composed with `-t0`. The image's own `CMD.sh` doesn't pass `-t0`, so the
|
||||
adapter invokes `/lib/elastix-install/bin/elastix` directly.
|
||||
- **Models are not in the image.** The TorchScript model(s) must be present
|
||||
under the mounted `/Data/Models` (the repo caches them in
|
||||
`data/impact_models/`, bind-mounted read-only).
|
||||
- **CPU fallback:** `--impact-gpu -1` runs without `--gpus` and with
|
||||
`ImpactGPU -1` / mixed precision off (much slower).
|
||||
|
|
@ -28,15 +28,17 @@ see REGISTRATION_BENCHMARK.md). The `--merge` mode then combines
|
|||
the per-pipeline JSON files (accumulated across envs) into one summary + plot.
|
||||
|
||||
Usage (repo root):
|
||||
# main env (SimpleITK + elastix; impact needs Docker + the image; deepreg unavailable)
|
||||
# main env (SimpleITK + elastix + antspyx; impact needs Docker + the image;
|
||||
# deepreg unavailable)
|
||||
source /opt/conda/etc/profile.d/conda.sh && conda activate longitudinal-313
|
||||
python scripts/bench_registration.py --pipelines simpleitk elastix impact
|
||||
python scripts/bench_registration.py --pipelines simpleitk elastix antspyx
|
||||
|
||||
# isolated env that has antspyx (see REGISTRATION_BENCHMARK.md)
|
||||
source /opt/conda/etc/profile.d/conda.sh && conda activate <ants312>
|
||||
python scripts/bench_registration.py --pipelines antspyx simpleitk
|
||||
# impact (GPU, Docker) — one-time setup in REGISTRATION_BENCHMARK.md
|
||||
python scripts/bench_registration.py --pipelines impact
|
||||
|
||||
# combine everything produced so far into one table + plot
|
||||
# any pipeline that is not importable in the current env is recorded as
|
||||
# unavailable, so the script is env-portable; --merge combines the
|
||||
# per-pipeline JSONs accumulated across envs into one table + plot
|
||||
python scripts/bench_registration.py --merge
|
||||
"""
|
||||
import argparse
|
||||
|
|
@ -482,12 +484,12 @@ def parse_args():
|
|||
ap.add_argument("--impact-gpu", type=int, default=None,
|
||||
help="GPU device index for IMPACT (default: auto-spread over "
|
||||
"the host GPUs by pid; -1 forces CPU)")
|
||||
ap.add_argument("--impact-iters", type=int, default=400,
|
||||
help="IMPACT per-resolution max iterations (default 400, a converged "
|
||||
"rigid budget; the authors' full recommended is 500)")
|
||||
ap.add_argument("--impact-samples", type=int, default=1500,
|
||||
help="IMPACT random spatial samples per iteration (default 1500; "
|
||||
"full recommended 2000)")
|
||||
ap.add_argument("--impact-iters", type=int, default=500,
|
||||
help="IMPACT per-resolution max iterations (default 500, the authors' "
|
||||
"recommended Jacobian budget; lower to trade accuracy for speed)")
|
||||
ap.add_argument("--impact-samples", type=int, default=2000,
|
||||
help="IMPACT random spatial samples per iteration (default 2000, "
|
||||
"the recommended budget)")
|
||||
ap.add_argument("--qa-images", action="store_true",
|
||||
help="also write a checkreg-style PNG (checkerboard blend + color "
|
||||
"overlay of the inverse-warped CT vs the original MR) per case")
|
||||
|
|
|
|||
|
|
@ -746,7 +746,7 @@ def _impact_param_map(model_cont_path, gpu, iters, samples, voxel_sizes,
|
|||
return "\n".join(L) + "\n"
|
||||
|
||||
|
||||
def register_impact(ct, t1c, model=None, gpu=None, iters=400, samples=1500,
|
||||
def register_impact(ct, t1c, model=None, gpu=None, iters=500, samples=2000,
|
||||
threads=12, keep_scratch=False, scales=(6.0, 3.0, 1.5, 1.0),
|
||||
downsample=False):
|
||||
"""IMPACT rigid CT->T1c (fixed=T1c, moving=CT) in the Docker image
|
||||
|
|
@ -767,11 +767,11 @@ def register_impact(ct, t1c, model=None, gpu=None, iters=400, samples=1500,
|
|||
Compose`` the final transform = ``T_main(T_init(p))``),
|
||||
- uses a rigid 6-DOF ``EulerTransform`` (the 5.x name for the classic
|
||||
3D rigid), ASGD, a 4-level scale pyramid (no image resampling).
|
||||
Budget: the default ``iters=400`` / ``samples=1500`` is a *converged rigid*
|
||||
budget (measured: recovers the inter-session tilt the intensity-metric
|
||||
pipelines miss). The authors' fully-recommended Jacobian budget is
|
||||
``iters=500`` / ``samples=2000`` (deformable-oriented, ~25% slower); pass
|
||||
those for maximum fidelity. Expect minutes per case (GPU), not seconds.
|
||||
Budget: the default ``iters=500`` / ``samples=2000`` is the authors'
|
||||
recommended Jacobian budget. It is *much* slower than the intensity-metric
|
||||
pipelines (minutes per case on GPU vs seconds) because every iteration runs
|
||||
the TotalSegmentator feature extractor on the sampled patches with
|
||||
backpropagation. Lower ``iters``/``samples`` to trade accuracy for speed.
|
||||
The result is read back from ``TransformParameters.0.itk.txt``
|
||||
(an ``Euler3DTransform`` in the box/world physical frame) and composed with
|
||||
the ``-t0`` translation, then checked by :func:`fix_orientation`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue