MATLAB pipeline for preprocessing and reconstructing randomly undersampled 3D-EPI data acquired on GE and Siemens 3T scanners. Successor to fmri-prep. Sequence design lives in rand3depi.
| Library | Purpose |
|---|---|
| Pulseq | Siemens sequence definition (mr.opts) |
| TOPPE | GE sequence definition & ift3 |
| BART | ESPIRiT sensitivity maps, coil compression, L1-SENSE reconstruction |
| Orchestra (private) | Reading GE ScanArchive .h5 files |
| hmriutils | EPI ramp-sample gridding, odd/even ghost correction |
epi-preprocessing/
├── config.m # All user-editable paths and tunable parameters (edit this first)
├── run_preprocessing.m # Batch driver — calls preprocess() for each sequence in cfg.seqnames
├── preprocess.m # Stage 1 — raw data → zero-filled k-space volume (one sequence)
├── run_bart.m # Stage 2 driver — L1-wavelet + TV reconstruction via BART pics
├── run_cg_sense.m # Stage 2 driver — CG-SENSE reconstruction (no BART dependency)
├── run_rss.m # Stage 2 driver — root-sum-of-squares (no smaps, no BART)
├── recon_frames.m # Stage 2 core — shared smaps loading + frame-loop
├── cg_sense.m # CG-SENSE solver (unregularized conjugate gradient)
├── set_seq_paths.m # Helper — populate per-sequence cfg.seqname / cfg.fn.* fields
└── process_smaps.m # Helper — mask, crop, and resize sensitivity maps
params.m is not stored in the repository — it is loaded at runtime from cfg.fn.params, which points to the per-acquisition sequence directory in the data folder.
The pipeline runs in two sequential stages. Edit config.m to point at your data before running anything.
run_preprocessing.m is the batch driver: it reads cfg.seqnames and calls preprocess() once per sequence. preprocess.m converts raw scanner output for one sequence into a zero-filled, coil-compressed k-space volume ready for iterative reconstruction.
noise scan ──► whitening matrix
│
GRE data ──► whiten ──► PCA compression matrix (Nvcoils virtual coils)
│ │
▼ │
ESPIRiT smaps ◄───────────┤
(saved to disk) │
│
cal data ──► whiten ──► compress ──►┤──► odd/even phase offsets (a)
│ kx trajectory (kxo / kxe)
│
EPI data ──► whiten ──► compress ──► grid (NUFFT) ──► phase-correct
│
▼
scatter into zero-filled volume
[Nx, Ny, Nz, Nvcoils, Nframes]
│
▼
write to .mat via matfile
(frame-by-frame, bounded memory)
Key memory note: ksp_gre and ksp_epi_raw are never in memory simultaneously. The output is written frame-by-frame using matfile so the full time series is never allocated.
Checkpoint/resume: last_completed_frame is written to the output matfile after each frame. If preprocess.m is interrupted and restarted, it detects the checkpoint and skips already-completed frames.
Nvcoils selection: Nvcoils is chosen automatically from the eigenvalue spectrum of the whitened GRE sample covariance. Components are retained until cfg.cc_energy_thresh of total variance is explained, subject to a minimum of 2R (ensuring enough virtual coils for SENSE reconstruction).
A quick sanity-check reconstruction (RSS or matched-filter SENSE) of the first 6 steady-state frames is displayed at the end via interactive4D.
All three drivers read the zero-filled k-space produced by Stage 1 and delegate to the shared recon_frames.m, passing a per-frame reconstruction function handle. Each driver then saves its own .mat file (v7.3) containing the complex image and all reconstruction parameters.
zero-filled k-space ──► (load all frames, then parfor)
│
sensitivity maps ──────────── ┤
▼
run_bart.m: BART pics -R W:7:0:λ_l1 -R T:7:0:λ_tv -i 100 -S
run_cg_sense.m: unregularized CG-SENSE
run_rss.m: 3-D IFFT + root-sum-of-squares (no smaps, no BART)
│
▼
img [Nx, Ny, Nz, Nframes]
│
▼
.mat write to recon/basic/ (image + recon parameters)
run_bart.m uses BART pics with L1-wavelet (-R W:7:0:λ_l1) and total-variation (-R T:7:0:λ_tv) regularization over spatial dims. The combined terms trigger ADMM. The -S flag is required because the randomized EPI trajectory has no ACS region; iteration count is 100 to ensure convergence at R = 6. Output filename: <seqname>_recon_bart_l1_r<λ_l1>_tv_r<λ_tv>.mat.
run_cg_sense.m uses the built-in cg_sense.m solver — a bare conjugate-gradient SENSE loop with no regularization. No BART dependency. Output filename: <seqname>_recon_cgs_i<N>.mat.
run_rss.m applies a 3-D IFFT followed by root-sum-of-squares coil combination — no sensitivity maps, no BART, no regularization. Requires only standard MATLAB and TOPPE (toppe.utils.ift3). Output filename: <seqname>_recon_rss.mat.
All three save to <datdir>/recon/basic/.
Called from preprocess.m (Stage 1) and, as a fallback, from recon_frames.m (Stage 2). Applies a four-step post-processing pipeline to the raw maps returned by makeSmaps:
- Support mask — threshold the last ESPIRiT eigenvalue map; zero out background voxels.
- z-crop — trim the GRE volume symmetrically in z to match the EPI slab FOV. Assumes shared isocenter; mismatched isocenters will cause spatial misregistration.
- Interpolation —
imresize3to the EPI acquisition grid[Nx, Ny, Nz]. - Normalization — divide by the root-sum-of-squares across coils so
sum(|s_c|²) ≤ 1everywhere, matching the ESPIRiT convention expected by BARTpics.
| Parameter | EPI | GRE (sensitivity maps) |
|---|---|---|
| Resolution | 2.4 × 2.4 × 2.4 mm | 2.0 × 2.0 × 2.0 mm |
| Matrix | 90 × 90 × 60 | 108 × 108 × 108 |
| FOV | 21.6 × 21.6 × 14.4 cm | 21.6 × 21.6 × 21.6 cm |
| TE | 30 ms | ~1/Δf_fat + 0.8 ms (in-phase) |
| TR (shot) | volumeTR / Nshots | 6 ms |
| Volume TR | 800 ms | — |
| Flip angle | Ernst (~13°) | Ernst (~4°) |
| Acceleration R | 6 | — |
| Echo train length | 75 | — |
| Virtual coils | auto (≥ 2R) | — |
| Regularization λ | 0.005 | — |
| B0 | 3 T | 3 T |
- Clone the repo and add dependencies to your MATLAB path.
- Edit
config.m— setcfg.datdirandcfg.seqnames(one or more sequence names). All per-sequence paths are resolved automatically byset_seq_paths.m. - Run Stage 1:
For a single sequence set
run('run_preprocessing.m')cfg.seqnames = {'caipi'}. For batch, add more entries:Setcfg.seqnames = {'caipi', 'pd'};
cfg.interactive = falseto suppress blocking visualization windows during batch runs. - Inspect the sanity-check reconstruction. Adjust
cfg.delay(k-space center offset; runcalibrate_delay.mto tune automatically) andcfg.threshold_maskif needed. - Run Stage 2 (choose one):
Each script processes all sequences in
run('run_bart.m') % L1-wavelet + TV SENSE via BART pics run('run_cg_sense.m') % Unregularized CG-SENSE, no BART needed run('run_rss.m') % Root-sum-of-squares, no smaps, no BART needed
cfg.seqnames. - The final reconstruction is saved as a
.matfile (v7.3) with the complex image and all reconstruction parameters:- BART:
<datdir>/recon/basic/<seqname>_recon_bart_l1_r<λ_l1>_tv_r<λ_tv>.mat - CG-SENSE:
<datdir>/recon/basic/<seqname>_recon_cgs_i<N>.mat - RSS:
<datdir>/recon/basic/<seqname>_recon_rss.mat
- BART:
All tunable parameters live in config.m. Key fields:
| Field | Default | Description |
|---|---|---|
cfg.seqnames |
{'caipi', 'pd'} |
Cell array of sequence names to process; add entries for batch runs |
cfg.cc_energy_thresh |
0.9 | Fraction of coil-data variance to retain; auto-selects Nvcoils from GRE eigenvalue spectrum with a 2R lower bound |
cfg.delay |
−1 | k-space center offset (samples); scalar or containers.Map keyed by seqname; adjust if ghost artifacts appear (see calibrate_delay.m) |
cfg.SENSEmethod |
'bart' |
'bart' (ESPIRiT) or 'pisco' |
cfg.threshold_mask |
0.2 | ESPIRiT eigenvalue threshold for support mask |
cfg.lamb_l1 |
0.005 | L1-wavelet regularization weight λ for BART pics (-R W:7:0:λ) |
cfg.lamb_tv |
0.005 | Total-variation regularization weight for BART pics (-R T:7:0:λ) |
cfg.num_iter |
100 | Max CG iterations for cg_sense(); matches the -i 100 flag used in run_bart.m |
cfg.Nframes |
Inf |
Maximum frames to reconstruct in Stage 2; defaults to all available frames in the matfile if the file contains fewer |
cfg.doSENSE |
true |
false falls back to root-sum-of-squares |
cfg.useParfor |
false |
true uses parfor for parallel frame reconstruction; false runs serially (easier debugging) |
cfg.interactive |
false |
true enables blocking interactive4D windows and the eigenvalue figure |
cfg.showEPIphaseDiff |
true |
Plot odd/even phase difference during calibration (only when cfg.interactive is also true) |
cfg.useOrchestra |
true |
Use Orchestra library to read ScanArchive .h5 files |
- Sequence design: rextlfung/rand3depi
- Previous pipeline: rextlfung/fmri-prep