Ejemplo n.º 1
0
def init_fitlins_wf(database_path,
                    out_dir,
                    analysis_level,
                    space,
                    desc=None,
                    model=None,
                    participants=None,
                    smoothing=None,
                    drop_missing=False,
                    base_dir=None,
                    name='fitlins_wf'):
    from nipype.pipeline import engine as pe
    from nipype.interfaces import utility as niu
    from ..interfaces.bids import (ModelSpecLoader, LoadBIDSModel, BIDSSelect,
                                   BIDSDataSink)
    from ..interfaces.nistats import DesignMatrix, FirstLevelModel, SecondLevelModel
    from ..interfaces.visualizations import (DesignPlot, DesignCorrelationPlot,
                                             ContrastMatrixPlot,
                                             GlassBrainPlot)
    from ..interfaces.utils import MergeAll, CollateWithMetadata

    wf = pe.Workflow(name=name, base_dir=base_dir)

    # Find the appropriate model file(s)
    specs = ModelSpecLoader(database_path=database_path)
    if model is not None:
        specs.inputs.model = model

    model_dict = specs.run().outputs.model_spec
    if not model_dict:
        raise RuntimeError("Unable to find or construct models")
    if isinstance(model_dict, list):
        raise RuntimeError(
            "Currently unable to run multiple models in parallel - "
            "please specify model")
    #
    # Load and run the model
    #
    loader = pe.Node(LoadBIDSModel(database_path=database_path,
                                   model=model_dict,
                                   selectors={
                                       'desc': desc,
                                       'space': space
                                   }),
                     name='loader')

    if participants is not None:
        loader.inputs.selectors['subject'] = participants
    if database_path is not None:
        loader.inputs.database_path = database_path

    # Select preprocessed BOLD series to analyze
    getter = pe.Node(BIDSSelect(database_path=database_path,
                                selectors={
                                    'suffix': 'bold',
                                    'extension': ['nii.gz', 'nii', 'gii']
                                }),
                     name='getter')

    if smoothing:
        smoothing_params = smoothing.split(':', 2)
        # Convert old style and warn; this should turn into an (informative) error around 0.5.0
        if smoothing_params[0] == 'iso':
            smoothing_params = (smoothing_params[1], 'l1', smoothing_params[0])
            warnings.warn(
                "The format for smoothing arguments has changed. Please use "
                f"{':'.join(smoothing_params)} instead of {smoothing}.",
                FutureWarning)
        # Add defaults to simplify later logic
        if len(smoothing_params) == 1:
            smoothing_params.extend(('l1', 'iso'))
        elif len(smoothing_params) == 2:
            smoothing_params.append('iso')

        smoothing_fwhm, smoothing_level, smoothing_type = smoothing_params
        smoothing_fwhm = float(smoothing_fwhm)
        if smoothing_type not in ('iso'):
            raise ValueError(f"Unknown smoothing type {smoothing_type}")

        # Check that smmoothing level exists in model
        if smoothing_level.lower().startswith("l"):
            if int(smoothing_level[1:]) > len(model_dict['Steps']):
                raise ValueError(f"Invalid smoothing level {smoothing_level}")
        elif smoothing_level.lower() not in (step['Level'].lower()
                                             for step in model_dict['Steps']):
            raise ValueError(f"Invalid smoothing level {smoothing_level}")

    design_matrix = pe.MapNode(DesignMatrix(drop_missing=drop_missing),
                               iterfield=['session_info', 'bold_file'],
                               name='design_matrix')

    l1_model = pe.MapNode(
        FirstLevelModel(),
        iterfield=['design_matrix', 'contrast_info', 'bold_file', 'mask_file'],
        mem_gb=3,
        name='l1_model')

    def _deindex(tsv):
        from pathlib import Path
        import pandas as pd
        out_tsv = str(Path.cwd() / Path(tsv).name)
        pd.read_csv(tsv, sep='\t', index_col=0).to_csv(out_tsv,
                                                       sep='\t',
                                                       index=False)
        return out_tsv

    deindex_tsv = pe.MapNode(niu.Function(function=_deindex),
                             iterfield=['tsv'],
                             name='deindex_tsv')

    # Set up common patterns
    image_pattern = 'reports/[sub-{subject}/][ses-{session}/]figures/[run-{run}/]' \
        '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \
        '[_rec-{reconstruction}][_run-{run}][_echo-{echo}]_' \
        '{suffix<design|corr|contrasts>}.svg'
    contrast_plot_pattern = 'reports/[sub-{subject}/][ses-{session}/]figures/[run-{run}/]' \
        '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \
        '[_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}]_' \
        'contrast-{contrast}_stat-{stat<effect|variance|z|p|t|F|FEMA>}_ortho.png'
    design_matrix_pattern = '[sub-{subject}/][ses-{session}/]' \
        '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \
        '[_rec-{reconstruction}][_run-{run}][_echo-{echo}]_{suffix<design>}.tsv'
    contrast_pattern = '[sub-{subject}/][ses-{session}/]' \
        '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \
        '[_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}]_' \
        'contrast-{contrast}_stat-{stat<effect|variance|z|p|t|F|FEMA>}_statmap.nii.gz'
    model_map_pattern = '[sub-{subject}/][ses-{session}/]' \
        '[sub-{subject}_][ses-{session}_]task-{task}[_acq-{acquisition}]' \
        '[_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}]_' \
        'stat-{stat<rSquare|logLikelihood>}_statmap.nii.gz'
    # Set up general interfaces
    #
    # HTML snippets to be included directly in report, not
    # saved as individual derivative files
    #

    reportlet_dir = Path(base_dir) / 'reportlets' / 'fitlins'
    reportlet_dir.mkdir(parents=True, exist_ok=True)
    snippet_pattern = '[sub-{subject}/][ses-{session}/][sub-{subject}_]' \
        '[ses-{session}_]task-{task}_[run-{run}_]snippet.html'
    ds_model_warnings = pe.MapNode(BIDSDataSink(
        base_directory=str(reportlet_dir), path_patterns=snippet_pattern),
                                   iterfield=['entities', 'in_file'],
                                   run_without_submitting=True,
                                   name='ds_model_warning')

    plot_design = pe.MapNode(DesignPlot(image_type='svg'),
                             iterfield='data',
                             name='plot_design')

    plot_corr = pe.MapNode(DesignCorrelationPlot(image_type='svg'),
                           iterfield=['data', 'contrast_info'],
                           name='plot_corr')

    plot_l1_contrast_matrix = pe.MapNode(ContrastMatrixPlot(image_type='svg'),
                                         iterfield=['data', 'contrast_info'],
                                         name='plot_l1_contrast_matrix')

    ds_design = pe.MapNode(BIDSDataSink(base_directory=out_dir,
                                        fixed_entities={'suffix': 'design'},
                                        path_patterns=image_pattern),
                           iterfield=['entities', 'in_file'],
                           run_without_submitting=True,
                           name='ds_design')

    ds_design_matrix = pe.MapNode(BIDSDataSink(
        base_directory=out_dir,
        fixed_entities={'suffix': 'design'},
        path_patterns=design_matrix_pattern),
                                  iterfield=['entities', 'in_file'],
                                  run_without_submitting=True,
                                  name='ds_design_matrix')

    ds_corr = pe.MapNode(BIDSDataSink(base_directory=out_dir,
                                      fixed_entities={'suffix': 'corr'},
                                      path_patterns=image_pattern),
                         iterfield=['entities', 'in_file'],
                         run_without_submitting=True,
                         name='ds_corr')

    ds_l1_contrasts = pe.MapNode(BIDSDataSink(
        base_directory=out_dir,
        fixed_entities={'suffix': 'contrasts'},
        path_patterns=image_pattern),
                                 iterfield=['entities', 'in_file'],
                                 run_without_submitting=True,
                                 name='ds_l1_contrasts')

    #
    # General Connections
    #
    wf.connect([
        (loader, ds_model_warnings, [('warnings', 'in_file')]),
        (loader, design_matrix, [('design_info', 'session_info')]),
        (getter, design_matrix, [('bold_files', 'bold_file')]),
        (getter, l1_model, [('bold_files', 'bold_file'),
                            ('mask_files', 'mask_file')]),
        (design_matrix, l1_model, [('design_matrix', 'design_matrix')]),
        (design_matrix, plot_design, [('design_matrix', 'data')]),
        (design_matrix, plot_l1_contrast_matrix, [('design_matrix', 'data')]),
        (design_matrix, plot_corr, [('design_matrix', 'data')]),
        (design_matrix, deindex_tsv, [('design_matrix', 'tsv')]),
        (deindex_tsv, ds_design_matrix, [('out', 'in_file')]),
    ])

    stage = None
    model = l1_model
    for ix, step in enumerate(step['Level'] for step in model_dict['Steps']):
        # Set up elements common across levels

        #
        # Because pybids generates the entire model in one go, we will need
        # various helper nodes to select the correct portions of the model
        #

        level = 'l{:d}'.format(ix + 1)

        # TODO: No longer used at higher level, suggesting we can simply return
        # entities from loader as a single list
        select_entities = pe.Node(niu.Select(index=ix),
                                  name='select_{}_entities'.format(level),
                                  run_without_submitting=True)

        select_contrasts = pe.Node(niu.Select(index=ix),
                                   name='select_{}_contrasts'.format(level),
                                   run_without_submitting=True)

        # Squash the results of MapNodes that may have generated multiple maps
        # into single lists.
        # Do the same with corresponding metadata - interface will complain if shapes mismatch
        collate = pe.Node(MergeAll([
            'effect_maps', 'variance_maps', 'stat_maps', 'zscore_maps',
            'pvalue_maps', 'contrast_metadata'
        ],
                                   check_lengths=(not drop_missing)),
                          name='collate_{}'.format(level),
                          run_without_submitting=True)

        #
        # Plotting
        #

        plot_contrasts = pe.MapNode(GlassBrainPlot(image_type='png'),
                                    iterfield='data',
                                    name='plot_{}_contrasts'.format(level))

        #
        # Derivatives
        #

        collate_outputs = pe.Node(CollateWithMetadata(fields=[
            'effect_maps', 'variance_maps', 'stat_maps', 'pvalue_maps',
            'zscore_maps'
        ],
                                                      field_to_metadata_map={
                                                          'effect_maps': {
                                                              'stat': 'effect'
                                                          },
                                                          'variance_maps': {
                                                              'stat':
                                                              'variance'
                                                          },
                                                          'pvalue_maps': {
                                                              'stat': 'p'
                                                          },
                                                          'zscore_maps': {
                                                              'stat': 'z'
                                                          },
                                                      }),
                                  name=f'collate_{level}_outputs')

        ds_contrast_maps = pe.Node(BIDSDataSink(
            base_directory=out_dir, path_patterns=contrast_pattern),
                                   run_without_submitting=True,
                                   name='ds_{}_contrast_maps'.format(level))

        ds_contrast_plots = pe.Node(BIDSDataSink(
            base_directory=out_dir, path_patterns=contrast_plot_pattern),
                                    run_without_submitting=True,
                                    name='ds_{}_contrast_plots'.format(level))

        if ix == 0:
            ds_model_maps = pe.Node(BIDSDataSink(
                base_directory=out_dir, path_patterns=model_map_pattern),
                                    run_without_submitting=True,
                                    name='ds_{}_model_maps'.format(level))

            collate_mm = pe.Node(MergeAll(['model_maps', 'model_metadata'],
                                          check_lengths=(not drop_missing)),
                                 name='collate_mm_{}'.format(level),
                                 run_without_submitting=True)

            wf.connect([
                (loader, select_entities, [('entities', 'inlist')]),
                (select_entities, getter, [('out', 'entities')]),
                (select_entities, ds_model_warnings, [('out', 'entities')]),
                (select_entities, ds_design, [('out', 'entities')]),
                (select_entities, ds_design_matrix, [('out', 'entities')]),
                (plot_design, ds_design, [('figure', 'in_file')]),
                (select_contrasts, plot_l1_contrast_matrix,
                 [('out', 'contrast_info')]),
                (select_contrasts, plot_corr, [('out', 'contrast_info')]),
                (select_entities, ds_l1_contrasts, [('out', 'entities')]),
                (select_entities, ds_corr, [('out', 'entities')]),
                (plot_l1_contrast_matrix, ds_l1_contrasts, [('figure',
                                                             'in_file')]),
                (plot_corr, ds_corr, [('figure', 'in_file')]),
                (model, collate_mm, [('model_maps', 'model_maps'),
                                     ('model_metadata', 'model_metadata')]),
                (collate_mm, ds_model_maps, [('model_maps', 'in_file'),
                                             ('model_metadata', 'entities')]),
            ])

        #  Set up higher levels
        else:
            model = pe.MapNode(SecondLevelModel(),
                               iterfield=['contrast_info'],
                               name='{}_model'.format(level))

            wf.connect([
                (stage, model, [('effect_maps', 'effect_maps'),
                                ('variance_maps', 'variance_maps'),
                                ('contrast_metadata', 'stat_metadata')]),
            ])

        if smoothing and smoothing_level in (step, level):
            model.inputs.smoothing_fwhm = smoothing_fwhm

        wf.connect([
            (loader, select_contrasts, [('contrast_info', 'inlist')]),
            (select_contrasts, model, [('out', 'contrast_info')]),
            (model, collate, [('effect_maps', 'effect_maps'),
                              ('variance_maps', 'variance_maps'),
                              ('stat_maps', 'stat_maps'),
                              ('zscore_maps', 'zscore_maps'),
                              ('pvalue_maps', 'pvalue_maps'),
                              ('contrast_metadata', 'contrast_metadata')]),
            (collate, collate_outputs, [
                ('contrast_metadata', 'metadata'),
                ('effect_maps', 'effect_maps'),
                ('variance_maps', 'variance_maps'),
                ('stat_maps', 'stat_maps'),
                ('zscore_maps', 'zscore_maps'),
                ('pvalue_maps', 'pvalue_maps'),
            ]),
            (collate, plot_contrasts, [('stat_maps', 'data')]),
            (collate_outputs, ds_contrast_maps, [('out', 'in_file'),
                                                 ('metadata', 'entities')]),
            (collate, ds_contrast_plots, [('contrast_metadata', 'entities')]),
            (plot_contrasts, ds_contrast_plots, [('figure', 'in_file')]),
        ])

        stage = model
        if step == analysis_level:
            break

    return wf
Ejemplo n.º 2
0
def init_bbreg_wf(use_bbr, bold2t1w_dof, omp_nthreads, name='bbreg_wf'):
    """
    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.util.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 ::
        :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
        t1_2_fsnative_reverse_transform
            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)
        t1_brain
            Unused (see :py:func:`~fmriprep.workflows.util.init_fsl_bbr_wf`)
        t1_seg
            Unused (see :py:func:`~fmriprep.workflows.util.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 nine degrees of freedom to account
for distortions remaining in the BOLD reference.
"""

    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')

    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(fs.utils.LTAConvert(out_fsl=True),
                          name='lta2fsl_fwd')
    lta2fsl_inv = pe.Node(fs.utils.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, [('t1_2_fsnative_reverse_transform', 'in_lta2')
                                 ]),
        (lta_concat, lta2fsl_fwd, [('out_file', 'in_lta')]),
        (lta_concat, lta2fsl_inv, [('out_file', 'in_lta')]),
        (inputnode, fsl2itk_fwd, [('t1_brain', 'reference_file'),
                                  ('in_file', 'source_file')]),
        (inputnode, fsl2itk_inv, [('in_file', 'reference_file'),
                                  ('t1_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(fs.utils.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
Ejemplo n.º 3
0
def create_motion_correct_pipeline(name='motion_correct'):
    """Creates a pipeline that corrects for motion artifact in dMRI sequences.
    It takes a series of diffusion weighted images and rigidly co-registers
    them to one reference image. Finally, the b-matrix is rotated accordingly
    (Leemans et al. 2009 - http://www.ncbi.nlm.nih.gov/pubmed/19319973),
    making use of the rotation matrix obtained by FLIRT.

    .. warning::

    IMPORTANT NOTICE: this workflow rotates the b-vectors, so please be adviced
    that not all the dicom converters ensure the consistency between the resulting
    nifti orientation and the b matrix table (e.g. dcm2nii checks it).


    Example
    -------

    >>> nipype_motioncorrect = create_motion_correct_pipeline('nipype_motioncorrect')
    >>> nipype_motioncorrect.inputs.inputnode.in_file = 'diffusion.nii'
    >>> nipype_motioncorrect.inputs.inputnode.in_bvec = 'diffusion.bvec'
    >>> nipype_motioncorrect.inputs.inputnode.ref_num = 0
    >>> nipype_motioncorrect.run() # doctest: +SKIP

    Inputs::

        inputnode.in_file
        inputnode.ref_num
        inputnode.in_bvec

    Outputs::

        outputnode.motion_corrected
        outputnode.out_bvec

    """

    inputnode = pe.Node(
        niu.IdentityInterface(
            fields=['in_file', 'ref_num', 'in_bvec']),
                        name='inputnode')

    pipeline = pe.Workflow(name=name)

    split = pe.Node(fsl.Split(dimension='t'), name='split')
    pick_ref = pe.Node(niu.Select(), name='pick_ref')
    coregistration = pe.MapNode(fsl.FLIRT(no_search=True, interp='spline',
                                padding_size=1, dof=6), name='coregistration', iterfield=['in_file'])
    rotate_bvecs = pe.Node(niu.Function(input_names=['in_bvec', 'in_matrix'], output_names=[
                           'out_file'], function=_rotate_bvecs), name='rotate_b_matrix')
    merge = pe.Node(fsl.Merge(dimension='t'), name='merge')
    outputnode = pe.Node(
        niu.IdentityInterface(
            fields=['motion_corrected', 'out_bvec']),
                        name='outputnode')

    pipeline.connect([
                       (inputnode, split, [('in_file', 'in_file')])
                      ,(split, pick_ref, [('out_files', 'inlist')])
                      ,(inputnode, pick_ref, [('ref_num', 'index')])
                      ,(split, coregistration, [('out_files', 'in_file')])
                      ,(inputnode, rotate_bvecs, [('in_bvec', 'in_bvec')])
                      ,(coregistration, rotate_bvecs, [('out_matrix_file', 'in_matrix')])
                      ,(pick_ref, coregistration, [('out', 'reference')])
                      ,(coregistration, merge, [('out_file', 'in_files')])
                      ,(merge, outputnode, [('merged_file', 'motion_corrected')])
                      ,(rotate_bvecs, outputnode, [('out_file', 'out_bvec')])
                    ])

    return pipeline
Ejemplo n.º 4
0
def create_workflow():
    featpreproc = pe.Workflow(name="featpreproc")

    featpreproc.base_dir = os.path.join(ds_root, 'workingdirs')

    # ===================================================================
    #                  _____                   _
    #                 |_   _|                 | |
    #                   | |  _ __  _ __  _   _| |_
    #                   | | | '_ \| '_ \| | | | __|
    #                  _| |_| | | | |_) | |_| | |_
    #                 |_____|_| |_| .__/ \__,_|\__|
    #                             | |
    #                             |_|
    # ===================================================================

    # ------------------ Specify variables
    inputnode = pe.Node(
        niu.IdentityInterface(fields=[
            'funcs',
            'subject_id',
            'session_id',
            'fwhm',  # smoothing
            'highpass'
        ]),
        name="inputspec")

    # SelectFiles
    templates = {
        'ref_manual_fmapmask':  # was: manual_fmapmask
        'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/'
            'sub-eddy_ses-20170511_magnitude1_res-1x1x1_manualmask.nii.gz',

        'ref_fmap_magnitude':
        'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/'
            'sub-eddy_ses-20170511_magnitude1_res-1x1x1_reference.nii.gz',

        'ref_fmap_phasediff':
        'derivatives/resampled-isotropic-1mm/sub-eddy/ses-20170511/fmap/'
            'sub-eddy_ses-20170511_phasediff_res-1x1x1_preproc'
            '.nii.gz',

        # 'manualweights':
        # 'manual-masks/sub-eddy/ses-20170511/func/'
        #     'sub-eddy_ses-20170511_task-curvetracing_run-01_frame-50_bold'
        #     '_res-1x1x1_manualweights.nii.gz',

        'ref_func':  # was: manualmask_func_ref
        'derivatives/manual-masks/sub-eddy/ses-20170607/func/'
            'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_'
            'res-1x1x1_fnirt_reference.nii.gz',

        'ref_funcmask':  # was: manualmask
        'derivatives/manual-masks/sub-eddy/ses-20170607/func/'
            'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_'
            'res-1x1x1_fnirt_mask.nii.gz',

        'ref_t1':
        'derivatives/manual-masks/sub-eddy/ses-20170511/anat/'
            'sub-eddy_ses-20170511_T1w_res-1x1x1_reference.nii.gz',

        'ref_t1mask':
        'derivatives/manual-masks/sub-eddy/ses-20170511/anat/'
            'sub-eddy_ses-20170511_T1w_res-1x1x1_manualmask.nii.gz',

        # 'funcs':
        # 'resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/func/'
        #     # 'sub-{subject_id}_ses-{session_id}*_bold_res-1x1x1_preproc'
        #     'sub-{subject_id}_ses-{session_id}*run-01_bold_res-1x1x1_preproc'
        #     # '.nii.gz',
        #     '_nvol10.nii.gz',

        'fmap_phasediff':
        'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/'
            'sub-{subject_id}_ses-{session_id}_phasediff_res-1x1x1_preproc'
            '.nii.gz',

        'fmap_magnitude':
        'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/'
            'sub-{subject_id}_ses-{session_id}_magnitude1_res-1x1x1_preproc'
            '.nii.gz',

        # 'fmap_mask':
        # 'transformed-manual-fmap-mask/sub-{subject_id}/ses-{session_id}/fmap/'
        #     'sub-{subject_id}_ses-{session_id}_'
        #     'magnitude1_res-1x1x1_preproc.nii.gz',
    }

    inputfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir),
                         name="input_files")

    featpreproc.connect([(inputnode, inputfiles, [
        ('subject_id', 'subject_id'),
        ('session_id', 'session_id'),
    ])])

    # ===================================================================
    #                   ____        _               _
    #                  / __ \      | |             | |
    #                 | |  | |_   _| |_ _ __  _   _| |_
    #                 | |  | | | | | __| '_ \| | | | __|
    #                 | |__| | |_| | |_| |_) | |_| | |_
    #                  \____/ \__,_|\__| .__/ \__,_|\__|
    #                                  | |
    #                                  |_|
    # ===================================================================

    # ------------------ Output Files
    # Datasink
    outputfiles = pe.Node(nio.DataSink(base_directory=ds_root,
                                       container='derivatives/featpreproc',
                                       parameterization=True),
                          name="output_files")

    # Use the following DataSink output substitutions
    # each tuple is only matched once per file
    outputfiles.inputs.substitutions = [
        ('/_mc_method_afni3dAllinSlices/', '/'),
        ('/_mc_method_afni3dAllinSlices/', '/'),  # needs to appear twice
        ('/oned_file/', '/'),
        ('/out_file/', '/'),
        ('/oned_matrix_save/', '/'),
        ('subject_id_', 'sub-'),
        ('session_id_', 'ses-'),
    ]
    # Put result into a BIDS-like format
    outputfiles.inputs.regexp_substitutions = [
        (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1'),
        (r'/_addmean[0-9]+/', r'/func/'),
        (r'/_funcbrains[0-9]+/', r'/func/'),
        (r'/_maskfunc[0-9]+/', r'/func/'),
        (r'/_mc[0-9]+/', r'/func/'),
        (r'/_meanfunc[0-9]+/', r'/func/'),
        (r'/_outliers[0-9]+/', r'/func/'),
        (r'_run_id_[0-9][0-9]', r''),
    ]
    outputnode = pe.Node(interface=util.IdentityInterface(fields=[
        'motion_parameters',
        'motion_corrected',
        'motion_plots',
        'motion_outlier_files',
        'mask',
        'smoothed_files',
        'highpassed_files',
        'mean',
        'func_unwarp',
        'ref_func',
        'ref_funcmask',
        'ref_t1',
        'ref_t1mask',
    ]),
                         name='outputspec')

    # ===================================================================
    #                  _____ _            _ _
    #                 |  __ (_)          | (_)
    #                 | |__) | _ __   ___| |_ _ __   ___
    #                 |  ___/ | '_ \ / _ \ | | '_ \ / _ \
    #                 | |   | | |_) |  __/ | | | | |  __/
    #                 |_|   |_| .__/ \___|_|_|_| |_|\___|
    #                         | |
    #                         |_|
    # ===================================================================

    #  ~|~ _ _  _  _ |` _  _ _ _    _ _  _  _|  _
    #   | | (_|| |_\~|~(_)| | | |  | | |(_|_\|<_\
    #
    # Transform manual skull-stripped masks to multiple images
    # --------------------------------------------------------
    # should just be used as input to motion correction,
    # after mc, all functionals should be aligned to reference
    transmanmask_mc = transform_manualmask.create_workflow()

    # - - - - - - Connections - - - - - - -
    featpreproc.connect([(inputfiles, transmanmask_mc, [
        ('subject_id', 'in.subject_id'),
        ('session_id', 'in.session_id'),
    ])])

    featpreproc.connect(inputfiles, 'ref_funcmask', transmanmask_mc,
                        'in.manualmask')
    featpreproc.connect(inputnode, 'funcs', transmanmask_mc, 'in.funcs')
    featpreproc.connect(inputfiles, 'ref_func', transmanmask_mc,
                        'in.manualmask_func_ref')

    # fieldmaps not being used
    if False:
        trans_fmapmask = transmanmask_mc.clone('trans_fmapmask')
        featpreproc.connect(inputfiles, 'ref_manual_fmapmask', trans_fmapmask,
                            'in.manualmask')
        featpreproc.connect(inputfiles, 'fmap_magnitude', trans_fmapmask,
                            'in.funcs')
        featpreproc.connect(inputfiles, 'ref_func', trans_fmapmask,
                            'in.manualmask_func_ref')

    #  |\/| _ _|_. _  _    _ _  _ _ _  __|_. _  _
    #  |  |(_) | |(_)| |  (_(_)| | (/_(_ | |(_)| |
    #
    # Perform motion correction, using some pipeline
    # --------------------------------------------------------
    # mc = motioncorrection_workflow.create_workflow_afni()

    # Register an image from the functionals to the reference image
    median_func = pe.MapNode(
        interface=fsl.maths.MedianImage(dimension="T"),
        name='median_func',
        iterfield=('in_file'),
    )
    pre_mc = motioncorrection_workflow.create_workflow_allin_slices(
        name='premotioncorrection')

    featpreproc.connect([
        (inputnode, median_func, [
            ('funcs', 'in_file'),
        ]),
        (median_func, pre_mc, [
            ('out_file', 'in.funcs'),
        ]),
        (
            inputfiles,
            pre_mc,
            [
                # median func image will be used a reference / base
                ('ref_func', 'in.ref_func'),
                ('ref_funcmask', 'in.ref_func_weights'),
            ]),
        (
            transmanmask_mc,
            pre_mc,
            [
                ('funcreg.out_file', 'in.funcs_masks'),  # use mask as weights
            ]),
        (pre_mc, outputnode, [
            ('mc.out_file', 'pre_motion_corrected'),
            ('mc.oned_file', 'pre_motion_parameters.oned_file'),
            ('mc.oned_matrix_save', 'pre_motion_parameters.oned_matrix_save'),
        ]),
        (
            outputnode,
            outputfiles,
            [
                ('pre_motion_corrected', 'pre_motion_corrected.out_file'),
                ('pre_motion_parameters.oned_file',
                 'pre_motion_corrected.oned_file'
                 ),  # warp parameters in ASCII (.1D)
                ('pre_motion_parameters.oned_matrix_save',
                 'pre_motion_corrected.oned_matrix_save'
                 ),  # transformation matrices for each sub-brick
            ]),
    ])

    mc = motioncorrection_workflow.create_workflow_allin_slices(
        name='motioncorrection',
        iterfield=('in_file', 'ref_file', 'in_weight_file'))
    # - - - - - - Connections - - - - - - -
    featpreproc.connect([
        (inputnode, mc, [
            ('funcs', 'in.funcs'),
        ]),
        (
            pre_mc,
            mc,
            [
                # the median image realigned to the reference functional will serve as reference
                #  this way motion correction is done to an image more similar to the functionals
                ('mc.out_file', 'in.ref_func'),
            ]),
        (
            inputfiles,
            mc,
            [
                # Check and make sure the ref func mask is close enough to the registered median
                # image.
                ('ref_funcmask', 'in.ref_func_weights'),
            ]),
        (
            transmanmask_mc,
            mc,
            [
                ('funcreg.out_file', 'in.funcs_masks'),  # use mask as weights
            ]),
        (mc, outputnode, [
            ('mc.out_file', 'motion_corrected'),
            ('mc.oned_file', 'motion_parameters.oned_file'),
            ('mc.oned_matrix_save', 'motion_parameters.oned_matrix_save'),
        ]),
        (
            outputnode,
            outputfiles,
            [
                ('motion_corrected', 'motion_corrected.out_file'),
                ('motion_parameters.oned_file', 'motion_corrected.oned_file'
                 ),  # warp parameters in ASCII (.1D)
                ('motion_parameters.oned_matrix_save',
                 'motion_corrected.oned_matrix_save'
                 ),  # transformation matrices for each sub-brick
            ]),
    ])

    #  |~. _ | _| _ _  _  _    _ _  _ _ _  __|_. _  _
    #  |~|(/_|(_|| | |(_||_)  (_(_)| | (/_(_ | |(_)| |
    #                    |
    # Unwarp EPI distortions
    # --------------------------------------------------------

    # Performing motion correction to a reference that is undistorted,
    # so b0_unwarp is currently not needed
    if False:
        b0_unwarp = undistort_workflow.create_workflow()

        featpreproc.connect([
            (
                inputfiles,
                b0_unwarp,
                [  # ('subject_id', 'in.subject_id'),
                    # ('session_id', 'in.session_id'),
                    ('fmap_phasediff', 'in.fmap_phasediff'),
                    ('fmap_magnitude', 'in.fmap_magnitude'),
                ]),
            (mc, b0_unwarp, [
                ('mc.out_file', 'in.funcs'),
            ]),
            (transmanmask_mc, b0_unwarp, [
                ('funcreg.out_file', 'in.funcmasks'),
            ]),
            (trans_fmapmask, b0_unwarp, [('funcreg.out_file', 'in.fmap_mask')
                                         ]),
            (b0_unwarp, outputfiles, [
                ('out.funcs', 'func_unwarp.funcs'),
                ('out.funcmasks', 'func_unwarp.funcmasks'),
            ]),
            (b0_unwarp, outputnode, [
                ('out.funcs', 'func_unwarp.funcs'),
                ('out.funcmasks', 'mask'),
            ]),
        ])

    # undistort the reference images
    if False:
        b0_unwarp_ref = b0_unwarp.clone('b0_unwarp_ref')
        featpreproc.connect([
            (
                inputfiles,
                b0_unwarp_ref,
                [  # ('subject_id', 'in.subject_id'),
                    # ('session_id', 'in.session_id'),
                    ('ref_fmap_phasediff', 'in.fmap_phasediff'),
                    ('ref_fmap_magnitude', 'in.fmap_magnitude'),
                    ('ref_manual_fmapmask', 'in.fmap_mask'),
                    ('ref_func', 'in.funcs'),
                    ('ref_funcmask', 'in.funcmasks'),
                ]),
            (b0_unwarp_ref, outputfiles, [
                ('out.funcs', 'func_unwarp_ref.func'),
                ('out.funcmasks', 'func_unwarp_ref.funcmask'),
            ]),
            (b0_unwarp_ref, outputnode, [
                ('out.funcs', 'ref_func'),
                ('out.funcmasks', 'ref_mask'),
            ]),
        ])
    else:
        featpreproc.connect([
            (inputfiles, outputfiles, [
                ('ref_func', 'reference/func'),
                ('ref_funcmask', 'reference/func_mask'),
            ]),
            (inputfiles, outputnode, [
                ('ref_func', 'ref_func'),
                ('ref_funcmask', 'ref_funcmask'),
            ]),
        ])

    # |~) _  _ . __|_ _  _  _|_ _   |~) _  |` _  _ _  _  _ _
    # |~\(/_(_||_\ | (/_|    | (_)  |~\(/_~|~(/_| (/_| |(_(/_
    #        _|
    # Register all functionals to common reference
    # --------------------------------------------------------
    if False:  # this is now done during motion correction
        # FLIRT cost: intermodal: corratio, intramodal: least squares and normcorr
        reg_to_ref = pe.MapNode(  # intra-modal
            # some runs need to be scaled along the anterior-posterior direction
            interface=fsl.FLIRT(dof=12, cost='normcorr'),
            name='reg_to_ref',
            iterfield=('in_file', 'in_weight'),
        )
        refEPI_to_refT1 = pe.Node(
            # some runs need to be scaled along the anterior-posterior direction
            interface=fsl.FLIRT(dof=12, cost='corratio'),
            name='refEPI_to_refT1',
        )
        # combine func -> ref_func and ref_func -> ref_T1
        reg_to_refT1 = pe.MapNode(
            interface=fsl.ConvertXFM(concat_xfm=True),
            name='reg_to_refT1',
            iterfield=('in_file'),
        )

        reg_funcs = pe.MapNode(
            interface=fsl.preprocess.ApplyXFM(),
            name='reg_funcs',
            iterfield=('in_file', 'in_matrix_file'),
        )
        reg_funcmasks = pe.MapNode(interface=fsl.preprocess.ApplyXFM(),
                                   name='reg_funcmasks',
                                   iterfield=('in_file', 'in_matrix_file'))

        def deref_list(x):
            assert len(x) == 1
            return x[0]

        featpreproc.connect([
            (
                b0_unwarp,
                reg_to_ref,  # --> reg_to_ref, (A)
                [
                    ('out.funcs', 'in_file'),
                    ('out.funcmasks', 'in_weight'),
                ]),
            (b0_unwarp_ref, reg_to_ref, [
                (('out.funcs', deref_list), 'reference'),
                (('out.funcmasks', deref_list), 'ref_weight'),
            ]),
            (
                b0_unwarp_ref,
                refEPI_to_refT1,  # --> refEPI_to_refT1 (B)
                [
                    (('out.funcs', deref_list), 'in_file'),
                    (('out.funcmasks', deref_list), 'in_weight'),
                ]),
            (inputfiles, refEPI_to_refT1, [
                ('ref_t1', 'reference'),
                ('ref_t1mask', 'ref_weight'),
            ]),
            (
                reg_to_ref,
                reg_to_refT1,  # --> reg_to_refT1 (A*B)
                [
                    ('out_matrix_file', 'in_file'),
                ]),
            (refEPI_to_refT1, reg_to_refT1, [
                ('out_matrix_file', 'in_file2'),
            ]),
            (
                reg_to_refT1,
                reg_funcs,  # --> reg_funcs
                [
                    # ('out_matrix_file', 'in_matrix_file'),
                    ('out_file', 'in_matrix_file'),
                ]),
            (b0_unwarp, reg_funcs, [
                ('out.funcs', 'in_file'),
            ]),
            (b0_unwarp_ref, reg_funcs, [
                (('out.funcs', deref_list), 'reference'),
            ]),
            (
                reg_to_refT1,
                reg_funcmasks,  # --> reg_funcmasks
                [
                    # ('out_matrix_file', 'in_matrix_file'),
                    ('out_file', 'in_matrix_file'),
                ]),
            (b0_unwarp, reg_funcmasks, [
                ('out.funcmasks', 'in_file'),
            ]),
            (b0_unwarp_ref, reg_funcmasks, [
                (('out.funcs', deref_list), 'reference'),
            ]),
            (reg_funcs, outputfiles, [
                ('out_file', 'common_ref.func'),
            ]),
            (reg_funcmasks, outputfiles, [
                ('out_file', 'common_ref.funcmask'),
            ]),
        ])

    #  |\/| _ _|_. _  _    _   _|_|. _  _ _
    #  |  |(_) | |(_)| |  (_)|_|| ||(/_| _\
    #
    # --------------------------------------------------------

    # Apply brain masks to functionals
    # --------------------------------------------------------

    # Dilate mask
    """
    Dilate the mask
    """
    if False:
        dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil',
                                                         op_string='-dilF'),
                                iterfield=['in_file'],
                                name='dilatemask')
        featpreproc.connect(reg_funcmasks, 'out_file', dilatemask, 'in_file')
    else:
        dilatemask = pe.Node(interface=fsl.ImageMaths(suffix='_dil',
                                                      op_string='-dilF'),
                             name='dilatemask')
        featpreproc.connect(inputfiles, 'ref_funcmask', dilatemask, 'in_file')

    featpreproc.connect(dilatemask, 'out_file', outputfiles, 'dilate_mask')

    funcbrains = pe.MapNode(fsl.BinaryMaths(operation='mul'),
                            iterfield=('in_file', 'operand_file'),
                            name='funcbrains')

    featpreproc.connect([
        (mc, funcbrains, [
            ('mc.out_file', 'in_file'),
        ]),
        (dilatemask, funcbrains, [
            ('out_file', 'operand_file'),
        ]),
        (funcbrains, outputfiles, [
            ('out_file', 'funcbrains'),
        ]),
    ])
    # Detect motion outliers
    # --------------------------------------------------------

    import nipype.algorithms.rapidart as ra
    outliers = pe.MapNode(
        ra.ArtifactDetect(
            mask_type='file',
            # trying to "disable" `norm_threshold`:
            use_norm=True,
            norm_threshold=10.0,  # combines translations in mm and rotations
            # use_norm=Undefined,
            # translation_threshold=1.0,  # translation in mm
            # rotation_threshold=0.02,  # rotation in radians
            zintensity_threshold=3.0,  # z-score
            parameter_source='AFNI',
            save_plot=True),
        iterfield=('realigned_files', 'realignment_parameters', 'mask_file'),
        name='outliers')

    featpreproc.connect([
        (
            mc,
            outliers,
            [  # ('mc.par_file', 'realignment_parameters'),
                ('mc.oned_file', 'realignment_parameters'),
            ]),
        (funcbrains, outliers, [
            ('out_file', 'realigned_files'),
        ]),
        (dilatemask, outliers, [
            ('out_file', 'mask_file'),
        ]),
        (
            outliers,
            outputfiles,
            [
                ('outlier_files', 'motion_outliers.@outlier_files'),
                ('plot_files', 'motion_outliers.@plot_files'),
                ('displacement_files', 'motion_outliers.@displacement_files'),
                ('intensity_files', 'motion_outliers.@intensity_files'),
                ('mask_files', 'motion_outliers.@mask_files'),
                ('statistic_files', 'motion_outliers.@statistic_files'),
                # ('norm_files', 'outliers.@norm_files'),
            ]),
        (mc, outputnode, [
            ('mc.oned_file', 'motion_parameters'),
        ]),
        (
            outliers,
            outputnode,
            [
                ('outlier_files', 'motion_outlier_files'),
                ('plot_files', 'motion_plots.@plot_files'),
                ('displacement_files', 'motion_outliers.@displacement_files'),
                ('intensity_files', 'motion_outliers.@intensity_files'),
                ('mask_files', 'motion_outliers.@mask_files'),
                ('statistic_files', 'motion_outliers.@statistic_files'),
                # ('norm_files', 'outliers.@norm_files'),
            ])
    ])
    """
    Determine the 2nd and 98th percentile intensities of each functional run
    """
    getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'),
                           iterfield=['in_file'],
                           name='getthreshold')
    if False:
        featpreproc.connect(b0_unwarp, 'out.funcs', getthresh, 'in_file')
    else:
        featpreproc.connect(mc, 'mc.out_file', getthresh, 'in_file')
    """
    Threshold the first run of functional data at 10% of the 98th percentile
    """

    threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char',
                                                    suffix='_thresh'),
                           iterfield=['in_file', 'op_string'],
                           name='threshold')
    if False:
        featpreproc.connect(b0_unwarp, 'out.funcs', threshold, 'in_file')
    else:
        featpreproc.connect(mc, 'mc.out_file', threshold, 'in_file')
    """
    Define a function to get 10% of the intensity
    """
    def getthreshop(thresh):
        return ['-thr %.10f -Tmin -bin' % (0.1 * val[1]) for val in thresh]

    featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold,
                        'op_string')
    """
    Determine the median value of the functional runs using the mask
    """
    medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                           iterfield=['in_file', 'mask_file'],
                           name='medianval')
    if False:
        featpreproc.connect(b0_unwarp, 'out.funcs', medianval, 'in_file')
    else:
        featpreproc.connect(mc, 'mc.out_file', medianval, 'in_file')

    featpreproc.connect(threshold, 'out_file', medianval, 'mask_file')

    # (~ _  _ _|_. _ |  (~ _ _  _  _ _|_|_ . _  _
    # _)|_)(_| | |(_||  _)| | |(_)(_) | | ||| |(_|
    #   |                                       _|
    # Spatial smoothing (SUSAN)
    # --------------------------------------------------------

    # create_susan_smooth takes care of calculating the mean and median
    #   functional, applying mask to functional, and running the smoothing
    smooth = create_susan_smooth(separate_masks=False)
    featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm')

    # featpreproc.connect(b0_unwarp, 'out.funcs', smooth, 'inputnode.in_files')
    if False:
        featpreproc.connect(reg_funcs, 'out_file', smooth,
                            'inputnode.in_files')
    else:
        featpreproc.connect(mc, 'mc.out_file', smooth, 'inputnode.in_files')

    featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file')

    # -------------------------------------------------------
    # The below is from workflows/fmri/fsl/preprocess.py
    """
    Mask the smoothed data with the dilated mask
    """

    maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                           iterfield=['in_file', 'in_file2'],
                           name='maskfunc3')
    featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3,
                        'in_file')

    featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2')

    concatnode = pe.Node(interface=util.Merge(2), name='concat')

    tolist = lambda x: [x]

    def chooseindex(fwhm):
        if fwhm < 1:
            return [0]
        else:
            return [1]

    # maskfunc2 is the functional data before SUSAN
    if False:
        featpreproc.connect(b0_unwarp, ('out.funcs', tolist), concatnode,
                            'in1')
    else:
        featpreproc.connect(mc, ('mc.out_file', tolist), concatnode, 'in1')
    # maskfunc3 is the functional data after SUSAN
    featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2')
    """
    The following nodes select smooth or unsmoothed data depending on the
    fwhm. This is because SUSAN defaults to smoothing the data with about the
    voxel size of the input data if the fwhm parameter is less than 1/3 of the
    voxel size.
    """
    selectnode = pe.Node(interface=util.Select(), name='select')

    featpreproc.connect(concatnode, 'out', selectnode, 'inlist')

    featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index')
    featpreproc.connect(selectnode, 'out', outputfiles, 'smoothed_files')
    """
    Scale the median value of the run is set to 10000.
    """

    meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'),
                           iterfield=['in_file', 'op_string'],
                           name='meanscale')
    featpreproc.connect(selectnode, 'out', meanscale, 'in_file')
    """
    Define a function to get the scaling factor for intensity normalization
    """

    featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale,
                        'op_string')

    # |_|. _ |_  _  _  _ _
    # | ||(_|| ||_)(_|_\_\
    #      _|   |
    # Temporal filtering
    # --------------------------------------------------------

    highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'),
                          iterfield=['in_file'],
                          name='highpass')
    highpass_operand = lambda x: '-bptf %.10f -1' % x
    featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass,
                        'op_string')
    featpreproc.connect(meanscale, 'out_file', highpass, 'in_file')

    version = 0
    if fsl.Info.version() and \
            LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'):
        version = 507

    if version < 507:
        featpreproc.connect(highpass, 'out_file', outputnode,
                            'highpassed_files')
    else:
        """
        Add back the mean removed by the highpass filter operation as
            of FSL 5.0.7
        """
        meanfunc4 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                        suffix='_mean'),
                               iterfield=['in_file'],
                               name='meanfunc4')

        featpreproc.connect(meanscale, 'out_file', meanfunc4, 'in_file')
        addmean = pe.MapNode(interface=fsl.BinaryMaths(operation='add'),
                             iterfield=['in_file', 'operand_file'],
                             name='addmean')
        featpreproc.connect(highpass, 'out_file', addmean, 'in_file')
        featpreproc.connect(meanfunc4, 'out_file', addmean, 'operand_file')
        featpreproc.connect(addmean, 'out_file', outputnode,
                            'highpassed_files')
    """
    Generate a mean functional image from the first run
    """
    meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                    suffix='_mean'),
                           iterfield=['in_file'],
                           name='meanfunc3')

    featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file')
    featpreproc.connect(meanfunc3, 'out_file', outputfiles, 'mean')

    featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean_highpassed')
    featpreproc.connect(outputnode, 'highpassed_files', outputfiles,
                        'highpassed_files')

    return (featpreproc)
Ejemplo n.º 5
0
def init_fsl_bbr_wf(use_bbr,
                    asl2t1w_dof,
                    asl2t1w_init,
                    sloppy=False,
                    name='fsl_bbr_wf'):
    """
    Build a workflow to run FSL's ``flirt``.

    This workflow uses FSL FLIRT to register a ASL image to a T1-weighted
    structural image, using a boundary-based registration (BBR) cost function.
    It is a counterpart to :py:func:`~aslprep.workflows.asl.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 aslprep.workflows.asl.registration import init_fsl_bbr_wf
            wf = init_fsl_bbr_wf(use_bbr=True, asl2t1w_dof=9, asl2t1w_init='register')


    Parameters
    ----------
    use_bbr : :obj:`bool` or None
        Enable/disable boundary-based registration refinement.
        If ``None``, test BBR result for distortion before accepting.
    asl2t1w_dof : 6, 9 or 12
        Degrees-of-freedom for ASL-T1w registration
    asl2t1w_init : str, 'header' or 'register'
        If ``'header'``, use header information for initialization of ASL and T1 images.
        If ``'register'``, align volumes by their centers.
    name : :obj:`str`, optional
        Workflow name (default: fsl_bbr_wf)

    Inputs
    ------
    in_file
        Reference ASL 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:`~aslprep.workflows.asl.registration.init_bbreg_wf`)
    subjects_dir
        Unused (see :py:func:`~aslprep.workflows.asl.registration.init_bbreg_wf`)
    subject_id
        Unused (see :py:func:`~aslprep.workflows.asl.registration.init_bbreg_wf`)

    Outputs
    -------
    itk_asl_to_t1
        Affine transform from ``ref_asl_brain`` to T1w space (ITK format)
    itk_t1_to_asl
        Affine transform from T1 space to ASL 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 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',
            'fsnative2t1w_xfm',
            'subjects_dir',
            'subject_id',  # BBRegister
            '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')]),
    ])

    # 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

    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,
                                              output_names="out"),
                                 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
Ejemplo n.º 6
0
"""Use :class:`nipype.interfaces.spm.EstimateModel` to determine the
parameters of the model.
"""

level1estimate = pe.Node(spm.EstimateModel(), name="level1estimate")
level1estimate.inputs.estimation_method = {'Classical': 1}
"""Use :class:`nipype.interfaces.spm.EstimateContrast` to estimate the
first level contrasts specified in a few steps above.
"""

contrastestimate = pe.Node(spm.EstimateContrast(), name="contrastestimate")
"""Use :class: `nipype.interfaces.utility.Select` to select each contrast for
reporting.
"""

selectcontrast = pe.Node(niu.Select(), name="selectcontrast")
"""Use :class:`nipype.interfaces.fsl.Overlay` to combine the statistical output of
the contrast estimate and a background image into one volume.
"""

overlaystats = pe.Node(fsl.Overlay(), name="overlaystats")
overlaystats.inputs.stat_thresh = (3, 10)
overlaystats.inputs.show_negative_stats = True
overlaystats.inputs.auto_thresh_bg = True
"""Use :class:`nipype.interfaces.fsl.Slicer` to create images of the overlaid
statistical volumes for a report of the first-level results.
"""

slicestats = pe.Node(fsl.Slicer(), name="slicestats")
slicestats.inputs.all_axial = True
slicestats.inputs.image_width = 750
preprocessing.connect(iter_fwhm, "fwhm", isotropic_surface_smooth,
                      "surface_fwhm")
preprocessing.connect(iter_fwhm, "fwhm", isotropic_surface_smooth, "vol_fwhm")
preprocessing.connect(recon_all, 'subjects_dir', isotropic_surface_smooth,
                      'subjects_dir')

merge_smoothed_files = pe.Node(interface=util.Merge(3),
                               name='merge_smoothed_files')
preprocessing.connect(isotropic_voxel_smooth, 'smoothed_files',
                      merge_smoothed_files, 'in1')
preprocessing.connect(anisotropic_voxel_smooth, 'outputnode.smoothed_files',
                      merge_smoothed_files, 'in2')
preprocessing.connect(isotropic_surface_smooth, 'smoothed_file',
                      merge_smoothed_files, 'in3')

select_smoothed_files = pe.Node(interface=util.Select(),
                                name="select_smoothed_files")
preprocessing.connect(merge_smoothed_files, 'out', select_smoothed_files,
                      'inlist')


def chooseindex(roi):
    return {
        'isotropic_voxel': range(0, 4),
        'anisotropic_voxel': range(4, 8),
        'isotropic_surface': range(8, 12)
    }[roi]


preprocessing.connect(iter_smoothing_method, ("smoothing_method", chooseindex),
                      select_smoothed_files, 'index')
def epi_pipeline(name="susceptibility_distortion_correction_using_t1"):
    """Perform EPI correction.

    This workflow allows to correct for echo-planar induced 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 parcellation.

    Warnings:
        This workflow rotates the b-vectors.

    Notes:
        Nir et al. (2015): Connectivity network measures predict volumetric atrophy in mild cognitive impairment
        Leow et al. (2007): Statistical Properties of Jacobian Maps and the Realization of
        Unbiased Large Deformation Nonlinear Image Registration
    """
    import nipype.interfaces.ants as ants
    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 .dwi_preprocessing_using_t1_utils import (
        ants_combine_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",
    )

    ants_registration = pe.Node(
        interface=ants.registration.RegistrationSynQuick(transform_type="br",
                                                         dimension=3),
        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_combine_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")
    # fmt: off
    wf.connect([
        (inputnode, split, [("DWI", "in_file")]),
        (split, pick_ref, [("out_files", "inlist")]),
        (pick_ref, flirt_b0_2_t1, [("out", "in_file")]),
        (inputnode, flirt_b0_2_t1, [("T1", "reference")]),
        (inputnode, rot_bvec, [("bvec", "in_bvec")]),
        (flirt_b0_2_t1, expend_matrix, [("out_matrix_file", "in_matrix")]),
        (inputnode, expend_matrix, [("bvec", "in_bvec")]),
        (expend_matrix, rot_bvec, [("out_matrix_list", "in_matrix")]),
        (inputnode, ants_registration, [("T1", "fixed_image")]),
        (flirt_b0_2_t1, ants_registration, [("out_file", "moving_image")]),
        (inputnode, c3d_flirt2ants, [("T1", "reference_file")]),
        (pick_ref, c3d_flirt2ants, [("out", "source_file")]),
        (flirt_b0_2_t1, c3d_flirt2ants, [("out_matrix_file", "transform_file")
                                         ]),
        (c3d_flirt2ants, change_transform, [("itk_transform",
                                             "input_affine_file")]),
        (ants_registration, merge_transform, [("forward_warp_field", "in1")]),
        (ants_registration, merge_transform, [("out_matrix", "in2")]),
        (change_transform, merge_transform, [("updated_affine_file", "in3")]),
        (inputnode, apply_transform, [("T1", "fix_image")]),
        (split, apply_transform, [("out_files", "moving_image")]),
        (merge_transform, apply_transform, [("out", "ants_warp_affine")]),
        (apply_transform, jacobian, [("out_warp_field", "deformationField")]),
        (apply_transform, jacmult, [("out_warped", "operand_files")]),
        (jacobian, jacmult, [("outputImage", "in_file")]),
        (jacmult, thres, [("out_file", "in_file")]),
        (thres, merge, [("out_file", "in_files")]),
        (merge, outputnode, [("merged_file", "DWIs_epicorrected")]),
        (flirt_b0_2_t1, outputnode, [("out_matrix_file",
                                      "DWI_2_T1_Coregistration_matrix")]),
        (ants_registration, outputnode,
         [("forward_warp_field", "epi_correction_deformation_field"),
          ("out_matrix", "epi_correction_affine_transform"),
          ("warped_image", "epi_correction_image_warped")]),
        (merge_transform, outputnode, [("out", "warp_epi")]),
        (rot_bvec, outputnode, [("out_file", "out_bvec")]),
    ])
    # fmt: on
    return wf
Ejemplo n.º 9
0
# Node 9
node_fsl_subtract = pe.Node(interface=fsl.BinaryMaths(),
                            name='node_fsl_subtract')
#node_fsl_subtract.inputs.in_file = root_dir + nipype_dir + nlin_displacement_field_4d_gz
#node_fsl_subtract.inputs.operand_file = root_dir + nipype_dir + affine_displacement_field_4d_gz
node_fsl_subtract.inputs.operation = 'sub'
node_fsl_subtract.inputs.out_file = root_dir + nipype_dir + nlin_displacement_field_4d_only

# Node 10
node_fsl_split = pe.Node(interface=fsl.Split(), name='node_fsl_split')
node_fsl_split.inputs.dimension = 't'
#node_fsl_split.inputs.in_file = root_dir + nipype_dir + nlin_displacement_field_4d_only
node_fsl_split.inputs.out_base_name = root_dir + nipype_dir + nlin_displacement_field_4d_only_split

# Node 11
node_select1 = pe.Node(interface=util.Select(), name='node_select1')
node_select1.inputs.index = [0]
node_select1.inputs.inlist = [
    root_dir + nipype_dir + nlin_displacement_field_4d_only_split1,
    root_dir + nipype_dir + nlin_displacement_field_4d_only_split2,
    root_dir + nipype_dir + nlin_displacement_field_4d_only_split3
]

# Node 12
node_select2 = pe.Node(interface=util.Select(), name='node_select2')
node_select2.inputs.index = [1]
node_select2.inputs.inlist = [
    root_dir + nipype_dir + nlin_displacement_field_4d_only_split1,
    root_dir + nipype_dir + nlin_displacement_field_4d_only_split2,
    root_dir + nipype_dir + nlin_displacement_field_4d_only_split3
]
Ejemplo n.º 10
0
def init_atropos_wf(
    name="atropos_wf",
    use_random_seed=True,
    omp_nthreads=None,
    mem_gb=3.0,
    padding=10,
    in_segmentation_model=tuple(ATROPOS_MODELS["T1w"].values()),
    bspline_fitting_distance=200,
    wm_prior=False,
):
    """
    Create an ANTs' ATROPOS workflow for brain tissue segmentation.

    Re-interprets supersteps 6 and 7 of ``antsBrainExtraction.sh``,
    which refine the mask previously computed with the spatial
    normalization to the template.
    The workflow also executes steps 8 and 9 of the brain extraction
    workflow.

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

            from niworkflows.anat.ants import init_atropos_wf
            wf = init_atropos_wf()

    Parameters
    ----------
    name : str, optional
        Workflow name (default: "atropos_wf").
    use_random_seed : bool
        Whether ATROPOS should generate a random seed based on the
        system's clock
    omp_nthreads : int
        Maximum number of threads an individual process may use
    mem_gb : float
        Estimated peak memory consumption of the most hungry nodes
        in the workflow
    padding : int
        Pad images with zeros before processing
    in_segmentation_model : tuple
        A k-means segmentation is run to find gray or white matter
        around the edge of the initial brain mask warped from the
        template.
        This produces a segmentation image with :math:`$K$` classes,
        ordered by mean intensity in increasing order.
        With this option, you can control  :math:`$K$` and tell the script which
        classes represent CSF, gray and white matter.
        Format (K, csfLabel, gmLabel, wmLabel).
        Examples:
        ``(3,1,2,3)`` for T1 with K=3, CSF=1, GM=2, WM=3 (default),
        ``(3,3,2,1)`` for T2 with K=3, CSF=3, GM=2, WM=1,
        ``(3,1,3,2)`` for FLAIR with K=3, CSF=1 GM=3, WM=2,
        ``(4,4,2,3)`` uses K=4, CSF=4, GM=2, WM=3.
    bspline_fitting_distance : float
        The size of the b-spline mesh grid elements, in mm (default: 200)
    wm_prior : :obj:`bool`
        Whether the WM posterior obtained with ATROPOS should be regularized with a prior
        map (typically, mapped from the template). When ``wm_prior`` is ``True`` the input
        field ``wm_prior`` of the input node must be connected.

    Inputs
    ------
    in_files : list
        The original anatomical images passed in to the brain-extraction workflow.
    in_corrected : list
        :abbr:`INU (intensity non-uniformity)`-corrected files.
    in_mask : str
        Brain mask calculated previously.
    wm_prior : :obj:`str`
        Path to the WM prior probability map, aligned with the individual data.

    Outputs
    -------
    out_file : :obj:`str`
        Path of the corrected and brain-extracted result, using the ATROPOS refinement.
    bias_corrected : :obj:`str`
        Path of the corrected and result, using the ATROPOS refinement.
    bias_image : :obj:`str`
        Path of the estimated INU bias field, using the ATROPOS refinement.
    out_mask : str
        Refined brain mask
    out_segm : str
        Output segmentation
    out_tpms : str
        Output :abbr:`TPMs (tissue probability maps)`


    """
    wf = pe.Workflow(name)

    out_fields = [
        "bias_corrected", "bias_image", "out_mask", "out_segm", "out_tpms"
    ]

    inputnode = pe.Node(
        niu.IdentityInterface(
            fields=["in_files", "in_corrected", "in_mask", "wm_prior"]),
        name="inputnode",
    )
    outputnode = pe.Node(niu.IdentityInterface(fields=["out_file"] +
                                               out_fields),
                         name="outputnode")

    copy_xform = pe.Node(CopyXForm(fields=out_fields),
                         name="copy_xform",
                         run_without_submitting=True)

    # Morphological dilation, radius=2
    dil_brainmask = pe.Node(ImageMath(operation="MD",
                                      op2="2",
                                      copy_header=True),
                            name="dil_brainmask")
    # Get largest connected component
    get_brainmask = pe.Node(
        ImageMath(operation="GetLargestComponent", copy_header=True),
        name="get_brainmask",
    )

    # Run atropos (core node)
    atropos = pe.Node(
        Atropos(
            convergence_threshold=0.0,
            dimension=3,
            initialization="KMeans",
            likelihood_model="Gaussian",
            mrf_radius=[1, 1, 1],
            mrf_smoothing_factor=0.1,
            n_iterations=3,
            number_of_tissue_classes=in_segmentation_model[0],
            save_posteriors=True,
            use_random_seed=use_random_seed,
        ),
        name="01_atropos",
        n_procs=omp_nthreads,
        mem_gb=mem_gb,
    )

    # massage outputs
    pad_segm = pe.Node(
        ImageMath(operation="PadImage", op2=f"{padding}", copy_header=False),
        name="02_pad_segm",
    )
    pad_mask = pe.Node(
        ImageMath(operation="PadImage", op2=f"{padding}", copy_header=False),
        name="03_pad_mask",
    )

    # Split segmentation in binary masks
    sel_labels = pe.Node(
        niu.Function(function=_select_labels,
                     output_names=["out_wm", "out_gm", "out_csf"]),
        name="04_sel_labels",
    )
    sel_labels.inputs.labels = list(reversed(in_segmentation_model[1:]))

    # Select largest components (GM, WM)
    # ImageMath ${DIMENSION} ${EXTRACTION_WM} GetLargestComponent ${EXTRACTION_WM}
    get_wm = pe.Node(ImageMath(operation="GetLargestComponent"),
                     name="05_get_wm")
    get_gm = pe.Node(ImageMath(operation="GetLargestComponent"),
                     name="06_get_gm")

    # Fill holes and calculate intersection
    # ImageMath ${DIMENSION} ${EXTRACTION_TMP} FillHoles ${EXTRACTION_GM} 2
    # MultiplyImages ${DIMENSION} ${EXTRACTION_GM} ${EXTRACTION_TMP} ${EXTRACTION_GM}
    fill_gm = pe.Node(ImageMath(operation="FillHoles", op2="2"),
                      name="07_fill_gm")
    mult_gm = pe.Node(
        MultiplyImages(dimension=3, output_product_image="08_mult_gm.nii.gz"),
        name="08_mult_gm",
    )

    # MultiplyImages ${DIMENSION} ${EXTRACTION_WM} ${ATROPOS_WM_CLASS_LABEL} ${EXTRACTION_WM}
    # ImageMath ${DIMENSION} ${EXTRACTION_TMP} ME ${EXTRACTION_CSF} 10
    relabel_wm = pe.Node(
        MultiplyImages(
            dimension=3,
            second_input=in_segmentation_model[-1],
            output_product_image="09_relabel_wm.nii.gz",
        ),
        name="09_relabel_wm",
    )
    me_csf = pe.Node(ImageMath(operation="ME", op2="10"), name="10_me_csf")

    # ImageMath ${DIMENSION} ${EXTRACTION_GM} addtozero ${EXTRACTION_GM} ${EXTRACTION_TMP}
    # MultiplyImages ${DIMENSION} ${EXTRACTION_GM} ${ATROPOS_GM_CLASS_LABEL} ${EXTRACTION_GM}
    # ImageMath ${DIMENSION} ${EXTRACTION_SEGMENTATION} addtozero ${EXTRACTION_WM} ${EXTRACTION_GM}
    add_gm = pe.Node(ImageMath(operation="addtozero"), name="11_add_gm")
    relabel_gm = pe.Node(
        MultiplyImages(
            dimension=3,
            second_input=in_segmentation_model[-2],
            output_product_image="12_relabel_gm.nii.gz",
        ),
        name="12_relabel_gm",
    )
    add_gm_wm = pe.Node(ImageMath(operation="addtozero"), name="13_add_gm_wm")

    # Superstep 7
    # Split segmentation in binary masks
    sel_labels2 = pe.Node(
        niu.Function(function=_select_labels,
                     output_names=["out_gm", "out_wm"]),
        name="14_sel_labels2",
    )
    sel_labels2.inputs.labels = in_segmentation_model[2:]

    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} addtozero ${EXTRACTION_MASK} ${EXTRACTION_TMP}
    add_7 = pe.Node(ImageMath(operation="addtozero"), name="15_add_7")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} ME ${EXTRACTION_MASK} 2
    me_7 = pe.Node(ImageMath(operation="ME", op2="2"), name="16_me_7")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} GetLargestComponent ${EXTRACTION_MASK}
    comp_7 = pe.Node(ImageMath(operation="GetLargestComponent"),
                     name="17_comp_7")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK} 4
    md_7 = pe.Node(ImageMath(operation="MD", op2="4"), name="18_md_7")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} FillHoles ${EXTRACTION_MASK} 2
    fill_7 = pe.Node(ImageMath(operation="FillHoles", op2="2"),
                     name="19_fill_7")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} addtozero ${EXTRACTION_MASK} \
    # ${EXTRACTION_MASK_PRIOR_WARPED}
    add_7_2 = pe.Node(ImageMath(operation="addtozero"), name="20_add_7_2")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} MD ${EXTRACTION_MASK} 5
    md_7_2 = pe.Node(ImageMath(operation="MD", op2="5"), name="21_md_7_2")
    # ImageMath ${DIMENSION} ${EXTRACTION_MASK} ME ${EXTRACTION_MASK} 5
    me_7_2 = pe.Node(ImageMath(operation="ME", op2="5"), name="22_me_7_2")

    # De-pad
    depad_mask = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding),
                         name="23_depad_mask")
    depad_segm = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding),
                         name="24_depad_segm")
    depad_gm = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding),
                       name="25_depad_gm")
    depad_wm = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding),
                       name="26_depad_wm")
    depad_csf = pe.Node(ImageMath(operation="PadImage", op2="-%d" % padding),
                        name="27_depad_csf")

    msk_conform = pe.Node(niu.Function(function=_conform_mask),
                          name="msk_conform")
    merge_tpms = pe.Node(niu.Merge(in_segmentation_model[0]),
                         name="merge_tpms")

    sel_wm = pe.Node(niu.Select(), name="sel_wm", run_without_submitting=True)
    if not wm_prior:
        sel_wm.inputs.index = in_segmentation_model[-1] - 1

    copy_xform_wm = pe.Node(CopyXForm(fields=["wm_map"]),
                            name="copy_xform_wm",
                            run_without_submitting=True)

    # Refine INU correction
    inu_n4_final = pe.MapNode(
        N4BiasFieldCorrection(
            dimension=3,
            save_bias=True,
            copy_header=True,
            n_iterations=[50] * 5,
            convergence_threshold=1e-7,
            shrink_factor=4,
            bspline_fitting_distance=bspline_fitting_distance,
        ),
        n_procs=omp_nthreads,
        name="inu_n4_final",
        iterfield=["input_image"],
    )

    try:
        inu_n4_final.inputs.rescale_intensities = True
    except ValueError:
        warn(
            "N4BiasFieldCorrection's --rescale-intensities option was added in ANTS 2.1.0 "
            f"({inu_n4_final.interface.version} found.) Please consider upgrading.",
            UserWarning,
        )

    # Apply mask
    apply_mask = pe.MapNode(ApplyMask(),
                            iterfield=["in_file"],
                            name="apply_mask")

    # fmt: off
    wf.connect([
        (inputnode, dil_brainmask, [("in_mask", "op1")]),
        (inputnode, copy_xform, [(("in_files", _pop), "hdr_file")]),
        (inputnode, copy_xform_wm, [(("in_files", _pop), "hdr_file")]),
        (inputnode, pad_mask, [("in_mask", "op1")]),
        (inputnode, atropos, [("in_corrected", "intensity_images")]),
        (inputnode, inu_n4_final, [("in_files", "input_image")]),
        (inputnode, msk_conform, [(("in_files", _pop), "in_reference")]),
        (dil_brainmask, get_brainmask, [("output_image", "op1")]),
        (get_brainmask, atropos, [("output_image", "mask_image")]),
        (atropos, pad_segm, [("classified_image", "op1")]),
        (pad_segm, sel_labels, [("output_image", "in_segm")]),
        (sel_labels, get_wm, [("out_wm", "op1")]),
        (sel_labels, get_gm, [("out_gm", "op1")]),
        (get_gm, fill_gm, [("output_image", "op1")]),
        (get_gm, mult_gm, [("output_image", "first_input")]),
        (fill_gm, mult_gm, [("output_image", "second_input")]),
        (get_wm, relabel_wm, [("output_image", "first_input")]),
        (sel_labels, me_csf, [("out_csf", "op1")]),
        (mult_gm, add_gm, [("output_product_image", "op1")]),
        (me_csf, add_gm, [("output_image", "op2")]),
        (add_gm, relabel_gm, [("output_image", "first_input")]),
        (relabel_wm, add_gm_wm, [("output_product_image", "op1")]),
        (relabel_gm, add_gm_wm, [("output_product_image", "op2")]),
        (add_gm_wm, sel_labels2, [("output_image", "in_segm")]),
        (sel_labels2, add_7, [("out_wm", "op1"), ("out_gm", "op2")]),
        (add_7, me_7, [("output_image", "op1")]),
        (me_7, comp_7, [("output_image", "op1")]),
        (comp_7, md_7, [("output_image", "op1")]),
        (md_7, fill_7, [("output_image", "op1")]),
        (fill_7, add_7_2, [("output_image", "op1")]),
        (pad_mask, add_7_2, [("output_image", "op2")]),
        (add_7_2, md_7_2, [("output_image", "op1")]),
        (md_7_2, me_7_2, [("output_image", "op1")]),
        (me_7_2, depad_mask, [("output_image", "op1")]),
        (add_gm_wm, depad_segm, [("output_image", "op1")]),
        (relabel_wm, depad_wm, [("output_product_image", "op1")]),
        (relabel_gm, depad_gm, [("output_product_image", "op1")]),
        (sel_labels, depad_csf, [("out_csf", "op1")]),
        (depad_csf, merge_tpms, [("output_image", "in1")]),
        (depad_gm, merge_tpms, [("output_image", "in2")]),
        (depad_wm, merge_tpms, [("output_image", "in3")]),
        (depad_mask, msk_conform, [("output_image", "in_mask")]),
        (msk_conform, copy_xform, [("out", "out_mask")]),
        (depad_segm, copy_xform, [("output_image", "out_segm")]),
        (merge_tpms, copy_xform, [("out", "out_tpms")]),
        (atropos, sel_wm, [("posteriors", "inlist")]),
        (sel_wm, copy_xform_wm, [("out", "wm_map")]),
        (copy_xform_wm, inu_n4_final, [("wm_map", "weight_image")]),
        (inu_n4_final, copy_xform, [("output_image", "bias_corrected"),
                                    ("bias_image", "bias_image")]),
        (copy_xform, apply_mask, [("bias_corrected", "in_file"),
                                  ("out_mask", "in_mask")]),
        (apply_mask, outputnode, [("out_file", "out_file")]),
        (copy_xform, outputnode, [
            ("bias_corrected", "bias_corrected"),
            ("bias_image", "bias_image"),
            ("out_mask", "out_mask"),
            ("out_segm", "out_segm"),
            ("out_tpms", "out_tpms"),
        ]),
    ])
    # fmt: on

    if wm_prior:
        from nipype.algorithms.metrics import FuzzyOverlap

        def _argmax(in_dice):
            import numpy as np

            return np.argmax(in_dice)

        match_wm = pe.Node(
            niu.Function(function=_matchlen),
            name="match_wm",
            run_without_submitting=True,
        )
        overlap = pe.Node(FuzzyOverlap(),
                          name="overlap",
                          run_without_submitting=True)

        apply_wm_prior = pe.Node(niu.Function(function=_improd),
                                 name="apply_wm_prior")

        # fmt: off
        wf.disconnect([
            (copy_xform_wm, inu_n4_final, [("wm_map", "weight_image")]),
        ])
        wf.connect([
            (inputnode, apply_wm_prior, [("in_mask", "in_mask"),
                                         ("wm_prior", "op2")]),
            (inputnode, match_wm, [("wm_prior", "value")]),
            (atropos, match_wm, [("posteriors", "reference")]),
            (atropos, overlap, [("posteriors", "in_ref")]),
            (match_wm, overlap, [("out", "in_tst")]),
            (overlap, sel_wm, [(("class_fdi", _argmax), "index")]),
            (copy_xform_wm, apply_wm_prior, [("wm_map", "op1")]),
            (apply_wm_prior, inu_n4_final, [("out", "weight_image")]),
        ])
        # fmt: on
    return wf
Ejemplo n.º 11
0
def make_workflow():
    default_conf = [
        {
            str('step'): 32,
            str('blur_fwhm'): 16,
            str('iterations'): 20
        },
        {
            str('step'): 16,
            str('blur_fwhm'): 8,
            str('iterations'): 20
        },
        {
            str('step'): 12,
            str('blur_fwhm'): 6,
            str('iterations'): 20
        },
        {
            str('step'): 8,
            str('blur_fwhm'): 4,
            str('iterations'): 20
        },
        {
            str('step'): 6,
            str('blur_fwhm'): 3,
            str('iterations'): 20
        },
        {
            str('step'): 4,
            str('blur_fwhm'): 2,
            str('iterations'): 10
        },
        {
            str('step'): 2,
            str('blur_fwhm'): 1,
            str('iterations'): 10
        },
    ]

    #FAST_EXAMPLE_BASE_DIR='/data/lfs2/model-mie/pyScripts/python3volgenmodel-nipype/volgenmodel-fast-example/'
    FAST_EXAMPLE_BASE_DIR = str('/scratch/volgenmodel-fast-example/')
    # Top level workflow.
    workflow = pe.Workflow(name="workflow")

    # FIXME
    # Just for testing, tell Nipype to keep all outputs.
    # workflow.config['execution'] = {'remove_unnecessary_outputs': 'false'}

    workflow.base_dir = os.path.abspath(FAST_EXAMPLE_BASE_DIR)

    infiles = sorted(
        glob.glob(os.path.join(FAST_EXAMPLE_BASE_DIR, 'mouse*mnc')))

    datasource = pe.Node(interface=nio.DataGrabber(sort_filelist=True),
                         name='datasource_mouse')
    datasource.inputs.base_directory = os.path.abspath(FAST_EXAMPLE_BASE_DIR)
    datasource.inputs.template = 'mouse*.mnc'

    datasink = pe.Node(interface=nio.DataSink(), name="datasink")
    datasink.inputs.base_directory = os.path.abspath(
        os.path.join(FAST_EXAMPLE_BASE_DIR, str('volgenmodel_final_output')))

    opt = {
        'verbose': 0,
        'clobber': 0,
        'fake': 0,
        'check': 1,
        'clean': 0,
        'keep_tmp': 0,
        # 'workdir': os.path.join(os.getcwd(), 'work'), # "./$me-work",
        'batch': 0,
        'symmetric': 0,
        'symmetric_dir': 'x',
        'normalise': 1,
        'model_norm_thresh': 0.1,
        'model_min_step': 0.5,
        'pad': 10,
        'iso': 1,
        'config_file': None,
        'linmethod': 'bestlinreg',
        'init_model': None,
        'output_model': None,
        'output_stdev': None,
        'fit_stages': 'lin,lin,lin,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3',
    }

    # from create-model.sh
    opt['symmetric'] = 1
    opt['symmetric_dir'] = 'x'
    opt['check'] = 1
    opt['normalise'] = 1
    opt['model_norm_thresh'] = 0.1
    opt['model_min_step'] = 0.5
    opt['pad'] = 5
    # opt['config_file'] = os.path.join(FAST_EXAMPLE_BASE_DIR, 'fit.10-genmodel.conf')
    opt['config_file'] = None
    opt['fit_stages'] = 'lin, 0, 0, 1, 1, 2, 2, 3, 3'
    opt['output_model'] = 'model.mnc'
    opt['output_stdev'] = 'stdev.mnc'
    # opt['workdir'] = '/scratch/volgenmodel-fast-example/work'
    opt['verbose'] = 1
    opt['clobber'] = 1

    def eval_to_int(x):
        try:
            return int(x)
        except:
            return x

    # setup the fit stages
    fit_stages = opt['fit_stages'].split(',')
    fit_stages = list(map(eval_to_int, fit_stages))

    # check for infiles and create files array
    if opt['verbose']: print("+++ INFILES\n")

    dirs = [None] * len(infiles)
    files = [None] * len(infiles)
    fileh = {}
    sub_id = []

    c = 0

    for z in infiles:
        dir = None
        f = None

        c_txt = '%04d' % c

        # check
        assert os.path.exists(z)

        # set up arrays
        dirs[c] = os.path.split(z)[0]  # &dirname($_);
        files[c] = c_txt + '-' + os.path.basename(
            z)  # "$c_txt-" . &basename($_);
        files[c] = files[c].replace('.mnc', '')  # =~ s/\.mnc$//;
        fileh[files[c]] = c
        sub_id.append(c)

        if opt['verbose']:
            print("  | [{c_txt}] {d} / {f}".format(c_txt=c_txt,
                                                   d=dirs[c],
                                                   f=files[c]))
        c += 1

    conf = read_conf_array(opt)

    # sanity check for fit config
    if fit_stages[-1] > (len(conf) - 1):
        assert False, (
            "Something is amiss with fit config, requested a "
            "fit step ($fit_stages[-1]) beyond what is defined in the "
            "fitting protocol (size: $#conf)\n\n")

    #rename
    #renameFiles = pe.MapNode(interface=Rename(format_string="importDcm2Mnc%(sd)04d_normStepSize_", keep_ext=True),
    #iterfield=['in_file', 'sd'], name='RenameFile')
    #renameFiles.inputs.sd = sub_id

    #workflow.connect(datasource, 'outfiles', renameFiles, 'in_file')

    # do pre-processing
    preprocess_volcentre = pe.MapNode(interface=Volcentre(zero_dircos=True),
                                      name='preprocess_volcentre',
                                      iterfield=['input_file'])

    #workflow.connect(renameFiles, 'out_file', preprocess_volcentre, 'input_file')
    workflow.connect(datasource, 'outfiles', preprocess_volcentre,
                     'input_file')

    # normalise
    if opt['normalise']:
        preprocess_threshold_blur = pe.MapNode(
            interface=deepcopy(
                calc_threshold_blur_preprocess
            ),  # Beware! Need deepcopy since calc_threshold_blur_preprocess is not a constructor!
            name='preprocess_threshold_blur',
            iterfield=['input_file'])

        workflow.connect(preprocess_volcentre, 'output_file',
                         preprocess_threshold_blur, 'input_file')

        preprocess_normalise = pe.MapNode(
            interface=Norm(cutoff=opt['model_norm_thresh'],
                           threshold=True,
                           threshold_perc=opt['model_norm_thresh']),
            # output_file=nrmfile),
            name='preprocess_normalise',
            iterfield=['input_file', 'threshold_blur'])

        workflow.connect(preprocess_threshold_blur, 'threshold_blur',
                         preprocess_normalise, 'threshold_blur')

        # do_cmd('mv -f %s %s' % (nrmfile, resfiles[f],))
    else:
        preprocess_normalise_id = utils.Function(
            input_names=['input_file'],
            output_names=['output_file'],
            function=IdentityFile,
        )

        preprocess_normalise = pe.MapNode(interface=preprocess_normalise_id,
                                          name='preprocess_normalise',
                                          iterfield=['input_file'])

    workflow.connect(preprocess_volcentre, 'output_file', preprocess_normalise,
                     'input_file')

    # extend/pad
    if opt['pad'] > 0:
        #smoothPadValue = 2
        preprocess_volpad = pe.MapNode(
            interface=Volpad(distance=opt['pad'],
                             smooth=True,
                             smooth_distance=5),
            # output_file=fitfiles[f]),
            name='preprocess_volpad',
            iterfield=['input_file'])
    else:
        preprocess_volpad_id = utils.Function(
            input_names=['input_file'],
            output_names=['output_file'],
            function=IdentityFile,
        )

        preprocess_volpad = pe.MapNode(interface=preprocess_volpad_id,
                                       name='preprocess_volpad',
                                       iterfield=['input_file'])

    workflow.connect(preprocess_normalise, 'output_file', preprocess_volpad,
                     'input_file')

    # isotropic resampling
    if opt['iso']:
        preprocess_voliso = pe.MapNode(
            interface=Voliso(avgstep=True),  # output_file=isofile),
            name='preprocess_voliso',
            iterfield=['input_file'])
    else:
        preprocess_voliso_id = utils.Function(
            input_names=['input_file'],
            output_names=['output_file'],
            function=IdentityFile,
        )

        preprocess_voliso = pe.MapNode(interface=preprocess_voliso_id,
                                       name='preprocess_iso',
                                       iterfield=['input_file'])

    workflow.connect(preprocess_volpad, 'output_file', preprocess_voliso,
                     'input_file')

    # checkfile
    if opt['check']:
        preprocess_pik = pe.MapNode(
            interface=Pik(triplanar=True,
                          sagittal_offset=10),  # output_file=chkfile),
            name='preprocess_pik',
            iterfield=['input_file'])
    else:
        preprocess_pik_id = utils.Function(
            input_names=['input_file'],
            output_names=['output_file'],
            function=IdentityFile,
        )

        preprocess_pik = pe.MapNode(interface=preprocess_pik_id,
                                    name='preprocess_pik',
                                    iterfield=['input_file'])

    workflow.connect(preprocess_volpad, 'output_file', preprocess_pik,
                     'input_file')

    # setup the initial model
    if opt['init_model'] is not None:
        # cmodel = opt['init_model']
        raise NotImplemented
        # To do this, make a data grabber that sends the MNC file to
        # the identity_transformation node below.
    else:
        # Select the 'first' output file from volpad (fitfiles[] in the original volgenmodel).
        select_first_volpad = pe.Node(interface=utils.Select(index=[0]),
                                      name='select_first_volpad')
        workflow.connect(preprocess_volpad, 'output_file', select_first_volpad,
                         'inlist')

        # Select the 'first' input file to calculate the fhwm3d parameter (infiles[] in the original volgenmodel).
        select_first_datasource = pe.Node(interface=utils.Select(index=[0]),
                                          name='select_first_datasource')
        workflow.connect(datasource, 'outfiles', select_first_datasource,
                         'inlist')

        # Calculate the fhwm3d parameter using the first datasource.
        initial_model_fwhm3d = pe.Node(
            interface=deepcopy(calc_initial_model_fwhm3d),
            name='initial_model_fwhm3d'
        )  # Beware! Need deepcopy since calc_initial_model_fwhm3d is not a constructor!
        workflow.connect(select_first_datasource, 'out', initial_model_fwhm3d,
                         'input_file')

        initial_model = pe.Node(
            interface=Blur(
            ),  # output_file_base=os.path.join(opt['workdir'], '00-init-model')),
            name='initial_model')

        workflow.connect(select_first_volpad, 'out', initial_model,
                         'input_file')
        workflow.connect(initial_model_fwhm3d, 'fwhm3d', initial_model,
                         'fwhm3d')

    # Current model starts off as the initial model.
    cmodel = initial_model

    identity_transformation = pe.Node(
        interface=Gennlxfm(
            step=conf[0]['step']),  # output_file=initxfm, also output_grid!
        name='identity_transformation')

    workflow.connect(initial_model, 'output_file', identity_transformation,
                     'like')

    # get last linear stage from fit config

    s = None
    end_stage = None

    snum = 0
    lastlin = 0
    for snum in range(len(
            fit_stages)):  # for($snum = 0; $snum <= $#fit_stages; $snum++){
        if fit_stages[snum] == 'lin':
            lastlin = snum  # "%02d" % snum

    print("+++ Last Linear stage:", lastlin)

    # Foreach end stage in the fitting profile
    print("+++ Fitting")

    last_linear_stage_xfm_node = None

    for snum in range(len(fit_stages)):
        snum_txt = None
        end_stage = None
        # f = None
        # cworkdir = None
        # conf_fname = None
        # modxfm = [None] * len(files)
        # rsmpl = [None] * len(files)

        end_stage = fit_stages[snum]
        snum_txt = "%02d_" % snum
        print("  + [Stage: {snum_txt}] End stage: {end_stage}".format(
            snum_txt=snum_txt, end_stage=end_stage))

        # make subdir in working dir for files
        # cworkdir = os.path.join(opt['workdir'], snum_txt)
        # if not os.path.exists(cworkdir):
        #     do_cmd('mkdir ' + cworkdir)

        # set up model and xfm names
        # avgxfm = os.path.join(cworkdir, "avgxfm.xfm")
        # iavgfile = os.path.join(cworkdir, "model.iavg.mnc")
        # istdfile = os.path.join(cworkdir, "model.istd.mnc")
        # stage_model = os.path.join(cworkdir, "model.avg.mnc")
        # iavgfilechk = os.path.join(cworkdir, "model.iavg.jpg")
        # istdfilechk = os.path.join(cworkdir, "model.istd.jpg")
        # stage_modelchk = os.path.join(cworkdir, "model.avg.jpg")

        # create the ISO model
        # isomodel_base = os.path.join(cworkdir, "fit-model-iso")
        if end_stage == 'lin':
            _idx = 0
        else:
            _idx = end_stage
        modelmaxstep = conf[_idx]['step'] / 4

        # check that the resulting model won't be too large
        # this seems confusing but it actually makes sense...
        if float(modelmaxstep) < float(opt['model_min_step']):
            modelmaxstep = opt['model_min_step']

        print("   -- Model Max step:", modelmaxstep)

        norm = pe.Node(
            interface=Norm(cutoff=opt['model_norm_thresh'],
                           threshold=True,
                           threshold_perc=opt['model_norm_thresh'],
                           threshold_blur=3),
            # output_threshold_mask=isomodel_base + ".msk.mnc"),
            # input_file=cmodel,
            # output_file=isomodel_base + ".nrm.mnc"),
            name='norm_' + snum_txt)

        workflow.connect(cmodel, 'output_file', norm, 'input_file')
        voliso = pe.Node(
            interface=Voliso(maxstep=modelmaxstep),
            # input_file=isomodel_base + ".nrm.mnc",
            # output_file=isomodel_base + ".mnc"),
            name='voliso_' + snum_txt)
        workflow.connect(norm, 'output_file', voliso, 'input_file')
        if opt['check']:
            pik = pe.Node(
                interface=Pik(triplanar=True,
                              horizontal_triplanar_view=True,
                              scale=4,
                              tile_size=400,
                              sagittal_offset=10),
                # input_file=isomodel_base + ".mnc",
                # output_file=isomodel_base + ".jpg"),
                name='pik_check_voliso' + snum_txt)

            workflow.connect(voliso, 'output_file', pik, 'input_file')
        # create the isomodel fit mask
        #chomp($step_x = `mincinfo -attvalue xspace:step $isomodel_base.msk.mnc`);
        step_x = 1
        blur = pe.Node(
            interface=Blur(fwhm=step_x *
                           15),  # input_file=isomodel_base + ".msk.mnc",
            # output_file_base=isomodel_base + ".msk"),
            name='blur_' + snum_txt)

        workflow.connect(norm, 'output_threshold_mask', blur, 'input_file')

        mincmath = pe.Node(
            interface=Math(test_gt=0.1),
            # input_files=[isomodel_base + ".msk_blur.mnc"],
            # output_file=isomodel_base + ".fit-msk.mnc"),
            name='mincmath_' + snum_txt)

        workflow.connect(blur, 'output_file', mincmath, 'input_files')

        # linear or nonlinear fit
        if end_stage == 'lin':
            print("---Linear fit---")
        else:
            print("---Non Linear fit---")

            # create nlin fit config
            if end_stage != 'lin':
                write_conf = pe.Node(
                    interface=deepcopy(
                        write_stage_conf_file
                    ),  # Beware! Need deepcopy since write_stage_conf_file is not a constructor!
                    name='write_conf_' + snum_txt)

                write_conf.inputs.snum = snum
                write_conf.inputs.snum_txt = snum_txt
                write_conf.inputs.conf = conf
                write_conf.inputs.end_stage = end_stage

        # register each file in the input series
        if end_stage == 'lin':
            assert opt['linmethod'] == 'bestlinreg'
            bestlinreg = pe.MapNode(
                interface=BestLinReg(),
                # source=isomodel_base + ".mnc",
                # target=fitfiles[f],
                # output_xfm=modxfm[f]),
                name='register_' + snum_txt,
                iterfield=['target'])

            workflow.connect(voliso, 'output_file', bestlinreg, 'source')
            workflow.connect(preprocess_voliso, 'output_file', bestlinreg,
                             'target')

            if snum == lastlin:
                last_linear_stage_xfm_node = bestlinreg

            modxfm = bestlinreg
        else:
            xfmconcat = pe.MapNode(
                interface=XfmConcat(),
                # input_files=[os.path.join(opt['workdir'], lastlin, files[f] + ".xfm"), initxfm],
                # output_file=initcnctxfm),
                name='xfmconcat_for_nlpfit_' + snum_txt,
                iterfield=['input_files'])

            merge_lastlin_initxfm = pe.MapNode(interface=utils.Merge(2),
                                               name='merge_lastlin_initxfm_' +
                                               snum_txt,
                                               iterfield=['in1'])

            workflow.connect(last_linear_stage_xfm_node, 'output_xfm',
                             merge_lastlin_initxfm, 'in1')
            workflow.connect(identity_transformation, 'output_file',
                             merge_lastlin_initxfm, 'in2')

            workflow.connect(merge_lastlin_initxfm, 'out', xfmconcat,
                             'input_files')

            workflow.connect(identity_transformation, 'output_grid', xfmconcat,
                             'input_grid_files')

            nlpfit = pe.MapNode(
                interface=NlpFit(),
                # init_xfm=initcnctxfm,
                # config_file=conf_fname),
                # source_mask=isomodel_base + ".fit-msk.mnc",
                # source=isomodel_base + ".mnc",
                # target=fitfiles[f],
                # output_xfm=modxfm[f]),
                name='nlpfit_' + snum_txt,
                iterfield=['target', 'init_xfm'])

            workflow.connect(write_conf, 'conf_fname', nlpfit, 'config_file')

            workflow.connect(xfmconcat, 'output_file', nlpfit, 'init_xfm')
            workflow.connect(mincmath, 'output_file', nlpfit, 'source_mask')
            workflow.connect(voliso, 'output_file', nlpfit, 'source')
            workflow.connect(
                preprocess_voliso, 'output_file', nlpfit, 'target'
            )  # Make sure that fitfiles[f] is preprocess_voliso at this point in the program.

            workflow.connect(xfmconcat, 'output_grids', nlpfit,
                             'input_grid_files')

            modxfm = nlpfit

        # average xfms
        xfmavg = pe.Node(
            interface=XfmAvg(),
            # input_files=modxfm,
            # output_file=avgxfm),
            name='xfmavg_' + snum_txt)

        if end_stage != 'lin':
            workflow.connect(nlpfit, 'output_grid', xfmavg, 'input_grid_files')

        workflow.connect(
            modxfm, 'output_xfm', xfmavg, 'input_files'
        )  # check that this works - multiple outputs of MapNode going into single list of xfmavg.

        if end_stage == 'lin':
            xfmavg.interface.inputs.ignore_nonlinear = True
        else:
            xfmavg.interface.inputs.ignore_linear = True

        # invert model xfm
        xfminvert = pe.MapNode(
            interface=XfmInvert(),
            # input_file=modxfm[f],
            # output_file=invxfm),
            name='xfminvert_' + snum_txt,
            iterfield=['input_file'])

        workflow.connect(modxfm, 'output_xfm', xfminvert, 'input_file')

        # concat: invxfm, avgxfm
        merge_xfm = pe.MapNode(interface=utils.Merge(2),
                               name='merge_xfm_' + snum_txt,
                               iterfield=['in1'])

        workflow.connect(xfminvert, 'output_file', merge_xfm, 'in1')
        workflow.connect(xfmavg, 'output_file', merge_xfm, 'in2')

        # Collect grid f iles of xfminvert and xvmavg. This is in two steps.
        #
        # 1. Merge MapNode results.
        merge_xfm_mapnode_result = pe.Node(interface=utils.Merge(1),
                                           name='merge_xfm_mapnode_result_' +
                                           snum_txt)
        workflow.connect(xfminvert, 'output_grid', merge_xfm_mapnode_result,
                         'in1')

        # 2. Merge xfmavg's single output with the result from step 1.
        merge_xfmavg_and_step1 = pe.Node(interface=utils.Merge(2),
                                         name='merge_xfmavg_and_step1' +
                                         snum_txt)
        workflow.connect(merge_xfm_mapnode_result, 'out',
                         merge_xfmavg_and_step1, 'in1')
        workflow.connect(xfmavg, 'output_grid', merge_xfmavg_and_step1, 'in2')

        xfmconcat = pe.MapNode(
            interface=XfmConcat(),
            # input_files=[invxfm, avgxfm],
            # output_file=resxfm),
            name='xfmconcat_' + snum_txt,
            iterfield=['input_files'])

        workflow.connect(merge_xfm, 'out', xfmconcat, 'input_files')

        workflow.connect(merge_xfmavg_and_step1, 'out', xfmconcat,
                         'input_grid_files')

        # resample
        resample = pe.MapNode(
            interface=Resample(sinc_interpolation=True),
            # transformation=resxfm,
            # like=isomodel_base + ".mnc",
            # input_file=resfiles[f],
            # output_file=rsmpl[f]),
            name='resample_' + snum_txt,
            iterfield=['input_file', 'transformation'])

        workflow.connect(preprocess_normalise, 'output_file', resample,
                         'input_file')

        workflow.connect(xfmconcat, 'output_file', resample, 'transformation')

        workflow.connect(voliso, 'output_file', resample, 'like')

        if opt['check']:
            pik_check_resample = pe.MapNode(
                interface=Pik(triplanar=True, sagittal_offset=10),
                # input_file=rsmpl[f],
                # output_file=chkfile),
                name='pik_check_resample_' + snum_txt,
                iterfield=['input_file'])

            workflow.connect(resample, 'output_file', pik_check_resample,
                             'input_file')

        # create model
        bigaverage = pe.Node(
            interface=BigAverage(output_float=True, robust=True),
            # tmpdir=os.path.join(opt['workdir'], 'tmp'),
            # sd_file=istdfile,
            # input_files=rsmpl,
            # output_file=iavgfile),
            name='bigaverage_' + snum_txt,
            iterfield=['input_file'])

        workflow.connect(resample, 'output_file', bigaverage, 'input_files')

        if opt['check']:
            pik_check_iavg = pe.Node(
                interface=Pik(triplanar=True,
                              horizontal_triplanar_view=True,
                              scale=4,
                              tile_size=400,
                              sagittal_offset=10),
                # input_file=iavgfile,
                # output_file=iavgfilechk),
                name='pik_check_iavg_' + snum_txt)

            workflow.connect(bigaverage, 'output_file', pik_check_iavg,
                             'input_file')

        # do symmetric averaging if required
        if opt['symmetric']:
            # symxfm = os.path.join(cworkdir, 'model.sym.xfm')
            # symfile = os.path.join(cworkdir, 'model.iavg-short.mnc')

            # convert double model to short
            resample_to_short = pe.Node(
                interface=Reshape(write_short=True),
                # input_file=iavgfile,
                # output_file=symfile),
                name='resample_to_short_' + snum_txt)

            workflow.connect(bigaverage, 'output_file', resample_to_short,
                             'input_file')

            assert opt['symmetric_dir'] == 'x'  #  handle other cases
            volsymm_on_short = pe.Node(
                interface=VolSymm(x=True),
                # input_file=symfile,
                # trans_file=symxfm, # This is an output!
                # output_file=stage_model),
                name='volsymm_on_short_' + snum_txt)

            workflow.connect(resample_to_short, 'output_file',
                             volsymm_on_short, 'input_file')

            # set up fit args
            if end_stage == 'lin':
                volsymm_on_short.interface.inputs.fit_linear = True
            else:
                volsymm_on_short.interface.inputs.fit_nonlinear = True
                workflow.connect(write_conf, 'conf_fname', volsymm_on_short,
                                 'config_file')

        else:
            # do_cmd('ln -s -f %s %s' % (os.path.basename(iavgfile), stage_model,))
            volsymm_on_short_id = utils.Function(
                input_names=['input_file'],
                output_names=['output_file'],
                function=IdentityFile,
            )

            volsymm_on_short = pe.Node(interface=volsymm_on_short_id,
                                       name='volsymm_on_short_' + snum_txt)

            workflow.connect(bigaverage, 'output_file', volsymm_on_short,
                             'input_file')

        # We finally have the stage model.
        stage_model = volsymm_on_short

        if opt['check']:
            pik_on_stage_model = pe.Node(
                interface=Pik(triplanar=True,
                              horizontal_triplanar_view=True,
                              scale=4,
                              tile_size=400,
                              sagittal_offset=10),
                # input_file=stage_model,
                # output_file=stage_modelchk),
                name='pik_on_stage_model_' + snum_txt)

            workflow.connect(stage_model, 'output_file', pik_on_stage_model,
                             'input_file')

        # if on last step, copy model to $opt{'output_model'}
        if snum == len(fit_stages) - 1:
            workflow.connect(stage_model, 'output_file', datasink, 'model')

            # create and output standard deviation file if requested
            if opt['output_stdev'] is not None:
                if opt['symmetric']:
                    assert opt['symmetric_dir'] == 'x'  # handle other cases
                    volsymm_final_model = pe.Node(
                        interface=VolSymm(x=True, nofit=True),
                        # input_file=istdfile,
                        # trans_file=symxfm, # This is an output!
                        # output_file=opt['output_stdev']),
                        name='volsymm_final_model_' + snum_txt)

                    workflow.connect(bigaverage, 'sd_file',
                                     volsymm_final_model, 'input_file')
                    workflow.connect(volsymm_on_short, 'trans_file',
                                     volsymm_final_model, 'trans_file')
                    workflow.connect(volsymm_on_short, 'output_grid',
                                     volsymm_final_model, 'input_grid_files')
                    workflow.connect(volsymm_final_model, 'output_file',
                                     datasink,
                                     'stdev')  # we ignore opt['output_stdev']
                else:
                    # do_cmd('cp -f %s %s' % (istdfile, opt['output_stdev'],))
                    workflow.connect(bigaverage, 'sd_file', datasink,
                                     'stdev')  # we ignore opt['output_stdev']

        cmodel = stage_model

    return workflow
Ejemplo n.º 12
0
"""

level1estimate = pe.Node(interface=spm.EstimateModel(), name="level1estimate")
level1estimate.inputs.estimation_method = {'Classical': 1}

"""Use :class:`nipype.interfaces.spm.EstimateContrast` to estimate the
first level contrasts specified in a few steps above.
"""

contrastestimate = pe.Node(interface=spm.EstimateContrast(), name="contrastestimate")

"""Use :class: `nipype.interfaces.utility.Select` to select each contrast for
reporting.
"""

selectcontrast = pe.Node(interface=util.Select(), name="selectcontrast")

"""Use :class:`nipype.interfaces.fsl.Overlay` to combine the statistical output of
the contrast estimate and a background image into one volume.
"""

overlaystats = pe.Node(interface=fsl.Overlay(), name="overlaystats")
overlaystats.inputs.stat_thresh = (3, 10)
overlaystats.inputs.show_negative_stats = True
overlaystats.inputs.auto_thresh_bg = True

"""Use :class:`nipype.interfaces.fsl.Slicer` to create images of the overlaid
statistical volumes for a report of the first-level results.
"""

slicestats = pe.Node(interface=fsl.Slicer(), name="slicestats")
# Add default arguments in the parser
default_parser_argument(parser)

# Parse the input arguments
args = parser.parse_args()

# Create the output folder if it does not exists
result_dir = os.path.abspath(args.output_dir)
if not os.path.exists(result_dir):
    os.mkdir(result_dir)

input_images = [os.path.abspath(f) for f in args.input_img]

# Create an iterable to loop over all input images
selector = pe.Node(niu.Select(inlist=input_images),
                   name='selector',
                   iterables=('index', range(len(input_images))))

# Create the workflow
db = os.path.abspath(args.database)
workflow = create_steps_propagation_pipeline(name='steps_propagation',
                                             aligned_templates=get_db_template_alignment(db))
workflow.inputs.input_node.database_file = db
workflow.base_dir = result_dir
workflow.connect(selector, 'out', workflow.get_node('input_node'), 'in_file')

# output the graph if required
if args.graph is True:
    generate_graph(workflow=workflow)
    sys.exit(0)
Ejemplo n.º 14
0
def create_gif_pseudoct_workflow(in_ute_echo2_file,
                                 in_ute_umap_dir,
                                 in_db_file,
                                 cpp_dir,
                                 in_t1_file=None,
                                 in_t2_file=None,
                                 in_mask_file=None,
                                 in_nac_pet_dir=None,
                                 name='gif_pseudoct'):
    """create_niftyseg_gif_propagation_pipeline.
    @param in_ute_echo2_file  input UTE echo file
    @param in_ute_umap_dir    input UTE umap file
    @param in_db_file         input database xml file for the GIF algorithm
    @param cpp_dir            cpp directory
    @param in_t1_file         input T1 target file
    @param in_t2_file         input T2 target file
    @param in_mask_file       optional input mask for the target T1 file
    @param name               optional name of the pipeline
    """

    in_file = in_t1_file if in_t1_file else in_t2_file
    subject_id = split_filename(os.path.basename(in_file))[1]

    workflow = pe.Workflow(name=name)
    workflow.base_output_dir = name

    gif = pe.Node(interface=Gif(database_file=in_db_file,
                                cpp_dir=cpp_dir,
                                lncc_ker=3,
                                regNMI=True,
                                regBE=0.01),
                  name='gif')
    if in_mask_file:
        gif.inputs.mask_file = in_mask_file

    # Create empty masks for the bias correction to cover the full image
    t1_full_mask = pe.Node(interface=niu.Function(input_names=['in_file'],
                                                  output_names=['out_file'],
                                                  function=create_full_mask),
                           name='t1_full_mask')
    t1_full_mask.inputs.in_file = in_t1_file
    t2_full_mask = pe.Node(interface=niu.Function(input_names=['in_file'],
                                                  output_names=['out_file'],
                                                  function=create_full_mask),
                           name='t2_full_mask')
    t2_full_mask.inputs.in_file = in_t2_file

    # Create bias correction nodes that are adapted to our needs. i.e. Boost the T2 bias correction
    bias_correction_t1 = pe.Node(interface=N4BiasCorrection(),
                                 name='bias_correction_t1')
    if in_t1_file:
        bias_correction_t1.inputs.in_file = in_t1_file

    # Create bias correction nodes that are adapted to our needs. i.e. Boost the T2 bias correction
    bias_correction_t2 = pe.Node(interface=N4BiasCorrection(
        in_maxiter=300, in_convergence=0.0001),
                                 name='bias_correction_t2')
    if in_t2_file:
        bias_correction_t2.inputs.in_file = in_t2_file

    # Only connect the nodes if the input image exist respectively
    if in_t1_file:
        workflow.connect(t1_full_mask, 'out_file', bias_correction_t1,
                         'mask_file')
    if in_t2_file:
        workflow.connect(t2_full_mask, 'out_file', bias_correction_t2,
                         'mask_file')

    if in_t1_file and in_t2_file:
        affine_mr_target = pe.Node(interface=niftyreg.RegAladin(maxit_val=10),
                                   name='affine_mr_target')
        workflow.connect(bias_correction_t1, 'out_file', affine_mr_target,
                         'ref_file')
        workflow.connect(bias_correction_t2, 'out_file', affine_mr_target,
                         'flo_file')
        resample_mr_target = pe.Node(
            interface=niftyreg.RegResample(pad_val=float('nan')),
            name='resample_MR_target')
        workflow.connect(bias_correction_t1, 'out_file', resample_mr_target,
                         'ref_file')
        workflow.connect(bias_correction_t2, 'out_file', resample_mr_target,
                         'flo_file')
        lister = pe.Node(interface=niu.Merge(2), name='lister')
        merger = pe.Node(interface=fsl.Merge(dimension='t',
                                             output_type='NIFTI_GZ'),
                         name='fsl_merge')
        workflow.connect(affine_mr_target, 'aff_file', resample_mr_target,
                         'trans_file')
        workflow.connect(bias_correction_t1, 'out_file', lister, 'in1')
        workflow.connect(resample_mr_target, 'out_file', lister, 'in2')
        workflow.connect(lister, 'out', merger, 'in_files')
        workflow.connect(merger, 'merged_file', gif, 'in_file')
    else:
        if in_t1_file:
            workflow.connect(bias_correction_t1, 'out_file', gif, 'in_file')
        if in_t2_file:
            workflow.connect(bias_correction_t2, 'out_file', gif, 'in_file')

    pct_hu_to_umap = pe.Node(interface=niu.Function(
        input_names=['pCT_file', 'structural_mri_file', 'ute_echo2_file'],
        output_names=['pct_umap_file'],
        function=convert_pct_hu_to_umap),
                             name='pCT_HU_to_umap')
    pct_hu_to_umap.inputs.structural_mri_file = in_file
    pct_hu_to_umap.inputs.ute_echo2_file = in_ute_echo2_file
    workflow.connect(gif, 'synth_file', pct_hu_to_umap, 'pCT_file')

    pct2dcm_pct_umap = pe.Node(interface=Pct2Dcm(in_umap_name='pCT_umap'),
                               name='pct2dcm_pct_umap')
    workflow.connect(pct_hu_to_umap, 'pct_umap_file', pct2dcm_pct_umap,
                     'in_umap_file')
    pct2dcm_pct_umap.inputs.in_ute_umap_dir = os.path.abspath(in_ute_umap_dir)

    merger_output_number = 2

    pct2dcm_ute_umap_end = None
    pct2dcm_pct_umap_end = None
    if in_nac_pet_dir:

        ute_umap_dcm2nii = pe.Node(
            interface=Dcm2nii(source_dir=in_ute_umap_dir),
            name='ute_umap_dcm2nii')
        first_item_selector = pe.Node(interface=niu.Select(index=0),
                                      name='first_item_selector')
        workflow.connect(ute_umap_dcm2nii, 'converted_files',
                         first_item_selector, 'inlist')

        nac_extractor = pe.Node(interface=niu.Function(
            input_names=['dicom_folder'],
            output_names=['nifti_file'],
            function=extract_nac_pet),
                                name='nac_extractor')
        nac_extractor.inputs.dicom_folder = in_nac_pet_dir

        ute_to_nac_registration = pe.Node(
            interface=niftyreg.RegAladin(rig_only_flag=True),
            name='ute_to_nac_registration')
        workflow.connect(nac_extractor, 'nifti_file', ute_to_nac_registration,
                         'ref_file')
        ute_to_nac_registration.inputs.flo_file = in_ute_echo2_file

        ute_resample = pe.Node(interface=niftyreg.RegResample(),
                               name='ute_resample')
        workflow.connect(first_item_selector, 'out', ute_resample, 'ref_file')
        workflow.connect(first_item_selector, 'out', ute_resample, 'flo_file')
        workflow.connect(ute_to_nac_registration, 'aff_file', ute_resample,
                         'aff_file')

        pct2dcm_ute_umap_end = pe.Node(
            interface=Pct2Dcm(in_umap_name='UTE_umap_end'),
            name='pct2dcm_ute_umap_end')
        workflow.connect(ute_resample, 'res_file', pct2dcm_ute_umap_end,
                         'in_umap_file')
        pct2dcm_ute_umap_end.inputs.in_ute_umap_dir = os.path.abspath(
            in_ute_umap_dir)

        pct_resample = pe.Node(interface=niftyreg.RegResample(),
                               name='pct_resample')
        workflow.connect(pct_hu_to_umap, 'pct_umap_file', pct_resample,
                         'ref_file')
        workflow.connect(pct_hu_to_umap, 'pct_umap_file', pct_resample,
                         'flo_file')
        workflow.connect(ute_to_nac_registration, 'aff_file', pct_resample,
                         'aff_file')

        pct2dcm_pct_umap_end = pe.Node(
            interface=Pct2Dcm(in_umap_name='pCT_umap_end'),
            name='pct2dcm_pct_umap_end')
        workflow.connect(pct_resample, 'res_file', pct2dcm_pct_umap_end,
                         'in_umap_file')
        pct2dcm_pct_umap_end.inputs.in_ute_umap_dir = os.path.abspath(
            in_ute_umap_dir)

        merger_output_number = 4

    # merge output
    output_merger = pe.Node(
        interface=niu.Merge(numinputs=merger_output_number),
        name='output_merger')
    workflow.connect(gif, 'synth_file', output_merger, 'in1')
    workflow.connect(pct2dcm_pct_umap, 'output_file', output_merger, 'in2')

    renamer = pe.Node(interface=niu.Rename(format_string=subject_id +
                                           "_%(type)s",
                                           keep_ext=True),
                      name='renamer')
    if in_nac_pet_dir:
        workflow.connect(pct2dcm_ute_umap_end, 'output_file', output_merger,
                         'in3')
        workflow.connect(pct2dcm_pct_umap_end, 'output_file', output_merger,
                         'in4')
        renamer.inputs.type = ['synth', 'pct', 'ute_end', 'pct_end']
    else:
        renamer.inputs.type = ['synth', 'pct']
    workflow.connect(output_merger, 'out', renamer, 'in_file')

    return workflow
Ejemplo n.º 15
0
    def build_core_nodes(self):
        """Build and connect the core nodes of the pipeline.

        Notes:
            - If `FSLOUTPUTTYPE` environment variable is not set, `nipype` takes
            NIFTI by default.

        Todo:
            - [x] Detect space automatically.
            - [ ] Allow for custom parcellations (See TODOs in utils).

        """
        import nipype.interfaces.freesurfer as fs
        import nipype.interfaces.fsl as fsl
        import nipype.interfaces.mrtrix3 as mrtrix3
        import nipype.interfaces.utility as niu
        import nipype.pipeline.engine as npe
        from nipype.interfaces.mrtrix3.tracking import Tractography
        from nipype.interfaces.mrtrix.preprocess import MRTransform

        import clinica.pipelines.dwi_connectome.dwi_connectome_utils as utils
        from clinica.lib.nipype.interfaces.mrtrix3.reconst import EstimateFOD
        from clinica.utils.exceptions import ClinicaCAPSError
        from clinica.utils.mri_registration import (
            convert_flirt_transformation_to_mrtrix_transformation,
        )

        # Nodes
        # =====
        # B0 Extraction (only if space=b0)
        # -------------
        split_node = npe.Node(name="Reg-0-DWI-B0Extraction", interface=fsl.Split())
        split_node.inputs.output_type = "NIFTI_GZ"
        split_node.inputs.dimension = "t"
        select_node = npe.Node(name="Reg-0-DWI-B0Selection", interface=niu.Select())
        select_node.inputs.index = 0

        # B0 Brain Extraction (only if space=b0)
        # -------------------
        mask_node = npe.Node(name="Reg-0-DWI-BrainMasking", interface=fsl.ApplyMask())
        mask_node.inputs.output_type = "NIFTI_GZ"

        # T1-to-B0 Registration (only if space=b0)
        # ---------------------
        t12b0_reg_node = npe.Node(
            name="Reg-1-T12B0Registration",
            interface=fsl.FLIRT(
                dof=6,
                interp="spline",
                cost="normmi",
                cost_func="normmi",
            ),
        )
        t12b0_reg_node.inputs.output_type = "NIFTI_GZ"

        # MGZ File Conversion (only if space=b0)
        # -------------------
        t1_brain_conv_node = npe.Node(
            name="Reg-0-T1-T1BrainConvertion", interface=fs.MRIConvert()
        )
        wm_mask_conv_node = npe.Node(
            name="Reg-0-T1-WMMaskConvertion", interface=fs.MRIConvert()
        )

        # WM Transformation (only if space=b0)
        # -----------------
        wm_transform_node = npe.Node(
            name="Reg-2-WMTransformation", interface=fsl.ApplyXFM()
        )
        wm_transform_node.inputs.apply_xfm = True

        # Nodes Generation
        # ----------------
        label_convert_node = npe.MapNode(
            name="0-LabelsConversion",
            iterfield=["in_file", "in_config", "in_lut", "out_file"],
            interface=mrtrix3.LabelConvert(),
        )
        label_convert_node.inputs.in_config = utils.get_conversion_luts()
        label_convert_node.inputs.in_lut = utils.get_luts()

        # FSL flirt matrix to MRtrix matrix Conversion (only if space=b0)
        # --------------------------------------------
        fsl2mrtrix_conv_node = npe.Node(
            name="Reg-2-FSL2MrtrixConversion",
            interface=niu.Function(
                input_names=[
                    "in_source_image",
                    "in_reference_image",
                    "in_flirt_matrix",
                    "name_output_matrix",
                ],
                output_names=["out_mrtrix_matrix"],
                function=convert_flirt_transformation_to_mrtrix_transformation,
            ),
        )

        # Parc. Transformation (only if space=b0)
        # --------------------
        parc_transform_node = npe.MapNode(
            name="Reg-2-ParcTransformation",
            iterfield=["in_files", "out_filename"],
            interface=MRTransform(),
        )

        # Response Estimation
        # -------------------
        resp_estim_node = npe.Node(
            name="1a-ResponseEstimation", interface=mrtrix3.ResponseSD()
        )
        resp_estim_node.inputs.algorithm = "tournier"

        # FOD Estimation
        # --------------
        fod_estim_node = npe.Node(name="1b-FODEstimation", interface=EstimateFOD())
        fod_estim_node.inputs.algorithm = "csd"

        # Tracts Generation
        # -----------------
        tck_gen_node = npe.Node(name="2-TractsGeneration", interface=Tractography())
        tck_gen_node.inputs.select = self.parameters["n_tracks"]
        tck_gen_node.inputs.algorithm = "iFOD2"

        # Connectome Generation
        # ---------------------
        # only the parcellation and output filename should be iterable, the tck
        # file stays the same.
        conn_gen_node = npe.MapNode(
            name="3-ConnectomeGeneration",
            iterfield=["in_parc", "out_file"],
            interface=mrtrix3.BuildConnectome(),
        )

        # Print begin message
        # -------------------
        print_begin_message = npe.MapNode(
            interface=niu.Function(
                input_names=["in_bids_or_caps_file"],
                function=utils.print_begin_pipeline,
            ),
            iterfield="in_bids_or_caps_file",
            name="WriteBeginMessage",
        )

        # Print end message
        # -----------------
        print_end_message = npe.MapNode(
            interface=niu.Function(
                input_names=["in_bids_or_caps_file", "final_file"],
                function=utils.print_end_pipeline,
            ),
            iterfield=["in_bids_or_caps_file"],
            name="WriteEndMessage",
        )

        # CAPS File names Generation
        # --------------------------
        caps_filenames_node = npe.Node(
            name="CAPSFilenamesGeneration",
            interface=niu.Function(
                input_names="dwi_file",
                output_names=self.get_output_fields(),
                function=utils.get_caps_filenames,
            ),
        )

        # Connections
        # ===========
        # Computation of the diffusion model, tractography & connectome
        # -------------------------------------------------------------
        # fmt: off
        self.connect(
            [
                (self.input_node, print_begin_message, [("dwi_file", "in_bids_or_caps_file")]),
                (self.input_node, caps_filenames_node, [("dwi_file", "dwi_file")]),
                # Response Estimation
                (self.input_node, resp_estim_node, [("dwi_file", "in_file")]),  # Preproc. DWI
                (self.input_node, resp_estim_node, [("dwi_brainmask_file", "in_mask")]),  # B0 brain mask
                (self.input_node, resp_estim_node, [("grad_fsl", "grad_fsl")]),  # bvecs and bvals
                (caps_filenames_node, resp_estim_node, [("response", "wm_file")]),  # output response filename
                # FOD Estimation
                (self.input_node, fod_estim_node, [("dwi_file", "in_file")]),  # Preproc. DWI
                (resp_estim_node, fod_estim_node, [("wm_file", "wm_txt")]),  # Response (txt file)
                (self.input_node, fod_estim_node, [("dwi_brainmask_file", "mask_file")]),  # B0 brain mask
                (self.input_node, fod_estim_node, [("grad_fsl", "grad_fsl")]),  # T1-to-B0 matrix file
                (caps_filenames_node, fod_estim_node, [("fod", "wm_odf")]),  # output odf filename
                # Tracts Generation
                (fod_estim_node, tck_gen_node, [("wm_odf", "in_file")]),  # ODF file
                (caps_filenames_node, tck_gen_node, [("tracts", "out_file")]),  # output tck filename
                # Label Conversion
                (self.input_node, label_convert_node, [("atlas_files", "in_file")]),  # atlas image files
                (caps_filenames_node, label_convert_node, [("nodes", "out_file")]),  # converted atlas image filenames
                # Connectomes Generation
                (tck_gen_node, conn_gen_node, [("out_file", "in_file")]),
                (caps_filenames_node, conn_gen_node, [("connectomes", "out_file")]),
            ]
        )
        # Registration T1-DWI (only if space=b0)
        # -------------------
        if self.parameters["dwi_space"] == "b0":
            self.connect(
                [
                    # MGZ Files Conversion
                    (self.input_node, t1_brain_conv_node, [("t1_brain_file", "in_file")]),
                    (self.input_node, wm_mask_conv_node, [("wm_mask_file", "in_file")]),
                    # B0 Extraction
                    (self.input_node, split_node, [("dwi_file", "in_file")]),
                    (split_node, select_node, [("out_files", "inlist")]),
                    # Masking
                    (select_node, mask_node, [("out", "in_file")]),  # B0
                    (self.input_node, mask_node, [("dwi_brainmask_file", "mask_file")]),  # Brain mask
                    # T1-to-B0 Registration
                    (t1_brain_conv_node, t12b0_reg_node, [("out_file", "in_file")]),  # Brain
                    (mask_node, t12b0_reg_node, [("out_file", "reference")]),  # B0 brain-masked
                    # WM Transformation
                    (wm_mask_conv_node, wm_transform_node, [("out_file", "in_file")]),  # Brain mask
                    (mask_node, wm_transform_node, [("out_file", "reference")]),  # BO brain-masked
                    (t12b0_reg_node, wm_transform_node, [("out_matrix_file", "in_matrix_file")]),  # T1-to-B0 matrix file
                    # FSL flirt matrix to MRtrix matrix Conversion
                    (t1_brain_conv_node, fsl2mrtrix_conv_node, [("out_file", "in_source_image")]),
                    (mask_node, fsl2mrtrix_conv_node, [("out_file", "in_reference_image")]),
                    (t12b0_reg_node, fsl2mrtrix_conv_node, [("out_matrix_file", "in_flirt_matrix")]),
                    # Apply registration without resampling on parcellations
                    (label_convert_node, parc_transform_node, [("out_file", "in_files")]),
                    (fsl2mrtrix_conv_node, parc_transform_node, [("out_mrtrix_matrix", "linear_transform")]),
                    (caps_filenames_node, parc_transform_node, [("nodes", "out_filename")]),
                ]
            )
        # Special care for Parcellation & WM mask
        # ---------------------------------------
        if self.parameters["dwi_space"] == "b0":
            self.connect(
                [
                    (wm_transform_node, tck_gen_node, [("out_file", "seed_image")]),
                    (parc_transform_node, conn_gen_node, [("out_file", "in_parc")]),
                    (parc_transform_node, self.output_node, [("out_file", "nodes")]),
                ]
            )
        elif self.parameters["dwi_space"] == "T1w":
            self.connect(
                [
                    (self.input_node, tck_gen_node, [("wm_mask_file", "seed_image")]),
                    (label_convert_node, conn_gen_node, [("out_file", "in_parc")]),
                    (label_convert_node, self.output_node, [("out_file", "nodes")]),
                ]
            )
        else:
            raise ClinicaCAPSError(
                "Bad preprocessed DWI space. Please check your CAPS folder."
            )
        # Outputs
        # -------
        self.connect(
            [
                (resp_estim_node, self.output_node, [("wm_file", "response")]),
                (fod_estim_node, self.output_node, [("wm_odf", "fod")]),
                (tck_gen_node, self.output_node, [("out_file", "tracts")]),
                (conn_gen_node, self.output_node, [("out_file", "connectomes")]),
                (self.input_node, print_end_message, [("dwi_file", "in_bids_or_caps_file")]),
                (conn_gen_node, print_end_message, [("out_file", "final_file")]),
            ]
        )
Ejemplo n.º 16
0
def init_bbreg_wf(
    *,
    omp_nthreads,
    debug=False,
    epi2t1w_init="register",
    epi2t1w_dof=6,
    name="bbreg_wf",
    use_bbr=None,
):
    """
    Build a workflow to run FreeSurfer's ``bbregister``.

    This workflow uses FreeSurfer's ``bbregister`` to register a EPI 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 niworkflows.anat.coregistration import init_bbreg_wf
            wf = init_bbreg_wf(omp_nthreads=1)

    Parameters
    ----------
    use_bbr : :obj:`bool` or None
        Enable/disable boundary-based registration refinement.
        If ``None``, test BBR result for distortion before accepting.
    epi2t1w_dof : 6, 9 or 12
        Degrees-of-freedom for EPI-T1w registration
    epi2t1w_init : :obj:`str`, ``"header"`` or ``"register"``
        If ``"header"``, use header information for initialization of EPI and T1 images.
        If ``"register"``, align volumes by their centers.
    name : :obj:`str`, optional
        Workflow name (default: ``bbreg_wf``)

    Inputs
    ------
    in_file
        Reference EPI image to be registered
    fsnative2t1w_xfm
        FSL-style affine matrix translating from FreeSurfer T1.mgz to T1w
    subjects_dir
        Sets FreeSurfer's ``$SUBJECTS_DIR``
    subject_id
        FreeSurfer subject ID (must have a corresponding 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_epi_to_t1w
        Affine transform from the reference EPI to T1w space (ITK format)
    itk_t1w_to_epi
        Affine transform from T1w space to EPI space (ITK format)
    out_report
        Reportlet for assessing registration quality
    fallback
        Boolean indicating whether BBR was rejected (mri_coreg registration returned)

    """
    from ..engine.workflows import LiterateWorkflow as Workflow

    # See https://github.com/nipreps/fmriprep/issues/768
    from ..interfaces.freesurfer import (
        PatchedBBRegisterRPT as BBRegisterRPT,
        PatchedMRICoregRPT as MRICoregRPT,
        PatchedLTAConvert as LTAConvert,
    )
    from ..interfaces.nitransforms import ConcatenateXFMs

    workflow = Workflow(name=name)
    workflow.__desc__ = """\
The EPI 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"
        }[epi2t1w_dof],
        reason="" if epi2t1w_dof == 6 else
        "to account for distortions remaining in the EPI reference",
    )

    inputnode = pe.Node(
        niu.IdentityInterface([
            "in_file",
            "fsnative2t1w_xfm",
            "subjects_dir",
            "subject_id",  # BBRegister
            "t1w_dseg",  # FLIRT BBR
            "t1w_brain",  # FLIRT BBR
        ]),
        name="inputnode",
    )
    outputnode = pe.Node(
        niu.IdentityInterface(
            ["itk_epi_to_t1w", "itk_t1w_to_epi", "out_report", "fallback"]),
        name="outputnode",
    )

    if epi2t1w_init not in ("register", "header"):
        raise ValueError(
            f"Unknown EPI-T1w initialization option: {epi2t1w_init}")

    # For now make BBR unconditional - in the future, we can fall back to identity,
    # but adding the flexibility without testing seems a bit dangerous
    if epi2t1w_init == "header":
        if use_bbr is False:
            raise ValueError("Cannot disable BBR and use header registration")
        if use_bbr is None:
            LOGGER.warning(
                "Initializing BBR with header; affine fallback disabled")
            use_bbr = True

    merge_ltas = pe.Node(niu.Merge(2),
                         name="merge_ltas",
                         run_without_submitting=True)
    concat_xfm = pe.Node(ConcatenateXFMs(inverse=True), name="concat_xfm")

    # fmt:off
    workflow.connect([
        # Output ITK transforms
        (inputnode, merge_ltas, [("fsnative2t1w_xfm", "in2")]),
        (merge_ltas, concat_xfm, [("out", "in_xfms")]),
        (concat_xfm, outputnode, [("out_xfm", "itk_epi_to_t1w")]),
        (concat_xfm, outputnode, [("out_inv", "itk_t1w_to_epi")]),
    ])
    # fmt:on

    if debug is True:
        from ..interfaces.nibabel import RegridToZooms

        downsample = pe.Node(RegridToZooms(zooms=(4.0, 4.0, 4.0), smooth=True),
                             name="downsample")
        workflow.connect([(inputnode, downsample, [("in_file", "in_file")])])

    mri_coreg = pe.Node(
        MRICoregRPT(
            dof=epi2t1w_dof,
            sep=[4],
            ftol=0.0001,
            linmintol=0.01,
            generate_report=not use_bbr,
        ),
        name="mri_coreg",
        n_procs=omp_nthreads,
        mem_gb=5,
    )

    # Use mri_coreg
    if epi2t1w_init == "register":
        # fmt:off
        workflow.connect([
            (inputnode, mri_coreg, [("subjects_dir", "subjects_dir"),
                                    ("subject_id", "subject_id")]),
        ])
        # fmt:on

        if not debug:
            workflow.connect(inputnode, "in_file", mri_coreg, "source_file")
        else:
            workflow.connect(downsample, "out_file", mri_coreg, "source_file")

        # Short-circuit workflow building, use initial registration
        if use_bbr is False:
            # fmt:off
            workflow.connect([
                (mri_coreg, outputnode, [("out_report", "out_report")]),
                (mri_coreg, merge_ltas, [("out_lta_file", "in1")]),
            ])
            # fmt:on
            outputnode.inputs.fallback = True
            return workflow

    # Use bbregister
    bbregister = pe.Node(
        BBRegisterRPT(
            dof=epi2t1w_dof,
            contrast_type="t2",
            registered_file=True,
            out_lta_file=True,
            generate_report=True,
        ),
        name="bbregister",
        mem_gb=12,
    )

    # fmt:off
    workflow.connect([
        (inputnode, bbregister, [("subjects_dir", "subjects_dir"),
                                 ("subject_id", "subject_id")]),
    ])
    # fmt:on

    if not debug:
        workflow.connect(inputnode, "in_file", bbregister, "source_file")
    else:
        workflow.connect(downsample, "out_file", bbregister, "source_file")

    if epi2t1w_init == "header":
        bbregister.inputs.init = "header"
    else:
        workflow.connect([(mri_coreg, bbregister, [("out_lta_file",
                                                    "init_reg_file")])])

    # Short-circuit workflow building, use boundary-based registration
    if use_bbr is True:
        # fmt:off
        workflow.connect([
            (bbregister, outputnode, [("out_report", "out_report")]),
            (bbregister, merge_ltas, [("out_lta_file", "in1")]),
        ])
        # fmt:on

        outputnode.inputs.fallback = False
        return workflow

    # Only reach this point if epi2t1w_init is "register" and use_bbr is None
    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")

    # fmt:off
    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, merge_ltas, [("out", "in1")]),
        # 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")]),
    ])
    # fmt:on

    return workflow
Ejemplo n.º 17
0
def init_brain_extraction_wf(name='brain_extraction_wf',
                             in_template='OASIS30ANTs',
                             template_spec=None,
                             use_float=True,
                             normalization_quality='precise',
                             omp_nthreads=None,
                             mem_gb=3.0,
                             bids_suffix='T1w',
                             atropos_refine=True,
                             atropos_use_random_seed=True,
                             atropos_model=None,
                             use_laplacian=True,
                             bspline_fitting_distance=200):
    """
    A Nipype implementation of the official ANTs' ``antsBrainExtraction.sh``
    workflow (only for 3D images).

    The official workflow is built as follows (and this implementation
    follows the same organization):

      1. Step 1 performs several clerical tasks (adding padding, calculating
         the Laplacian of inputs, affine initialization) and the core
         spatial normalization.
      2. Maps the brain mask into target space using the normalization
         calculated in 1.
      3. Superstep 1b: smart binarization of the brain mask
      4. Superstep 6: apply ATROPOS and massage its outputs
      5. Superstep 7: use results from 4 to refine the brain mask


    .. workflow::
        :graph2use: orig
        :simple_form: yes
        from niworkflows.anat import init_brain_extraction_wf
        wf = init_brain_extraction_wf()


    **Parameters**

        in_template : str
            Name of the skull-stripping template ('OASIS30ANTs', 'NKI', or
            path).
            The brain template from which regions will be projected
            Anatomical template created using e.g. LPBA40 data set with
            ``buildtemplateparallel.sh`` in ANTs.
            The workflow will automatically search for a brain probability
            mask created using e.g. LPBA40 data set which have brain masks
            defined, and warped to anatomical template and
            averaged resulting in a probability image.
        use_float : bool
            Whether single precision should be used
        normalization_quality : str
            Use more precise or faster registration parameters
            (default: ``precise``, other possible values: ``testing``)
        omp_nthreads : int
            Maximum number of threads an individual process may use
        mem_gb : float
            Estimated peak memory consumption of the most hungry nodes
            in the workflow
        bids_suffix : str
            Sequence type of the first input image. For a list of acceptable values
            see https://bids-specification.readthedocs.io/en/latest/\
04-modality-specific-files/01-magnetic-resonance-imaging-data.html#anatomy-imaging-data
        atropos_refine : bool
            Enables or disables the whole ATROPOS sub-workflow
        atropos_use_random_seed : bool
            Whether ATROPOS should generate a random seed based on the
            system's clock
        atropos_model : tuple or None
            Allows to specify a particular segmentation model, overwriting
            the defaults based on ``bids_suffix``
        use_laplacian : bool
            Enables or disables alignment of the Laplacian as an additional
            criterion for image registration quality (default: True)
        bspline_fitting_distance : float
            The size of the b-spline mesh grid elements, in mm (default: 200)
        name : str, optional
            Workflow name (default: antsBrainExtraction)


    **Inputs**

        in_files
            List of input anatomical images to be brain-extracted,
            typically T1-weighted.
            If a list of anatomical images is provided, subsequently
            specified images are used during the segmentation process.
            However, only the first image is used in the registration
            of priors.
            Our suggestion would be to specify the T1w as the first image.
        in_mask
            (optional) Mask used for registration to limit the metric
            computation to a specific region.


    **Outputs**


        out_file
            Skull-stripped and :abbr:`INU (intensity non-uniformity)`-corrected ``in_files``
        out_mask
            Calculated brain mask
        bias_corrected
            The ``in_files`` input images, after :abbr:`INU (intensity non-uniformity)`
            correction, before skull-stripping.
        bias_image
            The :abbr:`INU (intensity non-uniformity)` field estimated for each
            input in ``in_files``
        out_segm
            Output segmentation by ATROPOS
        out_tpms
            Output :abbr:`TPMs (tissue probability maps)` by ATROPOS


    """
    from templateflow.api import get as get_template
    wf = pe.Workflow(name)

    template_spec = template_spec or {}

    # suffix passed via spec takes precedence
    template_spec['suffix'] = template_spec.get('suffix', bids_suffix)

    tpl_target_path, common_spec = get_template_specs(
        in_template,
        template_spec=template_spec)

    # Get probabilistic brain mask if available
    tpl_mask_path = get_template(
        in_template, label='brain', suffix='probseg', **common_spec) or \
        get_template(in_template, desc='brain', suffix='mask', **common_spec)

    if omp_nthreads is None or omp_nthreads < 1:
        omp_nthreads = cpu_count()

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

    # Try to find a registration mask, set if available
    tpl_regmask_path = get_template(
        in_template, desc='BrainCerebellumExtraction', suffix='mask',
        **common_spec)
    if tpl_regmask_path:
        inputnode.inputs.in_mask = str(tpl_regmask_path)

    outputnode = pe.Node(niu.IdentityInterface(
        fields=['out_file', 'out_mask', 'bias_corrected', 'bias_image',
                'out_segm', 'out_tpms']),
        name='outputnode')

    copy_xform = pe.Node(CopyXForm(
        fields=['out_file', 'out_mask', 'bias_corrected', 'bias_image']),
        name='copy_xform', run_without_submitting=True)

    trunc = pe.MapNode(ImageMath(operation='TruncateImageIntensity', op2='0.01 0.999 256'),
                       name='truncate_images', iterfield=['op1'])
    inu_n4 = pe.MapNode(
        N4BiasFieldCorrection(
            dimension=3, save_bias=False, copy_header=True,
            n_iterations=[50] * 4, convergence_threshold=1e-7, shrink_factor=4,
            bspline_fitting_distance=bspline_fitting_distance),
        n_procs=omp_nthreads, name='inu_n4', iterfield=['input_image'])

    res_tmpl = pe.Node(ResampleImageBySpacing(
        out_spacing=(4, 4, 4), apply_smoothing=True), name='res_tmpl')
    res_tmpl.inputs.input_image = tpl_target_path
    res_target = pe.Node(ResampleImageBySpacing(
        out_spacing=(4, 4, 4), apply_smoothing=True), name='res_target')

    lap_tmpl = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'),
                       name='lap_tmpl')
    lap_tmpl.inputs.op1 = tpl_target_path
    lap_target = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'),
                         name='lap_target')
    mrg_tmpl = pe.Node(niu.Merge(2), name='mrg_tmpl')
    mrg_tmpl.inputs.in1 = tpl_target_path
    mrg_target = pe.Node(niu.Merge(2), name='mrg_target')

    # Initialize transforms with antsAI
    init_aff = pe.Node(AI(
        metric=('Mattes', 32, 'Regular', 0.25),
        transform=('Affine', 0.1),
        search_factor=(15, 0.1),
        principal_axes=False,
        convergence=(10, 1e-6, 10),
        verbose=True),
        name='init_aff',
        n_procs=omp_nthreads)

    # Tolerate missing ANTs at construction time
    _ants_version = Registration().version
    if _ants_version and parseversion(_ants_version) >= Version('2.3.0'):
        init_aff.inputs.search_grid = (40, (0, 40, 40))

    # Set up spatial normalization
    settings_file = 'antsBrainExtraction_%s.json' if use_laplacian \
        else 'antsBrainExtractionNoLaplacian_%s.json'
    norm = pe.Node(Registration(from_file=pkgr_fn(
        'niworkflows.data', settings_file % normalization_quality)),
        name='norm',
        n_procs=omp_nthreads,
        mem_gb=mem_gb)
    norm.inputs.float = use_float
    fixed_mask_trait = 'fixed_image_mask'
    if _ants_version and parseversion(_ants_version) >= Version('2.2.0'):
        fixed_mask_trait += 's'

    map_brainmask = pe.Node(
        ApplyTransforms(interpolation='Gaussian', float=True),
        name='map_brainmask',
        mem_gb=1
    )
    map_brainmask.inputs.input_image = str(tpl_mask_path)

    thr_brainmask = pe.Node(ThresholdImage(
        dimension=3, th_low=0.5, th_high=1.0, inside_value=1,
        outside_value=0), name='thr_brainmask')

    # Morphological dilation, radius=2
    dil_brainmask = pe.Node(ImageMath(operation='MD', op2='2'),
                            name='dil_brainmask')
    # Get largest connected component
    get_brainmask = pe.Node(ImageMath(operation='GetLargestComponent'),
                            name='get_brainmask')

    # Refine INU correction
    inu_n4_final = pe.MapNode(
        N4BiasFieldCorrection(
            dimension=3, save_bias=True, copy_header=True,
            n_iterations=[50] * 5, convergence_threshold=1e-7, shrink_factor=4,
            bspline_fitting_distance=bspline_fitting_distance),
        n_procs=omp_nthreads, name='inu_n4_final', iterfield=['input_image'])

    # Apply mask
    apply_mask = pe.MapNode(ApplyMask(), iterfield=['in_file'], name='apply_mask')

    wf.connect([
        (inputnode, trunc, [('in_files', 'op1')]),
        (inputnode, copy_xform, [(('in_files', _pop), 'hdr_file')]),
        (inputnode, inu_n4_final, [('in_files', 'input_image')]),
        (inputnode, init_aff, [('in_mask', 'fixed_image_mask')]),
        (inputnode, norm, [('in_mask', fixed_mask_trait)]),
        (inputnode, map_brainmask, [(('in_files', _pop), 'reference_image')]),
        (trunc, inu_n4, [('output_image', 'input_image')]),
        (inu_n4, res_target, [
            (('output_image', _pop), 'input_image')]),
        (res_tmpl, init_aff, [('output_image', 'fixed_image')]),
        (res_target, init_aff, [('output_image', 'moving_image')]),
        (init_aff, norm, [('output_transform', 'initial_moving_transform')]),
        (norm, map_brainmask, [
            ('reverse_transforms', 'transforms'),
            ('reverse_invert_flags', 'invert_transform_flags')]),
        (map_brainmask, thr_brainmask, [('output_image', 'input_image')]),
        (thr_brainmask, dil_brainmask, [('output_image', 'op1')]),
        (dil_brainmask, get_brainmask, [('output_image', 'op1')]),
        (inu_n4_final, apply_mask, [('output_image', 'in_file')]),
        (get_brainmask, apply_mask, [('output_image', 'mask_file')]),
        (get_brainmask, copy_xform, [('output_image', 'out_mask')]),
        (apply_mask, copy_xform, [('out_file', 'out_file')]),
        (inu_n4_final, copy_xform, [('output_image', 'bias_corrected'),
                                    ('bias_image', 'bias_image')]),
        (copy_xform, outputnode, [
            ('out_file', 'out_file'),
            ('out_mask', 'out_mask'),
            ('bias_corrected', 'bias_corrected'),
            ('bias_image', 'bias_image')]),
    ])

    if use_laplacian:
        lap_tmpl = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'),
                           name='lap_tmpl')
        lap_tmpl.inputs.op1 = tpl_target_path
        lap_target = pe.Node(ImageMath(operation='Laplacian', op2='1.5 1'),
                             name='lap_target')
        mrg_tmpl = pe.Node(niu.Merge(2), name='mrg_tmpl')
        mrg_tmpl.inputs.in1 = tpl_target_path
        mrg_target = pe.Node(niu.Merge(2), name='mrg_target')
        wf.connect([
            (inu_n4, lap_target, [
                (('output_image', _pop), 'op1')]),
            (lap_tmpl, mrg_tmpl, [('output_image', 'in2')]),
            (inu_n4, mrg_target, [('output_image', 'in1')]),
            (lap_target, mrg_target, [('output_image', 'in2')]),
            (mrg_tmpl, norm, [('out', 'fixed_image')]),
            (mrg_target, norm, [('out', 'moving_image')]),
        ])
    else:
        norm.inputs.fixed_image = tpl_target_path
        wf.connect([
            (inu_n4, norm, [
                (('output_image', _pop), 'moving_image')]),
        ])

    if atropos_refine:
        atropos_model = atropos_model or list(ATROPOS_MODELS[bids_suffix].values())
        atropos_wf = init_atropos_wf(
            use_random_seed=atropos_use_random_seed,
            omp_nthreads=omp_nthreads,
            mem_gb=mem_gb,
            in_segmentation_model=atropos_model,
        )
        sel_wm = pe.Node(niu.Select(index=atropos_model[-1] - 1), name='sel_wm',
                         run_without_submitting=True)

        wf.disconnect([
            (get_brainmask, apply_mask, [('output_image', 'mask_file')]),
            (copy_xform, outputnode, [('out_mask', 'out_mask')]),
        ])
        wf.connect([
            (inu_n4, atropos_wf, [
                ('output_image', 'inputnode.in_files')]),
            (thr_brainmask, atropos_wf, [
                ('output_image', 'inputnode.in_mask')]),
            (get_brainmask, atropos_wf, [
                ('output_image', 'inputnode.in_mask_dilated')]),
            (atropos_wf, sel_wm, [('outputnode.out_tpms', 'inlist')]),
            (sel_wm, inu_n4_final, [('out', 'weight_image')]),
            (atropos_wf, apply_mask, [
                ('outputnode.out_mask', 'mask_file')]),
            (atropos_wf, outputnode, [
                ('outputnode.out_mask', 'out_mask'),
                ('outputnode.out_segm', 'out_segm'),
                ('outputnode.out_tpms', 'out_tpms')]),
        ])
    return wf
def susceptibility_distortion_correction_using_t1(
        name='susceptibility_distortion_correction_using_t1'):
    """
    Susceptibility distortion correction using the T1w image.

    This workflow allows to correct for echo-planar induced 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).

    Args:
        name (Optional[str]): Name of the workflow.

    Inputnode:
        in_t1 (str): T1w image.
        in_dwi (str): DWI dataset

    Outputnode:
        out_dwi (str): Corrected DWI dataset
        out_warp (str): Out warp allowing DWI to T1 registration and
            susceptibilty induced artifacts correction
        out_b0_to_t1_rigid_body_matrix (str): B0 to T1 image FLIRT rigid body
            FSL coregistration matrix
        out_t1_to_b0_rigid_body_matrix (str): T1 to B0 image FLIRT rigid body
            FSL coregistration matrix
        out_t1_coregistered_to_b0 (str): T1 image rigid body coregistered to
            the B0 image
        out_b0_to_t1_syn_deformation_field (str): B0 to T1 image ANTs SyN
            ITK warp
        out_b0_to_t1_affine_matrix (str): B0 to T1 image ANTs affine ITK
            coregistration matrix

    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


    Returns:
        The workflow

    Example:
        >>> epi = susceptibility_distortion_correction_using_t1()
        >>> epi.inputs.inputnode.in_dwi = 'dwi.nii'
        >>> epi.inputs.inputnode.in_t1 = 'T1w.nii'
        >>> epi.run() # doctest: +SKIP
    """
    import nipype
    import nipype.pipeline.engine as pe
    import nipype.interfaces.utility as niu
    import nipype.interfaces.fsl as fsl
    import clinica.pipelines.dwi_preprocessing_using_t1.dwi_preprocessing_using_t1_utils as utils

    def expend_matrix_list(in_matrix, in_bvec):
        import numpy as np

        bvecs = np.loadtxt(in_bvec).T
        out_matrix_list = [in_matrix]

        out_matrix_list = out_matrix_list * len(bvecs)

        return out_matrix_list

    def rotate_bvecs(in_bvec, in_matrix):
        """
        Rotates the input bvec file accordingly with a list of matrices.
        .. note:: the input affine matrix transforms points in the destination
          image to their corresponding coordinates in the original image.
          Therefore, this matrix should be inverted first, as we want to know
          the target position of :math:`\\vec{r}`.
        """
        import os
        import numpy as np

        name, fext = os.path.splitext(os.path.basename(in_bvec))
        if fext == '.gz':
            name, _ = os.path.splitext(name)
        out_file = os.path.abspath('%s_rotated.bvec' % name)
        bvecs = np.loadtxt(
            in_bvec).T  # Warning, bvecs.txt are not in the good configuration, need to put '.T'
        new_bvecs = []

        if len(bvecs) != len(in_matrix):
            raise RuntimeError(('Number of b-vectors (%d) and rotation '
                                'matrices (%d) should match.') %
                               (len(bvecs), len(in_matrix)))

        for bvec, mat in zip(bvecs, in_matrix):
            if np.all(bvec == 0.0):
                new_bvecs.append(bvec)
            else:
                invrot = np.linalg.inv(np.loadtxt(mat))[:3, :3]
                newbvec = invrot.dot(bvec)
                new_bvecs.append((newbvec / np.linalg.norm(newbvec)))

        np.savetxt(out_file, np.array(new_bvecs).T, fmt='%0.15f')
        return out_file

    inputnode = pe.Node(
        niu.IdentityInterface(fields=['in_t1', 'in_dwi', 'in_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_to_t1 = pe.Node(interface=fsl.FLIRT(dof=6),
                            name='flirt_b0_to_t1')
    flirt_b0_to_t1.inputs.interp = "spline"
    flirt_b0_to_t1.inputs.cost = 'normmi'
    flirt_b0_to_t1.inputs.cost_func = 'normmi'

    if nipype.__version__.split('.') < ['0', '13', '0']:
        apply_xfm = pe.Node(interface=fsl.ApplyXfm(),
                            name='apply_xfm')
    else:
        apply_xfm = pe.Node(interface=fsl.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')

    ants_registration_syn_quick = pe.Node(interface=niu.Function(
        input_names=['fix_image', 'moving_image'],
        output_names=['image_warped', 'affine_matrix',
                      'warp', 'inverse_warped', 'inverse_warp'],
        function=utils.ants_registration_syn_quick),
        name='ants_registration_syn_quick')

    merge_transform = pe.Node(niu.Merge(2), name='MergeTransforms')

    combine_warp = pe.Node(interface=niu.Function(
        input_names=['in_file', 'transforms_list', 'reference'],
        output_names=['out_warp'],
        function=utils.ants_combine_transform), name='combine_warp')

    coeffs = pe.Node(fsl.WarpUtils(out_format='spline'), name='CoeffComp')

    fsl_transf = pe.Node(fsl.WarpUtils(out_format='field'),
                         name='fsl_transf')

    warp_epi = pe.Node(fsl.ConvertWarp(), name='warp_epi')

    apply_warp = pe.MapNode(interface=fsl.ApplyWarp(),
                            iterfield=['in_file'], name='apply_warp')
    apply_warp.inputs.interp = 'spline'

    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_to_t1_coregistration_matrix',
                'itk_dwi_t1_coregistration_matrix',
                'epi_correction_deformation_field',
                'epi_correction_affine_transform',
                'merge_epi_transform', 'out_dwi', 'out_warp',
                'out_bvec']), name='outputnode')

    wf = pe.Workflow(name=name)

    wf.connect([
        (inputnode, split, [('in_dwi', 'in_file')]),  # noqa

        (split, pick_ref, [('out_files', 'inlist')]),  # noqa

        (pick_ref,  flirt_b0_to_t1, [('out',     'in_file')]),  # noqa
        (inputnode, flirt_b0_to_t1, [('in_t1', 'reference')]),  # noqa

        (flirt_b0_to_t1, expend_matrix, [('out_matrix_file', 'in_matrix')]),  # noqa
        (inputnode,      expend_matrix, [('in_bvec',           'in_bvec')]),  # noqa

        (inputnode,     rot_bvec, [('in_bvec', 'in_bvec')]),  # noqa
        (expend_matrix, rot_bvec, [('out_matrix_list', 'in_matrix')]),  # noqa
        
        (inputnode,                   ants_registration_syn_quick, [('in_t1',       'fix_image')]),  # noqa
        (flirt_b0_to_t1,              ants_registration_syn_quick, [('out_file', 'moving_image')]),  # noqa

        (ants_registration_syn_quick, merge_transform, [('affine_matrix', 'in2'),  # noqa
                                                        ('warp',          'in1')]),  # noqa

        (flirt_b0_to_t1,  combine_warp, [('out_file',    'in_file')]),  # noqa
        (merge_transform, combine_warp, [('out', 'transforms_list')]),  # noqa
        (inputnode,       combine_warp, [('in_t1',     'reference')]),  # noqa

        (inputnode,    coeffs, [('in_t1',  'reference')]),  # noqa
        (combine_warp, coeffs, [('out_warp', 'in_file')]),  # noqa

        (coeffs,    fsl_transf, [('out_file', 'in_file')]),  # noqa
        (inputnode, fsl_transf, [('in_t1',  'reference')]),  # noqa

        (inputnode,      warp_epi, [('in_t1',        'reference')]),  # noqa
        (flirt_b0_to_t1, warp_epi, [('out_matrix_file', 'premat')]),  # noqa
        (fsl_transf,     warp_epi, [('out_file',         'warp1')]),  # noqa

        (warp_epi,  apply_warp, [('out_file', 'field_file')]),  # noqa
        (split,     apply_warp, [('out_files',   'in_file')]),  # noqa
        (inputnode, apply_warp, [('in_t1',      'ref_file')]),  # noqa

        (apply_warp, thres, [('out_file', 'in_file')]),  # noqa

        (thres, merge, [('out_file', 'in_files')]),  # noqa
        # Outputnode
        (merge,                       outputnode, [('merged_file',                             'out_dwi')]),  # noqa
        (flirt_b0_to_t1,              outputnode, [('out_matrix_file', 'dwi_to_t1_coregistration_matrix')]),  # noqa
        (ants_registration_syn_quick, outputnode, [('warp',             'epi_correction_deformation_field'),  # noqa
                                                   ('affine_matrix',   'epi_correction_affine_transform')]),  # noqa
        (warp_epi,                    outputnode, [('out_file',                               'out_warp')]),  # noqa
        (rot_bvec,                    outputnode, [('out_file',                               'out_bvec')]),  # noqa
    ])
    return wf
Ejemplo n.º 19
0
def create_featreg_preproc(name='featpreproc',
                           whichvol='middle',
                           deletetimepoints=4):
    """Create a FEAT preprocessing workflow with registration to one volume of the first run

    Parameters
    ----------

    name : name of workflow (default: featpreproc)
    whichvol : which volume of the first run to register to ('first', 'middle', 'mean')

    Inputs::

        inputspec.func : functional runs (filename or list of filenames)
        inputspec.fwhm : fwhm for smoothing with SUSAN
        inputspec.highpass : HWHM in TRs (if created with highpass=True)

    Outputs::

        outputspec.reference : volume to which runs are realigned
        outputspec.motion_parameters : motion correction parameters
        outputspec.realigned_files : motion corrected files
        outputspec.motion_plots : plots of motion correction parameters
        outputspec.mask : mask file used to mask the brain
        outputspec.smoothed_files : smoothed functional data
        outputspec.meanscaled_files : meanscaled data

    Example
    -------

    >>> from nipype.workflows.fsl import create_featreg_preproc
    >>> import os
    >>> preproc = create_featreg_preproc()
    >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii']
    >>> preproc.inputs.inputspec.fwhm = 5
    >>> preproc.inputs.inputspec.highpass = 128./(2*2.5)
    >>> preproc.base_dir = '/tmp'
    >>> preproc.run() # doctest: +SKIP

    >>> preproc = create_featreg_preproc(highpass=False, whichvol='mean')
    >>> preproc.inputs.inputspec.func = 'f3.nii'
    >>> preproc.inputs.inputspec.fwhm = 5
    >>> preproc.base_dir = '/tmp'
    >>> preproc.run() # doctest: +SKIP
    """

    featpreproc = pe.Workflow(name=name)

    # Set up a node to define all inputs and outputs required for the
    # preprocessing workflow

    inputnode = pe.Node(interface=util.IdentityInterface(fields=[
        'func',
        'fwhm',
    ]),
                        name='inputspec')
    outputnode = pe.Node(interface=util.IdentityInterface(fields=[
        'reference',
        'motion_parameters',
        'realigned_files',
        'motion_rot_plots',
        'motion_trans_plots',
        'mask',
        'maskfunc_files',
        'smoothed_files',
        'meanscaled_files',
    ]),
                         name='outputspec')

    # Convert functional images to float representation. Since there can
    # be more than one functional run we use a MapNode to convert each
    # run.

    deletevol = pe.MapNode(interface=fsl.ExtractROI(t_min=deletetimepoints),
                           iterfield=['in_file', 't_size'],
                           name='deletevol')
    img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float',
                                                    op_string='',
                                                    suffix='_dtype'),
                           iterfield=['in_file'],
                           name='img2float')
    featpreproc.connect(inputnode, 'func', deletevol, 'in_file')
    featpreproc.connect(inputnode, ('func', getTTP, deletetimepoints),
                        deletevol, 't_size')
    featpreproc.connect(deletevol, 'roi_file', img2float, 'in_file')

    # Extract the first volume of the each run as the reference

    if whichvol != 'mean':
        extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1),
                                 iterfield=['in_file'],
                                 name='extractref')
        featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file')
        featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol),
                            extract_ref, 't_min')
        featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference')

    # Realign the functional runs to the reference (1st volume of first run)

    motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats=True,
                                                      save_plots=True,
                                                      save_rms=True,
                                                      interpolation='sinc'),
                                name='realign',
                                iterfield=['in_file', 'ref_file'])
    featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file')
    if whichvol != 'mean':
        featpreproc.connect(extract_ref, 'roi_file', motion_correct,
                            'ref_file')
    else:
        motion_correct.inputs.mean_vol = True
        featpreproc.connect(motion_correct, 'mean_img', outputnode,
                            'reference')

    featpreproc.connect(motion_correct, 'par_file', outputnode,
                        'motion_parameters')
    featpreproc.connect(motion_correct, 'out_file', outputnode,
                        'realigned_files')

    # Plot the estimated motion parameters

    plot_rotations = pe.MapNode(interface=fsl.PlotMotionParams(
        in_source='fsl', plot_type='rotations'),
                                name='plot_rotations',
                                iterfield=['in_file'])
    plot_translations = pe.MapNode(interface=fsl.PlotMotionParams(
        in_source='fsl', plot_type='translations'),
                                   name='plot_translations',
                                   iterfield=['in_file'])

    # plot_motion.iterables = ('plot_type', ['rotations', 'translations'])
    featpreproc.connect(motion_correct, 'par_file', plot_rotations, 'in_file')
    featpreproc.connect(motion_correct, 'par_file', plot_translations,
                        'in_file')
    featpreproc.connect(plot_rotations, 'out_file', outputnode,
                        'motion_rot_plots')
    featpreproc.connect(plot_translations, 'out_file', outputnode,
                        'motion_trans_plots')

    # Extract the mean volume of the first functional run

    meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                   suffix='_mean'),
                          iterfield=['in_file'],
                          name='meanfunc')
    featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file')

    # Strip the skull from the mean functional to generate a mask

    meanfuncmask = pe.MapNode(interface=fsl.BET(mask=True,
                                                no_output=True,
                                                frac=0.3),
                              iterfield=['in_file'],
                              name='meanfuncmask')

    featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file')

    # Mask the functional runs with the extracted mask

    maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet',
                                                   op_string='-mas'),
                          iterfield=['in_file', 'in_file2'],
                          name='maskfunc')
    featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file')
    featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2')

    # Determine the 2nd and 98th percentile intensities of each functional run

    getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'),
                           iterfield=['in_file'],
                           name='getthreshold')
    featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file')

    # Threshold the first run of the functional data at 10% of the 98th percentile

    threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char',
                                                    suffix='_thresh'),
                           iterfield=['in_file', 'op_string'],
                           name='threshold')
    featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file')

    # Define a function to get 10% of the intensity

    featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold,
                        'op_string')

    # Determine the median value of the functional runs using the mask

    medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                           iterfield=['in_file', 'mask_file'],
                           name='medianval')
    featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file')
    featpreproc.connect(threshold, 'out_file', medianval, 'mask_file')

    # Dilate the mask

    dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil',
                                                     op_string='-dilF'),
                            iterfield=['in_file'],
                            name='dilatemask')
    featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file')
    featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask')

    # Mask the motion corrected functional runs with the dilated mask

    maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                           iterfield=['in_file', 'in_file2'],
                           name='maskfunc2')
    featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file')
    featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2')
    featpreproc.connect(maskfunc2, 'out_file', outputnode, 'maskfunc_files')

    # Smooth each run using SUSAN with the brightness threshold set to 75%
    # of the median value for each run and a mask consituting the mean
    # functional

    smooth = create_susan_smooth()

    featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm')
    featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files')
    featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file')

    # Mask the smoothed data with the dilated mask

    maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                           iterfield=['in_file', 'in_file2'],
                           name='maskfunc3')
    featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3,
                        'in_file')

    featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2')

    concatnode = pe.Node(interface=util.Merge(2), name='concat')
    featpreproc.connect(maskfunc2, ('out_file', tolist), concatnode, 'in1')
    featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2')

    # The following nodes select smooth or unsmoothed data depending on the
    # fwhm. This is because SUSAN defaults to smoothing the data with about the
    # voxel size of the input data if the fwhm parameter is less than 1/3 of the
    # voxel size.

    selectnode = pe.Node(interface=util.Select(), name='select')

    featpreproc.connect(concatnode, 'out', selectnode, 'inlist')

    featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index')
    featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files')

    # Scale the median value of the run is set to 10000

    meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'),
                           iterfield=['in_file', 'op_string'],
                           name='meanscale')

    featpreproc.connect(selectnode, 'out', meanscale, 'in_file')
    featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale,
                        'op_string')

    # Mask the scaled data with the dilated mask

    maskfunc4 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                           iterfield=['in_file', 'in_file2'],
                           name='maskfunc4')

    featpreproc.connect(meanscale, 'out_file', maskfunc4, 'in_file')
    featpreproc.connect(dilatemask, 'out_file', maskfunc4, 'in_file2')
    featpreproc.connect(maskfunc4, 'out_file', outputnode, 'meanscaled_files')

    return featpreproc
Ejemplo n.º 20
0
def init_n4_only_wf(atropos_model=None,
                    atropos_refine=True,
                    atropos_use_random_seed=True,
                    bids_suffix='T1w',
                    mem_gb=3.0,
                    name='n4_only_wf',
                    omp_nthreads=None):
    """
    Build a workflow to sidetrack brain extraction on skull-stripped datasets.

    An alternative workflow to "init_brain_extraction_wf", for anatomical
    images which have already been brain extracted.

      1. Creates brain mask assuming all zero voxels are outside the brain
      2. Applies N4 bias field correction
      3. (Optional) apply ATROPOS and massage its outputs
      4. Use results from 3 to refine N4 bias field correction

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

            from niworkflows.anat.ants import init_n4_only_wf
            wf = init_n4_only_wf()

    Parameters
    ----------
    omp_nthreads : int
        Maximum number of threads an individual process may use
    mem_gb : float
        Estimated peak memory consumption of the most hungry nodes
    bids_suffix : str
        Sequence type of the first input image. For a list of acceptable values see
        https://bids-specification.readthedocs.io/en/latest/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#anatomy-imaging-data
    atropos_refine : bool
        Enables or disables the whole ATROPOS sub-workflow
    atropos_use_random_seed : bool
        Whether ATROPOS should generate a random seed based on the
        system's clock
    atropos_model : tuple or None
        Allows to specify a particular segmentation model, overwriting
        the defaults based on ``bids_suffix``
    name : str, optional
        Workflow name (default: ``'n4_only_wf'``).

    Inputs
    ------
    in_files
        List of input anatomical images to be bias corrected,
        typically T1-weighted.
        If a list of anatomical images is provided, subsequently
        specified images are used during the segmentation process.
        However, only the first image is used in the registration
        of priors.
        Our suggestion would be to specify the T1w as the first image.

    Outputs
    -------
    out_file
        :abbr:`INU (intensity non-uniformity)`-corrected ``in_files``
    out_mask
        Calculated brain mask
    bias_corrected
        Same as "out_file", provided for consistency with brain extraction
    bias_image
        The :abbr:`INU (intensity non-uniformity)` field estimated for each
        input in ``in_files``
    out_segm
        Output segmentation by ATROPOS
    out_tpms
        Output :abbr:`TPMs (tissue probability maps)` by ATROPOS

    """
    wf = pe.Workflow(name)

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

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'out_file', 'out_mask', 'bias_corrected', 'bias_image', 'out_segm',
        'out_tpms'
    ]),
                         name='outputnode')

    # Create brain mask
    thr_brainmask = pe.Node(Binarize(thresh_low=2), name='binarize')

    # INU correction
    inu_n4_final = pe.MapNode(N4BiasFieldCorrection(
        dimension=3,
        save_bias=True,
        copy_header=True,
        n_iterations=[50] * 5,
        convergence_threshold=1e-7,
        shrink_factor=4,
        bspline_fitting_distance=200),
                              n_procs=omp_nthreads,
                              name='inu_n4_final',
                              iterfield=['input_image'])

    # Check ANTs version
    try:
        inu_n4_final.inputs.rescale_intensities = True
    except ValueError:
        warn(
            "The installed ANTs version too old. Please consider upgrading to "
            "2.1.0 or greater.", DeprecationWarning)

    wf.connect([(inputnode, inu_n4_final, [('in_files', 'input_image')]),
                (inputnode, thr_brainmask, [(('in_files', _pop), 'in_file')]),
                (thr_brainmask, outputnode, [('out_mask', 'out_mask')]),
                (inu_n4_final, outputnode, [('output_image', 'out_file')]),
                (inu_n4_final, outputnode, [('output_image', 'bias_corrected')
                                            ]),
                (inu_n4_final, outputnode, [('bias_image', 'bias_image')])])

    # If atropos refine, do in4 twice
    if atropos_refine:
        # Morphological dilation, radius=2
        dil_brainmask = pe.Node(ImageMath(operation='MD', op2='2'),
                                name='dil_brainmask')
        # Get largest connected component
        get_brainmask = pe.Node(ImageMath(operation='GetLargestComponent'),
                                name='get_brainmask')
        atropos_model = atropos_model or list(
            ATROPOS_MODELS[bids_suffix].values())
        atropos_wf = init_atropos_wf(
            use_random_seed=atropos_use_random_seed,
            omp_nthreads=omp_nthreads,
            mem_gb=mem_gb,
            in_segmentation_model=atropos_model,
        )
        sel_wm = pe.Node(niu.Select(index=atropos_model[-1] - 1),
                         name='sel_wm',
                         run_without_submitting=True)

        gunzip_4n4 = pe.MapNode(Gunzip(),
                                name="gunzip_con",
                                iterfield=['in_file'])

        inu_n4 = pe.MapNode(
            Segment(gm_output_type=[False, False, False],
                    wm_output_type=[False, False, False],
                    csf_output_type=[False, False, False],
                    clean_masks="thorough",
                    save_bias_corrected=True,
                    bias_regularization=0.001,
                    bias_fwhm=30,
                    sampling_distance=3,
                    use_mcr=True,
                    affine_regularization="mni"),
            iterfield=['data'],
            name='inu_n4',
            mem_gb=32,
            serial=True,
            run_without_submitting=True)  # n_procs=1 for reproducibility

        wf.connect([
            (inputnode, gunzip_4n4, [('in_files', 'in_file')]),
            (gunzip_4n4, inu_n4, [('out_file', 'data')]),
            (inu_n4, atropos_wf, [('bias_corrected_image',
                                   'inputnode.in_files')]),
            (thr_brainmask, atropos_wf, [('out_mask', 'inputnode.in_mask')]),
            (thr_brainmask, dil_brainmask, [('out_mask', 'op1')]),
            (dil_brainmask, get_brainmask, [('output_image', 'op1')]),
            (get_brainmask, atropos_wf, [('output_image',
                                          'inputnode.in_mask_dilated')]),
            (atropos_wf, sel_wm, [('outputnode.out_tpms', 'inlist')]),
            (sel_wm, inu_n4_final, [('out', 'weight_image')]),
            (atropos_wf, outputnode, [('outputnode.out_segm', 'out_segm'),
                                      ('outputnode.out_tpms', 'out_tpms')]),
        ])

    return wf
Ejemplo n.º 21
0
def bspline(name='BSplineEvaluation', shapes=['gyrus'], snr_list=[300], N=1):
    """ A workflow to evaluate registration methods generating a gold standard
    with random bspline deformations.

    A list of nipype workflows can be plugged-in, using the methods input. If
    methods is None, then a default regseg method is run.


      methods = [identity_wf(n_tissues=2), default_regseg()]

    Inputs in methods workflows
    ---------------------------

    methods workflows must define the following inputs:
        inputnode.in_surf - the input prior / surfaces in orig space
        inputnode.in_dist - the distorted images
        inputnode.in_tpms - the distorted TPMs (tissue probability maps)
        inputnode.in_orig - the original images, undistorted


    Outputs in methods workflows
    ----------------------------

        outputnode.out_corr - the distorted images, after correction
        outputnode.out_tpms - the corrected TPMs
        outputnode.out_surf - the original priors after distortion
          (if available)
        outputnode.out_disp - the displacement field, at image grid resoluton

    """
    wf = pe.Workflow(name=name)
    inputnode = pe.Node(niu.IdentityInterface(fields=[
        'grid_size', 'out_csv', 'lo_matrix', 'rep_id', 'hi_matrix', 'snr',
        'cortex', 'shape'
    ]),
                        name='inputnode')

    inputnode.iterables = [
        ('shape', shapes),
        ('snr', snr_list),
        ('rep_id', list(range(N))),
    ]

    # inputnode.iterables = [('shape', np.atleast_1d(shapes).tolist()),
    #                        ('snr', snr_list),
    #                        ('rep_id', list(range(N)))]

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'out_file', 'out_tpms', 'out_surfs', 'out_field', 'out_coeff',
        'out_overlap'
    ]),
                         name='outputnode')

    phantom = generate_phantom()
    wf.connect([(inputnode, phantom, [('shape', 'inputnode.shape'),
                                      ('grid_size', 'inputnode.grid_size'),
                                      ('lo_matrix', 'inputnode.lo_matrix'),
                                      ('hi_matrix', 'inputnode.hi_matrix'),
                                      ('snr', 'inputnode.snr'),
                                      ('cortex', 'inputnode.cortex'),
                                      ('rep_id', 'inputnode.repetition_id')])])

    regseg_low = default_regseg('REGSEG_low')
    ev_regseg_low = registration_ev(name=('Ev_%s' % regseg_low.name))
    ev_regseg_low.inputs.infonode.method = 'REGSEG'
    ev_regseg_low.inputs.infonode.resolution = 'lo'
    norm_low = pe.Node(Normalize(), name='NormalizeFinal_low')
    export0 = pe.Node(ExportSlices(all_axis=True), name='Export_lo')
    sel0 = pe.Node(niu.Select(index=[0]), name='SelectT1w_lo')

    wf.connect([
        (inputnode, ev_regseg_low, [('shape', 'infonode.shape'),
                                    ('snr', 'infonode.snr'),
                                    ('rep_id', 'infonode.repetition')]),
        (phantom, ev_regseg_low, [('refnode.out_signal', 'refnode.in_imag'),
                                  ('refnode.out_tpms', 'refnode.in_tpms'),
                                  ('out_lowres.out_surfs', 'refnode.in_surf'),
                                  ('refnode.out_mask', 'refnode.in_mask'),
                                  ('out_lowres.out_field', 'refnode.in_field')
                                  ]),
        (phantom, regseg_low, [('refnode.out_surfs', 'inputnode.in_surf'),
                               ('out_lowres.out_signal', 'inputnode.in_fixed'),
                               ('out_lowres.out_mask', 'inputnode.in_mask')]),
        # (regseg_low, norm_low, [
        #     ('outputnode.out_tpms', 'in_files')]),
        (regseg_low, ev_regseg_low,
         [('outputnode.out_corr', 'tstnode.in_imag'),
          ('outputnode.out_surf', 'tstnode.in_surf'),
          ('outputnode.out_field', 'tstnode.in_field')]),
        (norm_low, ev_regseg_low, [('out_files', 'tstnode.in_tpms')]),
        (phantom, sel0, [('out_lowres.out_signal', 'inlist')]),
        (sel0, export0, [('out', 'reference')]),
        (phantom, export0, [('out_lowres.out_surfs', 'sgreen')]),
        (regseg_low, export0, [('outputnode.out_surf', 'syellow')]),
        (inputnode, ev_regseg_low, [('out_csv', 'infonode.out_csv')])
    ])

    regseg_hi = default_regseg('REGSEG_hi')
    ev_regseg_hi = registration_ev(name=('Ev_%s' % regseg_hi.name))
    ev_regseg_hi.inputs.infonode.method = 'REGSEG'
    ev_regseg_hi.inputs.infonode.resolution = 'hi'
    norm_hi = pe.Node(Normalize(), name='NormalizeFinal_hi')
    export1 = pe.Node(ExportSlices(all_axis=True), name='Export_hi')
    sel1 = pe.Node(niu.Select(index=[0]), name='SelectT1w_hi')

    wf.connect([
        (inputnode, ev_regseg_hi, [('shape', 'infonode.shape'),
                                   ('snr', 'infonode.snr'),
                                   ('rep_id', 'infonode.repetition')]),
        (phantom, ev_regseg_hi, [('refnode.out_signal', 'refnode.in_imag'),
                                 ('refnode.out_tpms', 'refnode.in_tpms'),
                                 ('out_hires.out_surfs', 'refnode.in_surf'),
                                 ('refnode.out_mask', 'refnode.in_mask'),
                                 ('out_hires.out_field', 'refnode.in_field')]),
        (phantom, regseg_hi, [('refnode.out_surfs', 'inputnode.in_surf'),
                              ('out_hires.out_signal', 'inputnode.in_fixed'),
                              ('out_hires.out_mask', 'inputnode.in_mask')]),
        # (regseg_hi, norm_hi, [
        #     ('outputnode.out_tpms', 'in_files')]),
        (regseg_hi, ev_regseg_hi, [('outputnode.out_corr', 'tstnode.in_imag'),
                                   ('outputnode.out_surf', 'tstnode.in_surf'),
                                   ('outputnode.out_field', 'tstnode.in_field')
                                   ]),
        (norm_hi, ev_regseg_hi, [('out_files', 'tstnode.in_tpms')]),
        (phantom, sel1, [('out_hires.out_signal', 'inlist')]),
        (sel0, export1, [('out', 'reference')]),
        (phantom, export1, [('out_hires.out_surfs', 'sgreen')]),
        (regseg_hi, export1, [('outputnode.out_surf', 'syellow')]),
        (inputnode, ev_regseg_hi, [('out_csv', 'infonode.out_csv')])
    ])

    # Connect in_field in case it is an identity workflow
    # if 'in_field' in [item[0] for item in reg.inputs.inputnode.items()]:
    #     wf.connect(phantom, 'out_lowres.out_field',
    #                reg, 'inputnode.in_field')

    return wf
Ejemplo n.º 22
0
    def build_core_nodes(self):
        """Build and connect the core nodes of the pipeline.

        Notes:
            - If `FSLOUTPUTTYPE` environment variable is not set, `nipype` takes
            NIFTI by default.

        Todos:
            - [x] Detect space automatically.
            - [ ] Allow for custom parcellations (See TODOs in utils).

        """
        import nipype.interfaces.utility as niu
        import nipype.pipeline.engine as npe
        import nipype.interfaces.fsl as fsl
        import nipype.interfaces.freesurfer as fs
        import nipype.interfaces.mrtrix3 as mrtrix3
        from clinica.lib.nipype.interfaces.mrtrix3.reconst import EstimateFOD
        from clinica.lib.nipype.interfaces.mrtrix3.tracking import Tractography
        from clinica.utils.exceptions import ClinicaCAPSError
        from clinica.utils.stream import cprint
        import clinica.pipelines.dwi_connectome.dwi_connectome_utils as utils

        # cprint('Building the pipeline...')

        # Nodes
        # =====

        # B0 Extraction
        # -------------
        split_node = npe.Node(name="B0Extraction",
                              interface=fsl.Split())
        split_node.inputs.output_type = "NIFTI_GZ"
        split_node.inputs.dimension = 't'
        select_node = npe.Node(name="B0Selection", interface=niu.Select())
        select_node.inputs.index = 0

        # B0 Brain Extraction
        # -------------------
        mask_node = npe.Node(name="BrainMasking",
                             interface=fsl.ApplyMask())
        mask_node.inputs.output_type = "NIFTI_GZ"

        # T1-to-B0 Registration
        # ---------------------
        t12b0_reg_node = npe.Node(name="T12B0Registration",
                                  interface=fsl.FLIRT())
        t12b0_reg_node.inputs.output_type = "NIFTI_GZ"

        # MGZ File Convertion
        # -------------------
        t1_brain_conv_node = npe.Node(name="T1BrainConvertion",
                                      interface=fs.MRIConvert())
        wm_mask_conv_node = npe.Node(name="WMMaskConvertion",
                                     interface=fs.MRIConvert())

        # WM Transformation
        # -----------------
        wm_transform_node = npe.Node(name="WMTransformation",
                                     interface=fsl.ApplyXFM())
        wm_transform_node.inputs.apply_xfm = True

        # Response Estimation
        # -------------------
        resp_estim_node = npe.Node(name="ResponseEstimation",
                                   interface=mrtrix3.ResponseSD())
        resp_estim_node.inputs.algorithm = 'tournier'

        # FOD Estimation
        # --------------
        fod_estim_node = npe.Node(name="FODEstimation",
                                  interface=EstimateFOD())
        fod_estim_node.inputs.algorithm = 'csd'

        # Tracts Generation
        # -----------------
        tck_gen_node = npe.Node(name="TractsGeneration",
                                interface=utils.Tractography())
        tck_gen_node.inputs.n_tracks = self.parameters['n_tracks']
        # BUG: Info package does not exist
        # from nipype.interfaces.mrtrix3.base import Info
        # from distutils.version import LooseVersion
        #
        # if Info.looseversion() >= LooseVersion("3.0"):
        #     tck_gen_node.inputs.select = self.parameters['n_tracks']
        # elif Info.looseversion() <= LooseVersion("0.4"):
        #     tck_gen_node.inputs.n_tracks = self.parameters['n_tracks']
        # else:
        #     from clinica.utils.exceptions import ClinicaException
        #     raise ClinicaException("Your MRtrix version is not supported.")

        # Nodes Generation
        # ----------------
        label_convert_node = npe.MapNode(name="LabelsConversion",
                                         iterfield=['in_file', 'in_config',
                                                    'in_lut', 'out_file'],
                                         interface=mrtrix3.LabelConvert())
        label_convert_node.inputs.in_config = utils.get_conversion_luts()
        label_convert_node.inputs.in_lut = utils.get_luts()

        # Connectome Generation
        # ---------------------
        # only the parcellation and output filename should be iterable, the tck
        # file stays the same.
        conn_gen_node = npe.MapNode(name="ConnectomeGeneration",
                                    iterfield=['in_parc', 'out_file'],
                                    interface=mrtrix3.BuildConnectome())

        # Print begin message
        # -------------------
        print_begin_message = npe.Node(
            interface=niu.Function(
                input_names=['in_bids_or_caps_file'],
                function=utils.print_begin_pipeline),
            iterfield='in_bids_or_caps_file',
            name='WriteBeginMessage')

        # Print end message
        # -----------------
        print_end_message = npe.Node(
            interface=niu.Function(
                input_names=['in_bids_or_caps_file', 'final_file', 'final_file'],
                function=utils.print_end_pipeline),
            iterfield=['in_bids_or_caps_file', 'final_file'],
            name='WriteEndMessage')

        # CAPS FIlenames Generation
        # -------------------------
        caps_filenames_node = npe.Node(name='CAPSFilenamesGeneration',
                                       interface=niu.Function(
                                               input_names='dwi_file',
                                               output_names=self.get_output_fields(),
                                               function=utils.get_caps_filenames))

        # Connections
        # ===========
        self.connect([
            (self.input_node, print_begin_message, [('dwi_file', 'in_bids_or_caps_file')]),  # noqa
        ])
        # WM in B0-space Transformation
        # -----------------------------
        if self.parameters['dwi_space'] == 'b0':
            self.connect([
                # MGZ Files Convertion
                (self.input_node, t1_brain_conv_node,
                 [('t1_brain_file', 'in_file')]),

                # noqa
                (self.input_node, wm_mask_conv_node,
                 [('wm_mask_file', 'in_file')]),
                # noqa
                # B0 Extraction
                (self.input_node, split_node, [('dwi_file', 'in_file')]),
                # noqa
                (split_node, select_node, [('out_files', 'inlist')]),
                # noqa
                # Masking
                (select_node, mask_node, [('out', 'in_file')]),  # B0 #noqa
                (self.input_node, mask_node,
                 [('dwi_brainmask_file', 'mask_file')]),
                # Brain mask #noqa #noqa
                # T1-to-B0 Registration
                (t1_brain_conv_node, t12b0_reg_node, [('out_file', 'in_file')]),
                # Brain #noqa
                (mask_node, t12b0_reg_node, [('out_file', 'reference')]),
                # B0 brain-masked #noqa
                # WM Transformation
                (wm_mask_conv_node, wm_transform_node,
                 [('out_file', 'in_file')]),  # Brain mask #noqa
                (mask_node, wm_transform_node, [('out_file', 'reference')]),
                # BO brain-masked #noqa
                (t12b0_reg_node, wm_transform_node,
                 [('out_matrix_file', 'in_matrix_file')]),
                # T1-to-B0 matrix file #noqa
            ])

        # Tractography
        # ------------
        self.connect([
            (self.input_node, caps_filenames_node, [('dwi_file', 'dwi_file')]),
            # Response Estimation
            (self.input_node, resp_estim_node, [('dwi_file', 'in_file')]),
            # Preproc. DWI #noqa
            (self.input_node, resp_estim_node,
             [('dwi_brainmask_file', 'in_mask')]),  # B0 brain mask #noqa
            (self.input_node, resp_estim_node, [('grad_fsl', 'grad_fsl')]),
            # bvecs and bvals #noqa
            (caps_filenames_node, resp_estim_node, [('response', 'wm_file')]),
            # output response filename #noqa
            # FOD Estimation
            (self.input_node, fod_estim_node, [('dwi_file', 'in_file')]),
            # Preproc. DWI #noqa
            (resp_estim_node, fod_estim_node, [('wm_file', 'wm_txt')]),
            # Response (txt file) #noqa
            (self.input_node, fod_estim_node,
             [('dwi_brainmask_file', 'mask_file')]),  # B0 brain mask #noqa
            (self.input_node, fod_estim_node, [('grad_fsl', 'grad_fsl')]),
            # T1-to-B0 matrix file #noqa
            (caps_filenames_node, fod_estim_node, [('fod', 'wm_odf')]),
            # output odf filename #noqa
            # Tracts Generation
            (fod_estim_node, tck_gen_node, [('wm_odf', 'in_file')]),
            # ODF file #noqa
            (caps_filenames_node, tck_gen_node, [('tracts', 'out_file')]),
            # output tck filename #noqa
            # Label Conversion
            (self.input_node, label_convert_node, [('atlas_files', 'in_file')]),
            # atlas image files #noqa
            (caps_filenames_node, label_convert_node, [('nodes', 'out_file')]),
            # converted atlas image filenames #noqa
            # Connectomes Generation
            (tck_gen_node, conn_gen_node, [('out_file', 'in_file')]),
            # output odf filename #noqa
            (label_convert_node, conn_gen_node, [('out_file', 'in_parc')]),
            # output odf filename #noqa
            (caps_filenames_node, conn_gen_node, [('connectomes', 'out_file')]),
            # output odf filename #noqa
        ])

        if self.parameters['dwi_space'] == 'b0':
            self.connect([
                (wm_transform_node, tck_gen_node, [('out_file', 'seed_image')])
                # ODF file #noqa
            ])
        elif self.parameters['dwi_space'] == 'T1w':
            self.connect([
                (
                    self.input_node, tck_gen_node,
                    [('wm_mask_file', 'seed_image')])
                # ODF file #noqa
            ])
        else:
            raise ClinicaCAPSError(
                    'Bad preprocessed DWI space. Please check your CAPS '
                    'folder.')

        # Outputs
        # ------
        self.connect([
            (resp_estim_node, self.output_node, [('wm_file', 'response')]),
            # T1-to-B0 matrix file #noqa
            (fod_estim_node, self.output_node, [('wm_odf', 'fod')]),
            # T1-to-B0 matrix file #noqa
            (tck_gen_node, self.output_node, [('out_file', 'tracts')]),
            # T1-to-B0 matrix file #noqa
            (label_convert_node, self.output_node, [('out_file', 'nodes')]),
            # T1-to-B0 matrix file #noqa
            (conn_gen_node, self.output_node, [('out_file', 'connectomes')]),
            # T1-to-B0 matrix file #noqa
            (self.input_node, print_end_message, [('dwi_file', 'in_bids_or_caps_file')]),  # noqa
            (conn_gen_node,   print_end_message, [('out_file', 'final_file')]),  # noqa
        ])
Ejemplo n.º 23
0
def init_bbreg_wf(use_bbr,
                  asl2t1w_dof,
                  asl2t1w_init,
                  omp_nthreads,
                  name='bbreg_wf'):
    """
    Build a workflow to run FreeSurfer's ``bbregister``.

    This workflow uses FreeSurfer's ``bbregister`` to register a ASL image to
    a T1-weighted structural image.

    It is a counterpart to :py:func:`~aslprep.workflows.asl.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 aslprep.workflows.asl.registration import init_bbreg_wf
            wf = init_bbreg_wf(use_bbr=True, asl2t1w_dof=9,
                               asl2t1w_init='register', omp_nthreads=1)


    Parameters
    ----------
    use_bbr : :obj:`bool` or None
        Enable/disable boundary-based registration refinement.
        If ``None``, test BBR result for distortion before accepting.
    asl2t1w_dof : 6, 9 or 12
        Degrees-of-freedom for ASL-T1w registration
    asl2t1w_init : str, 'header' or 'register'
        If ``'header'``, use header information for initialization of ASL and T1 images.
        If ``'register'``, align volumes by their centers.
    name : :obj:`str`, optional
        Workflow name (default: bbreg_wf)

    Inputs
    ------
    in_file
        Reference ASL 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:`~aslprep.workflows.asl.registration.init_fsl_bbr_wf`)
    t1w_dseg
        Unused (see :py:func:`~aslprep.workflows.asl.registration.init_fsl_bbr_wf`)

    Outputs
    -------
    itk_asl_to_t1
        Affine transform from ``ref_asl_brain`` to T1 space (ITK format)
    itk_t1_to_asl
        Affine transform from T1 space to ASL space (ITK format)
    out_report
        Reportlet for assessing registration quality
    fallback
        Boolean indicating whether BBR was rejected (mri_coreg registration returned)

    """
    from ...niworkflows.engine.workflows import LiterateWorkflow as Workflow

    from ...niworkflows.interfaces.freesurfer import (
        PatchedBBRegisterRPT as BBRegisterRPT, PatchedMRICoregRPT as
        MRICoregRPT, PatchedLTAConvert as LTAConvert)
    from ...niworkflows.interfaces.nitransforms import ConcatenateXFMs

    workflow = Workflow(name=name)
    workflow.__desc__ = """\
The ASL 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'
    }[asl2t1w_dof],
           reason='' if asl2t1w_dof == 6 else
           'to account for distortions remaining in the ASL 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_asl_to_t1', 'itk_t1_to_asl', 'out_report', 'fallback']),
                         name='outputnode')

    if asl2t1w_init not in ("register", "header"):
        raise ValueError(
            f"Unknown ASL-T1w initialization option: {asl2t1w_init}")

    # For now make BBR unconditional - in the future, we can fall back to identity,
    # but adding the flexibility without testing seems a bit dangerous
    if asl2t1w_init == "header":
        if use_bbr is False:
            raise ValueError("Cannot disable BBR and use header registration")
        if use_bbr is None:
            LOGGER.warning(
                "Initializing BBR with header; affine fallback disabled")
            use_bbr = True

    merge_ltas = pe.Node(niu.Merge(2),
                         name='merge_ltas',
                         run_without_submitting=True)
    concat_xfm = pe.Node(ConcatenateXFMs(inverse=True), name='concat_xfm')

    workflow.connect([
        # Output ITK transforms
        (inputnode, merge_ltas, [('fsnative2t1w_xfm', 'in2')]),
        (merge_ltas, concat_xfm, [('out', 'in_xfms')]),
        (concat_xfm, outputnode, [('out_xfm', 'itk_asl_to_t1')]),
        (concat_xfm, outputnode, [('out_inv', 'itk_t1_to_asl')]),
    ])

    # Define both nodes, but only connect conditionally
    mri_coreg = pe.Node(MRICoregRPT(dof=asl2t1w_dof,
                                    sep=[4],
                                    ftol=0.0001,
                                    linmintol=0.01,
                                    generate_report=not use_bbr),
                        name='mri_coreg',
                        n_procs=omp_nthreads,
                        mem_gb=5)

    bbregister = pe.Node(BBRegisterRPT(dof=asl2t1w_dof,
                                       contrast_type='t2',
                                       registered_file=True,
                                       out_lta_file=True,
                                       generate_report=True),
                         name='bbregister',
                         mem_gb=12)

    # Use mri_coreg
    if asl2t1w_init == "register":
        workflow.connect([
            (inputnode, mri_coreg, [('subjects_dir', 'subjects_dir'),
                                    ('subject_id', 'subject_id'),
                                    ('in_file', 'source_file')]),
        ])

        # Short-circuit workflow building, use initial registration
        if use_bbr is False:
            workflow.connect([
                (mri_coreg, outputnode, [('out_report', 'out_report')]),
                (mri_coreg, merge_ltas, [('out_lta_file', 'in1')])
            ])
            outputnode.inputs.fallback = True

            return workflow

    # Use bbregister
    workflow.connect([
        (inputnode, bbregister, [('subjects_dir', 'subjects_dir'),
                                 ('subject_id', 'subject_id'),
                                 ('in_file', 'source_file')]),
    ])

    if asl2t1w_init == "header":
        bbregister.inputs.init = "header"
    else:
        workflow.connect([(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, merge_ltas, [('out_lta_file', 'in1')])])
        outputnode.inputs.fallback = False

        return workflow

    # Only reach this point if asl2t1w_init is "register" and use_bbr is None

    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, merge_ltas, [('out', 'in1')]),
        # 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
Ejemplo n.º 24
0
def create_parallelfeat_preproc(name='featpreproc', highpass=True):
    """Preprocess each run with FSL independently of the others

    Parameters
    ----------

    ::

      name : name of workflow (default: featpreproc)
      highpass : boolean (default: True)

    Inputs::

        inputspec.func : functional runs (filename or list of filenames)
        inputspec.fwhm : fwhm for smoothing with SUSAN
        inputspec.highpass : HWHM in TRs (if created with highpass=True)

    Outputs::

        outputspec.reference : volume to which runs are realigned
        outputspec.motion_parameters : motion correction parameters
        outputspec.realigned_files : motion corrected files
        outputspec.motion_plots : plots of motion correction parameters
        outputspec.mask : mask file used to mask the brain
        outputspec.smoothed_files : smoothed functional data
        outputspec.highpassed_files : highpassed functional data (if highpass=True)
        outputspec.mean : mean file

    Example
    -------

    >>> preproc = create_parallelfeat_preproc()
    >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii']
    >>> preproc.inputs.inputspec.fwhm = 5
    >>> preproc.inputs.inputspec.highpass = 128./(2*2.5)
    >>> preproc.base_dir = '/tmp'
    >>> preproc.run() # doctest: +SKIP

    >>> preproc = create_parallelfeat_preproc(highpass=False)
    >>> preproc.inputs.inputspec.func = 'f3.nii'
    >>> preproc.inputs.inputspec.fwhm = 5
    >>> preproc.base_dir = '/tmp'
    >>> preproc.run() # doctest: +SKIP
    """

    featpreproc = pe.Workflow(name=name)

    """
    Set up a node to define all inputs required for the preprocessing workflow

    """

    if highpass:
        inputnode = pe.Node(interface=util.IdentityInterface(fields=['func',
                                                                     'fwhm',
                                                                     'highpass']),
                            name='inputspec')
        outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference',
                                                                  'motion_parameters',
                                                                  'realigned_files',
                                                                  'motion_plots',
                                                                  'mask',
                                                                  'smoothed_files',
                                                                  'highpassed_files',
                                                                  'mean']),
                         name='outputspec')
    else:
        inputnode = pe.Node(interface=util.IdentityInterface(fields=['func',
                                                                     'fwhm']),
                            name='inputspec')
        outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference',
                                                                  'motion_parameters',
                                                                  'realigned_files',
                                                                  'motion_plots',
                                                                  'mask',
                                                                  'smoothed_files',
                                                                  'mean']),
                         name='outputspec')

    """
    Set up a node to define outputs for the preprocessing workflow

    """

    """
    Convert functional images to float representation. Since there can
    be more than one functional run we use a MapNode to convert each
    run.
    """

    img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float',
                                                 op_string = '',
                                                 suffix='_dtype'),
                           iterfield=['in_file'],
                           name='img2float')
    featpreproc.connect(inputnode, 'func', img2float, 'in_file')

    """
    Extract the first volume of the first run as the reference
    """

    extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1),
                             iterfield=['in_file', 't_min'],
                             name = 'extractref')

    featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file')
    featpreproc.connect(img2float, ('out_file', pickmiddle), extract_ref, 't_min')
    featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference')

    """
    Realign the functional runs to the reference (1st volume of first run)
    """

    motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True,
                                                      save_plots = True),
                                name='realign',
                                iterfield = ['in_file', 'ref_file'])
    featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file')
    featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file')
    featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters')
    featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files')

    """
    Plot the estimated motion parameters
    """

    plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'),
                            name='plot_motion',
                            iterfield=['in_file'])
    plot_motion.iterables = ('plot_type', ['rotations', 'translations'])
    featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file')
    featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots')

    """
    Extract the mean volume of the first functional run
    """

    meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string = '-Tmean',
                                                   suffix='_mean'),
                          iterfield=['in_file'],
                          name='meanfunc')
    featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file')

    """
    Strip the skull from the mean functional to generate a mask
    """

    meanfuncmask = pe.MapNode(interface=fsl.BET(mask = True,
                                             no_output=True,
                                             frac = 0.3),
                              iterfield=['in_file'],
                              name = 'meanfuncmask')
    featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file')

    """
    Mask the functional runs with the extracted mask
    """

    maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet',
                                                   op_string='-mas'),
                          iterfield=['in_file', 'in_file2'],
                          name = 'maskfunc')
    featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file')
    featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2')


    """
    Determine the 2nd and 98th percentile intensities of each functional run
    """

    getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'),
                           iterfield = ['in_file'],
                           name='getthreshold')
    featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file')


    """
    Threshold the first run of the functional data at 10% of the 98th percentile
    """

    threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char',
                                                 suffix='_thresh'),
                           iterfield=['in_file', 'op_string'],
                           name='threshold')
    featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file')

    """
    Define a function to get 10% of the intensity
    """

    featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string')

    """
    Determine the median value of the functional runs using the mask
    """

    medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                           iterfield = ['in_file', 'mask_file'],
                           name='medianval')
    featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file')
    featpreproc.connect(threshold, 'out_file', medianval, 'mask_file')

    """
    Dilate the mask
    """

    dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil',
                                                  op_string='-dilF'),
                            iterfield=['in_file'],
                            name='dilatemask')
    featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file')
    featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask')

    """
    Mask the motion corrected functional runs with the dilated mask
    """

    maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                          iterfield=['in_file', 'in_file2'],
                          name='maskfunc2')
    featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file')
    featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2')

    """
    Smooth each run using SUSAN with the brightness threshold set to 75%
    of the median value for each run and a mask consituting the mean
    functional
    """

    smooth = create_susan_smooth()

    featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm')
    featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files')
    featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file')

    """
    Mask the smoothed data with the dilated mask
    """

    maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                          iterfield=['in_file', 'in_file2'],
                          name='maskfunc3')
    featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file')

    featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2')


    concatnode = pe.Node(interface=util.Merge(2),
                         name='concat')
    featpreproc.connect(maskfunc2,('out_file', tolist), concatnode, 'in1')
    featpreproc.connect(maskfunc3,('out_file', tolist), concatnode, 'in2')

    """
    The following nodes select smooth or unsmoothed data depending on the
    fwhm. This is because SUSAN defaults to smoothing the data with about the
    voxel size of the input data if the fwhm parameter is less than 1/3 of the
    voxel size.
    """
    selectnode = pe.Node(interface=util.Select(),name='select')

    featpreproc.connect(concatnode, 'out', selectnode, 'inlist')

    featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index')
    featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files')


    """
    Scale the median value of the run is set to 10000
    """

    meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'),
                          iterfield=['in_file','op_string'],
                          name='meanscale')
    featpreproc.connect(selectnode, 'out', meanscale, 'in_file')

    """
    Define a function to get the scaling factor for intensity normalization
    """

    featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string')

    """
    Perform temporal highpass filtering on the data
    """

    if highpass:
        highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'),
                              iterfield=['in_file'],
                              name='highpass')
        featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string')
        featpreproc.connect(meanscale, 'out_file', highpass, 'in_file')
        featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files')

    """
    Generate a mean functional image from the first run
    """

    meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                    suffix='_mean'),
                           iterfield=['in_file'],
                          name='meanfunc3')
    if highpass:
        featpreproc.connect(highpass, 'out_file', meanfunc3, 'in_file')
    else:
        featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file')

    featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean')


    return featpreproc
Ejemplo n.º 25
0
def init_anat_template_wf(*,
                          longitudinal,
                          omp_nthreads,
                          num_t1w,
                          name='anat_template_wf'):
    """
    Generate a canonically-oriented, structural average from all input T1w images.

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

            from smriprep.workflows.anatomical import init_anat_template_wf
            wf = init_anat_template_wf(
                longitudinal=False, omp_nthreads=1, num_t1w=1)

    Parameters
    ----------
    longitudinal : :obj:`bool`
        Create unbiased structural average, regardless of number of inputs
        (may increase runtime)
    omp_nthreads : :obj:`int`
        Maximum number of threads an individual process may use
    num_t1w : :obj:`int`
        Number of T1w images
    name : :obj:`str`, optional
        Workflow name (default: anat_template_wf)

    Inputs
    ------
    t1w
        List of T1-weighted structural images

    Outputs
    -------
    t1w_ref
        Structural reference averaging input T1w images, defining the T1w space.
    t1w_realign_xfm
        List of affine transforms to realign input T1w images
    out_report
        Conformation report

    """
    workflow = Workflow(name=name)

    if num_t1w > 1:
        workflow.__desc__ = """\
A T1w-reference map was computed after registration of
{num_t1w} T1w images (after INU-correction) using
`mri_robust_template` [FreeSurfer {fs_ver}, @fs_template].
""".format(num_t1w=num_t1w, fs_ver=fs.Info().looseversion() or '<ver>')

    inputnode = pe.Node(niu.IdentityInterface(fields=['t1w']),
                        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(
        fields=['t1w_ref', 't1w_valid_list', 't1w_realign_xfm', 'out_report']),
                         name='outputnode')

    # 0. Reorient T1w image(s) to RAS and resample to common voxel space
    t1w_ref_dimensions = pe.Node(TemplateDimensions(),
                                 name='t1w_ref_dimensions')
    t1w_conform = pe.MapNode(Conform(),
                             iterfield='in_file',
                             name='t1w_conform')

    workflow.connect([
        (inputnode, t1w_ref_dimensions, [('t1w', 't1w_list')]),
        (t1w_ref_dimensions, t1w_conform, [('t1w_valid_list', 'in_file'),
                                           ('target_zooms', 'target_zooms'),
                                           ('target_shape', 'target_shape')]),
        (t1w_ref_dimensions, outputnode, [('out_report', 'out_report'),
                                          ('t1w_valid_list', 't1w_valid_list')
                                          ]),
    ])

    if num_t1w == 1:
        get1st = pe.Node(niu.Select(index=[0]), name='get1st')
        outputnode.inputs.t1w_realign_xfm = [
            pkgr('smriprep', 'data/itkIdentityTransform.txt')
        ]

        workflow.connect([
            (t1w_conform, get1st, [('out_file', 'inlist')]),
            (get1st, outputnode, [('out', 't1w_ref')]),
        ])

        return workflow

    t1w_conform_xfm = pe.MapNode(LTAConvert(in_lta='identity.nofile',
                                            out_lta=True),
                                 iterfield=['source_file', 'target_file'],
                                 name='t1w_conform_xfm')

    # 1. Template (only if several T1w images)
    # 1a. Correct for bias field: the bias field is an additive factor
    #     in log-transformed intensity units. Therefore, it is not a linear
    #     combination of fields and N4 fails with merged images.
    # 1b. Align and merge if several T1w images are provided
    n4_correct = pe.MapNode(N4BiasFieldCorrection(dimension=3,
                                                  copy_header=True),
                            iterfield='input_image',
                            name='n4_correct',
                            n_procs=1)  # n_procs=1 for reproducibility
    # StructuralReference is fs.RobustTemplate if > 1 volume, copying otherwise
    t1w_merge = pe.Node(
        StructuralReference(
            auto_detect_sensitivity=True,
            initial_timepoint=1,  # For deterministic behavior
            intensity_scaling=True,  # 7-DOF (rigid + intensity)
            subsample_threshold=200,
            fixed_timepoint=not longitudinal,
            no_iteration=not longitudinal,
            transform_outputs=True,
        ),
        mem_gb=2 * num_t1w - 1,
        name='t1w_merge')

    # 2. Reorient template to RAS, if needed (mri_robust_template may set to LIA)
    t1w_reorient = pe.Node(image.Reorient(), name='t1w_reorient')

    merge_xfm = pe.MapNode(niu.Merge(2),
                           name='merge_xfm',
                           iterfield=['in1', 'in2'],
                           run_without_submitting=True)
    concat_xfms = pe.MapNode(ConcatenateXFMs(inverse=True),
                             name="concat_xfms",
                             iterfield=['in_xfms'],
                             run_without_submitting=True)

    def _set_threads(in_list, maximum):
        return min(len(in_list), maximum)

    workflow.connect([
        (t1w_ref_dimensions, t1w_conform_xfm, [('t1w_valid_list',
                                                'source_file')]),
        (t1w_conform, t1w_conform_xfm, [('out_file', 'target_file')]),
        (t1w_conform, n4_correct, [('out_file', 'input_image')]),
        (t1w_conform, t1w_merge,
         [(('out_file', _set_threads, omp_nthreads), 'num_threads'),
          (('out_file', add_suffix, '_template'), 'out_file')]),
        (n4_correct, t1w_merge, [('output_image', 'in_files')]),
        (t1w_merge, t1w_reorient, [('out_file', 'in_file')]),
        # Combine orientation and template transforms
        (t1w_conform_xfm, merge_xfm, [('out_lta', 'in1')]),
        (t1w_merge, merge_xfm, [('transform_outputs', 'in2')]),
        (merge_xfm, concat_xfms, [('out', 'in_xfms')]),
        # Output
        (t1w_reorient, outputnode, [('out_file', 't1w_ref')]),
        (concat_xfms, outputnode, [('out_xfm', 't1w_realign_xfm')]),
    ])

    return workflow
Ejemplo n.º 26
0
def create_fsl_fs_preproc(name='preproc', highpass=True, whichvol='middle'):
    """Create a FEAT preprocessing workflow together with freesurfer

    Parameters
    ----------

    ::

        name : name of workflow (default: preproc)
        highpass : boolean (default: True)
        whichvol : which volume of the first run to register to ('first', 'middle', 'mean')

    Inputs::

        inputspec.func : functional runs (filename or list of filenames)
        inputspec.fwhm : fwhm for smoothing with SUSAN
        inputspec.highpass : HWHM in TRs (if created with highpass=True)
        inputspec.subject_id : freesurfer subject id
        inputspec.subjects_dir : freesurfer subjects dir

    Outputs::

        outputspec.reference : volume to which runs are realigned
        outputspec.motion_parameters : motion correction parameters
        outputspec.realigned_files : motion corrected files
        outputspec.motion_plots : plots of motion correction parameters
        outputspec.mask_file : mask file used to mask the brain
        outputspec.smoothed_files : smoothed functional data
        outputspec.highpassed_files : highpassed functional data (if highpass=True)
        outputspec.reg_file : bbregister registration files
        outputspec.reg_cost : bbregister registration cost files

    Example
    -------

    >>> preproc = create_fsl_fs_preproc(whichvol='first')
    >>> preproc.inputs.inputspec.highpass = 128./(2*2.5)
    >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii']
    >>> preproc.inputs.inputspec.subjects_dir = '.'
    >>> preproc.inputs.inputspec.subject_id = 's1'
    >>> preproc.inputs.inputspec.fwhm = 6
    >>> preproc.run() # doctest: +SKIP
    """

    featpreproc = pe.Workflow(name=name)

    """
    Set up a node to define all inputs required for the preprocessing workflow

    """

    if highpass:
        inputnode = pe.Node(interface=util.IdentityInterface(fields=['func',
                                                                     'fwhm',
                                                                     'subject_id',
                                                                     'subjects_dir',
                                                                     'highpass']),
                            name='inputspec')
        outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference',
                                                                  'motion_parameters',
                                                                  'realigned_files',
                                                                  'motion_plots',
                                                                  'mask_file',
                                                                  'smoothed_files',
                                                                  'highpassed_files',
                                                                  'reg_file',
                                                                  'reg_cost'
                                                                  ]),
                         name='outputspec')
    else:
        inputnode = pe.Node(interface=util.IdentityInterface(fields=['func',
                                                                     'fwhm',
                                                                     'subject_id',
                                                                     'subjects_dir'
                                                                     ]),
                            name='inputspec')
        outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference',
                                                                  'motion_parameters',
                                                                  'realigned_files',
                                                                  'motion_plots',
                                                                  'mask_file',
                                                                  'smoothed_files',
                                                                  'reg_file',
                                                                  'reg_cost'
                                                                  ]),
                         name='outputspec')

    """
    Set up a node to define outputs for the preprocessing workflow

    """

    """
    Convert functional images to float representation. Since there can
    be more than one functional run we use a MapNode to convert each
    run.
    """

    img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float',
                                                 op_string = '',
                                                 suffix='_dtype'),
                           iterfield=['in_file'],
                           name='img2float')
    featpreproc.connect(inputnode, 'func', img2float, 'in_file')


    """
    Extract the first volume of the first run as the reference
    """

    if whichvol != 'mean':
        extract_ref = pe.Node(interface=fsl.ExtractROI(t_size=1),
                                 iterfield=['in_file'],
                                 name = 'extractref')
        featpreproc.connect(img2float, ('out_file', pickfirst), extract_ref, 'in_file')
        featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min')
        featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference')


    """
    Realign the functional runs to the reference (1st volume of first run)
    """

    motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True,
                                                      save_plots = True,
                                                      interpolation = 'sinc'),
                                name='realign',
                                iterfield = ['in_file'])
    featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file')
    if whichvol != 'mean':
        featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file')
    else:
        motion_correct.inputs.mean_vol = True
        featpreproc.connect(motion_correct, 'mean_img', outputnode, 'reference')

    featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters')
    featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files')

    """
    Plot the estimated motion parameters
    """

    plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'),
                            name='plot_motion',
                            iterfield=['in_file'])
    plot_motion.iterables = ('plot_type', ['rotations', 'translations'])
    featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file')
    featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots')

    """Get the mask from subject for each run
    """

    maskflow = create_getmask_flow()
    featpreproc.connect([(inputnode, maskflow, [('subject_id','inputspec.subject_id'),
                                             ('subjects_dir', 'inputspec.subjects_dir')])])
    maskflow.inputs.inputspec.contrast_type = 't2'
    if whichvol != 'mean':
        featpreproc.connect(extract_ref, 'roi_file', maskflow, 'inputspec.source_file')
    else:
        featpreproc.connect(motion_correct, ('mean_img', pickfirst), maskflow, 'inputspec.source_file')


    """
    Mask the functional runs with the extracted mask
    """

    maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet',
                                                   op_string='-mas'),
                          iterfield=['in_file'],
                          name = 'maskfunc')
    featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file')
    featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc, 'in_file2')

    """
    Smooth each run using SUSAN with the brightness threshold set to 75%
    of the median value for each run and a mask consituting the mean
    functional
    """

    smooth = create_susan_smooth(separate_masks=False)

    featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm')
    featpreproc.connect(maskfunc, 'out_file', smooth, 'inputnode.in_files')
    featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), smooth, 'inputnode.mask_file')

    """
    Mask the smoothed data with the dilated mask
    """

    maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                    op_string='-mas'),
                          iterfield=['in_file'],
                          name='maskfunc3')
    featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file')
    featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc3, 'in_file2')


    concatnode = pe.Node(interface=util.Merge(2),
                         name='concat')
    featpreproc.connect(maskfunc, ('out_file', tolist), concatnode, 'in1')
    featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2')

    """
    The following nodes select smooth or unsmoothed data depending on the
    fwhm. This is because SUSAN defaults to smoothing the data with about the
    voxel size of the input data if the fwhm parameter is less than 1/3 of the
    voxel size.
    """
    selectnode = pe.Node(interface=util.Select(),name='select')

    featpreproc.connect(concatnode, 'out', selectnode, 'inlist')

    featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index')
    featpreproc.connect(selectnode, 'out', outputnode, 'smoothed_files')


    """
    Scale the median value of the run is set to 10000
    """

    meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'),
                          iterfield=['in_file','op_string'],
                          name='meanscale')
    featpreproc.connect(selectnode, 'out', meanscale, 'in_file')

    """
    Determine the median value of the functional runs using the mask
    """

    medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                           iterfield = ['in_file'],
                           name='medianval')
    featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file')
    featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), medianval, 'mask_file')

    """
    Define a function to get the scaling factor for intensity normalization
    """

    featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string')

    """
    Perform temporal highpass filtering on the data
    """

    if highpass:
        highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'),
                              iterfield=['in_file'],
                              name='highpass')
        featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string')
        featpreproc.connect(meanscale, 'out_file', highpass, 'in_file')
        featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files')

    featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), outputnode, 'mask_file')
    featpreproc.connect(maskflow, 'outputspec.reg_file', outputnode, 'reg_file')
    featpreproc.connect(maskflow, 'outputspec.reg_cost', outputnode, 'reg_cost')

    return featpreproc
Ejemplo n.º 27
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
Ejemplo n.º 28
0
def apply_all_corrections(name='UnwarpArtifacts'):
    """
    Combines two lists of linear transforms with the deformation field
    map obtained typically after the SDC process.
    Additionally, computes the corresponding bspline coefficients and
    the map of determinants of the jacobian.
    """

    inputnode = pe.Node(
        niu.IdentityInterface(fields=['in_sdc', 'in_hmc', 'in_ecc', 'in_dwi']),
        name='inputnode')
    outputnode = pe.Node(niu.IdentityInterface(
        fields=['out_file', 'out_warp', 'out_coeff', 'out_jacobian']),
                         name='outputnode')
    warps = pe.MapNode(fsl.ConvertWarp(relwarp=True),
                       iterfield=['premat', 'postmat'],
                       name='ConvertWarp')

    selref = pe.Node(niu.Select(index=[0]), name='Reference')

    split = pe.Node(fsl.Split(dimension='t'), name='SplitDWIs')
    unwarp = pe.MapNode(fsl.ApplyWarp(),
                        iterfield=['in_file', 'field_file'],
                        name='UnwarpDWIs')

    coeffs = pe.MapNode(fsl.WarpUtils(out_format='spline'),
                        iterfield=['in_file'],
                        name='CoeffComp')
    jacobian = pe.MapNode(fsl.WarpUtils(write_jacobian=True),
                          iterfield=['in_file'],
                          name='JacobianComp')
    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')

    wf = pe.Workflow(name=name)
    wf.connect([(inputnode, warps, [('in_sdc', 'warp1'), ('in_hmc', 'premat'),
                                    ('in_ecc', 'postmat'),
                                    ('in_dwi', 'reference')]),
                (inputnode, split, [('in_dwi', 'in_file')]),
                (split, selref, [('out_files', 'inlist')]),
                (warps, unwarp, [('out_file', 'field_file')]),
                (split, unwarp, [('out_files', 'in_file')]),
                (selref, unwarp, [('out', 'ref_file')]),
                (selref, coeffs, [('out', 'reference')]),
                (warps, coeffs, [('out_file', 'in_file')]),
                (selref, jacobian, [('out', 'reference')]),
                (coeffs, jacobian, [('out_file', 'in_file')]),
                (unwarp, jacmult, [('out_file', 'in_file')]),
                (jacobian, jacmult, [('out_jacobian', 'operand_files')]),
                (jacmult, thres, [('out_file', 'in_file')]),
                (thres, merge, [('out_file', 'in_files')]),
                (warps, outputnode, [('out_file', 'out_warp')]),
                (coeffs, outputnode, [('out_file', 'out_coeff')]),
                (jacobian, outputnode, [('out_jacobian', 'out_jacobian')]),
                (merge, outputnode, [('merged_file', 'out_file')])])
    return wf
Ejemplo n.º 29
0
def create_fixedfx(c, first_c=foo0, name='fixedfx'):
    from nipype.workflows.fmri.fsl.estimate import create_fixed_effects_flow
    import nipype.interfaces.io as nio  # i/o routines
    import nipype.interfaces.utility as util  # utility
    import nipype.pipeline.engine as pe  # pypeline engine
    selectnode = pe.Node(interface=util.IdentityInterface(fields=['runs']),
                         name='idselect')

    selectnode.iterables = ('runs', [range(0,
                                           c.num_runs)])  # this is really bad.

    copeselect = pe.MapNode(interface=util.Select(),
                            name='copeselect',
                            iterfield=['inlist'])

    varcopeselect = pe.MapNode(interface=util.Select(),
                               name='varcopeselect',
                               iterfield=['inlist'])

    dofselect = pe.Node(interface=util.Select(), name='dofselect')
    """if c.test_mode:
        infosource = pe.Node(interface=util.IdentityInterface(fields=['subject_id','fwhm']), name="infosource")
        infosource.iterables = [('subject_id', [c.subjects[0]]),
                                ('fwhm',prep_c.fwhm)]
    else:
        infosource = pe.Node(interface=util.IdentityInterface(fields=['subject_id','fwhm']), name="infosource")
        infosource.iterables = [('subject_id', c.subjects),('fwhm',prep_c.fwhm)]

    datasource = pe.Node(interface=nio.DataGrabber(infields=['subject_id','fwhm'],
                                                   outfields=['copes', 
                                                              'varcopes',
                                                              'dof_files',
                                                              'mask_file']),
                         name = 'datasource')

    datasource.inputs.base_directory = c.sink_dir
    datasource.inputs.template ='*'
    datasource.inputs.sort_filelist = True
    datasource.inputs.field_template = dict(copes='%s/modelfit/contrasts/fwhm_%s/_estimate_contrast*/cope%02d*.nii*',
                                            varcopes='%s/modelfit/contrasts/fwhm_%s/_estimate_contrast*/varcope%02d*.nii*',
                                            dof_files='%s/modelfit/dofs/fwhm_%s/*/*',
                                            mask_file='%s/preproc/mask/*.nii*')"""

    datasource = c.datagrabber.create_dataflow()
    infosource = datasource.get_node('subject_id_iterable')

    fixedfx = create_fixed_effects_flow()

    fixedfxflow = pe.Workflow(name=name)
    fixedfxflow.config = {'execution': {'crashdump_dir': c.crash_dir}}

    #overlay = create_overlay_workflow(c,name='overlay')

    subjectinfo = pe.Node(util.Function(input_names=['subject_id'],
                                        output_names=['output']),
                          name='subjectinfo')
    subjectinfo.inputs.function_str = first_c.subjectinfo

    contrasts = pe.Node(util.Function(input_names=['subject_id'],
                                      output_names=['contrasts']),
                        name='getcontrasts')
    contrasts.inputs.function_str = first_c.contrasts

    #get_info = pe.Node(util.Function(input_names=['cons','info'], output_names=['info'], function=getinfo), name='getinfo')
    get_subs = pe.Node(util.Function(input_names=['subject_id', 'cons'],
                                     output_names=['subs'],
                                     function=getsubs),
                       name='getsubs')

    #fixedfxflow.connect(infosource, 'subject_id',           datasource, 'subject_id')

    #fixedfxflow.connect(infosource, ('subject_id',getinfo, c.getcontrasts, c.subjectinfo), datasource, 'template_args')

    fixedfxflow.connect(infosource, 'subject_id', contrasts, 'subject_id')
    fixedfxflow.connect(infosource, 'subject_id', subjectinfo, 'subject_id')
    #fixedfxflow.connect(contrasts, 'contrasts', get_info, 'cons')
    #fixedfxflow.connect(subjectinfo, 'output', get_info, 'info')
    #fixedfxflow.connect(get_info,'info',datasource,'template_args')

    #fixedfxflow.connect(infosource, 'fwhm',                 datasource, 'fwhm')
    fixedfxflow.connect(datasource, 'datagrabber.copes', copeselect, 'inlist')
    fixedfxflow.connect(selectnode, 'runs', copeselect, 'index')
    fixedfxflow.connect(datasource, 'datagrabber.copes', fixedfx,
                        'inputspec.copes')
    fixedfxflow.connect(datasource, 'datagrabber.varcopes', varcopeselect,
                        'inlist')
    fixedfxflow.connect(selectnode, 'runs', varcopeselect, 'index')
    fixedfxflow.connect(datasource, 'datagrabber.varcopes', fixedfx,
                        'inputspec.varcopes')
    fixedfxflow.connect(datasource, 'datagrabber.dof_files', dofselect,
                        'inlist')
    fixedfxflow.connect(selectnode, 'runs', dofselect, 'index')
    fixedfxflow.connect(datasource, 'datagrabber.dof_files', fixedfx,
                        'inputspec.dof_files')
    fixedfxflow.connect(datasource, ('datagrabber.copes', num_copes), fixedfx,
                        'l2model.num_copes')
    fixedfxflow.connect(datasource, 'datagrabber.mask_file', fixedfx,
                        'flameo.mask_file')
    #fixedfxflow.connect(infosource, 'subject_id',           overlay, 'inputspec.subject_id')
    #fixedfxflow.connect(infosource, 'fwhm',                 overlay, 'inputspec.fwhm')
    #fixedfxflow.connect(fixedfx, 'outputspec.zstats',       overlay, 'inputspec.stat_image')

    datasink = pe.Node(interface=nio.DataSink(), name="datasink")
    datasink.inputs.base_directory = c.sink_dir
    # store relevant outputs from various stages of the 1st level analysis
    fixedfxflow.connect([
        (
            infosource,
            datasink,
            [
                ('subject_id', 'container'),
                #(('subject_id', getsubs, c.getcontrasts), 'substitutions')
            ]),
        (fixedfx, datasink, [
            ('outputspec.copes', 'fixedfx.@copes'),
            ('outputspec.varcopes', 'fixedfx.@varcopes'),
            ('outputspec.tstats', 'fixedfx.@tstats'),
            ('outputspec.zstats', 'fixedfx.@zstats'),
            ('outputspec.res4d', 'fixedfx.@pvals'),
        ])
    ])
    fixedfxflow.connect(infosource, 'subject_id', get_subs, 'subject_id')
    fixedfxflow.connect(contrasts, 'contrasts', get_subs, 'cons')
    fixedfxflow.connect(get_subs, 'subs', datasink, 'substitutions')
    #fixedfxflow.connect(overlay, 'slicestats.out_file', datasink, 'overlays')
    return fixedfxflow
Ejemplo n.º 30
0
NodeHash_17173a00.inputs.local_directory = '/tmp'
NodeHash_17173a00.inputs.sort_filelist = True
NodeHash_17173a00.inputs.template = '*'
NodeHash_17173a00.inputs.template_args = dict(func=[['sub_id', 'sub_id']], events=[['sub_id', 'sub_id']], anat=[['sub_id', 'sub_id']])
NodeHash_17173a00.inputs.field_template = dict(func='%s/func/%s_task-simon_run-1_bold.nii.gz', events='%s/func/%s_task-simon_run-1_events.tsv', anat='%s/anat/%s_T1w.nii.gz')

#Wraps command **bet**
NodeHash_20af2180 = pe.MapNode(interface = fsl.BET(), name = 'NodeName_20af2180', iterfield = ['in_file'])
NodeHash_20af2180.inputs.frac = 0.3
NodeHash_20af2180.inputs.robust = True

#Wraps command **fast**
NodeHash_10d41ed0 = pe.MapNode(interface = fsl.FAST(), name = 'NodeName_10d41ed0', iterfield = ['in_files'])

#Basic interface class to select specific elements from a list
NodeHash_282e9070 = pe.MapNode(interface = utility.Select(), name = 'NodeName_282e9070', iterfield = ['inlist'])
NodeHash_282e9070.inputs.index = 2

#Wraps command **fslmaths**
NodeHash_313f8780 = pe.MapNode(interface = fsl.Threshold(), name = 'NodeName_313f8780', iterfield = ['in_file'])
NodeHash_313f8780.inputs.args = '-bin'
NodeHash_313f8780.inputs.thresh = 0.5

#Wraps command **fslroi**
NodeHash_1af07380 = pe.MapNode(interface = fsl.ExtractROI(), name = 'NodeName_1af07380', iterfield = ['in_file'])
NodeHash_1af07380.inputs.t_min = 1
NodeHash_1af07380.inputs.t_size = 1

#Wraps command **flirt**
NodeHash_5accf90 = pe.MapNode(interface = fsl.FLIRT(), name = 'NodeName_5accf90', iterfield = ['in_file', 'reference'])