Example #1
0
def afni3dQwarp(oppose_pe,matched_pe,source_warp):
    qwarp = afni.QwarpPlusMinus()
    qwarp.inputs.in_file =oppose_pe
    qwarp.inputs.nopadWARP = True
    qwarp.inputs.noweight=True
    qwarp.inputs.pblur = [0.05,0.05]
    qwarp.inputs.blur=[-1,-1]
    qwarp.inputs.minpatch=9
    qwarp.inputs.noXdis=True
    qwarp.inputs.noZdis=True
    qwarp.inputs.base_file = matched_pe
    qwarp.inputs.out_file= source_warp+'.nii.gz'
    qwarp.run()  
    return source_warp+'_PLUS_WARP.nii.gz'
def blip_distcor_wf(wf_name='blip_distcor'):
    """Execute AFNI 3dQWarp to calculate the distortion "unwarp" for phase
    encoding direction EPI field map distortion correction.

        1. Skull-strip the opposite-direction phase encoding EPI.
        2. Transform the opposite-direction phase encoding EPI to the
           skull-stripped functional and pass this as the base_file to
           AFNI 3dQWarp (plus-minus).
        3. If there is a same-direction phase encoding EPI, also skull-strip
           this, and transform it to the skull-stripped functional. Then, pass
           this as the in_file to AFNI 3dQWarp (plus-minus).
        4. If there isn't a same-direction, pass the functional in as the
           in_file of AFNI 3dQWarp (plus-minus).
        5. Convert the 3dQWarp transforms to ANTs/ITK format.
        6. Use antsApplyTransforms, with the original functional as both the
           input and the reference, and apply the warp from 3dQWarp. The
           output of this can then proceed to func_preproc.

    :param wf_name:
    :return:
    """

    wf = pe.Workflow(name=wf_name)

    input_node = pe.Node(util.IdentityInterface(
        fields=['func_mean', 'opposite_pe_epi', 'same_pe_epi']),
                         name='inputspec')

    output_node = pe.Node(util.IdentityInterface(fields=[
        'blip_warp', 'blip_warp_inverse', 'new_func_mean', 'new_func_mask'
    ]),
                          name='outputspec')

    skullstrip_opposite_pe = skullstrip_functional(
        skullstrip_tool='afni',
        wf_name="{0}_skullstrip_opp_pe".format(wf_name))

    wf.connect(input_node, 'opposite_pe_epi', skullstrip_opposite_pe,
               'inputspec.func')

    opp_pe_to_func = pe.Node(interface=fsl.FLIRT(), name='opp_pe_to_func')
    opp_pe_to_func.inputs.cost = 'corratio'

    wf.connect(skullstrip_opposite_pe, 'outputspec.func_brain', opp_pe_to_func,
               'in_file')
    wf.connect(input_node, 'func_mean', opp_pe_to_func, 'reference')

    prep_qwarp_input_imports = ['import os', 'import subprocess']
    prep_qwarp_input = \
        pe.Node(function.Function(input_names=['same_pe_epi',
                                               'func_mean'],
                                  output_names=['qwarp_input'],
                                  function=same_pe_direction_prep,
                                  imports=prep_qwarp_input_imports),
                name='prep_qwarp_input')

    wf.connect(input_node, 'same_pe_epi', prep_qwarp_input, 'same_pe_epi')
    wf.connect(input_node, 'func_mean', prep_qwarp_input, 'func_mean')

    calc_blip_warp = pe.Node(afni.QwarpPlusMinus(), name='calc_blip_warp')
    calc_blip_warp.inputs.plusminus = True
    calc_blip_warp.inputs.outputtype = "NIFTI_GZ"
    calc_blip_warp.inputs.out_file = os.path.abspath("Qwarp.nii.gz")

    wf.connect(opp_pe_to_func, 'out_file', calc_blip_warp, 'base_file')
    wf.connect(prep_qwarp_input, 'qwarp_input', calc_blip_warp, 'in_file')

    convert_afni_warp_imports = ['import os', 'import nibabel as nb']
    convert_afni_warp = \
        pe.Node(function.Function(input_names=['afni_warp'],
                                  output_names=['ants_warp'],
                                  function=convert_afni_to_ants,
                                  imports=convert_afni_warp_imports),
                name='convert_afni_warp')

    wf.connect(calc_blip_warp, 'source_warp', convert_afni_warp, 'afni_warp')

    # TODO: inverse source_warp (node:source_warp_inverse)
    # wf.connect(###
    # output_node, 'blip_warp_inverse')

    undistort_func_mean = pe.Node(interface=ants.ApplyTransforms(),
                                  name='undistort_func_mean',
                                  mem_gb=.1)

    undistort_func_mean.inputs.out_postfix = '_antswarp'
    undistort_func_mean.interface.num_threads = 1
    undistort_func_mean.inputs.interpolation = "LanczosWindowedSinc"
    undistort_func_mean.inputs.dimension = 3
    undistort_func_mean.inputs.input_image_type = 0

    wf.connect(input_node, 'func_mean', undistort_func_mean, 'input_image')
    wf.connect(input_node, 'func_mean', undistort_func_mean, 'reference_image')
    wf.connect(convert_afni_warp, 'ants_warp', undistort_func_mean,
               'transforms')

    create_new_mask = skullstrip_functional(
        skullstrip_tool='afni', wf_name="{0}_new_func_mask".format(wf_name))
    wf.connect(undistort_func_mean, 'output_image', create_new_mask,
               'inputspec.func')

    wf.connect(convert_afni_warp, 'ants_warp', output_node, 'blip_warp')

    wf.connect(undistort_func_mean, 'output_image', output_node,
               'new_func_mean')
    wf.connect(create_new_mask, 'outputspec.func_brain_mask', output_node,
               'new_func_mask')

    return wf
Example #3
0
def init_3dQwarp_wf(omp_nthreads=1, debug=False, name="pepolar_estimate_wf"):
    """
    Create the PEPOLAR field estimation workflow based on AFNI's ``3dQwarp``.

    This workflow takes in two EPI files that MUST have opposed
    :abbr:`PE (phase-encoding)` direction.
    Therefore, EPIs with orthogonal PE directions are not supported.

    Workflow Graph
        .. workflow ::
            :graph2use: orig
            :simple_form: yes

            from sdcflows.workflows.fit.pepolar import init_3dQwarp_wf
            wf = init_3dQwarp_wf()

    Parameters
    ----------
    debug : :obj:`bool`
        Whether a fast configuration of topup (less accurate) should be applied.
    name : :obj:`str`
        Name for this workflow
    omp_nthreads : :obj:`int`
        Parallelize internal tasks across the number of CPUs given by this option.

    Inputs
    ------
    in_data : :obj:`list` of :obj:`str`
        A list of two EPI files, the first of which will be taken as reference.

    Outputs
    -------
    fmap : :obj:`str`
        The path of the estimated fieldmap.
    fmap_ref : :obj:`str`
        The path of an unwarped conversion of the first element of ``in_data``.

    """
    from nipype.interfaces import afni
    from niworkflows.interfaces.header import CopyHeader
    from niworkflows.interfaces.fixes import (
        FixHeaderRegistration as Registration,
        FixHeaderApplyTransforms as ApplyTransforms,
    )
    from niworkflows.interfaces.freesurfer import StructuralReference
    from niworkflows.func.util import init_enhance_and_skullstrip_bold_wf
    from ...utils.misc import front as _front, last as _last
    from ...interfaces.utils import Flatten, ConvertWarp

    workflow = Workflow(name=name)
    workflow.__desc__ = f"""{_PEPOLAR_DESC} \
with `3dQwarp` (@afni; AFNI {''.join(['%02d' % v for v in afni.Info().version() or []])}).
"""

    inputnode = pe.Node(niu.IdentityInterface(fields=["in_data", "metadata"]),
                        name="inputnode")

    outputnode = pe.Node(niu.IdentityInterface(fields=["fmap", "fmap_ref"]),
                         name="outputnode")

    flatten = pe.Node(Flatten(), name="flatten")
    sort_pe = pe.Node(
        niu.Function(function=_sorted_pe,
                     output_names=["sorted", "qwarp_args"]),
        name="sort_pe",
        run_without_submitting=True,
    )

    merge_pes = pe.MapNode(
        StructuralReference(
            auto_detect_sensitivity=True,
            initial_timepoint=1,
            fixed_timepoint=True,  # Align to first image
            intensity_scaling=True,
            # 7-DOF (rigid + intensity)
            no_iteration=True,
            subsample_threshold=200,
            out_file="template.nii.gz",
        ),
        name="merge_pes",
        iterfield=["in_files"],
    )

    pe0_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads,
                                                 name="pe0_wf")
    pe1_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads,
                                                 name="pe1_wf")

    align_pes = pe.Node(
        Registration(
            from_file=_pkg_fname("sdcflows", "data/translation_rigid.json"),
            output_warped_image=True,
        ),
        name="align_pes",
        n_procs=omp_nthreads,
    )

    qwarp = pe.Node(
        afni.QwarpPlusMinus(
            blur=[-1, -1],
            environ={"OMP_NUM_THREADS": f"{min(omp_nthreads, 4)}"},
            minpatch=9,
            nopadWARP=True,
            noweight=True,
            pblur=[0.05, 0.05],
        ),
        name="qwarp",
        n_procs=min(omp_nthreads, 4),
    )

    to_ants = pe.Node(ConvertWarp(), name="to_ants", mem_gb=0.01)

    cphdr_warp = pe.Node(CopyHeader(), name="cphdr_warp", mem_gb=0.01)

    unwarp_reference = pe.Node(
        ApplyTransforms(
            dimension=3,
            float=True,
            interpolation="LanczosWindowedSinc",
        ),
        name="unwarp_reference",
    )

    # fmt: off
    workflow.connect([
        (inputnode, flatten, [("in_data", "in_data"),
                              ("metadata", "in_meta")]),
        (flatten, sort_pe, [("out_list", "inlist")]),
        (sort_pe, qwarp, [("qwarp_args", "args")]),
        (sort_pe, merge_pes, [("sorted", "in_files")]),
        (merge_pes, pe0_wf, [(("out_file", _front), "inputnode.in_file")]),
        (merge_pes, pe1_wf, [(("out_file", _last), "inputnode.in_file")]),
        (pe0_wf, align_pes, [("outputnode.skull_stripped_file", "fixed_image")
                             ]),
        (pe1_wf, align_pes, [("outputnode.skull_stripped_file", "moving_image")
                             ]),
        (pe0_wf, qwarp, [("outputnode.skull_stripped_file", "in_file")]),
        (align_pes, qwarp, [("warped_image", "base_file")]),
        (inputnode, cphdr_warp, [(("in_data", _front), "hdr_file")]),
        (qwarp, cphdr_warp, [("source_warp", "in_file")]),
        (cphdr_warp, to_ants, [("out_file", "in_file")]),
        (to_ants, unwarp_reference, [("out_file", "transforms")]),
        (inputnode, unwarp_reference, [("in_reference", "reference_image"),
                                       ("in_reference", "input_image")]),
        (unwarp_reference, outputnode, [("output_image", "fmap_ref")]),
        (to_ants, outputnode, [("out_file", "fmap")]),
    ])
    # fmt: on
    return workflow
Example #4
0
def init_pepolar_unwarp_wf(omp_nthreads=1,
                           matched_pe=False,
                           name="pepolar_unwarp_wf"):
    """
    Create the PE-Polar field estimation workflow.

    This workflow takes in a set of EPI files with opposite phase encoding
    direction than the target file and calculates a displacements field
    (in other words, an ANTs-compatible warp file).

    This procedure works if there is only one '_epi' file is present
    (as long as it has the opposite phase encoding direction to the target
    file). The target file will be used to estimate the field distortion.
    However, if there is another '_epi' file present with a matching
    phase encoding direction to the target it will be used instead.

    Currently, different phase encoding dimension in the target file and the
    '_epi' file(s) (for example 'i' and 'j') is not supported.

    The warp field correcting for the distortions is estimated using AFNI's
    3dQwarp, with displacement estimation limited to the target file phase
    encoding direction.

    It also calculates a new mask for the input dataset that takes into
    account the distortions.

    .. workflow ::
        :graph2use: orig
        :simple_form: yes

        from sdcflows.workflows.pepolar import init_pepolar_unwarp_wf
        wf = init_pepolar_unwarp_wf()


    **Parameters**:

        matched_pe : bool
            Whether the input ``fmaps_epi`` will contain images with matched
            PE blips or not. Please use :func:`sdcflows.workflows.pepolar.check_pes`
            to determine whether they exist or not.
        name : str
            Name for this workflow
        omp_nthreads : int
            Parallelize internal tasks across the number of CPUs given by this option.

    **Inputs**:

        fmaps_epi : list of tuple(pathlike, str)
            The list of EPI images that will be used in PE-Polar correction, and
            their corresponding ``PhaseEncodingDirection`` metadata.
            The workflow will use the ``bold_pe_dir`` input to separate out those
            EPI acquisitions with opposed PE blips and those with matched PE blips
            (the latter could be none, and ``in_reference_brain`` would then be
            used). The workflow raises a ``ValueError`` when no images with
            opposed PE blips are found.
        bold_pe_dir : str
            The baseline PE direction.
        in_reference : pathlike
            The baseline reference image (must correspond to ``bold_pe_dir``).
        in_reference_brain : pathlike
            The reference image above, but skullstripped.
        in_mask : pathlike
            Not used, present only for consistency across fieldmap estimation
            workflows.


    **Outputs**:

        out_reference : pathlike
            The ``in_reference`` after unwarping
        out_reference_brain : pathlike
            The ``in_reference`` after unwarping and skullstripping
        out_warp : pathlike
            The corresponding :abbr:`DFM (displacements field map)` compatible with
            ANTs.
        out_mask : pathlike
            Mask of the unwarped input file

    """
    workflow = Workflow(name=name)
    workflow.__desc__ = """\
A deformation field to correct for susceptibility distortions was estimated
based on two echo-planar imaging (EPI) references with opposing phase-encoding
directions, using `3dQwarp` @afni (AFNI {afni_ver}).
""".format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []]))

    inputnode = pe.Node(niu.IdentityInterface(fields=[
        'fmaps_epi', 'in_reference', 'in_reference_brain', 'in_mask',
        'bold_pe_dir'
    ]),
                        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'out_reference', 'out_reference_brain', 'out_warp', 'out_mask'
    ]),
                         name='outputnode')

    prepare_epi_wf = init_prepare_epi_wf(omp_nthreads=omp_nthreads,
                                         matched_pe=matched_pe,
                                         name="prepare_epi_wf")

    qwarp = pe.Node(afni.QwarpPlusMinus(
        pblur=[0.05, 0.05],
        blur=[-1, -1],
        noweight=True,
        minpatch=9,
        nopadWARP=True,
        environ={'OMP_NUM_THREADS': '%d' % omp_nthreads}),
                    name='qwarp',
                    n_procs=omp_nthreads)

    to_ants = pe.Node(niu.Function(function=_fix_hdr),
                      name='to_ants',
                      mem_gb=0.01)

    cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01)

    unwarp_reference = pe.Node(ANTSApplyTransformsRPT(
        dimension=3,
        generate_report=False,
        float=True,
        interpolation='LanczosWindowedSinc'),
                               name='unwarp_reference')

    enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(
        omp_nthreads=omp_nthreads)

    workflow.connect([
        (inputnode, qwarp, [(('bold_pe_dir', _qwarp_args), 'args')]),
        (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]),
        (inputnode, prepare_epi_wf, [('fmaps_epi', 'inputnode.maps_pe'),
                                     ('bold_pe_dir', 'inputnode.epi_pe'),
                                     ('in_reference_brain',
                                      'inputnode.ref_brain')]),
        (prepare_epi_wf, qwarp, [('outputnode.opposed_pe', 'base_file'),
                                 ('outputnode.matched_pe', 'in_file')]),
        (qwarp, cphdr_warp, [('source_warp', 'in_file')]),
        (cphdr_warp, to_ants, [('out_file', 'in_file')]),
        (to_ants, unwarp_reference, [('out', 'transforms')]),
        (inputnode, unwarp_reference, [('in_reference', 'reference_image'),
                                       ('in_reference', 'input_image')]),
        (unwarp_reference, enhance_and_skullstrip_bold_wf,
         [('output_image', 'inputnode.in_file')]),
        (unwarp_reference, outputnode, [('output_image', 'out_reference')]),
        (enhance_and_skullstrip_bold_wf, outputnode,
         [('outputnode.mask_file', 'out_mask'),
          ('outputnode.skull_stripped_file', 'out_reference_brain')]),
        (to_ants, outputnode, [('out', 'out_warp')]),
    ])

    return workflow
Example #5
0
def init_pepolar_unwarp_wf(dwi_meta,
                           epi_fmaps,
                           omp_nthreads=1,
                           name="pepolar_unwarp_wf"):
    """
    This workflow takes in a set of EPI files with opposite phase encoding
    direction than the target file and calculates a displacements field
    (in other words, an ANTs-compatible warp file).

    This procedure works if there is only one '_epi' file is present
    (as long as it has the opposite phase encoding direction to the target
    file). The target file will be used to estimate the field distortion.
    However, if there is another '_epi' file present with a matching
    phase encoding direction to the target it will be used instead.

    Currently, different phase encoding dimension in the target file and the
    '_epi' file(s) (for example 'i' and 'j') is not supported.

    The warp field correcting for the distortions is estimated using AFNI's
    3dQwarp, with displacement estimation limited to the target file phase
    encoding direction.

    It also calculates a new mask for the input dataset that takes into
    account the distortions.

    .. workflow ::
        :graph2use: orig
        :simple_form: yes

        from qsiprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf
        wf = init_pepolar_unwarp_wf(
            dwi_meta={'PhaseEncodingDirection': 'j'},
            epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')],
            omp_nthreads=8)


    Inputs

        in_reference
            the reference image
        in_reference_brain
            the reference image skullstripped
        in_mask
            a brain mask corresponding to ``in_reference``

    Outputs

        out_reference
            the ``in_reference`` after unwarping
        out_warp
            the corresponding :abbr:`DFM (displacements field map)` compatible with
            ANTs

    """
    dwi_file_pe = dwi_meta["PhaseEncodingDirection"]

    args = '-noXdis -noYdis -noZdis'
    rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[dwi_file_pe[0]]
    args = args.replace(rm_arg, '')

    workflow = Workflow(name=name)
    workflow.__desc__ = """\
A deformation field to correct for susceptibility distortions was estimated
based on two echo-planar imaging (EPI) references with opposing phase-encoding
directions, using `3dQwarp` @afni (AFNI {afni_ver}).
""".format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []]))

    inputnode = pe.Node(niu.IdentityInterface(
        fields=['in_reference', 'in_reference_brain', 'in_mask']),
                        name='inputnode')

    outputnode = pe.Node(
        niu.IdentityInterface(fields=['out_reference', 'out_warp']),
        name='outputnode')

    prepare_epi_opposite_wf = init_prepare_dwi_epi_wf(
        omp_nthreads=omp_nthreads, name="prepare_epi_opposite_wf")
    prepare_epi_opposite_wf.inputs.inputnode.fmaps = epi_fmaps

    qwarp = pe.Node(afni.QwarpPlusMinus(
        pblur=[0.05, 0.05],
        blur=[-1, -1],
        noweight=True,
        minpatch=9,
        nopadWARP=True,
        environ={'OMP_NUM_THREADS': '%d' % omp_nthreads},
        args=args),
                    name='qwarp',
                    n_procs=omp_nthreads)

    workflow.connect([(inputnode, prepare_epi_opposite_wf,
                       [('in_reference_brain', 'inputnode.ref_brain')]),
                      (prepare_epi_opposite_wf, qwarp, [('outputnode.out_file',
                                                         'base_file')]),
                      (inputnode, qwarp, [('in_reference_brain', 'in_file')])])

    to_ants = pe.Node(niu.Function(function=_fix_hdr),
                      name='to_ants',
                      mem_gb=0.01)

    cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01)

    unwarp_reference = pe.Node(ANTSApplyTransformsRPT(
        dimension=3,
        generate_report=False,
        float=True,
        interpolation='LanczosWindowedSinc'),
                               name='unwarp_reference')

    workflow.connect([
        (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]),
        (qwarp, cphdr_warp, [('source_warp', 'in_file')]),
        (cphdr_warp, to_ants, [('out_file', 'in_file')]),
        (to_ants, unwarp_reference, [('out', 'transforms')]),
        (inputnode, unwarp_reference, [('in_reference', 'reference_image'),
                                       ('in_reference', 'input_image')]),
        (unwarp_reference, outputnode, [('output_image', 'out_reference')]),
        (to_ants, outputnode, [('out', 'out_warp')]),
    ])

    return workflow
Example #6
0
def init_bidirectional_b0_unwarping_wf(template_plus_pe, omp_nthreads=1,
                                       name="bidirectional_pepolar_unwarping_wf"):
    """
    This workflow takes in a set of b0 files with opposite phase encoding
    direction and calculates displacement fields
    (in other words, an ANTs-compatible warp file). This is intended to be run
    in the case where there are two dwi series in the same session with reverse
    phase encoding directions.

    The warp field correcting for the distortions is estimated using AFNI's
    3dQwarp, with displacement estimation limited to the target file phase
    encoding direction.

    It also calculates a new mask for the input dataset that takes into
    account the distortions.

    .. workflow ::
        :graph2use: orig
        :simple_form: yes

        from qsiprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf
        wf = init_pepolar_unwarp_wf(
            bold_meta={'PhaseEncodingDirection': 'j'},
            epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')],
            omp_nthreads=8)


    Inputs

        template_plus
            b0 template in one PE
        template_minus
            b0_template in the other PE

    Outputs

        out_reference
            the ``in_reference`` after unwarping
        out_reference_brain
            the ``in_reference`` after unwarping and skullstripping
        out_warp_plus
            the corresponding :abbr:`DFM (displacements field map)` to correct
            ``template_plus``
        out_warp_minus
            the corresponding :abbr:`DFM (displacements field map)` to correct
            ``template_minus``
        out_mask
            mask of the unwarped input file

    """
    args = '-noXdis -noYdis -noZdis'
    rm_arg = {'i': '-noXdis',
              'j': '-noYdis',
              'k': '-noZdis'}[template_plus_pe[0]]
    args = args.replace(rm_arg, '')

    workflow = Workflow(name=name)
    workflow.__desc__ = """\
A deformation field to correct for susceptibility distortions was estimated
based on two b0 templates created from dwi series with opposing phase-encoding
directions, using `3dQwarp` @afni (AFNI {afni_ver}).
""".format(afni_ver=''.join(['%02d' % v for v in afni.Info().version() or []]))

    inputnode = pe.Node(niu.IdentityInterface(
        fields=['template_plus', 'template_minus', 't1w_brain']),
        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(
        fields=['out_reference', 'out_reference_brain', 'out_affine_plus', 'out_warp_plus',
                'out_affine_minus', 'out_warp_minus', 'out_mask']), name='outputnode')
    # Create high-contrast ref images
    plus_ref_wf = init_dwi_reference_wf(name='plus_ref_wf')
    minus_ref_wf = init_dwi_reference_wf(name='minus_ref_wf')

    # Align the two reference images to the midpoint
    inputs_to_list = pe.Node(niu.Merge(2), name='inputs_to_list')
    align_reverse_pe_wf = init_b0_hmc_wf(align_to='iterative', transform='Rigid')
    get_midpoint_transforms = pe.Node(niu.Split(splits=[1, 1], squeeze=True),
                                      name="get_midpoint_transforms")
    plus_to_midpoint = pe.Node(ants.ApplyTransforms(float=True,
                                                    interpolation='LanczosWindowedSinc',
                                                    dimension=3),
                               name='plus_to_midpoint')
    minus_to_midpoint = pe.Node(ants.ApplyTransforms(float=True,
                                                     interpolation='LanczosWindowedSinc',
                                                     dimension=3),
                                name='minus_to_midpoint')

    qwarp = pe.Node(afni.QwarpPlusMinus(pblur=[0.05, 0.05],
                                        blur=[-1, -1],
                                        noweight=True,
                                        minpatch=9,
                                        nopadWARP=True,
                                        environ={'OMP_NUM_THREADS': '%d' % omp_nthreads},
                                        args=args),
                    name='qwarp', n_procs=omp_nthreads)

    to_ants_plus = pe.Node(niu.Function(function=_fix_hdr), name='to_ants_plus',
                           mem_gb=0.01)
    to_ants_minus = pe.Node(niu.Function(function=_fix_hdr), name='to_ants_minus',
                            mem_gb=0.01)

    cphdr_plus_warp = pe.Node(CopyHeader(), name='cphdr_plus_warp', mem_gb=0.01)
    cphdr_minus_warp = pe.Node(CopyHeader(), name='cphdr_minus_warp', mem_gb=0.01)

    unwarp_plus_reference = pe.Node(ants.ApplyTransforms(dimension=3,
                                                         float=True,
                                                         interpolation='LanczosWindowedSinc'),
                                    name='unwarp_plus_reference')
    unwarp_minus_reference = pe.Node(ants.ApplyTransforms(dimension=3,
                                                          float=True,
                                                          interpolation='LanczosWindowedSinc'),
                                     name='unwarp_minus_reference')
    unwarped_to_list = pe.Node(niu.Merge(2), name="unwarped_to_list")
    merge_unwarped = pe.Node(ants.AverageImages(dimension=3, normalize=True),
                             name="merge_unwarped")

    final_ref = init_dwi_reference_wf(name="final_ref")

    workflow.connect([
        (inputnode, plus_ref_wf, [('template_plus', 'inputnode.b0_template')]),
        (plus_ref_wf, inputs_to_list, [('outputnode.ref_image', 'in1')]),
        (inputnode, minus_ref_wf, [('template_minus', 'inputnode.b0_template')]),
        (minus_ref_wf, inputs_to_list, [('outputnode.ref_image', 'in2')]),
        (inputs_to_list, align_reverse_pe_wf, [('out', 'inputnode.b0_images')]),
        (align_reverse_pe_wf, get_midpoint_transforms, [('outputnode.forward_transforms',
                                                         'inlist')]),
        (get_midpoint_transforms, outputnode, [('out1', 'out_affine_plus'),
                                               ('out2', 'out_affine_minus')]),
        (plus_ref_wf, plus_to_midpoint, [('outputnode.ref_image', 'input_image')]),
        (minus_ref_wf, minus_to_midpoint, [('outputnode.ref_image', 'input_image')]),
        (get_midpoint_transforms, plus_to_midpoint, [('out1', 'transforms')]),
        (align_reverse_pe_wf, plus_to_midpoint, [('outputnode.final_template',
                                                  'reference_image')]),
        (get_midpoint_transforms, minus_to_midpoint, [('out2', 'transforms')]),
        (align_reverse_pe_wf, minus_to_midpoint, [('outputnode.final_template',
                                                  'reference_image')]),
        (plus_to_midpoint, qwarp, [('output_image', 'in_file')]),
        (minus_to_midpoint, qwarp, [('output_image', 'base_file')]),
        (align_reverse_pe_wf, cphdr_plus_warp, [('outputnode.final_template', 'hdr_file')]),
        (align_reverse_pe_wf, cphdr_minus_warp, [('outputnode.final_template', 'hdr_file')]),
        (qwarp, cphdr_plus_warp, [('source_warp', 'in_file')]),
        (qwarp, cphdr_minus_warp, [('base_warp', 'in_file')]),
        (cphdr_plus_warp, to_ants_plus, [('out_file', 'in_file')]),
        (cphdr_minus_warp, to_ants_minus, [('out_file', 'in_file')]),

        (to_ants_minus, unwarp_minus_reference, [('out', 'transforms')]),
        (minus_to_midpoint, unwarp_minus_reference, [('output_image', 'reference_image'),
                                                     ('output_image', 'input_image')]),
        (to_ants_minus, outputnode, [('out', 'out_warp_minus')]),

        (to_ants_plus, unwarp_plus_reference, [('out', 'transforms')]),
        (plus_to_midpoint, unwarp_plus_reference, [('output_image', 'reference_image'),
                                                   ('output_image', 'input_image')]),
        (to_ants_plus, outputnode, [('out', 'out_warp_plus')]),

        (unwarp_plus_reference, unwarped_to_list, [('output_image', 'in1')]),
        (unwarp_minus_reference, unwarped_to_list, [('output_image', 'in2')]),
        (unwarped_to_list, merge_unwarped, [('out', 'images')]),

        (merge_unwarped, final_ref, [('output_average_image', 'inputnode.b0_template')]),
        (final_ref, outputnode, [('outputnode.ref_image', 'out_reference'),
                                 ('outputnode.ref_image_brain', 'out_reference_brain'),
                                 ('outputnode.dwi_mask', 'out_mask')])
    ])

    return workflow
Example #7
0
def init_pepolar_unwarp_wf(bold_meta,
                           epi_fmaps,
                           omp_nthreads=1,
                           name="pepolar_unwarp_wf"):
    """
    This workflow takes in a set of EPI files with opposite phase encoding
    direction than the target file and calculates a displacements field
    (in other words, an ANTs-compatible warp file).

    This procedure works if there is only one '_epi' file is present
    (as long as it has the opposite phase encoding direction to the target
    file). The target file will be used to estimate the field distortion.
    However, if there is another '_epi' file present with a matching
    phase encoding direction to the target it will be used instead.

    Currently, different phase encoding dimension in the target file and the
    '_epi' file(s) (for example 'i' and 'j') is not supported.

    The warp field correcting for the distortions is estimated using AFNI's
    3dQwarp, with displacement estimation limited to the target file phase
    encoding direction.

    It also calculates a new mask for the input dataset that takes into
    account the distortions.

    .. workflow ::
        :graph2use: orig
        :simple_form: yes

        from fmriprep.workflows.fieldmap.pepolar import init_pepolar_unwarp_wf
        wf = init_pepolar_unwarp_wf(
            bold_meta={'PhaseEncodingDirection': 'j'},
            epi_fmaps=[('/dataset/sub-01/fmap/sub-01_epi.nii.gz', 'j-')],
            omp_nthreads=8)


    Inputs

        in_reference
            the reference image
        in_reference_brain
            the reference image skullstripped
        in_mask
            a brain mask corresponding to ``in_reference``

    Outputs

        out_reference
            the ``in_reference`` after unwarping
        out_reference_brain
            the ``in_reference`` after unwarping and skullstripping
        out_warp
            the corresponding :abbr:`DFM (displacements field map)` compatible with
            ANTs
        out_mask
            mask of the unwarped input file

    """
    bold_file_pe = bold_meta["PhaseEncodingDirection"]

    args = '-noXdis -noYdis -noZdis'
    rm_arg = {'i': '-noXdis', 'j': '-noYdis', 'k': '-noZdis'}[bold_file_pe[0]]
    args = args.replace(rm_arg, '')

    usable_fieldmaps_matching_pe = []
    usable_fieldmaps_opposite_pe = []
    for fmap, fmap_pe in epi_fmaps:
        if fmap_pe == bold_file_pe:
            usable_fieldmaps_matching_pe.append(fmap)
        elif fmap_pe[0] == bold_file_pe[0]:
            usable_fieldmaps_opposite_pe.append(fmap)

    if not usable_fieldmaps_opposite_pe:
        raise Exception("None of the discovered fieldmaps has the right "
                        "phase encoding direction. Possibly a problem with "
                        "metadata. If not, rerun with '--ignore fieldmaps' to "
                        "skip distortion correction step.")

    workflow = pe.Workflow(name=name)
    inputnode = pe.Node(niu.IdentityInterface(
        fields=['in_reference', 'in_reference_brain', 'in_mask']),
                        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'out_reference', 'out_reference_brain', 'out_warp', 'out_mask'
    ]),
                         name='outputnode')

    prepare_epi_opposite_wf = init_prepare_epi_wf(
        omp_nthreads=omp_nthreads, name="prepare_epi_opposite_wf")
    prepare_epi_opposite_wf.inputs.inputnode.fmaps = usable_fieldmaps_opposite_pe

    qwarp = pe.Node(afni.QwarpPlusMinus(
        pblur=[0.05, 0.05],
        blur=[-1, -1],
        noweight=True,
        minpatch=9,
        nopadWARP=True,
        environ={'OMP_NUM_THREADS': '%d' % omp_nthreads},
        args=args),
                    name='qwarp',
                    n_procs=omp_nthreads)

    workflow.connect([
        (inputnode, prepare_epi_opposite_wf, [('in_reference_brain',
                                               'inputnode.ref_brain')]),
        (prepare_epi_opposite_wf, qwarp, [('outputnode.out_file', 'base_file')
                                          ]),
    ])

    if usable_fieldmaps_matching_pe:
        prepare_epi_matching_wf = init_prepare_epi_wf(
            omp_nthreads=omp_nthreads, name="prepare_epi_matching_wf")
        prepare_epi_matching_wf.inputs.inputnode.fmaps = usable_fieldmaps_matching_pe

        workflow.connect([
            (inputnode, prepare_epi_matching_wf, [('in_reference_brain',
                                                   'inputnode.ref_brain')]),
            (prepare_epi_matching_wf, qwarp, [('outputnode.out_file',
                                               'source_file')]),
        ])
    else:
        workflow.connect([(inputnode, qwarp, [('in_reference_brain',
                                               'source_file')])])

    to_ants = pe.Node(niu.Function(function=_fix_hdr),
                      name='to_ants',
                      mem_gb=0.01)

    cphdr_warp = pe.Node(CopyHeader(), name='cphdr_warp', mem_gb=0.01)

    unwarp_reference = pe.Node(ANTSApplyTransformsRPT(
        dimension=3,
        generate_report=False,
        float=True,
        interpolation='LanczosWindowedSinc'),
                               name='unwarp_reference')

    enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(
        omp_nthreads=omp_nthreads)

    workflow.connect([
        (inputnode, cphdr_warp, [('in_reference', 'hdr_file')]),
        (qwarp, cphdr_warp, [('source_warp', 'in_file')]),
        (cphdr_warp, to_ants, [('out_file', 'in_file')]),
        (to_ants, unwarp_reference, [('out', 'transforms')]),
        (inputnode, unwarp_reference, [('in_reference', 'reference_image'),
                                       ('in_reference', 'input_image')]),
        (unwarp_reference, enhance_and_skullstrip_bold_wf,
         [('output_image', 'inputnode.in_file')]),
        (unwarp_reference, outputnode, [('output_image', 'out_reference')]),
        (enhance_and_skullstrip_bold_wf, outputnode,
         [('outputnode.mask_file', 'out_mask'),
          ('outputnode.skull_stripped_file', 'out_reference_brain')]),
        (to_ants, outputnode, [('out', 'out_warp')]),
    ])

    return workflow
def run_workflows(session=None, csv_file=None):
    from nipype import config
    #config.enable_debug_mode()

    # ------------------ Specify variables
    ds_root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

    data_dir = ds_root
    output_dir = 'derivatives/undistort'
    working_dir = 'workingdirs'

    # ------------------ Input Files
    infosource = Node(IdentityInterface(fields=[
        'subject_id',
        'session_id',
        'run_id',
        'refsubject_id',
    ]), name="infosource")

    if csv_file is not None:
      print('=== reading csv ===')
      # Read csv and use pandas to set-up image and ev-processing
      df = pd.read_csv(csv_file)
      # init lists
      sub_img=[]; ses_img=[]; run_img=[]; ref_img=[]
      
      # fill lists to iterate mapnodes
      for index, row in df.iterrows():
        for r in row.run.strip("[]").split(" "):
            sub_img.append(row.subject)
            ses_img.append(row.session)
            run_img.append(r)
            if 'refsubject' in df.columns:
                if row.refsubject == 'nan':
                    # empty field
                    ref_img.append(row.subject)
                else:
                    # non-empty field
                    ref_img.append(row.refsubject) 
            else:
                ref_img.append(row.subject)

      infosource.iterables = [
            ('subject_id', sub_img),
            ('session_id', ses_img),
            ('run_id', run_img),
            ('refsubject_id', ref_img),
        ]
      infosource.synchronize = True
    else:
      print("No csv-file specified. Don't know what data to process.")


    # SelectFiles
    templates = {
        'image': 
        'derivatives/resampled-isotropic-1mm/'
        'sub-{subject_id}/ses-{session_id}/func/'
        'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc.nii.gz',
        'image_invPE': 
        'derivatives/resampled-isotropic-1mm/'
        'sub-{subject_id}/ses-{session_id}/fmap/'
        'sub-{subject_id}_ses-{session_id}*run-{run_id}_epi_res-1x1x1_preproc.nii.gz',
    }
    
    inputfiles = Node(
        nio.SelectFiles(templates,
                        base_directory=data_dir), 
                        name="input_files")

    # Datasink
    outfiles = Node(nio.DataSink(
        base_directory=ds_root,
        container=output_dir,
        parameterization=True),
        name="outfiles")

    # Use the following DataSink output substitutions
    outfiles.inputs.substitutions = [
        ('refsubject_id_', 'ref-'),
        ('subject_id_', 'sub-'),
        ('session_id_', 'ses-'),
        ('resampled-isotropic-1mm','undistort'),
        ('undistort/ud_func', 'undistort'),
    ]  
       
    outfiles.inputs.regexp_substitutions = [
        (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1/func'),
        (r'_ref-([a-zA-Z0-9]+)_run_id_[0-9][0-9]', r''),
    ]
    
    templates_mv = {
        'ud_minus': 
        'derivatives/resampled-isotropic-1mm/'
        'sub-{subject_id}/ses-{session_id}/func/'
        'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_MINUS.nii.gz',
        'ud_minus_warp': 
        'derivatives/resampled-isotropic-1mm/'
        'sub-{subject_id}/ses-{session_id}/func/'
        'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_MINUS_WARP.nii.gz',
        'ud_plus': 
        'derivatives/resampled-isotropic-1mm/'
        'sub-{subject_id}/ses-{session_id}/func/'
        'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_PLUS.nii.gz',
        'ud_plus_warp': 
        'derivatives/resampled-isotropic-1mm/'
        'sub-{subject_id}/ses-{session_id}/func/'
        'sub-{subject_id}_ses-{session_id}*run-{run_id}_bold_res-1x1x1_preproc_PLUS_WARP.nii.gz',
    }
    
    mv_infiles = Node(
        nio.SelectFiles(templates_mv,
                        base_directory=data_dir), 
                        name="mv_infiles")

    # Datasink
    mv_outfiles = Node(nio.DataSink(
        base_directory=ds_root,
        container=output_dir,
        parameterization=True),
        name="mv_outfiles")

    # Use the following DataSink output substitutions
    mv_outfiles.inputs.substitutions = [
        ('refsubject_id_', 'ref-'),
        ('subject_id_', 'sub-'),
        ('session_id_', 'ses-'),
        ('resampled-isotropic-1mm','undistort'),
        ('undistort/ud_func', 'undistort'),
    ]  
       
    mv_outfiles.inputs.regexp_substitutions = [
        (r'sub-([a-zA-Z0-9]+)_ses-([a-zA-Z0-9]+)', r'sub-\1/ses-\2/func/qwarp_plusminus/sub-\1_ses-\2'),
    ]    
    
    # -------------------------------------------- Create Pipeline
    undistort = Workflow(
        name='undistort',
        base_dir=os.path.join(ds_root, working_dir))

    undistort.connect([
        (infosource, inputfiles,
         [('subject_id', 'subject_id'),
          ('session_id', 'session_id'),
          ('run_id', 'run_id'),
          ('refsubject_id', 'refsubject_id'),
          ])])
               
    qwarp = Node(afni.QwarpPlusMinus(
        nopadWARP=True,outputtype='NIFTI_GZ'),
                    iterfield=('in_file'),name='qwarp')       
        
    undistort.connect(inputfiles, 'image',
                        qwarp, 'in_file')
    undistort.connect(inputfiles, 'image_invPE',
                        qwarp, 'base_file') 
    undistort.connect(inputfiles, 'image',
                        qwarp, 'out_file')    
  
    nwarp = Node(afni.NwarpApply(out_file='%s_undistort.nii.gz'),name='nwarp')
    
    undistort.connect(inputfiles, 'image',
                     nwarp, 'in_file')
    undistort.connect(qwarp, 'source_warp',
                     nwarp, 'warp')
    undistort.connect(inputfiles, 'image',
                     nwarp, 'master')
    undistort.connect(nwarp, 'out_file',
                     outfiles, 'ud_func')

    undistort.stop_on_first_crash = False  # True
    undistort.keep_inputs = True
    undistort.remove_unnecessary_outputs = False
    undistort.write_graph()
    undistort.run()

    mv_ud = Workflow(
        name='mv_ud',
        base_dir=os.path.join(ds_root, working_dir))

    mv_ud.connect([
        (infosource, mv_infiles,
         [('subject_id', 'subject_id'),
          ('session_id', 'session_id'),
          ('run_id', 'run_id'),
          ('refsubject_id', 'refsubject_id'),
          ])])
    
    mv_ud.connect(mv_infiles, 'ud_minus',
                        mv_outfiles, 'ud_func.@ud_minus')
    mv_ud.connect(mv_infiles, 'ud_plus',
                        mv_outfiles, 'ud_func.@ud_plus')
    mv_ud.connect(mv_infiles, 'ud_minus_warp',
                        mv_outfiles, 'ud_func.@ud_minus_warp')
    mv_ud.connect(mv_infiles, 'ud_plus_warp',
                        mv_outfiles, 'ud_func.@ud_plus_warp')
    
    mv_ud.stop_on_first_crash = False  # True
    mv_ud.keep_inputs = True
    mv_ud.remove_unnecessary_outputs = False
    mv_ud.write_graph()
    mv_ud.run()

    # remove the undistorted files from the ...derivatives/resampled folder
    for index, row in df.iterrows():
        fpath = os.path.join(data_dir,'derivatives','resampled-isotropic-1mm',
                     'sub-' + row.subject,'ses-' + str(row.session),'func')
        for f in glob.glob(os.path.join(fpath,'*US*.nii.gz')):
            os.remove(f)