Skip to content

feat: add adaptive sampling, motion registration, MEDL, and e2e ADS configs - #316

Open
georgeyiasemis wants to merge 127 commits into
mainfrom
feature/adaptive-registration
Open

feat: add adaptive sampling, motion registration, MEDL, and e2e ADS configs#316
georgeyiasemis wants to merge 127 commits into
mainfrom
feature/adaptive-registration

Conversation

@georgeyiasemis

@georgeyiasemis georgeyiasemis commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Description

This PR brings end-to-end adaptive dynamic sampling (ADS) and optional motion registration into DIRECT, together with the paper experiment layouts for the MIDL 2026 e2e ADS works.

It wires learned sampling policies and registration models through the MRI training/inference stack, ships validated train + inference YAML pairs under projects/e2e_ads_recon and projects/e2e_ads_recon_reg, and hardens config/checkpoint loading so those projects (and Hub-style inference YAMLs) run cleanly on current main (including ruff 0.16).

What's new

  • Adaptive k-space sampling (direct/nn/adaptive/): policies for 1D/2D and static/dynamic masks, rejection-sampling as a typed training path (RejectionSamplingError), and budgeting that works with static ACS on dynamic volumes.
  • Motion registration (direct/registration/, direct/nn/registration/): VoxelMorph-style learned registration plus classical utilities (demons, optical flow, elastic deformation); joint or decoupled recon↔registration training.
  • MEDL reconstruction model and related engine support.
  • 3D VarNet model/config/engine paths used by the ADS setups.
  • Key-based losses (source_key / target_key) and displacement-field visualization for registration runs.
  • Paper projects:
    • projects/e2e_ads_recon — adaptive sampling + reconstruction (vSHARP / MEDL variants, frame/init2 configs).
    • projects/e2e_ads_recon_reg — same family with registration (vSHARP / VarNet / LOUPE / fixed-mask variants, phase/frame and disjoint options).
  • Sibling inference YAMLs (*_inference.yaml): one inference file per model with active val-4× masking and commented 6×/8× alternatives (no per-rate file explosion).

What's changed

  • MRI engines (vSHARP, RIM, CIRIM, shared MRIModelEngine) call into adaptive sampling / registration when configured; writers can persist registration volumes and displacement fields.
  • Config system: training / validation are optional so inference-only YAMLs are valid; logging.log_interval controls TensorBoard/scalar flush cadence.
  • DYNAMIC masking correctly receives a time dimension; float adaptive masks are handled in DC fill / masking; Calgary-Campinas mask downloads cast acceleration to int filenames.
  • Checkpoint loading raises a clear RuntimeError on missing keys; ModConv conv_out_bias / AdaIN kwargs wiring aligned so paper weights load under current UNet construction.
  • filter_arguments_by_signature keeps **kwargs-based model builders working when configs carry extra image_* fields.
  • Docs/READMEs for adaptive, registration, MEDL, and both e2e ADS projects; Python 3.12 / elasticdeform notes where needed.
  • Merged with latest main ruff cleanup without dropping the above behavior.

User impact

  • You can train and run the e2e ADS (and ADS+registration) paper configs from this repo with the published naming and sibling inference YAMLs.
  • Inference configs can omit training / validation blocks; use *_inference.yaml (or the minimizer) instead of maintaining many rate-specific files.
  • Registration models need a reference image in the batch (or registration_simulate_reference in transforms); without that, predict fails with a missing reference_image key.
  • Adaptive / DYNAMIC sampling may skip batches on rejection-sampling failure during training (logged retries) instead of always aborting immediately.

georgeyiasemis and others added 2 commits August 10, 2026 10:31
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@georgeyiasemis georgeyiasemis changed the title Feature/adaptive registration feat: add adaptive sampling, motion registration, MEDL, and e2e ADS configs Aug 10, 2026
georgeyiasemis and others added 5 commits August 11, 2026 01:50
…onfig

LPD/VarSplit/ViT engines were refining coil sensitivities twice via _do_iteration
and forward_function. ConjGrad Calgary configs need unet_conv_out_bias so Hub
weights with conv_out.bias load correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Zoo validation metrics used crop_outer_slices (slice 50:-50). Released Hub
YAMLs omitted it, so full-volume eval scored outer slices. Mirror configs under
projects/calgary_campinas/configs/inference and keep the flag in minimize_inference_yaml.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add an inference block with crop_outer_slices and commented 5x/10x
accelerations to each base_*.yaml instead of separate Hub-style YAMLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Fill missing Unet/RIM/VarNet/LPD/RecurrentVarNet fields from the trained
configs and add 4x inference with commented 8x masking for Hub-style predict.

Co-authored-by: Cursor <cursoragent@cursor.com>
Rename AXT1 ablation to t6_h128_nl2, drop fastmri metrics, set inference batch_size to 10, and sync Calgary VarNet comparison params.

Co-authored-by: Cursor <cursoragent@cursor.com>
@georgeyiasemis georgeyiasemis linked an issue Aug 11, 2026 that may be closed by this pull request
6 tasks
@georgeyiasemis
georgeyiasemis requested review from jonasteuwen and a lite review from Copilot August 11, 2026 13:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@jonasteuwen jonasteuwen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at my comments

Comment on lines +40 to +43

for i in range(batch_size):
x = inputs[i : i + 1]
count = 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do @jit.compile? Or doesn't that work here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because of the rejection sampling

Comment on lines +91 to +96
super().__init__()
self.slope = slope
self.clamp = clamp

self.fun = ThresholdSigmoidMaskFunction.apply

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer self._variable for class private variables

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines -49 to -50
data["sensitivity_map"] = self.compute_sensitivity_map(data["sensitivity_map"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this gone?

@georgeyiasemis georgeyiasemis Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug. Sens estimation is already happening in main engine

Comment thread direct/nn/rim/rim.py Outdated
Comment on lines +299 to +301
self.image_initialization = (
image_initialization.lower() if isinstance(image_initialization, str) else image_initialization
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, feels like you should override the __eq__ in the object and make sure str doesn't end up here.

Comment on lines -67 to -68
data["sensitivity_map"] = self.compute_sensitivity_map(data["sensitivity_map"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this go

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bug. Sens estimation is already happening in main engine

Comment on lines +26 to +38
def _deform_random_grid(*args, **kwargs) -> list[np.ndarray]:
"""Lazy import wrapper for :func:`elasticdeform.deform_random_grid`.

``elasticdeform`` wheels may be incompatible with NumPy 2.x.

Returns
-------
list[np.ndarray]
Deformed images returned by ``elasticdeform``.
"""
try:
from elasticdeform import deform_random_grid
except ImportError as exc:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a function that can be forked/vendored?

Comment thread direct/environment.py Outdated
Comment on lines +346 to +348
curr_model_cfg.update(operator_kwargs)
# Drop keys unknown to the constructor (e.g. ModConv defaults on legacy modules).
additional_models[k] = curr_model(**filter_arguments_by_signature(curr_model, curr_model_cfg))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this cause silent bugs?

Comment thread docker/Dockerfile Outdated
ARG PYTHON="3.12"

# TODO: conda installs its own version of cuda
FROM nvidia/cuda:${CUDA}-devel-ubuntu18.04

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably need to boost this too

Comment thread pyproject.toml
Comment on lines 49 to 51
"pytest>=9",
"ruff>=0.16.1",
"ruff>=0.14",
"ty>=0.0.38",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep it to 0.16.1 or you run into issues.

georgeyiasemis and others added 17 commits August 12, 2026 01:00
Prefer InitType for RIM init, private binarizer attrs, warn on dropped
model kwargs, vendor SciPy elastic deform (drop elasticdeform), pin ruff
0.16.1, and boost the Docker CUDA/PyTorch stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove `from __future__ import annotations` from Python files touched in this PR and delete the obsolete `projects/scripts/minimize_inference_yaml.py` helper from the repository.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Predict without targets when inference.metrics is empty, only write
metrics_inference.json when metrics are computed, and fix DirectEnum
from_str typing after dropping future annotations.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align the annotation with the (volumes, metrics) tuple returned by
inference, and silence BaseConfig/subclass attribute checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep ADS as an example rather than baking acquisition-specific wording
into the H5 writer API docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop branch-only batch_size bumps and redundant regularizer_dropout
defaults that did not change behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Restore validation metrics and main inference batch sizes, remove
redundant model defaults (dropouts, unused RIM initializer fields),
and keep only real diffs: inference blocks and SENSE enum casing.

Co-authored-by: Cursor <cursoragent@cursor.com>
Inference lives on the training YAMLs (4x active, 8x commented); the
rate-specific copies were duplicates. Update the project README accordingly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

direct v2.2.0 tracking

3 participants