Skip to content

Running noninteractively

For scripting, batch processing, and reproducible runs, drive QSMxT from the command line. (Prefer to point and click? See Running interactively — everything below is available there too.)

qsmxt run is the heart of the tool: it discovers every phase/magnitude run in a BIDS dataset and reconstructs a quantitative susceptibility map for each.

Terminal window
qsmxt run <BIDS_DIR> [OUTPUT_DIR]
  • <BIDS_DIR> — input BIDS directory
  • [OUTPUT_DIR] — defaults to <BIDS_DIR>; results go to <OUTPUT_DIR>/derivatives/qsmxt/

With no options, QSMxT runs with sensible defaults end to end. Need a BIDS dataset first? See Converting DICOMs to BIDS below.

Each run flows through the pipeline below. The method at every stage is configurable — see Algorithms.

  1. Masking — generate a brain/region mask (--mask-preset, or custom --mask sections).
  2. Phase offset removal — remove receiver phase offsets on multi-echo data.
  3. Phase unwrapping — romeo, laplacian, prelude or medi-region-growing.
  4. Echo combination / B0 mapping — combine echoes into a field map (--b0-estimation, --b0-weight-type).
  5. Background field removal — vsharp, pdf, lbv, and more.
  6. Dipole inversion — ilsqr (default), rts, tv, tgv, medi, … (31 algorithms, and HEIDI streaking reduction after them).
  7. Referencing — reference the susceptibility values (e.g. to the mean).

Not every inversion runs every stage: total-field methods (TFI, QSMART, AutoQSM, NeXtQSM, multi-echo TGV) skip background field removal, and the wrapped-phase methods (iQSM, iQSM+, single-echo TGV) skip unwrapping and echo combination too. See Algorithms.

Terminal window
qsmxt run study/bids \
--mask-preset robust-threshold \
--unwrapping-algorithm romeo \
--bf-algorithm vsharp \
--qsm-algorithm ilsqr
Option Choices
--qsm-algorithm 20 classical (rts, tv, tkd, tgv, medi, ilsqr, …) and 12 deep-learning (xqsm, qsmnet, iqsm, …)
--unwrapping-algorithm romeo, laplacian (QSMART’s reference), prelude, medi-region-growing
--bf-algorithm vsharp, pdf, lbv, ismv, sharp, resharp, harperella, iharperella, bfrnet, iqfm
--mask-preset robust-threshold, bet, hd-bet, bet-and-phase, rs2-net, mindgrab
--masking-input magnitude-first, magnitude, magnitude-last, phase-quality

Every value of every option is listed in the CLI reference and described in Algorithms.

--masking-input overrides the image the mask is computed from and combines with --mask-preset — e.g. --mask-preset bet --masking-input magnitude-first runs BET on the first-echo magnitude. For fully custom masking, see --mask in Algorithms.

--pipeline starts from a whole pipeline: qsmxt-defaults, or an original toolbox’s (medi-toolbox, sti-suite-ilsqr, sti-suite-star, qsmart, heidi, chi-sep-ilsqr, chi-sep-medi, chi-sepnet, decompose). A toolbox pipeline is its method and the original authors’ settings that method brings with it (see Recommendations). The stages it covers (masking, field mapping, background removal, inversion, referencing, χ-separation) start from QSMxT’s defaults; every other flag given then changes any setting.

Terminal window
# The MEDI toolbox's pipeline: BET, the complex fit, region-growing unwrapping, PDF, MEDI
qsmxt run study/bids --pipeline medi-toolbox
# The same, with a different MEDI regularisation weight
qsmxt run study/bids --pipeline medi-toolbox --medi-lambda 500

The run’s settings file (pipeline_config.toml) records the pipeline as reference_pipeline, and the TUI’s command preview writes a configuration on a pipeline as --pipeline plus the flags where it departs from it.

Many methods only work as intended with certain other settings: the ones their authors used. QSMxT checks the configuration against what each chosen method’s reference pipeline does (the pipeline rules in QSM.rs, each with its source) and says where it departs. It never changes a setting on its own.

  • Required: the method cannot run otherwise (MEDI-mSMV on fewer than three echoes). The run is refused, with the change that resolves it.
  • Recommended or discouraged: there is a stated reason another value is better. The authors give it, QSM.rs measured it, or the combination is known to fail. Each is logged as a warning before the runs start, a dismissed one too (marked “dismissed, kept”). --accept-recommendations applies them.
  • Suggested: measured to be better on some data but not all (for example in vivo, but not on the simulated head). Logged with its reason like a recommendation, but --accept-recommendations leaves it, and it is not listed in the methods Note. Apply it by setting the value yourself.
  • Original authors’ setting: the method’s authors used another value (their code, paper or README does it), and no reason is given that it is better. It is not a recommendation. It is logged as information, listed in the methods Note, and applied only with --accept-reference-settings. It has nothing to dismiss (--dismiss-recommendation refuses it).
Terminal window
# List every recommendation for this configuration and dataset, and each covered
# setting's reference value
qsmxt run study/bids --qsm-algorithm medi --dry
# Apply every recommended value that is not dismissed (the authors' settings are left as they are)
qsmxt run study/bids --qsm-algorithm medi --accept-recommendations
# Also follow the chosen methods' original settings (here MEDI's PDF, BET and complex fit)
qsmxt run study/bids --qsm-algorithm medi --accept-recommendations --accept-reference-settings
# Keep a choice: dismiss a recommended or discouraged finding for the setting's current value
qsmxt run study/bids --qsm-algorithm fansi --fansi-te first \
--dismiss-recommendation fansi.te-spacing=first

--dry with either flag shows the settings that would change and the resulting command. Two equally strong suggestions that disagree on one setting are left for you to choose, and a recommendation wins over an authors’ setting on the same setting. A dismissal is saved in the configuration (dismissed_recommendations), and stops applying once the setting changes. At the end of a run, the log lists what was applied, what was dismissed, and the departures that were kept. Each run’s record keeps the same, and methods.md has a Note, before its references, that lists the departures the runs kept.

Process only part of a dataset with glob patterns:

Terminal window
# Only these subjects
qsmxt run study/bids --include "sub-01*" "sub-02*"
# Everything except a session
qsmxt run study/bids --exclude "*ses-pilot*"
# Cap the number of echoes used
qsmxt run study/bids --num-echoes 4
Terminal window
# Receiver phase-offset removal (on by default for multi-echo)
qsmxt run study/bids --phase-offset-removal true
# Bipolar readout correction (needs ≥ 3 echoes)
qsmxt run study/bids --bipolar-correction

ROMEO unwrapping has several modes — per-echo (default) or template-based, with inter-echo 2π correction. See qsmxt run --help for the full set of --romeo-* flags.

Reproduce a run exactly by saving settings to a TOML file and passing --config:

Terminal window
qsmxt init > pipeline.toml # generate a starting config
qsmxt run study/bids --config pipeline.toml

See Configuration.

Intermediate results are cached to disk. Re-running the same dataset skips any stage that already completed, so tweaking a late-stage option doesn’t recompute the whole pipeline.

study/bids/derivatives/qsmxt/
└── sub-01/
├── anat/
│ ├── sub-01_…_Chimap.nii
│ ├── sub-01_…_Chimap.json
│ ├── sub-01_…_mask.nii
│ ├── sub-01_…_desc-qsm_mask.nii
│ └── sub-01_…_part-mag_T2starw.nii
└── fmap/
├── sub-01_…_desc-total_fieldmap.nii
└── sub-01_…_desc-total_magnitude.nii

Outputs are uncompressed NIfTI (.nii), each with a JSON sidecar recording how it was made (GeneratedBy, units, and for susceptibility maps the reference). The total field is published in Hz under fmap/. Every file and its name is listed in Output files.

mask is the brain mask; desc-qsm_mask is the part of it the susceptibility map is defined on — the brain mask less the rim that background-field removal eroded. The Chimap is referenced over, and is 0 outside, desc-qsm_mask; the χ-separation maps (--do-chisep) are fitted over it and are 0 outside it too.

At the top of derivatives/qsmxt/, methods.md describes the processing in prose ready for a methods section, with citations for the exact methods used for your data and parameters; pipeline.svg draws the same pipeline, one box per step (pipeline.mmd is the same graph in Mermaid); pipeline_config.toml holds the resolved settings (reusable with --config), and qsmxt.log the log.

Some things are only decided as a run goes: a mask supplied with --use-custom-masks that cannot be used is computed instead, R2′ is measured where a multi-echo spin-echo scan allows and estimated otherwise. Once every run is done, methods.md and the graph are written from what the runs did: a choice every run made is stated as done, and the graph’s boxes say how many runs skipped a step or decided something in it. They cover every run in the output directory that completed with the current settings, whichever command processed it, so re-running one subject with --include does not narrow them; a run last processed with other settings, or whose last attempt did not finish, is left out. A run processed differently from the rest gets its own methods text and graph in changes/ (<run>_methods.md, <run>_desc-ran_pipeline.mmd and .svg); the folder is only there when some run differs. Each run’s methods text, a record of what each step did and why (*_runrecord.json), and its graph are also kept in its workflow/ directory.

qsmxt run --dry prints the pipeline graph and the methods text as planned, without running anything or writing any file.

Every susceptibility map is referenced (see Referencing), and each map’s sidecar records what with QsmReference (mean, none, or the region), QsmReferenceOffsetPpm (the value subtracted), QsmReferenceVoxels (how many voxels it was measured over) and, for a region, QsmReferenceLabels (its label ids). The same facts for every map of every run are collected in desc-reference_summary.tsv at the top of the derivatives:

Column Meaning
subject, session, acquisition, reconstruction, inversion, run The run’s BIDS entities
map The map, by its BIDS suffix with any desc- (Chimap, desc-singlepass_Chimap, desc-cosmos_Chimap, …)
reference What zero was pinned to (QsmReference)
offset_ppm The value subtracted, in ppm (QsmReferenceOffsetPpm)
n_voxels Voxels the reference was measured over (QsmReferenceVoxels)

It is the file to scan for a subject whose reference region came out small or empty.

Some workflows want the maps back in DICOM — to push them into a PACS, open them in a clinical viewer, or hand them to a radiologist beside the source study. --export-dicom writes a DICOM series for every final map alongside the NIfTIs:

Terminal window
qsmxt run study/bids --export-dicom

Each map gets its own folder under the subject’s extra_files/, one file per slice:

study/bids/derivatives/qsmxt/
└── sub-01/
├── anat/
│ └── sub-01_…_Chimap.nii
└── extra_files/
└── sub-01_Chimap_dicoms/
├── sub-01_Chimap_0001.dcm
├── sub-01_Chimap_0002.dcm
└── …

Every final map the run produced is exported. The magnitude and χ need no extra flag; the supplementary maps are exported once you’ve asked for them:

Map Produced by Folder suffix / --dicom-outputs token
Magnitude (the echoes combined) any run with magnitude images magnitude
Susceptibility (χ) the default pipeline Chimap
SWI --do-swi swi
SWI minIP --do-swi minIP
SWI filtered phase --swi-filtered-phase desc-swi_phase
Paramagnetic SMWI --do-smwi desc-paramagnetic_smwi
Diamagnetic SMWI --do-smwi desc-diamagnetic_smwi
Paramagnetic SMWI minIP --do-smwi desc-paramagneticsmwi_minIP
Diamagnetic SMWI minIP --do-smwi desc-diamagneticsmwi_minIP
T2* --do-t2starmap T2starmap
R2* --do-r2starmap R2starmap
R2 --do-r2map R2map
R2′ --do-r2primemap R2primemap
Paramagnetic χ --do-chisep desc-paramagnetic_Chimap
Diamagnetic χ --do-chisep desc-diamagnetic_Chimap
Total χ --do-chisep desc-total_Chimap
Segmentation (a DICOM SEG, below) --do-segmentation, --use-custom-dseg dseg

To export only some of them, pass those tokens to --dicom-outputs (case-insensitive), comma-separated or by repeating the flag; a name that is not in the table is refused:

Terminal window
qsmxt run study/bids --do-chisep --export-dicom \
--dicom-outputs chimap,desc-paramagnetic_chimap --dicom-outputs desc-diamagnetic_chimap

In the TUI, the Reconstruction tab’s DICOM section lists the maps the settings make, each ticked; untick the ones to leave out.

The multi-orientation reconstructions stay NIfTI-only.

The segmentation as a DICOM SEG. The segmentation is written as one DICOM Segmentation object (sub-01_dseg_dicoms/sub-01_dseg.dcm): each structure is a binary segment, drawn on a series exported with it on the same grid, so a viewer overlays it there (OHIF, Weasis 4.3 or later, and 3D Slicer with the QuantitativeReporting extension; check your PACS’s DICOM conformance statement for Segmentation Storage). The series is the first of these the export writes: the magnitude, χ, SWI, T2*, R2*, R2, R2′, the χ-separation maps, SMWI (not a minIP, which is a projection); the log names it. A --dicom-outputs that selects dseg and none of those the run makes is refused before the run. SynthSeg’s structures are coded with the standard’s codes for brain structures (DICOM PS3.16 CID 7153 and CID 7140), with left and right as modifiers, so a viewer can name them in any language. A supplied segmentation (--use-custom-dseg) need not use FreeSurfer’s numbering, so its labels are coded only as intracranial structures and named from its dseg.tsv.

Filing them beside the source study. By default the derived series carry a synthesised identity: patient fields are left empty and a stable StudyInstanceUID is derived from the subject and session. That’s fine for a local viewer, but a PACS will file the maps as a study of their own. Point --source-dicom at the original DICOMs and QSMxT inherits the identity from the first readable DICOM it finds there — PatientID, StudyInstanceUID, FrameOfReferenceUID, study date/time, accession number — so the maps land beside the acquisition they came from:

Terminal window
qsmxt run study/bids --export-dicom --source-dicom /data/dicoms/sub-01

Without the original DICOMs to hand, a dataset converted with qsmxt dicom-convert --keep-dicom-uids does nearly as well: its sidecars keep the study’s StudyInstanceUID and FrameOfReferenceUID, so the exported series file into the original study and line up with its images, though with no patient fields. --source-dicom still wins where it is given.

SeriesInstanceUID and SOPInstanceUID are always newly generated: these are distinct derived series, never a rewrite of the originals. Every instance is tagged DERIVED\SECONDARY, and each map gets a stable SeriesNumber so viewers list them in a predictable order.

A few more things worth knowing:

  • Slice geometry (ImageOrientationPatient, ImagePositionPatient, SliceLocation, spacing) is read from the output NIfTI’s affine, so the series overlays the source acquisition in a viewer and reflects any oblique resampling the run applied.
  • Instances are uncompressed MR Image Storage, Explicit VR Little Endian. The acquisition’s patient position, repetition time, echo train length, body part and laterality are filled in too: from --source-dicom when given, else from the run’s BIDS sidecar (PatientPosition, RepetitionTime, EchoTrainLength, BodyPart, as dcm2niix and qsmxt dicom-convert write them), else left empty rather than guessed. The segmentation is a Segmentation Storage object.
  • Export is best-effort: a failure is logged and the run still succeeds.
  • --export-dicom is a pipeline setting, so it can live in a config file as export_dicom = true under [pipeline], and it carries through to qsmxt slurm. --source-dicom and --dicom-outputs are flags on qsmxt run itself.

This is the mirror image of dicom-convert below, which brings DICOMs in. Both directions are built into the binary — no external converter to install.

QSMxT needs BIDS-formatted input. The dicom-convert command builds it for you from a directory of DICOMs, applying an automatic best-guess classification to every series with no prompts — ideal for scripting and batch jobs.

Terminal window
qsmxt dicom-convert <DICOM_DIR> <OUTPUT_DIR>
  • <DICOM_DIR> — input directory, searched recursively
  • <OUTPUT_DIR> — output BIDS directory

Before writing anything, inspect how QSMxT classifies your data:

Terminal window
qsmxt dicom-convert /data/dicoms study/bids --dry-run

Example output:

Detected 6 unique series (auto-classified):
acq-SWI3mm SWI_3mm [Magnitude] 2×TEs=[9.4…19.7]ms (uncombined, 32 coils) -> sub-01_acq-SWI3mm_rec-uncombined_coil-<NN>_echo-<N>_part-mag_MEGRE
acq-SWI3mm SWI_3mm [Magnitude] 2×TEs=[9.4…19.7]ms -> sub-01_acq-SWI3mm_echo-<N>_part-mag_MEGRE
acq-SWI3mm SWI_3mm [Magnitude] 2×TEs=[9.4…19.7]ms -> sub-01_acq-SWI3mm_rec-filtered_echo-<N>_part-mag_MEGRE
acq-SWI3mm SWI_3mm [Phase] 2×TEs=[9.4…19.7]ms (uncombined, 32 coils) -> sub-01_acq-SWI3mm_rec-uncombined_coil-<NN>_echo-<N>_part-phase_MEGRE
acq-SWI3mm SWI_3mm [Phase] 2×TEs=[9.4…19.7]ms -> sub-01_acq-SWI3mm_echo-<N>_part-phase_MEGRE
acq-t1scout t1_scout [Extra] TE=2.0ms -> extra_data/1_t1scout

Each row is a unique series, deduplicated across subjects, so you review a classification once even if many subjects share it. The arrow shows the name it will get. Warnings follow the list: an incomplete export (missing slices, empty files, magnitude and phase with different numbers of images), a series whose files name different patients, and two original series that look like the same images.

By default the conversion is fully de-identified (dcm2niix’s -ba y): the sidecars carry the acquisition parameters but no patient fields, dates or DICOM UIDs. --keep-dicom-uids keeps the study’s StudyInstanceUID, SeriesInstanceUID, StudyID and dates (dcm2niix’s -ba o, which still strips the patient’s name, ID, birth date and the like) and adds the FrameOfReferenceUID, under the DICOM keywords as BIDS recommends for metadata it does not define:

Terminal window
qsmxt dicom-convert /data/dicoms study/bids --keep-dicom-uids

A later DICOM export then files its series into the original study in a PACS, without --source-dicom.

  • Gradient-echo magnitude, phase, real and imaginary images — from the DICOM image type, GE’s private image-type tag, or the Enhanced DICOM frame type; split correctly even when a vendor packs them under a single SeriesInstanceUID (some Philips and GE exports). Only gradient-echo sequences are labelled this way.
  • Echoes — a single-echo acquisition is named T2starw, a multi-echo one MEGRE with echo-N. Series exported one echo at a time are numbered by echo time.
  • Runs and acquisitions — series of one protocol acquired together are one run. A repeat with the same settings is run-2; a repeat with different settings (echo times, TR, flip angle, matrix, slice thickness, voxel size, receive coil) is a separate acquisition, acq-<protocol>2.
  • The scanner’s own reconstructions — kept next to the images they came from, each with its own name: susceptibility map rec-scanner_Chimap, T2*/R2* maps rec-scanner_T2starmap/rec-scanner_R2starmap, SWI rec-swi_part-mag_T2starw, minimum-intensity projection rec-mip_T2starw, filtered magnitude rec-filtered. Other reconstructions of the same images get a rec- label from their image type or description; the magnitude from the same reconstruction as the phase is the main one.
  • T1-weighted structurals — MPRAGE, SPACE/TFE T1, MP2RAGE UNI images and other T1w series are T1w; MP2RAGE inversion images are inv-1/inv-2 MP2RAGE, and its T1 map T1map.
  • Individual coil images — each coil element of an uncombined acquisition is converted separately with a coil-NN label (rec-uncombined when the scanner-combined images are there too). A series with a single coil element is the combined image.
  • Duplicates — the same images exported twice are converted once; two different original reconstructions of one acquisition keep the first and put the other in extra_data/.
  • Everything else — scouts, reformats, FLAIR, diffusion, Dixon and other vendor maps are kept in sub-*/[ses-*/]extra_data/ as dcm2niix names them, outside BIDS (listed in .bidsignore). Reports, presentation states and other non-image files are listed and skipped.
  • DICOM files without the DICOM file header (older exports) are read as well.

No two series are ever written to the same file: all names are decided before anything is written, and a name two series would share is reported as an error naming both. A series dcm2niix cannot convert leaves nothing behind.

Output is a BIDS tree of sub-*/[ses-*/]anat/ NIfTIs with JSON sidecars (EchoTime in seconds), a dataset_description.json, a README and a .bidsignore, ready for the pipeline. Already have NIfTIs? You can assemble a BIDS dataset by hand — see Input data for the expected layout, and run qsmxt validate to check it’s pipeline-ready.

For large cohorts, QSMxT can fan work out across an HPC cluster instead of running everything on one machine.

Terminal window
qsmxt slurm study/bids --account myaccount

--account (the SLURM account to charge) is required. This writes SLURM batch scripts for your dataset to <OUTPUT_DIR>/derivatives/qsmxt/slurm/, and with --submit submits them. Run qsmxt slurm --help for the available options (partition, time limits, resource requests, and run selection).

qsmxt slurm accepts the same pipeline options as qsmxt run (algorithms, masking, per-algorithm parameters, --config, --num-echoes, --force, --clean-intermediates, --debug, and DICOM export’s --source-dicom and --dicom-outputs, …), so any qsmxt run command becomes a SLURM one by swapping the subcommand and adding the scheduler flags:

Terminal window
qsmxt slurm study/bids --qsm-algorithm tgv --unwrapping-algorithm laplacian \
--account myaccount --time 04:00:00

The settings are checked, and every run is checked against them, before any script is written, so a bad setting is reported once rather than by every job. The resolved pipeline is written to <OUTPUT_DIR>/derivatives/qsmxt/pipeline_config.toml and every generated job runs against that file.

  • One job per run (qsmxt_<run>.sh) runs qsmxt run --no-dataset-steps --include <run>: that run’s pipeline and nothing else. It logs to logs/<run>.log and writes nothing at dataset level.
  • One aggregation job (qsmxt_aggregate.sh) runs qsmxt aggregate over all the runs once every run job has succeeded (--dependency=afterok:<all run job ids>). It does what a local qsmxt run does after its runs finish:
    • multi-orientation (COSMOS/STI) reconstruction across the runs of each group;
    • desc-reference_summary.tsv, the dataset-level statistics table and cohort figures;
    • BIDS-Prov records under prov/;
    • dataset_description.json, pipeline_config.toml, methods.md, pipeline.mmd/.svg, changes/;
    • qsmxt.log, which collects every run’s log followed by its own;
    • --clean-intermediates, if you asked for it.

The result is the same dataset a local qsmxt run produces, apart from timestamps and the command recorded in the provenance (qsmxt slurm …).

--submit submits the run jobs and then the aggregation job with the dependency. Without it, bash <OUTPUT_DIR>/derivatives/qsmxt/slurm/submit.sh does the same.

Each job’s memory request is estimated from its run’s size and the pipeline. The aggregation job is sized for the largest multi-orientation group. --mem sets one figure for every job instead, with a warning for any run estimated to need more.

If a run job fails, SLURM never starts the aggregation job: with afterok it stays pending (or is cancelled, depending on the cluster’s settings). Fix the cause, re-run that run’s script (sbatch slurm/qsmxt_<run>.sh; its finished steps are cached), then submit slurm/qsmxt_aggregate.sh yourself. qsmxt aggregate refuses any run that has not completed under the same configuration, and names it.

Even without a scheduler, a single qsmxt run is parallel. QSMxT processes runs concurrently and detects available memory to choose a safe degree of parallelism automatically, so it fills your cores without exhausting RAM. Inside a Slurm job or a container, the job’s or container’s memory limit counts, not the whole machine’s memory: the log’s Memory: line says which applied. --mem-limit-gb sets the limit yourself.

qsmxt run, slurm, aggregate and validate exit with an error (status 1) when the BIDS directory does not exist, or when it has no runs to process (none at all, or none that match --include, --exclude and --num-echoes).

Because intermediate results are cached to disk, a job that is requeued or resumed picks up where it left off rather than recomputing completed stages — handy when a cluster preempts or time-limits a job.

  1. Convert and validate on a login node or small interactive job:
    Terminal window
    qsmxt dicom-convert /data/dicoms study/bids
    qsmxt validate study/bids
  2. Dial in settings (locally or via the TUI) and save them:
    Terminal window
    qsmxt init > pipeline.toml
  3. Generate and submit the jobs (one per run, then the aggregation job):
    Terminal window
    qsmxt slurm study/bids --config pipeline.toml --account myaccount --submit