def create_epi_t1_nonlinear_pipeline(name='epi_t1_nonlinear'): """Creates a pipeline that performs nonlinear EPI to T1 registration using the antsRegistration tool. Beforehand, the T1 image has to be processed in freesurfer and the EPI timeseries should be realigned. Example ------- >>> nipype_epi_t1_nonlin = create_epi_t1_nonlinear_pipeline('nipype_epi_t1_nonlin') >>> nipype_epi_t1_nonlin.inputs.inputnode.fs_subject_id = '123456' >>> nipype_epi_t1_nonlin.inputs.inputnode.fs_subjects_dir = '/project/data/freesurfer' >>> nipype_epi_t1_nonlin.inputs.inputnode.realigned_epi = 'mcflirt.nii.gz' >>> nipype_epi_t1_nonlin.run() Inputs:: inputnode.fs_subject_id # subject id used in freesurfer inputnode.fs_subjects_dir # path to freesurfer output inputnode.realigned_epi # realigned EPI timeseries Outputs:: outputnode.lin_epi2anat # ITK format outputnode.lin_anat2epi # ITK format outputnode.nonlin_epi2anat # ANTs specific 5D deformation field outputnode.nonlin_anat2epi # ANTs specific 5D deformation field """ nonreg = Workflow(name='epi_t1_nonlinear') # input inputnode = Node(interface=util.IdentityInterface( fields=['fs_subject_id', 'fs_subjects_dir', 'realigned_epi']), name='inputnode') # calculate the temporal mean image of the realigned timeseries tmean = Node(interface=fsl.maths.MeanImage(dimension='T', output_type='NIFTI_GZ'), name='tmean') nonreg.connect(inputnode, 'realigned_epi', tmean, 'in_file') # import brain.mgz and ribbon.mgz from freesurfer directory fs_import = Node(interface=nio.FreeSurferSource(), name='freesurfer_import') nonreg.connect(inputnode, 'fs_subjects_dir', fs_import, 'subjects_dir') nonreg.connect(inputnode, 'fs_subject_id', fs_import, 'subject_id') # convert brain.mgz to niigz mriconvert = Node(interface=fs.MRIConvert(out_type='niigz'), name='mriconvert') nonreg.connect(fs_import, 'brain', mriconvert, 'in_file') # calculate rigid transformation of mean epi to t1 with bbregister bbregister = Node(interface=fs.BBRegister(init='fsl', contrast_type='t2', out_fsl_file=True), name='bbregister') nonreg.connect(inputnode, 'fs_subjects_dir', bbregister, 'subjects_dir') nonreg.connect(inputnode, 'fs_subject_id', bbregister, 'subject_id') nonreg.connect(tmean, 'out_file', bbregister, 'source_file') # convert linear transformation to itk format compatible with ants itk = Node(interface=c3.C3dAffineTool(fsl2ras=True, itk_transform='epi2anat_affine.txt'), name='itk') nonreg.connect(tmean, 'out_file', itk, 'source_file') nonreg.connect(mriconvert, 'out_file', itk, 'reference_file') nonreg.connect(bbregister, 'out_fsl_file', itk, 'transform_file') # get aparc aseg mask # create brainmask from aparc+aseg def get_aparc_aseg(files): for name in files: if 'aparc+aseg' in name: return name aparc_aseg_mask = Node(fs.Binarize(min=0.1, dilate=10, erode=7, out_type='nii.gz', binary_file='aparc_aseg_mask.nii.gz'), name='aparc_aseg_mask') # fill holes in mask fillholes = Node(fsl.maths.MathsCommand(args='-fillh'), name='fillholes') nonreg.connect([(fs_import, aparc_aseg_mask, [ (('aparc_aseg', get_aparc_aseg), 'in_file') ]), (aparc_aseg_mask, fillholes, [('binary_file', 'in_file')])]) #create bounding box mask and rigidly transform into anatomical (fs) space fov = Node(interface=fs.model.Binarize(min=0.0, out_type='nii.gz'), name='fov') nonreg.connect(tmean, 'out_file', fov, 'in_file') fov_trans = Node(interface=ants.resampling.ApplyTransforms( dimension=3, interpolation='NearestNeighbor'), name='fov_trans') nonreg.connect(itk, ('itk_transform', filename_to_list), fov_trans, 'transforms') nonreg.connect(fov, 'binary_file', fov_trans, 'input_image') nonreg.connect(fillholes, 'out_file', fov_trans, 'reference_image') #nonreg.connect(ribbon, 'binary_file', fov_trans, 'reference_image') # intersect both masks intersect = Node(interface=fsl.maths.BinaryMaths(operation='mul'), name='intersect') nonreg.connect(fillholes, 'out_file', intersect, 'in_file') #nonreg.connect(ribbon, 'binary_file', intersect, 'in_file') nonreg.connect(fov_trans, 'output_image', intersect, 'operand_file') # inversly transform mask and mask original epi mask_trans = Node(interface=ants.resampling.ApplyTransforms( dimension=3, interpolation='NearestNeighbor', invert_transform_flags=[True]), name='mask_trans') nonreg.connect(itk, ('itk_transform', filename_to_list), mask_trans, 'transforms') nonreg.connect(intersect, 'out_file', mask_trans, 'input_image') nonreg.connect(tmean, 'out_file', mask_trans, 'reference_image') maskepi = Node(interface=fs.utils.ApplyMask(), name='maskepi') nonreg.connect(mask_trans, 'output_image', maskepi, 'mask_file') nonreg.connect(tmean, 'out_file', maskepi, 'in_file') # mask anatomical image (brain) maskanat = Node(interface=fs.utils.ApplyMask(), name='maskanat') nonreg.connect(intersect, 'out_file', maskanat, 'mask_file') nonreg.connect(mriconvert, 'out_file', maskanat, 'in_file') # invert masked anatomical image anat_min_max = Node(interface=fsl.utils.ImageStats(op_string='-R'), name='anat_min_max') epi_min_max = Node(interface=fsl.utils.ImageStats(op_string='-r'), name='epi_min_max') nonreg.connect(maskanat, 'out_file', anat_min_max, 'in_file') nonreg.connect(tmean, 'out_file', epi_min_max, 'in_file') def calc_inversion(anat_min_max, epi_min_max): mul = -(epi_min_max[1] - epi_min_max[0]) / (anat_min_max[1] - anat_min_max[0]) add = abs(anat_min_max[1] * mul) + epi_min_max[0] return mul, add calcinv = Node(interface=Function( input_names=['anat_min_max', 'epi_min_max'], output_names=['mul', 'add'], function=calc_inversion), name='calcinv') nonreg.connect(anat_min_max, 'out_stat', calcinv, 'anat_min_max') nonreg.connect(epi_min_max, 'out_stat', calcinv, 'epi_min_max') mulinv = Node(interface=fsl.maths.BinaryMaths(operation='mul'), name='mulinv') addinv = Node(interface=fsl.maths.BinaryMaths(operation='add'), name='addinv') nonreg.connect(maskanat, 'out_file', mulinv, 'in_file') nonreg.connect(calcinv, 'mul', mulinv, 'operand_value') nonreg.connect(mulinv, 'out_file', addinv, 'in_file') nonreg.connect(calcinv, 'add', addinv, 'operand_value') # nonlinear transformation of masked anat to masked epi with ants antsreg = Node(interface=ants.registration.Registration( dimension=3, invert_initial_moving_transform=True, metric=['CC'], metric_weight=[1.0], radius_or_number_of_bins=[4], sampling_strategy=['None'], transforms=['SyN'], args='-g .1x1x.1', transform_parameters=[(0.10, 3, 0)], number_of_iterations=[[10, 5]], convergence_threshold=[1e-06], convergence_window_size=[10], shrink_factors=[[2, 1]], smoothing_sigmas=[[1, 0.5]], sigma_units=['vox'], use_estimate_learning_rate_once=[True], use_histogram_matching=[True], collapse_output_transforms=True, output_inverse_warped_image=True, output_warped_image=True), name='antsreg') nonreg.connect(itk, 'itk_transform', antsreg, 'initial_moving_transform') nonreg.connect(maskepi, 'out_file', antsreg, 'fixed_image') nonreg.connect(addinv, 'out_file', antsreg, 'moving_image') # output def second_element(file_list): return file_list[1] def first_element(file_list): return file_list[0] outputnode = Node(interface=util.IdentityInterface(fields=[ 'lin_epi2anat', 'lin_anat2epi', 'nonlin_epi2anat', 'nonlin_anat2epi' ]), name='outputnode') nonreg.connect(itk, 'itk_transform', outputnode, 'lin_epi2anat') nonreg.connect(antsreg, ('forward_transforms', first_element), outputnode, 'lin_anat2epi') nonreg.connect(antsreg, ('forward_transforms', second_element), outputnode, 'nonlin_anat2epi') nonreg.connect(antsreg, ('reverse_transforms', second_element), outputnode, 'nonlin_epi2anat') return nonreg
def init_fsl_bbr_wf(use_bbr, bold2t1w_dof, name='fsl_bbr_wf'): """ This workflow uses FSL FLIRT to register a BOLD image to a T1-weighted structural image, using a boundary-based registration (BBR) cost function. It is a counterpart to :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf`, which performs the same task using FreeSurfer's ``bbregister``. The ``use_bbr`` option permits a high degree of control over registration. If ``False``, standard, rigid coregistration will be performed by FLIRT. If ``True``, FLIRT-BBR will be seeded with the initial transform found by the rigid coregistration. If ``None``, after FLIRT-BBR is run, the resulting affine transform will be compared to the initial transform found by FLIRT. Excessive deviation will result in rejecting the BBR refinement and accepting the original, affine registration. .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_fsl_bbr_wf wf = init_fsl_bbr_wf(use_bbr=True, bold2t1w_dof=9) Parameters use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration name : str, optional Workflow name (default: fsl_bbr_wf) Inputs in_file Reference BOLD image to be registered t1_brain Skull-stripped T1-weighted structural image t1_seg FAST segmentation of ``t1_brain`` t1_2_fsnative_reverse_transform Unused (see :py:func:`~fmriprep.workflows.util.init_bbreg_wf`) subjects_dir Unused (see :py:func:`~fmriprep.workflows.util.init_bbreg_wf`) subject_id Unused (see :py:func:`~fmriprep.workflows.util.init_bbreg_wf`) Outputs itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (rigid FLIRT registration returned) """ workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD reference was then co-registered to the T1w reference using `flirt` [FSL {fsl_ver}, @flirt] with the boundary-based registration [@bbr] cost-function. Co-registration was configured with nine degrees of freedom to account for distortions remaining in the BOLD reference. """.format(fsl_ver=FLIRTRPT().version or '<ver>') inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 't1_2_fsnative_reverse_transform', 'subjects_dir', 'subject_id', # BBRegister 't1_seg', 't1_brain' ]), # FLIRT BBR name='inputnode') outputnode = pe.Node(niu.IdentityInterface( ['itk_bold_to_t1', 'itk_t1_to_bold', 'out_report', 'fallback']), name='outputnode') wm_mask = pe.Node(niu.Function(function=extract_wm), name='wm_mask') flt_bbr_init = pe.Node(FLIRTRPT(dof=6, generate_report=not use_bbr, uses_qform=True), name='flt_bbr_init') invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invt_bbr', mem_gb=DEFAULT_MEMORY_MIN_GB) # BOLD to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd', mem_gb=DEFAULT_MEMORY_MIN_GB) fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, flt_bbr_init, [('in_file', 'in_file'), ('t1_brain', 'reference')]), (inputnode, fsl2itk_fwd, [('t1_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1_brain', 'source_file')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_bold_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_bold')]), ]) # Short-circuit workflow building, use rigid registration if use_bbr is False: workflow.connect([ (flt_bbr_init, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr_init, fsl2itk_fwd, [('out_matrix_file', 'transform_file') ]), (flt_bbr_init, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = True return workflow flt_bbr = pe.Node(FLIRTRPT(cost_func='bbr', dof=bold2t1w_dof, generate_report=True, schedule=op.join(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch')), name='flt_bbr') workflow.connect([ (inputnode, wm_mask, [('t1_seg', 'in_seg')]), (inputnode, flt_bbr, [('in_file', 'in_file'), ('t1_brain', 'reference')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), (wm_mask, flt_bbr, [('out', 'wm_seg')]), ]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: workflow.connect([ (flt_bbr, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (flt_bbr, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = False return workflow transforms = pe.Node(niu.Merge(2), run_without_submitting=True, name='transforms') reports = pe.Node(niu.Merge(2), run_without_submitting=True, name='reports') compare_transforms = pe.Node(niu.Function(function=compare_xforms), name='compare_transforms') select_transform = pe.Node(niu.Select(), run_without_submitting=True, name='select_transform') select_report = pe.Node(niu.Select(), run_without_submitting=True, name='select_report') fsl_to_lta = pe.MapNode(fs.utils.LTAConvert(out_lta=True), iterfield=['in_fsl'], name='fsl_to_lta') workflow.connect([ (flt_bbr, transforms, [('out_matrix_file', 'in1')]), (flt_bbr_init, transforms, [('out_matrix_file', 'in2')]), # Convert FSL transforms to LTA (RAS2RAS) transforms and compare (inputnode, fsl_to_lta, [('in_file', 'source_file'), ('t1_brain', 'target_file')]), (transforms, fsl_to_lta, [('out', 'in_fsl')]), (fsl_to_lta, compare_transforms, [('out_lta', 'lta_list')]), (compare_transforms, outputnode, [('out', 'fallback')]), # Select output transform (transforms, select_transform, [('out', 'inlist')]), (compare_transforms, select_transform, [('out', 'index')]), (select_transform, invt_bbr, [('out', 'in_file')]), (select_transform, fsl2itk_fwd, [('out', 'transform_file')]), (flt_bbr, reports, [('out_report', 'in1')]), (flt_bbr_init, reports, [('out_report', 'in2')]), (reports, select_report, [('out', 'inlist')]), (compare_transforms, select_report, [('out', 'index')]), (select_report, outputnode, [('out', 'out_report')]), ]) return workflow
maskT2 = pe.Node(interface=fsl.ImageMaths(op_string = '-mas', suffix = '_bet'), name='maskT2') preproc.connect(t2tot1, 'out_file', maskT2, 'in_file') preproc.connect(inflate_mask, 'out_file', maskT2, 'in_file2') # apply inflated mask to t1 maskT1 = pe.Node(interface=fsl.ImageMaths(op_string = '-mas', suffix = '_bet'), name='maskT1') preproc.connect(T1_to_standard, 'out_file', maskT1, 'in_file') preproc.connect(inflate_mask, 'out_file', maskT1, 'in_file2') # affine co-reg (flirt) of T1 to caltech atlas init_t1_to_atlas_coreg = pe.Node(interface=fsl.FLIRT(dof=6, output_type = "NIFTI_GZ"), name='init_t1_to_atlas_coreg') preproc.connect(maskT1, 'out_file', init_t1_to_atlas_coreg, 'in_file') preproc.connect(downsample_atlas_t1, 'out_file', init_t1_to_atlas_coreg, 'reference') # convert transform matrix to ants format (ITK) fsl2ras = pe.Node(interface=c3.C3dAffineTool(fsl2ras = True, itk_transform=True), name='fsl2ras') preproc.connect(downsample_atlas_t1, 'out_file', fsl2ras, 'reference_file') preproc.connect(maskT1, 'out_file', fsl2ras, 'source_file') preproc.connect(init_t1_to_atlas_coreg, 'out_matrix_file', fsl2ras, 'transform_file') # merge fixed and moving images into list merge_fixed = pe.Node(interface=util.Merge(2, axis='hstack'), name='merge_fixed') preproc.connect(downsample_atlas_t1, 'out_file', merge_fixed, 'in1') preproc.connect(downsample_atlas_t2, 'out_file', merge_fixed, 'in2') merge_moving = pe.Node(interface=util.Merge(2, axis='hstack'), name='merge_moving') preproc.connect(maskT1, 'out_file', merge_moving, 'in1') preproc.connect(maskT2, 'out_file', merge_moving, 'in2') # assemble the inputs for coregistration of t2/t1 to caltech atlas structs_to_atlas_coreg_input = pe.Node(interface=util.IdentityInterface(fields=['fixed_image', 'moving_image', 'fixed_image_mask', 'moving_image_mask', 'initial_moving_transform']), name='structs_to_atlas_coreg_input') preproc.connect(merge_fixed, ('out', unlist), structs_to_atlas_coreg_input, 'fixed_image')
def get_post_struct_norm_workflow(name='normalize_post_struct'): """ Base post-structural workflow for normalization Parameters ---------- name : name of workflow. Default = 'normalize_post_struct' Inputs ------ inputspec.template_file : inputspec.unwarped_brain : inputspec.warp_field : inputspec.affine_transformation : inputspec.out_fsl_file : inputspec.moving_image : inputspec.mean_func : inputspec.use_nearest: Outputs ------- outputspec.warped_image : Returns ------- workflow : post-structural normalization workflow """ #inputs to workflow import nipype.interfaces.freesurfer as fs import nipype.interfaces.ants as ants import nipype.pipeline.engine as pe import nipype.interfaces.utility as util inputspec = pe.Node(util.IdentityInterface(fields=[ 'template_file', 'unwarped_brain', 'warp_field', 'affine_transformation', 'out_fsl_file', 'moving_image', 'mean_func', "use_nearest" ]), name='inputspec') #makes fsl-style coregistration ANTS compatible fsl_reg_2_itk = pe.Node(c3.C3dAffineTool(fsl2ras=True), name='fsl_reg_2_itk') #collects series of transformations to be applied to the moving images collect_transforms = pe.Node(util.Merge(3), name='collect_transforms') #performs series of transformations on moving images warp_images = pe.MapNode(ants.WarpTimeSeriesImageMultiTransform(), name='warp_images', iterfield=['input_image', 'dimension']) #collects workflow outputs outputspec = pe.Node(util.IdentityInterface(fields=['warped_image']), name='outputspec') #initializes and connects workflow nodes normalize_post_struct = pe.Workflow(name=name) normalize_post_struct.connect([ (inputspec, fsl_reg_2_itk, [('unwarped_brain', 'reference_file')]), (inputspec, fsl_reg_2_itk, [('out_fsl_file', 'transform_file')]), (inputspec, fsl_reg_2_itk, [('mean_func', 'source_file')]), (fsl_reg_2_itk, collect_transforms, [('itk_transform', 'in3')]), (inputspec, collect_transforms, [('warp_field', 'in1'), ('affine_transformation', 'in2')]), (inputspec, warp_images, [('moving_image', 'input_image')]), (inputspec, warp_images, [(('moving_image', get_image_dimensions), 'dimension')]), (inputspec, warp_images, [('template_file', 'reference_image'), ('use_nearest', 'use_nearest')]), (collect_transforms, warp_images, [('out', 'transformation_series')]), (warp_images, outputspec, [('output_image', 'warped_image')]) ]) return normalize_post_struct
def create_coreg_pipeline(name='coreg'): # fsl output type fsl.FSLCommand.set_default_output_type('NIFTI_GZ') # initiate workflow coreg = Workflow(name='coreg') #inputnode inputnode = Node(util.IdentityInterface(fields=[ 'epi_median', 'fs_subjects_dir', 'fs_subject_id', 'uni_highres', ]), name='inputnode') # outputnode outputnode = Node(util.IdentityInterface(fields=[ 'uni_lowres', 'epi2lowres', 'epi2lowres_mat', 'epi2lowres_dat', 'highres2lowres', 'highres2lowres_mat', 'highres2lowres_dat', 'epi2highres_lin', 'epi2highres_lin_mat', 'epi2highres_lin_itk' ]), name='outputnode') # convert mgz head file for reference fs_import = Node(interface=nio.FreeSurferSource(), name='fs_import') brain_convert = Node(fs.MRIConvert(out_type='niigz', out_file='uni_lowres.nii.gz'), name='brain_convert') coreg.connect([(inputnode, fs_import, [('fs_subjects_dir', 'subjects_dir'), ('fs_subject_id', 'subject_id')]), (fs_import, brain_convert, [('brain', 'in_file')]), (brain_convert, outputnode, [('out_file', 'uni_lowres')])]) # linear registration epi median to lowres mp2rage with bbregister bbregister_epi = Node(fs.BBRegister(contrast_type='t2', out_fsl_file='epi2lowres.mat', out_reg_file='epi2lowres.dat', registered_file='epi2lowres.nii.gz', init='fsl', epi_mask=True), name='bbregister_epi') coreg.connect([ (inputnode, bbregister_epi, [('fs_subjects_dir', 'subjects_dir'), ('fs_subject_id', 'subject_id'), ('epi_median', 'source_file')]), (bbregister_epi, outputnode, [('out_fsl_file', 'epi2lowres_mat'), ('out_reg_file', 'epi2lowres_dat'), ('registered_file', 'epi2lowres')]) ]) # linear register highres mp2rage to lowres mp2rage bbregister_anat = Node(fs.BBRegister( contrast_type='t1', out_fsl_file='highres2lowres.mat', out_reg_file='highres2lowres.dat', registered_file='highres2lowres.nii.gz', init='fsl'), name='bbregister_anat') coreg.connect([ (inputnode, bbregister_anat, [('fs_subjects_dir', 'subjects_dir'), ('fs_subject_id', 'subject_id'), ('uni_highres', 'source_file')]), (bbregister_anat, outputnode, [('out_fsl_file', 'highres2lowres_mat'), ('out_reg_file', 'highres2lowres_dat'), ('registered_file', 'highres2lowres')]) ]) # invert highres2lowres transform invert = Node(fsl.ConvertXFM(invert_xfm=True), name='invert') coreg.connect([(bbregister_anat, invert, [('out_fsl_file', 'in_file')])]) # concatenate epi2highres transforms concat = Node(fsl.ConvertXFM(concat_xfm=True, out_file='epi2highres_lin.mat'), name='concat') coreg.connect([(bbregister_epi, concat, [('out_fsl_file', 'in_file')]), (invert, concat, [('out_file', 'in_file2')]), (concat, outputnode, [('out_file', 'epi2higres_lin_mat')])]) # convert epi2highres transform into itk format itk = Node(interface=c3.C3dAffineTool(fsl2ras=True, itk_transform='epi2highres_lin.txt'), name='itk') coreg.connect([(inputnode, itk, [('epi_median', 'source_file'), ('uni_highres', 'reference_file')]), (concat, itk, [('out_file', 'transform_file')]), (itk, outputnode, [('itk_transform', 'epi2highres_lin_itk') ])]) # transform epi to highres epi2highres = Node(ants.ApplyTransforms( dimension=3, output_image='epi2highres_lin.nii.gz', interpolation='BSpline', ), name='epi2highres') coreg.connect([ (inputnode, epi2highres, [('uni_highres', 'reference_image'), ('epi_median', 'input_image')]), (itk, epi2highres, [('itk_transform', 'transforms')]), (epi2highres, outputnode, [('output_image', 'epi2highres_lin')]) ]) return coreg
def ants_apply_warps_func_mni(workflow, output_name, func_key, ref_key, num_strat, strat, interpolation_method='LanczosWindowedSinc', distcor=False, map_node=False, inverse=False, symmetry='asymmetric', input_image_type=0, num_ants_cores=1): """ Applies previously calculated ANTS registration transforms to input images. This workflow employs the antsApplyTransforms tool: http://stnava.github.io/ANTs/ Parameters ---------- name : string, optional Name of the workflow. Returns ------- apply_ants_warp_wf : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: workflow: Nipype workflow object the workflow containing the resources involved output_name: str what the name of the warped functional should be when written to the resource pool func_key: string resource pool key correspoding to the node containing the 3D or 4D functional file to be written into MNI space, use 'leaf' for a leaf node ref_key: string resource pool key correspoding to the file path to the template brain used for functional-to-template registration num_strat: int the number of strategy objects strat: C-PAC Strategy object a strategy with one or more resource pools interpolation_method: str which interpolation to use when applying the warps, commonly used options are 'Linear', 'Bspline', 'LanczosWindowedSinc' (default) for derivatives and image data 'NearestNeighbor' for masks distcor: boolean indicates whether a distortion correction transformation should be added to the transforms, this of course requires that a distortion correction map exist in the resource pool map_node: boolean indicates whether a mapnode should be used, if TRUE func_key is expected to correspond to a list of resources that should each be written into standard space with the other parameters inverse: boolean writes the invrse of the transform, i.e. MNI->EPI instead of EPI->MNI input_image_type: int argument taken by the ANTs apply warp tool; in this case, should be 0 for scalars (default) and 3 for 4D functional time-series num_ants_cores: int the number of CPU cores dedicated to ANTS anatomical-to-standard registration Workflow Outputs:: outputspec.output_image : string (nifti file) Normalized output file Workflow Graph: .. image:: :width: 500 Detailed Workflow Graph: .. image:: :width: 500 Apply the functional-to-structural and structural-to-template warps to the 4D functional time-series to warp it to template space. Parameters ---------- """ # if the input is a string, assume that it is resource pool key, # if it is a tuple, assume that it is a node, outfile pair, # otherwise, something funky is going on if isinstance(func_key, str): if func_key == "leaf": input_node, input_out = strat.get_leaf_properties() else: input_node, input_out = strat[func_key] elif isinstance(func_key, tuple): input_node, input_out = func_key if isinstance(ref_key, str): ref_node, ref_out = strat[ref_key] elif isinstance(ref_key, tuple): ref_node, ref_out = func_key # when inverse is enabled, we want to update the name of various # nodes so that we know they were inverted inverse_string = '' if inverse is True: inverse_string = '_inverse' # make sure that resource pool has some required resources before proceeding if 'fsl_mat_as_itk' not in strat: fsl_reg_2_itk = pe.Node(c3.C3dAffineTool(), name='fsl_reg_2_itk_{0}'.format(num_strat)) 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 node, out_file = strat['functional_to_anat_linear_xfm'] workflow.connect(node, out_file, fsl_reg_2_itk, 'transform_file') node, out_file = strat['anatomical_brain'] workflow.connect(node, out_file, fsl_reg_2_itk, 'reference_file') ref_node, ref_out = strat['mean_functional'] workflow.connect(ref_node, ref_out, 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_{0}'.format(num_strat)) workflow.connect(fsl_reg_2_itk, 'itk_transform', change_transform, 'input_affine_file') strat.update_resource_pool( {'fsl_mat_as_itk': (change_transform, 'updated_affine_file')}) strat.append_name(fsl_reg_2_itk.name) # stack of transforms to be combined to acheive the desired transformation num_transforms = 5 collect_transforms_key = \ 'collect_transforms{0}'.format(inverse_string) if distcor is True: num_transforms = 6 collect_transforms_key = \ 'collect_transforms{0}{1}'.format('_distcor', inverse_string) if collect_transforms_key not in strat: # handle both symmetric and asymmetric transforms ants_transformation_dict = { 'asymmetric': { 'anatomical_to_mni_nonlinear_xfm': 'anatomical_to_mni_nonlinear_xfm', 'ants_affine_xfm': 'ants_affine_xfm', 'ants_rigid_xfm': 'ants_rigid_xfm', 'ants_initial_xfm': 'ants_initial_xfm', 'blip_warp': 'blip_warp', 'blip_warp_inverse': 'blip_warp_inverse', 'fsl_mat_as_itk': 'fsl_mat_as_itk', }, 'symmetric': { 'anatomical_to_mni_nonlinear_xfm': 'ants_symm_warp_field', 'ants_affine_xfm': 'ants_symm_affine_xfm', 'ants_rigid_xfm': 'ants_symm_rigid_xfm', 'ants_initial_xfm': 'ants_symm_initial_xfm', 'blip_warp': 'blip_warp', 'blip_warp_inverse': 'blip_warp_inverse', 'fsl_mat_as_itk': 'fsl_mat_as_itk', } } # transforms to be concatenated, the first element of each tuple is # the resource pool key related to the resource that should be # connected in, and the second element is the input to which it # should be connected if inverse is True: if distcor is True: # Field file from anatomical nonlinear registration transforms_to_combine = [\ ('anatomical_to_mni_nonlinear_xfm', 'in6'), ('ants_affine_xfm', 'in5'), ('ants_rigid_xfm', 'in4'), ('ants_initial_xfm', 'in3'), ('fsl_mat_as_itk', 'in2'), ('blip_warp_inverse', 'in1')] else: transforms_to_combine = [\ ('anatomical_to_mni_nonlinear_xfm', 'in5'), ('ants_affine_xfm', 'in4'), ('ants_rigid_xfm', 'in3'), ('ants_initial_xfm', 'in2'), ('fsl_mat_as_itk', 'in1')] else: transforms_to_combine = [\ ('anatomical_to_mni_nonlinear_xfm', 'in1'), ('ants_affine_xfm', 'in2'), ('ants_rigid_xfm', 'in3'), ('ants_initial_xfm', 'in4'), ('fsl_mat_as_itk', 'in5')] if distcor is True: transforms_to_combine.append(('blip_warp', 'in6')) # define the node collect_transforms = pe.Node(util.Merge(num_transforms), name='collect_transforms{0}_{1}'.format( inverse_string, num_strat)) # wire in the various tranformations for transform_key, input_port in transforms_to_combine: node, out_file = strat[ants_transformation_dict[symmetry] [transform_key]] workflow.connect(node, out_file, collect_transforms, input_port) # set the output strat.update_resource_pool( {collect_transforms_key: (collect_transforms, 'out')}) strat.append_name(collect_transforms.name) #### now we add in the apply ants warps node if map_node: apply_ants_warp = pe.MapNode( interface=ants.ApplyTransforms(), name='apply_ants_warp_{0}_mapnode{1}_{2}'.format( output_name, inverse_string, num_strat), iterfield=['input_image'], mem_gb=1.5) else: apply_ants_warp = pe.Node(interface=ants.ApplyTransforms(), name='apply_ants_warp_{0}{1}_{2}'.format( output_name, inverse_string, num_strat), mem_gb=1.5) apply_ants_warp.inputs.out_postfix = '_antswarp' apply_ants_warp.interface.num_threads = int(num_ants_cores) if inverse is True: apply_ants_warp.inputs.invert_transform_flags = \ [True, True, True, True, False] # input_image_type: # (0 or 1 or 2 or 3) # Option specifying the input image type of scalar # (default), vector, tensor, or time series. apply_ants_warp.inputs.input_image_type = input_image_type apply_ants_warp.inputs.dimension = 3 apply_ants_warp.inputs.interpolation = interpolation_method node, out_file = strat[ref_key] workflow.connect(node, out_file, apply_ants_warp, 'reference_image') collect_node, collect_out = strat[collect_transforms_key] workflow.connect(collect_node, collect_out, apply_ants_warp, 'transforms') workflow.connect(input_node, input_out, apply_ants_warp, 'input_image') strat.update_resource_pool( {output_name: (apply_ants_warp, 'output_image')}) strat.append_name(apply_ants_warp.name) return workflow
def init_epi_reg_wf(freesurfer, bold2t1w_dof, bold_file_size_gb, output_spaces, output_dir, name='epi_reg_wf', use_fieldwarp=False): """ Uses FSL FLIRT with the BBR cost function to find the transform that maps the EPI space into the T1-space """ workflow = pe.Workflow(name=name) inputnode = pe.Node( niu.IdentityInterface(fields=['name_source', 'ref_epi_brain', 'ref_epi_mask', 't1_preproc', 't1_brain', 't1_mask', 't1_seg', 'epi_split', 'hmc_xforms', 'subjects_dir', 'subject_id', 'fs_2_t1_transform', 'fieldwarp']), name='inputnode' ) outputnode = pe.Node( niu.IdentityInterface(fields=['mat_epi_to_t1', 'mat_t1_to_epi', 'itk_epi_to_t1', 'itk_t1_to_epi', 'epi_t1', 'epi_mask_t1', 'fs_reg_file', 'out_report']), name='outputnode' ) if freesurfer: bbregister = pe.Node( BBRegisterRPT( dof=bold2t1w_dof, contrast_type='t2', init='coreg', registered_file=True, out_fsl_file=True, generate_report=True), name='bbregister' ) def apply_fs_transform(fs_2_t1_transform, bbreg_transform): import os import numpy as np out_file = os.path.abspath('transform.mat') fs_xfm = np.loadtxt(fs_2_t1_transform) bbrxfm = np.loadtxt(bbreg_transform) out_xfm = fs_xfm.dot(bbrxfm) assert np.allclose(out_xfm[3], [0, 0, 0, 1]) out_xfm[3] = [0, 0, 0, 1] np.savetxt(out_file, out_xfm, fmt='%.12g') return out_file transformer = pe.Node(niu.Function(function=apply_fs_transform), name='transformer', run_without_submitting=True) else: wm_mask = pe.Node(niu.Function(function=_extract_wm), name='wm_mask') flt_bbr_init = pe.Node(FLIRTRPT(generate_report=True, dof=6), name='flt_bbr_init') flt_bbr = pe.Node( FLIRTRPT(generate_report=True, cost_func='bbr', dof=bold2t1w_dof), name='flt_bbr') flt_bbr.inputs.schedule = op.join(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch') # make equivalent warp fields invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invt_bbr') # EPI to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd') fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv') workflow.connect([ (inputnode, fsl2itk_fwd, [('t1_preproc', 'reference_file'), ('ref_epi_brain', 'source_file')]), (inputnode, fsl2itk_inv, [('ref_epi_brain', 'reference_file'), ('t1_preproc', 'source_file')]), (invt_bbr, outputnode, [('out_file', 'mat_t1_to_epi')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_epi_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_epi')]), ]) gen_ref = pe.Node(GenerateSamplingReference(), name='gen_ref') mask_t1w_tfm = pe.Node( ants.ApplyTransforms(interpolation='NearestNeighbor', float=True), name='mask_t1w_tfm' ) workflow.connect([ (inputnode, gen_ref, [('ref_epi_brain', 'moving_image'), ('t1_brain', 'fixed_image')]), (gen_ref, mask_t1w_tfm, [('out_file', 'reference_image')]), (fsl2itk_fwd, mask_t1w_tfm, [('itk_transform', 'transforms')]), (inputnode, mask_t1w_tfm, [('ref_epi_mask', 'input_image')]), (mask_t1w_tfm, outputnode, [('output_image', 'epi_mask_t1')]) ]) if use_fieldwarp: merge_transforms = pe.MapNode(niu.Merge(3), iterfield=['in3'], name='merge_transforms', run_without_submitting=True) workflow.connect([ (inputnode, merge_transforms, [('fieldwarp', 'in2'), ('hmc_xforms', 'in3')]) ]) else: merge_transforms = pe.MapNode(niu.Merge(2), iterfield=['in2'], name='merge_transforms', run_without_submitting=True) workflow.connect([ (inputnode, merge_transforms, [('hmc_xforms', 'in2')]) ]) merge = pe.Node(Merge(), name='merge') merge.interface.estimated_memory_gb = bold_file_size_gb * 3 epi_to_t1w_transform = pe.MapNode( ants.ApplyTransforms(interpolation="LanczosWindowedSinc", float=True), iterfield=['input_image', 'transforms'], name='epi_to_t1w_transform') epi_to_t1w_transform.terminal_output = 'file' workflow.connect([ (fsl2itk_fwd, merge_transforms, [('itk_transform', 'in1')]), (merge_transforms, epi_to_t1w_transform, [('out', 'transforms')]), (epi_to_t1w_transform, merge, [('output_image', 'in_files')]), (inputnode, merge, [('name_source', 'header_source')]), (merge, outputnode, [('out_file', 'epi_t1')]), (inputnode, epi_to_t1w_transform, [('epi_split', 'input_image')]), (gen_ref, epi_to_t1w_transform, [('out_file', 'reference_image')]), ]) if freesurfer: workflow.connect([ (inputnode, bbregister, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id')]), (inputnode, bbregister, [('ref_epi_brain', 'source_file')]), (inputnode, transformer, [('fs_2_t1_transform', 'fs_2_t1_transform')]), (bbregister, transformer, [('out_fsl_file', 'bbreg_transform')]), (transformer, invt_bbr, [('out', 'in_file')]), (transformer, fsl2itk_fwd, [('out', 'transform_file')]), (transformer, outputnode, [('out', 'mat_epi_to_t1')]), (bbregister, outputnode, [('out_reg_file', 'fs_reg_file'), ('out_report', 'out_report')]), ]) else: workflow.connect([ (inputnode, wm_mask, [('t1_seg', 'in_file')]), (inputnode, flt_bbr_init, [('ref_epi_brain', 'in_file')]), (inputnode, flt_bbr_init, [('t1_brain', 'reference')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), (inputnode, flt_bbr, [('t1_brain', 'reference')]), (inputnode, flt_bbr, [('ref_epi_brain', 'in_file')]), (wm_mask, flt_bbr, [('out', 'wm_seg')]), (flt_bbr, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (flt_bbr, outputnode, [('out_matrix_file', 'mat_epi_to_t1'), ('out_report', 'out_report')]), ]) return workflow
def create_wf_c3d_fsl_to_itk(map_node, input_image_type=0, name='create_wf_c3d_fsl_to_itk'): """ Converts an FSL-format output matrix to an ITK-format (ANTS) matrix for use with ANTS registration tools. Parameters ---------- name : string, optional Name of the workflow. Returns ------- fsl_to_itk_conversion : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: inputspec.affine_file : string (nifti file) Output matrix of FSL-based functional to anatomical registration inputspec.reference_file : string (nifti file) File of skull-stripped anatomical brain to be used in affine conversion inputspec.source_file : string (nifti file) Should match the input of the apply warp (in_file) unless you are applying the warp to a 4-d file, in which case this file should be a mean_functional file Workflow Outputs:: outputspec.itk_transform : string (nifti file) Converted affine transform in ITK format usable with ANTS """ import nipype.interfaces.c3 as c3 from nipype.interfaces.utility import Function from CPAC.registration.utils import change_itk_transform_type from nipype.interfaces.afni import preprocess fsl_to_itk_conversion = pe.Workflow(name=name) inputspec = pe.Node(util.IdentityInterface(fields=['affine_file', 'reference_file', 'source_file']), name='inputspec') # converts FSL-format .mat affine xfm into ANTS-format .txt # .mat affine comes from Func->Anat registration if map_node == 0: fsl_reg_2_itk = pe.Node(c3.C3dAffineTool(), name='fsl_reg_2_itk') elif map_node == 1: fsl_reg_2_itk = pe.MapNode(c3.C3dAffineTool(), name='fsl_reg_2_itk_mapnode', iterfield=['source_file']) fsl_reg_2_itk.inputs.itk_transform = True fsl_reg_2_itk.inputs.fsl2ras = True itk_imports = ['import os'] if map_node == 0: 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') elif map_node == 1: change_transform = pe.MapNode(util.Function( input_names=['input_affine_file'], output_names=['updated_affine_file'], function=change_itk_transform_type, imports=itk_imports), name='change_transform_type', iterfield=['input_affine_file']) outputspec = pe.Node(util.IdentityInterface(fields=['itk_transform']), name='outputspec') fsl_to_itk_conversion.connect(inputspec, 'affine_file', fsl_reg_2_itk, 'transform_file') fsl_to_itk_conversion.connect(inputspec, 'reference_file', fsl_reg_2_itk, 'reference_file') # source_file input of the conversion must be a 3D file, so if the source # file is 4D (input_image_type=3), average it into a 3D file first if input_image_type == 0: fsl_to_itk_conversion.connect(inputspec, 'source_file', fsl_reg_2_itk, 'source_file') elif input_image_type == 3: try: tstat_source = pe.Node(interface=preprocess.TStat(), name='fsl_to_itk_tcat_source') except AttributeError: from nipype.interfaces.afni import utils as afni_utils tstat_source = pe.Node(interface=afni_utils.TStat(), name='fsl_to_itk_tcat_source') tstat_source.inputs.outputtype = 'NIFTI_GZ' tstat_source.inputs.options = '-mean' fsl_to_itk_conversion.connect(inputspec, 'source_file', tstat_source, 'in_file') fsl_to_itk_conversion.connect(tstat_source, 'out_file', fsl_reg_2_itk, 'source_file') fsl_to_itk_conversion.connect(fsl_reg_2_itk, 'itk_transform', change_transform, 'input_affine_file') fsl_to_itk_conversion.connect(change_transform, 'updated_affine_file', outputspec, 'itk_transform') return fsl_to_itk_conversion
if c in valid_chars) + "_roi.nii.gz" sphere = pe.Node(fsl.ImageMaths(), name="sphere") sphere.inputs.out_data_type = "float" sphere.inputs.op_string = "-kernel sphere 6 -fmean -bin" wf.connect(point, "out_file", sphere, "in_file") wf.connect(roi_infosource, ("roi", format_filename), sphere, "out_file") # mask2func = pe.Node(fsl.ApplyWarp(), name="mask2func") # mask2func.inputs.datatype = "float" # wf.connect(normalize, 'invert_warp.inverted_warp_file', mask2func, "field_file") # wf.connect(datagrabber, "preprocessed_epi", mask2func, "ref_file") # wf.connect(sphere, "out_file", mask2func, "in_file") # wf.connect(inv_func2anat, 'out_file', mask2func, "postmat") func2anat_fsl2itk = pe.Node(c3.C3dAffineTool(), name="func2anat_fsl2itk") func2anat_fsl2itk.inputs.itk_transform = True func2anat_fsl2itk.inputs.fsl2ras = True wf.connect(datagrabber, "func2anat_transform", func2anat_fsl2itk, "transform_file") wf.connect(datagrabber, "epi_mask", func2anat_fsl2itk, "source_file") wf.connect(ants_normalize, "brain_2nii.out_file", func2anat_fsl2itk, "reference_file") collect_transforms = pe.Node(util.Merge(3), name='collect_transforms') wf.connect(func2anat_fsl2itk, "itk_transform", collect_transforms, "in1") wf.connect(ants_normalize, "outputspec.affine_transformation", collect_transforms, "in2") wf.connect(ants_normalize, "outputspec.inverse_warp", collect_transforms, "in3")
def init_epi_hmc_wf(metadata, bold_file_size_gb, ignore, name='epi_hmc_wf'): """ Performs :abbr:`HMC (head motion correction)` over the input :abbr:`EPI (echo-planar imaging)` image. """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['epi']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['xforms', 'epi_hmc', 'epi_split', 'epi_mask', 'ref_image', 'ref_image_brain', 'movpar_file', 'n_volumes_to_discard', 'epi_mask_report']), name='outputnode') def normalize_motion_func(in_file, format): import os import numpy as np from nipype.utils.misc import normalize_mc_params mpars = np.loadtxt(in_file) # mpars is N_t x 6 mpars = np.apply_along_axis(func1d=normalize_mc_params, axis=1, arr=mpars, source=format) np.savetxt("motion_params.txt", mpars) return os.path.abspath("motion_params.txt") normalize_motion = pe.Node(niu.Function(function=normalize_motion_func), name="normalize_motion", run_without_submitting=True) normalize_motion.inputs.format = "FSL" # Head motion correction (hmc) hmc = pe.Node(fsl.MCFLIRT( save_mats=True, save_plots=True), name='EPI_hmc') hmc.interface.estimated_memory_gb = bold_file_size_gb * 3 hcm2itk = pe.MapNode(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), iterfield=['transform_file'], name='hcm2itk') enhance_and_skullstrip_epi_wf = init_enhance_and_skullstrip_epi_wf() gen_ref = pe.Node(EstimateReferenceImage(), name="gen_ref") workflow.connect([ (inputnode, gen_ref, [('epi', 'in_file')]), (gen_ref, enhance_and_skullstrip_epi_wf, [('ref_image', 'inputnode.in_file')]), (gen_ref, hmc, [('ref_image', 'ref_file')]), (enhance_and_skullstrip_epi_wf, outputnode, [('outputnode.bias_corrected_file', 'ref_image'), ('outputnode.mask_file', 'epi_mask'), ('outputnode.out_report', 'epi_mask_report'), ('outputnode.skull_stripped_file', 'ref_image_brain')]), ]) split = pe.Node(fsl.Split(dimension='t'), name='split') split.interface.estimated_memory_gb = bold_file_size_gb * 3 if "SliceTiming" in metadata and 'slicetiming' not in ignore: LOGGER.info('Slice-timing correction will be included.') def create_custom_slice_timing_file_func(metadata): import os slice_timings = metadata["SliceTiming"] slice_timings_ms = [str(t) for t in slice_timings] out_file = "timings.1D" with open("timings.1D", "w") as fp: fp.write("\t".join(slice_timings_ms)) return os.path.abspath(out_file) create_custom_slice_timing_file = pe.Node( niu.Function(function=create_custom_slice_timing_file_func), name="create_custom_slice_timing_file", run_without_submitting=True) create_custom_slice_timing_file.inputs.metadata = metadata slice_timing_correction = pe.Node(interface=afni.TShift(), name='slice_timing_correction') slice_timing_correction.inputs.outputtype = 'NIFTI_GZ' slice_timing_correction.inputs.tr = str(metadata["RepetitionTime"]) + "s" def prefix_at(x): return "@" + x workflow.connect([ (inputnode, slice_timing_correction, [('epi', 'in_file')]), (gen_ref, slice_timing_correction, [('n_volumes_to_discard', 'ignore')]), (create_custom_slice_timing_file, slice_timing_correction, [ (('out', prefix_at), 'tpattern')]), (slice_timing_correction, hmc, [('out_file', 'in_file')]) ]) else: workflow.connect([ (inputnode, hmc, [('epi', 'in_file')]) ]) workflow.connect([ (hmc, hcm2itk, [('mat_file', 'transform_file')]), (gen_ref, hcm2itk, [('ref_image', 'source_file'), ('ref_image', 'reference_file')]), (hcm2itk, outputnode, [('itk_transform', 'xforms')]), (hmc, normalize_motion, [('par_file', 'in_file')]), (normalize_motion, outputnode, [('out', 'movpar_file')]), (inputnode, split, [('epi', 'in_file')]), (split, outputnode, [('out_files', 'epi_split')]), ]) return workflow
def epi_pipeline(name='susceptibility_distortion_correction_using_t1'): """ This workflow allows to correct for echo-planareinduced susceptibility artifacts without fieldmap (e.g. ADNI Database) by elastically register DWIs to their respective baseline T1-weighted structural scans using an inverse consistent registration algorithm with a mutual information cost function (SyN algorithm). This workflow allows also a coregistration of DWIs with their respective baseline T1-weighted structural scans in order to latter combine tracks and cortex parcelation. .. warning:: This workflow rotates the `b`-vectors' .. References .. Nir et al. (Neurobiology of Aging 2015)- Connectivity network measures predict volumetric atrophy in mild cognitive impairment Leow et al. (IEEE Trans Med Imaging 2007)- Statistical Properties of Jacobian Maps and the Realization of Unbiased Large Deformation Nonlinear Image Registration Example ------- >>> epi = epi_pipeline() >>> epi.inputs.inputnode.DWI = 'DWI.nii' >>> epi.inputs.inputnode.bvec = 'bvec.txt' >>> epi.inputs.inputnode.T1 = 'T1.nii' >>> epi.run() # doctest: +SKIP """ from clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_utils import create_jacobian_determinant_image, change_itk_transform_type, expend_matrix_list, rotate_bvecs, ants_registration_syn_quick, ants_warp_image_multi_transform, ants_combin_transform import nipype.pipeline.engine as pe import nipype.interfaces.utility as niu import nipype.interfaces.fsl as fsl import nipype.interfaces.c3 as c3 inputnode = pe.Node(niu.IdentityInterface(fields=['T1', 'DWI', 'bvec']), name='inputnode') split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs') pick_ref = pe.Node(niu.Select(), name='Pick_b0') pick_ref.inputs.index = [0] flirt_b0_2_T1 = pe.Node(interface=fsl.FLIRT(dof=6), name='flirt_B0_2_T1') flirt_b0_2_T1.inputs.interp = "spline" flirt_b0_2_T1.inputs.cost = 'normmi' flirt_b0_2_T1.inputs.cost_func = 'normmi' apply_xfm = pe.Node(interface=fsl.preprocess.ApplyXFM(), name='apply_xfm') apply_xfm.inputs.apply_xfm = True expend_matrix = pe.Node( interface=niu.Function( input_names=['in_matrix', 'in_bvec'], output_names=['out_matrix_list'], function=expend_matrix_list), name='expend_matrix') rot_bvec = pe.Node( niu.Function( input_names=['in_matrix', 'in_bvec'], output_names=['out_file'], function=rotate_bvecs), name='Rotate_Bvec') antsRegistrationSyNQuick = pe.Node( interface=niu.Function( input_names=['fix_image', 'moving_image'], output_names=['image_warped', 'affine_matrix', 'warp', 'inverse_warped', 'inverse_warp'], function=ants_registration_syn_quick), name='antsRegistrationSyNQuick') c3d_flirt2ants = pe.Node(c3.C3dAffineTool(), name='fsl_reg_2_itk') c3d_flirt2ants.inputs.itk_transform = True c3d_flirt2ants.inputs.fsl2ras = True change_transform = pe.Node(niu.Function( input_names=['input_affine_file'], output_names=['updated_affine_file'], function=change_itk_transform_type), name='change_transform_type') merge_transform = pe.Node(niu.Merge(3), name='MergeTransforms') apply_transform = pe.MapNode(interface=niu.Function(input_names=['fix_image', 'moving_image', 'ants_warp_affine'], output_names=['out_warp_field', 'out_warped'], function=ants_combin_transform), iterfield=['moving_image'], name='warp_filed') jacobian = pe.MapNode(interface=niu.Function(input_names=['imageDimension', 'deformationField', 'outputImage'], output_names=['outputImage'], function=create_jacobian_determinant_image), iterfield=['deformationField'], name='jacobian') jacobian.inputs.imageDimension = 3 jacobian.inputs.outputImage = 'Jacobian_image.nii.gz' jacmult = pe.MapNode(fsl.MultiImageMaths(op_string='-mul %s'), iterfield=['in_file', 'operand_files'], name='ModulateDWIs') thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=['in_file'], name='RemoveNegative') merge = pe.Node(fsl.Merge(dimension='t'), name='MergeDWIs') outputnode = pe.Node(niu.IdentityInterface(fields=['DWI_2_T1_Coregistration_matrix', 'epi_correction_deformation_field', 'epi_correction_affine_transform', 'epi_correction_image_warped', 'DWIs_epicorrected', 'warp_epi', 'out_bvec' ]), name='outputnode') wf = pe.Workflow(name='epi_pipeline') wf.connect([(inputnode, split, [('DWI', 'in_file')])]) wf.connect([(split, pick_ref, [('out_files', 'inlist')])]) wf.connect([(pick_ref, flirt_b0_2_T1, [('out', 'in_file')])]) wf.connect([(inputnode, flirt_b0_2_T1, [('T1', 'reference')])]) wf.connect([(inputnode, rot_bvec, [('bvec', 'in_bvec')])]) wf.connect([(flirt_b0_2_T1, expend_matrix, [('out_matrix_file', 'in_matrix')])]) wf.connect([(inputnode, expend_matrix, [('bvec', 'in_bvec')])]) wf.connect([(expend_matrix, rot_bvec, [('out_matrix_list', 'in_matrix')])]) wf.connect([(inputnode, antsRegistrationSyNQuick, [('T1', 'fix_image')])]) wf.connect([(flirt_b0_2_T1, antsRegistrationSyNQuick, [('out_file', 'moving_image')])]) wf.connect([(inputnode, c3d_flirt2ants, [('T1', 'reference_file')])]) wf.connect([(pick_ref, c3d_flirt2ants, [('out', 'source_file')])]) wf.connect([(flirt_b0_2_T1, c3d_flirt2ants, [('out_matrix_file', 'transform_file')])]) wf.connect([(c3d_flirt2ants, change_transform, [('itk_transform', 'input_affine_file')])]) wf.connect([(antsRegistrationSyNQuick, merge_transform, [('warp', 'in1')])]) wf.connect([(antsRegistrationSyNQuick, merge_transform, [('affine_matrix', 'in2')])]) wf.connect([(change_transform, merge_transform, [('updated_affine_file', 'in3')])]) wf.connect([(inputnode, apply_transform, [('T1', 'fix_image')])]) wf.connect([(split, apply_transform, [('out_files', 'moving_image')])]) wf.connect([(merge_transform, apply_transform, [('out', 'ants_warp_affine')])]) wf.connect([(apply_transform, jacobian, [('out_warp_field', 'deformationField')])]) wf.connect([(apply_transform, jacmult, [('out_warped', 'operand_files')])]) wf.connect([(jacobian, jacmult, [('outputImage', 'in_file')])]) wf.connect([(jacmult, thres, [('out_file', 'in_file')])]) wf.connect([(thres, merge, [('out_file', 'in_files')])]) wf.connect([(merge, outputnode, [('merged_file', 'DWIs_epicorrected')])]) wf.connect([(flirt_b0_2_T1, outputnode, [('out_matrix_file', 'DWI_2_T1_Coregistration_matrix')])]) wf.connect([(antsRegistrationSyNQuick, outputnode, [('warp', 'epi_correction_deformation_field'), ('affine_matrix', 'epi_correction_affine_transform'), ('image_warped', 'epi_correction_image_warped')])]) wf.connect([(merge_transform, outputnode, [('out', 'warp_epi')])]) wf.connect([(rot_bvec, outputnode, [('out_file', 'out_bvec')])]) return wf
(mosaic_suvr_pvc, datasink, [('mosaic', 'QC.@mosaic_suvr_pvc')]), ]) av1451_workflow.connect([ (SUVR_pvc_workflow, MNI_workflow, [('SUVR.out_file', 'transform_suvr_pvc.in_file')]), ]) MNI_workflow.write_graph('MNI.dot', graph2use='colored', simple_form=True) # 7. MNI SPACE -- deformable alignment onto study-specific template # placeholder pet = Node(interface=IdentityInterface(fields=['pet']), name="pet") # convert the affine transform from PET to MRI obtained via FSL to ANTs format convert2itk = Node(interface=c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name="convert2itk") # merge affine transform to MRI with composite warp to study-specific template in MNI space # first should be composite # second should be pet-to-mri merge_list = Node(Merge(2), name='merge_list') merge_list.inputs.in1 = args.mnitransform warp_pet = Node( interface=ants.ApplyTransforms(reference_image=blsa_template_mni), name="warp_pet") warp_suvr = warp_pet.clone(name='warp_suvr') mask_pet = Node(interface=fsl.ImageMaths(op_string=' -mul ', suffix='_mul',
def init_fsl_gebbr_wf(use_bbr, asl2t1w_dof, asl2t1w_init, sloppy=False, name='fsl_bbr_wf'): """ """ from ...niworkflows.engine.workflows import LiterateWorkflow as Workflow from ...niworkflows.utils.images import dseg_label as _dseg_label from ...niworkflows.interfaces.freesurfer import PatchedLTAConvert as LTAConvert from ...niworkflows.interfaces.registration import FLIRTRPT workflow = Workflow(name=name) workflow.__desc__ = """\ The ASL reference was then co-registered to the T1w reference using `flirt` [FSL {fsl_ver}, @flirt] with the boundary-based registration [@bbr] cost-function. Co-registration was configured with nine degrees of freedom to account for distortions remaining in the ASL reference. """.format(fsl_ver=FLIRTRPT().version or '<ver>') inputnode = pe.Node( niu.IdentityInterface(['in_file', 't1w_dseg', 't1w_brain']), # FLIRT BBR name='inputnode') outputnode = pe.Node(niu.IdentityInterface( ['itk_asl_to_t1', 'itk_t1_to_asl', 'out_report', 'fallback']), name='outputnode') wm_mask = pe.Node(niu.Function(function=_dseg_label), name='wm_mask') wm_mask.inputs.label = 2 # BIDS default is WM=2 flt_bbr_init = pe.Node(FLIRTRPT(dof=6, generate_report=not use_bbr, uses_qform=True), name='flt_bbr_init') if asl2t1w_init not in ("register", "header"): raise ValueError( f"Unknown ASL-T1w initialization option: {asl2t1w_init}") if asl2t1w_init == "header": raise NotImplementedError( "Header-based registration initialization not supported for FSL") invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invt_bbr', mem_gb=DEFAULT_MEMORY_MIN_GB) # ASL to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd', mem_gb=DEFAULT_MEMORY_MIN_GB) fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, flt_bbr_init, [('in_file', 'in_file'), ('t1w_brain', 'reference')]), (inputnode, fsl2itk_fwd, [('t1w_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1w_brain', 'source_file')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_asl_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_asl')]), ]) outputnode.inputs.fallback = True # Short-circuit workflow building, use rigid registration if use_bbr is False: workflow.connect([ (flt_bbr_init, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr_init, fsl2itk_fwd, [('out_matrix_file', 'transform_file') ]), (flt_bbr_init, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = True return workflow flt_bbr = pe.Node(FLIRTRPT(cost_func='bbr', dof=asl2t1w_dof, generate_report=True), name='flt_bbr') FSLDIR = os.getenv('FSLDIR') if FSLDIR: flt_bbr.inputs.schedule = op.join(FSLDIR, 'etc/flirtsch/bbr.sch') else: # Should mostly be hit while building docs LOGGER.warning("FSLDIR unset - using packaged BBR schedule") flt_bbr.inputs.schedule = pkgr.resource_filename( 'aslprep', 'data/flirtsch/bbr.sch') workflow.connect([ (inputnode, wm_mask, [('t1w_dseg', 'in_seg')]), (inputnode, flt_bbr, [('in_file', 'in_file')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), ]) if sloppy is True: downsample = pe.Node(niu.Function( function=_conditional_downsampling, output_names=["out_file", "out_mask"]), name='downsample') workflow.connect([ (inputnode, downsample, [("t1w_brain", "in_file")]), (wm_mask, downsample, [("out", "in_mask")]), (downsample, flt_bbr, [('out_file', 'reference'), ('out_mask', 'wm_seg')]), ]) else: workflow.connect([ (inputnode, flt_bbr, [('t1w_brain', 'reference')]), (wm_mask, flt_bbr, [('out', 'wm_seg')]), ]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: workflow.connect([ (flt_bbr, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (flt_bbr, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = False return workflow
def init_bbreg_wf(use_bbr, bold2t1w_dof, omp_nthreads, name='bbreg_wf'): """ Build a workflow to run FreeSurfer's ``bbregister``. This workflow uses FreeSurfer's ``bbregister`` to register a BOLD image to a T1-weighted structural image. It is a counterpart to :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf`, which performs the same task using FSL's FLIRT with a BBR cost function. The ``use_bbr`` option permits a high degree of control over registration. If ``False``, standard, affine coregistration will be performed using FreeSurfer's ``mri_coreg`` tool. If ``True``, ``bbregister`` will be seeded with the initial transform found by ``mri_coreg`` (equivalent to running ``bbregister --init-coreg``). If ``None``, after ``bbregister`` is run, the resulting affine transform will be compared to the initial transform found by ``mri_coreg``. Excessive deviation will result in rejecting the BBR refinement and accepting the original, affine registration. Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_bbreg_wf wf = init_bbreg_wf(use_bbr=True, bold2t1w_dof=9, omp_nthreads=1) Parameters ---------- use_bbr : bool or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration name : str, optional Workflow name (default: bbreg_wf) Inputs ------ in_file Reference BOLD image to be registered fsnative2t1w_xfm FSL-style affine matrix translating from FreeSurfer T1.mgz to T1w subjects_dir FreeSurfer SUBJECTS_DIR subject_id FreeSurfer subject ID (must have folder in SUBJECTS_DIR) t1w_brain Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf`) t1w_dseg Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_fsl_bbr_wf`) Outputs ------- itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1 space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (mri_coreg registration returned) """ workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD reference was then co-registered to the T1w reference using `bbregister` (FreeSurfer) which implements boundary-based registration [@bbr]. Co-registration was configured with {dof} degrees of freedom{reason}. """.format(dof={6: 'six', 9: 'nine', 12: 'twelve'}[bold2t1w_dof], reason='' if bold2t1w_dof == 6 else 'to account for distortions remaining in the BOLD reference') inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 'fsnative2t1w_xfm', 'subjects_dir', 'subject_id', # BBRegister 't1w_dseg', 't1w_brain']), # FLIRT BBR name='inputnode') outputnode = pe.Node( niu.IdentityInterface(['itk_bold_to_t1', 'itk_t1_to_bold', 'out_report', 'fallback']), name='outputnode') mri_coreg = pe.Node( MRICoregRPT(dof=bold2t1w_dof, sep=[4], ftol=0.0001, linmintol=0.01, generate_report=not use_bbr), name='mri_coreg', n_procs=omp_nthreads, mem_gb=5) lta_concat = pe.Node(ConcatenateLTA(out_file='out.lta'), name='lta_concat') # XXX LTA-FSL-ITK may ultimately be able to be replaced with a straightforward # LTA-ITK transform, but right now the translation parameters are off. lta2fsl_fwd = pe.Node(LTAConvert(out_fsl=True), name='lta2fsl_fwd') lta2fsl_inv = pe.Node(LTAConvert(out_fsl=True, invert=True), name='lta2fsl_inv') fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd', mem_gb=DEFAULT_MEMORY_MIN_GB) fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, mri_coreg, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id'), ('in_file', 'source_file')]), # Output ITK transforms (inputnode, lta_concat, [('fsnative2t1w_xfm', 'in_lta2')]), (lta_concat, lta2fsl_fwd, [('out_file', 'in_lta')]), (lta_concat, lta2fsl_inv, [('out_file', 'in_lta')]), (inputnode, fsl2itk_fwd, [('t1w_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1w_brain', 'source_file')]), (lta2fsl_fwd, fsl2itk_fwd, [('out_fsl', 'transform_file')]), (lta2fsl_inv, fsl2itk_inv, [('out_fsl', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_bold_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_bold')]), ]) # Short-circuit workflow building, use initial registration if use_bbr is False: workflow.connect([ (mri_coreg, outputnode, [('out_report', 'out_report')]), (mri_coreg, lta_concat, [('out_lta_file', 'in_lta1')])]) outputnode.inputs.fallback = True return workflow bbregister = pe.Node( BBRegisterRPT(dof=bold2t1w_dof, contrast_type='t2', registered_file=True, out_lta_file=True, generate_report=True), name='bbregister', mem_gb=12) workflow.connect([ (inputnode, bbregister, [('subjects_dir', 'subjects_dir'), ('subject_id', 'subject_id'), ('in_file', 'source_file')]), (mri_coreg, bbregister, [('out_lta_file', 'init_reg_file')]), ]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: workflow.connect([ (bbregister, outputnode, [('out_report', 'out_report')]), (bbregister, lta_concat, [('out_lta_file', 'in_lta1')])]) outputnode.inputs.fallback = False return workflow transforms = pe.Node(niu.Merge(2), run_without_submitting=True, name='transforms') reports = pe.Node(niu.Merge(2), run_without_submitting=True, name='reports') lta_ras2ras = pe.MapNode(LTAConvert(out_lta=True), iterfield=['in_lta'], name='lta_ras2ras', mem_gb=2) compare_transforms = pe.Node(niu.Function(function=compare_xforms), name='compare_transforms') select_transform = pe.Node(niu.Select(), run_without_submitting=True, name='select_transform') select_report = pe.Node(niu.Select(), run_without_submitting=True, name='select_report') workflow.connect([ (bbregister, transforms, [('out_lta_file', 'in1')]), (mri_coreg, transforms, [('out_lta_file', 'in2')]), # Normalize LTA transforms to RAS2RAS (inputs are VOX2VOX) and compare (transforms, lta_ras2ras, [('out', 'in_lta')]), (lta_ras2ras, compare_transforms, [('out_lta', 'lta_list')]), (compare_transforms, outputnode, [('out', 'fallback')]), # Select output transform (transforms, select_transform, [('out', 'inlist')]), (compare_transforms, select_transform, [('out', 'index')]), (select_transform, lta_concat, [('out', 'in_lta1')]), # Select output report (bbregister, reports, [('out_report', 'in1')]), (mri_coreg, reports, [('out_report', 'in2')]), (reports, select_report, [('out', 'inlist')]), (compare_transforms, select_report, [('out', 'index')]), (select_report, outputnode, [('out', 'out_report')]), ]) return workflow
def ants_apply_warps_func_mni(workflow, output_name, func_key, ref_key, num_strat, strat, interpolation_method='LanczosWindowedSinc', distcor=False, map_node=False, inverse=False, symmetry='asymmetric', input_image_type=0, num_ants_cores=1, registration_template='t1', func_type='non-ica-aroma', num_cpus=1): """ Applies previously calculated ANTS registration transforms to input images. This workflow employs the antsApplyTransforms tool: http://stnava.github.io/ANTs/ Parameters ---------- name : string, optional Name of the workflow. Returns ------- apply_ants_warp_wf : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: workflow: Nipype workflow object the workflow containing the resources involved output_name: str what the name of the warped functional should be when written to the resource pool func_key: string resource pool key correspoding to the node containing the 3D or 4D functional file to be written into MNI space, use 'leaf' for a leaf node ref_key: string resource pool key correspoding to the file path to the template brain used for functional-to-template registration num_strat: int the number of strategy objects strat: C-PAC Strategy object a strategy with one or more resource pools interpolation_method: str which interpolation to use when applying the warps, commonly used options are 'Linear', 'Bspline', 'LanczosWindowedSinc' (default) for derivatives and image data 'NearestNeighbor' for masks distcor: boolean indicates whether a distortion correction transformation should be added to the transforms, this of course requires that a distortion correction map exist in the resource pool map_node: boolean indicates whether a mapnode should be used, if TRUE func_key is expected to correspond to a list of resources that should each be written into standard space with the other parameters inverse: boolean writes the invrse of the transform, i.e. MNI->EPI instead of EPI->MNI input_image_type: int argument taken by the ANTs apply warp tool; in this case, should be 0 for scalars (default) and 3 for 4D functional time-series num_ants_cores: int the number of CPU cores dedicated to ANTS anatomical-to-standard registration registration_template: str which template to use as a target for the apply warps ('t1' or 'epi'), should be the same as the target used in the warp calculation (registration) func_type: str 'non-ica-aroma' or 'ica-aroma' - how to handle the functional time series based on the particular demands of ICA-AROMA processed time series num_cpus: int the number of CPUs dedicated to each participant workflow - this is used to determine how to parallelize the warp application step Workflow Outputs:: outputspec.output_image : string (nifti file) Normalized output file Workflow Graph: .. image:: :width: 500 Detailed Workflow Graph: .. image:: :width: 500 Apply the functional-to-structural and structural-to-template warps to the 4D functional time-series to warp it to template space. Parameters ---------- """ # if the input is a string, assume that it is resource pool key, # if it is a tuple, assume that it is a node, outfile pair, # otherwise, something funky is going on if isinstance(func_key, str): if func_key == "leaf": input_node, input_out = strat.get_leaf_properties() else: input_node, input_out = strat[func_key] elif isinstance(func_key, tuple): input_node, input_out = func_key if isinstance(ref_key, str): ref_node, ref_out = strat[ref_key] elif isinstance(ref_key, tuple): ref_node, ref_out = func_key # when inverse is enabled, we want to update the name of various # nodes so that we know they were inverted inverse_string = '' if inverse is True: inverse_string = '_inverse' # make sure that resource pool has some required resources before proceeding if 'fsl_mat_as_itk' not in strat and registration_template == 't1': fsl_reg_2_itk = pe.Node(c3.C3dAffineTool(), name='fsl_reg_2_itk_{0}'.format(num_strat)) 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 node, out_file = strat['functional_to_anat_linear_xfm'] workflow.connect(node, out_file, fsl_reg_2_itk, 'transform_file') node, out_file = strat['anatomical_brain'] workflow.connect(node, out_file, fsl_reg_2_itk, 'reference_file') ref_node, ref_out = strat['mean_functional'] workflow.connect(ref_node, ref_out, 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_{0}'.format(num_strat)) workflow.connect(fsl_reg_2_itk, 'itk_transform', change_transform, 'input_affine_file') strat.update_resource_pool( {'fsl_mat_as_itk': (change_transform, 'updated_affine_file')}) strat.append_name(fsl_reg_2_itk.name) # stack of transforms to be combined to acheive the desired transformation num_transforms = 5 collect_transforms_key = \ 'collect_transforms{0}'.format(inverse_string) if distcor is True and func_type not in 'ica-aroma': num_transforms = 6 collect_transforms_key = \ 'collect_transforms{0}{1}'.format('_distcor', inverse_string) if collect_transforms_key not in strat: if registration_template == 't1': # handle both symmetric and asymmetric transforms ants_transformation_dict = { 'asymmetric': { 'anatomical_to_mni_nonlinear_xfm': 'anatomical_to_mni_nonlinear_xfm', 'mni_to_anatomical_nonlinear_xfm': 'mni_to_anatomical_nonlinear_xfm', 'ants_affine_xfm': 'ants_affine_xfm', 'ants_rigid_xfm': 'ants_rigid_xfm', 'ants_initial_xfm': 'ants_initial_xfm', 'blip_warp': 'blip_warp', 'blip_warp_inverse': 'blip_warp_inverse', 'fsl_mat_as_itk': 'fsl_mat_as_itk', }, 'symmetric': { 'anatomical_to_mni_nonlinear_xfm': 'anatomical_to_symmetric_mni_nonlinear_xfm', 'mni_to_anatomical_nonlinear_xfm': 'symmetric_mni_to_anatomical_nonlinear_xfm', 'ants_affine_xfm': 'ants_symmetric_affine_xfm', 'ants_rigid_xfm': 'ants_symmetric_rigid_xfm', 'ants_initial_xfm': 'ants_symmetric_initial_xfm', 'blip_warp': 'blip_warp', 'blip_warp_inverse': 'blip_warp_inverse', 'fsl_mat_as_itk': 'fsl_mat_as_itk', } } # transforms to be concatenated, the first element of each tuple is # the resource pool key related to the resource that should be # connected in, and the second element is the input to which it # should be connected if inverse is True: if distcor is True and func_type not in 'ica-aroma': # Field file from anatomical nonlinear registration transforms_to_combine = [\ ('mni_to_anatomical_nonlinear_xfm', 'in6'), ('ants_affine_xfm', 'in5'), ('ants_rigid_xfm', 'in4'), ('ants_initial_xfm', 'in3'), ('fsl_mat_as_itk', 'in2'), ('blip_warp_inverse', 'in1')] else: transforms_to_combine = [\ ('mni_to_anatomical_nonlinear_xfm', 'in5'), ('ants_affine_xfm', 'in4'), ('ants_rigid_xfm', 'in3'), ('ants_initial_xfm', 'in2'), ('fsl_mat_as_itk', 'in1')] else: transforms_to_combine = [\ ('anatomical_to_mni_nonlinear_xfm', 'in1'), ('ants_affine_xfm', 'in2'), ('ants_rigid_xfm', 'in3'), ('ants_initial_xfm', 'in4'), ('fsl_mat_as_itk', 'in5')] if distcor is True and func_type not in 'ica-aroma': transforms_to_combine.append(('blip_warp', 'in6')) if registration_template == 'epi': # handle both symmetric and asymmetric transforms ants_transformation_dict = { 'asymmetric': { 'func_to_epi_nonlinear_xfm': 'func_to_epi_nonlinear_xfm', 'epi_to_func_nonlinear_xfm': 'epi_to_func_nonlinear_xfm', 'func_to_epi_ants_affine_xfm': 'func_to_epi_ants_affine_xfm', 'func_to_epi_ants_rigid_xfm': 'func_to_epi_ants_rigid_xfm', 'func_to_epi_ants_initial_xfm': 'func_to_epi_ants_initial_xfm', # 'blip_warp': 'blip_warp', # 'blip_warp_inverse': 'blip_warp_inverse', # 'fsl_mat_as_itk': 'fsl_mat_as_itk', }, # 'symmetric': { # 'func_to_epi_nonlinear_xfm': 'anatomical_to_mni_nonlinear_xfm', # 'func_to_epi_ants_affine_xfm': 'func_to_epi_ants_affine_xfm', # 'func_to_epi_ants_rigid_xfm': 'func_to_epi_ants_rigid_xfm', # 'func_to_epi_ants_initial_xfm': 'ants_initial_xfm', # 'blip_warp': 'blip_warp', # 'blip_warp_inverse': 'blip_warp_inverse', # 'fsl_mat_as_itk': 'fsl_mat_as_itk', # } } # transforms to be concatenated, the first element of each tuple is # the resource pool key related to the resource that should be # connected in, and the second element is the input to which it # should be connected if inverse is True: if distcor is True and func_type not in 'ica-aroma': # Field file from anatomical nonlinear registration transforms_to_combine = [\ ('epi_to_func_nonlinear_xfm', 'in4'), ('func_to_epi_ants_affine_xfm', 'in3'), ('func_to_epi_ants_rigid_xfm', 'in2'), ('func_to_epi_ants_initial_xfm', 'in1')] else: transforms_to_combine = [\ ('epi_to_func_nonlinear_xfm', 'in4'), ('func_to_epi_ants_affine_xfm', 'in3'), ('func_to_epi_ants_rigid_xfm', 'in2'), ('func_to_epi_ants_initial_xfm', 'in1')] else: transforms_to_combine = [\ ('func_to_epi_nonlinear_xfm', 'in1'), ('func_to_epi_ants_affine_xfm', 'in2'), ('func_to_epi_ants_rigid_xfm', 'in3'), ('func_to_epi_ants_initial_xfm', 'in4')] # define the node collect_transforms = pe.Node( util.Merge(num_transforms), name='collect_transforms_{0}_{1}_{2}_{3}'.format( output_name, inverse_string, registration_template, num_strat)) # wire in the various transformations for transform_key, input_port in transforms_to_combine: try: node, out_file = strat[ants_transformation_dict[symmetry] [transform_key]] except KeyError: raise Exception(locals()) workflow.connect(node, out_file, collect_transforms, input_port) # check transform list (if missing any init/rig/affine) and exclude Nonetype check_transform = pe.Node( util.Function( input_names=['transform_list'], output_names=['checked_transform_list', 'list_length'], function=check_transforms), name='check_transforms{0}_{1}_{2}_{3}'.format( output_name, inverse_string, registration_template, num_strat)) workflow.connect(collect_transforms, 'out', check_transform, 'transform_list') # 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='inverse_transform_flags_{0}_{1}_{2}_{3}'.format( output_name, inverse_string, registration_template, num_strat)) workflow.connect(check_transform, 'checked_transform_list', inverse_transform_flags, 'transform_list') # set the output strat.update_resource_pool({ collect_transforms_key: (check_transform, 'checked_transform_list') }) strat.append_name(check_transform.name) strat.append_name(inverse_transform_flags.name) #### now we add in the apply ants warps node if int(num_cpus) > 1 and input_image_type == 3: # parallelize time series warp application map_node = True if map_node: apply_ants_warp = pe.MapNode( interface=ants.ApplyTransforms(), name='apply_ants_warp_{0}_mapnode_{1}_{2}_{3}'.format( output_name, inverse_string, registration_template, num_strat), iterfield=['input_image'], mem_gb=10.0) else: apply_ants_warp = pe.Node( interface=ants.ApplyTransforms(), name='apply_ants_warp_{0}_{1}_{2}_{3}'.format( output_name, inverse_string, registration_template, num_strat), mem_gb=10.0) apply_ants_warp.inputs.out_postfix = '_antswarp' apply_ants_warp.interface.num_threads = int(num_ants_cores) if inverse is True: workflow.connect(inverse_transform_flags, 'inverse_transform_flags', apply_ants_warp, 'invert_transform_flags') # input_image_type: # (0 or 1 or 2 or 3) # Option specifying the input image type of scalar # (default), vector, tensor, or time series. apply_ants_warp.inputs.input_image_type = input_image_type apply_ants_warp.inputs.dimension = 3 apply_ants_warp.inputs.interpolation = interpolation_method node, out_file = strat[ref_key] workflow.connect(node, out_file, apply_ants_warp, 'reference_image') collect_node, collect_out = strat[collect_transforms_key] workflow.connect(collect_node, collect_out, apply_ants_warp, 'transforms') if output_name == "functional_to_standard": # write out the composite functional to standard transforms write_composite_xfm = pe.Node( interface=ants.ApplyTransforms(), name='write_composite_xfm_{0}_{1}_{2}_{3}'.format( output_name, inverse_string, registration_template, num_strat), mem_gb=8.0) write_composite_xfm.inputs.print_out_composite_warp_file = True write_composite_xfm.inputs.output_image = "func_to_standard_xfm.nii.gz" workflow.connect(input_node, input_out, write_composite_xfm, 'input_image') write_composite_xfm.inputs.input_image_type = input_image_type write_composite_xfm.inputs.dimension = 3 write_composite_xfm.inputs.interpolation = interpolation_method node, out_file = strat[ref_key] workflow.connect(node, out_file, write_composite_xfm, 'reference_image') collect_node, collect_out = strat[collect_transforms_key] workflow.connect(collect_node, collect_out, write_composite_xfm, 'transforms') # write_composite_inv_xfm = pe.Node( # interface=ants.ApplyTransforms(), # name='write_composite_xfm_{0}_{1}_{2}_{3}'.format(output_name, # '_inverse', registration_template, num_strat), mem_gb=1.5) # write_composite_inv_xfm.inputs.print_out_composite_warp_file = True # write_composite_inv_xfm.inputs.output_image = "func_to_standard_inverse-xfm.nii.gz" # # workflow.connect(input_node, input_out, # write_composite_inv_xfm, 'input_image') # # workflow.connect(inverse_transform_flags, 'inverse_transform_flags', # write_composite_inv_xfm, 'invert_transform_flags') # # # write_composite_inv_xfm.inputs.input_image_type = input_image_type # write_composite_inv_xfm.inputs.dimension = 3 # write_composite_inv_xfm.inputs.interpolation = interpolation_method # # node, out_file = strat[ref_key] # workflow.connect(node, out_file, # write_composite_inv_xfm, 'reference_image') # # collect_node, collect_out = strat[collect_transforms_key] # workflow.connect(collect_node, collect_out, # write_composite_inv_xfm, 'transforms') strat.update_resource_pool({ "functional_to_standard_xfm": (write_composite_xfm, 'output_image') }) #"functional_to_standard_inverse-xfm": (write_composite_inv_xfm, 'output_image') #}) # parallelize the apply warp, if multiple CPUs, and it's a time series! if int(num_cpus) > 1 and input_image_type == 3: node_id = f'_{output_name}_{inverse_string}_{registration_template}_{num_strat}' chunk_imports = ['import nibabel as nb'] chunk = pe.Node(Function(input_names=['func_file', 'n_cpus'], output_names=['TR_ranges'], function=chunk_ts, imports=chunk_imports), name=f'chunk_{node_id}') chunk.inputs.n_cpus = int(num_cpus) workflow.connect(input_node, input_out, chunk, 'func_file') split_imports = ['import os', 'import subprocess'] split = pe.Node(Function(input_names=['func_file', 'tr_ranges'], output_names=['split_funcs'], function=split_ts_chunks, imports=split_imports), name=f'split_{node_id}') workflow.connect(input_node, input_out, split, 'func_file') workflow.connect(chunk, 'TR_ranges', split, 'tr_ranges') workflow.connect(split, 'split_funcs', apply_ants_warp, 'input_image') func_concat = pe.Node(interface=afni_utils.TCat(), name=f'func_concat_{node_id}') func_concat.inputs.outputtype = 'NIFTI_GZ' workflow.connect(apply_ants_warp, 'output_image', func_concat, 'in_files') strat.update_resource_pool({output_name: (func_concat, 'out_file')}) else: workflow.connect(input_node, input_out, apply_ants_warp, 'input_image') strat.update_resource_pool( {output_name: (apply_ants_warp, 'output_image')}) strat.append_name(apply_ants_warp.name) return workflow
def epi_pipeline(name="susceptibility_distortion_correction_using_t1"): """ This workflow allows to correct for echo-planareinduced susceptibility artifacts without fieldmap (e.g. ADNI Database) by elastically register DWIs to their respective baseline T1-weighted structural scans using an inverse consistent registration algorithm with a mutual information cost function (SyN algorithm). This workflow allows also a coregistration of DWIs with their respective baseline T1-weighted structural scans in order to latter combine tracks and cortex parcelation. .. warning:: This workflow rotates the `b`-vectors' .. References .. Nir et al. (Neurobiology of Aging 2015)- Connectivity network measures predict volumetric atrophy in mild cognitive impairment Leow et al. (IEEE Trans Med Imaging 2007)- Statistical Properties of Jacobian Maps and the Realization of Unbiased Large Deformation Nonlinear Image Registration Example ------- >>> epi = epi_pipeline() >>> epi.inputs.inputnode.DWI = 'DWI.nii' >>> epi.inputs.inputnode.bvec = 'bvec.txt' >>> epi.inputs.inputnode.T1 = 'T1.nii' >>> epi.run() # doctest: +SKIP """ import nipype.interfaces.c3 as c3 import nipype.interfaces.fsl as fsl import nipype.interfaces.utility as niu import nipype.pipeline.engine as pe from clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_utils import ( ants_combin_transform, ants_registration_syn_quick, ants_warp_image_multi_transform, change_itk_transform_type, create_jacobian_determinant_image, expend_matrix_list, rotate_bvecs, ) inputnode = pe.Node(niu.IdentityInterface(fields=["T1", "DWI", "bvec"]), name="inputnode") split = pe.Node(fsl.Split(dimension="t"), name="SplitDWIs") pick_ref = pe.Node(niu.Select(), name="Pick_b0") pick_ref.inputs.index = [0] flirt_b0_2_T1 = pe.Node(interface=fsl.FLIRT(dof=6), name="flirt_B0_2_T1") flirt_b0_2_T1.inputs.interp = "spline" flirt_b0_2_T1.inputs.cost = "normmi" flirt_b0_2_T1.inputs.cost_func = "normmi" apply_xfm = pe.Node(interface=fsl.preprocess.ApplyXFM(), name="apply_xfm") apply_xfm.inputs.apply_xfm = True expend_matrix = pe.Node( interface=niu.Function( input_names=["in_matrix", "in_bvec"], output_names=["out_matrix_list"], function=expend_matrix_list, ), name="expend_matrix", ) rot_bvec = pe.Node( niu.Function( input_names=["in_matrix", "in_bvec"], output_names=["out_file"], function=rotate_bvecs, ), name="Rotate_Bvec", ) antsRegistrationSyNQuick = pe.Node( interface=niu.Function( input_names=["fix_image", "moving_image"], output_names=[ "image_warped", "affine_matrix", "warp", "inverse_warped", "inverse_warp", ], function=ants_registration_syn_quick, ), name="antsRegistrationSyNQuick", ) c3d_flirt2ants = pe.Node(c3.C3dAffineTool(), name="fsl_reg_2_itk") c3d_flirt2ants.inputs.itk_transform = True c3d_flirt2ants.inputs.fsl2ras = True change_transform = pe.Node( niu.Function( input_names=["input_affine_file"], output_names=["updated_affine_file"], function=change_itk_transform_type, ), name="change_transform_type", ) merge_transform = pe.Node(niu.Merge(3), name="MergeTransforms") apply_transform = pe.MapNode( interface=niu.Function( input_names=["fix_image", "moving_image", "ants_warp_affine"], output_names=["out_warp_field", "out_warped"], function=ants_combin_transform, ), iterfield=["moving_image"], name="warp_filed", ) jacobian = pe.MapNode( interface=niu.Function( input_names=["imageDimension", "deformationField", "outputImage"], output_names=["outputImage"], function=create_jacobian_determinant_image, ), iterfield=["deformationField"], name="jacobian", ) jacobian.inputs.imageDimension = 3 jacobian.inputs.outputImage = "Jacobian_image.nii.gz" jacmult = pe.MapNode( fsl.MultiImageMaths(op_string="-mul %s"), iterfield=["in_file", "operand_files"], name="ModulateDWIs", ) thres = pe.MapNode(fsl.Threshold(thresh=0.0), iterfield=["in_file"], name="RemoveNegative") merge = pe.Node(fsl.Merge(dimension="t"), name="MergeDWIs") outputnode = pe.Node( niu.IdentityInterface(fields=[ "DWI_2_T1_Coregistration_matrix", "epi_correction_deformation_field", "epi_correction_affine_transform", "epi_correction_image_warped", "DWIs_epicorrected", "warp_epi", "out_bvec", ]), name="outputnode", ) wf = pe.Workflow(name="epi_pipeline") wf.connect([(inputnode, split, [("DWI", "in_file")])]) wf.connect([(split, pick_ref, [("out_files", "inlist")])]) wf.connect([(pick_ref, flirt_b0_2_T1, [("out", "in_file")])]) wf.connect([(inputnode, flirt_b0_2_T1, [("T1", "reference")])]) wf.connect([(inputnode, rot_bvec, [("bvec", "in_bvec")])]) wf.connect([(flirt_b0_2_T1, expend_matrix, [("out_matrix_file", "in_matrix")])]) wf.connect([(inputnode, expend_matrix, [("bvec", "in_bvec")])]) wf.connect([(expend_matrix, rot_bvec, [("out_matrix_list", "in_matrix")])]) wf.connect([(inputnode, antsRegistrationSyNQuick, [("T1", "fix_image")])]) wf.connect([(flirt_b0_2_T1, antsRegistrationSyNQuick, [("out_file", "moving_image")])]) wf.connect([(inputnode, c3d_flirt2ants, [("T1", "reference_file")])]) wf.connect([(pick_ref, c3d_flirt2ants, [("out", "source_file")])]) wf.connect([(flirt_b0_2_T1, c3d_flirt2ants, [("out_matrix_file", "transform_file")])]) wf.connect([(c3d_flirt2ants, change_transform, [("itk_transform", "input_affine_file")])]) wf.connect([(antsRegistrationSyNQuick, merge_transform, [("warp", "in1")]) ]) wf.connect([(antsRegistrationSyNQuick, merge_transform, [("affine_matrix", "in2")])]) wf.connect([(change_transform, merge_transform, [("updated_affine_file", "in3")])]) wf.connect([(inputnode, apply_transform, [("T1", "fix_image")])]) wf.connect([(split, apply_transform, [("out_files", "moving_image")])]) wf.connect([(merge_transform, apply_transform, [("out", "ants_warp_affine") ])]) wf.connect([(apply_transform, jacobian, [("out_warp_field", "deformationField")])]) wf.connect([(apply_transform, jacmult, [("out_warped", "operand_files")])]) wf.connect([(jacobian, jacmult, [("outputImage", "in_file")])]) wf.connect([(jacmult, thres, [("out_file", "in_file")])]) wf.connect([(thres, merge, [("out_file", "in_files")])]) wf.connect([(merge, outputnode, [("merged_file", "DWIs_epicorrected")])]) wf.connect([( flirt_b0_2_T1, outputnode, [("out_matrix_file", "DWI_2_T1_Coregistration_matrix")], )]) wf.connect([( antsRegistrationSyNQuick, outputnode, [ ("warp", "epi_correction_deformation_field"), ("affine_matrix", "epi_correction_affine_transform"), ("image_warped", "epi_correction_image_warped"), ], )]) wf.connect([(merge_transform, outputnode, [("out", "warp_epi")])]) wf.connect([(rot_bvec, outputnode, [("out_file", "out_bvec")])]) return wf
def epi_mean_t1_registration(name='EPIMeanNormalization', settings=None): """ Uses FSL FLIRT with the BBR cost function to find the transform that maps the EPI space into the T1-space """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['epi', 'epi_mean', 't1_brain', 't1_seg']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['mat_epi_to_t1']), name='outputnode') # Extract wm mask from segmentation wm_mask = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=_extract_wm), name='WM_mask') flt_bbr_init = pe.Node(fsl.FLIRT(dof=6, out_matrix_file='init.mat'), name='Flirt_BBR_init') flt_bbr = pe.Node(fsl.FLIRT(dof=6, cost_func='bbr'), name='Flirt_BBR') flt_bbr.inputs.schedule = op.join(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch') # make equivalent warp fields invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='Flirt_BBR_Inv') # EPI to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd') fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv') # Write EPI mean in T1w space ds_t1w = pe.Node(DerivativesDataSink(base_directory=settings['output_dir'], suffix='hmc_t1'), name='DerivHMC_T1w') # Write registrated file in the designated output dir ds_tfm_fwd = pe.Node(DerivativesDataSink( base_directory=settings['output_dir'], suffix='epi2t1w_affine'), name='DerivEPI_to_T1w_fwd') ds_tfm_inv = pe.Node(DerivativesDataSink( base_directory=settings['output_dir'], suffix='t1w2epi_affine'), name='DerivEPI_to_T1w_inv') workflow.connect([ (inputnode, wm_mask, [('t1_seg', 'in_file')]), (inputnode, flt_bbr_init, [('t1_brain', 'reference')]), (inputnode, fsl2itk_fwd, [('t1_brain', 'reference_file'), ('epi_mean', 'source_file')]), (inputnode, fsl2itk_inv, [('epi_mean', 'reference_file'), ('t1_brain', 'source_file')]), (inputnode, flt_bbr_init, [('epi_mean', 'in_file')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), (inputnode, flt_bbr, [('t1_brain', 'reference')]), (inputnode, flt_bbr, [('epi_mean', 'in_file')]), (wm_mask, flt_bbr, [('out_file', 'wm_seg')]), (flt_bbr, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'mat_epi_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'mat_t1_to_epi')]), (inputnode, ds_tfm_fwd, [('epi', 'source_file')]), (inputnode, ds_tfm_inv, [('epi', 'source_file')]), (inputnode, ds_t1w, [('epi', 'source_file')]), (fsl2itk_fwd, ds_tfm_fwd, [('itk_transform', 'in_file')]), (fsl2itk_inv, ds_tfm_inv, [('itk_transform', 'in_file')]), (flt_bbr, ds_t1w, [('out_file', 'in_file')]) ]) # Plots for report png_sbref_t1 = pe.Node(niu.Function( input_names=['in_file', 'overlay_file', 'out_file'], output_names=['out_file'], function=stripped_brain_overlay), name='PNG_sbref_t1') png_sbref_t1.inputs.out_file = 'sbref_to_t1.png' # Write corrected file in the designated output dir ds_png = pe.Node(DerivativesDataSink(base_directory=settings['output_dir'], suffix='epi_to_t1'), name='DerivativesPNG') workflow.connect([(flt_bbr, png_sbref_t1, [('out_file', 'overlay_file')]), (inputnode, png_sbref_t1, [('t1_seg', 'in_file')]), (inputnode, ds_png, [('epi', 'source_file')]), (png_sbref_t1, ds_png, [('out_file', 'in_file')])]) return workflow
def create_fsl_to_itk_conversion(mapnode, name='fsl_to_itk_conversion'): """ Converts an FSL-format output matrix to an ITK-format (ANTS) matrix for use with ANTS registration tools. Parameters ---------- name : string, optional Name of the workflow. Returns ------- fsl_to_itk_conversion : nipype.pipeline.engine.Workflow Notes ----- Workflow Inputs:: inputspec.transform_file : string (nifti file) Output matrix of FSL-based functional to anatomical registration inputspec.reference_file : string (nifti file) File of skull-stripped anatomical brain to be used in affine conversion inputspec.source_file : string (nifti file) Should match the input of the apply warp (in_file) unless you are applying the warp to a 4-d file, in which case this file should be a mean_functional file Workflow Outputs:: outputspec.itk_transform : string (nifti file) Converted affine transform in ITK format usable with ANTS """ fsl_to_itk_conversion = pe.Workflow(name=name) inputspec = pe.Node(util.IdentityInterface( fields=['transform_file', 'reference_file', 'source_file']), name='inputspec') # converts FSL-format .mat affine xfm into ANTS-format .txt # .mat affine comes from Func->Anat registration if mapnode == 0: fsl_reg_2_itk = pe.Node(c3.C3dAffineTool(), name='fsl_reg_2_itk') elif mapnode == 1: fsl_reg_2_itk = pe.MapNode(interface=c3.C3dAffineTool(), name='fsl_reg_2_itk', iterfield=['source_file']) fsl_reg_2_itk.inputs.itk_transform = True fsl_reg_2_itk.inputs.fsl2ras = True outputspec = pe.Node(util.IdentityInterface(fields=['itk_transform']), name='outputspec') fsl_to_itk_conversion.connect(inputspec, 'transform_file', fsl_reg_2_itk, 'transform_file') fsl_to_itk_conversion.connect(inputspec, 'reference_file', fsl_reg_2_itk, 'reference_file') fsl_to_itk_conversion.connect(inputspec, 'source_file', fsl_reg_2_itk, 'source_file') fsl_to_itk_conversion.connect(fsl_reg_2_itk, 'itk_transform', outputspec, 'itk_transform') return fsl_to_itk_conversion
def epi_hmc(name='EPI_HMC', settings=None): """ Performs :abbr:`HMC (head motion correction)` over the input :abbr:`EPI (echo-planar imaging)` image. """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=['epi']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['epi_brain', 'xforms', 'epi_mask', 'epi_mean']), name='outputnode') bet = pe.Node(fsl.BET(functional=True, frac=0.6), name='EPI_bet') # Head motion correction (hmc) hmc = pe.Node(fsl.MCFLIRT(save_mats=True, save_plots=True, mean_vol=True), name='EPI_hmc') pick_1st = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name='EPIPickFirst') hcm2itk = pe.MapNode(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), iterfield=['transform_file'], name='hcm2itk') avscale = pe.MapNode(fsl.utils.AvScale(all_param=True), name='AvScale', iterfield=['mat_file']) avs_format = pe.Node(FormatHMCParam(), name='AVScale_Format') # Calculate EPI mask on the average after HMC bet_hmc = pe.Node(fsl.BET(mask=True, frac=0.6), name='EPI_hmc_bet') workflow.connect([(inputnode, pick_1st, [('epi', 'in_file')]), (inputnode, bet, [('epi', 'in_file')]), (bet, hmc, [('out_file', 'in_file')]), (hmc, hcm2itk, [('mat_file', 'transform_file')]), (pick_1st, hcm2itk, [('roi_file', 'source_file'), ('roi_file', 'reference_file')]), (hcm2itk, outputnode, [('itk_transform', 'xforms')]), (hmc, outputnode, [('out_file', 'epi_brain')]), (hmc, avscale, [('mat_file', 'mat_file')]), (avscale, avs_format, [('translations', 'translations'), ('rot_angles', 'rot_angles')]), (hmc, bet_hmc, [('mean_img', 'in_file')]), (hmc, avscale, [('mean_img', 'ref_file')]), (bet_hmc, outputnode, [('mask_file', 'epi_mask'), ('out_file', 'epi_mean')])]) # Write corrected file in the designated output dir ds_hmc = pe.Node(DerivativesDataSink(base_directory=settings['output_dir'], suffix='hmc'), name='DerivativesHMC') ds_mats = pe.Node(DerivativesDataSink( base_directory=settings['output_dir'], suffix='hmc'), name='DerivativesHMCmats') ds_mask = pe.Node(DerivativesDataSink( base_directory=settings['output_dir'], suffix='hmc_bmask'), name='DerivativesEPImask') ds_motion = pe.Node(DerivativesDataSink( base_directory=settings['output_dir'], suffix='hmc'), name='DerivativesParamsHMC') workflow.connect([(inputnode, ds_hmc, [('epi', 'source_file')]), (inputnode, ds_mats, [('epi', 'source_file')]), (inputnode, ds_mask, [('epi', 'source_file')]), (inputnode, ds_motion, [('epi', 'source_file')]), (hmc, ds_hmc, [('out_file', 'in_file')]), (hcm2itk, ds_mats, [('itk_transform', 'in_file')]), (bet_hmc, ds_mask, [('mask_file', 'in_file')]), (avs_format, ds_motion, [('out_file', 'in_file')])]) return workflow
def init_fsl_bbr_wf(use_bbr, bold2t1w_dof, bold2t1w_init, sloppy=False, name='fsl_bbr_wf'): """ Build a workflow to run FSL's ``flirt``. This workflow uses FSL FLIRT to register a BOLD image to a T1-weighted structural image, using a boundary-based registration (BBR) cost function. It is a counterpart to :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf`, which performs the same task using FreeSurfer's ``bbregister``. The ``use_bbr`` option permits a high degree of control over registration. If ``False``, standard, rigid coregistration will be performed by FLIRT. If ``True``, FLIRT-BBR will be seeded with the initial transform found by the rigid coregistration. If ``None``, after FLIRT-BBR is run, the resulting affine transform will be compared to the initial transform found by FLIRT. Excessive deviation will result in rejecting the BBR refinement and accepting the original, affine registration. Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.registration import init_fsl_bbr_wf wf = init_fsl_bbr_wf(use_bbr=True, bold2t1w_dof=9, bold2t1w_init='register') Parameters ---------- use_bbr : :obj:`bool` or None Enable/disable boundary-based registration refinement. If ``None``, test BBR result for distortion before accepting. bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration bold2t1w_init : str, 'header' or 'register' If ``'header'``, use header information for initialization of BOLD and T1 images. If ``'register'``, align volumes by their centers. name : :obj:`str`, optional Workflow name (default: fsl_bbr_wf) Inputs ------ in_file Reference BOLD image to be registered t1w_brain Skull-stripped T1-weighted structural image t1w_dseg FAST segmentation of ``t1w_brain`` fsnative2t1w_xfm Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf`) subjects_dir Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf`) subject_id Unused (see :py:func:`~fmriprep.workflows.bold.registration.init_bbreg_wf`) Outputs ------- itk_bold_to_t1 Affine transform from ``ref_bold_brain`` to T1w space (ITK format) itk_t1_to_bold Affine transform from T1 space to BOLD space (ITK format) out_report Reportlet for assessing registration quality fallback Boolean indicating whether BBR was rejected (rigid FLIRT registration returned) """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.utils.images import dseg_label as _dseg_label from niworkflows.interfaces.freesurfer import PatchedLTAConvert as LTAConvert from niworkflows.interfaces.registration import FLIRTRPT workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD reference was then co-registered to the T1w reference using `flirt` [FSL {fsl_ver}, @flirt] with the boundary-based registration [@bbr] cost-function. Co-registration was configured with nine degrees of freedom to account for distortions remaining in the BOLD reference. """.format(fsl_ver=FLIRTRPT().version or '<ver>') inputnode = pe.Node( niu.IdentityInterface([ 'in_file', 'fsnative2t1w_xfm', 'subjects_dir', 'subject_id', # BBRegister 't1w_dseg', 't1w_brain' ]), # FLIRT BBR name='inputnode') outputnode = pe.Node(niu.IdentityInterface( ['itk_bold_to_t1', 'itk_t1_to_bold', 'out_report', 'fallback']), name='outputnode') wm_mask = pe.Node(niu.Function(function=_dseg_label), name='wm_mask') wm_mask.inputs.label = 2 # BIDS default is WM=2 flt_bbr_init = pe.Node(FLIRTRPT(dof=6, generate_report=not use_bbr, uses_qform=True), name='flt_bbr_init') if bold2t1w_init not in ("register", "header"): raise ValueError( f"Unknown BOLD-T1w initialization option: {bold2t1w_init}") if bold2t1w_init == "header": raise NotImplementedError( "Header-based registration initialization not supported for FSL") invt_bbr = pe.Node(fsl.ConvertXFM(invert_xfm=True), name='invt_bbr', mem_gb=DEFAULT_MEMORY_MIN_GB) # BOLD to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_fwd', mem_gb=DEFAULT_MEMORY_MIN_GB) fsl2itk_inv = pe.Node(c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name='fsl2itk_inv', mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ (inputnode, flt_bbr_init, [('in_file', 'in_file'), ('t1w_brain', 'reference')]), (inputnode, fsl2itk_fwd, [('t1w_brain', 'reference_file'), ('in_file', 'source_file')]), (inputnode, fsl2itk_inv, [('in_file', 'reference_file'), ('t1w_brain', 'source_file')]), (invt_bbr, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'itk_bold_to_t1')]), (fsl2itk_inv, outputnode, [('itk_transform', 'itk_t1_to_bold')]), ]) # Short-circuit workflow building, use rigid registration if use_bbr is False: workflow.connect([ (flt_bbr_init, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr_init, fsl2itk_fwd, [('out_matrix_file', 'transform_file') ]), (flt_bbr_init, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = True return workflow flt_bbr = pe.Node(FLIRTRPT(cost_func='bbr', dof=bold2t1w_dof, generate_report=True), name='flt_bbr') FSLDIR = os.getenv('FSLDIR') if FSLDIR: flt_bbr.inputs.schedule = op.join(FSLDIR, 'etc/flirtsch/bbr.sch') else: # Should mostly be hit while building docs LOGGER.warning("FSLDIR unset - using packaged BBR schedule") flt_bbr.inputs.schedule = pkgr.resource_filename( 'fmriprep', 'data/flirtsch/bbr.sch') workflow.connect([ (inputnode, wm_mask, [('t1w_dseg', 'in_seg')]), (inputnode, flt_bbr, [('in_file', 'in_file')]), (flt_bbr_init, flt_bbr, [('out_matrix_file', 'in_matrix_file')]), ]) if sloppy is True: downsample = pe.Node(niu.Function( function=_conditional_downsampling, output_names=["out_file", "out_mask"]), name='downsample') workflow.connect([ (inputnode, downsample, [("t1w_brain", "in_file")]), (wm_mask, downsample, [("out", "in_mask")]), (downsample, flt_bbr, [('out_file', 'reference'), ('out_mask', 'wm_seg')]), ]) else: workflow.connect([ (inputnode, flt_bbr, [('t1w_brain', 'reference')]), (wm_mask, flt_bbr, [('out', 'wm_seg')]), ]) # Short-circuit workflow building, use boundary-based registration if use_bbr is True: workflow.connect([ (flt_bbr, invt_bbr, [('out_matrix_file', 'in_file')]), (flt_bbr, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (flt_bbr, outputnode, [('out_report', 'out_report')]), ]) outputnode.inputs.fallback = False return workflow transforms = pe.Node(niu.Merge(2), run_without_submitting=True, name='transforms') reports = pe.Node(niu.Merge(2), run_without_submitting=True, name='reports') compare_transforms = pe.Node(niu.Function(function=compare_xforms), name='compare_transforms') select_transform = pe.Node(niu.Select(), run_without_submitting=True, name='select_transform') select_report = pe.Node(niu.Select(), run_without_submitting=True, name='select_report') fsl_to_lta = pe.MapNode(LTAConvert(out_lta=True), iterfield=['in_fsl'], name='fsl_to_lta') workflow.connect([ (flt_bbr, transforms, [('out_matrix_file', 'in1')]), (flt_bbr_init, transforms, [('out_matrix_file', 'in2')]), # Convert FSL transforms to LTA (RAS2RAS) transforms and compare (inputnode, fsl_to_lta, [('in_file', 'source_file'), ('t1w_brain', 'target_file')]), (transforms, fsl_to_lta, [('out', 'in_fsl')]), (fsl_to_lta, compare_transforms, [('out_lta', 'lta_list')]), (compare_transforms, outputnode, [('out', 'fallback')]), # Select output transform (transforms, select_transform, [('out', 'inlist')]), (compare_transforms, select_transform, [('out', 'index')]), (select_transform, invt_bbr, [('out', 'in_file')]), (select_transform, fsl2itk_fwd, [('out', 'transform_file')]), (flt_bbr, reports, [('out_report', 'in1')]), (flt_bbr_init, reports, [('out_report', 'in2')]), (reports, select_report, [('out', 'inlist')]), (compare_transforms, select_report, [('out', 'index')]), (select_report, outputnode, [('out', 'out_report')]), ]) return workflow
def init_bold_reg_wf( bold2t1w_dof, bold2t1w_init, mem_gb, omp_nthreads, name="bold_reg_wf", use_compression=True, write_report=True, ): """ Build a workflow to run same-subject, BOLD-to-T1w image-registration. Calculates the registration between a reference BOLD image and T1w-space. Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from fprodents.workflows.bold.registration import init_bold_reg_wf wf = init_bold_reg_wf(mem_gb=3, omp_nthreads=1, bold2t1w_dof=9, bold2t1w_init='register') Parameters ---------- bold2t1w_dof : 6, 9 or 12 Degrees-of-freedom for BOLD-T1w registration bold2t1w_init : str, 'header' or 'register' If ``'header'``, use header information for initialization of BOLD and T1 images. If ``'register'``, align volumes by their centers. mem_gb : :obj:`float` Size of BOLD file in GB omp_nthreads : :obj:`int` Maximum number of threads an individual process may use name : :obj:`str` Name of workflow (default: ``bold_reg_wf``) use_compression : :obj:`bool` Save registered BOLD series as ``.nii.gz`` use_fieldwarp : :obj:`bool` Include SDC warp in single-shot transform from BOLD to T1 write_report : :obj:`bool` Whether a reportlet should be stored Inputs ------ ref_bold_brain Reference image to which BOLD series is aligned If ``fieldwarp == True``, ``ref_bold_brain`` should be unwarped t1w_brain Skull-stripped ``t1w_preproc`` Outputs ------- bold2anat Affine transform from ``ref_bold_brain`` to T1 space (ITK format) anat2bold Affine transform from T1 space to BOLD space (ITK format) out_report Reportlet for assessing registration quality """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.reportlets.registration import FLIRTRPT workflow = Workflow(name=name) workflow.__desc__ = """\ The BOLD reference was then co-registered to the T2w reference using `flirt` [FSL {fsl_ver}, @flirt]. Co-registration was configured with six degrees of freedom. """.format(fsl_ver=FLIRTRPT().version or "<ver>") inputnode = pe.Node( niu.IdentityInterface(fields=["ref_bold_brain", "t1w_brain"]), name="inputnode") outputnode = pe.Node( niu.IdentityInterface(fields=["bold2anat", "anat2bold", "out_report"]), name="outputnode", ) coreg = pe.Node(FLIRTRPT(dof=bold2t1w_dof, generate_report=True, uses_qform=True, args="-basescale 1"), name="coreg") if bold2t1w_init not in ("register", "header"): raise ValueError( f"Unknown BOLD-T1w initialization option: {bold2t1w_init}") if bold2t1w_init == "header": raise NotImplementedError( "Header-based registration initialization not supported for FSL") invt_xfm = pe.Node(fsl.ConvertXFM(invert_xfm=True), name="invt_xfm", mem_gb=DEFAULT_MEMORY_MIN_GB) # BOLD to T1 transform matrix is from fsl, using c3 tools to convert to # something ANTs will like. fsl2itk_fwd = pe.Node( c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name="fsl2itk_fwd", mem_gb=DEFAULT_MEMORY_MIN_GB, ) fsl2itk_inv = pe.Node( c3.C3dAffineTool(fsl2ras=True, itk_transform=True), name="fsl2itk_inv", mem_gb=DEFAULT_MEMORY_MIN_GB, ) # fmt:off workflow.connect([ (inputnode, coreg, [('ref_bold_brain', 'in_file'), ('t1w_brain', 'reference')]), (coreg, invt_xfm, [('out_matrix_file', 'in_file')]), (coreg, fsl2itk_fwd, [('out_matrix_file', 'transform_file')]), (coreg, outputnode, [('out_report', 'out_report')]), (inputnode, fsl2itk_fwd, [('t1w_brain', 'reference_file'), ('ref_bold_brain', 'source_file')]), (inputnode, fsl2itk_inv, [('ref_bold_brain', 'reference_file'), ('t1w_brain', 'source_file')]), (invt_xfm, fsl2itk_inv, [('out_file', 'transform_file')]), (fsl2itk_fwd, outputnode, [('itk_transform', 'bold2anat')]), (fsl2itk_inv, outputnode, [('itk_transform', 'anat2bold')]), ]) # fmt:on if write_report: ds_report_reg = pe.Node( DerivativesDataSink(datatype="figures", dismiss_entities=("echo", )), name="ds_report_reg", run_without_submitting=True, desc="coreg", mem_gb=DEFAULT_MEMORY_MIN_GB, ) # fmt:off workflow.connect([ (outputnode, ds_report_reg, [('out_report', 'in_file')]), ]) # fmt:on return workflow