Skip to content

Commit 798768d

Browse files
changed order of gibbs unringing
1 parent 0feec00 commit 798768d

File tree

4 files changed

+95
-24
lines changed

4 files changed

+95
-24
lines changed

micaflow/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,8 @@ def format_help(self):
797797
"--output", required=True, help="Output path for the denoised image (.nii.gz)"
798798
)
799799
denoise_parser.add_argument("--b0-denoise", action='store_true', help="Denoise b0 volumes separately (default: False)")
800-
800+
denoise_parser.add_argument("--gibbs", action='store_true', help="Apply Gibbs ringing correction (default: False)")
801+
denoise_parser.add_argument("--threads", type=int, help="Number of threads to use (default: 1)")
801802

802803
# Motion Correction command
803804
motion_corr_parser = subparsers.add_parser(

micaflow/resources/Snakefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -537,14 +537,16 @@ if RUN_DWI:
537537
bvec = BVEC_FILE
538538
output:
539539
denoised = f"{TEMP_DIR}/{SUBJECT}_{SESSION}_denoised_DWI.nii.gz"
540-
threads: LIGHT_THREADS
540+
threads: HEAVY_THREADS
541541
shell:
542542
"""
543543
micaflow denoise \
544544
--input {input.moving} \
545545
--bval {input.bval} \
546546
--bvec {input.bvec} \
547-
--output {output.denoised}
547+
--output {output.denoised} \
548+
--gibbs \
549+
--threads {threads}
548550
"""
549551
rule dwi_b0_extraction:
550552
input:
@@ -606,7 +608,7 @@ if RUN_DWI:
606608
output:
607609
corrected = f"{TEMP_DIR}/{SUBJECT}_{SESSION}_denoised_bias-corrected_DWI.nii.gz",
608610
b0_corrected = f"{TEMP_DIR}/{SUBJECT}_{SESSION}_biascorrected-b0.nii.gz"
609-
threads: HEAVY_THREADS
611+
threads: LIGHT_THREADS
610612
shell:
611613
"""
612614
micaflow bias_correction \
@@ -615,7 +617,6 @@ if RUN_DWI:
615617
--b0-output {output.b0_corrected} \
616618
--output {output.corrected} \
617619
--shell-dimension {SHELL_DIMENSION} \
618-
--gibbs \
619620
--threads {threads}
620621
"""
621622
rule b0_synthseg:
@@ -945,7 +946,7 @@ if RUN_DWI:
945946
output:
946947
corrected = f"{TEMP_DIR}/{SUBJECT}_{SESSION}_denoised_bias-corrected_DWI.nii.gz",
947948
b0_corrected = f"{TEMP_DIR}/{SUBJECT}_{SESSION}_biascorrected-b0.nii.gz"
948-
threads: HEAVY_THREADS
949+
threads: LIGHT_THREADS
949950
shell:
950951
"""
951952
micaflow bias_correction \
@@ -955,7 +956,6 @@ if RUN_DWI:
955956
--output {output.corrected} \
956957
--mask {input.mask} \
957958
--shell-dimension {SHELL_DIMENSION} \
958-
--gibbs \
959959
--threads {threads}
960960
"""
961961

micaflow/scripts/bias_correction.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,24 @@ def print_help_message():
435435
{YELLOW}--output{RESET} FLAIR_corrected.nii.gz \\
436436
{YELLOW}--mode{RESET} 3d
437437
438+
{BLUE}# Example 5: Custom shell dimension{RESET}
439+
micaflow bias_correction \\
440+
{YELLOW}--input{RESET} DWI.nii.gz \\
441+
{YELLOW}--output{RESET} DWI_corrected.nii.gz \\
442+
{YELLOW}--b0{RESET} b0.nii.gz \\
443+
{YELLOW}--b0-output{RESET} b0_corrected.nii.gz \\
444+
{YELLOW}--shell-dimension{RESET} 4
445+
446+
{BLUE}# Example 6: With Gibbs ringing removal{RESET}
447+
micaflow bias_correction \\
448+
{YELLOW}--input{RESET} DWI.nii.gz \\
449+
{YELLOW}--output{RESET} DWI_corrected.nii.gz \\
450+
{YELLOW}--mask{RESET} brain_mask.nii.gz \\
451+
{YELLOW}--b0{RESET} b0.nii.gz \\
452+
{YELLOW}--b0-output{RESET} b0_corrected.nii.gz \\
453+
{YELLOW}--mode{RESET} 4d \\
454+
{YELLOW}--gibbs{RESET}
455+
438456
{CYAN}{BOLD}────────── WHAT IS BIAS FIELD? ────────────────────────{RESET}
439457
440458
{GREEN}Bias field (intensity non-uniformity):{RESET}
@@ -628,7 +646,7 @@ def bias_field_correction_3d(image_path, output_path, mask_path=None, gibbs=Fals
628646

629647

630648
def bias_field_correction_4d(image_path, mask_path=None, output_path=None,
631-
b0_path=None, b0_corrected_path=None, shell_dimension=3, gibbs=False, threads=1):
649+
b0_path=None, b0_corrected_path=None, shell_dimension=3):
632650
"""
633651
Apply N4 bias field correction to a 4D diffusion image.
634652
@@ -654,8 +672,6 @@ def bias_field_correction_4d(image_path, mask_path=None, output_path=None,
654672
shell_dimension : int, default=3
655673
Dimension along which diffusion volumes are organized (0-indexed).
656674
For standard NIfTI: dimension 3 (4th dimension).
657-
gibbs : bool, optional
658-
If True, apply Gibbs ringing removal before bias correction.
659675
660676
Returns
661677
-------
@@ -721,24 +737,13 @@ def bias_field_correction_4d(image_path, mask_path=None, output_path=None,
721737
print(f"{CYAN}Loading 4D diffusion image...{RESET}")
722738
img = ants.image_read(image_path)
723739

724-
if gibbs:
725-
print(f"{CYAN}Running Gibbs ringing removal on 4D data...{RESET}")
726-
arr = img.numpy()
727-
gibbs_removal(arr, slice_axis=2, n_points=3, inplace=True, num_processes=threads)
728-
img = img.new_image_like(arr)
729-
730740
img_data = img.numpy()
731741
print(f" Image shape: {img_data.shape}")
732742
print(f" Shell dimension: {shell_dimension}")
733743

734744
if b0_path:
735745
print(f"{CYAN}Loading b=0 image...{RESET}")
736746
b0_img = ants.image_read(b0_path)
737-
if gibbs:
738-
print(f"{CYAN}Running Gibbs ringing removal on b=0 image...{RESET}")
739-
arr_b0 = b0_img.numpy()
740-
gibbs_removal(arr_b0, slice_axis=2, n_points=3, inplace=True, num_processes=threads)
741-
b0_img = b0_img.new_image_like(arr_b0)
742747
else:
743748
b0_img = None
744749

@@ -1054,7 +1059,7 @@ def run_bias_field_correction(image_path, output_path, mask_path=None, mode="aut
10541059
if mode == "4d":
10551060
return bias_field_correction_4d(
10561061
image_path, mask_path, output_path,
1057-
b0_path, b0_corrected_path, shell_dimension, gibbs, threads
1062+
b0_path, b0_corrected_path, shell_dimension
10581063
)
10591064
else: # 3d
10601065
return bias_field_correction_3d(image_path, output_path, mask_path, gibbs, threads)

micaflow/scripts/denoise.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@
4646
--output <path/to/denoised_dwi.nii.gz> \\
4747
--b0-denoise
4848
49+
# With Gibbs ringing removal
50+
micaflow denoise \\
51+
--input <path/to/dwi.nii.gz> \\
52+
--bval <path/to/dwi.bval> \\
53+
--bvec <path/to/dwi.bvec> \\
54+
--output <path/to/denoised_dwi.nii.gz> \\
55+
--gibbs
56+
4957
Python API Usage:
5058
----------------
5159
>>> from micaflow.scripts.denoise import run_denoise
@@ -67,6 +75,15 @@
6775
... output="denoised_dwi.nii.gz",
6876
... b0_denoising=True
6977
... )
78+
>>>
79+
>>> # With Gibbs ringing removal
80+
>>> output = run_denoise(
81+
... moving="raw_dwi.nii.gz",
82+
... moving_bval="dwi.bval",
83+
... moving_bvec="dwi.bvec",
84+
... output="denoised_dwi.nii.gz",
85+
... gibbs=True
86+
... )
7087
7188
Pipeline Integration:
7289
--------------------
@@ -118,6 +135,7 @@
118135
import os
119136
import numpy as np
120137
from dipy.denoise.patch2self import patch2self
138+
from dipy.denoise.gibbs import gibbs_removal
121139
from dipy.io.gradients import read_bvals_bvecs
122140
from colorama import init, Fore, Style
123141

@@ -158,6 +176,8 @@ def print_help_message():
158176
{CYAN}{BOLD}─────────────────── OPTIONAL ARGUMENTS ───────────────────{RESET}
159177
{YELLOW}--b0-denoise{RESET}: Denoise b0 volumes separately (default: False)
160178
{MAGENTA}Experimental - not recommended for most cases{RESET}
179+
{YELLOW}--gibbs{RESET} : Apply Gibbs ringing removal after denoising
180+
{YELLOW}--threads{RESET} : Number of threads for Gibbs removal (default: 1)
161181
162182
{CYAN}{BOLD}──────────────────── EXAMPLE USAGE ──────────────────────{RESET}
163183
@@ -176,6 +196,14 @@ def print_help_message():
176196
{YELLOW}--output{RESET} denoised_dwi.nii.gz \\
177197
{YELLOW}--b0-denoise{RESET}
178198
199+
{BLUE}# With Gibbs ringing removal{RESET}
200+
micaflow denoise \\
201+
{YELLOW}--input{RESET} raw_dwi.nii.gz \\
202+
{YELLOW}--bval{RESET} dwi.bval \\
203+
{YELLOW}--bvec{RESET} dwi.bvec \\
204+
{YELLOW}--output{RESET} denoised_dwi.nii.gz \\
205+
{YELLOW}--gibbs{RESET}
206+
179207
{CYAN}{BOLD}────────────────── PATCH2SELF ALGORITHM ──────────────────{RESET}
180208
181209
{GREEN}How it works:{RESET}
@@ -226,7 +254,7 @@ def print_help_message():
226254
print(help_text)
227255

228256

229-
def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False):
257+
def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False, gibbs=False, threads=1):
230258
"""
231259
Denoise diffusion-weighted images using the Patch2Self algorithm.
232260
@@ -251,6 +279,10 @@ def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False):
251279
b0_denoising : bool, optional
252280
If True, denoise b0 volumes separately. Default: False.
253281
Standard practice is to exclude b0 volumes from denoising.
282+
gibbs : bool, optional
283+
If True, apply Gibbs ringing removal after denoising. Default: False.
284+
threads : int, optional
285+
Number of threads to use for Gibbs ringing removal. Default: 1.
254286
255287
Returns
256288
-------
@@ -293,6 +325,15 @@ def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False):
293325
... output="denoised_dwi.nii.gz",
294326
... b0_denoising=True
295327
... )
328+
>>>
329+
>>> # With Gibbs ringing removal
330+
>>> output = run_denoise(
331+
... moving="raw_dwi.nii.gz",
332+
... moving_bval="dwi.bval",
333+
... moving_bvec="dwi.bvec",
334+
... output="denoised_dwi.nii.gz",
335+
... gibbs=True
336+
... )
296337
"""
297338
# Validate input files exist
298339
for filepath, name in [(moving, "Input DWI"),
@@ -334,7 +375,18 @@ def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False):
334375
b0_threshold=50,
335376
b0_denoising=b0_denoising,
336377
)
378+
env = os.environ.copy()
379+
env["OPENBLAS_NUM_THREADS"] = "1"
380+
env["OMP_NUM_THREADS"] = "1"
381+
env["MKL_NUM_THREADS"] = "1"
382+
env["VECLIB_MAXIMUM_THREADS"] = "1"
383+
env["NUMEXPR_NUM_THREADS"] = "1"
384+
os.environ.update(env)
337385

386+
if gibbs:
387+
print(f"\n{CYAN}Applying Gibbs ringing removal...{RESET}")
388+
gibbs_removal(denoised, slice_axis=2, n_points=3, inplace=True, num_processes=threads)
389+
338390
print(f"\n{CYAN}Saving denoised image...{RESET}")
339391
nib.save(nib.Nifti1Image(denoised, moving_image.affine), output)
340392

@@ -380,6 +432,17 @@ def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False):
380432
action='store_true',
381433
help="Denoise b0 volumes separately (experimental, default: False)."
382434
)
435+
parser.add_argument(
436+
"--gibbs",
437+
action='store_true',
438+
help="Apply Gibbs ringing removal after denoising."
439+
)
440+
parser.add_argument(
441+
"--threads",
442+
type=int,
443+
default=1,
444+
help="Number of threads for Gibbs removal (default: 1)."
445+
)
383446

384447
args = parser.parse_args()
385448

@@ -389,7 +452,9 @@ def run_denoise(moving, moving_bval, moving_bvec, output, b0_denoising=False):
389452
args.bval,
390453
args.bvec,
391454
args.output,
392-
args.b0_denoise
455+
args.b0_denoise,
456+
args.gibbs,
457+
args.threads
393458
)
394459

395460
print(f"\n{GREEN}{BOLD}Denoising successfully completed!{RESET}")

0 commit comments

Comments
 (0)