Loading a series and an exam
A GE exam is a pile of series — a NIfTI here, a raw .mat recon there, sometimes both, sometimes
neither — and the folders holding them can be named anything. Loading one by hand means knowing in advance whether series 6
is an anatomical scan or a spectrum, and picking the matching GESeries subclass yourself. GEExam
exists so you don’t have to: point it at the exam’s data folder and it works that out from the
files’ own metadata (the JSON sidecar’s SeriesDescription, or the .mat header’s se_desc).
| Function | What it does here |
|---|---|
ExamBase | scans a data folder and classifies every series |
MRSSeries | loaded directly, for when you already know what a series is |
from pathlib import Path
from mnutils.GEExam import ExamBase
from mnutils.GESeries import MRSSeries
from mnutils.testing import build_fake_exam
DATA_FOLDER = build_fake_exam("brain_mrs_mrsi_exam")1. Point ExamBase at the exam¶
BASE_FOLDER is the exam’s top-level folder; ExamBase looks for a data/ subfolder inside it
by default (override with DATA_FOLDER= if your layout differs). On init it indexes every series’
NIfTI and raw-data presence and prints an overview — useful on its own when you’ve
forgotten what’s in a dataset.
exam = ExamBase(DATA_FOLDER)That overview is also available as a DataFrame, and series_dict shows the classification
ExamBase settled on before loading anything — nothing here has touched a NIfTI’s pixel data
yet.
exam.exam_overview.head()exam.series_dict[6], exam.series_dict[8](mnutils.GESeries.MRSSeries, mnutils.GESeries.MRSISeries)2. Load one series¶
load_series instantiates the right class and stores it on exam.all, keyed by series ID.
Everything about a series you’d normally want — its spectrum, its header fields, its scan time —
comes from that one object afterward.
exam.load_series(6)
mrs = exam[6]
print(type(mrs).__name__, mrs.protocol_name)MRSSeries MRS_unloc
GEExam picked MRSSeries, so the spectrum comes back with an averages dimension — one
spectrum per repetition. Averaging over it is the usual first step:
print(mrs.spec.dims, mrs.spec.shape)
avg = mrs.avg_spec
print(avg.dims, avg.shape)('averages', 'chemical_shift') (64, 2048)
('chemical_shift',) (2048,)
3. Or skip the exam and load a series directly¶
If you already know a series is single-voxel spectroscopy, you can construct MRSSeries (or any
other GESeries subclass — see the class hierarchy) directly with the
same (DATA_FOLDER, SERIES_ID) signature ExamBase uses internally. This is what you’d reach
for in a one-off script where pulling in the whole exam overview is more than you need.
mrs_direct = MRSSeries(DATA_FOLDER / "data", 6)
print(mrs_direct.spec.equals(mrs.spec))True
4. Load several series, or all of them¶
load_series also accepts a list; load_all loads everything ExamBase classified. For an exam
with several MRSI grids this can take a while — load only what you need.
exam.load_series([6, 8])
print(sorted(exam.all.keys()))[6, 8]