Skip to main content

Module iqsm

Module iqsm 

Source
Expand description

iQSM single-step deep-learning reconstruction (onnx feature).

iQSM (Gao 2022) maps raw wrapped MRI phase (radians) directly to susceptibility (ppm) — unwrapping, background removal and dipole inversion in one network (a learnable-Laplacian “LoT” front-end + a U-Net). The exported graph takes four inputs: phase, mask, te (s, scalar), b0 (T, scalar).

This mirrors the authors’ inference.run_iqsm: flip the phase sign, erode the mask by a radius-3 sphere, centre-pad each dim to a multiple of 16, run the net, multiply by the (padded) mask, and crop back. For multi-echo data each echo is reconstructed and combined with (magnitude·TE)² weighting by default (iqsm_multi_echo, IqsmEchoWeighting).

The authors run the whole volume in one pass, and so does iqsm. Its activations scale with the volume; the multi-echo entry point can instead run each echo patch by patch (a TileConfig), with memory bounded by the patch: an approximation, since the network’s unwrapping, background removal and inversion are not local, as for the other tiled networks (see super::tiled).

Weights are not bundled; the caller passes the exported iqsm.onnx bytes.

§Reference pipeline

From the authors’ code (sunhongfu/iQSM):

  • Normalisation: the LoT layer scales its output by 3 · 20e-3 / (B0 · TE) (models/unet_blocks.py:21-22), i.e. to the 3 T, TE = 20 ms reference, so any field strength and TE are handled through the te/b0 inputs.
  • Masking: with no mask supplied, run.py:499-506 runs FSL bet2 -m -f 0.5 on the first magnitude volume (bet2_utils.py:47,64).
  • Resolution: “for data with resolution finer than 0.7 mm isotropic, interpolate to 1 mm before reconstruction” (README.md:9; also legacy/matlab/demo_single_echo.m:21-23). The MATLAB demo interpolates anisotropic data to an isotropic grid at the finest voxel size in the complex domain (demo_single_echo.m:52-60). The Python inference.py accepts voxel_size but does not resample. This crate does not resample.
  • Phase masking: the MATLAB Save_Input.m:25 multiplies the phase by the eroded mask; the Python inference.py does not, and neither does this crate. The 3×3×3 LoT stencil output is multiplied by the eroded mask, so only the outermost shell of the eroded mask can differ.

§Agreement with the original

tests/models_onnx/field.rs runs it on a 36×32×28 box of in-vivo data (1 mm, 2.89 T, across the brain’s edge) against its original’s verified QSM-CI entry, iQSM bb163ef (iqsm) (QSMxT/QSM-CI#316; the fixture is regenerated in Actions by tests/data/dl_field_qsmci_small.py): per echo, NRMSE 9.8e-4 to 2.0e-3 %, max |Δ| up to 3.9e-6 ppm: float32 rounding.

The original runs one echo at a time and combines the echoes afterwards. The authors’ two implementations combine them differently (see IqsmEchoWeighting): their MATLAB echo fit, published with the paper, weights by mag·TE², and their Python driver by (mag·TE)². This crate defaults to (mag·TE)², the inverse-variance weight (each echo’s estimate has noise variance ∝ 1/(mag·TE)²), and offers the MATLAB’s mag·TE² as an option, for parity with it. On the box’s first and third echoes the driver’s combination is (mag·TE)² of its per-echo outputs to 6e-6 %, and the two weightings differ by 3.0 % NRMSE; iqsm_multi_echo matches the MATLAB fit of the original’s per-echo outputs to 9.4e-4 % (measured 2026-10-30), and with the Python weighting their (mag·TE)² combination to 9.5e-4 % (measured 2026-10-31).

§Pipeline rules

The facts above that a configuration can depart from are published as rules in crate::pipeline::rules::RULES (each with its strength, basis, reason and source): iqsm.mask-bet.

Re-exports§

pub use super::iqsm_echo_weighting::IqsmEchoWeighting;

Functions§

iqsm
Run iQSM on a single echo of wrapped phase.
iqsm_multi_echo
Multi-echo iQSM: reconstruct each echo and combine them with weighting (see IqsmEchoWeighting; the default is (magnitude·TE)², the inverse-variance weight). phases/magnitudes are per-echo volumes; tes the echo times (s). Without magnitudes the weights are TE² alone (uniform magnitude).
iqsm_with 🔒
lot_multi_echo 🔒
iqsm_multi_echo for either head of the network: model_id ("iqsm" or "iqfm") names the registry entry whose tile cost the tiled path budgets with.
sphere_erode 🔒
Binary erosion by a solid sphere of the given radius (voxels), matching scipy.ndimage.binary_erosion with border_value=0 (out-of-bounds = false).
tiled_echo 🔒
One echo patch by patch: erode the mask on the whole volume, then run run_patch (the network with no erosion of its own) on each tile. cost is the network’s tile cost, whose divisor (16: the LoT-Unet has four pooling levels) pads each patch.

Type Aliases§

Net 🔒
Runs the network on its input tensors: a whole-volume model (re-planned per shape) or a plan built once for a tile’s shape.