v0.8.0 to v0.12.0

67 added, 149 removed. Audit A to A.

---
name: mri-reconstruction
description: >-
- Actionable MRI image reconstruction — turn raw k-space into an image, and
- actually run it. Use this WHENEVER the user wants to reconstruct MR data or
- says things like "reconstruct this k-space", "run BART on this", "get an image
- from this .cfl / .h5 / twix file", or asks about parallel imaging
- (ESPIRiT/SENSE/GRAPPA), compressed sensing (PICS / L1-wavelet), coil
- sensitivity estimation, coil combination, or non-Cartesian / NUFFT
- reconstruction. This agent prefers to EXECUTE the reconstruction with BART or
- SigPy (not just describe it). It covers classical/analytic reconstruction —
- for anything TRAINED (unrolled networks, VarNet, self-supervised, diffusion
- priors, fastMRI models) hand off to the deep-learning-recon skill. Triggers:
- k-space, coil sensitivities, ESPIRiT, PICS, undersampled reconstruction,
- radial/spiral recon, `.cfl`/`.hdr`, ISMRMRD, Siemens twix, GE P-file.
+ Read before turning MR data into images or parameter maps: raw k-space
+ (Cartesian, radial, spiral, EPI; twix, ISMRMRD, numpy), GRAPPA/SENSE from a
+ sampling mask, coil combination and SNR units, off-resonance correction,
+ BART or SigPy, and T1, T2/EPG, T2*, water-fat (PDFF), ASL, DCE or QSM maps.
+ Prevents silent errors in encoding sign, acceleration pattern, orientation,
+ noise covariance and noise floor, water-fat swaps, timing origin and units.
+ Ships tested numpy helpers and a finish check. Trained networks:
+ deep-learning-recon; diffusion: diffusion-mri.
metadata:
author: Ke Wang
- version: "0.8.0"
+ version: "0.12.0"
---
- # MRFoundry: MRI Reconstruction (actionable)
-
- You are a reconstruction engineer: given k-space, produce an image — and run the
- pipeline, don't just talk about it. Default to **BART** (battle-tested, CLI,
- scriptable); use **SigPy** when the user is in Python. Confirm the data before
- running, then execute and inspect.
-
-
- ## Papers and textbooks
-
- See the [annotated reading list](references/reading-list.md) for primary papers,
- textbooks, publication details, direct source links and what each source supports.
- Use the [repo-wide reference index](https://github.com/KeWang0622/MRFoundry/blob/main/REFERENCES.md) to navigate across skills.
- When using a method, cite its specific source; distinguish paper evidence from
- software instructions and current venue/safety requirements.
-
-
- ## Evaluation and original-source credit
-
- Before comparing methods or making scientific claims, identify the intended task
- and the evidence needed to support it. Use the [evaluation and attribution guide](https://github.com/KeWang0622/MRFoundry/blob/main/skills/mri-research/references/evaluation-and-attribution.md)
- for evaluation planning, failure tests and auditing citations in the actual output.
- Report benchmark metrics when relevant; do not infer universal superiority or
- clinical validity from them. Cite original methods and software separately, and
- flag claims whose source or support could not be verified.
- If the shared guide is absent in a standalone install, retrieve
- `skills/mri-research/references/evaluation-and-attribution.md` from the
- [official repository](https://github.com/KeWang0622/MRFoundry).
-
- ## Project research memory
-
- If only the legacy `.mri-research/` notebook exists, migrate it with the project
- memory initializer before creating new notes; preserve existing evidence.
- For project experiments, read `.mrfoundry/INDEX.md` when present and retrieve
- only relevant preferences, environment notes and evidence-linked lessons. After
- meaningful runs or corrections, record outcomes, failures, limitations and next
- steps; revise scoped lessons without erasing history. Keep user preferences
- separate from scientific findings. Use the [project memory workflow](https://github.com/KeWang0622/MRFoundry/blob/main/skills/mri-research/references/project-memory.md)
- to initialize the folder or connect project `CLAUDE.md` / `AGENTS.md`. If the hub
- is absent, retrieve the reference from the official skill repository.
-
- ## Tool setup before execution
-
- For any application this skill uses, check for a compatible installation and
- follow the official upstream's setup instructions. Within the authorized task,
- install missing dependencies yourself in an isolated environment, run a small
- upstream example, then execute the user's workflow. Do not leave routine setup
- to the user or replace a missing tool with a homemade numerical implementation.
- Use established simulators/solvers; write only necessary configuration and glue.
- If blocked, report the actual obstacle and an established alternative.
- Read the [tool setup guide](https://github.com/KeWang0622/MRFoundry/blob/main/skills/mri-research/references/tool-setup.md) when installing,
- repairing, or choosing an execution environment. If the hub is not installed,
- retrieve that reference from the official `KeWang0622/MRFoundry` repository.
-
- ## Workflow
+ # MRFoundry: MRI Reconstruction and Quantitative Maps
- **0. Identify the k-space format** (ask or inspect). BART works in its own
- `.cfl`/`.hdr`, so other formats need a conversion step:
- - **BART `.cfl` + `.hdr`** — native; dims must be `[X Y Z COILS ...]` (coils on
- dim 3). Ready to use.
- - **Siemens twix `.dat`** — `bart twixread -A meas.dat ksp` writes a `.cfl`
- (`-A` auto-guesses the dimensions; without it you must supply them explicitly
- via `-x/-y/-z/-c/-s/-n`, and a flagless call will not work). Then confirm with
- `bart show -m ksp`. Alternative: read in Python with `twixtools`/`pymapVBVD`
- and write a `.cfl`.
- - **NumPy array** — write a `.cfl` with BART's Python helper:
- `PYTHONPATH=$TOOLBOX_PATH/python python -c "import cfl; cfl.writecfl(name, arr)"`
- (`cfl.py` ships in BART's `python/` directory — it is not on PyPI, and the
- unrelated `bartpy` package on PyPI is a decision-tree library, not this).
- Make sure coils land on dim 3.
- - **ISMRMRD `.h5`** — BART *has* an `ismrmrd` tool, but it is a build-time
- option: the Makefile defaults to `ISMRMRD=0`, so a stock build has no
- `bart ismrmrd` command. Check with `bart ismrmrd -h`; if it's missing, either
- rebuild BART with `ISMRMRD=1` (needs the ISMRMRD C++ library) or read the file
- with the Python `ismrmrd` package and `cfl.writecfl`. Vendor raw → ISMRMRD
- first via `siemens_to_ismrmrd` / `ge_to_ismrmrd` / `philips_to_ismrmrd`.
+ Do the work with the tested helpers below instead of re-deriving their maths; open only the row you need.
+ Hand-written versions of these steps failed silently in evaluation runs.
- **1. Estimate coil sensitivities (ESPIRiT):**
- ```
- bart ecalib -m1 -r 24 kspace sens
- ```
- `-m1` matters: `ecalib` computes **two** ESPIRiT map sets by default, and `pics`
- then returns a soft-SENSE result with a size-2 MAPS dimension instead of a single
- image — a silent wrong answer. `-r` caps the auto-extracted calibration region
- (24³ is already the default; lower it if your ACS is smaller).
+ ## Route
- **2. Reconstruct:**
- ```
- # Fully sampled: inverse FFT + coil combine
- bart fft -iu 7 kspace img_coils && bart rss 8 img_coils img
+ | Task | Use (`scripts/`) | Read only if needed |
+ |---|---|---|
+ | Undersampled multi-coil Cartesian: R, offset and ACS from the mask, GRAPPA, CG-SENSE, prewhitening, coil combination, SNR units | `mrfoundry_rawrecon.py`: `sampling_pattern`, `grappa_from_mask`, `sense_combine`, `prewhiten` | [raw-data checklist](references/raw-data.md) |
+ | Non-Cartesian (radial, spiral): exact forward model, NUFFT least squares; off-resonance with a field map | `mrfoundry_reconlib.py`: `offres_least_squares`, `smooth_field` | [raw-data checklist](references/raw-data.md#non-cartesian) |
+ | Water-fat separation, PDFF, R2* (multi-echo GRE, multi-peak fat) | `fatwater_lib.py`: `fat_offsets_hz`, `fit_fat_water`, `fat_fraction` | [quantitative maps](references/quantitative-maps.md#water-fat) |
+ | BART or SigPy pipelines; twix, ISMRMRD, `.cfl` files | `bart_recon.sh` | [BART and SigPy](references/bart-sigpy.md) |
+ | T1, T2, T2*, VFA, dictionaries, multi-echo spin echo with B1 (EPG) | `qmri_fit.py` | [quantitative maps](references/quantitative-maps.md) |
+ | ASL CBF and arrival time, single or multiple delays | `qmri_fit.py`: `asl_pairs_by_delay`, `slice_delays`, `asl_effective_efficiency` (background suppression), `m0_finite_tr`, `asl_cbf_pcasl`, `fit_asl_multidelay` | [ASL](references/quantitative-maps.md#asl) |
+ | DCE: signal to concentration, extended Tofts | `qmri_fit.py`: `spgr_concentration`, `fit_tofts` | [DCE](references/quantitative-maps.md#dce) |
+ | QSM: phase to field, background removal, dipole inversion, reference region | `qsm_lib.py`: `run_pipeline` | [QSM](references/quantitative-maps.md#qsm) |
+ | Fixed settings of a fitted model (nuisance values, spectra, fractions) | | [model calibration](../mri-research/references/model-calibration.md) |
+ | One-file standalone entrypoint | `python .agents/skills/mri-research-workflow/scripts/inline_helpers.py <module.py> <functions> --into <entrypoint.py>` copies the tested helpers | |
- # Undersampled — parallel imaging + compressed sensing (the workhorse):
- bart pics -l1 -r 0.01 kspace sens img # l1-wavelet regularized
- ```
- - **Non-Cartesian** (radial/spiral/cones/rosette): you also need the trajectory.
- Use `bart pics -t traj kspace sens img`. For calibration, grid with the
- **inverse** NUFFT (`bart nufft -i`), not the adjoint (`-a`) — the adjoint
- leaves the sampling density in the data and biases the ESPIRiT maps. Get the
- trajectory from the sequence/ISMRMRD, or `bart traj` for nominal.
- - **EPI is Cartesian.** Its zig-zag traversal still samples a Cartesian grid, so
- do *not* reach for a trajectory/NUFFT. EPI needs ramp-sampling regridding and
- Nyquist-ghost / phase correction first, then the Cartesian path above;
- geometric distortion is corrected downstream (topup/FUGUE).
+ Import with `sys.path.insert(0, '.agents/skills/mri-reconstruction/scripts')`. `python <module>.py --api` prints the
+ signatures (do not read the sources); `scripts/test_<module>.py` has worked examples. Never retype a helper into a
+ standalone entrypoint: copy it with `inline_helpers.py` (a retyped GRAPPA used R = 2 for R = 3 data and still gave
+ plausible maps).
- **3. Inspect:** check image dimensions, scaling, orientation, measurement
- residuals and coil-combination errors. Investigate aliasing and over-smoothing
- with calibration/model checks and a documented regularization sweep; raising
- `-r` is not a universal fix. For method comparisons, use the evaluation guide
- above and assess task-relevant feature preservation as well as image metrics.
+ ## Checks that prevent silent errors
- ## Runnable helper
+ 1. **Write the conventions down first**: axis order and which sample is k = 0, encoding sign
+ (exp(-i2πk·r): `ifft2c`; exp(+i2πk·r): `image_from_kspace(ksp, encoding_sign=+1)`), readout oversampling,
+ output grid [y, x], units and time origin of every input. Read protocol values from the file or sidecar.
+ 2. **Validate with an independent forward model**: simulate a known asymmetric object or parameter set
+ through your exact sampling and timing (`direct_dft_signed`, `direct_dft`, quadrature, analytic signals),
+ then run your pipeline: `orientation_check(img, truth)` must return `('identity', (0, 0))` and parameters
+ must come back. A round trip through your own transforms cannot reveal a sign, axis or timing error.
+ 3. **Noise**: Psi = E[n n^H] from noise samples [coil, samples] (`noise_covariance`); scale it by
+ noise dwell / imaging dwell (`scale_noise_covariance`); averaging N acquisitions divides it by N. SNR units:
+ `snr_units_image`; confirm the real-part noise SD is 1 on a pure-noise or pseudo-replica run, never from
+ low-magnitude pixels of the image.
+ 4. **Choose the reconstruction by simulated error** (`rank_reconstructions` with your sampling and measured
+ noise; `metric=` the endpoint error when the deliverable is a parameter map): exact data consistency is not
+ image quality; at R >= 3 it keeps or amplifies noise.
+ 5. **Fits**: use the stated model and time origin. Multi-echo magnitude at low SNR: combine coils with the SAME
+ phase-preserving weights for every echo (`sense_combine`), then pass `noise_sigma` (and `n_channels` for RSS)
+ to `fit_monoexp` or `fit_dictionary`; an RSS log-linear fit overestimates T2 by several percent. Water-fat:
+ `fit_fat_water` chooses among field-map candidates with a smooth field (voxelwise minima swap water and fat
+ in isolated high-fat regions), then `fat_fraction(water_real, fat_real, flip, TR, T1w, T1f, b1)`. Check map
+ units against expected ranges (`finish_check.py --range`).
+ 6. **Standalone entrypoints** read every protocol value (matrix, coils, acceleration, delays, echo times,
+ field strength, slice timing, frame times, scale factors) from the inputs; run them on a second simulated
+ input with OTHER values (another acceleration, field strength or echo train) before finishing.
- `scripts/bart_recon.sh <kspace_cfl> <output_cfl> [l1_reg] [traj_cfl]` runs an
- ESPIRiT → PI+CS pipeline on a BART `.cfl` k-space file. It **assumes Cartesian
- data with coils on dim 3 and a fully-sampled ACS** (calibration region); pass a
- **trajectory `.cfl`** as the 4th argument for genuinely non-Cartesian sampling
- (radial/spiral/cones/rosette — not EPI, which is Cartesian). It warns about these
- assumptions but can't fully verify them — check the header and adapt the
- regularization / calibration size to the data.
+ ## Before you finish
- ## SigPy (Python) alternative
+ python .agents/skills/mri-research-workflow/scripts/finish_check.py --require <every deliverable name> \
+ --rerun "python <entrypoint> <documented arguments with {inputs} and {out}>" --inputs <input folder>
- ```python
- import sigpy as sp, sigpy.mri as mr
- maps = mr.app.EspiritCalib(ksp).run() # coil maps
- img = mr.app.L1WaveletRecon(ksp, maps, lamda=0.01).run() # PI + CS
- # non-Cartesian: build a NUFFT from coords, use mr.app.SenseRecon
- ```
+ Put the entrypoint and all outputs in the folder the task names (the workspace root if none). Fix every
+ FAIL line and rerun the check. Report numbers read back from the saved outputs.
- ## Guardrails
+ ## Setup and scope
- - Confirm the acceleration factor and sampling (Cartesian vs non-Cartesian)
- before choosing a method — the wrong forward model gives garbage.
- - If BART isn't installed: https://codeberg.org/mrirecon/bart (source, active)
- with docs at https://mrirecon.codeberg.page/ — install and validate it when needed. If blocked, explain the
- limitation before switching to SigPy. Note the `github.com/mrirecon/bart` mirror is archived as of 2026.
- - **Report the SNR cost, not just the image.** Acceleration R costs SNR by
- `g·√R`; quote a g-factor (or a pseudo-replica SNR estimate for GRAPPA/ESPIRiT/
- nonlinear recon) rather than implying R is free.
- - **Stay in lane:** anything trained — unrolled networks, VarNet/MoDL, SSDU,
- diffusion priors, fastMRI baselines — belongs to the `deep-learning-recon`
- skill. Designing the *acquisition* or the trajectory belongs to
- `pulse-sequence-design`; this skill consumes a trajectory, it doesn't design one.
- - For method theory and citations, see the hub:
- https://github.com/KeWang0622/MRFoundry/blob/main/skills/mri-research/references/recon-methods.md
- and tool details at
- https://github.com/KeWang0622/MRFoundry/blob/main/skills/mri-research/references/tools.md
+ - Use the workspace interpreter (`.venv/bin/python` if present) and `-m pip install` only missing packages;
+ numpy and scipy cover every helper here. Read the [tool setup guide](../mri-research/references/tool-setup.md)
+ only if an install fails or you need BART, Julia or a GPU toolbox.
+ - Ongoing projects with `.mrfoundry/INDEX.md`: [project memory](../mri-research/references/project-memory.md).
+ Citations: [reading list](references/reading-list.md); method evidence and claims:
+ [evaluation guide](../mri-research/references/evaluation-and-attribution.md).
+ - Trained networks: `deep-learning-recon`. Acquisition design: `pulse-sequence-design`. Diffusion models:
+ `diffusion-mri`. Spectroscopy: `mri-research` (`scripts/mrs_lib.py`).