diff --git a/.circleci/ds210_outputs.txt b/.circleci/ds210_outputs.txt index ca0ec3df..4ee3a1e9 100644 --- a/.circleci/ds210_outputs.txt +++ b/.circleci/ds210_outputs.txt @@ -47,12 +47,6 @@ sub-02/func/sub-02_task-cuedSGT_run-01_echo-2_desc-preproc_bold.json sub-02/func/sub-02_task-cuedSGT_run-01_echo-2_desc-preproc_bold.nii.gz sub-02/func/sub-02_task-cuedSGT_run-01_echo-3_desc-preproc_bold.json sub-02/func/sub-02_task-cuedSGT_run-01_echo-3_desc-preproc_bold.nii.gz -sub-02/func/sub-02_task-cuedSGT_run-01_echo-1_desc-noise_boldmap.json -sub-02/func/sub-02_task-cuedSGT_run-01_echo-1_desc-noise_boldmap.nii.gz -sub-02/func/sub-02_task-cuedSGT_run-01_echo-2_desc-noise_boldmap.json -sub-02/func/sub-02_task-cuedSGT_run-01_echo-2_desc-noise_boldmap.nii.gz -sub-02/func/sub-02_task-cuedSGT_run-01_echo-3_desc-noise_boldmap.json -sub-02/func/sub-02_task-cuedSGT_run-01_echo-3_desc-noise_boldmap.nii.gz sub-02/func/sub-02_task-cuedSGT_run-01_from-boldref_to-auto00000_mode-image_xfm.json sub-02/func/sub-02_task-cuedSGT_run-01_from-boldref_to-auto00000_mode-image_xfm.txt sub-02/func/sub-02_task-cuedSGT_run-01_from-boldref_to-T1w_mode-image_desc-coreg_xfm.json diff --git a/fmriprep/interfaces/denoise.py b/fmriprep/interfaces/denoise.py index 12969bb0..7d303346 100644 --- a/fmriprep/interfaces/denoise.py +++ b/fmriprep/interfaces/denoise.py @@ -16,12 +16,12 @@ class _ValidateComplexInputSpec(BaseInterfaceInputSpec): - mag_file = File( + magnitude = File( exists=True, mandatory=True, desc='Magnitude BOLD EPI', ) - phase_file = File( + phase = File( exists=True, mandatory=False, desc='Phase BOLD EPI', @@ -29,8 +29,8 @@ class _ValidateComplexInputSpec(BaseInterfaceInputSpec): class _ValidateComplexOutputSpec(TraitedSpec): - mag_file = File(exists=True, desc='Validated magnitude file') - phase_file = File(exists=True, desc='Validated phase file') + magnitude = File(exists=True, desc='Validated magnitude file') + phase = File(exists=True, desc='Validated phase file') class ValidateComplex(SimpleInterface): @@ -42,12 +42,12 @@ class ValidateComplex(SimpleInterface): def _run_interface(self, runtime): import nibabel as nb - if not isdefined(self.inputs.phase_file): - self._results['mag_file'] = self.inputs.mag_file + if not isdefined(self.inputs.phase): + self._results['magnitude'] = self.inputs.magnitude return runtime - mag_img = nb.load(self.inputs.mag_file) - phase_img = nb.load(self.inputs.phase_file) + mag_img = nb.load(self.inputs.magnitude) + phase_img = nb.load(self.inputs.phase) n_mag_vols = mag_img.shape[3] n_phase_vols = phase_img.shape[3] @@ -57,8 +57,8 @@ def _run_interface(self, runtime): f'!= number of volumes in phase file ({n_phase_vols})' ) - self._results['mag_file'] = self.inputs.mag_file - self._results['phase_file'] = self.inputs.phase_file + self._results['magnitude'] = self.inputs.magnitude + self._results['phase'] = self.inputs.phase return runtime @@ -139,11 +139,11 @@ def _get_plotting_images(self): class _PolarToComplexInputSpec(MRTrix3BaseInputSpec): - mag_file = traits.File(exists=True, mandatory=True, position=0, argstr='%s') - phase_file = traits.File(exists=True, mandatory=True, position=1, argstr='%s') - out_file = traits.File( + magnitude = traits.File(exists=True, mandatory=True, position=0, argstr='%s') + phase = traits.File(exists=True, mandatory=True, position=1, argstr='%s') + complex = traits.File( exists=False, - name_source='mag_file', + name_source='magnitude', name_template='%s_complex.nii.gz', keep_extension=False, position=-1, @@ -152,7 +152,7 @@ class _PolarToComplexInputSpec(MRTrix3BaseInputSpec): class _PolarToComplexOutputSpec(TraitedSpec): - out_file = File(exists=True) + complex = File(exists=True) class PolarToComplex(MRTrix3Base): @@ -251,7 +251,7 @@ class _PhaseToRadInputSpec(BaseInterfaceInputSpec): """ - phase_file = File(exists=True, mandatory=True) + phase = File(exists=True, mandatory=True) class _PhaseToRadOutputSpec(TraitedSpec): @@ -291,7 +291,7 @@ class _PhaseToRadOutputSpec(TraitedSpec): """ - phase_file = File(exists=True) + phase = File(exists=True) class PhaseToRad(SimpleInterface): @@ -338,7 +338,7 @@ class PhaseToRad(SimpleInterface): output_spec = _PhaseToRadOutputSpec def _run_interface(self, runtime): - im = nb.load(self.inputs.phase_file) + im = nb.load(self.inputs.phase) data = im.get_fdata(caching='unchanged') # Read as float64 for safety hdr = im.header.copy() @@ -352,15 +352,15 @@ def _run_interface(self, runtime): hdr.set_xyzt_units('mm') # Set the output file name - self._results['phase_file'] = fname_presuffix( - self.inputs.phase_file, + self._results['phase'] = fname_presuffix( + self.inputs.phase, suffix='_rad.nii.gz', newpath=runtime.cwd, use_ext=False, ) # Save the output image - nb.Nifti1Image(data, None, hdr).to_filename(self._results['phase_file']) + nb.Nifti1Image(data, None, hdr).to_filename(self._results['phase']) return runtime diff --git a/fmriprep/workflows/bold/denoise.py b/fmriprep/workflows/bold/denoise.py index d2bb7450..61c149b0 100644 --- a/fmriprep/workflows/bold/denoise.py +++ b/fmriprep/workflows/bold/denoise.py @@ -63,7 +63,7 @@ def init_bold_dwidenoise_wf( :graph2use: orig :simple_form: yes - from fmriprep.workflows.bold import init_bold_dwidenoise_wf + from fmriprep.workflows.bold.denoise import init_bold_dwidenoise_wf wf = init_bold_dwidenoise_wf( has_phase=True, has_norf=True, @@ -75,7 +75,7 @@ def init_bold_dwidenoise_wf( has_phase : :obj:`bool` True if phase data are available. False if not. has_norf : :obj:`bool` - True if noRF data are available. False if not. + True if no-excitation (noRF) data are available. False if not. mem_gb : :obj:`dict` Size of BOLD file in GB - please note that this size should be calculated after resamplings that may extend @@ -85,22 +85,22 @@ def init_bold_dwidenoise_wf( Inputs ------ - mag_file + magnitude BOLD series NIfTI file - phase_file + phase Phase series NIfTI file - norf_file + magnitude_norf Noise map NIfTI file - phase_norf_file + phase_norf Phase noise map NIfTI file Outputs ------- - mag_file + magnitude Denoised BOLD series NIfTI file - phase_file + phase Denoised phase series NIfTI file - noise_file + noise Noise map NIfTI file """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow @@ -120,16 +120,16 @@ def init_bold_dwidenoise_wf( inputnode = pe.Node( niu.IdentityInterface( - fields=['mag_file', 'norf_file', 'phase_file', 'phase_norf_file'], + fields=['magnitude', 'magnitude_norf', 'phase', 'phase_norf'], ), name='inputnode', ) outputnode = pe.Node( niu.IdentityInterface( fields=[ - 'mag_file', - 'phase_file', - 'noise_file', + 'magnitude', + 'phase', + 'noise', ], ), name='outputnode', @@ -138,7 +138,9 @@ def init_bold_dwidenoise_wf( if has_norf: workflow.__desc__ += ' An empirical noise map was estimated from no-excitation volumes.' # Calculate noise map from noise volumes - # TODO: Figure out how to estimate the noise map from the noRF data + # TODO: Calculate the noise level from the noise volumes + # XXX: In NORDIC, noise level is estimated after scaling, phase filtering, + # and g-factor correction. noise_estimate = pe.Node( NoiseEstimate(), name='noise_estimate', @@ -148,8 +150,8 @@ def init_bold_dwidenoise_wf( validate_complex_norf = pe.Node(ValidateComplex(), name='validate_complex_norf') workflow.connect([ (inputnode, validate_complex_norf, [ - ('mag_norf_file', 'mag_file'), - ('phase_norf_file', 'phase_file'), + ('magnitude_norf', 'magnitude'), + ('phase_norf', 'phase'), ]), ]) # fmt:skip @@ -160,7 +162,7 @@ def init_bold_dwidenoise_wf( name='phase_to_radians_norf', ) workflow.connect([ - (validate_complex_norf, phase_to_radians_norf, [('phase_file', 'phase_file')]), + (validate_complex_norf, phase_to_radians_norf, [('phase', 'phase')]), ]) # fmt:skip combine_complex_norf = pe.Node( @@ -168,13 +170,13 @@ def init_bold_dwidenoise_wf( name='combine_complex_norf', ) workflow.connect([ - (validate_complex_norf, combine_complex_norf, [('mag_file', 'mag_file')]), - (phase_to_radians_norf, combine_complex_norf, [('phase_file', 'phase_file')]), + (validate_complex_norf, combine_complex_norf, [('magnitude', 'magnitude')]), + (phase_to_radians_norf, combine_complex_norf, [('phase', 'phase')]), (combine_complex_norf, noise_estimate, [('out_file', 'in_file')]), ]) # fmt:skip else: - workflow.connect([(inputnode, noise_estimate, [('mag_norf_file', 'in_file')])]) + workflow.connect([(inputnode, noise_estimate, [('magnitude_norf', 'in_file')])]) complex_buffer = pe.Node(niu.IdentityInterface(fields=['bold_file']), name='complex_buffer') if has_phase: @@ -192,19 +194,19 @@ def init_bold_dwidenoise_wf( PhaseToRad(), name='phase_to_radians', ) - workflow.connect([(validate_complex, phase_to_radians, [('phase_file', 'phase_file')])]) + workflow.connect([(validate_complex, phase_to_radians, [('phase', 'phase')])]) combine_complex = pe.Node( PolarToComplex(), name='combine_complex', ) workflow.connect([ - (validate_complex, combine_complex, [('mag_file', 'mag_file')]), - (phase_to_radians, combine_complex, [('phase_file', 'phase_file')]), - (combine_complex, complex_buffer, [('out_file', 'bold_file')]), + (validate_complex, combine_complex, [('magnitude', 'magnitude')]), + (phase_to_radians, combine_complex, [('phase', 'phase')]), + (combine_complex, complex_buffer, [('complex', 'bold_file')]), ]) # fmt:skip else: - workflow.connect([(inputnode, complex_buffer, [('mag_file', 'bold_file')])]) + workflow.connect([(inputnode, complex_buffer, [('magnitude', 'bold_file')])]) # Run NORDIC estimator = { @@ -230,8 +232,8 @@ def init_bold_dwidenoise_wf( name='split_complex', ) workflow.connect([ - (dwidenoise, split_magnitude, [('out_file', 'complex_file')]), - (split_magnitude, outputnode, [('out_file', 'mag_file')]), + (dwidenoise, split_magnitude, [('out_file', 'complex')]), + (split_magnitude, outputnode, [('out_file', 'magnitude')]), ]) # fmt:skip split_phase = pe.Node( @@ -239,8 +241,8 @@ def init_bold_dwidenoise_wf( name='split_phase', ) workflow.connect([ - (dwidenoise, split_phase, [('out_file', 'complex_file')]), - (split_phase, outputnode, [('out_file', 'phase_file')]), + (dwidenoise, split_phase, [('out_file', 'complex')]), + (split_phase, outputnode, [('out_file', 'phase')]), ]) # fmt:skip # Apply sqrt(2) scaling factor to noise map @@ -250,13 +252,13 @@ def init_bold_dwidenoise_wf( ) workflow.connect([ (noise_estimate, rescale_noise, [('noise_map', 'in_file_a')]), - (rescale_noise, outputnode, [('out_file', 'noise_file')]), + (rescale_noise, outputnode, [('out_file', 'noise')]), ]) # fmt:skip else: workflow.connect([ (dwidenoise, outputnode, [ - ('out_file', 'mag_file'), - ('noise_image', 'noise_file'), + ('out_file', 'magnitude'), + ('noise_image', 'noise'), ]), ]) # fmt:skip diff --git a/fmriprep/workflows/bold/fit.py b/fmriprep/workflows/bold/fit.py index a9f31686..bb7e674f 100644 --- a/fmriprep/workflows/bold/fit.py +++ b/fmriprep/workflows/bold/fit.py @@ -878,7 +878,7 @@ def init_bold_native_wf( outputnode.inputs.metadata = metadata denoisebuffer = pe.Node( - niu.IdentityInterface(fields=['bold_file', 'phase_file']), + niu.IdentityInterface(fields=['bold_file', 'phase']), name='denoisebuffer', ) boldbuffer = pe.Node( @@ -908,12 +908,12 @@ def init_bold_native_wf( mem_gb=mem_gb, ) workflow.connect([ - (validate_bold, dwidenoise_wf, [('out_file', 'inputnode.mag_file')]), + (validate_bold, dwidenoise_wf, [('out_file', 'inputnode.magnitude')]), (dwidenoise_wf, denoisebuffer, [ - ('outputnode.mag_file', 'bold_file'), - ('outputnode.phase_file', 'phase_file'), + ('outputnode.magnitude', 'bold_file'), + ('outputnode.phase', 'phase'), ]), - (dwidenoise_wf, outputnode, [('outputnode.noise_file', 'thermal_noise')]), + (dwidenoise_wf, outputnode, [('outputnode.noise', 'thermal_noise')]), ]) # fmt:skip if has_norf: @@ -922,7 +922,7 @@ def init_bold_native_wf( workflow.connect([ (echo_index, norf_source, [('echoidx', 'index')]), (norf_source, validate_norf, [('out', 'in_file')]), - (validate_norf, dwidenoise_wf, [('out_file', 'inputnode.norf_file')]), + (validate_norf, dwidenoise_wf, [('out_file', 'inputnode.magnitude_norf')]), ]) # fmt:skip if has_phase: @@ -931,7 +931,7 @@ def init_bold_native_wf( workflow.connect([ (echo_index, phase_source, [('echoidx', 'index')]), (phase_source, validate_phase, [('out', 'in_file')]), - (validate_phase, dwidenoise_wf, [('out_file', 'inputnode.phase_file')]), + (validate_phase, dwidenoise_wf, [('out_file', 'inputnode.phase')]), ]) # fmt:skip if has_phase and has_norf: @@ -943,7 +943,7 @@ def init_bold_native_wf( workflow.connect([ (echo_index, phase_norf_source, [('echoidx', 'index')]), (phase_norf_source, validate_phase_norf, [('out', 'in_file')]), - (validate_phase_norf, dwidenoise_wf, [('out_file', 'inputnode.phase_norf_file')]), + (validate_phase_norf, dwidenoise_wf, [('out_file', 'inputnode.phase_norf')]), ]) # fmt:skip else: workflow.connect([(validate_bold, denoisebuffer, [('out_file', 'bold_file')])])