From 440fdf914f2f12823172b1e9c2266fac8a4a28b5 Mon Sep 17 00:00:00 2001 From: Taylor Salo Date: Wed, 4 Sep 2024 09:29:13 -0400 Subject: [PATCH] Use GenericLabel over MultiLabel (#64) --- src/fmripost_aroma/interfaces/misc.py | 54 ++++++++++++++++++++ src/fmripost_aroma/interfaces/resampler.py | 57 ---------------------- src/fmripost_aroma/workflows/base.py | 5 +- src/fmripost_aroma/workflows/confounds.py | 4 +- src/fmripost_aroma/workflows/outputs.py | 4 +- 5 files changed, 61 insertions(+), 63 deletions(-) create mode 100644 src/fmripost_aroma/interfaces/misc.py delete mode 100644 src/fmripost_aroma/interfaces/resampler.py diff --git a/src/fmripost_aroma/interfaces/misc.py b/src/fmripost_aroma/interfaces/misc.py new file mode 100644 index 0000000..b391207 --- /dev/null +++ b/src/fmripost_aroma/interfaces/misc.py @@ -0,0 +1,54 @@ +"""Miscellaneous interfaces for fmriprep-aroma.""" + +from nipype.interfaces.base import ( + isdefined, + traits, +) +from nipype.utils.filemanip import fname_presuffix +from niworkflows.interfaces.fixes import ( + FixHeaderApplyTransforms, + _FixTraitApplyTransformsInputSpec, +) + + +class _ApplyTransformsInputSpec(_FixTraitApplyTransformsInputSpec): + # Nipype's version doesn't have GenericLabel + interpolation = traits.Enum( + 'Linear', + 'NearestNeighbor', + 'CosineWindowedSinc', + 'WelchWindowedSinc', + 'HammingWindowedSinc', + 'LanczosWindowedSinc', + 'MultiLabel', + 'Gaussian', + 'BSpline', + 'GenericLabel', + argstr='%s', + usedefault=True, + ) + + +class ApplyTransforms(FixHeaderApplyTransforms): + """A modified version of FixHeaderApplyTransforms from niworkflows. + + The niworkflows version of ApplyTransforms "fixes the resampled image header + to match the xform of the reference image". + This modification overrides the allowed interpolation values, + since FixHeaderApplyTransforms doesn't support GenericLabel, + which is preferred over MultiLabel. + """ + + input_spec = _ApplyTransformsInputSpec + + def _run_interface(self, runtime): + if not isdefined(self.inputs.output_image): + self.inputs.output_image = fname_presuffix( + self.inputs.input_image, + suffix='_trans.nii.gz', + newpath=runtime.cwd, + use_ext=False, + ) + + runtime = super()._run_interface(runtime) + return runtime diff --git a/src/fmripost_aroma/interfaces/resampler.py b/src/fmripost_aroma/interfaces/resampler.py deleted file mode 100644 index 80ea7bc..0000000 --- a/src/fmripost_aroma/interfaces/resampler.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Interfaces for resampling.""" - -from nipype.interfaces.base import ( - BaseInterfaceInputSpec, - File, - SimpleInterface, - TraitedSpec, - traits, -) - - -class _ResamplerInputSpec(BaseInterfaceInputSpec): - bold_file = File(exists=True, desc='BOLD file to resample.') - derivs_path = traits.Directory( - exists=True, - desc='Path to derivatives.', - ) - output_dir = traits.Directory( - exists=True, - desc='Output directory.', - ) - space = traits.Str( - 'MNI152NLin6Asym', - usedefault=True, - desc='Output space.', - ) - resolution = traits.Str( - '2', - usedefault=True, - desc='Output resolution.', - ) - - -class _ResamplerOutputSpec(TraitedSpec): - output_file = File(exists=True, desc='Resampled BOLD file.') - - -class Resampler(SimpleInterface): - """Apply the resampler workflow.""" - - input_spec = _ResamplerInputSpec - output_spec = _ResamplerOutputSpec - - def _run_interface(self, runtime): - from fmripost_aroma.utils import resampler - - output_file = resampler.main( - bold_file=self.inputs.bold_file, - derivs_path=self.inputs.derivs_path, - output_dir=self.inputs.output_dir, - space=self.inputs.space, - resolution=self.inputs.resolution, - ) - - self._results['output_file'] = output_file - - return runtime diff --git a/src/fmripost_aroma/workflows/base.py b/src/fmripost_aroma/workflows/base.py index 3cf831b..20d2f8f 100644 --- a/src/fmripost_aroma/workflows/base.py +++ b/src/fmripost_aroma/workflows/base.py @@ -376,10 +376,11 @@ def init_single_run_wf(bold_file): # Resample to MNI152NLin6Asym:res-2, for ICA-AROMA classification from fmriprep.workflows.bold.apply import init_bold_volumetric_resample_wf from fmriprep.workflows.bold.stc import init_bold_stc_wf - from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces.header import ValidateImage from templateflow.api import get as get_template + from fmripost_aroma.interfaces.misc import ApplyTransforms + workflow.__desc__ += """\ Raw BOLD series were resampled to MNI152NLin6Asym:res-2, for ICA-AROMA classification. """ @@ -451,7 +452,7 @@ def init_single_run_wf(bold_file): # Warp the mask as well mask_to_mni6 = pe.Node( ApplyTransforms( - interpolation='MultiLabel', + interpolation='GenericLabel', input_image=functional_cache['bold_mask_native'], reference_image=mni6_mask, transforms=[ diff --git a/src/fmripost_aroma/workflows/confounds.py b/src/fmripost_aroma/workflows/confounds.py index d288efa..84973af 100644 --- a/src/fmripost_aroma/workflows/confounds.py +++ b/src/fmripost_aroma/workflows/confounds.py @@ -77,11 +77,11 @@ def init_carpetplot_wf( from nipype.interfaces import utility as niu from nipype.pipeline import engine as pe from niworkflows.engine.workflows import LiterateWorkflow as Workflow - from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from templateflow.api import get as get_template from fmripost_aroma.config import DEFAULT_MEMORY_MIN_GB from fmripost_aroma.interfaces.bids import DerivativesDataSink + from fmripost_aroma.interfaces.misc import ApplyTransforms inputnode = pe.Node( niu.IdentityInterface( @@ -154,7 +154,7 @@ def init_carpetplot_wf( ), ), ], - interpolation='MultiLabel', + interpolation='GenericLabel', args='-u int', ), name='resample_parc', diff --git a/src/fmripost_aroma/workflows/outputs.py b/src/fmripost_aroma/workflows/outputs.py index 9000d4d..3c5c273 100644 --- a/src/fmripost_aroma/workflows/outputs.py +++ b/src/fmripost_aroma/workflows/outputs.py @@ -27,11 +27,11 @@ from fmriprep.utils.bids import dismiss_echo from nipype.interfaces import utility as niu from nipype.pipeline import engine as pe -from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.utils.images import dseg_label from fmripost_aroma.config import DEFAULT_MEMORY_MIN_GB from fmripost_aroma.interfaces.bids import DerivativesDataSink +from fmripost_aroma.interfaces.misc import ApplyTransforms def init_func_fit_reports_wf( @@ -82,7 +82,7 @@ def init_func_fit_reports_wf( # Warp the tissue segmentation to MNI dseg_to_mni6 = pe.Node( - ApplyTransforms(interpolation='MultiLabel'), + ApplyTransforms(interpolation='GenericLabel'), name='dseg_to_mni6', mem_gb=1, )