Efficient Rigid Ultrasound Slice-to-Volume Registration via State-Space Correlation and Closed-Form Pose Estimation
Niklas Schwarz1 · Jens Kleesiek1,2,3,4,5,6 · Moritz Rempe1,2,7
1Institute for AI in Medicine (IKIM), University Hospital Essen 2Cancer Research Center Cologne Essen (CCCE) 3RACOON Study Group, Site Essen 4Department of Physics, TU Dortmund 5German Cancer Consortium (DKTK), Partner Site Essen 6University of Duisburg-Essen 7Brigham and Women's Hospital, Harvard Medical School
Accepted at WACV 2027
Paper | Setup | Data | Checkpoints | Method | Results | Citation
SCoPE-Reg localizes an untracked 2D ultrasound frame inside a 3D reference volume. Instead of regressing the six-degree-of-freedom pose directly, it predicts the 3D volume coordinate of every encoded slice location together with an evidence weight, then recovers the pose in closed form with a parameter-free weighted Kabsch layer. A selective state-space (Mamba) scan builds these correspondences in linear time, so the slice–volume affinity matrix of cross-attention is never materialized and the parameter count does not depend on the input resolution.
| 0.73 mm | 2.27 mm | 100 % / 80 % | 6.49 M | 51 FPS |
|---|---|---|---|---|
| mean mTRE on CAMUS | mean mTRE on μ-RegPro | frames within 3 mm, CAMUS / μ-RegPro |
parameters, at every resolution |
at 512², one NVIDIA A100 |
As reported in the paper. Full comparison in Results.
This repository contains the reference implementation of our method, the preprocessing for both datasets, and two trained checkpoints. The baselines are third-party code and are not redistributed here; see Acknowledgments for their official repositories.
- Environment
- Download the data
- Preprocess
- Run the experiments: checkpoints, training, resolution, perturbation, ablations
- How it works: architecture, SSVF, pose solver and loss, paper-to-code map
- Results: qualitative, accuracy, cost
- Notes on reproduction
- Citation · Acknowledgments · License
Tip
Fastest way to see it run: set up the environment, download μ-RegPro (about 120 MB), preprocess it, and run python eval.py pretrained/proreg.
You need Linux, an NVIDIA GPU and conda. The results were produced with Python 3.12, PyTorch 2.5.1 (CUDA 12.1) and mamba-ssm 2.3.1 on one NVIDIA A100 80 GB. The selective scan is a compiled CUDA kernel, so training and evaluation require a GPU.
git clone https://github.com/TIO-IKIM/SCoPE-Reg.git
cd SCoPE-Reg
# Python plus the toolchain that compiles the Mamba CUDA kernels
conda create -n scope-reg -c nvidia -c conda-forge \
python=3.12 cuda-nvcc=12.9 gcc_linux-64 gxx_linux-64
conda activate scope-reg
# The order matters: the Mamba packages compile against the installed torch
pip install torch==2.5.1+cu121 torchvision==0.20.1+cu121 \
--index-url https://download.pytorch.org/whl/cu121
pip install setuptools wheel packaging
# Builds CUDA kernels for many GPU architectures: this can take about 45 minutes
pip install --no-build-isolation causal_conv1d==1.6.1 mamba_ssm==2.3.1 transformers==5.3.0
# Everything else, at the exact versions behind the reported results
pip install -r environment.txt natsort==8.4.0 torchgeometry==0.1.2 \
--extra-index-url https://download.pytorch.org/whl/cu121The finished environment takes about 8 GB of disk. Check that the compiled kernel loads and the GPU is visible:
python -c "import torch, selective_scan_cuda; print(torch.__version__, torch.cuda.get_device_name(0))"Troubleshooting the install
No matching distribution found for torch==2.5.1+cu121: this build exists only on the PyTorch index, not on PyPI. Keep the--index-url/--extra-index-urlflags.ImportError: cannot import name 'GenerateDecoderOnlyOutput': pip installed a newertransformersthanmamba_ssm2.3.1 supports. Runpip install transformers==5.3.0.ModuleNotFoundError: selective_scan_cuda:mamba_ssmwas installed without compiling its kernel. Reinstall it inside the activated environment withpip install --force-reinstall --no-deps --no-build-isolation --no-cache-dir mamba_ssm==2.3.1, and check thatnvcc -Vworks.
Both datasets are public and free for non-commercial research under CC BY-NC-SA 4.0. They are not redistributed here, so please respect their terms and cite them.
| Dataset | Content | Size | Official source |
|---|---|---|---|
| CAMUS | 500 patients, apical 2CH + 4CH echocardiography sequences (1000 volumes) | about 3.8 GB | Challenge page, download portal |
| μ-RegPro | 73 transrectal prostate cases; only the ultrasound (us_*) is used |
about 120 MB | Challenge page, Zenodo |
Pick a data root; every script reads it from SVR_DATA_ROOT (default ./datasets):
export SVR_DATA_ROOT=/path/to/datasets
mkdir -p "$SVR_DATA_ROOT"cd "$SVR_DATA_ROOT"
API=https://humanheart-project.creatis.insa-lyon.fr/database/api/v1/folder
wget -O database_nifti.zip "$API/63fde55f73e9f004868fb7ac/download"
wget -O database_split.zip "$API/66e27d12961576b1bad4e4e1/download"
unzip -q database_nifti.zip
unzip -q database_split.zip -d database_split # -d is required: the scripts expect the nested folder
cd -mkdir -p "$SVR_DATA_ROOT/ProstateMRUS_raw"
cd "$SVR_DATA_ROOT/ProstateMRUS_raw"
wget https://zenodo.org/records/8004388/files/train.zip
wget https://zenodo.org/records/8004388/files/val.zip
unzip -q train.zip && unzip -q val.zip
cd -You should now have:
$SVR_DATA_ROOT/
├── database_nifti/patient0001/patient0001_2CH_half_sequence.nii.gz (+ _gt, 4CH, ...)
├── database_split/database_split/subgroup_{training,validation,testing}.txt
└── ProstateMRUS_raw/{train,val}/{us_images,us_labels}/case000000.nii.gz (+ MR folders, unused)
Preprocessing has two stages per dataset. *_0.py builds a normalized volume and mask cache; *_1.py samples a fixed pool of frames and poses from it. Finally, make_manifests writes the case-level folds. Run all commands from the repository root, with SVR_DATA_ROOT set. On one A100, CAMUS at 128² takes about 3 minutes and μ-RegPro a few seconds.
python data/preprocess/camus_0.py --size 128 # NIfTI -> 32x128x128 cache
python data/preprocess/camus_1.py --size 128 --pm 10 # cache -> CAMUS128/pool_pm10
python -m data.make_manifests --dataset CAMUS --size 128python data/preprocess/proreg_0.py # TRUS -> 40x64x64 at 0.8 mm, 2 sub-volumes per case
PYTHONHASHSEED=0 python data/preprocess/proreg_1.py # cache -> ProReg/pool_pm10
python -m data.make_manifests --dataset ProRegKeep PYTHONHASHSEED=0: this script seeds its pose sampling with Python's per-process salted hash(), so without it every rebuild draws different test poses (details).
# Perturbation magnitudes (one cache serves every --pm)
for pm in 15 20 25; do
python data/preprocess/camus_1.py --size 128 --pm $pm
done
# Higher resolutions
for size in 192 384; do
python data/preprocess/camus_0.py --size $size
python data/preprocess/camus_1.py --size $size --pm 10
python -m data.make_manifests --dataset CAMUS --size $size
doneWhat the pipeline does
| CAMUS | μ-RegPro | |
|---|---|---|
| Volume |
|
|
| Spacing | Fixed 78.848 mm field of view, so spacing = 78.848 mm / size (0.616, 0.411, 0.205 mm) | Native 0.8 mm isotropic, never resampled |
| Volumes per case | 2 (2CH and 4CH) | 2 (non-overlapping 40-slice sub-volumes) |
| Third axis | Temporal: the ED–ES sequence | Spatial: real through-plane direction |
| Masks | LV endocardium, epicardium and LA | Prostate gland |
-
Normalization: intensities are mapped to
$[0, 1]$ between the 1st and 99th percentile. -
Fixed benchmark: each validation and test volume stores four pre-computed frames with their poses, sampled uniformly at magnitude
$\pm\mathrm{pm}$ , so every model is scored on identical frames. Training draws a fresh pose per sample, requiring at least 35 % foreground. -
Folds: every fold is a case-level 7:1:2 train/val/test split. Cases are shuffled once with a fixed seed and partitioned into ten chunks; fold
$k$ takes two chunks as test, one as val and seven as train. Every case is tested exactly once, and sub-volumes of one scan never straddle a split.
The cache is only an intermediate and can be deleted once the pool is built. Axis conventions and crop details are documented in data/preprocess/README.md.
| Paper result | Data needed | How |
|---|---|---|
| Table 1, CAMUS and μ-RegPro | CAMUS128/pool_pm10, ProReg/pool_pm10 |
4.1 one fold from a checkpoint, 4.2 all five folds |
| Table 2, cost vs. resolution | CAMUS{128,192,384} |
4.3 |
| Figure 4, pose perturbation | CAMUS128/pool_pm{10,15,20,25} |
4.4 |
| Table 3 and Table S3, ablations | CAMUS128/pool_pm10 |
4.5 |
Two trained models ship in pretrained/. Each directory is self-describing: weights, the resolved config, and the fold manifests the model was trained against.
| Directory | Dataset | Input | Perturbation | Fold |
|---|---|---|---|---|
pretrained/camus |
CAMUS | 1 of 5 | ||
pretrained/proreg |
μ-RegPro | 2 of 5 |
python eval.py pretrained/camus
python eval.py pretrained/proregThere is no --model flag: eval.py reads everything from the run directory, so a checkpoint cannot be evaluated as something it is not. It prints a table and writes eval_metrics.json and eval_metrics.csv next to the checkpoint.
Each checkpoint is one fold of a five-fold run, so its numbers differ from the five-fold means in Table 1. To check your setup:
- CAMUS: pools rebuild byte-identically, so the printed mTRE must equal the
best val mTREthateval.pyprints from the checkpoint. - μ-RegPro: the original pool cannot be regenerated, so the frames differ from the original run and the mTRE will not exactly match
best val mTRE. We measured differences of up to 0.1 mm on this fold (details).
| Group | Metrics |
|---|---|
| Registration | mTRE (mm, primary), TRE, TransErr (mm), RotErr (°) |
| Image | NCC, SSIM: the frame vs. the volume resampled at the predicted pose |
| Segmentation | Dice, HD95 (mm): the volume mask resampled at the predicted pose vs. the stored slice mask |
| Cost (batch 1) | Params (M), GFLOPs, FPS, peak VRAM (MiB) |
Built-in sanity check. Every evaluation also resamples at the ground-truth pose, which must give Dice 1.0 and HD95 0.0. If it does not, the pose convention or the pool is inconsistent and the numbers above it cannot be trusted.
Useful flags: --per-sample also writes per_sample.csv with one row per test frame (the source of the p95 and max columns), --limit 200 runs a quick check, and --split {test,val,train} selects the split.
Note
Regenerating the μ-RegPro manifests shifts test folds by one case, because 73 cases do not divide evenly: fold 2 has 14 test cases instead of this checkpoint's original 15. None of the 14 was in its training data, so the evaluation stays held out. To score the original 15 cases, run cp pretrained/proreg/manifests/* "$SVR_DATA_ROOT/ProReg/manifests/k5/", and re-run make_manifests before you train new folds.
configs/final.yaml holds everything that varies across the reported results. configs/select.py expands it into the flat config the trainer takes and injects the fixed values. The architecture is identical on both datasets; only the data description and the epoch budget differ.
python configs/select.py CAMUS > run_camus.yaml
python configs/select.py ProReg > run_proreg.yaml
python train.py --config run_camus.yaml --save_dir runs/camus # all 5 folds -> runs/camus/fold_<i>/
python train.py --config run_proreg.yaml --save_dir runs/proreg
python eval.py runs/camus --per-sample # adds a mean ± std row over the folds
python eval.py runs/proreg --per-sampleTo train a single fold, set it in the run config, for example sed -i 's/fold: all/fold: 0/' run_camus.yaml. Each run writes its resolved config next to the checkpoint, and the best checkpoint is selected on validation mTRE. Training resumes automatically from the last best checkpoint in --save_dir; pass --no-resume to start over.
| Setting | Value |
|---|---|
| Encoders | 2D and 3D ResNet-8, 8× downsampling, |
| SSVF |
|
| Loss |
|
| Optimizer | AdamW, lr |
| Schedule | Polynomial decay, power 0.9 |
| Epochs | 500 on CAMUS, 1500 on μ-RegPro |
At 128², training peaks at about 2 GB of GPU memory and an epoch takes about 30 seconds on an A100, so one 500-epoch fold runs for roughly 4 hours.
CAMUS is field-of-view anchored, so 128, 192 and 384 pixels sample the same physical window more finely. Set size and img_size in the CAMUS block of configs/final.yaml, build the pool (section 3), then train and evaluate:
# configs/final.yaml
CAMUS:
data:
size: 192
img_size: [32, 192, 192]python configs/select.py CAMUS > run_camus192.yaml
python train.py --config run_camus192.yaml --save_dir runs/camus192
python eval.py runs/camus192img_size is checked against the data, so a mismatch is an error rather than a silent resize. FPS, peak memory, GFLOPs and parameters come out of eval.py at the run's resolution.
Measure cost at any resolution, without data (for example 512²)
The 512² rows of Table 2 measure the model at that input shape; no data is preprocessed at 512².
python - <<'EOF'
import torch
from config import load_config, build_model
from eval import _Runner, cost_metrics
cfg = load_config("pretrained/camus/config.yaml")
device = torch.device("cuda")
d, h, w = 32, 512, 512
model = build_model(cfg, device, volume_shape=(d, h, w)).eval()
print(cost_metrics(_Runner(model, device), (d, h, w), (h, w), device, n_iters=50))
EOF--pm selects the perturbation magnitude, in voxels and degrees. Pools exist for whichever values you built in section 3:
for pm in 15 20 25; do
python train.py --config run_camus.yaml --pm $pm --save_dir runs/camus_pm$pm
python eval.py runs/camus_pm$pm --per-sample
done--arm selects a row of the ablation tables. Solver arms carry no head parameters, and head arms carry no solver parameters.
--arm |
Paper row |
|---|---|
wkps (default) |
CWCD + WKPS, the reported model |
kabsch_uniform |
CWCD + Kabsch Solver |
cwcd_heads |
CWCD + Direct Heads |
ssvf_heads |
SSVF + Direct Heads |
cross_attn_heads |
Cross Att. + Direct Heads |
kabsch_triplane |
Tri-plane variant from the supplement, with uniform Kabsch weights |
python configs/select.py CAMUS --arm ssvf_heads > run_ssvf_heads.yaml
python train.py --config run_ssvf_heads.yaml --save_dir runs/ssvf_heads
python eval.py runs/ssvf_heads --per-sampleThe regularization variants of the supplement are flags in configs/final.yaml on top of any arm. Each is on/off plus a weight:
flow_loss: false # dense supervision on the coordinate field
lambda_flow: 0.0
physics_loss: false # Navier-Cauchy residual on the coordinate field
lambda_physics: 0.0
Figure 1. 2D/3D encoders extract features, which the Correlation-Weighted Coordinate Decoder (CWCD) fuses with grids to predict dense coordinates q and evidence weights w. Weighted Kabsch then estimates the rigid pose in closed form.
Given a slice
Here
-
Feature encoding. Independent 2D and 3D ResNet-8 encoders downsample by a factor of 8 and output
$C = 256$ channels: slice features$\mathbf{F}_s$ and volume features$\mathbf{F}_v$ . -
Correlation-Weighted Coordinate Decoder (CWCD). The SSVF block fuses both feature streams with a fixed voxel grid
$\mathbf{G}_v$ and returns a coordinate numerator$\mathbf{Y}$ and an evidence mass$\mathbf{w}$ . Dividing the two gives one 3D volume coordinate$\mathbf{q}$ per encoded slice location. -
Weighted Kabsch Pose Solver (WKPS). The correspondences
$(\mathbf{G}_s, \mathbf{q})$ , weighted by$\mathbf{w}$ , determine the pose$\hat{\mathbf{T}}$ in closed form. This stage has no learnable parameters.
State-Space Slice–Volume Fusion (SSVF) is where the correspondences form. It replaces the dense slice–volume attention matrix with linear-time selective scans.
Figure 2. Slice/volume features and coordinate grids are fused across four spatial orderings. DI-SSM feature scans yield Yf, coordinate scans accumulate weighted locations into Z, and depth reduction yields the numerator Y and evidence w.
Reading the figure from top to bottom:
-
Project and augment. LayerNorm and a
$1 \times 1$ convolution expand both feature streams to$C' = 2C$ . The fixed voxel grid gains a constant channel,$\widetilde{\mathbf{G}}_v = [\mathbf{G}_v; 1]$ . The first three channels accumulate coordinates; the last one collects normalization mass. -
Scan with a dual-input state-space module (DI-SSM). Each axial plane
$d$ is flattened into a token sequence and processed by two scans:- The feature scan runs over the volume stream with its gates projected from the slice, which fuses appearance into
$\mathbf{Y}_f$ . - The coordinate scan reuses the scan kernel as a correspondence weight. Softplus gates and strictly negative dynamics make the kernel
$\kappa_{n,m}$ non-negative, so every slice token$n$ accumulates the volume coordinates it matches:
(\mathbf{Z}^{(k)})_{d,n} = \sum_{m \le n} \kappa_{n,m}\,(\widetilde{\mathbf{G}}_v)_{d,m}The kernel is shared across all channels and the residual term is zero, so the constant channel carries exactly the mass that was applied to the coordinates. These constraints are enforced by construction rather than by initialization.
- The feature scan runs over the volume stream with its gates projected from the slice, which fuses appearance into
-
Scan in four directions. A single scan is causal, so DI-SSM runs over row- and column-major orderings, forward and reversed, and the unflattened outputs are summed. Pairing each ordering with its reverse makes every slice–volume pair reachable. This costs
$\mathcal{O}(D'NC'S)$ time, whereas dense attention needs$\mathcal{O}(D'N^2C')$ time and an$\mathcal{O}(D'N^2)$ affinity matrix. -
Reduce depth by correlation. A bidirectional selective scan over depth, gated by
$\mathbf{Y}_f$ , produces positive depth weights$g_{d,i'}$ shared across channels. Summing over depth collapses the planes:\bar{\mathbf{Z}}_{i'} = \sum_{d=1}^{D'} g_{d,i'}\,\mathbf{Z}_{d,i'}, \qquad \mathbf{Y}_{i'} = (\bar{\mathbf{Z}})_{i',1:3}, \qquad w_{i'} = (\bar{\mathbf{Z}})_{i',4} -
Normalize. Dividing numerator by mass gives the predicted coordinate, which is a convex combination of volume-grid locations (exactly so as
$\varepsilon \to 0$ ). Unlike unconstrained regression, it therefore stays inside the convex hull of the encoded grid:\mathbf{q}_{i'} = \frac{\mathbf{Y}_{i'}}{w_{i'} + \varepsilon}
The mass
WKPS solves a weighted least-squares fit over the
The solution uses weighted centroids, the cross-covariance matrix and an SVD with determinant correction. It is differentiable almost everywhere, so pose supervision reaches the coordinates and weights directly.
Training uses pose supervision alone. Fourteen reference points
This couples rotation and translation geometrically, so no manually tuned loss weight is needed. Neither the coordinates nor the evidence weights receive direct supervision.
The whole model lives in model.py, with blocks named as in the paper.
| Paper | Code | Role |
|---|---|---|
| ResNet-8 encoders | ResNetEncoder8_2D, ResNetEncoder8_3D |
Slice and volume features |
| DI-SSM | DISSM |
Feature scan and coordinate scan |
| SSVF |
SSVFScan, SSVF
|
Four-direction fusion |
| Depth reduction | DepthReduction |
Correlation-weighted collapse over depth |
| CWCD | CWCD |
Coordinates |
| WKPS | wkps |
Closed-form weighted pose |
| Full model | SCoPEReg |
CWCD + WKPS |
| Ablation substitutes |
CrossAttentionFusion, TranslationHead, RotationHead
|
Cross-attention and direct regression heads |
PoseLoss |
The training objective |
Repository layout
train.py training entry point (both datasets)
eval.py evaluation entry point; writes all reported metrics
config.py config schema, defaults and validation
configs/final.yaml everything that varies across the reported results
configs/select.py expands it into the flat config the trainer takes
data/
pool_dataset.py pool loading, folds, pose conventions
data_utils.py pose sampling and slice extraction
make_manifests.py builds the k-fold manifests
preprocess/ raw volumes -> pools
model.py the whole model, blocks named as in the paper
metrics/ mTRE, translation/rotation error, Dice, HD95, NCC, SSIM, FPS, memory, params
losses/ pose, coordinate-flow and physics objectives
pretrained/ trained weights, each with its config and manifests
assets/ figures of this page
environment.txt package versions used for the reported results
All tables and figures in this section are from the paper (arXiv v2).
Each method shows the volume resampled at its predicted pose and the absolute difference to the target frame (black: agreement, red: error; shared scale). The outer columns show the volume center frame at the identity pose (left) and the target frame at the ground-truth pose (right).
Figure 3. Test cases near SCoPE-Reg's median mTRE. Top: CAMUS, echocardiography. Bottom: μ-RegPro, transrectal prostate.
Best, median, p95 and worst cases
Rows are ranked by SCoPE-Reg's frame-level mTRE, so the baseline columns show those same frames. The worst rows are included on purpose: hard frames remain hard, and on some of them a baseline does better.
Table 1 of the paper: mean±std over 5 folds, with p95 and max pooled across all test frames (4000 on CAMUS, 584 on μ-RegPro). mTRE, p95, max, TransErr and HD95 are in mm, RotErr in °, and ≤3 mm in % of frames. Bold marks the best result and underline the second best. Initial (identity) is the error with no registration at all.
CAMUS (
| Method | mTRE ↓ | p95 ↓ | max ↓ | TransErr ↓ | RotErr ↓ | ≤3 mm ↑ |
|---|---|---|---|---|---|---|
| Initial (identity) | 8.17±0.05 | 10.71 | 13.55 | 5.93±0.05 | 9.64±0.11 | 0.1±0.0 |
| FVR-Net | 4.90±0.56 | 10.89 | 15.12 | 3.31±0.55 | 6.36±0.27 | 3.0±0.8 |
| CU-Reg | 2.46±1.19 | 6.30 | 15.15 | 1.67±0.81 | 3.88±2.02 | 69.5±32.8 |
| EUReg | 1.24±0.15 | 2.01 | 12.47 | 0.58±0.07 | 1.64±0.25 | 97.2±0.6 |
| DreamReg | 6.50±0.05 | 9.10 | 11.78 | 4.31±0.02 | 8.95±0.12 | 1.1±0.2 |
| Ours | 0.73±0.04 | 1.37 | 5.49 | 0.42±0.02 | 1.10±0.06 | 100.0±0.1 |
| Method | Dice ↑ | HD95 ↓ | NCC ↑ | SSIM ↑ |
|---|---|---|---|---|
| FVR-Net | 0.942±0.011 | 5.56±0.70 | 0.731±0.052 | 0.436±0.021 |
| CU-Reg | 0.979±0.011 | 2.62±1.62 | 0.923±0.051 | 0.666±0.107 |
| EUReg | 0.986±0.003 | 1.63±0.20 | 0.964±0.006 | 0.789±0.029 |
| DreamReg | 0.928±0.003 | 7.20±0.27 | 0.669±0.007 | 0.303±0.005 |
| Ours | 0.988±0.001 | 1.49±0.16 | 0.972±0.002 | 0.849±0.010 |
μ-RegPro
| Method | mTRE ↓ | p95 ↓ | max ↓ | TransErr ↓ | RotErr ↓ | ≤3 mm ↑ |
|---|---|---|---|---|---|---|
| Initial (identity) | 8.44±0.26 | 11.53 | 13.86 | 7.67±0.28 | 9.55±0.33 | 0.5±0.7 |
| FVR-Net | 5.58±0.29 | 8.58 | 11.01 | 4.85±0.24 | 8.40±0.46 | 6.7±3.8 |
| CU-Reg | 4.43±0.28 | 7.58 | 9.81 | 3.84±0.37 | 7.77±0.28 | 21.7±5.7 |
| EUReg | 2.63±0.23 | 4.47 | 6.77 | 1.34±0.16 | 7.01±1.01 | 66.2±8.7 |
| DreamReg | 4.77±0.12 | 7.80 | 9.09 | 4.10±0.16 | 8.15±0.36 | 14.7±2.7 |
| Ours | 2.27±0.08 | 4.22 | 5.95 | 1.42±0.09 | 5.44±0.15 | 80.4±3.7 |
| Method | Dice ↑ | HD95 ↓ | NCC ↑ | SSIM ↑ |
|---|---|---|---|---|
| FVR-Net | 0.765±0.051 | 6.06±0.38 | 0.764±0.042 | 0.338±0.035 |
| CU-Reg | 0.801±0.056 | 5.10±0.77 | 0.915±0.013 | 0.600±0.019 |
| EUReg | 0.891±0.036 | 2.70±0.61 | 0.962±0.008 | 0.776±0.023 |
| DreamReg | 0.794±0.056 | 5.34±0.68 | 0.882±0.017 | 0.519±0.022 |
| Ours | 0.909±0.019 | 2.11±0.09 | 0.944±0.010 | 0.720±0.022 |
Table 2 of the paper: single-frame (batch 1) inference on CAMUS. FLOPs exclude the custom selective_scan kernels; FPS and memory are end-to-end. SCoPE-Reg's parameter count stays fixed across resolutions.
Show the full table
| Method | Res. | Params (M) | GFLOPs | FPS | Mem (MiB) |
|---|---|---|---|---|---|
| FVR-Net | 128² | 67.80 | 61.2 | 33 | 378 |
| 192² | 76.19 | 137.7 | 33 | 546 | |
| 384² | 151.68 | 550.8 | 18 | 1561 | |
| 512² | 243.96 | 979.2 | 13 | 2670 | |
| CU-Reg | 128² | 98.82 | 38.7 | 35 | 531 |
| 192² | 98.82 | 93.2 | 35 | 1068 | |
| 384² | 98.82 | 503.2 | 10 | 10877 | |
| 512² | 98.82 | 1135.1 | 4 | 33369 | |
| EUReg | 128² | 0.81 | 2.3 | 267 | 78 |
| 192² | 1.14 | 5.2 | 186 | 162 | |
| 384² | 2.91 | 20.9 | 56 | 616 | |
| 512² | 4.74 | 37.7 | 32 | 1088 | |
| DreamReg | 128² | 103.29 | 2.5 | 37 | 469 |
| 192² | 208.15 | 5.4 | 36 | 952 | |
| 384² | 774.38 | 21.4 | 24 | 3560 | |
| 512² | 1361.58 | 38.0 | 18 | 6264 | |
| Ours | 128² | 6.49 | 8.6 | 150 | 79 |
| 192² | 6.49 | 19.4 | 142 | 125 | |
| 384² | 6.49 | 77.7 | 77 | 396 | |
| 512² | 6.49 | 138.2 | 51 | 678 |
- mTRE is the mean displacement of five frame-relative targets (the center and four corners at 90 % of the frame extent), in millimetres. It is the paper's primary metric.
-
pmis in voxels and degrees. The samepmis therefore a different physical translation at a different resolution:$\pm 10$ corresponds to$\pm 6.2$ mm at 128² but less at higher resolutions. Only RotErr is directly comparable across resolutions. - CAMUS's third axis is time. Its volumes stack the ED–ES sequence, so out-of-plane pose components map onto cardiac phase rather than anatomical depth, and perturbations are simulated rather than tracked. This limits conclusions about physical 3D probe localization.
-
GFLOPs understate arithmetic cost, because they exclude the compiled
selective_scankernels. - Checkpoint selection of the provided weights. The two checkpoints come from the runs the paper reports, which selected checkpoints on a validation set that was not disjoint from the test set. The code here always uses a disjoint three-way 7:1:2 split, so models you train yourself are selected on a held-out validation set. On CAMUS this split changes only the train/val partition: the per-fold test cases are identical.
-
Rebuilding the pools. CAMUS seeds its poses from the patient ID, so a rebuilt pool is byte-identical to the original. μ-RegPro seeds them with Python's salted
hash();PYTHONHASHSEED=0makes a rebuild repeatable, but the pool behind the paper's runs used an unrecorded salt and cannot be regenerated. A rebuilt μ-RegPro pool therefore has different test poses, drawn from the same distribution, and fold-level numbers will not match the original runs exactly. Within any one pool, every model is still scored on identical frames.
If you use this code, the checkpoints or the benchmark protocol in your research, please cite our paper:
@misc{schwarz2026scopereg,
title = {{SCoPE-Reg}: Efficient Rigid Ultrasound Slice-to-Volume Registration via State-Space Correlation and Closed-Form Pose Estimation},
author = {Schwarz, Niklas and Kleesiek, Jens and Rempe, Moritz},
year = {2026},
eprint = {2608.28715},
archivePrefix = {arXiv},
primaryClass = {eess.IV},
note = {Accepted at WACV 2027},
url = {https://arxiv.org/abs/2608.28715}
}This work stands on the shoulders of the methods we compare against. Thanks to the authors of these baselines for publishing their work and code. If you use them, please cite them and follow their licenses.
| Method | Authors | Published | Code |
|---|---|---|---|
| FVR-Net | Guo, Xu, Xu, Wood, Yan | MICCAI 2021 | DIAL-RPI/FVR-Net |
| CU-Reg | Lei, Zhou, Pei, Zhao, Jin, Teoh, Qin, Heng | MICCAI 2024 | LLEIHIT/CU-Reg |
| EUReg | Wang, Wang | MICCAI 2025 | ZAX130/EUReg |
| DreamReg | Kang, Zhang, Shan, Gong, Ding, Cheng | arXiv 2606.18825 | kangluoyao/DreamReg |
We also build on Mamba (Gu & Dao) for the selective state-space model and on the dual-input conditioning of FusionMamba (Peng et al.).
Datasets. We thank the organizers of CAMUS and μ-RegPro for making their data public.
BibTeX for the baselines and datasets
@inproceedings{guo2021fvrnet,
title = {End-to-end Ultrasound Frame to Volume Registration},
author = {Guo, Hengtao and Xu, Xuanang and Xu, Sheng and Wood, Bradford J. and Yan, Pingkun},
booktitle = {Medical Image Computing and Computer Assisted Intervention -- MICCAI 2021},
volume = {12904},
pages = {56--65},
year = {2021},
doi = {10.1007/978-3-030-87202-1_6}
}
@inproceedings{lei2024cureg,
title = {Epicardium Prompt-guided Real-time Cardiac Ultrasound Frame-to-volume Registration},
author = {Lei, Long and Zhou, Jun and Pei, Jialun and Zhao, Baoliang and Jin, Yueming and Teoh, Yuen-Chun Jeremy and Qin, Jing and Heng, Pheng-Ann},
booktitle = {Medical Image Computing and Computer Assisted Intervention -- MICCAI 2024},
volume = {15002},
pages = {618--628},
year = {2024},
doi = {10.1007/978-3-031-72069-7_58}
}
@inproceedings{wang2025eureg,
title = {{EUReg}: End-to-End Framework for Efficient {2D}-{3D} Ultrasound Registration},
author = {Wang, Haiqiao and Wang, Yi},
booktitle = {Medical Image Computing and Computer Assisted Intervention -- MICCAI 2025},
volume = {15961},
pages = {175--185},
year = {2025},
doi = {10.1007/978-3-032-04937-7_17}
}
@misc{kang2026dreamreg,
title = {{DreamReg}: Belief-Driven World Model for {2D}-{3D} Ultrasound Registration},
author = {Kang, Luoyao and Zhang, Yuelin and Shan, Jiwei and Gong, Haifan and Ding, Qingpeng and Cheng, Shing Shin},
year = {2026},
eprint = {2606.18825},
archivePrefix = {arXiv}
}
@article{leclerc2019camus,
title = {Deep Learning for Segmentation Using an Open Large-Scale Dataset in {2D} Echocardiography},
author = {Leclerc, Sarah and Smistad, Erik and Pedrosa, Jo{\~a}o and {\O}stvik, Andreas and Cervenansky, Frederic and Espinosa, Florian and Espeland, Torvald and Berg, Erik Andreas Rye and Jodoin, Pierre-Marc and Grenier, Thomas and Lartizien, Carole and D'hooge, Jan and Lovstakken, Lasse and Bernard, Olivier},
journal = {IEEE Transactions on Medical Imaging},
volume = {38},
number = {9},
pages = {2198--2210},
year = {2019},
doi = {10.1109/TMI.2019.2900516}
}
@misc{baum2023muregpro,
title = {{$\mu$-RegPro}: {MICCAI} 2023 Challenge on Prostate {MR} to Ultrasound Registration -- Dataset},
author = {Baum, Zachary M. C. and Saeed, Shaheer U. and Bonmati, Ester and Barratt, Dean C. and Hu, Yipeng},
howpublished = {Zenodo},
year = {2023},
doi = {10.5281/zenodo.7870104}
}Funding. This work received funding from the Bruno & Helene Jöster Foundation, from KITE (Plattform für KI-Translation Essen) through the REACT-EU initiative, and from the Cancer Research Center Cologne Essen (CCCE).
The code in this repository is released under the MIT License. The datasets and baselines keep their own licenses and are not covered by it.

