Exemplo n.º 1
0
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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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')
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
                                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")
Exemplo n.º 10
0
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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
        (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',
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
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
Exemplo n.º 16
0
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
Exemplo n.º 17
0
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
Exemplo n.º 18
0
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
Exemplo n.º 19
0
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
Exemplo n.º 21
0
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