# Copyright (C) 2012-2023 C-PAC Developers
# This file is part of C-PAC.
# C-PAC is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
# C-PAC is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
# pylint: disable=too-many-lines,ungrouped-imports,wrong-import-order
from typing import Optional
from CPAC.pipeline import nipype_pipeline_engine as pe
from CPAC.pipeline.nodeblock import nodeblock
from nipype.interfaces import afni, ants, c3, fsl, utility as util
from nipype.interfaces.afni import utils as afni_utils
from CPAC.anat_preproc.lesion_preproc import create_lesion_preproc
from CPAC.func_preproc.utils import chunk_ts, split_ts_chunks
from CPAC.registration.utils import seperate_warps_list, \
check_transforms, \
generate_inverse_transform_flags, \
single_ants_xfm_to_list, \
interpolation_string, \
change_itk_transform_type, \
hardcoded_reg, \
one_d_to_mat, \
run_c3d, \
run_c4d
from CPAC.utils.interfaces.fsl import Merge as fslMerge
from CPAC.utils.typing import LIST_OR_STR, TUPLE
from CPAC.utils.utils import check_prov_for_motion_tool, check_prov_for_regtool
def apply_transform(wf_name, reg_tool, time_series=False, multi_input=False,
num_cpus=1, num_ants_cores=1):
if not reg_tool:
raise Exception("\n[!] Developer info: the 'reg_tool' parameter sent "
f"to the 'apply_transform' node for '{wf_name}' is "
f"empty.\n")
wf = pe.Workflow(name=wf_name)
inputNode = pe.Node(
util.IdentityInterface(fields=['input_image',
'reference',
'transform',
'interpolation']),
name='inputspec')
outputNode = pe.Node(
util.IdentityInterface(fields=['output_image']),
name='outputspec')
if int(num_cpus) > 1 and time_series:
# parallelize time series warp application
# we need the node to be a MapNode to feed in the list of functional
# time series chunks
multi_input = True
if reg_tool == 'ants':
if multi_input:
apply_warp = pe.MapNode(interface=ants.ApplyTransforms(),
name=f'apply_warp_{wf_name}',
iterfield=['input_image'],
mem_gb=0.7,
mem_x=(1708448960473801 /
151115727451828646838272,
'input_image'))
else:
apply_warp = pe.Node(interface=ants.ApplyTransforms(),
name=f'apply_warp_{wf_name}',
mem_gb=0.7,
mem_x=(1708448960473801 /
151115727451828646838272,
'input_image'))
apply_warp.inputs.dimension = 3
apply_warp.interface.num_threads = int(num_ants_cores)
if time_series:
apply_warp.inputs.input_image_type = 3
wf.connect(inputNode, 'reference', apply_warp, 'reference_image')
interp_string = pe.Node(util.Function(input_names=['interpolation',
'reg_tool'],
output_names=['interpolation'],
function=interpolation_string),
name=f'interp_string',
mem_gb=2.5)
interp_string.inputs.reg_tool = reg_tool
wf.connect(inputNode, 'interpolation', interp_string, 'interpolation')
wf.connect(interp_string, 'interpolation',
apply_warp, 'interpolation')
ants_xfm_list = \
pe.Node(util.Function(input_names=['transform'],
output_names=['transform_list'],
function=single_ants_xfm_to_list),
name=f'single_ants_xfm_to_list',
mem_gb=2.5)
wf.connect(inputNode, 'transform', ants_xfm_list, 'transform')
wf.connect(ants_xfm_list, 'transform_list', apply_warp, 'transforms')
# parallelize the apply warp, if multiple CPUs, and it's a time
# series!
if int(num_cpus) > 1 and time_series:
chunk_imports = ['import nibabel as nb']
chunk = pe.Node(util.Function(input_names=['func_file',
'n_chunks',
'chunk_size'],
output_names=['TR_ranges'],
function=chunk_ts,
imports=chunk_imports),
name=f'chunk_{wf_name}',
mem_gb=2.5)
#chunk.inputs.n_chunks = int(num_cpus)
# 10-TR sized chunks
chunk.inputs.chunk_size = 10
wf.connect(inputNode, 'input_image', chunk, 'func_file')
split_imports = ['import os', 'import subprocess']
split = pe.Node(util.Function(input_names=['func_file',
'tr_ranges'],
output_names=['split_funcs'],
function=split_ts_chunks,
imports=split_imports),
name=f'split_{wf_name}',
mem_gb=2.5)
wf.connect(inputNode, 'input_image', split, 'func_file')
wf.connect(chunk, 'TR_ranges', split, 'tr_ranges')
wf.connect(split, 'split_funcs', apply_warp, 'input_image')
func_concat = pe.Node(interface=afni_utils.TCat(),
name=f'func_concat_{wf_name}',
mem_gb=2.5)
func_concat.inputs.outputtype = 'NIFTI_GZ'
wf.connect(apply_warp, 'output_image', func_concat, 'in_files')
wf.connect(func_concat, 'out_file', outputNode, 'output_image')
else:
wf.connect(inputNode, 'input_image', apply_warp, 'input_image')
wf.connect(apply_warp, 'output_image', outputNode, 'output_image')
elif reg_tool == 'fsl':
if multi_input:
apply_warp = pe.MapNode(interface=fsl.ApplyWarp(),
name=f'fsl_apply_warp',
iterfield=['in_file'],
mem_gb=2.5)
else:
apply_warp = pe.Node(interface=fsl.ApplyWarp(),
name='fsl_apply_warp',
mem_gb=2.5)
interp_string = pe.Node(util.Function(input_names=['interpolation',
'reg_tool'],
output_names=['interpolation'],
function=interpolation_string),
name=f'interp_string',
mem_gb=2.5)
interp_string.inputs.reg_tool = reg_tool
wf.connect(inputNode, 'interpolation', interp_string, 'interpolation')
wf.connect(interp_string, 'interpolation', apply_warp, 'interp')
# mni to t1
wf.connect(inputNode, 'reference', apply_warp, 'ref_file')
# NOTE: C-PAC now converts all FSL xfm's to .nii, so even if the
# inputNode 'transform' is a linear xfm, it's a .nii and must
# go in as a warpfield file
wf.connect(inputNode, 'transform', apply_warp, 'field_file')
# parallelize the apply warp, if multiple CPUs, and it's a time
# series!
if int(num_cpus) > 1 and time_series:
chunk_imports = ['import nibabel as nb']
chunk = pe.Node(util.Function(input_names=['func_file',
'n_chunks',
'chunk_size'],
output_names=['TR_ranges'],
function=chunk_ts,
imports=chunk_imports),
name=f'chunk_{wf_name}',
mem_gb=2.5)
#chunk.inputs.n_chunks = int(num_cpus)
# 10-TR sized chunks
chunk.inputs.chunk_size = 10
wf.connect(inputNode, 'input_image', chunk, 'func_file')
split_imports = ['import os', 'import subprocess']
split = pe.Node(util.Function(input_names=['func_file',
'tr_ranges'],
output_names=['split_funcs'],
function=split_ts_chunks,
imports=split_imports),
name=f'split_{wf_name}',
mem_gb=2.5)
wf.connect(inputNode, 'input_image', split, 'func_file')
wf.connect(chunk, 'TR_ranges', split, 'tr_ranges')
wf.connect(split, 'split_funcs', apply_warp, 'in_file')
func_concat = pe.Node(interface=afni_utils.TCat(),
name=f'func_concat{wf_name}')
func_concat.inputs.outputtype = 'NIFTI_GZ'
wf.connect(apply_warp, 'out_file', func_concat, 'in_files')
wf.connect(func_concat, 'out_file', outputNode, 'output_image')
else:
wf.connect(inputNode, 'input_image', apply_warp, 'in_file')
wf.connect(apply_warp, 'out_file', outputNode, 'output_image')
return wf
def transform_derivative(wf_name, label, reg_tool, num_cpus, num_ants_cores,
ants_interp=None, fsl_interp=None, opt=None):
'''Transform output derivatives to template space.
This function is designed for use with the NodeBlock connection engine.
'''
wf = pe.Workflow(name=wf_name)
inputnode = pe.Node(util.IdentityInterface(fields=['in_file',
'reference',
'transform']),
name='inputspec')
multi_input = False
if 'statmap' in label:
multi_input = True
stack = False
if 'correlations' in label:
stack = True
apply_xfm = apply_transform(f'warp_{label}_to_template', reg_tool,
time_series=stack,
multi_input=multi_input,
num_cpus=num_cpus,
num_ants_cores=num_ants_cores)
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = ants_interp
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = fsl_interp
wf.connect(inputnode, 'in_file', apply_xfm, 'inputspec.input_image')
wf.connect(inputnode, 'reference', apply_xfm, 'inputspec.reference')
wf.connect(inputnode, 'transform', apply_xfm, 'inputspec.transform')
outputnode = pe.Node(util.IdentityInterface(fields=['out_file']),
name='outputspec')
wf.connect(apply_xfm, 'outputspec.output_image', outputnode, 'out_file')
return wf
def convert_pedir(pedir, convert='xyz_to_int'):
'''FSL Flirt requires pedir input encoded as an int'''
if convert == 'xyz_to_int':
conv_dct = {'x': 1, 'y': 2, 'z': 3, 'x-': -1, 'y-': -2, 'z-': -3,
'i': 1, 'j': 2, 'k': 3, 'i-': -1, 'j-': -2, 'k-': -3,
'-x': -1, '-i': -1, '-y': -2,
'-j': -2, '-z': -3, '-k': -3}
elif convert == 'ijk_to_xyz':
conv_dct = {'i': 'x', 'j': 'y', 'k': 'z',
'i-': 'x-', 'j-': 'y-', 'k-': 'z-'}
if isinstance(pedir, bytes):
pedir = pedir.decode()
if not isinstance(pedir, str):
raise Exception("\n\nPhase-encoding direction must be a "
"string value.\n\nValue: {0}"
"\n\n".format(pedir))
if pedir not in conv_dct.keys():
raise Exception("\n\nInvalid phase-encoding direction "
"entered: {0}\n\n".format(pedir))
pedir = conv_dct[pedir]
return pedir
def create_fsl_flirt_linear_reg(name='fsl_flirt_linear_reg'):
linear_register = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(fields=['input_brain',
'reference_brain',
'interp',
'ref_mask']),
name='inputspec')
outputspec = pe.Node(util.IdentityInterface(fields=['output_brain',
'linear_xfm',
'invlinear_xfm']),
name='outputspec')
linear_reg = pe.Node(interface=fsl.FLIRT(), name='linear_reg_0')
linear_reg.inputs.cost = 'corratio'
inv_flirt_xfm = pe.Node(interface=fsl.utils.ConvertXFM(),
name='inv_linear_reg0_xfm')
inv_flirt_xfm.inputs.invert_xfm = True
linear_register.connect(inputspec, 'input_brain',
linear_reg, 'in_file')
linear_register.connect(inputspec, 'reference_brain',
linear_reg, 'reference')
linear_register.connect(inputspec, 'interp',
linear_reg, 'interp')
linear_register.connect(linear_reg, 'out_file',
outputspec, 'output_brain')
linear_register.connect(linear_reg, 'out_matrix_file',
inv_flirt_xfm, 'in_file')
linear_register.connect(inv_flirt_xfm, 'out_file',
outputspec, 'invlinear_xfm')
linear_register.connect(linear_reg, 'out_matrix_file',
outputspec, 'linear_xfm')
return linear_register
[docs]def create_fsl_fnirt_nonlinear_reg(name='fsl_fnirt_nonlinear_reg'):
"""
Performs non-linear registration of an input file to a reference file
using FSL FNIRT.
Parameters
----------
name : string, optional
Name of the workflow.
Returns
-------
nonlinear_register : nipype.pipeline.engine.Workflow
Notes
-----
Workflow Inputs::
inputspec.input_skull : string (nifti file)
File of input brain with skull
inputspec.reference_skull : string (nifti file)
Target brain with skull to normalize to
inputspec.fnirt_config : string (fsl fnirt config file)
Configuration file containing parameters that can be
specified in fnirt
Workflow Outputs::
outputspec.output_brain : string (nifti file)
Normalizion of input brain file
outputspec.nonlinear_xfm : string
Nonlinear field coefficients file of nonlinear transformation
Registration Procedure:
1. Perform a nonlinear registration on an input file to the
reference file utilizing affine transformation from the previous
step as a starting point.
2. Invert the affine transformation to provide the user a
transformation (affine only) from the space of the reference
file to the input file.
Workflow Graph:
.. image:: ../images/nonlinear_register.dot.png
:width: 500
Detailed Workflow Graph:
.. image:: ../images/nonlinear_register_detailed.dot.png
:width: 500
"""
nonlinear_register = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(fields=['input_brain',
'input_skull',
'reference_brain',
'reference_skull',
'interp',
'ref_mask',
'linear_aff',
'fnirt_config']),
name='inputspec')
outputspec = pe.Node(util.IdentityInterface(fields=['output_brain',
'nonlinear_xfm']),
name='outputspec')
nonlinear_reg = pe.Node(interface=fsl.FNIRT(),
name='nonlinear_reg_1')
nonlinear_reg.inputs.fieldcoeff_file = True
nonlinear_reg.inputs.jacobian_file = True
brain_warp = pe.Node(interface=fsl.ApplyWarp(),
name='brain_warp')
nonlinear_register.connect(inputspec, 'input_skull',
nonlinear_reg, 'in_file')
nonlinear_register.connect(inputspec, 'reference_skull',
nonlinear_reg, 'ref_file')
nonlinear_register.connect(inputspec, 'interp',
brain_warp, 'interp')
nonlinear_register.connect(inputspec, 'ref_mask',
nonlinear_reg, 'refmask_file')
# FNIRT parameters are specified by FSL config file
# ${FSLDIR}/etc/flirtsch/TI_2_MNI152_2mm.cnf (or user-specified)
nonlinear_register.connect(inputspec, 'fnirt_config',
nonlinear_reg, 'config_file')
nonlinear_register.connect(inputspec, 'linear_aff',
nonlinear_reg, 'affine_file')
nonlinear_register.connect(nonlinear_reg, 'fieldcoeff_file',
outputspec, 'nonlinear_xfm')
nonlinear_register.connect(inputspec, 'input_brain',
brain_warp, 'in_file')
nonlinear_register.connect(nonlinear_reg, 'fieldcoeff_file',
brain_warp, 'field_file')
nonlinear_register.connect(inputspec, 'reference_brain',
brain_warp, 'ref_file')
nonlinear_register.connect(brain_warp, 'out_file',
outputspec, 'output_brain')
return nonlinear_register
[docs]def create_fsl_fnirt_nonlinear_reg_nhp(name='fsl_fnirt_nonlinear_reg_nhp'):
"""
Performs non-linear registration of an input file to a reference file
using FSL FNIRT.
Parameters
----------
name : string, optional
Name of the workflow.
Returns
-------
nonlinear_register : nipype.pipeline.engine.Workflow
Notes
-----
Workflow Inputs::
inputspec.input_skull : string (nifti file)
File of input brain with skull
inputspec.reference_skull : string (nifti file)
Target brain with skull to normalize to
inputspec.fnirt_config : string (fsl fnirt config file)
Configuration file containing parameters that can be
specified in fnirt
Workflow Outputs::
outputspec.output_brain : string (nifti file)
Normalizion of input brain file
outputspec.nonlinear_xfm : string
Nonlinear field coefficients file of nonlinear transformation
outputspec.nonlinear_warp : string
Nonlinear output file with warp field
Registration Procedure:
1. Perform a nonlinear registration on an input file to the
reference file utilizing affine transformation from the previous
step as a starting point.
2. Invert the affine transformation to provide the user a
transformation (affine only) from the space of the reference
file to the input file.
Workflow Graph:
.. image:: ../images/nonlinear_register.dot.png
:width: 500
Detailed Workflow Graph:
.. image:: ../images/nonlinear_register_detailed.dot.png
:width: 500
"""
nonlinear_register = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(fields=['input_brain',
'input_skull',
'reference_brain',
'reference_skull',
'interp',
'ref_mask',
'linear_aff',
'fnirt_config']),
name='inputspec')
outputspec = pe.Node(util.IdentityInterface(fields=['output_brain',
'output_head',
'output_mask',
'output_biasfield',
'nonlinear_xfm',
'nonlinear_warp']),
name='outputspec')
nonlinear_reg = pe.Node(interface=fsl.FNIRT(),
name='nonlinear_reg_1')
nonlinear_reg.inputs.fieldcoeff_file = True
nonlinear_reg.inputs.jacobian_file = True
nonlinear_reg.inputs.field_file = True
nonlinear_register.connect(inputspec, 'input_skull',
nonlinear_reg, 'in_file')
nonlinear_register.connect(inputspec, 'reference_skull',
nonlinear_reg, 'ref_file')
nonlinear_register.connect(inputspec, 'ref_mask',
nonlinear_reg, 'refmask_file')
nonlinear_register.connect(inputspec, 'fnirt_config',
nonlinear_reg, 'config_file')
nonlinear_register.connect(inputspec, 'linear_aff',
nonlinear_reg, 'affine_file')
brain_warp = pe.Node(interface=fsl.ApplyWarp(),
name='brain_warp')
brain_warp.inputs.interp = 'nn'
brain_warp.inputs.relwarp = True
nonlinear_register.connect(inputspec, 'input_brain',
brain_warp, 'in_file')
nonlinear_register.connect(nonlinear_reg, 'field_file',
brain_warp, 'field_file')
nonlinear_register.connect(inputspec, 'reference_skull',
brain_warp, 'ref_file')
head_warp = pe.Node(interface=fsl.ApplyWarp(),
name='head_warp')
head_warp.inputs.interp = 'spline'
head_warp.inputs.relwarp = True
nonlinear_register.connect(inputspec, 'input_brain',
head_warp, 'in_file')
nonlinear_register.connect(nonlinear_reg, 'field_file',
head_warp, 'field_file')
nonlinear_register.connect(inputspec, 'reference_skull',
head_warp, 'ref_file')
mask_warp = pe.Node(interface=fsl.ApplyWarp(),
name='mask_warp')
mask_warp.inputs.interp = 'nn'
mask_warp.inputs.relwarp = True
nonlinear_register.connect(inputspec, 'input_brain',
mask_warp, 'in_file')
nonlinear_register.connect(nonlinear_reg, 'field_file',
mask_warp, 'field_file')
nonlinear_register.connect(inputspec, 'reference_skull',
mask_warp, 'ref_file')
biasfield_warp = pe.Node(interface=fsl.ApplyWarp(),
name='biasfield_warp')
biasfield_warp.inputs.interp = 'spline'
biasfield_warp.inputs.relwarp = True
nonlinear_register.connect(inputspec, 'input_brain',
biasfield_warp, 'in_file')
nonlinear_register.connect(nonlinear_reg, 'field_file',
biasfield_warp, 'field_file')
nonlinear_register.connect(inputspec, 'reference_skull',
biasfield_warp, 'ref_file')
nonlinear_register.connect(nonlinear_reg, 'fieldcoeff_file',
outputspec, 'nonlinear_xfm')
nonlinear_register.connect(nonlinear_reg, 'field_file',
outputspec, 'nonlinear_warp')
nonlinear_register.connect(brain_warp, 'out_file',
outputspec, 'output_brain')
nonlinear_register.connect(head_warp, 'out_file',
outputspec, 'output_head')
nonlinear_register.connect(mask_warp, 'out_file',
outputspec, 'output_mask')
nonlinear_register.connect(biasfield_warp, 'out_file',
outputspec, 'output_biasfield')
return nonlinear_register
[docs]def create_register_func_to_anat(config, phase_diff_distcor=False,
name='register_func_to_anat'):
"""
Registers a functional scan in native space to anatomical space using a
linear transform and does not include bbregister.
Parameters
----------
config : configuration, mandatory
Pipeline configuration.
fieldmap_distortion : bool, optional
If field map-based distortion correction is being run, FLIRT should
take in the appropriate field map-related inputs.
name : string, optional
Name of the workflow.
Returns
-------
create_register_func_to_anat : nipype.pipeline.engine.Workflow
Notes
-----
Workflow Inputs::
inputspec.func : string (nifti file)
Input functional scan to be registered to anatomical space
inputspec.anat : string (nifti file)
Corresponding anatomical scan of subject
inputspec.interp : string
Type of interpolation to use
('trilinear' or 'nearestneighbour' or 'sinc')
Workflow Outputs::
outputspec.func_to_anat_linear_xfm_nobbreg : string (mat file)
Affine transformation from functional to anatomical native space
outputspec.anat_func_nobbreg : string (nifti file)
Functional scan registered to anatomical space
"""
register_func_to_anat = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(fields=['func',
'anat',
'dof',
'interp',
'fieldmap',
'fieldmapmask']),
name='inputspec')
inputNode_echospacing = pe.Node(
util.IdentityInterface(fields=['echospacing']),
name='echospacing_input')
inputNode_pedir = pe.Node(util.IdentityInterface(fields=['pedir']),
name='pedir_input')
outputspec = pe.Node(util.IdentityInterface(
fields=['func_to_anat_linear_xfm_nobbreg', 'anat_func_nobbreg']),
name='outputspec')
linear_reg = pe.Node(interface=fsl.FLIRT(),
name='linear_func_to_anat')
linear_reg.inputs.interp = config.registration_workflows['functional_registration']['coregistration']['interpolation']
linear_reg.inputs.cost = config.registration_workflows['functional_registration']['coregistration']['cost']
linear_reg.inputs.dof = config.registration_workflows['functional_registration']['coregistration']['dof']
if config.registration_workflows['functional_registration']['coregistration']['arguments'] is not None:
linear_reg.inputs.args = config.registration_workflows['functional_registration']['coregistration']['arguments']
if phase_diff_distcor:
conv_pedir = \
pe.Node(interface=util.Function(input_names=['pedir',
'convert'],
output_names=['pedir'],
function=convert_pedir),
name='coreg_convert_pedir')
conv_pedir.inputs.convert = 'xyz_to_int'
register_func_to_anat.connect(inputNode_pedir, 'pedir',
conv_pedir, 'pedir')
register_func_to_anat.connect(conv_pedir, 'pedir',
linear_reg, 'pedir')
register_func_to_anat.connect(inputspec, 'fieldmap',
linear_reg, 'fieldmap')
register_func_to_anat.connect(inputspec, 'fieldmapmask',
linear_reg, 'fieldmapmask')
register_func_to_anat.connect(inputNode_echospacing, 'echospacing',
linear_reg, 'echospacing')
register_func_to_anat.connect(inputspec, 'func', linear_reg, 'in_file')
register_func_to_anat.connect(inputspec, 'anat', linear_reg, 'reference')
register_func_to_anat.connect(inputspec, 'dof', linear_reg, 'dof')
register_func_to_anat.connect(inputspec, 'interp', linear_reg, 'interp')
register_func_to_anat.connect(linear_reg, 'out_matrix_file',
outputspec,
'func_to_anat_linear_xfm_nobbreg')
register_func_to_anat.connect(linear_reg, 'out_file',
outputspec, 'anat_func_nobbreg')
return register_func_to_anat
[docs]def create_register_func_to_anat_use_T2(config, name='register_func_to_anat_use_T2'):
# for monkey data
# ref: https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/GenericfMRIVolumeProcessingPipeline.sh#L287-L295
# https://github.com/HechengJin0/dcan-macaque-pipeline/blob/master/fMRIVolume/GenericfMRIVolumeProcessingPipeline.sh#L524-L535
"""
Registers a functional scan in native space to anatomical space using a
linear transform and does not include bbregister, use T1 and T2 image.
Parameters
----------
config : configuration, mandatory
Pipeline configuration.
name : string, optional
Name of the workflow.
Returns
-------
create_register_func_to_anat_use_T2 : nipype.pipeline.engine.Workflow
Notes
-----
Workflow Inputs::
inputspec.func : string (nifti file)
Input functional scan to be registered to anatomical space
inputspec.anat : string (nifti file)
Corresponding anatomical scan of subject
Workflow Outputs::
outputspec.func_to_anat_linear_xfm_nobbreg : string (mat file)
Affine transformation from functional to anatomical native space
outputspec.anat_func_nobbreg : string (nifti file)
Functional scan registered to anatomical space
"""
register_func_to_anat_use_T2 = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(fields=['func',
'T1_brain',
'T2_head',
'T2_brain']),
name='inputspec')
outputspec = pe.Node(util.IdentityInterface(fields=['func_to_anat_linear_xfm_nobbreg',
'func_to_anat_linear_warp_nobbreg',
'anat_func_nobbreg']),
name='outputspec')
# ${FSLDIR}/bin/flirt -interp spline -dof 6 -in ${fMRIFolder}/${ScoutName}_gdc -ref ${T1wFolder}/${T2wRestoreImage} -omat "$fMRIFolder"/Scout2T2w.mat -out ${fMRIFolder}/Scout2T2w.nii.gz -searchrx -30 30 -searchry -30 30 -searchrz -30 30 -cost mutualinfo
linear_reg_func_to_t2 = pe.Node(interface=fsl.FLIRT(),
name='linear_reg_func_to_t2')
linear_reg_func_to_t2.inputs.interp = 'spline'
linear_reg_func_to_t2.inputs.cost = 'mutualinfo'
linear_reg_func_to_t2.inputs.dof = 6
linear_reg_func_to_t2.inputs.searchr_x = [30, 30]
linear_reg_func_to_t2.inputs.searchr_y = [30, 30]
linear_reg_func_to_t2.inputs.searchr_z = [30, 30]
register_func_to_anat_use_T2.connect(inputspec, 'func', linear_reg_func_to_t2, 'in_file')
register_func_to_anat_use_T2.connect(inputspec, 'T2_head', linear_reg_func_to_t2, 'reference')
# ${FSLDIR}/bin/convert_xfm -omat "$fMRIFolder"/T2w2Scout.mat -inverse "$fMRIFolder"/Scout2T2w.mat
invt = pe.Node(interface=fsl.ConvertXFM(), name='convert_xfm')
invt.inputs.invert_xfm = True
register_func_to_anat_use_T2.connect(linear_reg_func_to_t2, 'out_matrix_file', invt, 'in_file')
# ${FSLDIR}/bin/applywarp --interp=nn -i ${T1wFolder}/${T2wRestoreImageBrain} -r ${fMRIFolder}/${ScoutName}_gdc --premat="$fMRIFolder"/T2w2Scout.mat -o ${fMRIFolder}/Scout_brain_mask.nii.gz
anat_to_func = pe.Node(interface=fsl.ApplyWarp(),
name='anat_to_func')
anat_to_func.inputs.interp = 'nn'
register_func_to_anat_use_T2.connect(inputspec, 'T2_brain', anat_to_func, 'in_file')
register_func_to_anat_use_T2.connect(inputspec, 'func', anat_to_func, 'ref_file')
register_func_to_anat_use_T2.connect(invt, 'out_file', anat_to_func, 'premat')
# ${FSLDIR}/bin/fslmaths ${fMRIFolder}/Scout_brain_mask.nii.gz -bin ${fMRIFolder}/Scout_brain_mask.nii.gz
func_brain_mask = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'func_brain_mask')
func_brain_mask.inputs.args = '-bin'
register_func_to_anat_use_T2.connect(anat_to_func, 'out_file', func_brain_mask, 'in_file')
# ${FSLDIR}/bin/fslmaths ${fMRIFolder}/${ScoutName}_gdc -mas ${fMRIFolder}/Scout_brain_mask.nii.gz ${fMRIFolder}/Scout_brain_dc.nii.gz
func_brain = pe.Node(interface=fsl.MultiImageMaths(),
name='func_brain')
func_brain.inputs.op_string = "-mas %s "
register_func_to_anat_use_T2.connect(inputspec, 'func', func_brain, 'in_file')
register_func_to_anat_use_T2.connect(func_brain_mask, 'out_file', func_brain, 'operand_files')
# ## re-registering the maked brain to the T1 brain:
# ${FSLDIR}/bin/flirt -interp spline -dof 6 -in ${fMRIFolder}/Scout_brain_dc.nii.gz -ref ${T1wFolder}/${T1wRestoreImageBrain} -omat "$fMRIFolder"/${ScoutName}_gdc2T1w_init.mat -out ${fMRIFolder}/${ScoutName}_gdc2T1w_brain_init -searchrx -30 30 -searchry -30 30 -searchrz -30 30 -cost mutualinfo
linear_reg_func_to_t1 = pe.Node(interface=fsl.FLIRT(),
name='linear_reg_func_to_t1')
linear_reg_func_to_t1.inputs.interp = 'spline'
linear_reg_func_to_t1.inputs.cost = 'mutualinfo'
linear_reg_func_to_t1.inputs.dof = 6
linear_reg_func_to_t1.inputs.searchr_x = [30, 30]
linear_reg_func_to_t1.inputs.searchr_y = [30, 30]
linear_reg_func_to_t1.inputs.searchr_z = [30, 30]
register_func_to_anat_use_T2.connect(func_brain, 'out_file', linear_reg_func_to_t1, 'in_file')
register_func_to_anat_use_T2.connect(inputspec, 'T1_brain', linear_reg_func_to_t1, 'reference')
# #taking out warpfield as it is not being made without a fieldmap.
# ${FSLDIR}/bin/convertwarp --relout --rel -r ${T1wFolder}/${T2wRestoreImage} --postmat=${fMRIFolder}/${ScoutName}_gdc2T1w_init.mat -o ${fMRIFolder}/${ScoutName}_gdc2T1w_init_warp
convert_warp = pe.Node(interface=fsl.ConvertWarp(), name='convert_warp')
convert_warp.inputs.out_relwarp = True
convert_warp.inputs.relwarp = True
register_func_to_anat_use_T2.connect(linear_reg_func_to_t1, 'out_matrix_file', convert_warp, 'postmat')
register_func_to_anat_use_T2.connect(inputspec, 'T2_head', convert_warp, 'reference')
register_func_to_anat_use_T2.connect(linear_reg_func_to_t1, 'out_matrix_file',
outputspec,
'func_to_anat_linear_xfm_nobbreg')
register_func_to_anat_use_T2.connect(convert_warp, 'out_file',
outputspec,
'func_to_anat_linear_warp_nobbreg')
register_func_to_anat_use_T2.connect(linear_reg_func_to_t1, 'out_file',
outputspec, 'anat_func_nobbreg')
return register_func_to_anat_use_T2
[docs]def create_bbregister_func_to_anat(phase_diff_distcor=False,
name='bbregister_func_to_anat'):
"""
Registers a functional scan in native space to structural. This is
meant to be used after create_nonlinear_register() has been run and
relies on some of its outputs.
Parameters
----------
fieldmap_distortion : bool, optional
If field map-based distortion correction is being run, FLIRT should
take in the appropriate field map-related inputs.
name : string, optional
Name of the workflow.
Returns
-------
register_func_to_anat : nipype.pipeline.engine.Workflow
Notes
-----
Workflow Inputs::
inputspec.func : string (nifti file)
Input functional scan to be registered to MNI space
inputspec.anat : string (nifti file)
Corresponding full-head or brain scan of subject
inputspec.linear_reg_matrix : string (mat file)
Affine matrix from linear functional to anatomical registration
inputspec.anat_wm_segmentation : string (nifti file)
White matter segmentation probability mask in anatomical space
inputspec.bbr_schedule : string (.sch file)
Boundary based registration schedule file for flirt command
Workflow Outputs::
outputspec.func_to_anat_linear_xfm : string (mat file)
Affine transformation from functional to anatomical native space
outputspec.anat_func : string (nifti file)
Functional data in anatomical space
"""
register_bbregister_func_to_anat = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(fields=['func',
'anat',
'linear_reg_matrix',
'anat_wm_segmentation',
'bbr_schedule',
'bbr_wm_mask_args',
'fieldmap',
'fieldmapmask']),
name='inputspec')
inputNode_echospacing = pe.Node(
util.IdentityInterface(fields=['echospacing']),
name='echospacing_input')
inputNode_pedir = pe.Node(util.IdentityInterface(fields=['pedir']),
name='pedir_input')
outputspec = pe.Node(util.IdentityInterface(
fields=['func_to_anat_linear_xfm', 'anat_func']), name='outputspec')
wm_bb_mask = pe.Node(interface=fsl.ImageMaths(),
name='wm_bb_mask')
register_bbregister_func_to_anat.connect(
inputspec, 'bbr_wm_mask_args',
wm_bb_mask, 'op_string')
register_bbregister_func_to_anat.connect(inputspec,
'anat_wm_segmentation',
wm_bb_mask, 'in_file')
def bbreg_args(bbreg_target):
return '-cost bbr -wmseg ' + bbreg_target
bbreg_func_to_anat = pe.Node(interface=fsl.FLIRT(),
name='bbreg_func_to_anat')
bbreg_func_to_anat.inputs.dof = 6
register_bbregister_func_to_anat.connect(
inputspec, 'bbr_schedule',
bbreg_func_to_anat, 'schedule')
register_bbregister_func_to_anat.connect(
wm_bb_mask, ('out_file', bbreg_args),
bbreg_func_to_anat, 'args')
register_bbregister_func_to_anat.connect(
inputspec, 'func',
bbreg_func_to_anat, 'in_file')
register_bbregister_func_to_anat.connect(
inputspec, 'anat',
bbreg_func_to_anat, 'reference')
register_bbregister_func_to_anat.connect(
inputspec, 'linear_reg_matrix',
bbreg_func_to_anat, 'in_matrix_file')
if phase_diff_distcor:
conv_pedir = \
pe.Node(interface=util.Function(input_names=['pedir',
'convert'],
output_names=['pedir'],
function=convert_pedir),
name='bbreg_convert_pedir')
conv_pedir.inputs.convert = 'xyz_to_int'
register_bbregister_func_to_anat.connect(inputNode_pedir, 'pedir',
conv_pedir, 'pedir')
register_bbregister_func_to_anat.connect(conv_pedir, 'pedir',
bbreg_func_to_anat, 'pedir')
register_bbregister_func_to_anat.connect(
inputspec, 'fieldmap',
bbreg_func_to_anat, 'fieldmap')
register_bbregister_func_to_anat.connect(
inputspec, 'fieldmapmask',
bbreg_func_to_anat, 'fieldmapmask')
register_bbregister_func_to_anat.connect(
inputNode_echospacing, 'echospacing',
bbreg_func_to_anat, 'echospacing')
register_bbregister_func_to_anat.connect(
bbreg_func_to_anat, 'out_matrix_file',
outputspec, 'func_to_anat_linear_xfm')
register_bbregister_func_to_anat.connect(
bbreg_func_to_anat, 'out_file',
outputspec, 'anat_func')
return register_bbregister_func_to_anat
[docs]def create_wf_calculate_ants_warp(
name='create_wf_calculate_ants_warp', num_threads=1, reg_ants_skull=1
):
'''
Calculates the nonlinear ANTS registration transform. This workflow
employs the antsRegistration tool:
http://stnava.github.io/ANTs/
Parameters
----------
name : string, optional
Name of the workflow.
Returns
-------
calc_ants_warp_wf : nipype.pipeline.engine.Workflow
Notes
-----
Some of the inputs listed below are lists or lists of lists. This is
because antsRegistration can perform multiple stages of calculations
depending on how the user configures their registration.
For example, if one wants to employ a different metric (with different
parameters) at each stage, the lists would be configured like this:
warp_wf.inputs.inputspec.transforms = ['Rigid','Affine','SyN']
warp_wf.inputs.inputspec.transform_parameters = [[0.1],[0.1],[0.1,3,0]]
..where each element in the first list is a metric to be used at each
stage, 'Rigid' being for stage 1, 'Affine' for stage 2, etc. The lists
within the list for transform_parameters would then correspond to each
stage's metric, with [0.1] applying to 'Rigid' and 'Affine' (stages 1 and
2), and [0.1,3,0] applying to 'SyN' of stage 3.
In some cases, when a parameter is not needed for a stage, 'None' must be
entered in its place if there are other parameters for other stages.
Workflow Inputs::
inputspec.moving_brain : string (nifti file)
File of brain to be normalized (registered)
inputspec.reference_brain : string (nifti file)
Target brain file to normalize to
inputspec.dimension : integer
Dimension of the image (default: 3)
inputspec.use_histogram_matching : boolean
Histogram match the images before registration
inputspec.winsorize_lower_quantile : float
Winsorize data based on quantiles (lower range)
inputspec.winsorize_higher_quantile : float
Winsorize data based on quantiles (higher range)
inputspec.metric : list of strings
Image metric(s) to be used at each stage
inputspec.metric_weight : list of floats
Modulate the per-stage weighting of the corresponding metric
inputspec.radius_or_number_of_bins : list of integers
Number of bins in each stage for the MI and Mattes metric, the
radius for other metrics
inputspec.sampling_strategy : list of strings
Sampling strategy (or strategies) to use for the metrics
{None, Regular, or Random}
inputspec.sampling_percentage : list of floats
Defines the sampling strategy
{float value, or None}
inputspec.number_of_iterations : list of lists of integers
Determines the convergence
inputspec.convergence_threshold : list of floats
Threshold compared to the slope of the line fitted in convergence
inputspec.convergence_window_size : list of integers
Window size of convergence calculations
inputspec.transforms : list of strings
Selection of transform options. See antsRegistration documentation
for a full list of options and their descriptions
inputspec.transform_parameters : list of lists of floats
Fine-tuning for the different transform options
inputspec.shrink_factors : list of lists of integers
Specify the shrink factor for the virtual domain (typically the
fixed image) at each level
inputspec.smoothing_sigmas : list of lists of floats
Specify the sigma of gaussian smoothing at each level
inputspec.fixed_image_mask: (an existing file name)
Mask used to limit metric sampling region of the fixed imagein all
stages
inputspec.interp : string
Type of interpolation to use
('Linear' or 'BSpline' or 'LanczosWindowedSinc')
Workflow Outputs::
outputspec.warp_field : string (nifti file)
Output warp field of registration
outputspec.inverse_warp_field : string (nifti file)
Inverse of the warp field of the registration
outputspec.ants_affine_xfm : string (.mat file)
The affine matrix of the registration
outputspec.ants_inverse_affine_xfm : string (.mat file)
The affine matrix of the reverse registration
outputspec.composite_transform : string (nifti file)
The combined transform including the warp field and rigid & affine
linear warps
outputspec.normalized_output_brain : string (nifti file)
Template-registered version of input brain
Registration Procedure:
1. Calculates a nonlinear anatomical-to-template registration.
.. exec::
from CPAC.registration import create_wf_calculate_ants_warp
wf = create_wf_calculate_ants_warp()
wf.write_graph(
graph2use='orig',
dotfilename='./images/generated/calculate_ants_warp.dot'
)
Workflow Graph:
.. image::
:width: 500
Detailed Workflow Graph:
.. image::
:width: 500
'''
calc_ants_warp_wf = pe.Workflow(name=name)
inputspec = pe.Node(util.IdentityInterface(
fields=['moving_brain',
'reference_brain',
'moving_skull',
'reference_skull',
'reference_mask',
'moving_mask',
'fixed_image_mask',
'ants_para',
'interp']),
name='inputspec')
outputspec = pe.Node(util.IdentityInterface(
fields=['ants_initial_xfm',
'ants_rigid_xfm',
'ants_affine_xfm',
'warp_field',
'inverse_warp_field',
'composite_transform',
'wait',
'normalized_output_brain']), name='outputspec')
# use ANTS to warp the masked anatomical image to a template image
'''
calculate_ants_warp = pe.Node(interface=ants.Registration(),
name='calculate_ants_warp')
calculate_ants_warp.inputs.output_warped_image = True
calculate_ants_warp.inputs.initial_moving_transform_com = 0
'''
reg_imports = ['import os', 'import subprocess']
calculate_ants_warp = \
pe.Node(interface=util.Function(input_names=['moving_brain',
'reference_brain',
'moving_skull',
'reference_skull',
'ants_para',
'moving_mask',
'reference_mask',
'fixed_image_mask',
'interp',
'reg_with_skull'],
output_names=['warp_list',
'warped_image'],
function=hardcoded_reg,
imports=reg_imports),
name='calc_ants_warp',
mem_gb=2.8,
mem_x=(2e-7, 'moving_brain', 'xyz'),
throttle=True)
calculate_ants_warp.interface.num_threads = num_threads
select_forward_initial = pe.Node(util.Function(
input_names=['warp_list', 'selection'],
output_names=['selected_warp'],
function=seperate_warps_list), name='select_forward_initial')
select_forward_initial.inputs.selection = "Initial"
select_forward_rigid = pe.Node(util.Function(
input_names=['warp_list', 'selection'],
output_names=['selected_warp'],
function=seperate_warps_list), name='select_forward_rigid')
select_forward_rigid.inputs.selection = "Rigid"
select_forward_affine = pe.Node(util.Function(
input_names=['warp_list', 'selection'],
output_names=['selected_warp'],
function=seperate_warps_list), name='select_forward_affine')
select_forward_affine.inputs.selection = "Affine"
select_forward_warp = pe.Node(util.Function(
input_names=['warp_list', 'selection'],
output_names=['selected_warp'],
function=seperate_warps_list), name='select_forward_warp')
select_forward_warp.inputs.selection = "Warp"
select_inverse_warp = pe.Node(util.Function(
input_names=['warp_list', 'selection'],
output_names=['selected_warp'],
function=seperate_warps_list), name='select_inverse_warp')
select_inverse_warp.inputs.selection = "Inverse"
calc_ants_warp_wf.connect(
inputspec, 'moving_brain',
calculate_ants_warp, 'moving_brain')
calc_ants_warp_wf.connect(
inputspec, 'reference_brain',
calculate_ants_warp, 'reference_brain')
if reg_ants_skull == 1:
calculate_ants_warp.inputs.reg_with_skull = 1
calc_ants_warp_wf.connect(
inputspec, 'moving_skull',
calculate_ants_warp, 'moving_skull')
calc_ants_warp_wf.connect(
inputspec, 'reference_skull',
calculate_ants_warp, 'reference_skull')
else:
calc_ants_warp_wf.connect(
inputspec, 'moving_brain',
calculate_ants_warp, 'moving_skull')
calc_ants_warp_wf.connect(
inputspec, 'reference_brain',
calculate_ants_warp, 'reference_skull')
calc_ants_warp_wf.connect(
inputspec, 'fixed_image_mask',
calculate_ants_warp, 'fixed_image_mask')
calc_ants_warp_wf.connect(inputspec, 'reference_mask',
calculate_ants_warp, 'reference_mask')
calc_ants_warp_wf.connect(inputspec, 'moving_mask',
calculate_ants_warp, 'moving_mask')
calc_ants_warp_wf.connect(inputspec, 'ants_para',
calculate_ants_warp, 'ants_para')
calc_ants_warp_wf.connect(
inputspec, 'interp',
calculate_ants_warp, 'interp')
# inter-workflow connections
calc_ants_warp_wf.connect(
calculate_ants_warp, 'warp_list',
select_forward_initial, 'warp_list')
calc_ants_warp_wf.connect(
calculate_ants_warp, 'warp_list',
select_forward_rigid, 'warp_list')
calc_ants_warp_wf.connect(
calculate_ants_warp, 'warp_list',
select_forward_affine, 'warp_list')
calc_ants_warp_wf.connect(
calculate_ants_warp, 'warp_list',
select_forward_warp, 'warp_list')
calc_ants_warp_wf.connect(
calculate_ants_warp, 'warp_list',
select_inverse_warp, 'warp_list')
# connections to outputspec
calc_ants_warp_wf.connect(
select_forward_initial, 'selected_warp',
outputspec, 'ants_initial_xfm')
calc_ants_warp_wf.connect(
select_forward_rigid, 'selected_warp',
outputspec, 'ants_rigid_xfm')
calc_ants_warp_wf.connect(
select_forward_affine, 'selected_warp',
outputspec, 'ants_affine_xfm')
calc_ants_warp_wf.connect(
select_forward_warp, 'selected_warp',
outputspec, 'warp_field')
calc_ants_warp_wf.connect(
select_inverse_warp, 'selected_warp',
outputspec, 'inverse_warp_field')
calc_ants_warp_wf.connect(
calculate_ants_warp, 'warped_image',
outputspec, 'normalized_output_brain')
return calc_ants_warp_wf
def FSL_registration_connector(wf_name, cfg, orig="T1w", opt=None,
symmetric=False, template="T1w"):
wf = pe.Workflow(name=wf_name)
inputNode = pe.Node(
util.IdentityInterface(fields=['input_brain',
'reference_brain',
'input_head',
'reference_head',
'input_mask',
'reference_mask',
'transform',
'interpolation',
'fnirt_config']),
name='inputspec')
sym = ''
symm = ''
if symmetric:
sym = 'sym'
symm = '_symmetric'
tmpl = ''
if template == 'EPI':
tmpl = 'EPI'
if opt == 'FSL' or opt == 'FSL-linear':
flirt_reg_anat_mni = create_fsl_flirt_linear_reg(
f'anat_mni_flirt_register{symm}'
)
# Input registration parameters
wf.connect(inputNode, 'interpolation',
flirt_reg_anat_mni, 'inputspec.interp')
wf.connect(inputNode, 'input_brain',
flirt_reg_anat_mni, 'inputspec.input_brain')
wf.connect(inputNode, 'reference_brain', flirt_reg_anat_mni,
'inputspec.reference_brain')
write_lin_composite_xfm = pe.Node(interface=fsl.ConvertWarp(),
name=f'fsl_lin-warp_to_nii{symm}')
wf.connect(inputNode, 'reference_brain',
write_lin_composite_xfm, 'reference')
wf.connect(flirt_reg_anat_mni, 'outputspec.linear_xfm',
write_lin_composite_xfm, 'premat')
write_invlin_composite_xfm = pe.Node(interface=fsl.ConvertWarp(),
name=f'fsl_invlin-warp_to_'
f'nii{symm}')
wf.connect(inputNode, 'reference_brain',
write_invlin_composite_xfm, 'reference')
wf.connect(flirt_reg_anat_mni, 'outputspec.invlinear_xfm',
write_invlin_composite_xfm, 'premat')
outputs = {
f'space-{sym}template_desc-preproc_{orig}': (
flirt_reg_anat_mni, 'outputspec.output_brain'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_desc-linear_xfm': (
write_lin_composite_xfm, 'out_file'),
f'from-{sym}{tmpl}template_to-{orig}_mode-image_desc-linear_xfm': (
write_invlin_composite_xfm, 'out_file'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_xfm': (
write_lin_composite_xfm, 'out_file')
}
if opt == 'FSL':
if cfg.registration_workflows['anatomical_registration']['registration']['FSL-FNIRT']['ref_resolution'] == \
cfg.registration_workflows['anatomical_registration']['resolution_for_anat']:
fnirt_reg_anat_mni = create_fsl_fnirt_nonlinear_reg(
f'anat_mni_fnirt_register{symm}'
)
else:
fnirt_reg_anat_mni = create_fsl_fnirt_nonlinear_reg_nhp(
f'anat_mni_fnirt_register{symm}'
)
wf.connect(inputNode, 'input_brain',
fnirt_reg_anat_mni, 'inputspec.input_brain')
wf.connect(inputNode, 'reference_brain',
fnirt_reg_anat_mni, 'inputspec.reference_brain')
wf.connect(inputNode, 'input_head',
fnirt_reg_anat_mni, 'inputspec.input_skull')
# NOTE: crossover from above opt block
wf.connect(flirt_reg_anat_mni, 'outputspec.linear_xfm',
fnirt_reg_anat_mni, 'inputspec.linear_aff')
wf.connect(inputNode, 'reference_head',
fnirt_reg_anat_mni, 'inputspec.reference_skull')
wf.connect(inputNode, 'reference_mask',
fnirt_reg_anat_mni, 'inputspec.ref_mask')
# assign the FSL FNIRT config file specified in pipeline config.yml
wf.connect(inputNode, 'fnirt_config',
fnirt_reg_anat_mni, 'inputspec.fnirt_config')
if cfg.registration_workflows['anatomical_registration']['registration']['FSL-FNIRT']['ref_resolution'] == \
cfg.registration_workflows['anatomical_registration']['resolution_for_anat']:
# NOTE: this is an UPDATE because of the opt block above
added_outputs = {
f'space-{sym}template_desc-preproc_{orig}': (
fnirt_reg_anat_mni, 'outputspec.output_brain'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_xfm': (
fnirt_reg_anat_mni, 'outputspec.nonlinear_xfm')
}
outputs.update(added_outputs)
else:
# NOTE: this is an UPDATE because of the opt block above
added_outputs = {
f'space-{sym}template_desc-preproc_{orig}': (
fnirt_reg_anat_mni, 'outputspec.output_brain'),
f'space-{sym}template_desc-head_{orig}': (
fnirt_reg_anat_mni, 'outputspec.output_head'),
f'space-{sym}template_desc-{orig}_mask': (
fnirt_reg_anat_mni, 'outputspec.output_mask'),
f'space-{sym}template_desc-T1wT2w_biasfield': (
fnirt_reg_anat_mni, 'outputspec.output_biasfield'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_xfm': (
fnirt_reg_anat_mni, 'outputspec.nonlinear_xfm'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_warp': (
fnirt_reg_anat_mni, 'outputspec.nonlinear_warp')
}
outputs.update(added_outputs)
return (wf, outputs)
def ANTs_registration_connector(wf_name, cfg, params, orig="T1w",
symmetric=False, template="T1w"):
wf = pe.Workflow(name=wf_name)
inputNode = pe.Node(
util.IdentityInterface(fields=['input_brain',
'reference_brain',
'input_head',
'reference_head',
'input_mask',
'reference_mask',
'transform',
'interpolation']),
name='inputspec')
sym = ''
symm = ''
if symmetric:
sym = 'sym'
symm = '_symmetric'
tmpl = ''
if template == 'EPI':
tmpl = 'EPI'
if params is None:
err_msg = '\n\n[!] C-PAC says: \nYou have selected ANTs as your ' \
'anatomical registration method.\n' \
'However, no ANTs parameters were specified.\n' \
'Please specify ANTs parameters properly and try again.'
raise Exception(err_msg)
ants_reg_anat_mni = \
create_wf_calculate_ants_warp(
f'anat_mni_ants_register{symm}',
num_threads=cfg.pipeline_setup['system_config'][
'num_ants_threads'],
reg_ants_skull=cfg['registration_workflows'][
'anatomical_registration']['reg_with_skull']
)
ants_reg_anat_mni.inputs.inputspec.ants_para = params
wf.connect(inputNode, 'interpolation',
ants_reg_anat_mni, 'inputspec.interp')
# calculating the transform with the skullstripped is
# reported to be better, but it requires very high
# quality skullstripping. If skullstripping is imprecise
# registration with skull is preferred
wf.connect(inputNode, 'input_brain',
ants_reg_anat_mni, 'inputspec.moving_brain')
wf.connect(inputNode, 'reference_brain',
ants_reg_anat_mni, 'inputspec.reference_brain')
wf.connect(inputNode, 'input_head',
ants_reg_anat_mni, 'inputspec.moving_skull')
wf.connect(inputNode, 'reference_head',
ants_reg_anat_mni, 'inputspec.reference_skull')
wf.connect(inputNode, 'input_mask',
ants_reg_anat_mni, 'inputspec.moving_mask')
wf.connect(inputNode, 'reference_mask',
ants_reg_anat_mni, 'inputspec.reference_mask')
ants_reg_anat_mni.inputs.inputspec.fixed_image_mask = None
if orig == 'T1w':
if cfg.registration_workflows['anatomical_registration'][
'registration']['ANTs']['use_lesion_mask']:
# Create lesion preproc node to apply afni Refit and Resample
lesion_preproc = create_lesion_preproc(
wf_name=f'lesion_preproc{symm}'
)
wf.connect(inputNode, 'lesion_mask',
lesion_preproc, 'inputspec.lesion')
wf.connect(lesion_preproc, 'outputspec.reorient',
ants_reg_anat_mni, 'inputspec.fixed_image_mask')
# combine the linear xfm's into one - makes it easier downstream
write_composite_linear_xfm = pe.Node(
interface=ants.ApplyTransforms(),
name=f'write_composite_linear{symm}_xfm',
mem_gb=1.155,
mem_x=(1708448960473801 / 1208925819614629174706176, 'input_image'))
write_composite_linear_xfm.inputs.print_out_composite_warp_file = True
write_composite_linear_xfm.inputs.output_image = \
f"from-{orig}_to-{sym}{tmpl}template_mode-image_desc-linear_xfm.nii.gz"
wf.connect(inputNode, 'input_brain',
write_composite_linear_xfm, 'input_image')
wf.connect(inputNode, 'reference_brain',
write_composite_linear_xfm, 'reference_image')
wf.connect(inputNode, 'interpolation',
write_composite_linear_xfm, 'interpolation')
write_composite_linear_xfm.inputs.input_image_type = 0
write_composite_linear_xfm.inputs.dimension = 3
collect_transforms = pe.Node(util.Merge(3),
name=f'collect_transforms{symm}',
mem_gb=0.8,
mem_x=(263474863123069 /
37778931862957161709568,
'in1'))
wf.connect(ants_reg_anat_mni, 'outputspec.ants_affine_xfm',
collect_transforms, 'in1')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_rigid_xfm',
collect_transforms, 'in2')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_initial_xfm',
collect_transforms, 'in3')
# check transform list to exclude Nonetype (missing) init/rig/affine
check_transform = pe.Node(
util.Function(input_names=['transform_list'],
output_names=['checked_transform_list',
'list_length'],
function=check_transforms),
name=f'check_transforms',
mem_gb=6)
wf.connect(collect_transforms, 'out', check_transform, 'transform_list')
wf.connect(check_transform, 'checked_transform_list',
write_composite_linear_xfm, 'transforms')
# combine the linear xfm's into one - makes it easier downstream
write_composite_invlinear_xfm = pe.Node(
interface=ants.ApplyTransforms(),
name=f'write_composite_invlinear{symm}_xfm',
mem_gb=1.05,
mem_x=(1367826948979337 / 151115727451828646838272, 'input_image'))
write_composite_invlinear_xfm.inputs.print_out_composite_warp_file = True
write_composite_invlinear_xfm.inputs.output_image = \
f"from-{sym}{tmpl}template_to-{orig}_mode-image_desc-linear_xfm.nii.gz"
wf.connect(inputNode, 'reference_brain',
write_composite_invlinear_xfm, 'input_image')
wf.connect(inputNode, 'input_brain',
write_composite_invlinear_xfm, 'reference_image')
wf.connect(inputNode, 'interpolation',
write_composite_invlinear_xfm, 'interpolation')
write_composite_invlinear_xfm.inputs.input_image_type = 0
write_composite_invlinear_xfm.inputs.dimension = 3
collect_inv_transforms = pe.Node(util.Merge(3),
name='collect_inv_transforms'
f'{symm}')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_initial_xfm',
collect_inv_transforms, 'in1')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_rigid_xfm',
collect_inv_transforms, 'in2')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_affine_xfm',
collect_inv_transforms, 'in3')
# check transform list to exclude Nonetype (missing) init/rig/affine
check_invlinear_transform = pe.Node(
util.Function(input_names=['transform_list'],
output_names=['checked_transform_list',
'list_length'],
function=check_transforms),
name=f'check_inv_transforms')
wf.connect(collect_inv_transforms, 'out',
check_invlinear_transform, 'transform_list')
wf.connect(check_invlinear_transform, 'checked_transform_list',
write_composite_invlinear_xfm, 'transforms')
# generate inverse transform flags, which depends on the
# number of transforms
inverse_transform_flags = pe.Node(
util.Function(input_names=['transform_list'],
output_names=['inverse_transform_flags'],
function=generate_inverse_transform_flags),
name=f'inverse_transform_flags')
wf.connect(check_invlinear_transform, 'checked_transform_list',
inverse_transform_flags, 'transform_list')
wf.connect(inverse_transform_flags, 'inverse_transform_flags',
write_composite_invlinear_xfm, 'invert_transform_flags')
# combine ALL xfm's into one - makes it easier downstream
write_composite_xfm = pe.Node(
interface=ants.ApplyTransforms(),
name=f'write_composite_{symm}xfm',
mem_gb=1.5)
write_composite_xfm.inputs.print_out_composite_warp_file = True
write_composite_xfm.inputs.output_image = \
f"from-{orig}_to-{sym}{tmpl}template_mode-image_xfm.nii.gz"
wf.connect(inputNode, 'input_brain', write_composite_xfm, 'input_image')
wf.connect(inputNode, 'reference_brain',
write_composite_xfm, 'reference_image')
wf.connect(inputNode, 'interpolation',
write_composite_xfm, 'interpolation')
write_composite_xfm.inputs.input_image_type = 0
write_composite_xfm.inputs.dimension = 3
collect_all_transforms = pe.Node(util.Merge(4),
name=f'collect_all_transforms'
f'{symm}')
wf.connect(ants_reg_anat_mni, 'outputspec.warp_field',
collect_all_transforms, 'in1')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_affine_xfm',
collect_all_transforms, 'in2')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_rigid_xfm',
collect_all_transforms, 'in3')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_initial_xfm',
collect_all_transforms, 'in4')
# check transform list to exclude Nonetype (missing) init/rig/affine
check_all_transform = pe.Node(
util.Function(input_names=['transform_list'],
output_names=['checked_transform_list',
'list_length'],
function=check_transforms),
name=f'check_all_transforms')
wf.connect(collect_all_transforms, 'out',
check_all_transform, 'transform_list')
wf.connect(check_all_transform, 'checked_transform_list',
write_composite_xfm, 'transforms')
# combine ALL xfm's into one - makes it easier downstream
write_composite_inv_xfm = pe.Node(
interface=ants.ApplyTransforms(),
name=f'write_composite_inv_{symm}xfm',
mem_gb=0.3,
mem_x=(6278549929741219 / 604462909807314587353088, 'input_image'))
write_composite_inv_xfm.inputs.print_out_composite_warp_file = True
write_composite_inv_xfm.inputs.output_image = \
f"from-{sym}{tmpl}template_to-{orig}_mode-image_xfm.nii.gz"
wf.connect(inputNode, 'reference_brain',
write_composite_inv_xfm, 'input_image')
wf.connect(inputNode, 'input_brain',
write_composite_inv_xfm, 'reference_image')
wf.connect(inputNode, 'interpolation',
write_composite_inv_xfm, 'interpolation')
write_composite_inv_xfm.inputs.input_image_type = 0
write_composite_inv_xfm.inputs.dimension = 3
collect_all_inv_transforms = pe.Node(util.Merge(4),
name=f'collect_all_inv_transforms'
f'{symm}')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_initial_xfm',
collect_all_inv_transforms, 'in1')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_rigid_xfm',
collect_all_inv_transforms, 'in2')
wf.connect(ants_reg_anat_mni, 'outputspec.ants_affine_xfm',
collect_all_inv_transforms, 'in3')
wf.connect(ants_reg_anat_mni, 'outputspec.inverse_warp_field',
collect_all_inv_transforms, 'in4')
# check transform list to exclude Nonetype (missing) init/rig/affine
check_all_inv_transform = pe.Node(
util.Function(input_names=['transform_list'],
output_names=['checked_transform_list',
'list_length'],
function=check_transforms),
name=f'check_all_inv_transforms')
wf.connect(collect_all_inv_transforms, 'out',
check_all_inv_transform, 'transform_list')
wf.connect(check_all_inv_transform, 'checked_transform_list',
write_composite_inv_xfm, 'transforms')
# generate inverse transform flags, which depends on the
# number of transforms
inverse_all_transform_flags = pe.Node(
util.Function(input_names=['transform_list'],
output_names=['inverse_transform_flags'],
function=generate_inverse_transform_flags),
name=f'inverse_all_transform_flags')
wf.connect(check_all_inv_transform, 'checked_transform_list',
inverse_all_transform_flags, 'transform_list')
wf.connect(inverse_all_transform_flags, 'inverse_transform_flags',
write_composite_inv_xfm, 'invert_transform_flags')
outputs = {
f'space-{sym}template_desc-preproc_{orig}': (
ants_reg_anat_mni, 'outputspec.normalized_output_brain'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_xfm': (
write_composite_xfm, 'output_image'),
f'from-{sym}{tmpl}template_to-{orig}_mode-image_xfm': (
write_composite_inv_xfm, 'output_image'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_desc-linear_xfm': (
write_composite_linear_xfm, 'output_image'),
f'from-{sym}{tmpl}template_to-{orig}_mode-image_desc-linear_xfm': (
write_composite_invlinear_xfm, 'output_image'),
f'from-{orig}_to-{sym}{tmpl}template_mode-image_desc-nonlinear_xfm': (
ants_reg_anat_mni, 'outputspec.warp_field'),
f'from-{sym}{tmpl}template_to-{orig}_mode-image_desc-nonlinear_xfm': (
ants_reg_anat_mni, 'outputspec.inverse_warp_field')
}
return (wf, outputs)
def bold_to_T1template_xfm_connector(wf_name, cfg, reg_tool, symmetric=False,
blip=False):
wf = pe.Workflow(name=wf_name)
inputNode = pe.Node(
util.IdentityInterface(fields=['input_brain',
'mean_bold',
'coreg_xfm',
'T1w-brain-template_funcreg',
'T1w_to_template_xfm',
'template_to_T1w_xfm',
'blip_warp']),
name='inputspec')
sym = ''
if symmetric:
sym = 'sym'
if reg_tool == 'ants':
fsl_reg_2_itk = pe.Node(c3.C3dAffineTool(), name='fsl_reg_2_itk')
fsl_reg_2_itk.inputs.itk_transform = True
fsl_reg_2_itk.inputs.fsl2ras = True
# convert the .mat from linear Func->Anat to
# ANTS format
wf.connect(inputNode, 'coreg_xfm', fsl_reg_2_itk, 'transform_file')
wf.connect(inputNode, 'input_brain', fsl_reg_2_itk, 'reference_file')
wf.connect(inputNode, 'mean_bold', fsl_reg_2_itk, 'source_file')
itk_imports = ['import os']
change_transform = pe.Node(util.Function(
input_names=['input_affine_file'],
output_names=['updated_affine_file'],
function=change_itk_transform_type,
imports=itk_imports),
name='change_transform_type')
wf.connect(fsl_reg_2_itk, 'itk_transform',
change_transform, 'input_affine_file')
# combine ALL xfm's into one - makes it easier downstream
write_composite_xfm = pe.Node(
interface=ants.ApplyTransforms(),
name=f'write_composite_xfm',
mem_gb=1.5)
write_composite_xfm.inputs.print_out_composite_warp_file = True
write_composite_xfm.inputs.output_image = \
f"from-bold_to-{sym}template_mode-image_xfm.nii.gz"
wf.connect(inputNode, 'mean_bold',
write_composite_xfm, 'input_image')
wf.connect(inputNode, 'T1w-brain-template_funcreg',
write_composite_xfm, 'reference_image')
write_composite_xfm.inputs.input_image_type = 0
write_composite_xfm.inputs.dimension = 3
write_composite_xfm.inputs.interpolation = \
cfg.registration_workflows['anatomical_registration'][
'registration']['ANTs']['interpolation']
if not blip:
collect_all_transforms = pe.Node(util.Merge(2),
name='collect_all_transforms')
else:
collect_all_transforms = pe.Node(util.Merge(3),
name='collect_all_transforms')
wf.connect(inputNode, 'blip_warp',
collect_all_transforms, 'in3')
wf.connect(inputNode, 'T1w_to_template_xfm',
collect_all_transforms, 'in1')
wf.connect(change_transform, 'updated_affine_file',
collect_all_transforms, 'in2')
wf.connect(collect_all_transforms, 'out',
write_composite_xfm, 'transforms')
write_composite_inv_xfm = pe.Node(
interface=ants.ApplyTransforms(),
name=f'write_composite_inv_xfm',
mem_gb=1.5)
write_composite_inv_xfm.inputs.print_out_composite_warp_file = True
write_composite_inv_xfm.inputs.invert_transform_flags = [True, False]
write_composite_inv_xfm.inputs.output_image = \
f"from-{sym}template_to-bold_mode-image_xfm.nii.gz"
wf.connect(inputNode, 'T1w-brain-template_funcreg',
write_composite_inv_xfm, 'input_image')
wf.connect(inputNode, 'mean_bold',
write_composite_inv_xfm, 'reference_image')
write_composite_inv_xfm.inputs.input_image_type = 0
write_composite_inv_xfm.inputs.dimension = 3
write_composite_inv_xfm.inputs.interpolation = \
cfg.registration_workflows['anatomical_registration'][
'registration']['ANTs']['interpolation']
collect_inv_transforms = pe.Node(util.Merge(2),
name='collect_inv_transforms')
wf.connect(change_transform, 'updated_affine_file',
collect_inv_transforms, 'in1')
wf.connect(inputNode, 'template_to_T1w_xfm',
collect_inv_transforms, 'in2')
wf.connect(collect_inv_transforms, 'out',
write_composite_inv_xfm, 'transforms')
outputs = {
f'from-bold_to-{sym}template_mode-image_xfm':
(write_composite_xfm, 'output_image'),
f'from-{sym}template_to-bold_mode-image_xfm':
(write_composite_inv_xfm, 'output_image')
}
elif reg_tool == 'fsl':
write_composite_xfm = pe.Node(interface=fsl.ConvertWarp(),
name='combine_fsl_warps')
wf.connect(inputNode, 'T1w-brain-template_funcreg',
write_composite_xfm, 'reference')
if blip:
wf.connect(inputNode, 'coreg_xfm',
write_composite_xfm, 'postmat')
wf.connect(inputNode, 'blip_warp',
write_composite_xfm, 'warp1')
wf.connect(inputNode, 'T1w_to_template_xfm',
write_composite_xfm, 'warp2')
else:
wf.connect(inputNode, 'coreg_xfm',
write_composite_xfm, 'premat')
wf.connect(inputNode, 'T1w_to_template_xfm',
write_composite_xfm, 'warp1')
outputs = {
f'from-bold_to-{sym}template_mode-image_xfm':
(write_composite_xfm, 'out_file'),
}
return (wf, outputs)
@nodeblock(
name="register_FSL_anat_to_template",
config=["registration_workflows", "anatomical_registration"],
switch=["run"],
option_key=["registration", "using"],
option_val=["FSL", "FSL-linear"],
inputs=[
(
["desc-preproc_T1w", "space-longitudinal_desc-reorient_T1w"],
["desc-brain_T1w", "space-longitudinal_desc-brain_T1w"],
),
"T1w-template",
"T1w-brain-template",
"FNIRT-T1w-template",
"FNIRT-T1w-brain-template",
"template-ref-mask",
],
outputs={
"space-template_desc-preproc_T1w": {"Template": "T1w-brain-template"},
"space-template_desc-head_T1w": {"Template": "T1w-template"},
"space-template_desc-T1w_mask": {"Template": "T1w-template"},
"space-template_desc-T1wT2w_biasfield": {"Template": "T1w-template"},
"from-T1w_to-template_mode-image_desc-linear_xfm": {
"Template": "T1w-template"},
"from-template_to-T1w_mode-image_desc-linear_xfm": {
"Template": "T1w-template"},
"from-T1w_to-template_mode-image_xfm": {"Template": "T1w-template"},
"from-T1w_to-template_mode-image_warp": {"Template": "T1w-template"},
"from-longitudinal_to-template_mode-image_desc-linear_xfm": {
"Template": "T1w-template"
},
"from-template_to-longitudinal_mode-image_desc-linear_xfm": {
"Template": "T1w-template"
},
"from-longitudinal_to-template_mode-image_xfm": {
"Template": "T1w-template"},
},
)
def register_FSL_anat_to_template(wf, cfg, strat_pool, pipe_num, opt=None):
fsl, outputs = FSL_registration_connector(f'register_{opt}_anat_to_'
f'template_{pipe_num}', cfg,
orig='T1w', opt=opt)
fsl.inputs.inputspec.interpolation = cfg.registration_workflows[
'anatomical_registration']['registration']['FSL-FNIRT'][
'interpolation']
fsl.inputs.inputspec.fnirt_config = cfg.registration_workflows[
'anatomical_registration']['registration']['FSL-FNIRT'][
'fnirt_config']
connect, brain = \
strat_pool.get_data(['desc-brain_T1w',
'space-longitudinal_desc-brain_T1w'],
report_fetched=True)
node, out = connect
wf.connect(node, out, fsl, 'inputspec.input_brain')
if cfg.registration_workflows['anatomical_registration']['registration']['FSL-FNIRT']['ref_resolution'] == \
cfg.registration_workflows['anatomical_registration']['resolution_for_anat']:
node, out = strat_pool.get_data('T1w-brain-template')
wf.connect(node, out, fsl, 'inputspec.reference_brain')
node, out = strat_pool.get_data('T1w-template')
wf.connect(node, out, fsl, 'inputspec.reference_head')
else:
node, out = strat_pool.get_data('FNIRT-T1w-brain-template')
wf.connect(node, out, fsl, 'inputspec.reference_brain')
node, out = strat_pool.get_data('FNIRT-T1w-template')
wf.connect(node, out, fsl, 'inputspec.reference_head')
node, out = strat_pool.get_data(["desc-preproc_T1w",
"space-longitudinal_desc-reorient_T1w"])
wf.connect(node, out, fsl, 'inputspec.input_head')
node, out = strat_pool.get_data('template-ref-mask')
wf.connect(node, out, fsl, 'inputspec.reference_mask')
if 'space-longitudinal' in brain:
for key in outputs.keys():
if 'from-T1w' in key:
new_key = key.replace('from-T1w', 'from-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
if 'to-T1w' in key:
new_key = key.replace('to-T1w', 'to-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
return (wf, outputs)
@nodeblock(
name="register_symmetric_FSL_anat_to_template",
config=["registration_workflows", "anatomical_registration"],
switch=["run"],
option_key=["registration", "using"],
option_val=["FSL", "FSL-linear"],
inputs=[
(
["desc-preproc_T1w", "space-longitudinal_desc-reorient_T1w"],
["desc-brain_T1w", "space-longitudinal_desc-brain_T1w"],
),
"T1w-template-symmetric",
"T1w-brain-template-symmetric",
"dilated-symmetric-brain-mask",
],
outputs={
"space-symtemplate_desc-preproc_T1w": {
"Template": "T1w-brain-template-symmetric"
},
"from-T1w_to-symtemplate_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-T1w_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-T1w_to-symtemplate_mode-image_xfm": {
"Template": "T1w-template-symmetric"
},
"from-longitudinal_to-symtemplate_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-longitudinal_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-longitudinal_to-symtemplate_mode-image_xfm": {
"Template": "T1w-template-symmetric"
},
},
)
def register_symmetric_FSL_anat_to_template(wf, cfg, strat_pool, pipe_num,
opt=None):
fsl, outputs = FSL_registration_connector(f'register_{opt}_anat_to_'
f'template_symmetric_'
f'{pipe_num}', cfg, orig='T1w',
opt=opt, symmetric=True)
fsl.inputs.inputspec.interpolation = cfg.registration_workflows[
'anatomical_registration']['registration']['FSL-FNIRT'][
'interpolation']
fsl.inputs.inputspec.fnirt_config = cfg.registration_workflows[
'anatomical_registration']['registration']['FSL-FNIRT'][
'fnirt_config']
connect, brain = \
strat_pool.get_data(['desc-brain_T1w',
'space-longitudinal_desc-brain_T1w'],
report_fetched=True)
node, out = connect
wf.connect(node, out, fsl, 'inputspec.input_brain')
node, out = strat_pool.get_data('T1w-brain-template-symmetric')
wf.connect(node, out, fsl, 'inputspec.reference_brain')
node, out = strat_pool.get_data(["desc-preproc_T1w",
"space-longitudinal_desc-reorient_T1w"])
wf.connect(node, out, fsl, 'inputspec.input_head')
node, out = strat_pool.get_data('T1w-template-symmetric')
wf.connect(node, out, fsl, 'inputspec.reference_head')
node, out = strat_pool.get_data('dilated-symmetric-brain-mask')
wf.connect(node, out, fsl, 'inputspec.reference_mask')
if 'space-longitudinal' in brain:
for key in outputs.keys():
if 'from-T1w' in key:
new_key = key.replace('from-T1w', 'from-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
if 'to-T1w' in key:
new_key = key.replace('to-T1w', 'to-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
return (wf, outputs)
@nodeblock(
name="register_FSL_EPI_to_template",
config=["registration_workflows", "functional_registration",
"EPI_registration"],
switch=["run"],
option_key="using",
option_val=["FSL", "FSL-linear"],
inputs=[
("sbref", "space-bold_desc-brain_mask"),
"EPI-template",
"EPI-template-mask",
],
outputs={
"space-template_desc-preproc_bold": {"Template": "EPI-template"},
"from-bold_to-EPItemplate_mode-image_desc-linear_xfm": {
"Template": "EPI-template"
},
"from-EPItemplate_to-bold_mode-image_desc-linear_xfm": {
"Template": "EPI-template"
},
"from-bold_to-EPItemplate_mode-image_xfm": {
"Template": "EPI-template"},
},
)
def register_FSL_EPI_to_template(wf, cfg, strat_pool, pipe_num, opt=None):
'''Directly register the mean functional to an EPI template. No T1w
involved.
'''
fsl, outputs = FSL_registration_connector(f'register_{opt}_EPI_to_'
f'template_{pipe_num}', cfg,
orig='bold', opt=opt,
template='EPI')
fsl.inputs.inputspec.interpolation = cfg['registration_workflows'][
'functional_registration']['EPI_registration']['FSL-FNIRT'][
'interpolation']
fsl.inputs.inputspec.fnirt_config = cfg['registration_workflows'][
'functional_registration']['EPI_registration']['FSL-FNIRT'][
'fnirt_config']
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, fsl, 'inputspec.input_brain')
node, out = strat_pool.get_data('EPI-template')
wf.connect(node, out, fsl, 'inputspec.reference_brain')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, fsl, 'inputspec.input_head')
node, out = strat_pool.get_data('EPI-template')
wf.connect(node, out, fsl, 'inputspec.reference_head')
node, out = strat_pool.get_data('EPI-template-mask')
wf.connect(node, out, fsl, 'inputspec.reference_mask')
return (wf, outputs)
@nodeblock(
name="register_ANTs_anat_to_template",
config=["registration_workflows", "anatomical_registration"],
switch=["run"],
option_key=["registration", "using"],
option_val="ANTS",
inputs=[
(
["desc-preproc_T1w", "space-longitudinal_desc-brain_T1w"],
[
"space-T1w_desc-brain_mask",
"space-longitudinal_desc-brain_mask",
"space-T1w_desc-acpcbrain_mask",
],
[
"desc-restore_T1w",
"desc-head_T1w",
"desc-preproc_T1w",
"space-longitudinal_desc-reorient_T1w",
],
"space-template_desc-head_T1w",
"space-template_desc-preproc_T1w",
),
"T1w-template",
"T1w-brain-template",
"T1w-brain-template-mask",
"label-lesion_mask",
],
outputs={
"space-template_desc-preproc_T1w": {
"Description": "The preprocessed T1w brain transformed to "
"template space.",
"Template": "T1w-template",
},
"from-T1w_to-template_mode-image_desc-linear_xfm": {
"Description": "Linear (affine) transform from T1w native space "
"to T1w-template space.",
"Template": "T1w-template",
},
"from-template_to-T1w_mode-image_desc-linear_xfm": {
"Description": "Linear (affine) transform from T1w-template space "
"to T1w native space.",
"Template": "T1w-template",
},
"from-T1w_to-template_mode-image_desc-nonlinear_xfm": {
"Description": "Nonlinear (warp field) transform from T1w native "
"space to T1w-template space.",
"Template": "T1w-template",
},
"from-template_to-T1w_mode-image_desc-nonlinear_xfm": {
"Description": "Nonlinear (warp field) transform from "
"T1w-template space to T1w native space.",
"Template": "T1w-template",
},
"from-T1w_to-template_mode-image_xfm": {
"Description": "Composite (affine + warp field) transform from "
"T1w native space to T1w-template space.",
"Template": "T1w-template",
},
"from-template_to-T1w_mode-image_xfm": {
"Description": "Composite (affine + warp field) transform from "
"T1w-template space to T1w native space.",
"Template": "T1w-template",
},
"from-longitudinal_to-template_mode-image_desc-linear_xfm": {
"Description": "Linear (affine) transform from "
"longitudinal-template space to T1w-template "
"space.",
"Template": "T1w-template",
},
"from-template_to-longitudinal_mode-image_desc-linear_xfm": {
"Description": "Linear (affine) transform from T1w-template "
"space to longitudinal-template space.",
"Template": "T1w-template",
},
"from-longitudinal_to-template_mode-image_desc-nonlinear_xfm": {
"Description": "Nonlinear (warp field) transform from "
"longitudinal-template space to T1w-template "
"space.",
"Template": "T1w-template",
},
"from-template_to-longitudinal_mode-image_desc-nonlinear_xfm": {
"Description": "Nonlinear (warp field) transform from "
"T1w-template space to longitudinal-template "
"space.",
"Template": "T1w-template",
},
"from-longitudinal_to-template_mode-image_xfm": {
"Description": "Composite (affine + warp field) transform from "
"longitudinal-template space to T1w-template "
"space.",
"Template": "T1w-template",
},
"from-template_to-longitudinal_mode-image_xfm": {
"Description": "Composite (affine + warp field) transform from "
"T1w-template space to longitudinal-template "
"space.",
"Template": "T1w-template",
},
},
)
def register_ANTs_anat_to_template(wf, cfg, strat_pool, pipe_num, opt=None):
params = cfg.registration_workflows['anatomical_registration'][
'registration']['ANTs']['T1_registration']
ants_rc, outputs = ANTs_registration_connector('ANTS_T1_to_template_'
f'{pipe_num}', cfg,
params, orig='T1w')
ants_rc.inputs.inputspec.interpolation = cfg.registration_workflows[
'anatomical_registration']['registration']['ANTs']['interpolation']
connect, brain = \
strat_pool.get_data(['desc-preproc_T1w',
'space-longitudinal_desc-brain_T1w'],
report_fetched=True)
node, out = connect
wf.connect(node, out, ants_rc, 'inputspec.input_brain')
t1w_brain_template = strat_pool.node_data('T1w-brain-template')
wf.connect(t1w_brain_template.node, t1w_brain_template.out,
ants_rc, 'inputspec.reference_brain')
# TODO check the order of T1w
node, out = strat_pool.get_data(["desc-restore_T1w", "desc-head_T1w",
"desc-preproc_T1w",
"space-longitudinal_desc-reorient_T1w"])
wf.connect(node, out, ants_rc, 'inputspec.input_head')
t1w_template = strat_pool.node_data('T1w-template')
wf.connect(t1w_template.node, t1w_template.out,
ants_rc, 'inputspec.reference_head')
brain_mask = strat_pool.node_data(["space-T1w_desc-brain_mask",
"space-longitudinal_desc-brain_mask",
"space-T1w_desc-acpcbrain_mask"])
wf.connect(brain_mask.node, brain_mask.out,
ants_rc, 'inputspec.input_mask')
if strat_pool.check_rpool('T1w-brain-template-mask'):
node, out = strat_pool.get_data('T1w-brain-template-mask')
wf.connect(node, out, ants_rc, 'inputspec.reference_mask')
if strat_pool.check_rpool('label-lesion_mask'):
node, out = strat_pool.get_data('label-lesion_mask')
wf.connect(node, out, ants_rc, 'inputspec.lesion_mask')
if 'space-longitudinal' in brain:
for key in outputs:
for direction in ['from', 'to']:
if f'{direction}-T1w' in key:
new_key = key.replace(f'{direction}-T1w',
f'{direction}-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
return (wf, outputs)
@nodeblock(
name="register_symmetric_ANTs_anat_to_template",
config=["registration_workflows", "anatomical_registration"],
switch=["run"],
option_key=["registration", "using"],
option_val="ANTS",
inputs=[
(
["desc-preproc_T1w", "space-longitudinal_desc-brain_T1w"],
["space-T1w_desc-brain_mask",
"space-longitudinal_desc-brain_mask"],
[
"desc-head_T1w",
"desc-preproc_T1w",
"space-longitudinal_desc-reorient_T1w",
],
),
"T1w-template-symmetric",
"T1w-brain-template-symmetric",
"dilated-symmetric-brain-mask",
"label-lesion_mask",
],
outputs={
"space-symtemplate_desc-preproc_T1w": {
"Template": "T1w-brain-template-symmetric"
},
"from-T1w_to-symtemplate_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-T1w_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-T1w_to-symtemplate_mode-image_desc-nonlinear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-T1w_mode-image_desc-nonlinear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-T1w_to-symtemplate_mode-image_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-T1w_mode-image_xfm": {
"Template": "T1w-template-symmetric"
},
"from-longitudinal_to-symtemplate_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-longitudinal_mode-image_desc-linear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-longitudinal_to-symtemplate_mode-image_desc-nonlinear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-longitudinal_mode-image_desc-nonlinear_xfm": {
"Template": "T1w-template-symmetric"
},
"from-longitudinal_to-symtemplate_mode-image_xfm": {
"Template": "T1w-template-symmetric"
},
"from-symtemplate_to-longitudinal_mode-image_xfm": {
"Template": "T1w-template-symmetric"
},
},
)
def register_symmetric_ANTs_anat_to_template(wf, cfg, strat_pool, pipe_num,
opt=None):
params = cfg.registration_workflows['anatomical_registration'][
'registration']['ANTs']['T1_registration']
ants, outputs = ANTs_registration_connector('ANTS_T1_to_template_'
f'symmetric_{pipe_num}', cfg,
params, orig='T1w',
symmetric=True)
ants.inputs.inputspec.interpolation = cfg.registration_workflows[
'anatomical_registration']['registration']['ANTs']['interpolation']
connect, brain = \
strat_pool.get_data(['desc-preproc_T1w',
'space-longitudinal_desc-brain_T1w'],
report_fetched=True)
node, out = connect
wf.connect(node, out, ants, 'inputspec.input_brain')
node, out = strat_pool.get_data('T1w-brain-template-symmetric')
wf.connect(node, out, ants, 'inputspec.reference_brain')
node, out = strat_pool.get_data(["desc-head_T1w", "desc-preproc_T1w",
"space-longitudinal_desc-reorient_T1w"])
wf.connect(node, out, ants, 'inputspec.input_head')
node, out = strat_pool.get_data('T1w-template-symmetric')
wf.connect(node, out, ants, 'inputspec.reference_head')
node, out = strat_pool.get_data(["space-T1w_desc-brain_mask",
"space-longitudinal_desc-brain_mask"])
wf.connect(node, out, ants, 'inputspec.input_mask')
node, out = strat_pool.get_data('dilated-symmetric-brain-mask')
wf.connect(node, out, ants, 'inputspec.reference_mask')
if strat_pool.check_rpool('label-lesion_mask'):
node, out = strat_pool.get_data('label-lesion_mask')
wf.connect(node, out, ants, 'inputspec.lesion_mask')
if 'space-longitudinal' in brain:
for key in outputs.keys():
if 'from-T1w' in key:
new_key = key.replace('from-T1w', 'from-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
if 'to-T1w' in key:
new_key = key.replace('to-T1w', 'to-longitudinal')
outputs[new_key] = outputs[key]
del outputs[key]
return (wf, outputs)
@nodeblock(
name="register_ANTs_EPI_to_template",
config=["registration_workflows", "functional_registration", "EPI_registration"],
switch=["run"],
option_key="using",
option_val="ANTS",
inputs=[
("sbref", "space-bold_desc-brain_mask"),
"EPI-template",
"EPI-template-mask",
],
outputs={
"space-template_desc-preproc_bold": {"Template": "EPI-template"},
"from-bold_to-EPItemplate_mode-image_desc-linear_xfm": {
"Template": "EPI-template"
},
"from-EPItemplate_to-bold_mode-image_desc-linear_xfm": {
"Template": "EPI-template"
},
"from-bold_to-EPItemplate_mode-image_desc-nonlinear_xfm": {
"Template": "EPI-template"
},
"from-EPItemplate_to-bold_mode-image_desc-nonlinear_xfm": {
"Template": "EPI-template"
},
"from-bold_to-EPItemplate_mode-image_xfm": {
"Template": "EPI-template"},
"from-EPItemplate_to-bold_mode-image_xfm": {
"Template": "EPI-template"},
},
)
def register_ANTs_EPI_to_template(wf, cfg, strat_pool, pipe_num, opt=None):
'''Directly register the mean functional to an EPI template. No T1w
involved.
'''
params = cfg.registration_workflows['functional_registration'][
'EPI_registration']['ANTs']['parameters']
ants, outputs = ANTs_registration_connector('ANTS_bold_to_EPI-template'
f'_{pipe_num}', cfg, params,
orig='bold', template='EPI')
ants.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['EPI_registration']['ANTs'][
'interpolation']
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, ants, 'inputspec.input_brain')
node, out = strat_pool.get_data('EPI-template')
wf.connect(node, out, ants, 'inputspec.reference_brain')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, ants, 'inputspec.input_head')
node, out = strat_pool.get_data('EPI-template')
wf.connect(node, out, ants, 'inputspec.reference_head')
node, out = strat_pool.get_data('space-bold_desc-brain_mask')
wf.connect(node, out, ants, 'inputspec.input_mask')
if strat_pool.check_rpool('EPI-template-mask'):
node, out = strat_pool.get_data('EPI-template-mask')
wf.connect(node, out, ants, 'inputspec.reference_mask')
return (wf, outputs)
@nodeblock(
name="overwrite_transform_anat_to_template",
switch=[
["registration_workflows", "anatomical_registration", "run"],
[
"registration_workflows",
"anatomical_registration",
"overwrite_transform",
"run",
],
],
option_key=[
"registration_workflows",
"anatomical_registration",
"overwrite_transform",
"using",
],
option_val="FSL",
inputs=[
(
"desc-restore-brain_T1w",
["desc-preproc_T1w", "space-longitudinal_desc-brain_T1w"],
["desc-restore_T1w", "desc-preproc_T1w", "desc-reorient_T1w",
"T1w"],
["desc-preproc_T1w", "desc-reorient_T1w", "T1w"],
"space-T1w_desc-brain_mask",
"T1w-template",
"from-T1w_to-template_mode-image_xfm",
"from-template_to-T1w_mode-image_xfm",
"space-template_desc-brain_T1w",
"space-template_desc-preproc_T1w",
)
],
outputs={
"space-template_desc-preproc_T1w": {"Template": "T1w-template"},
"space-template_desc-head_T1w": {"Template": "T1w-template"},
"space-template_desc-T1w_mask": {"Template": "T1w-template"},
"from-T1w_to-template_mode-image_xfm": {"Template": "T1w-template"},
"from-template_to-T1w_mode-image_xfm": {"Template": "T1w-template"},
},
)
def overwrite_transform_anat_to_template(wf, cfg, strat_pool, pipe_num,
opt=None):
xfm_prov = strat_pool.get_cpac_provenance(
'from-T1w_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
if opt.lower() == 'fsl' and reg_tool.lower() == 'ants':
# Apply head-to-head transforms on brain using ABCD-style registration
# Convert ANTs warps to FSL warps to be consistent with the functional registration
# Ref: https://github.com/DCAN-Labs/DCAN-HCP/blob/master/PostFreeSurfer/scripts/AtlasRegistrationToMNI152_ANTsbased.sh#L134-L172
# antsApplyTransforms -d 3 -i ${T1wRestore}.nii.gz -r ${Reference} \
# -t ${WD}/xfms/T1w_to_MNI_3Warp.nii.gz \
# -t ${WD}/xfms/T1w_to_MNI_2Affine.mat \
# -t ${WD}/xfms/T1w_to_MNI_1Rigid.mat \
# -t ${WD}/xfms/T1w_to_MNI_0DerivedInitialMovingTranslation.mat \
# -o [${WD}/xfms/ANTs_CombinedWarp.nii.gz,1]
ants_apply_warp_t1_to_template = pe.Node(interface=ants.ApplyTransforms(),
name=f'ANTS-ABCD_T1_to_template_{pipe_num}')
ants_apply_warp_t1_to_template.inputs.dimension = 3
ants_apply_warp_t1_to_template.inputs.print_out_composite_warp_file = True
ants_apply_warp_t1_to_template.inputs.output_image = 'ANTs_CombinedWarp.nii.gz'
node, out = strat_pool.get_data(['desc-restore_T1w', 'desc-preproc_T1w'])
wf.connect(node, out, ants_apply_warp_t1_to_template, 'input_image')
node, out = strat_pool.get_data('T1w-template')
wf.connect(node, out, ants_apply_warp_t1_to_template, 'reference_image')
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_xfm')
wf.connect(node, out, ants_apply_warp_t1_to_template, 'transforms')
# antsApplyTransforms -d 3 -i ${T1wImage}.nii.gz -r ${Reference} \
# -t [${WD}/xfms/T1w_to_MNI_0DerivedInitialMovingTranslation.mat,1] \
# -t [${WD}/xfms/T1w_to_MNI_1Rigid.mat,1] \
# -t [${WD}/xfms/T1w_to_MNI_2Affine.mat,1] \
# -t ${WD}/xfms/T1w_to_MNI_3InverseWarp.nii.gz \
# -o [${WD}/xfms/ANTs_CombinedInvWarp.nii.gz,1]
# T1wImage is ACPC aligned head
ants_apply_warp_template_to_t1 = pe.Node(interface=ants.ApplyTransforms(),
name=f'ANTS-ABCD_template_to_T1_{pipe_num}')
ants_apply_warp_template_to_t1.inputs.dimension = 3
ants_apply_warp_template_to_t1.inputs.print_out_composite_warp_file = True
ants_apply_warp_template_to_t1.inputs.output_image = 'ANTs_CombinedInvWarp.nii.gz'
node, out = strat_pool.get_data('desc-preproc_T1w')
wf.connect(node, out, ants_apply_warp_template_to_t1, 'input_image')
node, out = strat_pool.get_data('T1w-template')
wf.connect(node, out, ants_apply_warp_template_to_t1, 'reference_image')
node, out = strat_pool.get_data('from-template_to-T1w_mode-image_xfm')
wf.connect(node, out, ants_apply_warp_template_to_t1, 'transforms')
# c4d -mcs ${WD}/xfms/ANTs_CombinedWarp.nii.gz -oo ${WD}/xfms/e1.nii.gz ${WD}/xfms/e2.nii.gz ${WD}/xfms/e3.nii.gz
# -mcs: -multicomponent-split, -oo: -output-multiple
split_combined_warp = pe.Node(util.Function(input_names=['input',
'output_name'],
output_names=['output1',
'output2',
'output3'],
function=run_c4d),
name=f'split_combined_warp_{pipe_num}')
split_combined_warp.inputs.output_name = 'e'
wf.connect(ants_apply_warp_t1_to_template, 'output_image',
split_combined_warp, 'input')
# c4d -mcs ${WD}/xfms/ANTs_CombinedInvWarp.nii.gz -oo ${WD}/xfms/e1inv.nii.gz ${WD}/xfms/e2inv.nii.gz ${WD}/xfms/e3inv.nii.gz
split_combined_inv_warp = pe.Node(util.Function(input_names=['input',
'output_name'],
output_names=['output1',
'output2',
'output3'],
function=run_c4d),
name=f'split_combined_inv_warp_{pipe_num}')
split_combined_inv_warp.inputs.output_name = 'einv'
wf.connect(ants_apply_warp_template_to_t1, 'output_image',
split_combined_inv_warp, 'input')
# fslmaths ${WD}/xfms/e2.nii.gz -mul -1 ${WD}/xfms/e-2.nii.gz
change_e2_sign = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'change_e2_sign_{pipe_num}')
change_e2_sign.inputs.args = '-mul -1'
wf.connect(split_combined_warp, 'output2',
change_e2_sign, 'in_file')
# fslmaths ${WD}/xfms/e2inv.nii.gz -mul -1 ${WD}/xfms/e-2inv.nii.gz
change_e2inv_sign = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'change_e2inv_sign_{pipe_num}')
change_e2inv_sign.inputs.args = '-mul -1'
wf.connect(split_combined_inv_warp, 'output2',
change_e2inv_sign, 'in_file')
# fslmerge -t ${OutputTransform} ${WD}/xfms/e1.nii.gz ${WD}/xfms/e-2.nii.gz ${WD}/xfms/e3.nii.gz
merge_xfms_to_list = pe.Node(util.Merge(3),
name=f'merge_t1_to_template_xfms_to_list_{pipe_num}')
wf.connect(split_combined_warp, 'output1',
merge_xfms_to_list, 'in1')
wf.connect(change_e2_sign, 'out_file',
merge_xfms_to_list, 'in2')
wf.connect(split_combined_warp, 'output3',
merge_xfms_to_list, 'in3')
merge_xfms = pe.Node(interface=fslMerge(),
name=f'merge_t1_to_template_xfms_{pipe_num}')
merge_xfms.inputs.dimension = 't'
wf.connect(merge_xfms_to_list, 'out',
merge_xfms, 'in_files')
# fslmerge -t ${OutputInvTransform} ${WD}/xfms/e1inv.nii.gz ${WD}/xfms/e-2inv.nii.gz ${WD}/xfms/e3inv.nii.gz
merge_inv_xfms_to_list = pe.Node(util.Merge(3),
name=f'merge_template_to_t1_xfms_to_list_{pipe_num}')
wf.connect(split_combined_inv_warp, 'output1',
merge_inv_xfms_to_list, 'in1')
wf.connect(change_e2inv_sign, 'out_file',
merge_inv_xfms_to_list, 'in2')
wf.connect(split_combined_inv_warp, 'output3',
merge_inv_xfms_to_list, 'in3')
merge_inv_xfms = pe.Node(interface=fslMerge(),
name=f'merge_template_to_t1_xfms_{pipe_num}')
merge_inv_xfms.inputs.dimension = 't'
wf.connect(merge_inv_xfms_to_list, 'out',
merge_inv_xfms, 'in_files')
# applywarp --rel --interp=spline -i ${T1wRestore} -r ${Reference} -w ${OutputTransform} -o ${OutputT1wImageRestore}
fsl_apply_warp_t1_to_template = pe.Node(interface=fsl.ApplyWarp(),
name=f'FSL-ABCD_T1_to_template_{pipe_num}')
fsl_apply_warp_t1_to_template.inputs.relwarp = True
fsl_apply_warp_t1_to_template.inputs.interp = 'spline'
node, out = strat_pool.get_data(['desc-restore_T1w', 'desc-preproc_T1w'])
wf.connect(node, out, fsl_apply_warp_t1_to_template, 'in_file')
node, out = strat_pool.get_data('T1w-template')
wf.connect(node, out, fsl_apply_warp_t1_to_template, 'ref_file')
wf.connect(merge_xfms, 'merged_file',
fsl_apply_warp_t1_to_template, 'field_file')
# applywarp --rel --interp=nn -i ${T1wRestoreBrain} -r ${Reference} -w ${OutputTransform} -o ${OutputT1wImageRestoreBrain}
fsl_apply_warp_t1_brain_to_template = pe.Node(interface=fsl.ApplyWarp(),
name=f'FSL-ABCD_T1_brain_to_template_{pipe_num}')
fsl_apply_warp_t1_brain_to_template.inputs.relwarp = True
fsl_apply_warp_t1_brain_to_template.inputs.interp = 'nn'
# TODO connect T1wRestoreBrain, check T1wRestoreBrain quality
node, out = strat_pool.get_data('desc-preproc_T1w')
wf.connect(node, out, fsl_apply_warp_t1_brain_to_template, 'in_file')
node, out = strat_pool.get_data('T1w-template')
wf.connect(node, out, fsl_apply_warp_t1_brain_to_template, 'ref_file')
wf.connect(merge_xfms, 'merged_file',
fsl_apply_warp_t1_brain_to_template, 'field_file')
fsl_apply_warp_t1_brain_mask_to_template = pe.Node(interface=fsl.ApplyWarp(),
name=f'FSL-ABCD_T1_brain_mask_to_template_{pipe_num}')
fsl_apply_warp_t1_brain_mask_to_template.inputs.relwarp = True
fsl_apply_warp_t1_brain_mask_to_template.inputs.interp = 'nn'
node, out = strat_pool.get_data('space-T1w_desc-brain_mask')
wf.connect(node, out, fsl_apply_warp_t1_brain_mask_to_template, 'in_file')
node, out = strat_pool.get_data('T1w-template')
wf.connect(node, out, fsl_apply_warp_t1_brain_mask_to_template, 'ref_file')
wf.connect(merge_xfms, 'merged_file',
fsl_apply_warp_t1_brain_mask_to_template, 'field_file')
# fslmaths ${OutputT1wImageRestore} -mas ${OutputT1wImageRestoreBrain} ${OutputT1wImageRestoreBrain}
apply_mask = pe.Node(interface=fsl.maths.ApplyMask(),
name=f'get_t1_brain_{pipe_num}')
wf.connect(fsl_apply_warp_t1_to_template, 'out_file',
apply_mask, 'in_file')
wf.connect(fsl_apply_warp_t1_brain_to_template, 'out_file',
apply_mask, 'mask_file')
outputs = {
'space-template_desc-preproc_T1w': (apply_mask, 'out_file'),
'space-template_desc-head_T1w': (fsl_apply_warp_t1_to_template, 'out_file'),
'space-template_desc-T1w_mask': (fsl_apply_warp_t1_brain_mask_to_template, 'out_file'),
'from-T1w_to-template_mode-image_xfm': (merge_xfms, 'merged_file'),
'from-template_to-T1w_mode-image_xfm': (merge_inv_xfms, 'merged_file')
}
return (wf, outputs)
@nodeblock(
name="coregistration_prep_vol",
switch=["functional_preproc", "run"],
option_key=[
"registration_workflows",
"functional_registration",
"coregistration",
"func_input_prep",
"input",
],
option_val="Selected_Functional_Volume",
inputs=[("desc-brain_bold", ["desc-motion_bold", "bold"], "sbref")],
outputs=["sbref"],
)
def coregistration_prep_vol(wf, cfg, strat_pool, pipe_num, opt=None):
get_func_volume = pe.Node(interface=afni.Calc(),
name=f'get_func_volume_{pipe_num}')
get_func_volume.inputs.set(
expr='a',
single_idx=cfg.registration_workflows['functional_registration']['coregistration'][
'func_input_prep']['Selected Functional Volume']['func_reg_input_volume'],
outputtype='NIFTI_GZ'
)
if not cfg.registration_workflows['functional_registration'][
'coregistration']['func_input_prep']['reg_with_skull']:
node, out = strat_pool.get_data("desc-brain_bold")
else:
# TODO check which file is functional_skull_leaf
# TODO add a function to choose brain or skull?
node, out = strat_pool.get_data(["desc-motion_bold", "bold"])
wf.connect(node, out, get_func_volume, 'in_file_a')
coreg_input = (get_func_volume, 'out_file')
outputs = {
'sbref': coreg_input
}
return (wf, outputs)
@nodeblock(
name="coregistration_prep_mean",
switch=[["functional_preproc", "run"],
["functional_preproc", "coreg_prep", "run"]],
option_key=[
"registration_workflows",
"functional_registration",
"coregistration",
"func_input_prep",
"input",
],
option_val="Mean_Functional",
inputs=["desc-mean_bold"],
outputs=["sbref"],
)
def coregistration_prep_mean(wf, cfg, strat_pool, pipe_num, opt=None):
coreg_input = strat_pool.get_data("desc-mean_bold")
# TODO add mean skull
if cfg.registration_workflows['functional_registration'][
'coregistration']['func_input_prep']['Mean Functional'][
'n4_correct_func']:
n4_correct_func = pe.Node(
interface=
ants.N4BiasFieldCorrection(dimension=3,
copy_header=True,
bspline_fitting_distance=200),
shrink_factor=2,
name=f'func_mean_n4_corrected_{pipe_num}')
n4_correct_func.inputs.args = '-r True'
node, out = coreg_input
wf.connect(node, out, n4_correct_func, 'input_image')
coreg_input = (n4_correct_func, 'output_image')
outputs = {
'sbref': coreg_input
}
return (wf, outputs)
@nodeblock(
name="coregistration_prep_fmriprep",
switch=[["functional_preproc", "run"],
["functional_preproc", "coreg_prep", "run"]],
option_key=[
"registration_workflows",
"functional_registration",
"coregistration",
"func_input_prep",
"input",
],
option_val="fmriprep_reference",
inputs=["desc-ref_bold"],
outputs=["sbref"],
)
def coregistration_prep_fmriprep(wf, cfg, strat_pool, pipe_num, opt=None):
coreg_input = strat_pool.get_data("desc-ref_bold")
outputs = {
'sbref': coreg_input
}
return (wf, outputs)
@nodeblock(
name="coregistration",
config=["registration_workflows", "functional_registration", "coregistration"],
switch=["run"],
inputs=[
(
"sbref",
"desc-motion_bold",
"space-bold_label-WM_mask",
"despiked-fieldmap",
"fieldmap-mask",
"effectiveEchoSpacing",
"pe-direction",
),
(
"desc-preproc_T1w",
"desc-restore-brain_T1w",
"desc-preproc_T2w",
"desc-preproc_T2w",
"T2w",
["label-WM_probseg", "label-WM_mask"],
["label-WM_pveseg", "label-WM_mask"],
"desc-head_T1w",
"desc-head_T2w",
),
],
outputs=[
"space-T1w_sbref",
"from-bold_to-T1w_mode-image_desc-linear_xfm",
"from-bold_to-T1w_mode-image_desc-linear_warp",
],
)
def coregistration(wf, cfg, strat_pool, pipe_num, opt=None):
diff_complete = False
if strat_pool.check_rpool("despiked-fieldmap") and \
strat_pool.check_rpool("fieldmap-mask"):
diff_complete = True
if strat_pool.check_rpool('T2w') and cfg.anatomical_preproc['run_t2']:
# monkey data
func_to_anat = create_register_func_to_anat_use_T2(cfg,
f'func_to_anat_FLIRT_'
f'{pipe_num}')
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/GenericfMRIVolumeProcessingPipeline.sh#L177
# fslmaths "$fMRIFolder"/"$NameOffMRI"_mc -Tmean "$fMRIFolder"/"$ScoutName"_gdc
func_mc_mean = pe.Node(interface=afni_utils.TStat(),
name=f'func_motion_corrected_mean_{pipe_num}')
func_mc_mean.inputs.options = '-mean'
func_mc_mean.inputs.outputtype = 'NIFTI_GZ'
node, out = strat_pool.get_data("desc-motion_bold")
wf.connect(node, out, func_mc_mean, 'in_file')
wf.connect(func_mc_mean, 'out_file', func_to_anat, 'inputspec.func')
node, out = strat_pool.get_data('desc-preproc_T1w')
wf.connect(node, out, func_to_anat, 'inputspec.T1_brain')
node, out = strat_pool.get_data('desc-head_T2w')
wf.connect(node, out, func_to_anat, 'inputspec.T2_head')
node, out = strat_pool.get_data('desc-preproc_T2w')
wf.connect(node, out, func_to_anat, 'inputspec.T2_brain')
else:
# if field map-based distortion correction is on, but BBR is off,
# send in the distortion correction files here
func_to_anat = create_register_func_to_anat(cfg, diff_complete,
f'func_to_anat_FLIRT_'
f'{pipe_num}')
func_to_anat.inputs.inputspec.dof = cfg.registration_workflows[
'functional_registration']['coregistration']['dof']
func_to_anat.inputs.inputspec.interp = cfg.registration_workflows[
'functional_registration']['coregistration']['interpolation']
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, func_to_anat, 'inputspec.func')
if cfg.registration_workflows['functional_registration'][
'coregistration']['reference'] == 'brain':
# TODO: use JSON meta-data to confirm
node, out = strat_pool.get_data('desc-preproc_T1w')
elif cfg.registration_workflows['functional_registration'][
'coregistration']['reference'] == 'restore-brain':
node, out = strat_pool.get_data('desc-restore-brain_T1w')
wf.connect(node, out, func_to_anat, 'inputspec.anat')
if diff_complete:
node, out = strat_pool.get_data('effectiveEchoSpacing')
wf.connect(node, out, func_to_anat, 'echospacing_input.echospacing')
node, out = strat_pool.get_data('pe-direction')
wf.connect(node, out, func_to_anat, 'pedir_input.pedir')
node, out = strat_pool.get_data("despiked-fieldmap")
wf.connect(node, out, func_to_anat, 'inputspec.fieldmap')
node, out = strat_pool.get_data("fieldmap-mask")
wf.connect(node, out, func_to_anat, 'inputspec.fieldmapmask')
if strat_pool.check_rpool('T2w') and cfg.anatomical_preproc['run_t2']:
outputs = {
'space-T1w_sbref':
(func_to_anat, 'outputspec.anat_func_nobbreg'),
'from-bold_to-T1w_mode-image_desc-linear_xfm':
(func_to_anat, 'outputspec.func_to_anat_linear_xfm_nobbreg'),
'from-bold_to-T1w_mode-image_desc-linear_warp':
(func_to_anat, 'outputspec.func_to_anat_linear_warp_nobbreg')
}
else:
outputs = {
'space-T1w_sbref':
(func_to_anat, 'outputspec.anat_func_nobbreg'),
'from-bold_to-T1w_mode-image_desc-linear_xfm':
(func_to_anat, 'outputspec.func_to_anat_linear_xfm_nobbreg')
}
if True in cfg.registration_workflows['functional_registration'][
'coregistration']["boundary_based_registration"]["run"]:
func_to_anat_bbreg = create_bbregister_func_to_anat(diff_complete,
f'func_to_anat_'
f'bbreg_'
f'{pipe_num}')
func_to_anat_bbreg.inputs.inputspec.bbr_schedule = \
cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration'][
'bbr_schedule']
func_to_anat_bbreg.inputs.inputspec.bbr_wm_mask_args = \
cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration'][
'bbr_wm_mask_args']
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, func_to_anat_bbreg, 'inputspec.func')
if cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration'][
'reference'] == 'whole-head':
node, out = strat_pool.get_data('desc-head_T1w')
wf.connect(node, out, func_to_anat_bbreg, 'inputspec.anat')
elif cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration'][
'reference'] == 'brain':
node, out = strat_pool.get_data('desc-preproc_T1w')
wf.connect(node, out, func_to_anat_bbreg, 'inputspec.anat')
wf.connect(func_to_anat, 'outputspec.func_to_anat_linear_xfm_nobbreg',
func_to_anat_bbreg, 'inputspec.linear_reg_matrix')
if strat_pool.check_rpool('space-bold_label-WM_mask'):
node, out = strat_pool.get_data(["space-bold_label-WM_mask"])
wf.connect(node, out,
func_to_anat_bbreg, 'inputspec.anat_wm_segmentation')
else:
if cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration']['bbr_wm_map'] == 'probability_map':
node, out = strat_pool.get_data(["label-WM_probseg",
"label-WM_mask"])
elif cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration']['bbr_wm_map'] == 'partial_volume_map':
node, out = strat_pool.get_data(["label-WM_pveseg",
"label-WM_mask"])
wf.connect(node, out,
func_to_anat_bbreg, 'inputspec.anat_wm_segmentation')
if diff_complete:
node, out = strat_pool.get_data('effectiveEchoSpacing')
wf.connect(node, out,
func_to_anat_bbreg, 'echospacing_input.echospacing')
node, out = strat_pool.get_data('pe-direction')
wf.connect(node, out, func_to_anat_bbreg, 'pedir_input.pedir')
node, out = strat_pool.get_data("despiked-fieldmap")
wf.connect(node, out, func_to_anat_bbreg, 'inputspec.fieldmap')
node, out = strat_pool.get_data("fieldmap-mask")
wf.connect(node, out,
func_to_anat_bbreg, 'inputspec.fieldmapmask')
outputs = {
'space-T1w_sbref':
(func_to_anat_bbreg, 'outputspec.anat_func'),
'from-bold_to-T1w_mode-image_desc-linear_xfm':
(func_to_anat_bbreg, 'outputspec.func_to_anat_linear_xfm')
}
return (wf, outputs)
@nodeblock(
name="create_func_to_T1template_xfm",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["target_template", "using"],
option_val="T1_template",
inputs=[
(
"sbref",
"from-bold_to-T1w_mode-image_desc-linear_xfm",
"ants-blip-warp",
"fsl-blip-warp",
),
(
"from-T1w_to-template_mode-image_xfm",
"from-template_to-T1w_mode-image_xfm",
"desc-brain_T1w",
),
"T1w-brain-template-funcreg",
],
outputs={
"from-bold_to-template_mode-image_xfm": {
"Template": "T1w-brain-template-funcreg"
},
"from-template_to-bold_mode-image_xfm": {
"Template": "T1w-brain-template-funcreg"
},
},
)
def create_func_to_T1template_xfm(wf, cfg, strat_pool, pipe_num, opt=None):
'''Condense the BOLD-to-T1 coregistration transform and the T1-to-template
transform into one transform matrix.
'''
xfm_prov = strat_pool.get_cpac_provenance(
'from-T1w_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
xfm, outputs = bold_to_T1template_xfm_connector('create_func_to_T1w'
f'template_xfm_{pipe_num}',
cfg, reg_tool,
symmetric=False)
node, out = strat_pool.get_data(
'from-bold_to-T1w_mode-image_desc-linear_xfm')
wf.connect(node, out, xfm, 'inputspec.coreg_xfm')
node, out = strat_pool.get_data('desc-brain_T1w')
wf.connect(node, out, xfm, 'inputspec.input_brain')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, xfm, 'inputspec.mean_bold')
node, out = strat_pool.get_data('T1w-brain-template-funcreg')
wf.connect(node, out, xfm, 'inputspec.T1w-brain-template_funcreg')
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_xfm')
wf.connect(node, out, xfm, 'inputspec.T1w_to_template_xfm')
# FNIRT pipelines don't have an inverse nonlinear warp, make optional
if strat_pool.check_rpool('from-template_to-T1w_mode-image_xfm'):
node, out = strat_pool.get_data('from-template_to-T1w_mode-image_xfm')
wf.connect(node, out, xfm, 'inputspec.template_to_T1w_xfm')
if strat_pool.check_rpool('ants-blip-warp'):
if reg_tool == 'ants':
node, out = strat_pool.get_data('ants-blip-warp')
wf.connect(node, out, xfm, 'inputspec.blip_warp')
elif reg_tool == 'fsl':
# apply the ants blip warp separately
pass
elif strat_pool.check_rpool('fsl-blip-warp'):
if reg_tool == 'fsl':
node, out = strat_pool.get_data('fsl-blip-warp')
wf.connect(node, out, xfm, 'inputspec.blip_warp')
elif reg_tool == 'ants':
# apply the fsl blip warp separately
pass
return (wf, outputs)
@nodeblock(
name="create_func_to_T1template_symmetric_xfm",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["target_template", "using"],
option_val="T1_template",
inputs=[
(
"from-T1w_to-symtemplate_mode-image_xfm",
"from-symtemplate_to-T1w_mode-image_xfm",
"desc-brain_T1w",
),
("sbref", "from-bold_to-T1w_mode-image_desc-linear_xfm"),
"T1w-brain-template-symmetric-deriv",
],
outputs={
"from-bold_to-symtemplate_mode-image_xfm": {
"Template": "T1w-brain-template-symmetric-deriv"
},
"from-symtemplate_to-bold_mode-image_xfm": {
"Template": "T1w-brain-template-symmetric-deriv"
},
},
)
def create_func_to_T1template_symmetric_xfm(wf, cfg, strat_pool, pipe_num,
opt=None):
'''Condense the BOLD-to-T1 coregistration transform and the T1-to-
symmetric-template transform into one transform matrix.
'''
xfm_prov = strat_pool.get_cpac_provenance(
'from-T1w_to-symtemplate_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
xfm, outputs = bold_to_T1template_xfm_connector('create_func_to_T1wsymtem'
f'plate_xfm_{pipe_num}',
cfg, reg_tool,
symmetric=True)
node, out = strat_pool.get_data(
'from-bold_to-T1w_mode-image_desc-linear_xfm')
wf.connect(node, out, xfm, 'inputspec.coreg_xfm')
node, out = strat_pool.get_data('desc-brain_T1w')
wf.connect(node, out, xfm, 'inputspec.input_brain')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, xfm, 'inputspec.mean_bold')
node, out = strat_pool.get_data('T1w-brain-template-symmetric-deriv')
wf.connect(node, out, xfm, 'inputspec.T1w-brain-template_funcreg')
node, out = strat_pool.get_data('from-T1w_to-symtemplate_mode-image_xfm')
wf.connect(node, out, xfm, 'inputspec.T1w_to_template_xfm')
# FNIRT pipelines don't have an inverse nonlinear warp, make optional
if strat_pool.check_rpool('from-symtemplate_to-T1w_mode-image_xfm'):
node, out = \
strat_pool.get_data('from-symtemplate_to-T1w_mode-image_xfm')
wf.connect(node, out, xfm, 'inputspec.template_to_T1w_xfm')
return (wf, outputs)
@nodeblock(
name="apply_phasediff_to_timeseries_separately",
switch=[
[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"run",
],
["functional_preproc", "distortion_correction", "run"],
],
option_key=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"apply_transform",
"using",
],
option_val=["default", "single_step_resampling_from_stc", "abcd"],
inputs=[
(
"sbref",
"desc-preproc_bold",
"desc-stc_bold",
"bold",
"from-bold_to-T1w_mode-image_desc-linear_xfm",
),
"despiked-fieldmap",
"pe-direction",
"effectiveEchoSpacing",
],
outputs=["sbref", "desc-preproc_bold", "desc-stc_bold", "bold"],
)
def apply_phasediff_to_timeseries_separately(wf, cfg, strat_pool, pipe_num,
opt=None):
outputs = {'desc-preproc_bold': strat_pool.get_data("desc-preproc_bold")}
if not strat_pool.check_rpool("despiked-fieldmap"):
return (wf, outputs)
invert_coreg_xfm = pe.Node(interface=fsl.ConvertXFM(),
name=f'invert_coreg_xfm_{pipe_num}')
invert_coreg_xfm.inputs.invert_xfm = True
node, out = strat_pool.get_data("from-bold_to-T1w_mode-image_desc-linear_xfm")
wf.connect(node, out, invert_coreg_xfm, 'in_file')
warp_fmap = pe.Node(interface=fsl.ApplyWarp(),
name=f'warp_fmap_{pipe_num}')
node, out = strat_pool.get_data('despiked-fieldmap')
wf.connect(node, out, warp_fmap, 'in_file')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, warp_fmap, 'ref_file')
wf.connect(invert_coreg_xfm, 'out_file', warp_fmap, 'premat')
mask_fmap = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'mask_fmap_{pipe_num}')
mask_fmap.inputs.args = '-abs -bin'
wf.connect(warp_fmap, 'out_file', mask_fmap, 'in_file')
conv_pedir = \
pe.Node(interface=util.Function(input_names=['pedir',
'convert'],
output_names=['pedir'],
function=convert_pedir),
name=f'apply_phasediff_convert_pedir_{pipe_num}')
conv_pedir.inputs.convert = 'ijk_to_xyz'
node, out = strat_pool.get_data('pe-direction')
wf.connect(node, out, conv_pedir, 'pedir')
fugue_saveshift = pe.Node(interface=fsl.FUGUE(),
name=f'fugue_saveshift_{pipe_num}')
fugue_saveshift.inputs.save_shift = True
wf.connect(warp_fmap, 'out_file', fugue_saveshift, 'fmap_in_file')
wf.connect(mask_fmap, 'out_file', fugue_saveshift, 'mask_file')
# FSL calls effective echo spacing = dwell time (not accurate)
node, out = strat_pool.get_data('effectiveEchoSpacing')
wf.connect(node, out, fugue_saveshift, 'dwell_time')
wf.connect(conv_pedir, 'pedir', fugue_saveshift, 'unwarp_direction')
shift_warp = pe.Node(interface=fsl.ConvertWarp(),
name=f'shift_warp_{pipe_num}')
shift_warp.inputs.out_relwarp = True
wf.connect(fugue_saveshift, 'shift_out_file', shift_warp, 'shift_in_file')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, shift_warp, 'reference')
wf.connect(conv_pedir, 'pedir', shift_warp, 'shift_direction')
warp_bold = pe.Node(interface=fsl.ApplyWarp(),
name=f'warp_bold_phasediff_{pipe_num}')
warp_bold.inputs.relwarp = True
warp_bold.inputs.interp = 'spline'
if opt == 'default':
node, out = strat_pool.get_data('desc-preproc_bold')
out_label = 'desc-preproc_bold'
elif opt == 'single_step_resampling_from_stc':
node, out = strat_pool.get_data('desc-stc_bold')
out_label = 'desc-stc_bold'
elif opt == 'abcd':
node, out = strat_pool.get_data('bold')
out_label = 'bold'
wf.connect(node, out, warp_bold, 'in_file')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, warp_bold, 'ref_file')
wf.connect(shift_warp, 'out_file', warp_bold, 'field_file')
warp_sbref = pe.Node(interface=fsl.ApplyWarp(),
name=f'warp_sbref_phasediff_{pipe_num}')
warp_sbref.inputs.relwarp = True
warp_sbref.inputs.interp = 'spline'
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, warp_sbref, 'in_file')
wf.connect(node, out, warp_sbref, 'ref_file')
wf.connect(shift_warp, 'out_file', warp_sbref, 'field_file')
outputs = {
out_label: (warp_bold, 'out_file'),
'sbref': (warp_sbref, 'out_file')
}
return (wf, outputs)
@nodeblock(
name="apply_blip_to_timeseries_separately",
switch=[
[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"run",
],
["functional_preproc", "distortion_correction", "run"],
],
option_key=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"apply_transform",
"using",
],
option_val=["default", "single_step_resampling_from_stc", "abcd"],
inputs=[
(
"sbref",
"desc-preproc_bold",
"desc-stc_bold",
"bold",
"from-bold_to-template_mode-image_xfm",
"ants-blip-warp",
"fsl-blip-warp",
)
],
outputs=["desc-preproc_bold", "desc-stc_bold", "bold"],
)
def apply_blip_to_timeseries_separately(wf, cfg, strat_pool, pipe_num,
opt=None):
xfm_prov = strat_pool.get_cpac_provenance(
'from-bold_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
outputs = {'desc-preproc_bold': strat_pool.get_data("desc-preproc_bold")}
if strat_pool.check_rpool("ants-blip-warp"):
if reg_tool == 'fsl':
blip_node, blip_out = strat_pool.get_data("ants-blip-warp")
reg_tool = 'ants'
else:
return (wf, outputs)
elif strat_pool.check_rpool("fsl-blip-warp"):
if reg_tool == 'ants':
blip_node, blip_out = strat_pool.get_data("fsl-blip-warp")
reg_tool = 'fsl'
else:
return (wf, outputs)
else:
return (wf, outputs)
num_cpus = cfg.pipeline_setup['system_config'][
'max_cores_per_participant']
num_ants_cores = cfg.pipeline_setup['system_config']['num_ants_threads']
apply_xfm = apply_transform(f'warp_ts_to_blip_sep_{pipe_num}', reg_tool,
time_series=True, num_cpus=num_cpus,
num_ants_cores=num_ants_cores)
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'ANTs_pipelines']['interpolation']
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'FNIRT_pipelines']['interpolation']
connect = strat_pool.get_data("desc-preproc_bold")
if opt == 'default':
node, out = strat_pool.get_data('desc-preproc_bold')
out_label = 'desc-preproc_bold'
elif opt == 'single_step_resampling_from_stc':
node, out = strat_pool.get_data('desc-stc_bold')
out_label = 'desc-stc_bold'
elif opt == 'abcd':
node, out = strat_pool.get_data('bold')
out_label = 'bold'
wf.connect(node, out, apply_xfm, 'inputspec.input_image')
node, out = strat_pool.get_data("sbref")
wf.connect(node, out, apply_xfm, 'inputspec.reference')
wf.connect(blip_node, blip_out, apply_xfm, 'inputspec.transform')
outputs = {
out_label: (apply_xfm, 'outputspec.output_image')
}
return (wf, outputs)
@nodeblock(
name="transform_whole_head_T1w_to_T1template",
config=["registration_workflows", "anatomical_registration"],
switch=["run"],
inputs=[
(
"desc-head_T1w",
"from-T1w_to-template_mode-image_xfm",
"space-template_desc-head_T1w",
),
"T1w-template",
],
outputs={"space-template_desc-head_T1w": {"Template": "T1w-template"}},
)
def warp_wholeheadT1_to_template(wf, cfg, strat_pool, pipe_num, opt=None):
xfm_prov = strat_pool.get_cpac_provenance(
'from-T1w_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
num_cpus = cfg.pipeline_setup['system_config'][
'max_cores_per_participant']
num_ants_cores = cfg.pipeline_setup['system_config']['num_ants_threads']
apply_xfm = apply_transform(f'warp_wholehead_T1w_to_T1template_{pipe_num}',
reg_tool, time_series=False, num_cpus=num_cpus,
num_ants_cores=num_ants_cores)
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'ANTs_pipelines']['interpolation']
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'FNIRT_pipelines']['interpolation']
connect = strat_pool.get_data("desc-head_T1w")
node, out = connect
wf.connect(node, out, apply_xfm, 'inputspec.input_image')
node, out = strat_pool.get_data("T1w-template")
wf.connect(node, out, apply_xfm, 'inputspec.reference')
node, out = strat_pool.get_data("from-T1w_to-template_mode-image_xfm")
wf.connect(node, out, apply_xfm, 'inputspec.transform')
outputs = {
'space-template_desc-head_T1w': (apply_xfm, 'outputspec.output_image')
}
return (wf, outputs)
@nodeblock(
name="transform_T1mask_to_T1template",
switch=[
["registration_workflows", "anatomical_registration", "run"],
["anatomical_preproc", "run"],
["anatomical_preproc", "brain_extraction", "run"],
],
inputs=[
("space-T1w_desc-brain_mask", "from-T1w_to-template_mode-image_xfm"),
"T1w-template",
],
outputs={"space-template_desc-brain_mask": {"Template": "T1w-template"}},
)
def warp_T1mask_to_template(wf, cfg, strat_pool, pipe_num, opt=None):
xfm_prov = strat_pool.get_cpac_provenance(
'from-T1w_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
num_cpus = cfg.pipeline_setup['system_config'][
'max_cores_per_participant']
num_ants_cores = cfg.pipeline_setup['system_config']['num_ants_threads']
apply_xfm = apply_transform(f'warp_T1mask_to_T1template_{pipe_num}',
reg_tool, time_series=False, num_cpus=num_cpus,
num_ants_cores=num_ants_cores)
apply_xfm.inputs.inputspec.interpolation = "NearestNeighbor"
'''
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'ANTs_pipelines']['interpolation']
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'FNIRT_pipelines']['interpolation']
'''
connect = strat_pool.get_data("space-T1w_desc-brain_mask")
node, out = connect
wf.connect(node, out, apply_xfm, 'inputspec.input_image')
node, out = strat_pool.get_data("T1w-template")
wf.connect(node, out, apply_xfm, 'inputspec.reference')
node, out = strat_pool.get_data("from-T1w_to-template_mode-image_xfm")
wf.connect(node, out, apply_xfm, 'inputspec.transform')
outputs = {
'space-template_desc-brain_mask': (apply_xfm, 'outputspec.output_image')
}
return (wf, outputs)
@nodeblock(
name="transform_timeseries_to_T1template",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["apply_transform", "using"],
option_val="default",
inputs=[
("desc-preproc_bold", "from-bold_to-template_mode-image_xfm"),
"T1w-brain-template-funcreg",
],
outputs={
"space-template_desc-preproc_bold": {
"Template": "T1w-brain-template-funcreg"}
},
)
def warp_timeseries_to_T1template(wf, cfg, strat_pool, pipe_num, opt=None):
xfm_prov = strat_pool.get_cpac_provenance(
'from-bold_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
num_cpus = cfg.pipeline_setup['system_config'][
'max_cores_per_participant']
num_ants_cores = cfg.pipeline_setup['system_config']['num_ants_threads']
apply_xfm = apply_transform(f'warp_ts_to_T1template_{pipe_num}', reg_tool,
time_series=True, num_cpus=num_cpus,
num_ants_cores=num_ants_cores)
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'ANTs_pipelines']['interpolation']
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'FNIRT_pipelines']['interpolation']
connect = strat_pool.get_data("desc-preproc_bold")
node, out = connect
wf.connect(node, out, apply_xfm, 'inputspec.input_image')
node, out = strat_pool.get_data("T1w-brain-template-funcreg")
wf.connect(node, out, apply_xfm, 'inputspec.reference')
node, out = strat_pool.get_data("from-bold_to-template_mode-image_xfm")
wf.connect(node, out, apply_xfm, 'inputspec.transform')
outputs = {
'space-template_desc-preproc_bold': (apply_xfm, 'outputspec.output_image')
}
return (wf, outputs)
@nodeblock(
name="transform_timeseries_to_T1template_deriv",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["apply_transform", "using"],
option_val="default",
inputs=[
("desc-preproc_bold", "from-bold_to-template_mode-image_xfm"),
"T1w-brain-template-funcreg",
],
outputs={
"space-template_res-derivative_desc-preproc_bold": {
"Template": "T1w-brain-template-deriv"
}
},
)
def warp_timeseries_to_T1template_deriv(wf, cfg, strat_pool, pipe_num,
opt=None):
xfm_prov = strat_pool.get_cpac_provenance(
'from-bold_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
num_cpus = cfg.pipeline_setup['system_config'][
'max_cores_per_participant']
num_ants_cores = cfg.pipeline_setup['system_config']['num_ants_threads']
apply_xfm = apply_transform(f'warp_ts_to_T1template_{pipe_num}', reg_tool,
time_series=True, num_cpus=num_cpus,
num_ants_cores=num_ants_cores)
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'ANTs_pipelines']['interpolation']
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = cfg.registration_workflows[
'functional_registration']['func_registration_to_template'][
'FNIRT_pipelines']['interpolation']
connect = strat_pool.get_data("desc-preproc_bold")
node, out = connect
wf.connect(node, out, apply_xfm, 'inputspec.input_image')
node, out = strat_pool.get_data("T1w-brain-template-deriv")
wf.connect(node, out, apply_xfm, 'inputspec.reference')
node, out = strat_pool.get_data("from-bold_to-template_mode-image_xfm")
wf.connect(node, out, apply_xfm, 'inputspec.transform')
outputs = {
'space-template_res-derivative_desc-preproc_bold':
(apply_xfm, 'outputspec.output_image')
}
return (wf, outputs)
@nodeblock(
name="transform_timeseries_to_T1template_abcd",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["apply_transform", "using"],
option_val="abcd",
inputs=[
("desc-preproc_bold", "bold", "motion-basefile",
"coordinate-transformation"),
"from-T1w_to-template_mode-image_xfm",
"from-bold_to-T1w_mode-image_desc-linear_xfm",
"from-bold_to-template_mode-image_xfm",
"fsl-blip-warp",
"desc-preproc_T1w",
"space-template_res-bold_desc-brain_T1w",
"space-template_desc-bold_mask",
"T1w-brain-template-funcreg",
],
outputs={
"space-template_desc-preproc_bold": {
"Template": "T1w-brain-template-funcreg"},
"space-template_desc-scout_bold": {
"Template": "T1w-brain-template-funcreg"},
"space-template_desc-head_bold": {
"Template": "T1w-brain-template-funcreg"},
},
)
def warp_timeseries_to_T1template_abcd(wf, cfg, strat_pool, pipe_num, opt=None
):
# Apply motion correction, coreg, anat-to-template transforms on raw functional timeseries using ABCD-style registration
# Ref: https://github.com/DCAN-Labs/DCAN-HCP/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L168-L197
# https://github.com/DCAN-Labs/DCAN-HCP/blob/master/fMRIVolume/scripts/DistortionCorrectionAndEPIToT1wReg_FLIRTBBRAndFreeSurferBBRbased.sh#L548
# convertwarp --relout --rel -m ${WD}/fMRI2str.mat --ref=${T1wImage} --out=${WD}/fMRI2str.nii.gz
convert_func_to_anat_linear_warp = pe.Node(interface=fsl.ConvertWarp(),
name=f'convert_func_to_anat_linear_warp_{pipe_num}')
convert_func_to_anat_linear_warp.inputs.out_relwarp = True
convert_func_to_anat_linear_warp.inputs.relwarp = True
node, out = strat_pool.get_data('desc-preproc_T1w')
wf.connect(node, out, convert_func_to_anat_linear_warp, 'reference')
if strat_pool.check_rpool('fsl-blip-warp'):
node, out = strat_pool.get_data('from-bold_to-T1w_mode-image_desc-linear_xfm')
wf.connect(node, out, convert_func_to_anat_linear_warp, 'postmat')
node, out = strat_pool.get_data('fsl-blip-warp')
wf.connect(node, out, convert_func_to_anat_linear_warp, 'warp1')
else:
node, out = strat_pool.get_data('from-bold_to-T1w_mode-image_desc-linear_xfm')
wf.connect(node, out, convert_func_to_anat_linear_warp, 'premat')
# https://github.com/DCAN-Labs/DCAN-HCP/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L140
# convertwarp --relout --rel --warp1=${fMRIToStructuralInput} --warp2=${StructuralToStandard} --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --out=${OutputTransform}
convert_func_to_standard_warp = pe.Node(interface=fsl.ConvertWarp(),
name=f'convert_func_to_standard_warp_{pipe_num}')
convert_func_to_standard_warp.inputs.out_relwarp = True
convert_func_to_standard_warp.inputs.relwarp = True
wf.connect(convert_func_to_anat_linear_warp, 'out_file',
convert_func_to_standard_warp, 'warp1')
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_xfm')
wf.connect(node, out, convert_func_to_standard_warp, 'warp2')
node, out = strat_pool.get_data('space-template_res-bold_desc-brain_T1w')
wf.connect(node, out, convert_func_to_standard_warp, 'reference')
# TODO add condition: if no gradient distortion
# https://github.com/DCAN-Labs/DCAN-HCP/blob/master/fMRIVolume/GenericfMRIVolumeProcessingPipeline.sh#L283-L284
# fslroi "$fMRIFolder"/"$NameOffMRI"_gdc "$fMRIFolder"/"$NameOffMRI"_gdc_warp 0 3
extract_func_roi = pe.Node(interface=fsl.ExtractROI(),
name=f'extract_func_roi_{pipe_num}')
extract_func_roi.inputs.t_min = 0
extract_func_roi.inputs.t_size = 3
node, out = strat_pool.get_data('bold')
wf.connect(node, out, extract_func_roi, 'in_file')
# fslmaths "$fMRIFolder"/"$NameOffMRI"_gdc_warp -mul 0 "$fMRIFolder"/"$NameOffMRI"_gdc_warp
multiply_func_roi_by_zero = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'multiply_func_roi_by_zero_{pipe_num}')
multiply_func_roi_by_zero.inputs.args = '-mul 0'
wf.connect(extract_func_roi, 'roi_file',
multiply_func_roi_by_zero, 'in_file')
# https://github.com/DCAN-Labs/DCAN-HCP/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L168-L193
# fslsplit ${InputfMRI} ${WD}/prevols/vol -t
split_func = pe.Node(interface=fsl.Split(),
name=f'split_func_{pipe_num}')
split_func.inputs.dimension = 't'
node, out = strat_pool.get_data('bold')
wf.connect(node, out, split_func, 'in_file')
### Loop starts! ###
# convertwarp --relout --rel --ref=${WD}/prevols/vol${vnum}.nii.gz --warp1=${GradientDistortionField} --postmat=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum} --out=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_gdc_warp.nii.gz
convert_motion_distortion_warp = pe.MapNode(interface=fsl.ConvertWarp(),
name=f'convert_motion_distortion_warp_{pipe_num}',
iterfield=['reference', 'postmat'])
convert_motion_distortion_warp.inputs.out_relwarp = True
convert_motion_distortion_warp.inputs.relwarp = True
wf.connect(multiply_func_roi_by_zero, 'out_file',
convert_motion_distortion_warp, 'warp1')
wf.connect(split_func, 'out_files',
convert_motion_distortion_warp, 'reference')
node, out = strat_pool.get_data('coordinate-transformation')
wf.connect(node, out, convert_motion_distortion_warp, 'postmat')
# convertwarp --relout --rel --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --warp1=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_gdc_warp.nii.gz --warp2=${OutputTransform} --out=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_all_warp.nii.gz
convert_registration_warp = pe.MapNode(interface=fsl.ConvertWarp(),
name=f'convert_registration_warp_{pipe_num}',
iterfield=['warp1'])
convert_registration_warp.inputs.out_relwarp = True
convert_registration_warp.inputs.relwarp = True
node, out = strat_pool.get_data('space-template_res-bold_desc-brain_T1w')
wf.connect(node, out, convert_registration_warp, 'reference')
wf.connect(convert_motion_distortion_warp, 'out_file',
convert_registration_warp, 'warp1')
wf.connect(convert_func_to_standard_warp, 'out_file',
convert_registration_warp, 'warp2')
# fslmaths ${WD}/prevols/vol${vnum}.nii.gz -mul 0 -add 1 ${WD}/prevols/vol${vnum}_mask.nii.gz
generate_vol_mask = pe.MapNode(interface=fsl.maths.MathsCommand(),
name=f'generate_mask_{pipe_num}',
iterfield=['in_file'])
generate_vol_mask.inputs.args = '-mul 0 -add 1'
wf.connect(split_func, 'out_files',
generate_vol_mask, 'in_file')
# applywarp --rel --interp=spline --in=${WD}/prevols/vol${vnum}.nii.gz --warp=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_all_warp.nii.gz --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --out=${WD}/postvols/vol${vnum}.nii.gz
applywarp_func_to_standard = pe.MapNode(interface=fsl.ApplyWarp(),
name=f'applywarp_func_to_standard_{pipe_num}',
iterfield=['in_file', 'field_file'])
applywarp_func_to_standard.inputs.relwarp = True
applywarp_func_to_standard.inputs.interp = 'spline'
wf.connect(split_func, 'out_files',
applywarp_func_to_standard, 'in_file')
wf.connect(convert_registration_warp, 'out_file',
applywarp_func_to_standard, 'field_file')
node, out = strat_pool.get_data('space-template_res-bold_desc-brain_T1w')
wf.connect(node, out,
applywarp_func_to_standard, 'ref_file')
# applywarp --rel --interp=nn --in=${WD}/prevols/vol${vnum}_mask.nii.gz --warp=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_all_warp.nii.gz --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --out=${WD}/postvols/vol${vnum}_mask.nii.gz
applywarp_func_mask_to_standard = pe.MapNode(interface=fsl.ApplyWarp(),
name=f'applywarp_func_mask_to_standard_{pipe_num}',
iterfield=['in_file', 'field_file'])
applywarp_func_mask_to_standard.inputs.relwarp = True
applywarp_func_mask_to_standard.inputs.interp = 'nn'
wf.connect(generate_vol_mask, 'out_file',
applywarp_func_mask_to_standard, 'in_file')
wf.connect(convert_registration_warp, 'out_file',
applywarp_func_mask_to_standard, 'field_file')
node, out = strat_pool.get_data('space-template_res-bold_desc-brain_T1w')
wf.connect(node, out,
applywarp_func_mask_to_standard, 'ref_file')
### Loop ends! ###
# fslmerge -tr ${OutputfMRI} $FrameMergeSTRING $TR_vol
merge_func_to_standard = pe.Node(interface=fslMerge(),
name=f'merge_func_to_standard_{pipe_num}')
merge_func_to_standard.inputs.dimension = 't'
wf.connect(applywarp_func_to_standard, 'out_file',
merge_func_to_standard, 'in_files')
# fslmerge -tr ${OutputfMRI}_mask $FrameMergeSTRINGII $TR_vol
merge_func_mask_to_standard = pe.Node(interface=fslMerge(),
name='merge_func_mask_to_'
f'standard_{pipe_num}')
merge_func_mask_to_standard.inputs.dimension = 't'
wf.connect(applywarp_func_mask_to_standard, 'out_file',
merge_func_mask_to_standard, 'in_files')
# fslmaths ${OutputfMRI}_mask -Tmin ${OutputfMRI}_mask
find_min_mask = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'find_min_mask_{pipe_num}')
find_min_mask.inputs.args = '-Tmin'
wf.connect(merge_func_mask_to_standard, 'merged_file',
find_min_mask, 'in_file')
# Combine transformations: gradient non-linearity distortion + fMRI_dc to standard
# convertwarp --relout --rel --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --warp1=${GradientDistortionField} --warp2=${OutputTransform} --out=${WD}/Scout_gdc_MNI_warp.nii.gz
convert_dc_warp = pe.Node(interface=fsl.ConvertWarp(),
name=f'convert_dc_warp_{pipe_num}')
convert_dc_warp.inputs.out_relwarp = True
convert_dc_warp.inputs.relwarp = True
node, out = strat_pool.get_data('space-template_res-bold_desc-brain_T1w')
wf.connect(node, out, convert_dc_warp, 'reference')
wf.connect(multiply_func_roi_by_zero, 'out_file',
convert_dc_warp, 'warp1')
wf.connect(convert_func_to_standard_warp, 'out_file',
convert_dc_warp, 'warp2')
# applywarp --rel --interp=spline --in=${ScoutInput} -w ${WD}/Scout_gdc_MNI_warp.nii.gz -r ${WD}/${T1wImageFile}.${FinalfMRIResolution} -o ${ScoutOutput}
applywarp_scout = pe.Node(interface=fsl.ApplyWarp(),
name=f'applywarp_scout_input_{pipe_num}')
applywarp_scout.inputs.relwarp = True
applywarp_scout.inputs.interp = 'spline'
node, out = strat_pool.get_data('motion-basefile')
wf.connect(node, out, applywarp_scout, 'in_file')
node, out = strat_pool.get_data('space-template_res-bold_desc-brain_T1w')
wf.connect(node, out, applywarp_scout, 'ref_file')
wf.connect(convert_dc_warp, 'out_file', applywarp_scout, 'field_file')
# https://github.com/DCAN-Labs/DCAN-HCP/blob/master/fMRIVolume/scripts/IntensityNormalization.sh#L124-L127
# fslmaths ${InputfMRI} -mas ${BrainMask} -mas ${InputfMRI}_mask -thr 0 -ing 10000 ${OutputfMRI} -odt float
merge_func_mask = pe.Node(util.Merge(2),
name=f'merge_func_mask_{pipe_num}')
node, out = strat_pool.get_data('space-template_desc-bold_mask')
wf.connect(node, out, merge_func_mask, 'in1')
wf.connect(find_min_mask, 'out_file', merge_func_mask, 'in2')
extract_func_brain = pe.Node(interface=fsl.MultiImageMaths(),
name=f'extract_func_brain_{pipe_num}')
extract_func_brain.inputs.op_string = '-mas %s -mas %s -thr 0 -ing 10000'
extract_func_brain.inputs.output_datatype = 'float'
wf.connect(merge_func_to_standard, 'merged_file',
extract_func_brain, 'in_file')
wf.connect(merge_func_mask, 'out',
extract_func_brain, 'operand_files')
# fslmaths ${ScoutInput} -mas ${BrainMask} -mas ${InputfMRI}_mask -thr 0 -ing 10000 ${ScoutOutput} -odt float
extract_scout_brain = pe.Node(interface=fsl.MultiImageMaths(),
name=f'extract_scout_brain_{pipe_num}')
extract_scout_brain.inputs.op_string = '-mas %s -mas %s -thr 0 -ing 10000'
extract_scout_brain.inputs.output_datatype = 'float'
wf.connect(applywarp_scout, 'out_file',
extract_scout_brain, 'in_file')
wf.connect(merge_func_mask, 'out',
extract_scout_brain, 'operand_files')
outputs = {
'space-template_desc-preproc_bold': (extract_func_brain, 'out_file'),
'space-template_desc-scout_bold': (extract_scout_brain, 'out_file'),
'space-template_desc-head_bold': (merge_func_to_standard, 'merged_file')
}
return (wf, outputs)
@nodeblock(
name="transform_timeseries_to_T1template_dcan_nhp",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["apply_transform", "using"],
option_val="dcan_nhp",
inputs=[
(
["desc-reorient_bold", "bold"],
"coordinate-transformation",
"from-T1w_to-template_mode-image_warp",
"from-bold_to-T1w_mode-image_desc-linear_warp",
"T1w-template",
"space-template_desc-head_T1w",
"space-template_desc-T1w_mask",
"space-template_desc-T1wT2w_biasfield",
)
],
outputs={
"space-template_desc-preproc_bold": {"Template": "T1w-template"},
"space-template_desc-bold_mask": {"Template": "T1w-template"},
},
)
def warp_timeseries_to_T1template_dcan_nhp(wf, cfg, strat_pool, pipe_num,
opt=None):
# Apply motion correction, coreg, anat-to-template transforms on raw functional timeseries
# Ref: https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/scripts/OneStepResampling.sh
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L131
# ${FSLDIR}/bin/flirt -interp spline -in ${T1wImage} -ref ${T1wImage} -applyisoxfm $FinalfMRIResolution -out ${WD}/${T1wImageFile}.${FinalfMRIResolution}
anat_resample = pe.Node(interface=fsl.FLIRT(),
name=f'anat_resample_func_res_{pipe_num}'
)
anat_resample.inputs.apply_isoxfm = float(cfg.registration_workflows['functional_registration']['func_registration_to_template']['output_resolution']['func_preproc_outputs'].replace("mm", ""))
anat_resample.inputs.interp = 'spline'
node, out = strat_pool.get_data('space-template_desc-head_T1w')
wf.connect(node, out, anat_resample, 'in_file')
wf.connect(node, out, anat_resample, 'reference')
# ${FSLDIR}/bin/applywarp --rel --interp=spline -i ${T1wImage} -r ${ResampRefIm} --premat=$FSLDIR/etc/flirtsch/ident.mat -o ${WD}/${T1wImageFile}.${FinalfMRIResolution}
applywarp_anat_res = pe.Node(interface=fsl.ApplyWarp(),
name=f'anat_func_res_{pipe_num}')
applywarp_anat_res.inputs.relwarp = True
applywarp_anat_res.inputs.interp = 'spline'
applywarp_anat_res.inputs.premat = cfg.registration_workflows['anatomical_registration']['registration']['FSL-FNIRT']['identity_matrix']
node, out = strat_pool.get_data('space-template_desc-head_T1w')
wf.connect(node, out, applywarp_anat_res, 'in_file')
wf.connect(anat_resample, 'out_file', applywarp_anat_res, 'ref_file')
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L136-L138
# Create brain masks in this space (changing resolution)
# ${FSLDIR}/bin/applywarp --rel --interp=nn -i ${FreeSurferBrainMask}.nii.gz -r ${WD}/${T1wImageFile}.${FinalfMRIResolution} --premat=$FSLDIR/etc/flirtsch/ident.mat -o ${WD}/${FreeSurferBrainMaskFile}.${FinalfMRIResolution}.nii.gz
applywarp_anat_mask_res = pe.Node(interface=fsl.ApplyWarp(),
name=f'anat_mask_func_res_{pipe_num}')
applywarp_anat_mask_res.inputs.relwarp = True
applywarp_anat_mask_res.inputs.interp = 'nn'
applywarp_anat_mask_res.inputs.premat = cfg.registration_workflows['anatomical_registration']['registration']['FSL-FNIRT']['identity_matrix']
node, out = strat_pool.get_data('space-template_desc-T1w_mask')
wf.connect(node, out, applywarp_anat_mask_res, 'in_file')
wf.connect(applywarp_anat_res, 'out_file', applywarp_anat_mask_res, 'ref_file')
# ${FSLDIR}/bin/fslmaths ${WD}/${T1wImageFile}.${FinalfMRIResolution} -mas ${WD}/${FreeSurferBrainMaskFile}.${FinalfMRIResolution}.nii.gz ${WD}/${FreeSurferBrainMaskFile}.${FinalfMRIResolution}.nii.gz
T1_brain_res = pe.Node(interface=fsl.MultiImageMaths(),
name=f't1_brain_func_res_{pipe_num}')
T1_brain_res.inputs.op_string = "-mas %s "
wf.connect(applywarp_anat_res, 'out_file', T1_brain_res, 'in_file')
wf.connect(applywarp_anat_mask_res, 'out_file', T1_brain_res, 'operand_files')
# Create versions of the biasfield (changing resolution)
# ${FSLDIR}/bin/applywarp --rel --interp=spline -i ${BiasField} -r ${WD}/${FreeSurferBrainMaskFile}.${FinalfMRIResolution}.nii.gz --premat=$FSLDIR/etc/flirtsch/ident.mat -o ${WD}/${BiasFieldFile}.${FinalfMRIResolution}
applywarp_bias_field_res = pe.Node(interface=fsl.ApplyWarp(),
name=f'biasfiled_func_res_{pipe_num}')
applywarp_bias_field_res.inputs.relwarp = True
applywarp_bias_field_res.inputs.interp = 'spline'
applywarp_bias_field_res.inputs.premat = cfg.registration_workflows['anatomical_registration']['registration']['FSL-FNIRT']['identity_matrix']
node, out = strat_pool.get_data('space-template_desc-T1wT2w_biasfield')
wf.connect(node, out, applywarp_bias_field_res, 'in_file')
wf.connect(T1_brain_res, 'out_file', applywarp_bias_field_res, 'ref_file')
# ${FSLDIR}/bin/fslmaths ${WD}/${BiasFieldFile}.${FinalfMRIResolution} -thr 0.1 ${WD}/${BiasFieldFile}.${FinalfMRIResolution}
biasfield_thr = pe.Node(interface=fsl.MultiImageMaths(),
name=f'biasfiedl_thr_{pipe_num}')
biasfield_thr.inputs.op_string = "-thr 0.1"
wf.connect(applywarp_bias_field_res, 'out_file', biasfield_thr, 'in_file')
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L144-L146
# convertwarp --relout --rel --warp1=${fMRIToStructuralInput} --warp2=${StructuralToStandard} --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --out=${OutputTransform}
convert_func_to_standard_warp = pe.Node(interface=fsl.ConvertWarp(),
name=f'convert_func_to_standard_warp_{pipe_num}')
convert_func_to_standard_warp.inputs.out_relwarp = True
convert_func_to_standard_warp.inputs.relwarp = True
node, out = strat_pool.get_data('from-bold_to-T1w_mode-image_desc-linear_warp')
wf.connect(node, out, convert_func_to_standard_warp, 'warp1')
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_warp')
wf.connect(node, out, convert_func_to_standard_warp, 'warp2')
wf.connect(applywarp_anat_res, 'out_file', convert_func_to_standard_warp, 'reference')
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/GenericfMRIVolumeProcessingPipeline.sh#L157-L158
# fslroi "$fMRIFolder"/"$NameOffMRI"_gdc "$fMRIFolder"/"$NameOffMRI"_gdc_warp 0 3
extract_func_roi = pe.Node(interface=fsl.ExtractROI(),
name=f'extract_func_roi_{pipe_num}')
extract_func_roi.inputs.t_min = 0
extract_func_roi.inputs.t_size = 3
node, out = strat_pool.get_data(['desc-reorient_bold', 'bold'])
wf.connect(node, out, extract_func_roi, 'in_file')
# fslmaths "$fMRIFolder"/"$NameOffMRI"_gdc_warp -mul 0 "$fMRIFolder"/"$NameOffMRI"_gdc_warp
multiply_func_roi_by_zero = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'multiply_func_roi_by_zero_{pipe_num}')
multiply_func_roi_by_zero.inputs.args = '-mul 0'
wf.connect(extract_func_roi, 'roi_file',
multiply_func_roi_by_zero, 'in_file')
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/scripts/OneStepResampling.sh#L173
# fslsplit ${InputfMRI} ${WD}/prevols/vol -t
split_func = pe.Node(interface=fsl.Split(),
name=f'split_func_{pipe_num}')
split_func.inputs.dimension = 't'
node, out = strat_pool.get_data(['desc-reorient_bold', 'bold'])
wf.connect(node, out, split_func, 'in_file')
### Loop starts! ###
# convertwarp --relout --rel --ref=${WD}/prevols/vol${vnum}.nii.gz --warp1=${GradientDistortionField} --postmat=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum} --out=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_gdc_warp.nii.gz
convert_motion_distortion_warp = pe.MapNode(interface=fsl.ConvertWarp(),
name=f'convert_motion_distortion_warp_{pipe_num}',
iterfield=['reference', 'postmat'])
convert_motion_distortion_warp.inputs.out_relwarp = True
convert_motion_distortion_warp.inputs.relwarp = True
wf.connect(multiply_func_roi_by_zero, 'out_file',
convert_motion_distortion_warp, 'warp1')
wf.connect(split_func, 'out_files',
convert_motion_distortion_warp, 'reference')
node, out = strat_pool.get_data('coordinate-transformation')
wf.connect(node, out, convert_motion_distortion_warp, 'postmat')
# convertwarp --relout --rel --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --warp1=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_gdc_warp.nii.gz --warp2=${OutputTransform} --out=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_all_warp.nii.gz
convert_registration_warp = pe.MapNode(interface=fsl.ConvertWarp(),
name=f'convert_registration_warp_{pipe_num}',
iterfield=['warp1'])
convert_registration_warp.inputs.out_relwarp = True
convert_registration_warp.inputs.relwarp = True
wf.connect(applywarp_anat_res, 'out_file', convert_registration_warp, 'reference')
wf.connect(convert_motion_distortion_warp, 'out_file',
convert_registration_warp, 'warp1')
wf.connect(convert_func_to_standard_warp, 'out_file',
convert_registration_warp, 'warp2')
# fslmaths ${WD}/prevols/vol${vnum}.nii.gz -mul 0 -add 1 ${WD}/prevols/vol${vnum}_mask.nii.gz
generate_vol_mask = pe.MapNode(interface=fsl.maths.MathsCommand(),
name=f'generate_mask_{pipe_num}',
iterfield=['in_file'])
generate_vol_mask.inputs.args = '-mul 0 -add 1'
wf.connect(split_func, 'out_files',
generate_vol_mask, 'in_file')
# applywarp --rel --interp=spline --in=${WD}/prevols/vol${vnum}.nii.gz --warp=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_all_warp.nii.gz --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --out=${WD}/postvols/vol${vnum}.nii.gz
applywarp_func_to_standard = pe.MapNode(interface=fsl.ApplyWarp(),
name=f'applywarp_func_to_standard_{pipe_num}',
iterfield=['in_file', 'field_file'])
applywarp_func_to_standard.inputs.relwarp = True
applywarp_func_to_standard.inputs.interp = 'spline'
wf.connect(split_func, 'out_files',
applywarp_func_to_standard, 'in_file')
wf.connect(convert_registration_warp, 'out_file',
applywarp_func_to_standard, 'field_file')
wf.connect(applywarp_anat_res, 'out_file',
applywarp_func_to_standard, 'ref_file')
# applywarp --rel --interp=nn --in=${WD}/prevols/vol${vnum}_mask.nii.gz --warp=${MotionMatrixFolder}/${MotionMatrixPrefix}${vnum}_all_warp.nii.gz --ref=${WD}/${T1wImageFile}.${FinalfMRIResolution} --out=${WD}/postvols/vol${vnum}_mask.nii.gz
applywarp_func_mask_to_standard = pe.MapNode(interface=fsl.ApplyWarp(),
name=f'applywarp_func_mask_to_standard_{pipe_num}',
iterfield=['in_file', 'field_file'])
applywarp_func_mask_to_standard.inputs.relwarp = True
applywarp_func_mask_to_standard.inputs.interp = 'nn'
wf.connect(generate_vol_mask, 'out_file',
applywarp_func_mask_to_standard, 'in_file')
wf.connect(convert_registration_warp, 'out_file',
applywarp_func_mask_to_standard, 'field_file')
wf.connect(applywarp_anat_res, 'out_file',
applywarp_func_mask_to_standard, 'ref_file')
### Loop ends! ###
# fslmerge -tr ${OutputfMRI} $FrameMergeSTRING $TR_vol
merge_func_to_standard = pe.Node(interface=fslMerge(),
name=f'merge_func_to_standard_{pipe_num}')
merge_func_to_standard.inputs.dimension = 't'
wf.connect(applywarp_func_to_standard, 'out_file',
merge_func_to_standard, 'in_files')
# fslmerge -tr ${OutputfMRI}_mask $FrameMergeSTRINGII $TR_vol
merge_func_mask_to_standard = pe.Node(interface=fslMerge(),
name='merge_func_mask_to_'
f'standard_{pipe_num}')
merge_func_mask_to_standard.inputs.dimension = 't'
wf.connect(applywarp_func_mask_to_standard, 'out_file',
merge_func_mask_to_standard, 'in_files')
# fslmaths ${OutputfMRI}_mask -Tmin ${OutputfMRI}_mask
find_min_mask = pe.Node(interface=fsl.maths.MathsCommand(),
name=f'find_min_mask_{pipe_num}')
find_min_mask.inputs.args = '-Tmin'
wf.connect(merge_func_mask_to_standard, 'merged_file',
find_min_mask, 'in_file')
# https://github.com/DCAN-Labs/dcan-macaque-pipeline/blob/master/fMRIVolume/scripts/IntensityNormalization.sh#L113-L119
# fslmaths ${InputfMRI} -div ${BiasField} $jacobiancom -mas ${BrainMask} -mas ${InputfMRI}_mask -ing 10000 ${OutputfMRI} -odt float
merge_func_mask = pe.Node(util.Merge(3),
name=f'merge_operand_files_{pipe_num}')
wf.connect(biasfield_thr, 'out_file', merge_func_mask, 'in1')
wf.connect(applywarp_anat_mask_res, 'out_file', merge_func_mask, 'in2')
wf.connect(find_min_mask, 'out_file', merge_func_mask, 'in3')
extract_func_brain = pe.Node(interface=fsl.MultiImageMaths(),
name=f'extract_func_brain_{pipe_num}')
extract_func_brain.inputs.op_string = '-div %s -mas %s -mas %s -ing 10000'
extract_func_brain.inputs.output_datatype = 'float'
wf.connect(merge_func_to_standard, 'merged_file',
extract_func_brain, 'in_file')
wf.connect(merge_func_mask, 'out',
extract_func_brain, 'operand_files')
func_mask_final = pe.Node(interface=fsl.MultiImageMaths(),
name=f'func_mask_final_{pipe_num}')
func_mask_final.inputs.op_string = "-mas %s "
wf.connect(applywarp_anat_mask_res, 'out_file', func_mask_final, 'in_file')
wf.connect(find_min_mask, 'out_file', func_mask_final, 'operand_files')
outputs = {
'space-template_desc-preproc_bold': (extract_func_brain, 'out_file'),
'space-template_desc-bold_mask': (func_mask_final, 'out_file')
}
return (wf, outputs)
@nodeblock(
name="single_step_resample_stc_timeseries_to_T1template",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run"],
option_key=["apply_transform", "using"],
option_val="single_step_resampling_from_stc",
inputs=[
(
"sbref",
"desc-stc_bold",
"motion-basefile",
"space-bold_desc-brain_mask",
"coordinate-transformation",
"from-T1w_to-template_mode-image_xfm",
"from-bold_to-T1w_mode-image_desc-linear_xfm",
"from-bold_to-template_mode-image_xfm",
"ants-blip-warp",
"fsl-blip-warp",
"T1w",
"desc-preproc_T1w",
"T1w-brain-template-funcreg",
"T1w-brain-template-deriv",
)
],
outputs={
"space-template_desc-preproc_bold": {
"Template": "T1w-brain-template-funcreg"},
"space-template_desc-brain_bold": {
"Template": "T1w-brain-template-funcreg"},
"space-template_desc-bold_mask": {
"Template": "T1w-brain-template-funcreg"},
"space-template_desc-head_bold": {
"Template": "T1w-brain-template-funcreg"},
"space-template_res-derivative_desc-preproc_bold": {
"Template": "T1w-brain-template-deriv"
},
"space-template_res-derivative_desc-bold_mask": {
"Template": "T1w-brain-template-deriv"
},
},
)
def single_step_resample_timeseries_to_T1template(wf, cfg, strat_pool,
pipe_num, opt=None):
'''
Apply motion correction, coreg, anat-to-template transforms on
slice-time corrected functional timeseries based on fMRIPrep
pipeline
Copyright (c) 2015-2018, the CRN developers team.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of fmriprep nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
Ref: https://github.com/nipreps/fmriprep/blob/84a6005b/fmriprep/workflows/bold/resampling.py#L159-L419
''' # noqa: 501
xfm_prov = strat_pool.get_cpac_provenance(
'from-T1w_to-template_mode-image_xfm')
reg_tool = check_prov_for_regtool(xfm_prov)
bbr2itk = pe.Node(util.Function(input_names=['reference_file',
'source_file',
'transform_file'],
output_names=['itk_transform'],
function=run_c3d),
name=f'convert_bbr2itk_{pipe_num}')
if cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration'][
'reference'] == 'whole-head':
node, out = strat_pool.get_data('T1w')
wf.connect(node, out, bbr2itk, 'reference_file')
elif cfg.registration_workflows['functional_registration'][
'coregistration']['boundary_based_registration'][
'reference'] == 'brain':
node, out = strat_pool.get_data('desc-preproc_T1w')
wf.connect(node, out, bbr2itk, 'reference_file')
node, out = strat_pool.get_data('sbref')
wf.connect(node, out, bbr2itk, 'source_file')
node, out = strat_pool.get_data(
'from-bold_to-T1w_mode-image_desc-linear_xfm')
wf.connect(node, out, bbr2itk, 'transform_file')
split_func = pe.Node(interface=fsl.Split(),
name=f'split_func_{pipe_num}')
split_func.inputs.dimension = 't'
node, out = strat_pool.get_data('desc-stc_bold')
wf.connect(node, out, split_func, 'in_file')
### Loop starts! ###
motionxfm2itk = pe.MapNode(util.Function(
input_names=['reference_file',
'source_file',
'transform_file'],
output_names=['itk_transform'],
function=run_c3d),
name=f'convert_motionxfm2itk_{pipe_num}',
iterfield=['transform_file'])
node, out = strat_pool.get_data('motion-basefile')
wf.connect(node, out, motionxfm2itk, 'reference_file')
wf.connect(node, out, motionxfm2itk, 'source_file')
node, out = strat_pool.get_data('coordinate-transformation')
motion_correct_tool = check_prov_for_motion_tool(
strat_pool.get_cpac_provenance('coordinate-transformation'))
if motion_correct_tool == 'mcflirt':
wf.connect(node, out, motionxfm2itk, 'transform_file')
elif motion_correct_tool == '3dvolreg':
convert_transform = pe.Node(util.Function(
input_names=['one_d_filename'],
output_names=['transform_directory'],
function=one_d_to_mat,
imports=['import os', 'import numpy as np']),
name=f'convert_transform_{pipe_num}')
wf.connect(node, out, convert_transform, 'one_d_filename')
wf.connect(convert_transform, 'transform_directory',
motionxfm2itk, 'transform_file')
merge_num = 4
blip = False
if strat_pool.check_rpool('ants-blip-warp') and reg_tool == 'ants':
blip_node, blip_out = strat_pool.get_data('ants-blip-warp')
merge_num = 5
blip = True
elif strat_pool.check_rpool('fsl-blip-warp') and reg_tool == 'fsl':
blip_node, blip_out = strat_pool.get_data('fsl-blip-warp')
merge_num = 5
blip = True
collectxfm = pe.MapNode(util.Merge(merge_num),
name=f'collectxfm_func_to_standard_{pipe_num}',
iterfield=[f'in{merge_num}'])
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_xfm')
wf.connect(node, out, collectxfm, 'in1')
wf.connect(bbr2itk, 'itk_transform',
collectxfm, 'in2')
collectxfm.inputs.in3 = 'identity'
if blip:
wf.connect(blip_node, blip_out, collectxfm, 'in4')
wf.connect(motionxfm2itk, 'itk_transform',
collectxfm, f'in{merge_num}')
applyxfm_func_to_standard = pe.MapNode(
interface=ants.ApplyTransforms(),
name=f'applyxfm_func_to_standard_{pipe_num}',
iterfield=['input_image', 'transforms'])
applyxfm_func_to_standard.inputs.float = True
applyxfm_func_to_standard.inputs.interpolation = 'LanczosWindowedSinc'
applyxfm_derivfunc_to_standard = pe.MapNode(
interface=ants.ApplyTransforms(),
name=f'applyxfm_derivfunc_to_standard_{pipe_num}',
iterfield=['input_image', 'transforms'])
applyxfm_derivfunc_to_standard.inputs.float = True
applyxfm_derivfunc_to_standard.inputs.interpolation = 'LanczosWindowedSinc'
wf.connect(split_func, 'out_files',
applyxfm_func_to_standard, 'input_image')
wf.connect(split_func, 'out_files',
applyxfm_derivfunc_to_standard, 'input_image')
node, out = strat_pool.get_data('T1w-brain-template-funcreg')
wf.connect(node, out, applyxfm_func_to_standard, 'reference_image')
node, out = strat_pool.get_data('T1w-brain-template-deriv')
wf.connect(node, out, applyxfm_derivfunc_to_standard, 'reference_image')
wf.connect(collectxfm, 'out', applyxfm_func_to_standard, 'transforms')
wf.connect(collectxfm, 'out', applyxfm_derivfunc_to_standard, 'transforms')
### Loop ends! ###
merge_func_to_standard = pe.Node(interface=fslMerge(),
name=f'merge_func_to_standard_{pipe_num}')
merge_func_to_standard.inputs.dimension = 't'
wf.connect(applyxfm_func_to_standard, 'output_image',
merge_func_to_standard, 'in_files')
merge_derivfunc_to_standard = pe.Node(
interface=fslMerge(), name=f'merge_derivfunc_to_standard_{pipe_num}')
merge_derivfunc_to_standard.inputs.dimension = 't'
wf.connect(applyxfm_derivfunc_to_standard, 'output_image',
merge_derivfunc_to_standard, 'in_files')
applyxfm_func_mask_to_standard = pe.Node(
interface=ants.ApplyTransforms(),
name=f'applyxfm_func_mask_to_standard_{pipe_num}')
applyxfm_func_mask_to_standard.inputs.interpolation = 'MultiLabel'
node, out = strat_pool.get_data('space-bold_desc-brain_mask')
wf.connect(node, out, applyxfm_func_mask_to_standard, 'input_image')
node, out = strat_pool.get_data('T1w-brain-template-funcreg')
wf.connect(node, out, applyxfm_func_mask_to_standard, 'reference_image')
collectxfm_mask = pe.Node(
util.Merge(2), name=f'collectxfm_func_mask_to_standard_{pipe_num}')
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_xfm')
wf.connect(node, out, collectxfm_mask, 'in1')
wf.connect(bbr2itk, 'itk_transform', collectxfm_mask, 'in2')
wf.connect(collectxfm_mask, 'out',
applyxfm_func_mask_to_standard, 'transforms')
applyxfm_deriv_mask_to_standard = pe.Node(
interface=ants.ApplyTransforms(),
name=f'applyxfm_deriv_mask_to_standard_{pipe_num}')
applyxfm_deriv_mask_to_standard.inputs.interpolation = 'MultiLabel'
node, out = strat_pool.get_data('space-bold_desc-brain_mask')
wf.connect(node, out, applyxfm_deriv_mask_to_standard, 'input_image')
node, out = strat_pool.get_data('T1w-brain-template-deriv')
wf.connect(node, out, applyxfm_deriv_mask_to_standard, 'reference_image')
collectxfm_deriv_mask = pe.Node(
util.Merge(2), name=f'collectxfm_deriv_mask_to_standard_{pipe_num}')
node, out = strat_pool.get_data('from-T1w_to-template_mode-image_xfm')
wf.connect(node, out, collectxfm_deriv_mask, 'in1')
wf.connect(bbr2itk, 'itk_transform',
collectxfm_deriv_mask, 'in2')
wf.connect(collectxfm_deriv_mask, 'out',
applyxfm_deriv_mask_to_standard, 'transforms')
apply_mask = pe.Node(interface=fsl.maths.ApplyMask(),
name=f'get_func_brain_to_standard_{pipe_num}')
wf.connect(merge_func_to_standard, 'merged_file',
apply_mask, 'in_file')
wf.connect(applyxfm_func_mask_to_standard, 'output_image',
apply_mask, 'mask_file')
outputs = {
'space-template_desc-head_bold': (merge_func_to_standard,
'merged_file'),
'space-template_desc-brain_bold': (apply_mask, 'out_file'),
'space-template_desc-preproc_bold': (apply_mask, 'out_file'),
'space-template_desc-bold_mask': (applyxfm_func_mask_to_standard,
'output_image'),
'space-template_res-derivative_desc-preproc_bold':
(merge_derivfunc_to_standard, 'merged_file'),
'space-template_res-derivative_desc-bold_mask':
(applyxfm_deriv_mask_to_standard, 'output_image')
}
return (wf, outputs)
@nodeblock(
name="transform_sbref_to_T1template",
switch=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"run",
],
inputs=[
("sbref", "from-bold_to-template_mode-image_xfm"),
"T1w-brain-template-funcreg",
],
outputs={
"space-template_sbref": {
"Description": "Single-volume sbref of the BOLD time-series "
"transformed to template space.",
"Template": "T1w-brain-template-funcreg",
}
},
)
def warp_sbref_to_T1template(wf, cfg, strat_pool, pipe_num, opt=None):
xfm = 'from-bold_to-template_mode-image_xfm'
wf, apply_xfm = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'sbref', xfm,
reference='T1w-brain-template-funcreg', time_series=False)[:2]
outputs = {'space-template_sbref':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="transform_bold_mask_to_T1template",
switch=[
[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"run",
],
["registration_workflows", "anatomical_registration", "run"],
],
option_key=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"apply_transform",
"using",
],
option_val=["default", "abcd", "dcan_nhp"],
inputs=[
("space-bold_desc-brain_mask", "from-bold_to-template_mode-image_xfm"),
"T1w-brain-template-funcreg",
],
outputs={
"space-template_desc-bold_mask": {
"Template": "T1w-brain-template-funcreg"}})
def warp_bold_mask_to_T1template(wf, cfg, strat_pool, pipe_num, opt=None):
xfm = 'from-bold_to-template_mode-image_xfm'
wf, apply_xfm = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'space-bold_desc-brain_mask', xfm,
reference='T1w-brain-template-funcreg', time_series=False)[:2]
outputs = {'space-template_desc-bold_mask':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="transform_deriv_mask_to_T1template",
switch=[
[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"run",
],
["registration_workflows", "anatomical_registration", "run"],
],
option_key=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
"apply_transform",
"using",
],
option_val=["default", "abcd", "dcan_nhp"],
inputs=[
("space-bold_desc-brain_mask", "from-bold_to-template_mode-image_xfm"),
"T1w-brain-template-deriv",
],
outputs={
"space-template_res-derivative_desc-bold_mask": {
"Template": "T1w-brain-template-deriv"
}
},
)
def warp_deriv_mask_to_T1template(wf, cfg, strat_pool, pipe_num, opt=None):
'''Transform the BOLD mask to template space and to the resolution set for
the derivative outputs.
'''
xfm = 'from-bold_to-template_mode-image_xfm'
wf, apply_xfm = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'space-bold_desc-brain_mask', xfm,
reference='T1w-brain-template-deriv', time_series=False)[:2]
outputs = {'space-template_res-derivative_desc-bold_mask':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="transform_timeseries_to_EPItemplate",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run_EPI"],
inputs=[
("desc-preproc_bold", "from-bold_to-EPItemplate_mode-image_xfm"),
"EPI-template",
],
outputs={"space-template_desc-preproc_bold": {"Template": "EPI-template"}},
)
def warp_timeseries_to_EPItemplate(wf, cfg, strat_pool, pipe_num, opt=None):
xfm = 'from-bold_to-EPItemplate_mode-image_xfm'
wf, apply_xfm, resource = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'desc-preproc_bold', xfm,
time_series=True)
outputs = {f'space-template_{resource}':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="transform_bold_mean_to_EPItemplate",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run_EPI"],
inputs=[
("desc-mean_bold", "from-bold_to-EPItemplate_mode-image_xfm"),
"EPI-template",
],
outputs={"space-template_desc-mean_bold": {"Template": "EPI-template"}},
)
def warp_bold_mean_to_EPItemplate(wf, cfg, strat_pool, pipe_num, opt=None):
xfm = 'from-bold_to-EPItemplate_mode-image_xfm'
wf, apply_xfm = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'desc-mean_bold', xfm,
time_series=False)[:2]
outputs = {'space-template_desc-mean_bold':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="transform_bold_mask_to_EPItemplate",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run_EPI"],
inputs=[
("space-bold_desc-brain_mask",
"from-bold_to-EPItemplate_mode-image_xfm"),
"EPI-template",
],
outputs={"space-template_desc-bold_mask": {"Template": "EPI-template"}},
)
def warp_bold_mask_to_EPItemplate(wf, cfg, strat_pool, pipe_num, opt=None):
xfm = 'from-bold_to-EPItemplate_mode-image_xfm'
wf, apply_xfm = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'space-bold_desc-brain_mask', xfm,
time_series=False)[:2]
outputs = {'space-template_desc-bold_mask':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="transform_deriv_mask_to_EPItemplate",
config=[
"registration_workflows",
"functional_registration",
"func_registration_to_template",
],
switch=["run_EPI"],
inputs=[
("space-bold_desc-brain_mask",
"from-bold_to-EPItemplate_mode-image_xfm"),
"EPI-template",
],
outputs={
"space-template_res-derivative_desc-bold_mask": {
"Template": "EPI-template"}
},
)
def warp_deriv_mask_to_EPItemplate(wf, cfg, strat_pool, pipe_num, opt=None):
'''Transform the BOLD mask to template space and to the resolution set for
the derivative outputs.
'''
xfm = 'from-bold_to-EPItemplate_mode-image_xfm'
wf, apply_xfm = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, 'space-bold_desc-brain_mask', xfm,
time_series=False)[:2]
outputs = {'space-template_res-derivative_desc-bold_mask':
(apply_xfm, 'outputspec.output_image')}
return _warp_return(wf, apply_xfm, outputs)
@nodeblock(
name="warp_tissuemask_to_T1template",
switch=["registration_workflows", "anatomical_registration", "run"],
inputs=[
(
"label-CSF_mask",
"label-WM_mask",
"label-GM_mask",
"from-T1w_to-template_mode-image_xfm",
),
"T1w-template",
],
outputs={
"space-template_label-CSF_mask": {"Template": "T1w-template"},
"space-template_label-WM_mask": {"Template": "T1w-template"},
"space-template_label-GM_mask": {"Template": "T1w-template"},
},
)
def warp_tissuemask_to_T1template(wf, cfg, strat_pool, pipe_num, opt=None):
return warp_tissuemask_to_template(wf, cfg, strat_pool, pipe_num,
xfm='from-T1w_to-template_mode-image_'
'xfm', template_space='T1')
@nodeblock(
name="warp_tissuemask_to_EPItemplate",
switch=[
"registration_workflows",
"functional_registration",
"EPI_registration",
"run",
],
inputs=[
(
"label-CSF_mask",
"label-WM_mask",
"label-GM_mask",
"from-bold_to-EPItemplate_mode-image_xfm",
),
"EPI-template",
],
outputs={
"space-template_label-CSF_mask": {"Template": "EPI-template"},
"space-template_label-WM_mask": {"Template": "EPI-template"},
"space-template_label-GM_mask": {"Template": "EPI-template"},
},
)
def warp_tissuemask_to_EPItemplate(wf, cfg, strat_pool, pipe_num, opt=None):
return warp_tissuemask_to_template(wf, cfg, strat_pool, pipe_num,
xfm='from-bold_to-EPItemplate_'
'mode-image_xfm',
template_space='EPI')
def warp_tissuemask_to_template(wf, cfg, strat_pool, pipe_num, xfm,
template_space):
'''Function to apply transforms to tissue masks
Parameters
----------
wf, cfg, strat_pool, pipe_num
passed through from Node Block
xfm : str
transform
template_space : str
T1 or EPI
Returns
-------
wf : nipype.pipeline.engine.workflows.Workflow
outputs : dict
'''
tissue_types = ['CSF', 'WM', 'GM']
apply_xfm = {}
for tissue in tissue_types:
wf, apply_xfm[tissue] = warp_resource_to_template(
wf, cfg, strat_pool, pipe_num, f'label-{tissue}_mask', xfm,
time_series=False)[:2]
if template_space == 'T1':
template_space = ''
outputs = {f'space-{template_space}template_label-{tissue}_mask': (
apply_xfm[tissue], 'outputspec.output_image') for
tissue in tissue_types}
return _warp_return(wf, apply_xfm, outputs)
def warp_resource_to_template(wf: pe.Workflow, cfg, strat_pool, pipe_num: int,
input_resource: LIST_OR_STR, xfm: str,
reference: Optional[str] = None,
time_series: Optional[bool] = False
) -> TUPLE[pe.Workflow, pe.Workflow, str]:
'''Function to warp a resource into a template space
Parameters
----------
wf : pe.Workflow
cfg : CPAC.utils.configuration.Configuration
strat_pool : CPAC.pipeline.engine.ResourcePool
pipe_num : int
input_resource : str or list
key for the resource to warp to template
xfm : str
key for the transform to apply
reference : str, optional
key for reference if not using f'{template_space}-template'
time_series : boolean, optional
resource to transform is 4D?
Returns
-------
wf : pe.Workflow
original workflow with subworkflow to warp resource to template
connected
apply_xfm : pe.Workflow
subworkflow added to warp resource to template
resource : str
key of input resource in strat_pool
'''
# determine space we're warping to
template_space = xfm.split('_to-', 1)[1].split('template')[0]
if template_space == '':
template_space = 'T1w'
# determine tool used for registration
xfm_prov = strat_pool.get_cpac_provenance(xfm)
reg_tool = check_prov_for_regtool(xfm_prov)
# set 'resource'
if strat_pool.check_rpool(input_resource):
resource, input_resource = strat_pool.get_data(input_resource,
report_fetched=True)
else:
return wf, None, input_resource
# set 'reference' if not passed and determine subworkflow name
if reference is None:
subwf_input_name = input_resource
reference = f'{template_space}-template'
else:
subwf_input_name = '-'.join([
reference.split('-')[-1].split('_')[-1],
input_resource.split('-')[-1].split('_')[-1]])
# set up 'apply_transform' subworkflow
apply_xfm = apply_transform(f'warp_{subwf_input_name}_to_'
f'{template_space}template_{pipe_num}',
reg_tool, time_series=time_series,
num_cpus=cfg.pipeline_setup['system_config'][
'max_cores_per_participant'],
num_ants_cores=cfg.pipeline_setup[
'system_config']['num_ants_threads'])
# set appropriate 'interpolation' input based on registration tool
if reg_tool == 'ants':
apply_xfm.inputs.inputspec.interpolation = 'NearestNeighbor'
elif reg_tool == 'fsl':
apply_xfm.inputs.inputspec.interpolation = 'nn'
# connect nodes to subworkflow
node, out = resource
wf.connect(node, out, apply_xfm, 'inputspec.input_image')
node, out = strat_pool.get_data(reference)
wf.connect(node, out, apply_xfm, 'inputspec.reference')
node, out = strat_pool.get_data(xfm)
wf.connect(node, out, apply_xfm, 'inputspec.transform')
return wf, apply_xfm, input_resource
def _warp_return(wf: pe.Workflow, apply_xfm: Optional[pe.Workflow],
outputs: dict) -> TUPLE[pe.Workflow, dict]:
"""Check if we have a transform to apply; if not, don't add the outputs"""
if apply_xfm is None:
return wf, {}
return wf, outputs