Esempio n. 1
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
Esempio n. 2
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
Esempio n. 3
0
def init_prepare_epi_wf(omp_nthreads, matched_pe=False, name="prepare_epi_wf"):
    """
    Prepare opposed-PE EPI images for PE-POLAR SDC.

    This workflow takes in a set of EPI files and returns two 3D volumes with
    matching and opposed PE directions, ready to be used in field distortion
    estimation.

    The procedure involves: estimating a robust template using FreeSurfer's
    ``mri_robust_template``, bias field correction using ANTs ``N4BiasFieldCorrection``
    and AFNI ``3dUnifize``, skullstripping using FSL BET and AFNI ``3dAutomask``,
    and rigid coregistration to the reference using ANTs.

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

        from sdcflows.workflows.pepolar import init_prepare_epi_wf
        wf = init_prepare_epi_wf(omp_nthreads=8)


    **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**:

        epi_pe : str
            Phase-encoding direction of the EPI image to be corrected.
        maps_pe : list of tuple(pathlike, str)
            list of 3D or 4D NIfTI images
        ref_brain
            coregistration reference (skullstripped and bias field corrected)

    **Outputs**:

        opposed_pe : pathlike
            single 3D NIfTI file
        matched_pe : pathlike
            single 3D NIfTI file


    """
    inputnode = pe.Node(
        niu.IdentityInterface(fields=['epi_pe', 'maps_pe', 'ref_brain']),
        name='inputnode')

    outputnode = pe.Node(
        niu.IdentityInterface(fields=['opposed_pe', 'matched_pe']),
        name='outputnode')

    ants_settings = pkgr.resource_filename('sdcflows',
                                           'data/translation_rigid.json')

    split = pe.Node(niu.Function(function=_split_epi_lists), name='split')

    merge_op = pe.Node(
        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_op')

    ref_op_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads,
                                                    name='ref_op_wf')

    op2ref_reg = pe.Node(ants.Registration(from_file=ants_settings,
                                           output_warped_image=True),
                         name='op2ref_reg',
                         n_procs=omp_nthreads)

    workflow = Workflow(name=name)
    workflow.connect([
        (inputnode, split, [('maps_pe', 'in_files'), ('epi_pe', 'pe_dir')]),
        (split, merge_op, [(('out', _front), 'in_files')]),
        (merge_op, ref_op_wf, [('out_file', 'inputnode.in_file')]),
        (ref_op_wf, op2ref_reg, [('outputnode.skull_stripped_file',
                                  'moving_image')]),
        (inputnode, op2ref_reg, [('ref_brain', 'fixed_image')]),
        (op2ref_reg, outputnode, [('warped_image', 'opposed_pe')]),
    ])

    if not matched_pe:
        workflow.connect([
            (inputnode, outputnode, [('ref_brain', 'matched_pe')]),
        ])
        return workflow

    merge_ma = pe.Node(
        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_ma')

    ref_ma_wf = init_enhance_and_skullstrip_bold_wf(omp_nthreads=omp_nthreads,
                                                    name='ref_ma_wf')

    ma2ref_reg = pe.Node(ants.Registration(from_file=ants_settings,
                                           output_warped_image=True),
                         name='ma2ref_reg',
                         n_procs=omp_nthreads)

    workflow.connect([
        (split, merge_ma, [(('out', _last), 'in_files')]),
        (merge_ma, ref_ma_wf, [('out_file', 'inputnode.in_file')]),
        (ref_ma_wf, ma2ref_reg, [('outputnode.skull_stripped_file',
                                  'moving_image')]),
        (inputnode, ma2ref_reg, [('ref_brain', 'fixed_image')]),
        (ma2ref_reg, outputnode, [('warped_image', 'matched_pe')]),
    ])
    return workflow
Esempio n. 4
0
def init_sdc_unwarp_wf(omp_nthreads, debug, name='sdc_unwarp_wf'):
    """
    Apply the warping given by a displacements fieldmap.

    This workflow takes in a displacements field through which the
    input reference can be corrected for susceptibility-derived distortion.

    It also calculates a new mask for the input dataset, after the distortions
    have been accounted for.

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

        from sdcflows.workflows.unwarp import init_sdc_unwarp_wf
        wf = init_sdc_unwarp_wf(omp_nthreads=8,
                                debug=False)

    Parameters
    ----------
    omp_nthreads : int
        Maximum number of threads an individual process may use.
    debug : bool
        Run fast configurations of registrations.
    name : str
        Unique name of this workflow.

    Inputs
    ------
    in_warp : os.pathlike
        The :abbr:`DFM (displacements field map)` that corrects for
        susceptibility-derived distortions estimated elsewhere.
    in_reference : os.pathlike
        the reference image to be unwarped.
    in_reference_mask : os.pathlike
        the reference image mask to be unwarped

    Outputs
    -------
    out_reference : str
        the ``in_reference`` after unwarping
    out_reference_brain : str
        the ``in_reference`` after unwarping and skullstripping
    out_warp : str
        the ``in_warp`` field is forwarded for compatibility
    out_mask : str
        mask of the unwarped input file

    """
    workflow = Workflow(name=name)
    inputnode = pe.Node(niu.IdentityInterface(
        fields=['in_warp', 'in_reference', 'in_reference_mask']),
                        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'out_reference', 'out_reference_brain', 'out_warp', 'out_mask'
    ]),
                         name='outputnode')

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

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

    enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(
        omp_nthreads=omp_nthreads, pre_mask=True)
    workflow.connect([
        (inputnode, unwarp_reference, [('in_warp', 'transforms'),
                                       ('in_reference', 'reference_image'),
                                       ('in_reference', 'input_image')]),
        (inputnode, unwarp_mask, [('in_warp', 'transforms'),
                                  ('in_reference_mask', 'reference_image'),
                                  ('in_reference_mask', 'input_image')]),
        (unwarp_reference, enhance_and_skullstrip_bold_wf,
         [('output_image', 'inputnode.in_file')]),
        (unwarp_mask, enhance_and_skullstrip_bold_wf,
         [('output_image', 'inputnode.pre_mask')]),
        (inputnode, outputnode, [('in_warp', 'out_warp')]),
        (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')]),
    ])
    return workflow
Esempio n. 5
0
def init_sdc_unwarp_wf(omp_nthreads, fmap_demean, debug, name='sdc_unwarp_wf'):
    """
    Apply the warping given by a displacements fieldmap.

    This workflow takes in a displacements fieldmap and calculates the corresponding
    displacements field (in other words, an ANTs-compatible warp file).

    It also calculates a new mask for the input dataset that takes into account the distortions.
    The mask is restricted to the field of view of the fieldmap since outside of it corrections
    could not be performed.

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

        from sdcflows.workflows.unwarp import init_sdc_unwarp_wf
        wf = init_sdc_unwarp_wf(omp_nthreads=8,
                                fmap_demean=True,
                                debug=False)


    Inputs

        in_reference
            the reference image
        in_reference_brain
            the reference image (skull-stripped)
        in_mask
            a brain mask corresponding to ``in_reference``
        metadata
            metadata associated to the ``in_reference`` EPI input
        fmap
            the fieldmap in Hz
        fmap_ref
            the reference (anatomical) image corresponding to ``fmap``
        fmap_mask
            a brain mask corresponding to ``fmap``


    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_jacobian
            the jacobian of the field (for drop-out alleviation)
        out_mask
            mask of the unwarped input file

    """
    workflow = Workflow(name=name)
    inputnode = pe.Node(niu.IdentityInterface(fields=[
        'in_reference', 'in_reference_brain', 'in_mask', 'metadata',
        'fmap_ref', 'fmap_mask', 'fmap'
    ]),
                        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'out_reference', 'out_reference_brain', 'out_warp', 'out_mask',
        'out_jacobian'
    ]),
                         name='outputnode')

    # Register the reference of the fieldmap to the reference
    # of the target image (the one that shall be corrected)
    ants_settings = pkgr.resource_filename('sdcflows',
                                           'data/fmap-any_registration.json')
    if debug:
        ants_settings = pkgr.resource_filename(
            'sdcflows', 'data/fmap-any_registration_testing.json')
    fmap2ref_reg = pe.Node(ANTSRegistrationRPT(
        generate_report=True,
        from_file=ants_settings,
        output_inverse_warped_image=True,
        output_warped_image=True),
                           name='fmap2ref_reg',
                           n_procs=omp_nthreads)

    ds_report_reg = pe.Node(DerivativesDataSink(desc='magnitude',
                                                suffix='bold'),
                            name='ds_report_reg',
                            mem_gb=0.01,
                            run_without_submitting=True)

    # Map the VSM into the EPI space
    fmap2ref_apply = pe.Node(ANTSApplyTransformsRPT(generate_report=True,
                                                    dimension=3,
                                                    interpolation='BSpline',
                                                    float=True),
                             name='fmap2ref_apply')

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

    ds_report_vsm = pe.Node(DerivativesDataSink(desc='fieldmap',
                                                suffix='bold'),
                            name='ds_report_vsm',
                            mem_gb=0.01,
                            run_without_submitting=True)

    # Fieldmap to rads and then to voxels (VSM - voxel shift map)
    torads = pe.Node(FieldToRadS(fmap_range=0.5), name='torads')

    get_ees = pe.Node(niu.Function(function=_get_ees, output_names=['ees']),
                      name='get_ees')

    gen_vsm = pe.Node(fsl.FUGUE(save_unmasked_shift=True), name='gen_vsm')
    # Convert the VSM into a DFM (displacements field map)
    # or: FUGUE shift to ANTS warping.
    vsm2dfm = pe.Node(itk.FUGUEvsm2ANTSwarp(), name='vsm2dfm')
    jac_dfm = pe.Node(ants.CreateJacobianDeterminantImage(
        imageDimension=3, outputImage='jacobian.nii.gz'),
                      name='jac_dfm')

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

    fieldmap_fov_mask = pe.Node(FilledImageLike(dtype='uint8'),
                                name='fieldmap_fov_mask')

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

    apply_fov_mask = pe.Node(fsl.ApplyMask(), name="apply_fov_mask")

    enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(
        omp_nthreads=omp_nthreads, pre_mask=True)

    workflow.connect([
        (inputnode, fmap2ref_reg, [('fmap_ref', 'moving_image')]),
        (inputnode, fmap2ref_apply, [('in_reference', 'reference_image')]),
        (fmap2ref_reg, fmap2ref_apply, [('composite_transform', 'transforms')
                                        ]),
        (inputnode, fmap_mask2ref_apply, [('in_reference', 'reference_image')
                                          ]),
        (fmap2ref_reg, fmap_mask2ref_apply, [('composite_transform',
                                              'transforms')]),
        (fmap2ref_apply, ds_report_vsm, [('out_report', 'in_file')]),
        (inputnode, fmap2ref_reg, [('in_reference_brain', 'fixed_image')]),
        (fmap2ref_reg, ds_report_reg, [('out_report', 'in_file')]),
        (inputnode, fmap2ref_apply, [('fmap', 'input_image')]),
        (inputnode, fmap_mask2ref_apply, [('fmap_mask', 'input_image')]),
        (fmap2ref_apply, torads, [('output_image', 'in_file')]),
        (inputnode, get_ees, [('in_reference', 'in_file'),
                              ('metadata', 'in_meta')]),
        (fmap_mask2ref_apply, gen_vsm, [('output_image', 'mask_file')]),
        (get_ees, gen_vsm, [('ees', 'dwell_time')]),
        (inputnode, gen_vsm, [(('metadata', _get_pedir_fugue),
                               'unwarp_direction')]),
        (inputnode, vsm2dfm, [(('metadata', _get_pedir_bids), 'pe_dir')]),
        (torads, gen_vsm, [('out_file', 'fmap_in_file')]),
        (vsm2dfm, unwarp_reference, [('out_file', 'transforms')]),
        (inputnode, unwarp_reference, [('in_reference', 'reference_image')]),
        (inputnode, unwarp_reference, [('in_reference', 'input_image')]),
        (vsm2dfm, outputnode, [('out_file', 'out_warp')]),
        (vsm2dfm, jac_dfm, [('out_file', 'deformationField')]),
        (inputnode, fieldmap_fov_mask, [('fmap_ref', 'in_file')]),
        (fieldmap_fov_mask, fmap_fov2ref_apply, [('out_file', 'input_image')]),
        (inputnode, fmap_fov2ref_apply, [('in_reference', 'reference_image')]),
        (fmap2ref_reg, fmap_fov2ref_apply, [('composite_transform',
                                             'transforms')]),
        (fmap_fov2ref_apply, apply_fov_mask, [('output_image', 'mask_file')]),
        (unwarp_reference, apply_fov_mask, [('output_image', 'in_file')]),
        (apply_fov_mask, enhance_and_skullstrip_bold_wf,
         [('out_file', 'inputnode.in_file')]),
        (fmap_mask2ref_apply, enhance_and_skullstrip_bold_wf,
         [('output_image', 'inputnode.pre_mask')]),
        (apply_fov_mask, outputnode, [('out_file', 'out_reference')]),
        (enhance_and_skullstrip_bold_wf, outputnode,
         [('outputnode.mask_file', 'out_mask'),
          ('outputnode.skull_stripped_file', 'out_reference_brain')]),
        (jac_dfm, outputnode, [('jacobian_image', 'out_jacobian')]),
    ])

    if fmap_demean:
        # Demean within mask
        demean = pe.Node(DemeanImage(), name='demean')

        workflow.connect([
            (gen_vsm, demean, [('shift_out_file', 'in_file')]),
            (fmap_mask2ref_apply, demean, [('output_image', 'in_mask')]),
            (demean, vsm2dfm, [('out_file', 'in_file')]),
        ])

    else:
        workflow.connect([
            (gen_vsm, vsm2dfm, [('shift_out_file', 'in_file')]),
        ])

    return workflow
Esempio n. 6
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 sdcflows.workflows.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 = 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_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',
                                               'in_file')]),
        ])
    else:
        workflow.connect([(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')

    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
Esempio n. 7
0
def init_prepare_epi_wf(omp_nthreads, name="prepare_epi_wf"):
    """
    This workflow takes in a set of EPI files with with the same phase
    encoding direction and returns a single 3D volume ready to be used in
    field distortion estimation.

    The procedure involves: estimating a robust template using FreeSurfer's
    'mri_robust_template', bias field correction using ANTs N4BiasFieldCorrection
    and AFNI 3dUnifize, skullstripping using FSL BET and AFNI 3dAutomask,
    and rigid coregistration to the reference using ANTs.

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

        from sdcflows.workflows.pepolar import init_prepare_epi_wf
        wf = init_prepare_epi_wf(omp_nthreads=8)


    Inputs

        fmaps
            list of 3D or 4D NIfTI images
        ref_brain
            coregistration reference (skullstripped and bias field corrected)

    Outputs

        out_file
            single 3D NIfTI file

    """
    inputnode = pe.Node(niu.IdentityInterface(fields=['fmaps', 'ref_brain']),
                        name='inputnode')

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

    split = pe.MapNode(fsl.Split(dimension='t'),
                       iterfield='in_file',
                       name='split')

    merge = pe.Node(
        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')

    enhance_and_skullstrip_bold_wf = init_enhance_and_skullstrip_bold_wf(
        omp_nthreads=omp_nthreads)

    ants_settings = pkgr.resource_filename('sdcflows',
                                           'data/translation_rigid.json')
    fmap2ref_reg = pe.Node(ants.Registration(from_file=ants_settings,
                                             output_warped_image=True),
                           name='fmap2ref_reg',
                           n_procs=omp_nthreads)

    workflow = Workflow(name=name)

    def _flatten(l):
        from nipype.utils.filemanip import filename_to_list
        return [item for sublist in l for item in filename_to_list(sublist)]

    workflow.connect([
        (inputnode, split, [('fmaps', 'in_file')]),
        (split, merge, [(('out_files', _flatten), 'in_files')]),
        (merge, enhance_and_skullstrip_bold_wf, [('out_file',
                                                  'inputnode.in_file')]),
        (enhance_and_skullstrip_bold_wf, fmap2ref_reg,
         [('outputnode.skull_stripped_file', 'moving_image')]),
        (inputnode, fmap2ref_reg, [('ref_brain', 'fixed_image')]),
        (fmap2ref_reg, outputnode, [('warped_image', 'out_file')]),
    ])

    return workflow