Submit your algorithm

You can add your own QSM reconstruction method to QSM-CI and have it automatically run and scored against everyone else's (on the same data, with the same scoring) so the comparison is fair. This page is a step-by-step guide to doing that.

You write your method in any language (Python, MATLAB, Julia, Rust, …); the qsm-ci CLI handles the scaffolding, running, and scoring. Each step below has an optional ▸ panel with the technical detail.

1. Install the CLI

Install qsm-ci with pip (a virtualenv or conda env is recommended):

pip install qsm-ci
qsm-ci doctor            # checks you have what you need (Docker, etc.)

2. Create your submission

Run qsm-ci new. It asks a couple of questions and scaffolds a submission folder: algorithms/<your-method>/ with the boilerplate (manifest, run.sh, a starter recon file) filled in, so you only write the reconstruction itself.

qsm-ci new               # pick a step and a language; it names and fills the folder

QSM is computed as a pipeline of steps. You implement one step, and QSM-CI takes care of feeding the right data in and out. One of the first questions is which step your method does:

StepTakes inProduces
Field mappingphase & magnitude imagestotal field
Background field removaltotal fieldlocal (tissue) field
Dipole inversionlocal fieldsusceptibility map (χ)
χ-separationlocal field, R2′, χ map (& magnitude)χ+ (paramagnetic) & χ− (diamagnetic) source maps

Most methods do exactly one of these. If yours combines two (or goes all the way from phase to χ in one shot), there are combined options too: the panel has the full list and the exact inputs.

The exact inputs, outputs, and units

Your code reads the files its step takes in from a folder called /input, and writes what it produces to /output. Every field map and the χ map is in ppm, on the same image grid as mask.nii.gz.

Combined steps ("spans"), for methods that cross boundaries:

SpanTakes inProduces
unwrap+bfrphase, magnitudelocal field
bfr+dipoletotal fieldsusceptibility (χ)
end-to-endphase, magnitudesusceptibility (χ)

The files themselves: phase.nii.gz (radians), magnitude.nii.gz, mask.nii.gz (brain mask), and params.json (the scan settings). The produced files are totalfield.nii.gz / localfield.nii.gz / chimap.nii.gz, all in ppm. To convert a field from Hz: ppm = Hz · 1e6 / (γ · B0), γ = 42.576e6 Hz/T.

χ-separation is the one stage that produces two maps. It reads the local field, r2prime.nii.gz (R2′, Hz), chimap.nii.gz (χ_total) and multi-echo magnitude.nii.gz, and writes both chi-para.nii.gz (χ+, paramagnetic / iron) and chi-dia.nii.gz (χ−, diamagnetic / myelin·calcium, stored as a positive magnitude) to /output. qsm-ci new scaffolds a working χ-separation starter; each source map is scored separately on the leaderboard.

params.json carries the scan settings your method may need:

{ "TE": [0.004, 0.012, 0.020, 0.028],   // echo time(s), seconds
  "B0": 3.0,                            // field strength, tesla
  "B0_dir": [0.0, 0.0, 1.0],            // B0 direction, image coords
  "voxel_size": [1.0, 1.0, 1.0] }       // mm

If your method has a tunable setting, declare it in algorithm.yml and a caller can change it with qsm-ci run <method> --set threshold=0.2; the value arrives in an optional /input/config.json. When nobody overrides it, the file simply isn't there and your default applies.

3. Write your reconstruction

Open the starter file (recon.py, recon.m, …) and fill in your method. It reads its inputs from /input and writes its result to /output as a field or susceptibility map in ppm. Your code runs in a container, so you point at an image that has your dependencies rather than packaging your code into one: your files are mounted in at run time.

Exactly how your code is run

You never invoke the container yourself: qsm-ci run does it locally, and QSM-CI's servers do the same when scoring. It runs in two phases: a setup phase (with internet) builds your environment, then a run phase (no internet) executes your code. Under the hood that run is roughly:

docker run --rm --network none \
  -v <your-folder>:/algo:ro \
  -v <inputs>:/input:ro \
  -v <output-dir>:/output \
  <environment> bash /algo/run.sh

You don't type this, but it fixes the rules your code has to play by:

  • Your code is mounted at /algo (you don't bake it into an image).
  • No internet during the run, so everything must already be in the environment.
  • /input is read-only and holds only the files your step needs.
  • Write each output under its expected filename to /output.
  • There's a 2-hour limit; crashing or producing no/garbled output counts as a failed run.
Choosing an environment for your code

Two options (you don't build a custom image with your code inside):

  • Point at a ready-made image that already has your dependencies (the shared Python image, a Neurodesk container, …). Nothing to build.
  • Add a small Dockerfile that starts from any base and installs/downloads what you need (this happens in the setup phase, which has internet). Don't copy your code in; it's mounted for you.

For Python, Julia, and Rust this is fully self-serve: your source runs directly. MATLAB has one extra step; see below.

If your method is in MATLAB

Scoring runs offline and license-free, so MATLAB needs one extra step: you compile your .m yourself (the only step that needs a MATLAB licence), and the result runs on the free MATLAB Runtime. QSM-CI can't compile it for you.

On your machine (needs MATLAB + Compiler, on Linux):

mcc -m recon.m -o recon        # -> a standalone program (a native Linux binary)

Put that program into a MATLAB Runtime image and upload the image to a public registry:

FROM containers.mathworks.com/matlab-runtime:r2026a   # the free Runtime
COPY recon /opt/qsm-ci/recon
RUN chmod +x /opt/qsm-ci/recon
# docker build -t ghcr.io/you/your-method:v1 .  &&  docker push ghcr.io/you/your-method:v1

Point your algorithm.yml's image: at that upload. QSM-CI runs the compiled program on the free Runtime, offline.

What you actually submit is just text: algorithm.yml, run.sh, and your recon.m source. The compiled program isn't part of the submission; it lives in the image you uploaded. (Python/Julia/Rust skip all this; the source itself runs.)

No MATLAB Compiler licence? You can instead run plain .m on a licensed MATLAB image, or reuse an existing licensed container, but compiling is the recommended route.

4. Try it out locally

Before submitting, run your method on some data to check it works. If you also give it a ground truth to score against, qsm-ci uses the exact same code the leaderboard does, so the numbers you see match what you'll get online:

qsm-ci run <your-method> --localfield lf.nii.gz --mask m.nii.gz --params p.json --truth chi.nii.gz

No ground truth to hand? Generate a synthetic brain phantom with qsm-forward; it comes with its ground truth. The running guide covers all of this.

How scoring works

Your step is scored two ways:

  • On its own: it's given the perfect (ground-truth) inputs for its step and scored against ground truth. A clean measure of just your step.
  • In combination: it's chained with other people's steps (say, every background-removal method feeding your dipole inversion) and scored on the final χ map. This is the comparison grid you see on the leaderboard.

Your method never sees the answer it's being scored against and has no internet during the run, so it can't peek. The official score always comes from QSM-CI's servers, which hold a hidden test phantom; running locally just confirms your method works and gives you a rough idea of where you'll land.

The test phantom is the Realistic In Silico Head Phantom from the QSM Reconstruction Challenge 2.0 (Marques et al., Magn Reson Med 2021;86(1):526–542, doi:10.1002/mrm.28716), simulated with qsm-forward. Please cite it if you report QSM-CI results.

5. Submit it

When you're happy, qsm-ci submit commits your submission on a branch and opens a pull request:

qsm-ci submit <your-method>

It walks you through each step and confirms before anything leaves your machine. You need a fork of QSM-CI to open a PR; if you don't have one, qsm-ci submit can create it for you with the GitHub CLI (or take the URL of one you already have).

QSM-CI then runs and scores your method on its hidden test data and posts the numbers on your request. Once it's reviewed and accepted, your method joins the leaderboard alongside everyone else's.