def init_bold_std_trans_wf(freesurfer, mem_gb, omp_nthreads, standard_spaces, name='bold_std_trans_wf', use_compression=True, use_fieldwarp=False): """ This workflow samples functional images into standard space with a single resampling of the original BOLD series. .. workflow:: :graph2use: colored :simple_form: yes from collections import OrderedDict from fmriprep.workflows.bold import init_bold_std_trans_wf wf = init_bold_std_trans_wf( freesurfer=True, mem_gb=3, omp_nthreads=1, standard_spaces=OrderedDict([('MNI152Lin', {}), ('fsaverage', {'density': '10k'})]), ) **Parameters** freesurfer : bool Whether to generate FreeSurfer's aseg/aparc segmentations on BOLD space. mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use standard_spaces : OrderedDict Ordered dictionary where keys are TemplateFlow ID strings (e.g., ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNI152NLin2009cAsym``, or ``fsLR``), or paths pointing to custom templates organized in a TemplateFlow-like structure. Values of the dictionary aggregate modifiers (e.g., the value for the key ``MNI152Lin`` could be ``{'resolution': 2}`` if one wants the resampling to be done on the 2mm resolution version of the selected template). name : str Name of workflow (default: ``bold_std_trans_wf``) use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to MNI **Inputs** anat2std_xfm List of anatomical-to-standard space transforms generated during spatial normalization. bold_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_mask Skull-stripping mask of reference image bold_split Individual 3D volumes, not motion corrected fieldwarp a :abbr:`DFM (displacements field map)` in ITK format hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) name_source BOLD series NIfTI file Used to recover original information lost during processing templates List of templates that were applied as targets during spatial normalization. **Outputs** - Two outputnodes are available. One output node (with name ``poutputnode``) will be parameterized in a Nipype sense (see `Nipype iterables <https://miykael.github.io/nipype_tutorial/notebooks/basic_iteration.html>`__), and a second node (``outputnode``) will collapse the parameterized outputs into synchronous lists of the following fields: bold_std BOLD series, resampled to template space bold_std_ref Reference, contrast-enhanced summary of the BOLD series, resampled to template space bold_mask_std BOLD series mask in template space bold_aseg_std FreeSurfer's ``aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) bold_aparc_std FreeSurfer's ``aparc+aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) templates Template identifiers synchronized correspondingly to previously described outputs. """ # Filter ``standard_spaces`` vol_std_spaces = [ k for k in standard_spaces.keys() if not k.startswith('fs') ] workflow = Workflow(name=name) if len(vol_std_spaces) == 1: workflow.__desc__ = """\ The BOLD time-series were resampled into standard space, generating a *preprocessed BOLD run in {tpl} space*. """.format(tpl=vol_std_spaces) else: workflow.__desc__ = """\ The BOLD time-series were resampled into several standard spaces, correspondingly generating the following *spatially-normalized, preprocessed BOLD runs*: {tpl}. """.format(tpl=', '.join(vol_std_spaces)) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'anat2std_xfm', 'bold_aparc', 'bold_aseg', 'bold_mask', 'bold_split', 'fieldwarp', 'hmc_xforms', 'itk_bold_to_t1', 'name_source', 'templates', ]), name='inputnode') select_std = pe.Node(KeySelect(fields=['resolution', 'anat2std_xfm']), name='select_std', run_without_submitting=True) select_std.inputs.resolution = [ v.get('resolution') or v.get('res') or 'native' for k, v in list(standard_spaces.items()) if k in vol_std_spaces ] select_std.iterables = ('key', vol_std_spaces) select_tpl = pe.Node(niu.Function(function=_select_template), name='select_tpl', run_without_submitting=True) select_tpl.inputs.template_specs = standard_spaces gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB) mask_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_std_tfm', mem_gb=1) # Write corrected file in the designated output dir mask_merge_tfms = pe.Node(niu.Merge(2), name='mask_merge_tfms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, select_std, [('templates', 'keys'), ('anat2std_xfm', 'anat2std_xfm')]), (inputnode, mask_std_tfm, [('bold_mask', 'input_image')]), (inputnode, gen_ref, [(('bold_split', _first), 'moving_image')]), (inputnode, mask_merge_tfms, [(('itk_bold_to_t1', _aslist), 'in2')]), (select_std, select_tpl, [('key', 'template')]), (select_std, mask_merge_tfms, [('anat2std_xfm', 'in1')]), (select_std, gen_ref, [(('resolution', _is_native), 'keep_native')]), (select_tpl, gen_ref, [('out', 'fixed_image')]), (mask_merge_tfms, mask_std_tfm, [('out', 'transforms')]), (gen_ref, mask_std_tfm, [('out_file', 'reference_image')]), ]) nxforms = 4 if use_fieldwarp else 3 merge_xforms = pe.Node(niu.Merge(nxforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nxforms)])]) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in3')])]) bold_to_std_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_std_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3) # Generate a reference on the target T1w space gen_final_ref = init_bold_reference_wf(omp_nthreads=omp_nthreads, pre_mask=True) workflow.connect([ (inputnode, merge_xforms, [(('itk_bold_to_t1', _aslist), 'in2')]), (inputnode, merge, [('name_source', 'header_source')]), (inputnode, bold_to_std_transform, [('bold_split', 'input_image')]), (select_std, merge_xforms, [('anat2std_xfm', 'in1')]), (merge_xforms, bold_to_std_transform, [('out', 'transforms')]), (gen_ref, bold_to_std_transform, [('out_file', 'reference_image')]), (bold_to_std_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), (mask_std_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask') ]), ]) # Connect output nodes output_names = ['bold_std', 'bold_std_ref', 'bold_mask_std', 'templates'] if freesurfer: output_names += ['bold_aseg_std', 'bold_aparc_std'] # poutputnode - parametric output node poutputnode = pe.Node(niu.IdentityInterface(fields=output_names), name='poutputnode') workflow.connect([ (gen_final_ref, poutputnode, [('outputnode.ref_image', 'bold_std_ref') ]), (merge, poutputnode, [('out_file', 'bold_std')]), (mask_std_tfm, poutputnode, [('output_image', 'bold_mask_std')]), (select_std, poutputnode, [('key', 'templates')]), ]) if freesurfer: # Sample the parcellation files to functional space aseg_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='aseg_std_tfm', mem_gb=1) aparc_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='aparc_std_tfm', mem_gb=1) workflow.connect([ (inputnode, aseg_std_tfm, [('bold_aseg', 'input_image')]), (inputnode, aparc_std_tfm, [('bold_aparc', 'input_image')]), (select_std, aseg_std_tfm, [('anat2std_xfm', 'transforms')]), (select_std, aparc_std_tfm, [('anat2std_xfm', 'transforms')]), (gen_ref, aseg_std_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_std_tfm, [('out_file', 'reference_image')]), (aseg_std_tfm, poutputnode, [('output_image', 'bold_aseg_std')]), (aparc_std_tfm, poutputnode, [('output_image', 'bold_aparc_std')]), ]) # Connect outputnode to the parameterized outputnode outputnode = pe.JoinNode(niu.IdentityInterface(fields=output_names), name='outputnode', joinsource='select_std') workflow.connect([(poutputnode, outputnode, [(f, f) for f in output_names])]) return workflow
def init_bold_mni_trans_wf(template, mem_gb, omp_nthreads, name='bold_mni_trans_wf', template_out_grid='2mm', use_compression=True, use_fieldwarp=False): """ This workflow samples functional images to the MNI template in a "single shot" from the original BOLD series. .. workflow:: :graph2use: colored :simple_form: yes from fmriprep.workflows.bold import init_bold_mni_trans_wf wf = init_bold_mni_trans_wf(template='MNI152NLin2009cAsym', mem_gb=3, omp_nthreads=1, template_out_grid='native') **Parameters** template : str Name of template targeted by ``template`` output space mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use name : str Name of workflow (default: ``bold_mni_trans_wf``) template_out_grid : str Keyword ('native', '1mm' or '2mm') or path of custom reference image for normalization. use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to MNI **Inputs** itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) t1_2_mni_forward_transform ANTs-compatible affine-and-warp transform file bold_split Individual 3D volumes, not motion corrected bold_mask Skull-stripping mask of reference image name_source BOLD series NIfTI file Used to recover original information lost during processing hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format fieldwarp a :abbr:`DFM (displacements field map)` in ITK format **Outputs** bold_mni BOLD series, resampled to template space bold_mask_mni BOLD series mask in template space """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'itk_bold_to_t1', 't1_2_mni_forward_transform', 'name_source', 'bold_split', 'bold_mask', 'hmc_xforms', 'fieldwarp' ]), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['bold_mni', 'bold_mask_mni']), name='outputnode') def _aslist(in_value): if isinstance(in_value, list): return in_value return [in_value] gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB) template_str = nid.TEMPLATE_MAP[template] gen_ref.inputs.fixed_image = op.join(nid.get_dataset(template_str), '1mm_T1.nii.gz') mask_mni_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_mni_tfm', mem_gb=1) # Write corrected file in the designated output dir mask_merge_tfms = pe.Node(niu.Merge(2), name='mask_merge_tfms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) nxforms = 4 if use_fieldwarp else 3 merge_xforms = pe.Node(niu.Merge(nxforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nxforms)])]) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in3')])]) workflow.connect([ (inputnode, gen_ref, [(('bold_split', _first), 'moving_image')]), (inputnode, mask_merge_tfms, [('t1_2_mni_forward_transform', 'in1'), (('itk_bold_to_t1', _aslist), 'in2')]), (mask_merge_tfms, mask_mni_tfm, [('out', 'transforms')]), (mask_mni_tfm, outputnode, [('output_image', 'bold_mask_mni')]), (inputnode, mask_mni_tfm, [('bold_mask', 'input_image')]) ]) bold_to_mni_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_mni_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3) workflow.connect([ (inputnode, merge_xforms, [('t1_2_mni_forward_transform', 'in1'), (('itk_bold_to_t1', _aslist), 'in2')]), (merge_xforms, bold_to_mni_transform, [('out', 'transforms')]), (inputnode, merge, [('name_source', 'header_source')]), (inputnode, bold_to_mni_transform, [('bold_split', 'input_image')]), (bold_to_mni_transform, merge, [('out_files', 'in_files')]), (merge, outputnode, [('out_file', 'bold_mni')]), ]) if template_out_grid == 'native': workflow.connect([ (gen_ref, mask_mni_tfm, [('out_file', 'reference_image')]), (gen_ref, bold_to_mni_transform, [('out_file', 'reference_image') ]), ]) elif template_out_grid == '1mm' or template_out_grid == '2mm': mask_mni_tfm.inputs.reference_image = op.join( nid.get_dataset(template_str), '%s_brainmask.nii.gz' % template_out_grid) bold_to_mni_transform.inputs.reference_image = op.join( nid.get_dataset(template_str), '%s_T1.nii.gz' % template_out_grid) else: mask_mni_tfm.inputs.reference_image = template_out_grid bold_to_mni_transform.inputs.reference_image = template_out_grid return workflow
def init_bold_mni_trans_wf(template, freesurfer, mem_gb, omp_nthreads, name='bold_mni_trans_wf', template_out_grid='2mm', use_compression=True, use_fieldwarp=False): """ This workflow samples functional images to the MNI template in a "single shot" from the original BOLD series. .. workflow:: :graph2use: colored :simple_form: yes from fmriprep.workflows.bold import init_bold_mni_trans_wf wf = init_bold_mni_trans_wf(template='MNI152NLin2009cAsym', freesurfer=True, mem_gb=3, omp_nthreads=1, template_out_grid='native') **Parameters** template : str Name of template targeted by ``template`` output space freesurfer : bool Enable sampling of FreeSurfer files mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use name : str Name of workflow (default: ``bold_mni_trans_wf``) template_out_grid : str Keyword ('native', '1mm' or '2mm') or path of custom reference image for normalization. use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to MNI **Inputs** itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) t1_2_mni_forward_transform ANTs-compatible affine-and-warp transform file bold_split Individual 3D volumes, not motion corrected bold_mask Skull-stripping mask of reference image bold_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). name_source BOLD series NIfTI file Used to recover original information lost during processing hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format fieldwarp a :abbr:`DFM (displacements field map)` in ITK format **Outputs** bold_mni BOLD series, resampled to template space bold_mni_ref Reference, contrast-enhanced summary of the BOLD series, resampled to template space bold_mask_mni BOLD series mask in template space bold_aseg_mni FreeSurfer's ``aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) bold_aparc_mni FreeSurfer's ``aparc+aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) """ workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD time-series were resampled to {tpl} standard space, generating a *preprocessed BOLD run in {tpl} space*. """.format(tpl=template) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'itk_bold_to_t1', 't1_2_mni_forward_transform', 'name_source', 'bold_split', 'bold_mask', 'bold_aseg', 'bold_aparc', 'hmc_xforms', 'fieldwarp' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_mni', 'bold_mni_ref', 'bold_mask_mni', 'bold_aseg_mni', 'bold_aparc_mni' ]), name='outputnode') def _aslist(in_value): if isinstance(in_value, list): return in_value return [in_value] gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB) # Account for template aliases template_name = TEMPLATE_ALIASES.get(template, template) # Template path template_dir = get_template(template_name) gen_ref.inputs.fixed_image = str( template_dir / ('tpl-%s_space-MNI_res-01_T1w.nii.gz' % template_name)) mask_mni_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_mni_tfm', mem_gb=1) # Write corrected file in the designated output dir mask_merge_tfms = pe.Node(niu.Merge(2), name='mask_merge_tfms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, gen_ref, [(('bold_split', _first), 'moving_image')]), (inputnode, mask_mni_tfm, [('bold_mask', 'input_image')]), (inputnode, mask_merge_tfms, [('t1_2_mni_forward_transform', 'in1'), (('itk_bold_to_t1', _aslist), 'in2')]), (mask_merge_tfms, mask_mni_tfm, [('out', 'transforms')]), (mask_mni_tfm, outputnode, [('output_image', 'bold_mask_mni')]), ]) nxforms = 4 if use_fieldwarp else 3 merge_xforms = pe.Node(niu.Merge(nxforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nxforms)])]) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in3')])]) bold_to_mni_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_mni_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3) # Generate a reference on the target T1w space gen_final_ref = init_bold_reference_wf(omp_nthreads=omp_nthreads, pre_mask=True) workflow.connect([ (inputnode, merge_xforms, [('t1_2_mni_forward_transform', 'in1'), (('itk_bold_to_t1', _aslist), 'in2')]), (merge_xforms, bold_to_mni_transform, [('out', 'transforms')]), (inputnode, merge, [('name_source', 'header_source')]), (inputnode, bold_to_mni_transform, [('bold_split', 'input_image')]), (bold_to_mni_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), (mask_mni_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask') ]), (merge, outputnode, [('out_file', 'bold_mni')]), (gen_final_ref, outputnode, [('outputnode.ref_image', 'bold_mni_ref') ]), ]) if template_out_grid == 'native': workflow.connect([ (gen_ref, mask_mni_tfm, [('out_file', 'reference_image')]), (gen_ref, bold_to_mni_transform, [('out_file', 'reference_image') ]), ]) elif template_out_grid in ['1mm', '2mm']: res = int(template_out_grid[0]) mask_mni_tfm.inputs.reference_image = str( template_dir / ('tpl-%s_space-MNI_res-%02d_brainmask.nii.gz' % (template_name, res))) bold_to_mni_transform.inputs.reference_image = str( template_dir / ('tpl-%s_space-MNI_res-%02d_T1w.nii.gz' % (template_name, res))) else: mask_mni_tfm.inputs.reference_image = template_out_grid bold_to_mni_transform.inputs.reference_image = template_out_grid if freesurfer: # Sample the parcellation files to functional space aseg_mni_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='aseg_mni_tfm', mem_gb=1) aparc_mni_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='aparc_mni_tfm', mem_gb=1) workflow.connect([ (inputnode, aseg_mni_tfm, [('bold_aseg', 'input_image'), ('t1_2_mni_forward_transform', 'transforms')]), (inputnode, aparc_mni_tfm, [('bold_aparc', 'input_image'), ('t1_2_mni_forward_transform', 'transforms')]), (aseg_mni_tfm, outputnode, [('output_image', 'bold_aseg_mni')]), (aparc_mni_tfm, outputnode, [('output_image', 'bold_aparc_mni')]), ]) if template_out_grid == 'native': workflow.connect([ (gen_ref, aseg_mni_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_mni_tfm, [('out_file', 'reference_image')]), ]) elif template_out_grid in ['1mm', '2mm']: res = int(template_out_grid[0]) aseg_mni_tfm.inputs.reference_image = str( template_dir / ('tpl-%s_space-MNI_res-%02d_brainmask.nii.gz' % (template_name, res))) aparc_mni_tfm.inputs.reference_image = str( template_dir / ('tpl-%s_space-MNI_res-%02d_T1w.nii.gz' % (template_name, res))) else: aseg_mni_tfm.inputs.reference_image = template_out_grid aparc_mni_tfm.inputs.reference_image = template_out_grid return workflow
def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, use_compression=True, name='bold_t1_trans_wf'): """ Co-register the reference BOLD image to T1w-space. The workflow uses :abbr:`BBR (boundary-based registration)`. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_bold_t1_trans_wf wf = init_bold_t1_trans_wf(freesurfer=True, mem_gb=3, omp_nthreads=1) Parameters ---------- freesurfer : :obj:`bool` Enable FreeSurfer functional registration (bbregister) mem_gb : :obj:`float` Size of BOLD file in GB omp_nthreads : :obj:`int` Maximum number of threads an individual process may use use_compression : :obj:`bool` Save registered BOLD series as ``.nii.gz`` name : :obj:`str` Name of workflow (default: ``bold_reg_wf``) Inputs ------ name_source BOLD series NIfTI file Used to recover original information lost during processing ref_bold_brain Reference image to which BOLD series is aligned If ``fieldwarp == True``, ``ref_bold_brain`` should be unwarped ref_bold_mask Skull-stripping mask of reference image t1w_brain Skull-stripped bias-corrected structural template image t1w_mask Mask of the skull-stripped template image t1w_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). t1w_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_split Individual 3D BOLD volumes, not motion corrected hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) fieldwarp a :abbr:`DFM (displacements field map)` in ITK format Outputs ------- bold_t1 Motion-corrected BOLD series in T1 space bold_t1_ref Reference, contrast-enhanced summary of the motion-corrected BOLD series in T1w space bold_mask_t1 BOLD mask in T1 space bold_aseg_t1 FreeSurfer's ``aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). bold_aparc_t1 FreeSurfer's ``aparc+aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). See also -------- * :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf` * :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf` """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.func.util import init_bold_reference_wf from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces.itk import MultiApplyTransforms from niworkflows.interfaces.nilearn import Merge from niworkflows.interfaces.utils import GenerateSamplingReference workflow = Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'name_source', 'ref_bold_brain', 'ref_bold_mask', 't1w_brain', 't1w_mask', 't1w_aseg', 't1w_aparc', 'bold_split', 'fieldwarp', 'hmc_xforms', 'itk_bold_to_t1' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_t1', 'bold_t1_ref', 'bold_mask_t1', 'bold_aseg_t1', 'bold_aparc_t1' ]), name='outputnode') gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB mask_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel'), name='mask_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, gen_ref, [('ref_bold_brain', 'moving_image'), ('t1w_brain', 'fixed_image'), ('t1w_mask', 'fov_mask')]), (inputnode, mask_t1w_tfm, [('ref_bold_mask', 'input_image')]), (gen_ref, mask_t1w_tfm, [('out_file', 'reference_image')]), (inputnode, mask_t1w_tfm, [('itk_bold_to_t1', 'transforms')]), (mask_t1w_tfm, outputnode, [('output_image', 'bold_mask_t1')]), ]) if freesurfer: # Resample aseg and aparc in T1w space (no transforms needed) aseg_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', transforms='identity'), name='aseg_t1w_tfm', mem_gb=0.1) aparc_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', transforms='identity'), name='aparc_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, aseg_t1w_tfm, [('t1w_aseg', 'input_image')]), (inputnode, aparc_t1w_tfm, [('t1w_aparc', 'input_image')]), (gen_ref, aseg_t1w_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_t1w_tfm, [('out_file', 'reference_image')]), (aseg_t1w_tfm, outputnode, [('output_image', 'bold_aseg_t1')]), (aparc_t1w_tfm, outputnode, [('output_image', 'bold_aparc_t1')]), ]) bold_to_t1w_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_t1w_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) # merge 3D volumes into 4D timeseries merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb) # Generate a reference on the target T1w space gen_final_ref = init_bold_reference_wf(omp_nthreads, pre_mask=True) # Merge transforms placing the head motion correction last merge_xforms = pe.Node(niu.Merge(3), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, merge, [('name_source', 'header_source')]), ( inputnode, merge_xforms, [ ('hmc_xforms', 'in3'), # May be 'identity' if HMC already applied ('fieldwarp', 'in2'), # May be 'identity' if SDC already applied ('itk_bold_to_t1', 'in1') ]), (inputnode, bold_to_t1w_transform, [('bold_split', 'input_image')]), (merge_xforms, bold_to_t1w_transform, [('out', 'transforms')]), (gen_ref, bold_to_t1w_transform, [('out_file', 'reference_image')]), (bold_to_t1w_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), (mask_t1w_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask') ]), (merge, outputnode, [('out_file', 'bold_t1')]), (gen_final_ref, outputnode, [('outputnode.ref_image', 'bold_t1_ref')]), ]) return workflow
def init_bold_std_trans_wf( freesurfer, mem_gb, omp_nthreads, spaces, name='bold_std_trans_wf', use_compression=True, use_fieldwarp=False, ): """ Sample fMRI into standard space with a single-step resampling of the original BOLD series. .. important:: This workflow provides two outputnodes. One output node (with name ``poutputnode``) will be parameterized in a Nipype sense (see `Nipype iterables <https://miykael.github.io/nipype_tutorial/notebooks/basic_iteration.html>`__), and a second node (``outputnode``) will collapse the parameterized outputs into synchronous lists of the output fields listed below. Workflow Graph .. workflow:: :graph2use: colored :simple_form: yes from niworkflows.utils.spaces import SpatialReferences from fmriprep_rodents.workflows.bold import init_bold_std_trans_wf wf = init_bold_std_trans_wf( freesurfer=True, mem_gb=3, omp_nthreads=1, spaces=SpatialReferences( spaces=['MNI152Lin', ('MNIPediatricAsym', {'cohort': '6'})], checkpoint=True), ) Parameters ---------- freesurfer : :obj:`bool` Whether to generate FreeSurfer's aseg/aparc segmentations on BOLD space. mem_gb : :obj:`float` Size of BOLD file in GB omp_nthreads : :obj:`int` Maximum number of threads an individual process may use spaces : :py:class:`~niworkflows.utils.spaces.SpatialReferences` A container for storing, organizing, and parsing spatial normalizations. Composed of :py:class:`~niworkflows.utils.spaces.Reference` objects representing spatial references. Each ``Reference`` contains a space, which is a string of either TemplateFlow template IDs (e.g., ``MNI152Lin``, ``MNI152NLin6Asym``, ``MNIPediatricAsym``), nonstandard references (e.g., ``T1w`` or ``anat``, ``sbref``, ``run``, etc.), or a custom template located in the TemplateFlow root directory. Each ``Reference`` may also contain a spec, which is a dictionary with template specifications (e.g., a specification of ``{'resolution': 2}`` would lead to resampling on a 2mm resolution of the space). name : :obj:`str` Name of workflow (default: ``bold_std_trans_wf``) use_compression : :obj:`bool` Save registered BOLD series as ``.nii.gz`` use_fieldwarp : :obj:`bool` Include SDC warp in single-shot transform from BOLD to MNI Inputs ------ anat2std_xfm List of anatomical-to-standard space transforms generated during spatial normalization. bold_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_mask Skull-stripping mask of reference image bold_split Individual 3D volumes, not motion corrected fieldwarp a :abbr:`DFM (displacements field map)` in ITK format hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) name_source BOLD series NIfTI file Used to recover original information lost during processing templates List of templates that were applied as targets during spatial normalization. Outputs ------- bold_std BOLD series, resampled to template space bold_std_ref Reference, contrast-enhanced summary of the BOLD series, resampled to template space bold_mask_std BOLD series mask in template space bold_aseg_std FreeSurfer's ``aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) bold_aparc_std FreeSurfer's ``aparc+aseg.mgz`` atlas, in template space at the BOLD resolution (only if ``recon-all`` was run) template Template identifiers synchronized correspondingly to previously described outputs. """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.func.util import init_bold_reference_wf from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces.itk import MultiApplyTransforms from niworkflows.interfaces.utility import KeySelect from niworkflows.interfaces.utils import GenerateSamplingReference from niworkflows.interfaces.nilearn import Merge from niworkflows.utils.spaces import format_reference workflow = Workflow(name=name) output_references = spaces.cached.get_spaces(nonstandard=False, dim=(3, )) std_vol_references = [(s.fullname, s.spec) for s in spaces.references if s.standard and s.dim == 3] if len(output_references) == 1: workflow.__desc__ = """\ The BOLD time-series were resampled into standard space, generating a *preprocessed BOLD run in {tpl} space*. """.format(tpl=output_references[0]) elif len(output_references) > 1: workflow.__desc__ = """\ The BOLD time-series were resampled into several standard spaces, correspondingly generating the following *spatially-normalized, preprocessed BOLD runs*: {tpl}. """.format(tpl=', '.join(output_references)) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'anat2std_xfm', 'bold_aparc', 'bold_aseg', 'bold_mask', 'bold_split', 'fieldwarp', 'hmc_xforms', 'itk_bold_to_t1', 'name_source', 'templates', ]), name='inputnode') iterablesource = pe.Node(niu.IdentityInterface(fields=['std_target']), name='iterablesource') # Generate conversions for every template+spec at the input iterablesource.iterables = [('std_target', std_vol_references)] split_target = pe.Node(niu.Function( function=_split_spec, input_names=['in_target'], output_names=['space', 'template', 'spec']), run_without_submitting=True, name='split_target') select_std = pe.Node(KeySelect(fields=['anat2std_xfm']), name='select_std', run_without_submitting=True) select_tpl = pe.Node(niu.Function(function=_select_template), name='select_tpl', run_without_submitting=True) gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB) mask_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel'), name='mask_std_tfm', mem_gb=1) # Write corrected file in the designated output dir mask_merge_tfms = pe.Node(niu.Merge(2), name='mask_merge_tfms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) nxforms = 3 + use_fieldwarp merge_xforms = pe.Node(niu.Merge(nxforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nxforms)])]) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in3')])]) bold_to_std_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_std_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb * 3) # Generate a reference on the target standard space gen_final_ref = init_bold_reference_wf(omp_nthreads=omp_nthreads, pre_mask=True) workflow.connect([ (iterablesource, split_target, [('std_target', 'in_target')]), (iterablesource, select_tpl, [('std_target', 'template')]), (inputnode, select_std, [('anat2std_xfm', 'anat2std_xfm'), ('templates', 'keys')]), (inputnode, mask_std_tfm, [('bold_mask', 'input_image')]), (inputnode, gen_ref, [(('bold_split', _first), 'moving_image')]), (inputnode, merge_xforms, [(('itk_bold_to_t1', _aslist), 'in2')]), (inputnode, merge, [('name_source', 'header_source')]), (inputnode, mask_merge_tfms, [(('itk_bold_to_t1', _aslist), 'in2')]), (inputnode, bold_to_std_transform, [('bold_split', 'input_image')]), (split_target, select_std, [('space', 'key')]), (select_std, merge_xforms, [('anat2std_xfm', 'in1')]), (select_std, mask_merge_tfms, [('anat2std_xfm', 'in1')]), (split_target, gen_ref, [(('spec', _is_native), 'keep_native')]), (select_tpl, gen_ref, [('out', 'fixed_image')]), (merge_xforms, bold_to_std_transform, [('out', 'transforms')]), (gen_ref, bold_to_std_transform, [('out_file', 'reference_image')]), (gen_ref, mask_std_tfm, [('out_file', 'reference_image')]), (mask_merge_tfms, mask_std_tfm, [('out', 'transforms')]), (mask_std_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask') ]), (bold_to_std_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), ]) output_names = [ 'bold_mask_std', 'bold_std', 'bold_std_ref', 'spatial_reference', 'template', ] + freesurfer * ['bold_aseg_std', 'bold_aparc_std'] poutputnode = pe.Node(niu.IdentityInterface(fields=output_names), name='poutputnode') workflow.connect([ # Connecting outputnode (iterablesource, poutputnode, [(('std_target', format_reference), 'spatial_reference')]), (merge, poutputnode, [('out_file', 'bold_std')]), (gen_final_ref, poutputnode, [('outputnode.ref_image', 'bold_std_ref') ]), (mask_std_tfm, poutputnode, [('output_image', 'bold_mask_std')]), (select_std, poutputnode, [('key', 'template')]), ]) if freesurfer: # Sample the parcellation files to functional space aseg_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel'), name='aseg_std_tfm', mem_gb=1) aparc_std_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel'), name='aparc_std_tfm', mem_gb=1) workflow.connect([ (inputnode, aseg_std_tfm, [('bold_aseg', 'input_image')]), (inputnode, aparc_std_tfm, [('bold_aparc', 'input_image')]), (select_std, aseg_std_tfm, [('anat2std_xfm', 'transforms')]), (select_std, aparc_std_tfm, [('anat2std_xfm', 'transforms')]), (gen_ref, aseg_std_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_std_tfm, [('out_file', 'reference_image')]), (aseg_std_tfm, poutputnode, [('output_image', 'bold_aseg_std')]), (aparc_std_tfm, poutputnode, [('output_image', 'bold_aparc_std')]), ]) # Connect parametric outputs to a Join outputnode outputnode = pe.JoinNode(niu.IdentityInterface(fields=output_names), name='outputnode', joinsource='iterablesource') workflow.connect([ (poutputnode, outputnode, [(f, f) for f in output_names]), ]) return workflow
def init_bold_reg_wf(freesurfer, use_bbr, bold2t1w_dof, mem_gb, omp_nthreads, name='bold_reg_wf', use_compression=True, use_fieldwarp=False, write_report=True): """ This workflow registers the reference BOLD image to T1-space, using a boundary-based registration (BBR) cost function. If FreeSurfer-based preprocessing is enabled, the ``bbregister`` utility is used to align the BOLD images to the reconstructed subject, and the resulting transform is adjusted to target the T1 space. If FreeSurfer-based preprocessing is disabled, FSL FLIRT is used with the BBR cost function to directly target the T1 space. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_bold_reg_wf wf = init_bold_reg_wf(freesurfer=True, mem_gb=3, omp_nthreads=1, use_bbr=True, bold2t1w_dof=9) **Parameters** freesurfer : bool Enable FreeSurfer functional registration (bbregister) use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use name : str Name of workflow (default: ``bold_reg_wf``) use_compression : bool Save registered BOLD series as ``.nii.gz`` use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to T1 write_report : bool Whether a reportlet should be stored **Inputs** name_source BOLD series NIfTI file Used to recover original information lost during processing ref_bold_brain Reference image to which BOLD series is aligned If ``fieldwarp == True``, ``ref_bold_brain`` should be unwarped ref_bold_mask Skull-stripping mask of reference image t1_preproc Bias-corrected structural template image t1_brain Skull-stripped ``t1_preproc`` t1_mask Mask of the skull-stripped template image t1_seg Segmentation of preprocessed structural image, including gray-matter (GM), white-matter (WM) and cerebrospinal fluid (CSF) t1_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). t1_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_split Individual 3D BOLD volumes, not motion corrected hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID t1_2_fsnative_reverse_transform LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w fieldwarp a :abbr:`DFM (displacements field map)` in ITK format **Outputs** itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) bold_t1 Motion-corrected BOLD series in T1 space bold_mask_t1 BOLD mask in T1 space bold_aseg_t1 FreeSurfer's ``aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). bold_aparc_t1 FreeSurfer's ``aparc+aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). fallback Boolean indicating whether BBR was rejected (mri_coreg registration returned) **Subworkflows** * :py:func:`~fmriprep.workflows.util.init_bbreg_wf` * :py:func:`~fmriprep.workflows.util.init_fsl_bbr_wf` """ workflow = Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'name_source', 'ref_bold_brain', 'ref_bold_mask', 't1_preproc', 't1_brain', 't1_mask', 't1_seg', 't1_aseg', 't1_aparc', 'bold_split', 'hmc_xforms', 'subjects_dir', 'subject_id', 't1_2_fsnative_reverse_transform', 'fieldwarp' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'itk_bold_to_t1', 'itk_t1_to_bold', 'fallback', 'bold_t1', 'bold_mask_t1', 'bold_aseg_t1', 'bold_aparc_t1' ]), name='outputnode') if freesurfer: bbr_wf = init_bbreg_wf(use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof, omp_nthreads=omp_nthreads) else: bbr_wf = init_fsl_bbr_wf(use_bbr=use_bbr, bold2t1w_dof=bold2t1w_dof) workflow.connect([ (inputnode, bbr_wf, [('ref_bold_brain', 'inputnode.in_file'), ('t1_2_fsnative_reverse_transform', 'inputnode.t1_2_fsnative_reverse_transform'), ('subjects_dir', 'inputnode.subjects_dir'), ('subject_id', 'inputnode.subject_id'), ('t1_seg', 'inputnode.t1_seg'), ('t1_brain', 'inputnode.t1_brain')]), (bbr_wf, outputnode, [('outputnode.itk_bold_to_t1', 'itk_bold_to_t1'), ('outputnode.itk_t1_to_bold', 'itk_t1_to_bold'), ('outputnode.fallback', 'fallback')]), ]) gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB mask_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, gen_ref, [('ref_bold_brain', 'moving_image'), ('t1_brain', 'fixed_image'), ('t1_mask', 'fov_mask')]), (inputnode, mask_t1w_tfm, [('ref_bold_mask', 'input_image')]), (gen_ref, mask_t1w_tfm, [('out_file', 'reference_image')]), (bbr_wf, mask_t1w_tfm, [('outputnode.itk_bold_to_t1', 'transforms')]), (mask_t1w_tfm, outputnode, [('output_image', 'bold_mask_t1')]), ]) if freesurfer: # Resample aseg and aparc in T1w space (no transforms needed) aseg_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', transforms='identity', float=True), name='aseg_t1w_tfm', mem_gb=0.1) aparc_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', transforms='identity', float=True), name='aparc_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, aseg_t1w_tfm, [('t1_aseg', 'input_image')]), (inputnode, aparc_t1w_tfm, [('t1_aparc', 'input_image')]), (gen_ref, aseg_t1w_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_t1w_tfm, [('out_file', 'reference_image')]), (aseg_t1w_tfm, outputnode, [('output_image', 'bold_aseg_t1')]), (aparc_t1w_tfm, outputnode, [('output_image', 'bold_aparc_t1')]), ]) # Merge transforms placing the head motion correction last nforms = 2 + int(use_fieldwarp) merge_xforms = pe.Node(niu.Merge(nforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([(inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nforms)])]) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in2')])]) bold_to_t1w_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_t1w_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb) workflow.connect([ (bbr_wf, merge_xforms, [('outputnode.itk_bold_to_t1', 'in1')]), (merge_xforms, bold_to_t1w_transform, [('out', 'transforms')]), (inputnode, merge, [('name_source', 'header_source')]), (merge, outputnode, [('out_file', 'bold_t1')]), (inputnode, bold_to_t1w_transform, [('bold_split', 'input_image')]), (gen_ref, bold_to_t1w_transform, [('out_file', 'reference_image')]), (bold_to_t1w_transform, merge, [('out_files', 'in_files')]), ]) if write_report: ds_report_reg = pe.Node(DerivativesDataSink(), name='ds_report_reg', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) def _bold_reg_suffix(fallback, freesurfer): if fallback: return 'coreg' if freesurfer else 'flirt' return 'bbr' if freesurfer else 'flt_bbr' workflow.connect([ (bbr_wf, ds_report_reg, [('outputnode.out_report', 'in_file'), (('outputnode.fallback', _bold_reg_suffix, freesurfer), 'suffix')]), ]) return workflow
def init_bold_t1_trans_wf(freesurfer, mem_gb, omp_nthreads, multiecho=False, use_fieldwarp=False, use_compression=True, name='bold_t1_trans_wf'): """ This workflow registers the reference BOLD image to T1-space, using a boundary-based registration (BBR) cost function. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_bold_t1_trans_wf wf = init_bold_t1_trans_wf(freesurfer=True, mem_gb=3, omp_nthreads=1) **Parameters** freesurfer : bool Enable FreeSurfer functional registration (bbregister) use_fieldwarp : bool Include SDC warp in single-shot transform from BOLD to T1 multiecho : bool If multiecho data was supplied, HMC already performed mem_gb : float Size of BOLD file in GB omp_nthreads : int Maximum number of threads an individual process may use use_compression : bool Save registered BOLD series as ``.nii.gz`` name : str Name of workflow (default: ``bold_reg_wf``) **Inputs** name_source BOLD series NIfTI file Used to recover original information lost during processing ref_bold_brain Reference image to which BOLD series is aligned If ``fieldwarp == True``, ``ref_bold_brain`` should be unwarped ref_bold_mask Skull-stripping mask of reference image t1_brain Skull-stripped bias-corrected structural template image t1_mask Mask of the skull-stripped template image t1_aseg FreeSurfer's ``aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). t1_aparc FreeSurfer's ``aparc+aseg.mgz`` atlas projected into the T1w reference (only if ``recon-all`` was run). bold_split Individual 3D BOLD volumes, not motion corrected hmc_xforms List of affine transforms aligning each volume to ``ref_image`` in ITK format itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) fieldwarp a :abbr:`DFM (displacements field map)` in ITK format **Outputs** bold_t1 Motion-corrected BOLD series in T1 space bold_t1_ref Reference, contrast-enhanced summary of the motion-corrected BOLD series in T1w space bold_mask_t1 BOLD mask in T1 space bold_aseg_t1 FreeSurfer's ``aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). bold_aparc_t1 FreeSurfer's ``aparc+aseg.mgz`` atlas, in T1w-space at the BOLD resolution (only if ``recon-all`` was run). **Subworkflows** * :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf` * :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf` """ from .util import init_bold_reference_wf workflow = Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'name_source', 'ref_bold_brain', 'ref_bold_mask', 't1_brain', 't1_mask', 't1_aseg', 't1_aparc', 'bold_split', 'fieldwarp', 'hmc_xforms', 'itk_bold_to_t1' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_t1', 'bold_t1_ref', 'bold_mask_t1', 'bold_aseg_t1', 'bold_aparc_t1' ]), name='outputnode') gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.3) # 256x256x256 * 64 / 8 ~ 150MB mask_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', float=True), name='mask_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, gen_ref, [('ref_bold_brain', 'moving_image'), ('t1_brain', 'fixed_image'), ('t1_mask', 'fov_mask')]), (inputnode, mask_t1w_tfm, [('ref_bold_mask', 'input_image')]), (gen_ref, mask_t1w_tfm, [('out_file', 'reference_image')]), (inputnode, mask_t1w_tfm, [('itk_bold_to_t1', 'transforms')]), (mask_t1w_tfm, outputnode, [('output_image', 'bold_mask_t1')]), ]) if freesurfer: # Resample aseg and aparc in T1w space (no transforms needed) aseg_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', transforms='identity', float=True), name='aseg_t1w_tfm', mem_gb=0.1) aparc_t1w_tfm = pe.Node(ApplyTransforms(interpolation='MultiLabel', transforms='identity', float=True), name='aparc_t1w_tfm', mem_gb=0.1) workflow.connect([ (inputnode, aseg_t1w_tfm, [('t1_aseg', 'input_image')]), (inputnode, aparc_t1w_tfm, [('t1_aparc', 'input_image')]), (gen_ref, aseg_t1w_tfm, [('out_file', 'reference_image')]), (gen_ref, aparc_t1w_tfm, [('out_file', 'reference_image')]), (aseg_t1w_tfm, outputnode, [('output_image', 'bold_aseg_t1')]), (aparc_t1w_tfm, outputnode, [('output_image', 'bold_aparc_t1')]), ]) bold_to_t1w_transform = pe.Node(MultiApplyTransforms( interpolation="LanczosWindowedSinc", float=True, copy_dtype=True), name='bold_to_t1w_transform', mem_gb=mem_gb * 3 * omp_nthreads, n_procs=omp_nthreads) # merge 3D volumes into 4D timeseries merge = pe.Node(Merge(compress=use_compression), name='merge', mem_gb=mem_gb) # Generate a reference on the target T1w space gen_final_ref = init_bold_reference_wf(omp_nthreads, pre_mask=True) if not multiecho: # Merge transforms placing the head motion correction last nforms = 2 + int(use_fieldwarp) merge_xforms = pe.Node(niu.Merge(nforms), name='merge_xforms', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) if use_fieldwarp: workflow.connect([(inputnode, merge_xforms, [('fieldwarp', 'in2')]) ]) workflow.connect([ # merge transforms (inputnode, merge_xforms, [('hmc_xforms', 'in%d' % nforms), ('itk_bold_to_t1', 'in1')]), (merge_xforms, bold_to_t1w_transform, [('out', 'transforms')]), (inputnode, bold_to_t1w_transform, [('bold_split', 'input_image') ]), ]) else: from nipype.interfaces.fsl import Split as FSLSplit bold_split = pe.Node(FSLSplit(dimension='t'), name='bold_split', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, bold_split, [('bold_split', 'in_file')]), (bold_split, bold_to_t1w_transform, [('out_files', 'input_image') ]), (inputnode, bold_to_t1w_transform, [('itk_bold_to_t1', 'transforms')]), ]) workflow.connect([ (inputnode, merge, [('name_source', 'header_source')]), (gen_ref, bold_to_t1w_transform, [('out_file', 'reference_image')]), (bold_to_t1w_transform, merge, [('out_files', 'in_files')]), (merge, gen_final_ref, [('out_file', 'inputnode.bold_file')]), (mask_t1w_tfm, gen_final_ref, [('output_image', 'inputnode.bold_mask') ]), (merge, outputnode, [('out_file', 'bold_t1')]), (gen_final_ref, outputnode, [('outputnode.ref_image', 'bold_t1_ref')]), ]) return workflow
def init_anat_derivatives_wf( *, bids_root, freesurfer, num_t1w, output_dir, spaces, name='anat_derivatives_wf', tpm_labels=("GM", "WM", "CSF"), ): """ Set up a battery of datasinks to store derivatives in the right location. Parameters ---------- bids_root : :obj:`str` Root path of BIDS dataset freesurfer : :obj:`bool` FreeSurfer was enabled num_t1w : :obj:`int` Number of T1w images output_dir : :obj:`str` Directory in which to save derivatives name : :obj:`str` Workflow name (default: anat_derivatives_wf) tpm_labels : :obj:`tuple` Tissue probability maps in order Inputs ------ template Template space and specifications source_files List of input T1w images t1w_ref_xfms List of affine transforms to realign input T1w images t1w_preproc The T1w reference map, which is calculated as the average of bias-corrected and preprocessed T1w images, defining the anatomical space. t1w_mask Mask of the ``t1w_preproc`` t1w_dseg Segmentation in T1w space t1w_tpms Tissue probability maps in T1w space anat2std_xfm Nonlinear spatial transform to resample imaging data given in anatomical space into standard space. std2anat_xfm Inverse transform of ``anat2std_xfm`` std_t1w T1w reference resampled in one or more standard spaces. std_mask Mask of skull-stripped template, in standard space std_dseg Segmentation, resampled into standard space std_tpms Tissue probability maps in standard space t1w2fsnative_xfm LTA-style affine matrix translating from T1w to FreeSurfer-conformed subject space fsnative2t1w_xfm LTA-style affine matrix translating from FreeSurfer-conformed subject space to T1w surfaces GIFTI surfaces (gray/white boundary, midthickness, pial, inflated) t1w_fs_aseg FreeSurfer's aseg segmentation, in native T1w space t1w_fs_aparc FreeSurfer's aparc+aseg segmentation, in native T1w space """ from niworkflows.interfaces.utility import KeySelect from smriprep.interfaces import DerivativesDataSink from smriprep.workflows.outputs import ( _bids_relative, _combine_cohort, _is_native, _drop_path ) workflow = Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface( fields=['template', 'source_files', 't1w_ref_xfms', 't1w_preproc', 't1w_mask', 't1w_dseg', 't1w_tpms', 'anat2std_xfm', 'std2anat_xfm', 't1w2fsnative_xfm', 'fsnative2t1w_xfm', 'surfaces', 't1w_fs_aseg', 't1w_fs_aparc']), name='inputnode') raw_sources = pe.Node(niu.Function(function=_bids_relative), name='raw_sources') raw_sources.inputs.bids_root = bids_root ds_t1w_preproc = pe.Node( DerivativesDataSink(base_directory=output_dir, desc='preproc', compress=True), name='ds_t1w_preproc', run_without_submitting=True) ds_t1w_preproc.inputs.SkullStripped = False ds_t1w_mask = pe.Node( DerivativesDataSink(base_directory=output_dir, desc='brain', suffix='mask', compress=True), name='ds_t1w_mask', run_without_submitting=True) ds_t1w_mask.inputs.Type = 'Brain' ds_t1w_dseg = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='dseg', compress=True), name='ds_t1w_dseg', run_without_submitting=True) ds_t1w_tpms = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='probseg', compress=True), name='ds_t1w_tpms', run_without_submitting=True) ds_t1w_tpms.inputs.label = tpm_labels workflow.connect([ (inputnode, raw_sources, [('source_files', 'in_files')]), (inputnode, ds_t1w_preproc, [('t1w_preproc', 'in_file'), ('source_files', 'source_file')]), (inputnode, ds_t1w_mask, [('t1w_mask', 'in_file'), ('source_files', 'source_file')]), (inputnode, ds_t1w_tpms, [('t1w_tpms', 'in_file'), ('source_files', 'source_file')]), (inputnode, ds_t1w_dseg, [('t1w_dseg', 'in_file'), ('source_files', 'source_file')]), (raw_sources, ds_t1w_mask, [('out', 'RawSources')]), ]) # Transforms if spaces.get_spaces(nonstandard=False, dim=(3,)): ds_std2t1w_xfm = pe.MapNode( DerivativesDataSink(base_directory=output_dir, to='T1w', mode='image', suffix='xfm'), iterfield=('in_file', 'from'), name='ds_std2t1w_xfm', run_without_submitting=True) ds_t1w2std_xfm = pe.MapNode( DerivativesDataSink(base_directory=output_dir, mode='image', suffix='xfm', **{'from': 'T1w'}), iterfield=('in_file', 'to'), name='ds_t1w2std_xfm', run_without_submitting=True) workflow.connect([ (inputnode, ds_t1w2std_xfm, [ ('anat2std_xfm', 'in_file'), (('template', _combine_cohort), 'to'), ('source_files', 'source_file')]), (inputnode, ds_std2t1w_xfm, [ ('std2anat_xfm', 'in_file'), (('template', _combine_cohort), 'from'), ('source_files', 'source_file')]), ]) if num_t1w > 1: # Please note the dictionary unpacking to provide the from argument. # It is necessary because from is a protected keyword (not allowed as argument name). ds_t1w_ref_xfms = pe.MapNode( DerivativesDataSink(base_directory=output_dir, to='T1w', mode='image', suffix='xfm', extension='txt', **{'from': 'orig'}), iterfield=['source_file', 'in_file'], name='ds_t1w_ref_xfms', run_without_submitting=True) workflow.connect([ (inputnode, ds_t1w_ref_xfms, [('source_files', 'source_file'), ('t1w_ref_xfms', 'in_file')]), ]) # Write derivatives in standard spaces specified by --output-spaces if getattr(spaces, '_cached') is not None and spaces.cached.references: from niworkflows.interfaces.space import SpaceDataSource from niworkflows.interfaces.utils import GenerateSamplingReference from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms spacesource = pe.Node(SpaceDataSource(), name='spacesource', run_without_submitting=True) spacesource.iterables = ('in_tuple', [ (s.fullname, s.spec) for s in spaces.cached.get_standard(dim=(3,)) ]) gen_tplid = pe.Node(niu.Function(function=_fmt_cohort), name="gen_tplid", run_without_submitting=True) select_xfm = pe.Node(KeySelect( fields=['anat2std_xfm']), name='select_xfm', run_without_submitting=True) select_tpl = pe.Node(TemplateFlowSelect(), name='select_tpl', run_without_submitting=True) gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref', mem_gb=0.01) # Resample T1w-space inputs anat2std_t1w = pe.Node(ApplyTransforms( dimension=3, default_value=0, float=True, interpolation='LanczosWindowedSinc'), name='anat2std_t1w') anat2std_mask = pe.Node(ApplyTransforms( interpolation='MultiLabel'), name='anat2std_mask' ) anat2std_dseg = pe.Node(ApplyTransforms( interpolation='MultiLabel'), name='anat2std_dseg' ) anat2std_tpms = pe.MapNode(ApplyTransforms( dimension=3, default_value=0, float=True, interpolation='Gaussian'), iterfield=['input_image'], name='anat2std_tpms' ) ds_std_t1w = pe.Node( DerivativesDataSink(base_directory=output_dir, desc='preproc', keep_dtype=True, compress=True), name='ds_std_t1w', run_without_submitting=True) ds_std_t1w.inputs.SkullStripped = True ds_std_mask = pe.Node( DerivativesDataSink(base_directory=output_dir, desc='brain', suffix='mask', compress=True), name='ds_std_mask', run_without_submitting=True) ds_std_mask.inputs.Type = 'Brain' ds_std_dseg = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='dseg', compress=True), name='ds_std_dseg', run_without_submitting=True) ds_std_tpms = pe.Node( DerivativesDataSink(base_directory=output_dir, suffix='probseg', compress=True), name='ds_std_tpms', run_without_submitting=True) # CRITICAL: the sequence of labels here (CSF-GM-WM) is that of the output of FSL-FAST # (intensity mean, per tissue). This order HAS to be matched also by the ``tpms`` # output in the data/io_spec.json file. ds_std_tpms.inputs.label = tpm_labels workflow.connect([ (inputnode, anat2std_t1w, [('t1w_preproc', 'input_image')]), (inputnode, anat2std_mask, [('t1w_mask', 'input_image')]), (inputnode, anat2std_dseg, [('t1w_dseg', 'input_image')]), (inputnode, anat2std_tpms, [('t1w_tpms', 'input_image')]), (inputnode, gen_ref, [('t1w_preproc', 'moving_image')]), (inputnode, select_xfm, [ ('anat2std_xfm', 'anat2std_xfm'), ('template', 'keys')]), (spacesource, gen_tplid, [('space', 'template'), ('cohort', 'cohort')]), (gen_tplid, select_xfm, [('out', 'key')]), (spacesource, select_tpl, [('space', 'template'), ('cohort', 'cohort'), (('resolution', _no_native), 'resolution')]), (spacesource, gen_ref, [(('resolution', _is_native), 'keep_native')]), (select_tpl, gen_ref, [('t2w_file', 'fixed_image')]), (anat2std_t1w, ds_std_t1w, [('output_image', 'in_file')]), (anat2std_mask, ds_std_mask, [('output_image', 'in_file')]), (anat2std_dseg, ds_std_dseg, [('output_image', 'in_file')]), (anat2std_tpms, ds_std_tpms, [('output_image', 'in_file')]), (select_tpl, ds_std_mask, [(('brain_mask', _drop_path), 'RawSources')]), ]) workflow.connect( # Connect apply transforms nodes [ (gen_ref, n, [('out_file', 'reference_image')]) for n in (anat2std_t1w, anat2std_mask, anat2std_dseg, anat2std_tpms) ] + [ (select_xfm, n, [('anat2std_xfm', 'transforms')]) for n in (anat2std_t1w, anat2std_mask, anat2std_dseg, anat2std_tpms) ] # Connect the source_file input of these datasinks + [ (inputnode, n, [('source_files', 'source_file')]) for n in (ds_std_t1w, ds_std_mask, ds_std_dseg, ds_std_tpms) ] # Connect the space input of these datasinks + [ (spacesource, n, [ ('space', 'space'), ('cohort', 'cohort'), ('resolution', 'resolution') ]) for n in (ds_std_t1w, ds_std_mask, ds_std_dseg, ds_std_tpms) ] ) return workflow
def make_registration_wf(input_file, name, subject=subject, target=target, target_mask=target_mask, init_reg=init_reg, t1w_to_mni_transform=t1w_to_mni_transform, t1w_in_mni=t1w_in_mni, mni_brain_mask=mni_brain_mask, ants_numthreads=8): workflow = pe.Workflow(base_dir='/tmp/workflow_folders', name=name) input_node = pe.Node(niu.IdentityInterface(fields=[ 'input_file', 'target', 'target_mask', 't1w_to_mni_transform', 't1w_in_mni', 'mni_brain_mask' ]), name='inputspec') input_node.inputs.input_file = input_file input_node.inputs.target = target input_node.inputs.target_mask = target_mask input_node.inputs.init_reg = init_reg input_node.inputs.t1w_to_mni_transform = t1w_to_mni_transform input_node.inputs.t1w_in_mni = t1w_in_mni input_node.inputs.mni_brain_mask = mni_brain_mask convert_dtype = pe.Node(fsl.maths.MathsCommand(), name='convert_dtype') convert_dtype.inputs.output_datatype = 'double' workflow.connect(input_node, 'input_file', convert_dtype, 'in_file') inu_n4 = pe.Node( N4BiasFieldCorrection( dimension=3, save_bias=True, num_threads=ants_numthreads, rescale_intensities=True, copy_header=True, ), n_procs=ants_numthreads, name="inu_n4", ) workflow.connect(convert_dtype, 'out_file', inu_n4, 'input_image') register = pe.Node(Registration(from_file=registration_scheme, num_threads=ants_numthreads, verbose=True), name='registration') workflow.connect(inu_n4, 'output_image', register, 'moving_image') if init_reg: workflow.connect(input_node, 'init_reg', register, 'initial_moving_transform') workflow.connect(input_node, 'target', register, 'fixed_image') workflow.connect(input_node, 'target_mask', register, 'fixed_image_masks') def get_mask(input_image): from nilearn import image from nipype.utils.filemanip import split_filename import os.path as op _, fn, _ = split_filename(input_image) mask = image.math_img('im != 0', im=input_image) new_fn = op.abspath(fn + '_mask.nii.gz') mask.to_filename(new_fn) return new_fn mask_node = pe.Node(niu.Function(function=get_mask, input_names=['input_image'], output_names=['mask']), name='mask_node') workflow.connect(register, 'warped_image', mask_node, 'input_image') gen_grid_node = pe.Node(GenerateSamplingReference(), name='gen_grid_node') workflow.connect(mask_node, 'mask', gen_grid_node, 'fov_mask') workflow.connect(inu_n4, 'output_image', gen_grid_node, 'moving_image') workflow.connect(input_node, 'target', gen_grid_node, 'fixed_image') datasink_image_t1w = pe.Node(DerivativesDataSink( out_path_base='registration', compress=True, base_directory=op.join(bids_folder, 'derivatives')), name='datasink_image_t1w') workflow.connect(input_node, 'input_file', datasink_image_t1w, 'source_file') datasink_image_t1w.inputs.space = 'T1w' datasink_image_t1w.inputs.desc = 'registered' datasink_report_t1w = pe.Node(DerivativesDataSink( out_path_base='registration', space='T1w', base_directory=op.join(bids_folder, 'derivatives'), datatype='figures'), name='datasink_report_t1w') workflow.connect(input_node, 'input_file', datasink_report_t1w, 'source_file') datasink_report_t1w.inputs.space = 'T1w' transformer = pe.Node(ApplyTransforms( interpolation='LanczosWindowedSinc', generate_report=True, num_threads=ants_numthreads), n_procs=ants_numthreads, name='transformer') workflow.connect(transformer, 'output_image', datasink_image_t1w, 'in_file') workflow.connect(transformer, 'out_report', datasink_report_t1w, 'in_file') workflow.connect(inu_n4, 'output_image', transformer, 'input_image') workflow.connect(gen_grid_node, 'out_file', transformer, 'reference_image') workflow.connect(register, 'composite_transform', transformer, 'transforms') concat_transforms = pe.Node(niu.Merge(2), name='concat_transforms') workflow.connect(register, 'composite_transform', concat_transforms, 'in2') workflow.connect(input_node, 't1w_to_mni_transform', concat_transforms, 'in1') transformer_to_mni1 = pe.Node(ApplyTransforms( interpolation='LanczosWindowedSinc', generate_report=False, num_threads=ants_numthreads), n_procs=ants_numthreads, name='transformer_to_mni1') workflow.connect(inu_n4, 'output_image', transformer_to_mni1, 'input_image') workflow.connect(input_node, 't1w_in_mni', transformer_to_mni1, 'reference_image') workflow.connect(concat_transforms, 'out', transformer_to_mni1, 'transforms') mask_node_mni = pe.Node(niu.Function(function=get_mask, input_names=['input_image'], output_names=['mask']), name='mask_node_mni') workflow.connect(transformer_to_mni1, 'output_image', mask_node_mni, 'input_image') def join_masks(mask1, mask2): from nilearn import image from nipype.utils.filemanip import split_filename import os.path as op _, fn, _ = split_filename(mask1) new_mask = image.math_img('(im1 > 0) & (im2 > 0)', im1=mask1, im2=mask2) new_fn = op.abspath(fn + '_jointmask' + '.nii.gz') new_mask.to_filename(new_fn) return new_fn combine_masks_node = pe.Node(niu.Function( function=join_masks, input_names=['mask1', 'mask2'], output_names=['combined_mask']), name='combine_mask_node') workflow.connect(mask_node_mni, 'mask', combine_masks_node, 'mask1') workflow.connect(input_node, 'mni_brain_mask', combine_masks_node, 'mask2') gen_grid_node_mni = pe.Node(GenerateSamplingReference(), name='gen_grid_node_mni') workflow.connect(combine_masks_node, 'combined_mask', gen_grid_node_mni, 'fov_mask') workflow.connect(inu_n4, 'output_image', gen_grid_node_mni, 'moving_image') workflow.connect(input_node, 't1w_in_mni', gen_grid_node_mni, 'fixed_image') transformer_to_mni2 = pe.Node(ApplyTransforms( interpolation='LanczosWindowedSinc', generate_report=False, num_threads=ants_numthreads), n_procs=ants_numthreads, name='transformer_to_mni2') workflow.connect(inu_n4, 'output_image', transformer_to_mni2, 'input_image') workflow.connect(gen_grid_node_mni, 'out_file', transformer_to_mni2, 'reference_image') workflow.connect(concat_transforms, 'out', transformer_to_mni2, 'transforms') datasink_image_mni = pe.Node(DerivativesDataSink( out_path_base='registration', compress=True, base_directory=op.join(bids_folder, 'derivatives')), name='datasink_mni') datasink_image_mni.inputs.source_file = input_file datasink_image_mni.inputs.space = 'MNI152NLin2009cAsym' datasink_image_mni.inputs.desc = 'registered' workflow.connect(input_node, 'input_file', datasink_image_mni, 'source_file') workflow.connect(transformer_to_mni2, 'output_image', datasink_image_mni, 'in_file') return workflow