def init_qwarp_inversion_wf(omp_nthreads=1, name="qwarp_invert_wf"): """ Invert a warp produced by 3dqwarp and convert it to an ANTS formatted warp Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.base import init_qwarp_inversion_wf wf = init_qwarp_inversion_wf() Parameters ---------- name : str Name for this workflow omp_nthreads : int Parallelize internal tasks across the number of CPUs given by this option. Inputs ------ warp : pathlike The warp you want to invert. in_reference : pathlike The baseline reference image (must correspond to ``epi_pe_dir``). Outputs ------- out_warp : pathlike The corresponding inverted :abbr:`DFM (displacements field map)` compatible with ANTs. """ from ..interfaces.afni import InvertWarp workflow = Workflow(name=name) workflow.__desc__ = """\ A warp produced by 3dQwarp was inverted by `3dNwarpCat` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface(fields=['warp', 'in_reference']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_warp']), name='outputnode') invert = pe.Node(InvertWarp(), name='invert', n_procs=omp_nthreads) invert.inputs.outputtype = 'NIFTI_GZ' to_ants = pe.Node(niu.Function(function=_fix_hdr), name='to_ants', mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01) workflow.connect([ (inputnode, invert, [('warp', 'in_file')]), (invert, cphdr_warp, [('out_file', 'in_file')]), (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def init_pepolar_unwarp_wf(omp_nthreads=1, matched_pe=False, name="pepolar_unwarp_wf"): """ Create the PE-Polar field estimation workflow. This workflow takes in a set of EPI files with opposite phase encoding direction than the target file and calculates a displacements field (in other words, an ANTs-compatible warp file). This procedure works if there is only one '_epi' file is present (as long as it has the opposite phase encoding direction to the target file). The target file will be used to estimate the field distortion. However, if there is another '_epi' file present with a matching phase encoding direction to the target it will be used instead. Currently, different phase encoding dimension in the target file and the '_epi' file(s) (for example 'i' and 'j') is not supported. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from sdcflows.workflows.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf() **Parameters**: matched_pe : bool Whether the input ``fmaps_epi`` will contain images with matched PE blips or not. Please use :func:`sdcflows.workflows.pepolar.check_pes` to determine whether they exist or not. name : str Name for this workflow omp_nthreads : int Parallelize internal tasks across the number of CPUs given by this option. **Inputs**: fmaps_epi : list of tuple(pathlike, str) The list of EPI images that will be used in PE-Polar correction, and their corresponding ``PhaseEncodingDirection`` metadata. The workflow will use the ``bold_pe_dir`` input to separate out those EPI acquisitions with opposed PE blips and those with matched PE blips (the latter could be none, and ``in_reference_brain`` would then be used). The workflow raises a ``ValueError`` when no images with opposed PE blips are found. bold_pe_dir : str The baseline PE direction. in_reference : pathlike The baseline reference image (must correspond to ``bold_pe_dir``). in_reference_brain : pathlike The reference image above, but skullstripped. in_mask : pathlike Not used, present only for consistency across fieldmap estimation workflows. **Outputs**: out_reference : pathlike The ``in_reference`` after unwarping out_reference_brain : pathlike The ``in_reference`` after unwarping and skullstripping out_warp : pathlike The corresponding :abbr:`DFM (displacements field map)` compatible with ANTs. out_mask : pathlike Mask of the unwarped input file """ workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on two echo-planar imaging (EPI) references with opposing phase-encoding directions, using `3dQwarp` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'fmaps_epi', 'in_reference', 'in_reference_brain', 'in_mask', 'bold_pe_dir' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'out_reference', 'out_reference_brain', 'out_warp', 'out_mask' ]), name='outputnode') prepare_epi_wf = init_prepare_epi_wf(omp_nthreads=omp_nthreads, matched_pe=matched_pe, name="prepare_epi_wf") qwarp = pe.Node(afni.QwarpPlusMinus( pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}), name='qwarp', n_procs=omp_nthreads) to_ants = pe.Node(niu.Function(function=_fix_hdr), name='to_ants', mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01) unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf( omp_nthreads=omp_nthreads) workflow.connect([ (inputnode, qwarp, [(('bold_pe_dir', _qwarp_args), 'args')]), (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (inputnode, prepare_epi_wf, [('fmaps_epi', 'inputnode.maps_pe'), ('bold_pe_dir', 'inputnode.epi_pe'), ('in_reference_brain', 'inputnode.ref_brain')]), (prepare_epi_wf, qwarp, [('outputnode.opposed_pe', 'base_file'), ('outputnode.matched_pe', 'in_file')]), (qwarp, cphdr_warp, [('source_warp', 'in_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, unwarp_reference, [('out', 'transforms')]), (inputnode, unwarp_reference, [('in_reference', 'reference_image'), ('in_reference', 'input_image')]), (unwarp_reference, enhance_and_skullstrip_bold_wf, [('output_image', 'inputnode.in_file')]), (unwarp_reference, outputnode, [('output_image', 'out_reference')]), (enhance_and_skullstrip_bold_wf, outputnode, [('outputnode.mask_file', 'out_mask'), ('outputnode.skull_stripped_file', 'out_reference_brain')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def init_pepolar_unwarp_wf(dwi_meta, epi_fmaps, omp_nthreads=1, name="pepolar_unwarp_wf"): """ This workflow takes in a set of EPI files with opposite phase encoding direction than the target file and calculates a displacements field (in other words, an ANTs-compatible warp file). This procedure works if there is only one '_epi' file is present (as long as it has the opposite phase encoding direction to the target file). The target file will be used to estimate the field distortion. However, if there is another '_epi' file present with a matching phase encoding direction to the target it will be used instead. Currently, different phase encoding dimension in the target file and the '_epi' file(s) (for example 'i' and 'j') is not supported. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from qsiprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf( dwi_meta={'PhaseEncodingDirection': 'j'}, epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')], omp_nthreads=8) Inputs in_reference the reference image in_reference_brain the reference image skullstripped in_mask a brain mask corresponding to ``in_reference`` Outputs out_reference the ``in_reference`` after unwarping out_warp the corresponding :abbr:`DFM (displacements field map)` compatible with ANTs """ dwi_file_pe = dwi_meta["PhaseEncodingDirection"] args = '-noXdis -noYdis -noZdis' rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[dwi_file_pe[0]] args = args.replace(rm_arg, '') workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on two echo-planar imaging (EPI) references with opposing phase-encoding directions, using `3dQwarp` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface( fields=['in_reference', 'in_reference_brain', 'in_mask']), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['out_reference', 'out_warp']), name='outputnode') prepare_epi_opposite_wf = init_prepare_dwi_epi_wf( omp_nthreads=omp_nthreads, name="prepare_epi_opposite_wf") prepare_epi_opposite_wf.inputs.inputnode.fmaps = epi_fmaps qwarp = pe.Node(afni.QwarpPlusMinus( pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}, args=args), name='qwarp', n_procs=omp_nthreads) workflow.connect([(inputnode, prepare_epi_opposite_wf, [('in_reference_brain', 'inputnode.ref_brain')]), (prepare_epi_opposite_wf, qwarp, [('outputnode.out_file', 'base_file')]), (inputnode, qwarp, [('in_reference_brain', 'in_file')])]) to_ants = pe.Node(niu.Function(function=_fix_hdr), name='to_ants', mem_gb=0.01) cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01) unwarp_reference = pe.Node(ANTSApplyTransformsRPT( dimension=3, generate_report=False, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_reference') workflow.connect([ (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]), (qwarp, cphdr_warp, [('source_warp', 'in_file')]), (cphdr_warp, to_ants, [('out_file', 'in_file')]), (to_ants, unwarp_reference, [('out', 'transforms')]), (inputnode, unwarp_reference, [('in_reference', 'reference_image'), ('in_reference', 'input_image')]), (unwarp_reference, outputnode, [('output_image', 'out_reference')]), (to_ants, outputnode, [('out', 'out_warp')]), ]) return workflow
def init_bold_stc_wf(metadata, name='bold_stc_wf'): """ Create a workflow for :abbr:`STC (slice-timing correction)`. This workflow performs :abbr:`STC (slice-timing correction)` over the input :abbr:`BOLD (blood-oxygen-level dependent)` image. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold import init_bold_stc_wf wf = init_bold_stc_wf( metadata={"RepetitionTime": 2.0, "SliceTiming": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]}, ) Parameters ---------- metadata : :obj:`dict` BIDS metadata for BOLD file name : :obj:`str` Name of workflow (default: ``bold_stc_wf``) Inputs ------ bold_file BOLD series NIfTI file skip_vols Number of non-steady-state volumes detected at beginning of ``bold_file`` Outputs ------- stc_file Slice-timing corrected BOLD series NIfTI file """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.utils import CopyXForm workflow = Workflow(name=name) workflow.__desc__ = """\ BOLD runs were slice-time corrected using `3dTshift` from AFNI {afni_ver} [@afni, RRID:SCR_005927]. """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node( niu.IdentityInterface(fields=['bold_file', 'skip_vols']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['stc_file']), name='outputnode') LOGGER.log(25, 'Slice-timing correction will be included.') # It would be good to fingerprint memory use of afni.TShift slice_timing_correction = pe.Node(afni.TShift( outputtype='NIFTI_GZ', tr='{}s'.format(metadata["RepetitionTime"]), slice_timing=metadata['SliceTiming'], slice_encoding_direction=metadata.get('SliceEncodingDirection', 'k')), name='slice_timing_correction') copy_xform = pe.Node(CopyXForm(), name='copy_xform', mem_gb=0.1) workflow.connect([ (inputnode, slice_timing_correction, [('bold_file', 'in_file'), ('skip_vols', 'ignore')]), (slice_timing_correction, copy_xform, [('out_file', 'in_file')]), (inputnode, copy_xform, [('bold_file', 'hdr_file')]), (copy_xform, outputnode, [('out_file', 'stc_file')]), ]) return workflow
def init_bidirectional_b0_unwarping_wf(template_plus_pe, omp_nthreads=1, name="bidirectional_pepolar_unwarping_wf"): """ This workflow takes in a set of b0 files with opposite phase encoding direction and calculates displacement fields (in other words, an ANTs-compatible warp file). This is intended to be run in the case where there are two dwi series in the same session with reverse phase encoding directions. The warp field correcting for the distortions is estimated using AFNI's 3dQwarp, with displacement estimation limited to the target file phase encoding direction. It also calculates a new mask for the input dataset that takes into account the distortions. .. workflow :: :graph2use: orig :simple_form: yes from qsiprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf wf = init_pepolar_unwarp_wf( bold_meta={'PhaseEncodingDirection': 'j'}, epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')], omp_nthreads=8) Inputs template_plus b0 template in one PE template_minus b0_template in the other PE Outputs out_reference the ``in_reference`` after unwarping out_reference_brain the ``in_reference`` after unwarping and skullstripping out_warp_plus the corresponding :abbr:`DFM (displacements field map)` to correct ``template_plus`` out_warp_minus the corresponding :abbr:`DFM (displacements field map)` to correct ``template_minus`` out_mask mask of the unwarped input file """ args = '-noXdis -noYdis -noZdis' rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[template_plus_pe[0]] args = args.replace(rm_arg, '') workflow = Workflow(name=name) workflow.__desc__ = """\ A deformation field to correct for susceptibility distortions was estimated based on two b0 templates created from dwi series with opposing phase-encoding directions, using `3dQwarp` @afni (AFNI {afni_ver}). """.format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []])) inputnode = pe.Node(niu.IdentityInterface( fields=['template_plus', 'template_minus', 't1w_brain']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['out_reference', 'out_reference_brain', 'out_affine_plus', 'out_warp_plus', 'out_affine_minus', 'out_warp_minus', 'out_mask']), name='outputnode') # Create high-contrast ref images plus_ref_wf = init_dwi_reference_wf(name='plus_ref_wf') minus_ref_wf = init_dwi_reference_wf(name='minus_ref_wf') # Align the two reference images to the midpoint inputs_to_list = pe.Node(niu.Merge(2), name='inputs_to_list') align_reverse_pe_wf = init_b0_hmc_wf(align_to='iterative', transform='Rigid') get_midpoint_transforms = pe.Node(niu.Split(splits=[1, 1], squeeze=True), name="get_midpoint_transforms") plus_to_midpoint = pe.Node(ants.ApplyTransforms(float=True, interpolation='LanczosWindowedSinc', dimension=3), name='plus_to_midpoint') minus_to_midpoint = pe.Node(ants.ApplyTransforms(float=True, interpolation='LanczosWindowedSinc', dimension=3), name='minus_to_midpoint') qwarp = pe.Node(afni.QwarpPlusMinus(pblur=[0.05, 0.05], blur=[-1, -1], noweight=True, minpatch=9, nopadWARP=True, environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}, args=args), name='qwarp', n_procs=omp_nthreads) to_ants_plus = pe.Node(niu.Function(function=_fix_hdr), name='to_ants_plus', mem_gb=0.01) to_ants_minus = pe.Node(niu.Function(function=_fix_hdr), name='to_ants_minus', mem_gb=0.01) cphdr_plus_warp = pe.Node(CopyHeader(), name='cphdr_plus_warp', mem_gb=0.01) cphdr_minus_warp = pe.Node(CopyHeader(), name='cphdr_minus_warp', mem_gb=0.01) unwarp_plus_reference = pe.Node(ants.ApplyTransforms(dimension=3, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_plus_reference') unwarp_minus_reference = pe.Node(ants.ApplyTransforms(dimension=3, float=True, interpolation='LanczosWindowedSinc'), name='unwarp_minus_reference') unwarped_to_list = pe.Node(niu.Merge(2), name="unwarped_to_list") merge_unwarped = pe.Node(ants.AverageImages(dimension=3, normalize=True), name="merge_unwarped") final_ref = init_dwi_reference_wf(name="final_ref") workflow.connect([ (inputnode, plus_ref_wf, [('template_plus', 'inputnode.b0_template')]), (plus_ref_wf, inputs_to_list, [('outputnode.ref_image', 'in1')]), (inputnode, minus_ref_wf, [('template_minus', 'inputnode.b0_template')]), (minus_ref_wf, inputs_to_list, [('outputnode.ref_image', 'in2')]), (inputs_to_list, align_reverse_pe_wf, [('out', 'inputnode.b0_images')]), (align_reverse_pe_wf, get_midpoint_transforms, [('outputnode.forward_transforms', 'inlist')]), (get_midpoint_transforms, outputnode, [('out1', 'out_affine_plus'), ('out2', 'out_affine_minus')]), (plus_ref_wf, plus_to_midpoint, [('outputnode.ref_image', 'input_image')]), (minus_ref_wf, minus_to_midpoint, [('outputnode.ref_image', 'input_image')]), (get_midpoint_transforms, plus_to_midpoint, [('out1', 'transforms')]), (align_reverse_pe_wf, plus_to_midpoint, [('outputnode.final_template', 'reference_image')]), (get_midpoint_transforms, minus_to_midpoint, [('out2', 'transforms')]), (align_reverse_pe_wf, minus_to_midpoint, [('outputnode.final_template', 'reference_image')]), (plus_to_midpoint, qwarp, [('output_image', 'in_file')]), (minus_to_midpoint, qwarp, [('output_image', 'base_file')]), (align_reverse_pe_wf, cphdr_plus_warp, [('outputnode.final_template', 'hdr_file')]), (align_reverse_pe_wf, cphdr_minus_warp, [('outputnode.final_template', 'hdr_file')]), (qwarp, cphdr_plus_warp, [('source_warp', 'in_file')]), (qwarp, cphdr_minus_warp, [('base_warp', 'in_file')]), (cphdr_plus_warp, to_ants_plus, [('out_file', 'in_file')]), (cphdr_minus_warp, to_ants_minus, [('out_file', 'in_file')]), (to_ants_minus, unwarp_minus_reference, [('out', 'transforms')]), (minus_to_midpoint, unwarp_minus_reference, [('output_image', 'reference_image'), ('output_image', 'input_image')]), (to_ants_minus, outputnode, [('out', 'out_warp_minus')]), (to_ants_plus, unwarp_plus_reference, [('out', 'transforms')]), (plus_to_midpoint, unwarp_plus_reference, [('output_image', 'reference_image'), ('output_image', 'input_image')]), (to_ants_plus, outputnode, [('out', 'out_warp_plus')]), (unwarp_plus_reference, unwarped_to_list, [('output_image', 'in1')]), (unwarp_minus_reference, unwarped_to_list, [('output_image', 'in2')]), (unwarped_to_list, merge_unwarped, [('out', 'images')]), (merge_unwarped, final_ref, [('output_average_image', 'inputnode.b0_template')]), (final_ref, outputnode, [('outputnode.ref_image', 'out_reference'), ('outputnode.ref_image_brain', 'out_reference_brain'), ('outputnode.dwi_mask', 'out_mask')]) ]) return workflow
def init_bold_hmc_wf(mem_gb, omp_nthreads, name="bold_hmc_wf"): """ Build a workflow to estimate head-motion parameters. This workflow estimates the motion parameters to perform :abbr:`HMC (head motion correction)` over the input :abbr:`BOLD (blood-oxygen-level dependent)` image. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fprodents.workflows.bold.hmc import init_bold_hmc_wf wf = init_bold_hmc_wf( mem_gb=3, omp_nthreads=1) Parameters ---------- mem_gb : :obj:`float` Size of BOLD file in GB omp_nthreads : :obj:`int` Maximum number of threads an individual process may use name : :obj:`str` Name of workflow (default: ``bold_hmc_wf``) Inputs ------ bold_file BOLD series NIfTI file raw_ref_image Reference image to which BOLD series is motion corrected Outputs ------- xforms ITKTransform file aligning each volume to ``ref_image`` movpar_file MCFLIRT motion parameters, normalized to SPM format (X, Y, Z, Rx, Ry, Rz) rms_file Framewise displacement as measured by ``fsl_motion_outliers`` [Jenkinson2002]_. """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.confounds import NormalizeMotionParams workflow = Workflow(name=name) workflow.__desc__ = """\ Head-motion parameters with respect to the BOLD reference (transformation matrices, and six corresponding rotation and translation parameters) are estimated before any spatiotemporal filtering using `3dVolReg` [AFNI {afni_ver}, @afni, RRID:SCR_005927]. """.format(afni_ver=afni.Info().version() or "<ver>") inputnode = pe.Node( niu.IdentityInterface(fields=["bold_file", "raw_ref_image"]), name="inputnode") outputnode = pe.Node( niu.IdentityInterface(fields=["xforms", "movpar_file", "rmsd_file"]), name="outputnode", ) # Head motion correction (hmc) # mcflirt = pe.Node( # fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True), # name='mcflirt', mem_gb=mem_gb * 3) # fsl2itk = pe.Node(MCFLIRT2ITK(), name='fsl2itk', # mem_gb=0.05, n_procs=omp_nthreads) mc = pe.Node( afni.Volreg(zpad=4, outputtype="NIFTI_GZ", args="-prefix NULL -twopass"), name="mc", mem_gb=mem_gb * 3, ) mc2itk = pe.Node(Volreg2ITK(), name="mcitk", mem_gb=0.05) normalize_motion = pe.Node( NormalizeMotionParams(format="AFNI"), name="normalize_motion", mem_gb=DEFAULT_MEMORY_MIN_GB, ) # def _pick_rel(rms_files): # return rms_files[-1] # workflow.connect([ # (inputnode, mcflirt, [('raw_ref_image', 'ref_file'), # ('bold_file', 'in_file')]), # (inputnode, fsl2itk, [('raw_ref_image', 'in_source'), # ('raw_ref_image', 'in_reference')]), # (mcflirt, fsl2itk, [('mat_file', 'in_files')]), # (mcflirt, normalize_motion, [('par_file', 'in_file')]), # (mcflirt, outputnode, [(('rms_files', _pick_rel), 'rmsd_file')]), # (fsl2itk, outputnode, [('out_file', 'xforms')]), # (normalize_motion, outputnode, [('out_file', 'movpar_file')]), # ]) # fmt:off workflow.connect([ (inputnode, mc, [ ('raw_ref_image', 'basefile'), ('bold_file', 'in_file'), ]), (mc, mc2itk, [('oned_matrix_save', 'in_file')]), (mc, normalize_motion, [('oned_file', 'in_file')]), (mc2itk, outputnode, [('out_file', 'xforms')]), (normalize_motion, outputnode, [('out_file', 'movpar_file')]), ]) # fmt:on return workflow