Skip to content

Add phase correction options#1054

Open
tsalo wants to merge 13 commits into
mainfrom
phase-correction
Open

Add phase correction options#1054
tsalo wants to merge 13 commits into
mainfrom
phase-correction

Conversation

@tsalo

@tsalo tsalo commented Jun 6, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds optional phase correction for complex-valued (magnitude + phase) diffusion data. After complex MP-PCA denoising (dwidenoise), the complex signal can be rephased and the real channel retained for downstream processing instead of the magnitude — avoiding the Rician noise-floor bias that magnitude data carries. Three rephasing methods are provided, the report gains a phase-correction panel, and the phase→radian conversion is aligned to the MRtrix convention. The code for these methods is adapted from fragrussu/MRItools.
  • New CLI flag --dwi-phase-correction {none,tv,tvc,dc} (default none):
    • tv — total-variation rephasing on the magnitude/phase stack (Eichner 2015)
    • tvc — paper-faithful TV on the complex signal (Eichner 2015); wrap-free
    • dc — decorrelated-phase convolution filtering (Sprenger 2017); wrap-free
  • When enabled, the real channel after rephasing replaces the magnitude downstream. The option requires --denoise-method dwidenoise and phase data (a BIDS part-phase file); the parser warns and ignores it otherwise. Tunables dwi_phase_tv_weight (default 6.0) and dwi_phase_dc_kernel (default Opt5) are exposed in config.

Changes by area

  • New interface — qsiprep/interfaces/phase.py (515 lines)
    • rephase_tv, rephase_tv_complex, rephase_dc rephasing functions plus DC convolution kernels (B3–Opt5)
    • PhaseCorrect Nipype interface (method, tv_weight, dc_kernel) that writes the real channel and a report figure
  • Workflow wiring — workflows/dwi/merge.py
    • init_dwi_denoising_wf branches: when phase correction is on, inserts a PhaseCorrect node (+ phasecorrection report sink) in place of ComplexToMagnitude; otherwise behavior is unchanged
    • Boilerplate text updated per method; warns that phase correction is skipped for concatenated (post-merge) series
  • Derivatives — workflows/dwi/derivatives.py
    • Tags DWI/bval/bvec/gradient outputs with part-real when phase correction is active
  • Phase scaling fix — interfaces/dwi_merge.py
    • PhaseToRad now rescales to [−π, π] (was [0, 2π]) to match MRtrix's complex dwidenoise convention (mrcalc … pi 4096 -div -mult -polar). This matters for the tv real channel, which is not invariant to a global phase offset.
  • Gradient-file lookup — interfaces/images.py
    • New _find_gradient_file helper: complex acquisitions share one .bval/.bvec pair without the part- entity, so
  • ConformDwi falls back to a part-stripped name.
  • Report — data/reports-spec.yml
    • New "Complex Phase Correction" panel: real channel alongside the imaginary residual (low/high-b); a clean correction leaves only noise in the residual.
  • Docs — docs/preprocessing.rst, docs/conf.py, boilerplate.bib
    • Reorganized "Denoising and Merging Images" into per-step prose sections (denoising, phase correction, Gibbs, bias correction, b0 harmonization, ordering, combining)
    • Fixed all Sphinx warnings: refreshed 7 .. workflow:: directives to current signatures (seeded qsiprep.config in conf.py), converted 2 non-buildable examples to static code blocks, repaired a cross-reference, and added jezzard1995/brett2001 bib entries (with eichner2015real, sprenger2017real, yeh2019differential citations)
  • Tests (6 new files, ~375 lines)
    • test_interfaces_phase.py — each method recovers a known real signal and does not imprint magnitude anatomy onto the residual; DC kernels normalized; PhaseCorrect writes the real channel
    • test_interfaces_images.py — _find_gradient_file part-stripping fallback
    • test_parser_phase.py / test_config_phase.py — flag parsing, defaults, bad-value rejection
    • test_workflows_phase.py — node present when enabled, magnitude path unchanged when disabled, part-real tagging
    • test_boilerplate_phase.py — citations present
  • Notes

@tsalo tsalo added the enhancement New feature or request label Jun 6, 2026
@codecov-commenter

codecov-commenter commented Jun 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.53659% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 47.92%. Comparing base (b142af8) to head (67735b6).

Files with missing lines Patch % Lines
qsiprep/interfaces/phase.py 79.22% 11 Missing and 5 partials ⚠️
qsiprep/utils/phase.py 81.17% 9 Missing and 7 partials ⚠️
qsiprep/cli/parser.py 16.66% 4 Missing and 1 partial ⚠️
qsiprep/interfaces/images.py 76.92% 2 Missing and 1 partial ⚠️
qsiprep/interfaces/dwi_merge.py 0.00% 2 Missing ⚠️
qsiprep/workflows/dwi/merge.py 84.61% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1054      +/-   ##
==========================================
+ Coverage   47.13%   47.92%   +0.78%     
==========================================
  Files          66       68       +2     
  Lines        9836    10035     +199     
  Branches     1084     1123      +39     
==========================================
+ Hits         4636     4809     +173     
- Misses       4979     4995      +16     
- Partials      221      231      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread qsiprep/cli/parser.py Outdated
@tsalo

tsalo commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@fragrussu, this PR adapts your denoising code from imgrephaseTV.py and imgrephaseDC.py, and incorporates it into QSIPrep's denoising workflow. I was wondering, would you be willing to look at a few example outputs to see if they look right to you? I've noticed that there is more structure in the imaginary residuals for the low-b volumes than I'd expect, but this is the first time I've looked at outputs from this kind of method.

Also, I know that your code is licensed permissively, but would you like us to add you as a contributor to QSIPrep to reflect the fact that I'm incorporating your code with minimal modification?

@tsalo tsalo marked this pull request as ready for review June 8, 2026 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants