Tutorial
A complete, copy-pasteable walk-through: from a cold Julia session to a reconstructed cardiac image. It uses OCMR because it is the smallest source and needs no account or token — every file is a direct HTTP download of a few tens of MB.
If a term is unfamiliar, see Concepts & data model and the Glossary.
1. Install
using Pkg
Pkg.add(url = "https://github.com/hakkelt/MRITestData.jl") # not yet in the General registryMRITestData pulls in MRIFiles (and HDF5) but no reconstruction package. For the reconstruction step below you also need:
Pkg.add(["MRIReco", "MRICoilSensitivities"])2. Find a dataset
using MRITestData
# Every source:
list_sources()
# 7-element Vector{AbstractSource}: MRIDATA, OCMR_SOURCE, CMRXRECON2024, …
# Fully-sampled OCMR cine acquisitions at 1.5 T:
entries = list_datasets(OCMR_SOURCE; fully_sampled = true, field_strength = 1.5)
entry = first(entries)entry is a DatasetEntry. Inspect it:
julia> entry.id
"fs_0001_1_5T"
julia> entry.anatomy, entry.orientation, entry.field_strength
(:heart, :short_axis, 1.5)
julia> entry.receiver_channels # nothing — OCMR doesn't publish coil counts;
nothing # it's inside the file, known after load_raw
julia> entry.extra["scanner_model"]
"Siemens MAGNETOM Avanto"Filters accept a scalar (==), a tuple/vector (membership) or a predicate:
list_datasets(OCMR_SOURCE; field_strength = (1.5, 3.0))
list_datasets(OCMR_SOURCE; fully_sampled = e -> e == true)To search across sources, or on extra keys, or by free text, use query instead — see Searching across sources.
3. Download
Pick a download destination once — it is persisted across sessions, and nothing downloads until it is set:
MRITestData.set_download_path!(:cache) # the per-package Scratch cache
# MRITestData.set_download_path!("/data/mri") # …or a directory of your choicepath = download_dataset(entry)
# progress bar …
# "/…/scratchspaces/…/ocmr/fs_0001_1_5T.h5"The file lands in the configured location and is not re-downloaded next time. Transfers stream to a .part file and are renamed atomically, so an interrupted download never corrupts the cache. is_cached(entry) / cache_path(entry) query it; clear_cache() empties it.
You can skip this step — load_raw downloads on demand.
4. Load the raw data
raw = load_raw(entry) # or load_raw(path)raw is a RawAcquisitionData:
julia> length(raw.profiles) # readout lines
5808
julia> size(raw.profiles[1].data) # samples × channels
(384, 18) # 18-channel coil array
julia> raw.params["encodedSize"]
3-element Vector{Int64}: [384, 176, 1]
julia> raw.params["trajectory"]
"cartesian"See What load_raw returns for the full structure. The temporal cine frames are on the ISMRMRD contrast axis.
5. Reconstruct (fully sampled → direct)
MRITestData stops at raw data. Hand it to MRIReco.jl:
using MRIReco
acq = AcquisitionData(raw) # re-exported by MRIReco
params = MRIReco.defaultRecoParams()
params[:reco] = "direct" # inverse FFT — correct for fully-sampled data
img = MRIReco.reconstruction(acq, params) # AxisArray [x, y, z, echo, coil, rep]
julia> size(img)
(384, 176, 1, 25, 18, 1) # 25 cine frames, 18 coilsCombine coils (root-sum-of-squares) and view the first frame:
using MRIReco: AxisArrays
mag = sqrt.(sum(abs2, img.data; dims = 5))[:, :, 1, 1, 1, 1]
# (optional) drop the 2× readout oversampling
mag = mag[(size(mag, 1) ÷ 4 + 1):(3 * size(mag, 1) ÷ 4), :]
using PNGFiles
PNGFiles.save("ocmr_frame01.png", permutedims(mag ./ maximum(mag)))That is a clean cardiac cine frame:

6. Reconstruct undersampled data (parallel imaging)
OCMR's us_* files, all of CMRXRECON300, and fastMRI test/prostate/breast are undersampled — a direct recon aliases. Use CG-SENSE ("multiCoil") with coil sensitivity maps from an ESPIRiT calibration. Full runnable example: Reconstructing undersampled data.
using MRITestData, MRIReco, MRICoilSensitivities
using MRIReco: flag_is_set, flag_remove!
entry = first(list_datasets(CMRXRECON300; offline = true)) # R ≈ 3, ships ACS
raw = load_raw(entry)
acq = AcquisitionData(raw)
# CMRxRecon-300 writes the ACS lines into the same file, flagged
# ACQ_IS_PARALLEL_CALIBRATION. AcquisitionData drops them from the imaging data;
# rebuild a calibration-only acquisition (flag cleared) to estimate the maps.
calib = [p for p in raw.profiles if flag_is_set(p, "ACQ_IS_PARALLEL_CALIBRATION")]
for p in calib; flag_remove!(p, "ACQ_IS_PARALLEL_CALIBRATION"); end
acq_calib = AcquisitionData(RawAcquisitionData(raw.params, calib))
params = MRIReco.defaultRecoParams()
params[:reco] = "multiCoil"
params[:senseMaps] = espirit(acq_calib, (6, 6), 24; eigThresh_1 = 0.02, eigThresh_2 = 0.95)
img = MRIReco.reconstruction(acq, params) # de-aliased, coil-combinedThe same direct recon on this data would show ~3-fold aliasing:

Next steps
- Usage — filtering, the interactive browser, the self-updating index, caching.
- Dataset contents — exactly what each of the seven sources contains.
- Reconstruction with MRIReco — every source and modality, with example output dimensions and the reconstruction-method references.
- FAQ & troubleshooting — Synapse tokens, fastMRI credentials, disk footprint, network failures.