Expand description
SUSEP-Net deep-learning χ-separation (onnx feature).
SUSEP-Net (Li/Gao/Sun 2025) is a dual-branch 3D U-Net that maps three guidance
maps — QSM (χ_total, ppm), R2′ (Hz), local field (ppm) — to paramagnetic (χ+)
and diamagnetic (χ−) source magnitudes. Clean NCDHW ONNX export (three inputs
qsm,r2prime,lfs; two outputs chi_pos,chi_neg).
Recipe (mirrors the authors’ recon.py): z-score each input by the training
stats, zero outside the mask, post-pad each dim to a multiple of 8, run,
de-normalize the outputs, crop, and mask. We return χ− as a signed (≤ 0) value to match
the crate’s separation convention (chi_pos ≥ 0, chi_neg ≤ 0, chi_total).
Deviation: both decoders end in a 1×1 convolution with no activation (SQNet.py), so a
de-normalised magnitude can be negative, and test_demo.py saves it as it is. Here negative
χ+ and |χ−| are set to 0, since a negative source magnitude has no physical meaning, and
χ_total is χ+ + χ− after that. Every non-negative value is the network’s unchanged.
Array orientation. SUSEP-Net is a different group’s network from the χ-sepnet family and
its training orientation has not been established here; the authors’ recon.py does not
reorient, and neither does this function (it is handed a Grid, not an affine). Treat it as
assuming the usual axial convention — slice axis along B0 — and see
chisepnet for how little that turned out to be worth on the
χ-sepnet side; no equivalent measurement has been made for this network.
Whole volume vs patches. The authors run the whole volume in one forward pass, and
SusepNetParams::patch defaults to that (None). Activations then scale with the volume,
which a 32-bit host cannot afford — at 1 mm whole-brain the first conv block alone runs to
several GB against a 4 GB WASM heap — so such a host passes a patch and gets an overlapping
sliding window (0.75 stride, overlaps averaged) with memory bounded by the patch. Tiling a
net trained on whole volumes is an approximation: each patch is blind to structure
outside it. Patch dimensions must be multiples of 8 (three pooling levels).
Weights are not bundled; the caller passes the exported susep-net.onnx bytes
(see crate::models).
§Reference pipeline
Li M., …, Sun H., Gao Y. 2025 (arXiv:2506.13293):
- Training (§2.3): 3 T (Philips Ingenia Elition X), 0.9 × 0.9 × 1 mm, 8 echoes, TE1/ΔTE = 4.1/4.0 ms, with 64³ patches. The training inputs are simulated by the forward model (Eq. 1) from APART-QSM-reconstructed χ+/χ− and decay-kernel maps, so the network was never fit to one particular field-mapping chain.
- In-vivo inputs (§3.2): the local field from iQFM and the QSM from iQSM+, both computed directly from the multi-echo GRE phase. R2* and R2 come from mono-exponential fits in STI Suite (multi-echo GRE and multi-echo SE), and R2’ = R2* − R2.
- R2’ enters in Hz with no D_r scaling (the input normalisation in
SusepNetNorm).
Reference: Li, M., Chen, C., Li, Z., …, Sun, H., Gao, Y. (2025). “SUSEP-Net: Simulation-Supervised and Contrastive Learning-based Deep Neural Networks for Susceptibility Source Separation.” arXiv:2506.13293. Code: https://github.com/YangGaoUQ/SUSEP-Net
§Agreement with the original
tests/models_onnx/separation.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, SUSEP-Net (susep-net)
(QSMxT/QSM-CI#316; the fixture is regenerated in Actions by
tests/data/dl_separation_qsmci_small.py): χ+ and χ− NRMSE 2.4e-5 %, max |Δ| 1.7e-7 ppm
(measured 2026-10-30): float32 rounding. The original moves by about 1e-7 relative from run to
run, since its wrapper uses every CPU.
§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): susep-net.qsm-iqsm-plus.
Structs§
- Susep
NetNorm - Training z-score constants for SUSEP-Net (
all_mean_std.mat): each field is(mean, std). Inputs are normalized(x-mean)/std; outputs de-normalizedy*std + mean. - Susep
NetParams - Inference parameters for
susep_net.
Constants§
- POOL_
MULTIPLE - Three 2× pooling levels, so every patch dimension must be a multiple of this. Public (and
built without
onnx) so a host can check a patch, or state the rule, without restating the number. - WASM_
PATCH - A patch that fits a 32-bit (WASM) heap alongside a browser pipeline’s other volumes. Peak
memory, measured natively (single-threaded): about 1.8 KB per patch voxel plus ~0.9 GB for the
parsed model and buffers, so 2.0 GB at this patch, 2.8 GB at 128×128×64 and 12.5 GB for a
whole 184×256×128 volume. On the QSM-CI χ-separation phantom it agrees with the whole volume
to r ≥ 0.998 on both maps (
tests/chisep_xsepnet.rs).
Functions§
- susep_
net - Run SUSEP-Net χ-separation.