Skip to main content

Module fansi

Module fansi 

Source
Expand description

FANSI nonlinear TV / TGV dipole inversion.

Nonlinear total-variation (nlTV) and nonlinear total-generalized-variation (nlTGV) QSM dipole inversion with a nonlinear (wrapped-phase) data-fidelity term, solved with ADMM plus an inner complex-argument Newton iteration.

The nonlinear fidelity models the field data as exp(i * D x) and matches it to the wrapped local-field phase, which makes the reconstruction robust to phase-wrap / high-field regimes.

References:

  • nlTV: Milovic, C., Bilgic, B., Zhao, B., et al. (2018). “Fast nonlinear susceptibility inversion with variational regularization.” Magnetic Resonance in Medicine, 80(2):814-821. https://doi.org/10.1002/mrm.27073
  • nlTGV: the total-generalized-variation variant of the above.

Ported faithfully from the FANSI toolbox nlTV.m and nlTGV.m (https://gitlab.com/cmilovic/FANSI-toolbox).

§Units and weights

Like the other inversions, these take the local field in ppm and return ppm. nlTV.m and nlTGV.m work on the phase in radians, so the field is converted with 2*pi*gamma*b0*te first and the result converted back at the end; left in ppm, sin(x) ~ x and the data term is not nonlinear. b0 and echo_times are scan parameters that the pipeline fills from metadata, and FansiParams::te picks the echo time the conversion uses (by default the reference’s choice; see there).

The data weight is the magnitude, normalised as in FANSI’s in vivo example (min(2*mask*mag/max(mag), 1)), and squared as nlTV.m/nlTGV.m square params.weight. The magnitude is required: with a flat (mask) weight, the Newton step’s denominator W*cos(z2 - phase) + mu2 reaches 0 where the phase nears +-pi and the solver diverges (as nlTV.m does).

The finite differences are FANSI’s, on unit spacing whatever the voxel size.

§Reference pipeline

FANSI-toolbox at commit 93044a3 (https://gitlab.com/cmilovic/FANSI-toolbox). Its only full pipeline is script_DICOM_Philips.m (3T Philips, 5 echoes, TE1 4.0 ms, spacing 3.7 ms, 0.73x0.73x1 mm, B0 along z; header L1-6), which says it is “NOT an optimized pipeline”:

  • Mask: magnitude (M0) threshold plus an R2* range, then morphology, ending with a 2-voxel erosion “to avoid data corruption in the cortical areas” (L125-170); L126 “We encourage to use dedicated tools as BET2”.
  • Unwrapping: SEGUE per echo (L190-194); L183-187 also recommend ROMEO, and L196-197 say “Avoid Laplacian-based unwrapping for multiecho processing, as they remove harmonic components of the phase”.
  • Echo combination: a nonlinear multi-echo fit (nlme_L2gradient, weight mag*TE, L259-276), which also gives the noise map cnoise.
  • Background removal: FANSI’s own multiscale SMV, msmv_noextrapolation(..., 5) (L335); L290 allows LBV, PDF or V-SHARP instead. (This MSMV is not the mSMV of bg_removal.msmv_refine.)
  • Input: local*deltaTE, deltaTE = TE(4) - TE(2) with the comment “You may use also any individual echo time” (L364, L370). Weight m0.*mask./cnoise, divided by its maximum (L371-372). nlTGV is run with the same parameters as wTV (L421).
  • alpha1: no default in nlTV.m/nlTGV.m (required field); the toolbox’s demos use 2e-4 (script_fansi_phantom.m L196, script_qsmchallenge.m L73) and 3e-4 with mu1 = 1e-2 for nlTV on the 2016 challenge data (script_qsmchallenge.m L111-112), which is the challenge’s own LBV local field and BET mask eroded by 5 voxels (L22-24). The default here is that 2e-4; the in vivo pipeline picks alpha1 by L-curve / frequency analysis (L374-410).
  • Default weight in nlTV.m/nlTGV.m is ones (defaultW = ones(N), with a warning). Every demo call of nlTV/nlTGV passes a magnitude-derived weight (script_fansi_phantom.m L199 “Recommended for nonlinear algorithms”, script_cosmos_phantom.m L88, script_qsmchallenge.m L106-108); the mask-only weights in the demos go to the linear wTV/wTGV.

§Parity

Against the verified original (QSM-CI pair fansi-nltv, fansi-nltgv; FANSI-toolbox 93044a3, identical to licensed MATLAB R2023b), final-map χ NRMSE at both sides’ defaults on QSM-CI’s simulated head and the KKI travelling head (QSM-CI parity run 41403629314, QSM.rs#399–#403): nlTV sim 0.0069 %, KKI 0.0030 %; nlTGV sim 0.0063 %, KKI 0.0039 %, from nlTV 11.0 % / 9.5 %, nlTGV 11.0 % / 9.6 % (sim / KKI) before. The kernel was the whole gap. What remains is single (MATLAB) vs double (here) precision and FANSI’s γ, 42.58 against the crate’s 42.576 (the wrappers keep theirs). Fixture: tests/fansi_qsmci_parity.rs (relative L2 below 1e-5 on an oblique, anisotropic phantom).

§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): fansi.no-laplacian-multi-echo, fansi.te-spacing.

Structs§

FansiParams
FANSI nlTV/nlTGV parameters (nlTV.m / nlTGV.m defaults where they have them).

Functions§

fansi
FANSI nlTV/nlTGV dipole inversion.
nltgv 🔒
Nonlinear total-generalized-variation dipole inversion (FANSI nlTGV.m).
nltv 🔒
Nonlinear total-variation dipole inversion (FANSI nlTV.m).
norm2 🔒
L2 norm of a real slice.
phase_and_weight 🔒
ppm -> radians, and the squared data weight (see module docs).
spectral_mul_assign 🔒
Multiply a complex spectral array in place by a complex multiplier array.