From 07af0b7ef591a7f54339370082d846728dac637a Mon Sep 17 00:00:00 2001 From: David Meunier Date: Thu, 20 Jun 2024 13:30:33 +0200 Subject: [PATCH] Pad mask (#263) if pad_template: stereo_padded_brain_mask and stereo_padded_segmented brain_mask --- macapype/nodes/denoise.py | 123 +++++++++++++++++++++++++++ macapype/pipelines/full_pipelines.py | 41 ++++++++- macapype/pipelines/rename.py | 38 +++++++++ 3 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 macapype/nodes/denoise.py diff --git a/macapype/nodes/denoise.py b/macapype/nodes/denoise.py new file mode 100644 index 000000000..c8da994ea --- /dev/null +++ b/macapype/nodes/denoise.py @@ -0,0 +1,123 @@ + +from nipype.interfaces.ants.base import (ANTSCommand, ANTSCommandInputSpec) +from nipype.interfaces.base import TraitedSpec, File, traits, isdefined + + +class DenoiseImageInputSpec(ANTSCommandInputSpec): + dimension = traits.Enum( + 2, + 3, + 4, + argstr="-d %d", + desc="This option forces the image to be treated " + "as a specified-dimensional image. If not " + "specified, the program tries to infer the " + "dimensionality from the input image.", + ) + input_image = File( + exists=True, + argstr="-i %s", + mandatory=True, + desc="A scalar image is expected as input for noise correction.", + ) + + mask_image = File( + exists=True, + argstr="-x %s", + mandatory=False, + desc="Restriction of computation on the mask only", + ) + noise_model = traits.Enum( + "Gaussian", + "Rician", + argstr="-n %s", + usedefault=True, + desc=("Employ a Rician or Gaussian noise model."), + ) + shrink_factor = traits.Int( + default_value=1, + usedefault=True, + argstr="-s %s", + desc=( + "Running noise correction on large images can" + " be time consuming. To lessen computation time," + " the input image can be resampled. The shrink" + " factor, specified as a single integer, describes" + " this resampling. Shrink factor = 1 is the default." + ), + ) + output_image = File( + argstr="-o %s", + name_source=["input_image"], + hash_files=False, + keep_extension=True, + name_template="%s_noise_corrected", + desc="The output consists of the noise corrected" + " version of the input image.", + ) + save_noise = traits.Bool( + False, + mandatory=True, + usedefault=True, + desc=("True if the estimated noise should be saved to file."), + xor=["noise_image"], + ) + noise_image = File( + name_source=["input_image"], + hash_files=False, + keep_extension=True, + name_template="%s_noise", + desc="Filename for the estimated noise.", + ) + verbose = traits.Bool(False, argstr="-v", desc=("Verbose output.")) + + +class DenoiseImageOutputSpec(TraitedSpec): + output_image = File(exists=True) + noise_image = File() + + +class DenoiseImage(ANTSCommand): + """ + Examples + -------- + >>> import copy + >>> from nipype.interfaces.ants import DenoiseImage + >>> denoise = DenoiseImage() + >>> denoise.inputs.dimension = 3 + >>> denoise.inputs.input_image = 'im1.nii' + >>> denoise.cmdline + 'DenoiseImage -d 3 -i im1.nii -n Gaussian -o im1_noise_corrected.nii -s 1' + + >>> denoise_2 = copy.deepcopy(denoise) + >>> denoise_2.inputs.output_image = 'output_corrected_image.nii.gz' + >>> denoise_2.inputs.noise_model = 'Rician' + >>> denoise_2.inputs.shrink_factor = 2 + >>> denoise_2.cmdline + 'DenoiseImage -d 3 -i im1.nii -n Rician + -o output_corrected_image.nii.gz -s 2' + + >>> denoise_3 = DenoiseImage() + >>> denoise_3.inputs.input_image = 'im1.nii' + >>> denoise_3.inputs.save_noise = True + >>> denoise_3.cmdline + 'DenoiseImage -i im1.nii -n Gaussian + -o [ im1_noise_corrected.nii, im1_noise.nii ] -s 1' + + """ + + input_spec = DenoiseImageInputSpec + output_spec = DenoiseImageOutputSpec + _cmd = "DenoiseImage" + + def _format_arg(self, name, trait_spec, value): + if (name == "output_image") and ( + self.inputs.save_noise or isdefined(self.inputs.noise_image) + ): + newval = "[ {}, {} ]".format( + self._filename_from_source("output_image"), + self._filename_from_source("noise_image"), + ) + return trait_spec.argstr % newval + + return super()._format_arg(name, trait_spec, value) diff --git a/macapype/pipelines/full_pipelines.py b/macapype/pipelines/full_pipelines.py index f6c638a32..090f8d4d6 100644 --- a/macapype/pipelines/full_pipelines.py +++ b/macapype/pipelines/full_pipelines.py @@ -8,6 +8,8 @@ from nipype.interfaces import fsl from nipype.interfaces import ants +from nipype.interfaces.ants.utils import ImageMath + from nipype.interfaces.niftyreg.regutils import RegResample from ..utils.utils_nodes import NodeParams @@ -128,7 +130,8 @@ def create_full_spm_subpipes( "stereo_padded_T1", "stereo_padded_T2", - 'stereo_brain_mask', 'stereo_debiased_T1', + 'stereo_brain_mask', + 'stereo_debiased_T1', 'stereo_debiased_T2', 'stereo_masked_debiased_T1', 'stereo_masked_debiased_T2', @@ -871,13 +874,16 @@ def create_full_ants_subpipes( 'stereo_debiased_T1', 'stereo_debiased_T2', "native_debiased_T1", "native_debiased_T2", - 'stereo_brain_mask', 'stereo_masked_debiased_T1', + 'stereo_brain_mask', + 'stereo_padded_brain_mask', + 'stereo_masked_debiased_T1', 'stereo_masked_debiased_T2', 'native_brain_mask', "native_masked_debiased_T1", "native_masked_debiased_T2", 'stereo_segmented_brain_mask', + 'stereo_padded_segmented_brain_mask', 'stereo_prob_gm', 'stereo_prob_wm', 'stereo_prob_csf', "stereo_gen_5tt", @@ -1176,6 +1182,22 @@ def create_full_ants_subpipes( extract_pipe, "smooth_mask.out_file", outputnode, "stereo_brain_mask") + if "pad_template" in params["short_preparation_pipe"].keys(): + + pad_stereo_brain_mask = NodeParams( + ImageMath(), + params=parse_key(params["short_preparation_pipe"], + "pad_template"), + name="pad_stereo_brain_mask") + + seg_pipe.connect( + extract_pipe, "smooth_mask.out_file", + pad_stereo_brain_mask, "op1") + + seg_pipe.connect( + pad_stereo_brain_mask, "output_image", + outputnode, "stereo_padded_brain_mask") + if pad: pad_back( seg_pipe, data_preparation_pipe, inputnode, @@ -1603,6 +1625,21 @@ def create_full_ants_subpipes( brain_segment_pipe, "outputnode.prob_csf", outputnode, "native_prob_csf", params) + if "pad_template" in params["short_preparation_pipe"].keys(): + pad_stereo_stereo_brain_mask = NodeParams( + ImageMath(), + params=parse_key(params["short_preparation_pipe"], + "pad_template"), + name="pad_stereo_stereo_brain_mask") + + seg_pipe.connect( + brain_segment_pipe, "outputnode.segmented_file", + pad_stereo_stereo_brain_mask, "op1") + + seg_pipe.connect( + pad_stereo_stereo_brain_mask, "output_image", + outputnode, "stereo_padded_segmented_brain_mask") + # ############################################## export 5tt if "export_5tt_pipe" in params["brain_segment_pipe"]: diff --git a/macapype/pipelines/rename.py b/macapype/pipelines/rename.py index 6f276b670..2ee888b0b 100644 --- a/macapype/pipelines/rename.py +++ b/macapype/pipelines/rename.py @@ -176,6 +176,24 @@ def rename_all_brain_derivatives(params, main_workflow, segment_pnh_pipe, rename_stereo_brain_mask, 'out_file', datasink, '@stereo_brain_mask') + if "pad_template" in params["short_preparation_pipe"].keys(): + + rename_stereo_padded_brain_mask = pe.Node( + niu.Rename(), + name="rename_stereo_padded_brain_mask") + rename_stereo_padded_brain_mask.inputs.format_string = \ + pref_deriv + "_space-stereo_desc-pad_desc-brain_mask" + rename_stereo_padded_brain_mask.inputs.parse_string = parse_str + rename_stereo_padded_brain_mask.inputs.keep_ext = True + + main_workflow.connect( + segment_pnh_pipe, 'outputnode.stereo_padded_brain_mask', + rename_stereo_padded_brain_mask, 'in_file') + + main_workflow.connect( + rename_stereo_padded_brain_mask, 'out_file', + datasink, '@stereo_padded_brain_mask') + if "masked_correct_bias_pipe" in params.keys(): # rename masked_debiased_T1 @@ -340,6 +358,26 @@ def rename_all_brain_derivatives(params, main_workflow, segment_pnh_pipe, rename_stereo_segmented_brain_mask, 'out_file', datasink, '@stereo_segmented_brain_mask') + if "pad_template" in params["short_preparation_pipe"].keys(): + + rename_stereo_padded_segmented_brain_mask = pe.Node( + niu.Rename(), + name="rename_stereo_padded_segmented_brain_mask") + rename_stereo_padded_segmented_brain_mask.inputs.format_string = \ + pref_deriv + "_space-stereo_desc-pad_desc-brain_dseg" + rename_stereo_padded_segmented_brain_mask.inputs.parse_string = \ + parse_str + rename_stereo_padded_segmented_brain_mask.inputs.keep_ext = True + + main_workflow.connect( + segment_pnh_pipe, + 'outputnode.stereo_padded_segmented_brain_mask', + rename_stereo_padded_segmented_brain_mask, 'in_file') + + main_workflow.connect( + rename_stereo_padded_segmented_brain_mask, 'out_file', + datasink, '@stereo_padded_segmented_brain_mask') + # rename 5tt if "export_5tt_pipe" in params["brain_segment_pipe"].keys(): rename_stereo_gen_5tt = pe.Node(