예제 #1
0
def test_afni(tmp_path):
    seed(a=0x5E6128C4)

    m = 100
    n = 5

    column_names = [f"column_{i+1}" for i in range(n)]

    data_frame = pd.DataFrame(np.random.rand(m, n), columns=column_names)

    data_file = tmp_path / "data.tsv"
    data_frame.to_csv(data_file, sep="\t", header=True, index=False)

    to_afni = ToAFNI(in_file=data_file)

    cwd = tmp_path / "to_afni"
    cwd.mkdir()

    result = to_afni.run(cwd=cwd)
    assert result.outputs is not None

    oned_file = result.outputs.out_file
    metadata = result.outputs.metadata

    from_afni = FromAFNI(in_file=oned_file, metadata=metadata)

    cwd = tmp_path / "from_afni"
    cwd.mkdir()

    result = from_afni.run(cwd=cwd)
    assert result.outputs is not None

    test_data_frame = read_spreadsheet(result.outputs.out_file)
    assert np.allclose(data_frame.values, test_data_frame.values)

    cwd = tmp_path / "tproject"
    cwd.mkdir()

    tproject = afni.TProject(
        in_file=oned_file,
        out_file=cwd / "filt.1D",
        bandpass=(0.01, 0.1),
        TR=2,
        polort=1,
    )

    result = tproject.run(cwd=cwd)
    assert result.outputs is not None

    from_afni = FromAFNI(in_file=result.outputs.out_file, metadata=metadata)
    result = from_afni.run(cwd=cwd)
    assert result.outputs is not None

    test_data_frame = read_spreadsheet(result.outputs.out_file)
    assert not np.allclose(data_frame.values, test_data_frame.values)
예제 #2
0
파일: nuisance.py 프로젝트: tbweng/C-PAC
def create_nuisance_workflow(nuisance_selectors,
                             use_ants,
                             name='nuisance'):
    """
    Workflow for the removal of various signals considered to be noise from resting state
    fMRI data.  The residual signals for linear regression denoising is performed in a single
    model.  Therefore the residual time-series will be orthogonal to all signals.

    Parameters
    ----------
    :param nuisance_selectors: dictionary describing nuisance regression to be performed
    :param use_ants: flag indicating whether FNIRT or ANTS is used
    :param name: Name of the workflow, defaults to 'nuisance'
    :return: nuisance : nipype.pipeline.engine.Workflow
        Nuisance workflow.
        
    Notes
    -----

    Workflow Inputs
    ---------------
    Workflow Inputs::
    
        inputspec.functional_file_path : string (nifti file)
            Path to realigned and motion corrected functional image (nifti) file.

        inputspec.functional_brain_mask_file_path : string (nifti file)
            Whole brain mask corresponding to the functional data.

        inputspec.anatomical_file_path : string (nifti file)
            Corresponding preprocessed anatomical.
        inputspec.wm_mask_file_path : string (nifti file)
            Corresponding white matter mask.
        inputspec.csf_mask_file_path : string (nifti file)
            Corresponding cerebral spinal fluid mask.
        inputspec.gm_mask_file_path : string (nifti file)
            Corresponding grey matter mask.
        inputspec.lat_ventricles_mask_file_path : string (nifti file)
            Mask of lateral ventricles calculated from the Harvard Oxford Atlas.

        inputspec.mni_to_anat_linear_xfm_file_path: string (nifti file)
            FLIRT Linear MNI to Anat transform
        inputspec.anat_to_mni_initial_xfm_file_path: string (nifti file)
            ANTS initial transform from anat to MNI
        inputspec.anat_to_mni_rigid_xfm_file_path: string (nifti file)
            ANTS rigid (6 parameter, no scaling) transform from anat to MNI
        inputspec.anat_to_mni_affine_xfm_file_path: string (nifti file)
            ANTS affine (13 parameter, scales and shears) transform from anat to MNI

        inputspec.func_to_anat_linear_xfm_file_path: string (nifti file)
            FLIRT Linear Transform between functional and anatomical spaces 

        inputspec.motion_parameter_file_path : string (text file)
            Corresponding rigid-body motion parameters. Matrix in the file should be of shape 
            (`T`, `R`), `T` time points and `R` motion parameters.
        inputspec.fd_j_file_path : string (text file)
            Framewise displacement calculated from the volume alignment.
        inputspec.fd_p_file_path : string (text file)
            Framewise displacement calculated from the motion parameters.
        inputspec.dvars_file_path : string (text file)
            DVARS calculated from the functional data.

        inputspec.selector : Dictionary containing configuration parameters for nuisance regression.
            To not run a type of nuisance regression, it may be ommited from the dictionary.
            selector = {
                aCompCor: {
                    symmary: {
                        method: 'DetrendPC', aCompCor will always extract the principal components from
                            detrended tissues signal,
                        components: number of components to retain,
                    },
                    tissues: list of tissues to extract regressors.
                        Valid values are: 'WhiteMatter', 'CerebrospinalFluid',
                    extraction_resolution: None | floating point value indicating isotropic
                        resolution (ex. 2 for 2mm x 2mm x 2mm that data should be extracted at,
                        the corresponding tissue mask will be resampled to this resolution. The
                        functional data will also be resampled to this resolution, and the
                        extraction will occur at this new resolution. The goal is to avoid
                        contamination from undesired tissue components when extracting nuisance
                        regressors,
                    erode_mask: True | False, whether or not the mask should be eroded to
                        further avoid a mask overlapping with a different tissue class,
                    include_delayed: True | False, whether or not to include a one-frame delay regressor,
                        default to False,
                    include_squared: True | False, whether or not to include a squared regressor,
                        default to False,
                    include_delayed_squared: True | False, whether or not to include a squared one-frame
                        delay regressor, default to False,
                },
                tCompCor: {
                    symmary: {
                        method: 'PC', tCompCor will always extract the principal components from
                            BOLD signal,
                        components: number of components to retain,
                    },
                    threshold:
                        floating point number = cutoff as raw variance value,
                        floating point number followed by SD (ex. 1.5SD) = mean + a multiple of the SD,
                        floating point number followed by PCT (ex. 2PCT) = percentile from the top (ex is top 2%),
                    by_slice: True | False, whether or not the threshold criterion should be applied
                        by slice or across the entire volume, makes most sense for thresholds
                        using SD or PCT,
                    include_delayed: True | False,
                    include_squared: True | False,
                    include_delayed_squared: True | False,
                },
                WhiteMatter: {
                    symmary: {
                        method: 'PC', 'DetrendPC', 'Mean', 'NormMean' or 'DetrendNormMean',
                        components: number of components to retain, if PC,
                    },
                    extraction_resolution: None | floating point value (same as for aCompCor),
                    erode_mask: True | False (same as for aCompCor),
                    include_delayed: True | False (same as for aCompCor),
                    include_squared: True | False (same as for aCompCor),
                    include_delayed_squared: True | False (same as for aCompCor),
                },
                CerebrospinalFluid: {
                    symmary: {
                        method: 'PC', 'DetrendPC', 'Mean', 'NormMean' or 'DetrendNormMean',
                        components: number of components to retain, if PC,
                    },
                    extraction_resolution: None | floating point value (same as for aCompCor),
                    erode_mask: True | False (same as for aCompCor),
                    include_delayed: True | False (same as for aCompCor),
                    include_squared: True | False (same as for aCompCor),
                    include_delayed_squared: True | False (same as for aCompCor),
                },
                GreyMatter: {
                    symmary: {
                        method: 'PC', 'DetrendPC', 'Mean', 'NormMean' or 'DetrendNormMean',
                        components: number of components to retain, if PC,
                    },
                    extraction_resolution: None | floating point value (same as for aCompCor),
                    erode_mask: True | False (same as for aCompCor),
                    include_delayed: True | False (same as for aCompCor),
                    include_squared: True | False (same as for aCompCor),
                    include_delayed_squared: True | False (same as for aCompCor),
                },
                GlobalSignal: {
                    symmary: {
                        method: 'PC', 'DetrendPC', 'Mean', 'NormMean' or 'DetrendNormMean',
                        components: number of components to retain, if PC,
                    },
                    include_delayed: True | False (same as for aCompCor),
                    include_squared: True | False (same as for aCompCor),
                    include_delayed_squared: True | False (same as for aCompCor),
                },
                Motion: None | { 
                    include_delayed: True | False (same as for aCompCor),
                    include_squared: True | False (same as for aCompCor),
                    include_delayed_squared: True | False (same as for aCompCor),
                },
                Censor: {
                    method: 'Kill', 'Zero', 'Interpolate', 'SpikeRegression',
                    thresholds: list of dictionary, {
                        type: 'FD_J', 'FD_P', 'DVARS',
                        value: threshold value to be applied to metric
                    },
                    number_of_previous_trs_to_censor: integer, number of previous
                        TRs to censor (remove or regress, if spike regression)
                    number_of_subsequent_trs_to_censor: integer, number of
                        subsequent TRs to censor (remove or regress, if spike
                        regression)
                },
                PolyOrt: {
                    degree: integer, polynomial degree up to which will be removed,
                        e.g. 2 means constant + linear + quadratic, practically
                        that is probably, the most that will be need especially
                        if band pass filtering
                },
                Bandpass: {
                    bottom_frequency: floating point value, frequency in hertz of
                        the highpass part of the pass band, frequencies below this
                        will be removed,
                    top_frequency: floating point value, frequency in hertz of the
                        lowpass part of the pass band, frequencies above this
                        will be removed
                }
            }

    Workflow Outputs::

        outputspec.residual_file_path : string (nifti file)
            Path of residual file in nifti format
        outputspec.regressors_file_path : string (TSV file)
            Path of TSV file of regressors used. Column name indicates the regressors included .

    Nuisance Procedure:

    1. Compute nuisance regressors based on input selections.
    2. Calculate residuals with respect to these nuisance regressors in a
       single model for every voxel.

    High Level Workflow Graph:

    .. exec::
        from CPAC.nuisance import create_nuisance_workflow
        wf = create_nuisance_workflow({
            'PolyOrt': {'degree': 2},
            'tCompCor': {'summary': {'method': 'PC', 'components': 5}, 'threshold': '1.5SD', 'by_slice': True},
            'aCompCor': {'summary': {'method': 'PC', 'components': 5}, 'tissues': ['WhiteMatter', 'CerebrospinalFluid'], 'extraction_resolution': 2},
            'WhiteMatter': {'summary': {'method': 'PC', 'components': 5}, 'extraction_resolution': 2},
            'CerebrospinalFluid': {'summary': {'method': 'PC', 'components': 5}, 'extraction_resolution': 2, 'erode_mask': True},
            'GreyMatter': {'summary': {'method': 'PC', 'components': 5}, 'extraction_resolution': 2, 'erode_mask': True},
            'GlobalSignal': {'summary': 'Mean', 'include_delayed': True, 'include_squared': True, 'include_delayed_squared': True},
            'Motion': {'include_delayed': True, 'include_squared': True, 'include_delayed_squared': True},
            'Censor': {'method': 'Interpolate', 'thresholds': [{'type': 'FD_J', 'value': 0.5}, {'type': 'DVARS', 'value': 0.7}]}
        }, use_ants=False)

        wf.write_graph(
            graph2use='orig',
            dotfilename='./images/nuisance.dot'
        )

    .. image:: ../images/nuisance.png
       :width: 1000

    Detailed Workflow Graph:

    .. image:: ../images/nuisance_detailed.png
       :width: 1000

    """

    nuisance_wf = pe.Workflow(name=name)

    inputspec = pe.Node(util.IdentityInterface(fields=[
        'selector',
        'functional_file_path',

        'anatomical_file_path',
        'gm_mask_file_path',
        'wm_mask_file_path',
        'csf_mask_file_path',
        'lat_ventricles_mask_file_path',

        'functional_brain_mask_file_path',

        'func_to_anat_linear_xfm_file_path',
        'mni_to_anat_linear_xfm_file_path',
        'anat_to_mni_initial_xfm_file_path',
        'anat_to_mni_rigid_xfm_file_path',
        'anat_to_mni_affine_xfm_file_path',

        'motion_parameters_file_path',
        'fd_j_file_path',
        'fd_p_file_path',
        'dvars_file_path',
    ]), name='inputspec')

    outputspec = pe.Node(util.IdentityInterface(fields=['residual_file_path',
                                                        'regressors_file_path']),
                         name='outputspec')

    # Resources to create regressors
    pipeline_resource_pool = {
        "Anatomical": (inputspec, 'anatomical_file_path'),
        "Functional": (inputspec, 'functional_file_path'),
        "GlobalSignal": (inputspec, 'functional_brain_mask_file_path'),
        "WhiteMatter": (inputspec, 'wm_mask_file_path'),
        "CerebrospinalFluid": (inputspec, 'csf_mask_file_path'),
        "GreyMatter": (inputspec, 'gm_mask_file_path'),
        "Ventricles": (inputspec, 'lat_ventricles_mask_file_path'),

        "Transformations": {
            "func_to_anat_linear_xfm": (inputspec, "func_to_anat_linear_xfm_file_path"),
            "mni_to_anat_linear_xfm": (inputspec, "mni_to_anat_linear_xfm_file_path"),
            "anat_to_mni_initial_xfm": (inputspec, "anat_to_mni_initial_xfm_file_path"),
            "anat_to_mni_rigid_xfm": (inputspec, "anat_to_mni_rigid_xfm_file_path"),
            "anat_to_mni_affine_xfm": (inputspec, "anat_to_mni_affine_xfm_file_path"),
        }
    }

    # Regressor map to simplify construction of the needed regressors
    regressors = {
        'GreyMatter': ['grey_matter_summary_file_path', ()],
        'WhiteMatter': ['white_matter_summary_file_path', ()],
        'CerebrospinalFluid': ['csf_summary_file_path', ()],
        'aCompCor': ['acompcor_file_path', ()],
        'tCompCor': ['tcompcor_file_path', ()],
        'GlobalSignal': ['global_summary_file_path', ()],
        'DVARS': ['dvars_file_path', (inputspec, 'dvars_file_path')],
        'FD_J': ['framewise_displacement_j_file_path', (inputspec, 'framewise_displacement_j_file_path')],
        'FD_P': ['framewise_displacement_p_file_path', (inputspec, 'framewise_displacement_p_file_path')],
        'Motion': ['motion_parameters_file_path', (inputspec, 'motion_parameters_file_path')]
    }

    derived = ['tCompCor', 'aCompCor']
    tissues = ['GreyMatter', 'WhiteMatter', 'CerebrospinalFluid']

    for regressor_type, regressor_resource in regressors.items():

        if regressor_type not in nuisance_selectors:
            continue

        regressor_selector = nuisance_selectors[regressor_type]

        # Set summary method for tCompCor and aCompCor
        if regressor_type in derived:

            if 'summary' not in regressor_selector:
                regressor_selector['summary'] = {}

            if type(regressor_selector['summary']) is not dict:
                raise ValueError("Regressor {0} requires PC summary method, "
                                 "but {1} specified"
                                 .format(regressor_type,
                                         regressor_selector['summary']))

            regressor_selector['summary']['method'] = \
                'DetrendPC' if regressor_type == 'aCompCor' else 'PC'

            if not regressor_selector['summary'].get('components'):
                regressor_selector['summary']['components'] = 1

        # If regressor is not present, build up the regressor
        if not regressor_resource[1]:

            # We don't have the regressor, look for it in the resource pool,
            # build a corresponding key, this is seperated in to a mask key
            # and an extraction key, which when concatenated provide the
            # resource key for the regressor
            regressor_descriptor = {'tissue': regressor_type}

            if regressor_type == 'aCompCor':
                if not regressor_selector.get('tissues'):
                    raise ValueError("Tissue type required for aCompCor, "
                                     "but none specified")

                regressor_descriptor = {
                    'tissue': regressor_selector['tissues']
                }


            if regressor_type == 'tCompCor':
                if not regressor_selector.get('threshold'):
                    raise ValueError("Threshold required for tCompCor, "
                                     "but none specified.")

                regressor_descriptor = {
                    'tissue': 'FunctionalVariance-{}'
                              .format(regressor_selector['threshold'])
                }

                if regressor_selector.get('by_slice'):
                    regressor_descriptor['tissue'] += '-BySlice'
                else:
                    regressor_selector['by_slice'] = False


            # Add selector into regressor description

            if regressor_selector.get('extraction_resolution'):
                regressor_descriptor['resolution'] = \
                    str(regressor_selector['extraction_resolution']) + "mm"

            elif regressor_type in tissues:
                regressor_selector['extraction_resolution'] = "Functional"
                regressor_descriptor['resolution'] = "Functional"

            if regressor_selector.get('erode_mask'):
                regressor_descriptor['erosion'] = 'Eroded'

            if not regressor_selector.get('summary'):
                raise ValueError("Summary method required for {0}, "
                                 "but none specified".format(regressor_type))

            if type(regressor_selector['summary']) is dict:
                regressor_descriptor['extraction'] = \
                    regressor_selector['summary']['method']
            else:
                regressor_descriptor['extraction'] = \
                    regressor_selector['summary']

            if regressor_descriptor['extraction'] in ['DetrendPC', 'PC']:
                if not regressor_selector['summary'].get('components'):
                    raise ValueError("Summary method PC requires components, "
                                     "but received none.")

                regressor_descriptor['extraction'] += \
                    '_{0}'.format(regressor_selector['summary']['components'])

            if type(regressor_descriptor['tissue']) is not list:
                regressor_descriptor['tissue'] = \
                    [regressor_descriptor['tissue']]


            if regressor_selector.get('extraction_resolution') and \
                    regressor_selector["extraction_resolution"] != "Functional":

                functional_at_resolution_key = "Functional_{0}mm".format(
                    regressor_selector["extraction_resolution"]
                )

                anatomical_at_resolution_key = "Anatomical_{0}mm".format(
                    regressor_selector["extraction_resolution"]
                )

                if anatomical_at_resolution_key not in pipeline_resource_pool:

                    anat_resample = pe.Node(
                        interface=fsl.FLIRT(),
                        name='{}_flirt'
                             .format(anatomical_at_resolution_key)
                    )
                    anat_resample.inputs.apply_isoxfm = regressor_selector["extraction_resolution"]

                    nuisance_wf.connect(*(
                        pipeline_resource_pool['Anatomical'] +
                        (anat_resample, 'in_file')
                    ))

                    nuisance_wf.connect(*(
                        pipeline_resource_pool['Anatomical'] +
                        (anat_resample, 'reference')
                    ))

                    pipeline_resource_pool[anatomical_at_resolution_key] = \
                        (anat_resample, 'out_file')

                if functional_at_resolution_key not in pipeline_resource_pool:

                    func_resample = pe.Node(
                        interface=fsl.FLIRT(),
                        name='{}_flirt'
                             .format(functional_at_resolution_key)
                    )
                    func_resample.inputs.apply_xfm = True

                    nuisance_wf.connect(*(
                        pipeline_resource_pool['Transformations']['func_to_anat_linear_xfm'] +
                        (func_resample, 'in_matrix_file')
                    ))

                    nuisance_wf.connect(*(
                        pipeline_resource_pool['Functional'] +
                        (func_resample, 'in_file')
                    ))

                    nuisance_wf.connect(*(
                        pipeline_resource_pool[anatomical_at_resolution_key] +
                        (func_resample, 'reference')
                    ))

                    pipeline_resource_pool[functional_at_resolution_key] = \
                        (func_resample, 'out_file')

            # Create merger to summarize the functional timeseries
            regressor_mask_file_resource_keys = []
            for tissue in regressor_descriptor['tissue']:

                # Ignore non tissue masks
                if tissue not in tissues and \
                    not tissue.startswith('FunctionalVariance'):
                    regressor_mask_file_resource_keys += [tissue]
                    continue

                tissue_regressor_descriptor = regressor_descriptor.copy()
                tissue_regressor_descriptor['tissue'] = tissue

                # Generate resource masks
                (pipeline_resource_pool,
                 regressor_mask_file_resource_key) = \
                    generate_summarize_tissue_mask(
                        nuisance_wf,
                        pipeline_resource_pool,
                        tissue_regressor_descriptor,
                        regressor_selector,
                        use_ants=use_ants
                    )

                regressor_mask_file_resource_keys += \
                    [regressor_mask_file_resource_key]

            # Keep tissus ordered, to avoid duplicates
            regressor_mask_file_resource_keys = \
                list(sorted(regressor_mask_file_resource_keys))

            # Create key for the final regressors
            regressor_file_resource_key = "_".join([
                "-".join(regressor_descriptor[key])
                if type(regressor_descriptor[key]) == list
                else regressor_descriptor[key]

                for key in ['tissue', 'resolution', 'erosion', 'extraction']
                if key in regressor_descriptor
            ])

            if regressor_file_resource_key not in pipeline_resource_pool:

                # Retrieve summary from voxels at provided mask
                summarize_timeseries_node = pe.Node(
                    Function(
                        input_names=[
                            'functional_path',
                            'masks_path',
                            'summary'
                        ],
                        output_names=['components_path'],
                        function=summarize_timeseries,
                        as_module=True,
                    ),
                    name='{}_summarization'.format(regressor_type)
                )

                summarize_timeseries_node.inputs.summary = \
                    regressor_selector['summary']

                # Merge mask paths to extract voxel timeseries
                merge_masks_paths = pe.Node(
                    util.Merge(len(regressor_mask_file_resource_keys)),
                    name='{}_marge_masks'.format(regressor_type)
                )
                for i, regressor_mask_file_resource_key in \
                        enumerate(regressor_mask_file_resource_keys):

                    node, node_output = \
                        pipeline_resource_pool[regressor_mask_file_resource_key]

                    nuisance_wf.connect(
                        node, node_output,
                        merge_masks_paths, "in{}".format(i + 1)
                    )

                nuisance_wf.connect(
                    merge_masks_paths, 'out',
                    summarize_timeseries_node, 'masks_path'
                )

                functional_key = 'Functional'
                if regressor_selector.get('extraction_resolution') and \
                        regressor_selector["extraction_resolution"] != "Functional":

                    functional_key = 'Functional_{}mm'.format(
                        regressor_selector['extraction_resolution']
                    )

                nuisance_wf.connect(*(
                    pipeline_resource_pool[functional_key] +
                    (summarize_timeseries_node, 'functional_path')
                ))

                pipeline_resource_pool[regressor_file_resource_key] = \
                    (summarize_timeseries_node, 'components_path')

                # Add it to internal resource pool
                regressor_resource[1] = \
                    pipeline_resource_pool[regressor_file_resource_key]

    # Build regressors and combine them into a single file
    build_nuisance_regressors = pe.Node(Function(
        input_names=['functional_file_path',
                     'selector',
                     'grey_matter_summary_file_path',
                     'white_matter_summary_file_path',
                     'csf_summary_file_path',
                     'acompcor_file_path',
                     'tcompcor_file_path',
                     'global_summary_file_path',
                     'motion_parameters_file_path',
                     'censor_file_path'],
        output_names=['out_file'],
        function=gather_nuisance,
        as_module=True
    ), name="build_nuisance_regressors")

    nuisance_wf.connect(
        inputspec, 'functional_file_path',
        build_nuisance_regressors, 'functional_file_path'
    )

    nuisance_wf.connect(
        inputspec, 'selector',
        build_nuisance_regressors, 'selector'
    )

    # Check for any regressors to combine into files
    has_nuisance_regressors = any(
        regressor_resource[1]
        for regressor_key, regressor_resource
        in regressors.items()
    )

    if has_nuisance_regressors:
        for regressor_key, (regressor_arg, regressor_node) in regressors.items():
            if regressor_key in nuisance_selectors:
                nuisance_wf.connect(
                    regressor_node[0], regressor_node[1],
                    build_nuisance_regressors, regressor_arg
                )

    if nuisance_selectors.get('Censor'):

        censor_methods = ['Kill', 'Zero', 'Interpolate', 'SpikeRegression']

        censor_selector = nuisance_selectors.get('Censor')
        if censor_selector.get('method') not in censor_methods:
            raise ValueError("Improper censoring method specified ({0}), "
                             "should be one of {1}."
                             .format(censor_selector.get('method'),
                                     censor_methods))

        find_censors = pe.Node(Function(
            input_names=['fd_j_file_path',
                         'fd_j_threshold',
                         'fd_p_file_path',
                         'fd_p_threshold',
                         'dvars_file_path',
                         'dvars_threshold',
                         'number_of_previous_trs_to_censor',
                         'number_of_subsequent_trs_to_censor'],
            output_names=['out_file'],
            function=find_offending_time_points,
            as_module=True
        ), name="find_offending_time_points")

        if not censor_selector.get('thresholds'):
            raise ValueError(
                'Censoring requested, but thresh_metric not provided.'
            )

        for threshold in censor_selector['thresholds']:

            if 'type' not in threshold or threshold['type'] not in ['DVARS', 'FD_J', 'FD_P']:
                raise ValueError(
                    'Censoring requested, but with invalid threshold type.'
                )

            if 'value' not in threshold:
                raise ValueError(
                    'Censoring requested, but threshold not provided.'
                )

            if threshold['type'] == 'FD_J':
                find_censors.inputs.fd_j_threshold = threshold['value']
                nuisance_wf.connect(inputspec, "fd_j_file_path",
                                    find_censors, "fd_j_file_path")

            if threshold['type'] == 'FD_P':
                find_censors.inputs.fd_p_threshold = threshold['value']
                nuisance_wf.connect(inputspec, "fd_p_file_path",
                                    find_censors, "fd_p_file_path")

            if threshold['type'] == 'DVARS':
                find_censors.inputs.dvars_threshold = threshold['value']
                nuisance_wf.connect(inputspec, "dvars_file_path",
                                    find_censors, "dvars_file_path")

        if censor_selector.get('number_of_previous_trs_to_censor') and \
                censor_selector['method'] != 'SpikeRegression':

            find_censors.inputs.number_of_previous_trs_to_censor = \
                censor_selector['number_of_previous_trs_to_censor']

        else:
            find_censors.inputs.number_of_previous_trs_to_censor = 0

        if censor_selector.get('number_of_subsequent_trs_to_censor') and \
                censor_selector['method'] != 'SpikeRegression':

            find_censors.inputs.number_of_subsequent_trs_to_censor = \
                censor_selector['number_of_subsequent_trs_to_censor']

        else:
            find_censors.inputs.number_of_subsequent_trs_to_censor = 0

    # Use 3dTproject to perform nuisance variable regression
    nuisance_regression = pe.Node(interface=afni.TProject(),
                                  name='nuisance_regression')

    nuisance_regression.inputs.out_file = 'residuals.nii.gz'
    nuisance_regression.inputs.outputtype = 'NIFTI_GZ'
    nuisance_regression.inputs.norm = False

    if nuisance_selectors.get('Censor'):
        if nuisance_selectors['Censor']['method'] == 'SpikeRegression':
            nuisance_wf.connect(find_censors, 'out_file',
                                build_nuisance_regressors, 'censor_file_path')
        else:
            if nuisance_selectors['Censor']['method'] == 'Interpolate':
                nuisance_regression.inputs.cenmode = 'NTRP'
            else:
                nuisance_regression.inputs.cenmode = \
                    nuisance_selectors['Censor']['method'].upper()

            nuisance_wf.connect(find_censors, 'out_file',
                                nuisance_regression, 'censor')

    if nuisance_selectors.get('PolyOrt'):
        if not nuisance_selectors['PolyOrt'].get('degree'):
            raise ValueError("Polynomial orthogonalization requested, "
                             "but degree not provided.")

        nuisance_regression.inputs.polort = \
            nuisance_selectors['PolyOrt']['degree']

    else:
        nuisance_regression.inputs.polort = 0

    nuisance_wf.connect([
        (inputspec, nuisance_regression, [
            ('functional_file_path', 'in_file'),
            ('functional_brain_mask_file_path', 'mask'),
        ]),
    ])

    if has_nuisance_regressors:
        nuisance_wf.connect(build_nuisance_regressors, 'out_file',
                            nuisance_regression, 'ort')

    nuisance_wf.connect(nuisance_regression, 'out_file',
                        outputspec, 'residual_file_path')

    nuisance_wf.connect(build_nuisance_regressors, 'out_file',
                        outputspec, 'regressors_file_path')

    return nuisance_wf
예제 #3
0
def init_bandpass_filter_wf(bandpass_filter=None,
                            name=None,
                            suffix=None,
                            memcalc=MemoryCalculator()):
    """

    """
    type, low, high = bandpass_filter
    if name is None:
        name = f"{type}_bandpass_filter"
        if low is not None:
            name = f"{name}_{int(low * 1000):d}"
        if low is not None:
            name = f"{name}_{int(high * 1000):d}"
        name = f"{name}_wf"
    if suffix is not None:
        name = f"{name}_{suffix}"

    workflow = pe.Workflow(name=name)

    inputnode = pe.Node(
        niu.IdentityInterface(
            fields=["files", "mask", "low", "high", "vals", "repetition_time"
                    ]),
        name="inputnode",
    )
    outputnode = pe.Node(
        niu.IdentityInterface(fields=["files", "mask", "vals"]),
        name="outputnode",
    )

    workflow.connect(inputnode, "mask", outputnode, "mask")
    workflow.connect(inputnode, "vals", outputnode, "vals")

    if low is not None:
        inputnode.inputs.low = low
    else:
        inputnode.inputs.low = -1.0

    if high is not None:
        inputnode.inputs.high = high
    else:
        inputnode.inputs.high = -1.0

    addmeans = pe.MapNode(AddMeans(),
                          iterfield=["in_file", "mean_file"],
                          name="addmeans",
                          mem_gb=memcalc.series_std_gb * 2)
    workflow.connect(inputnode, "files", addmeans, "mean_file")

    workflow.connect(addmeans, "out_file", outputnode, "files")

    if type == "gaussian":
        calcsigma = pe.Node(
            niu.Function(
                input_names=["lp_width", "hp_width", "repetition_time"],
                output_names=["lp_sigma", "hp_sigma"],
                function=_calc_sigma,
            ),
            name="calcsigma",
        )
        workflow.connect(inputnode, "low", calcsigma, "lp_width")
        workflow.connect(inputnode, "high", calcsigma, "hp_width")
        workflow.connect(inputnode, "repetition_time", calcsigma,
                         "repetition_time")

        temporalfilter = pe.MapNode(TemporalFilter(),
                                    iterfield="in_file",
                                    name="temporalfilter",
                                    mem_gb=memcalc.series_std_gb)
        workflow.connect(calcsigma, "lp_sigma", temporalfilter,
                         "lowpass_sigma")
        workflow.connect(calcsigma, "hp_sigma", temporalfilter,
                         "highpass_sigma")
        workflow.connect(inputnode, "files", temporalfilter, "in_file")
        workflow.connect(inputnode, "mask", temporalfilter, "mask")

        workflow.connect(temporalfilter, "out_file", addmeans, "in_file")
    elif type == "frequency_based":
        toafni = pe.MapNode(ToAFNI(), iterfield="in_file", name="toafni")
        workflow.connect(inputnode, "files", toafni, "in_file")

        makeoutfname = pe.MapNode(
            niu.Function(
                input_names=["in_file"],
                output_names=["out_file"],
                function=_out_file_name,
            ),
            iterfield="in_file",
            name="tprojectoutfilename",
        )
        workflow.connect(toafni, "out_file", makeoutfname, "in_file")

        bandpassarg = pe.Node(
            niu.Function(
                input_names=["low", "high"],
                output_names=["out"],
                function=_bandpass_arg,
            ),
            name="bandpassarg",
        )  # cannot use merge here as we need a tuple
        workflow.connect(inputnode, "low", bandpassarg, "low")
        workflow.connect(inputnode, "high", bandpassarg, "high")

        tproject = pe.MapNode(afni.TProject(polort=1),
                              iterfield=["in_file", "out_file"],
                              name="tproject",
                              mem_gb=memcalc.series_std_gb * 2)
        workflow.connect(toafni, "out_file", tproject, "in_file")
        workflow.connect(bandpassarg, "out", tproject, "bandpass")
        workflow.connect(inputnode, "repetition_time", tproject, "TR")
        workflow.connect(makeoutfname, "out_file", tproject, "out_file")

        fromafni = pe.MapNode(FromAFNI(),
                              iterfield=["in_file", "metadata"],
                              name="fromafni")
        workflow.connect(toafni, "metadata", fromafni, "metadata")
        workflow.connect(tproject, "out_file", fromafni, "in_file")

        workflow.connect(fromafni, "out_file", addmeans, "in_file")
    else:
        raise ValueError(f"Unknown bandpass_filter type '{type}'")

    return workflow
예제 #4
0
def init_bold_filt_wf(variant=None, memcalc=MemoryCalculator()):
    assert variant is not None

    name = make_variant_bold_filt_wf_name(variant)

    workflow = pe.Workflow(name=name)

    inputnode = pe.Node(
        niu.IdentityInterface(fields=[
            *in_attrs_from_func_preproc_wf, *in_attrs_from_anat_preproc_wf,
            "metadata"
        ]),
        name="inputnode",
    )
    workflow.add_nodes([inputnode])

    metadatanode = pe.Node(niu.IdentityInterface(fields=["repetition_time"]),
                           name="metadatanode")
    workflow.connect([(inputnode, metadatanode, [
        (("metadata", get_repetition_time), "repetition_time")
    ])])

    bandpass = None

    ortendpoint = None

    boldfileendpoint = (inputnode, "bold_std")

    tagdict = dict(variant)

    # smoothing is done first
    if "smoothed" in tagdict:
        fwhm = float(tagdict["smoothed"])
        smooth_workflow = init_smooth_wf(fwhm=fwhm)
        workflow.connect(inputnode, "bold_mask_std", smooth_workflow,
                         "inputnode.mask_file")
        workflow.connect(*boldfileendpoint, smooth_workflow,
                         "inputnode.in_file")
        boldfileendpoint = (smooth_workflow, "outputnode.out_file")

    if "grand_mean_scaled" in tagdict:
        grand_mean = tagdict["grand_mean_scaled"]
        assert isinstance(grand_mean, float)
        grandmeanscaling = pe.Node(
            interface=GrandMeanScaling(grand_mean=grand_mean),
            name="grandmeanscaling")
        workflow.connect(*boldfileendpoint, grandmeanscaling, "in_file")
        workflow.connect(inputnode, "bold_mask_std", grandmeanscaling,
                         "mask_file")
        boldfileendpoint = (grandmeanscaling, "out_file")

    need_to_add_mean = False
    boldfileendpoint_for_meanfunc = boldfileendpoint

    # if we use gaussian band-pass filtering, we cannot orthogonalize these regressors
    # with respect to the filter, as afni tproject doesn't support this filter type
    # as such we need to remove them before to not re-introduce filtered-out variance
    simultaneous_bandpass_and_ort = True
    if "band_pass_filtered" in tagdict:
        type = first(tagdict["band_pass_filtered"])
        if type == "gaussian":
            simultaneous_bandpass_and_ort = False

    confounds_to_remove_before_filtering = set(("aroma_motion_[0-9]+", ))
    if (not simultaneous_bandpass_and_ort and "confounds_removed" in tagdict
            and not confounds_to_remove_before_filtering.isdisjoint(
                tagdict["confounds_removed"])):
        confoundsremovedset = set(tagdict["confounds_removed"])
        preconfoundsremoved = confounds_to_remove_before_filtering & confoundsremovedset
        postconfoundsremoved = confoundsremovedset - confounds_to_remove_before_filtering
        if len(postconfoundsremoved) == 0:
            del tagdict["confounds_removed"]
        else:
            tagdict["confounds_removed"] = postconfoundsremoved

        ortendpoint, _ = make_confoundsendpoint("pre", workflow,
                                                boldfileendpoint,
                                                list(preconfoundsremoved),
                                                memcalc)
        tproject = pe.Node(afni.TProject(polort=1, out_file="tproject.nii"),
                           name="pretproject")
        workflow.connect(*boldfileendpoint, tproject, "in_file")
        workflow.connect(metadatanode, "repetition_time", tproject, "TR")
        workflow.connect(*ortendpoint, tproject, "ort")
        boldfileendpoint = (tproject, "out_file")
        need_to_add_mean = True

    if "band_pass_filtered" in tagdict:
        type = first(tagdict["band_pass_filtered"])
        if type == "frequency_based":
            bandpass = tagdict["band_pass_filtered"][1:]
        elif type == "gaussian":

            def calc_highpass_sigma(temporal_filter_width=None,
                                    repetition_time=None):
                highpass_sigma = temporal_filter_width / (2.0 *
                                                          repetition_time)
                return highpass_sigma

            calchighpasssigma = pe.Node(
                interface=niu.Function(
                    input_names=["temporal_filter_width", "repetition_time"],
                    output_names=["highpass_sigma"],
                    function=calc_highpass_sigma,
                ),
                name="calchighpasssigma",
            )
            workflow.connect(metadatanode, "repetition_time",
                             calchighpasssigma, "repetition_time")
            calchighpasssigma.inputs.temporal_filter_width = second(
                tagdict["band_pass_filtered"])
            highpass = pe.Node(fsl.TemporalFilter(), name="gaussianfilter")
            workflow.connect(calchighpasssigma, "highpass_sigma", highpass,
                             "highpass_sigma")
            workflow.connect(*boldfileendpoint, highpass, "in_file")
            need_to_add_mean = True

    if "confounds_removed" in tagdict:
        confoundnames = tagdict["confounds_removed"]
        if len(confoundnames) > 0:
            ortendpoint, _ = make_confoundsendpoint("post", workflow,
                                                    boldfileendpoint,
                                                    confoundnames, memcalc)

    if bandpass is not None or ortendpoint is not None:
        tproject = pe.Node(afni.TProject(polort=1, out_file="tproject.nii"),
                           name="tproject")
        workflow.connect(*boldfileendpoint, tproject, "in_file")
        workflow.connect(metadatanode, "repetition_time", tproject, "TR")
        if bandpass is not None:
            tproject.inputs.bandpass = bandpass
        if ortendpoint is not None:
            workflow.connect(*ortendpoint, tproject, "ort")
        boldfileendpoint = (tproject, "out_file")
        need_to_add_mean = True

    if need_to_add_mean is True:
        meanfunc = pe.Node(interface=fsl.ImageMaths(op_string="-Tmean",
                                                    suffix="_mean"),
                           name="meanfunc")
        workflow.connect(*boldfileendpoint_for_meanfunc, meanfunc, "in_file")
        addmean = pe.Node(interface=fsl.BinaryMaths(operation="add"),
                          name="addmean")
        workflow.connect(*boldfileendpoint, addmean, "in_file")
        workflow.connect(meanfunc, "out_file", addmean, "operand_file")
        boldfileendpoint = (addmean, "out_file")

    applymask = pe.Node(
        interface=fsl.ApplyMask(),
        name="applymask",
        mem_gb=memcalc.volume_std_gb,
    )
    workflow.connect(*boldfileendpoint, applymask, "in_file")
    workflow.connect(inputnode, "bold_mask_std", applymask, "mask_file")
    boldfileendpoint = (applymask, "out_file")

    endpoints = [boldfileendpoint]  # boldfile is finished

    if "confounds_extract" in tagdict:  # last
        confoundnames = tagdict["confounds_extract"]
        confoundsextractendpoint, confoundsextractendpointwithheader = make_confoundsendpoint(
            "extract", workflow, boldfileendpoint, confoundnames, memcalc)
        endpoints.append(confoundsextractendpoint)
        endpoints.append(confoundsextractendpointwithheader)

    outnames = [f"out{i+1}" for i in range(len(endpoints))]

    outputnode = pe.Node(
        niu.IdentityInterface(fields=[*outnames, "mask_file"]),
        name="outputnode",
    )
    workflow.connect(inputnode, "bold_mask_std", outputnode, "mask_file")

    for outname, endpoint in zip(outnames, endpoints):
        workflow.connect(*endpoint, outputnode, outname)

    return workflow
def prepro_func(i):
    try:
        subj = i
        for s in (['session2']):

            # Define input files: 2xfMRI + 1xMPRAGE
            func1 = data_path + subj + '/Functional_scans/' + s[:-2] + s[
                -1] + '_a/epi.nii.gz'  #choose this for patients
            func2 = data_path + subj + '/Functional_scans/' + s[:-2] + s[
                -1] + '_b/epi.nii.gz'  #choose this for patients
            #anat = glob.glob(anat_path + subj +'/'+ s + '/anat/reorient/anat_*.nii.gz') #choose this for session 1
            lesion_mask_file = anat_path + subj + '/session1/anat/reorient/lesion_seg.nii.gz'
            old_lesion_mask_file = glob.glob(
                anat_path + subj +
                '/session1/anat/reorient/old_lesion_seg.nii.gz'
            )  #choose this for ones with no old lesion
            #old_lesion_mask_file = anat_path + subj +'/session1/anat/reorient/old_lesion_seg.nii.gz' #choose this for ones with old lesion
            anat = glob.glob(anat_path + subj + '/' + s +
                             '/anat/anat2hr/anat_*.nii.gz'
                             )  #choose this for sessions 2 and 3
            anat_CSF = glob.glob(
                anat_path + subj +
                '/session1/seg_anat/segmentation/anat_*_pve_0.nii.gz'
            )  # don't change, same for all sessions
            anat_WM = glob.glob(
                anat_path + subj +
                '/session1/seg_anat/segmentation/anat_*_pve_2.nii.gz'
            )  # don't change, same for all sessions
            anat_GM = glob.glob(
                anat_path + subj +
                '/session1/seg_anat/segmentation/anat_*_pve_1.nii.gz'
            )  # don't change, same for all sessions
            anat2MNI_fieldwarp = glob.glob(
                anat_path + subj +
                '/session1/anat/nonlinear_reg/anat_*_fieldwarp.nii.gz'
            )  # don't change, same for all sessions

            if not os.path.isdir(data_path + subj + '/' + s):  # No data exists
                continue

            if not os.path.isfile(func1):
                print '1. functional file ' + func1 + ' not found. Skipping!'
                continue

            if not os.path.isfile(func2):
                print '2. functional file ' + func2 + ' not found. Skipping!'
                continue

            if not anat:
                print 'Preprocessed anatomical file not found. Skipping!'
                continue
            if len(anat) > 1:
                print 'WARNING: found multiple files of preprocessed anatomical image!'
                continue
            anat = anat[0]

            if not anat2MNI_fieldwarp:
                print 'Anatomical registration to MNI152-space field file not found. Skipping!'
                continue
            if len(anat2MNI_fieldwarp) > 1:
                print 'WARNING: found multiple files of anat2MNI fieldwarp!'
                continue
            anat2MNI_fieldwarp = anat2MNI_fieldwarp[0]

            if not anat_CSF:
                anat_CSF = glob.glob(
                    anat_path + subj + '/' + s +
                    '/seg_anat/segmentation/anat_*_pve_0.nii.gz')
                if not anat_CSF:
                    print 'Anatomical segmentation CSF file not found. Skipping!'
                    continue
            if len(anat_CSF) > 1:
                print 'WARNING: found multiple files of anatomical CSF file!'
                continue
            anat_CSF = anat_CSF[0]

            if not anat_WM:
                anat_WM = glob.glob(
                    anat_path + subj + '/' + s +
                    '/seg_anat/segmentation/anat_*_pve_2.nii.gz')
                if not anat_WM:
                    print 'Anatomical segmentation WM file not found. Skipping!'
                    continue
            if len(anat_WM) > 1:
                print 'WARNING: found multiple files of anatomical WM file!'
                continue
            anat_WM = anat_WM[0]

            if not anat_GM:
                anat_GM = glob.glob(
                    anat_path + subj + '/' + s +
                    '/seg_anat/segmentation/anat_*_pve_1.nii.gz')
                if not anat_GM:
                    print 'Anatomical segmentation GM file not found. Skipping!'
                    continue
            if len(anat_GM) > 1:
                print 'WARNING: found multiple files of anatomical GM file!'
                continue
            anat_GM = anat_GM[0]

            if not os.path.isdir(results_path + subj):
                os.mkdir(results_path + subj)

            if not os.path.isdir(results_path + subj + '/' + s):
                os.mkdir(results_path + subj + '/' + s)

            for data in acquisitions:

                os.chdir(results_path + subj + '/' + s)
                print "Currently processing subject: " + subj + '/' + s + ' ' + data

                #Initialize workflows
                workflow = pe.Workflow(name=data)

                workflow.base_dir = '.'
                inputnode = pe.Node(
                    interface=util.IdentityInterface(fields=['source_file']),
                    name='inputspec')
                outputnode = pe.Node(
                    interface=util.IdentityInterface(fields=['result_func']),
                    name='outputspec')

                if data == 'func1':
                    inputnode.inputs.source_file = func1
                else:
                    inputnode.inputs.source_file = func2

                # Remove n_dummies first volumes
                trim = pe.Node(interface=Trim(begin_index=n_dummies),
                               name='trim')
                workflow.connect(inputnode, 'source_file', trim, 'in_file')

                # Motion correction + slice timing correction
                realign4d = pe.Node(interface=SpaceTimeRealigner(),
                                    name='realign4d')
                #realign4d.inputs.ignore_exception=True
                realign4d.inputs.slice_times = 'asc_alt_siemens'

                realign4d.inputs.slice_info = 2  # horizontal slices
                realign4d.inputs.tr = mytr  # TR in seconds
                workflow.connect(trim, 'out_file', realign4d, 'in_file')

                # Reorient
                #deoblique = pe.Node(interface=afni.Warp(deoblique=True, outputtype='NIFTI_GZ'), name='deoblique') #leave out if you don't need this
                #workflow.connect(realign4d, 'out_file', deoblique, 'in_file')
                reorient = pe.Node(
                    interface=fsl.Reorient2Std(output_type='NIFTI_GZ'),
                    name='reorient')
                workflow.connect(realign4d, 'out_file', reorient, 'in_file')

                # AFNI skullstrip and mean image skullstrip
                tstat1 = pe.Node(interface=afni.TStat(args='-mean',
                                                      outputtype="NIFTI_GZ"),
                                 name='tstat1')
                automask = pe.Node(interface=afni.Automask(
                    dilate=1, outputtype="NIFTI_GZ"),
                                   name='automask')
                skullstrip = pe.Node(interface=afni.Calc(
                    expr='a*b', outputtype="NIFTI_GZ"),
                                     name='skullstrip')
                tstat2 = pe.Node(interface=afni.TStat(args='-mean',
                                                      outputtype="NIFTI_GZ"),
                                 name='tstat2')

                workflow.connect(reorient, 'out_file', tstat1, 'in_file')
                workflow.connect(tstat1, 'out_file', automask, 'in_file')
                workflow.connect(automask, 'out_file', skullstrip, 'in_file_b')
                workflow.connect(reorient, 'out_file', skullstrip, 'in_file_a')
                workflow.connect(skullstrip, 'out_file', tstat2, 'in_file')

                # Register to anatomical space #can be changed
                #mean2anat = pe.Node(fsl.FLIRT(bins=40, cost='normmi', dof=7, interp='nearestneighbour', searchr_x=[-180,180], searchr_y=[-180,180], searchr_z=[-180,180]), name='mean2anat')
                mean2anat = pe.Node(fsl.FLIRT(bins=40,
                                              cost='normmi',
                                              dof=7,
                                              interp='nearestneighbour'),
                                    name='mean2anat')
                #mean2anat = pe.Node(fsl.FLIRT(no_search=True), name='mean2anat')
                mean2anat.inputs.reference = anat
                workflow.connect(tstat2, 'out_file', mean2anat, 'in_file')

                # Transform mean functional image
                warpmean = pe.Node(interface=fsl.ApplyWarp(), name='warpmean')
                warpmean.inputs.ref_file = MNI_brain
                warpmean.inputs.field_file = anat2MNI_fieldwarp
                workflow.connect(mean2anat, 'out_matrix_file', warpmean,
                                 'premat')
                workflow.connect(tstat2, 'out_file', warpmean, 'in_file')

                # ----- inversion matrix and eroded brain mask for regression -----

                # create inverse matrix from mean2anat registration
                invmat = pe.Node(fsl.ConvertXFM(), name='invmat')
                invmat.inputs.invert_xfm = True
                workflow.connect(mean2anat, 'out_matrix_file', invmat,
                                 'in_file')

                # erode functional brain mask
                erode_brain = pe.Node(fsl.ImageMaths(), name='erode_brain')
                erode_brain.inputs.args = '-kernel boxv 3 -ero'
                workflow.connect(automask, 'out_file', erode_brain, 'in_file')

                # register GM mask to functional image space, this is done for quality control
                reg_GM = pe.Node(fsl.preprocess.ApplyXFM(), name='register_GM')
                reg_GM.inputs.apply_xfm = True
                reg_GM.inputs.in_file = anat_GM
                workflow.connect(tstat2, 'out_file', reg_GM, 'reference')
                workflow.connect(invmat, 'out_file', reg_GM, 'in_matrix_file')

                # --------- motion regression and censor signals ------------------

                # normalize motion parameters
                norm_motion = pe.Node(interface=Function(
                    input_names=['in_file'],
                    output_names=['out_file'],
                    function=normalize_motion_data),
                                      name='normalize_motion')
                workflow.connect(realign4d, 'par_file', norm_motion, 'in_file')

                # create censor file, for censoring motion
                get_censor = pe.Node(afni.OneDToolPy(), name='motion_censors')
                get_censor.inputs.set_nruns = 1
                get_censor.inputs.censor_motion = (censor_thr, 'motion')
                get_censor.inputs.show_censor_count = True
                if overwrite: get_censor.inputs.args = '-overwrite'
                workflow.connect(norm_motion, 'out_file', get_censor,
                                 'in_file')

                # compute motion parameter derivatives (for use in regression)
                deriv_motion = pe.Node(afni.OneDToolPy(), name='deriv_motion')
                deriv_motion.inputs.set_nruns = 1
                deriv_motion.inputs.derivative = True
                if overwrite: deriv_motion.inputs.args = '-overwrite'
                deriv_motion.inputs.out_file = 'motion_derivatives.txt'
                workflow.connect(norm_motion, 'out_file', deriv_motion,
                                 'in_file')

                # scale motion parameters and get quadratures
                quadr_motion = pe.Node(interface=Function(
                    input_names=['in_file', 'multicol'],
                    output_names=['out_file', 'out_quadr_file'],
                    function=scale_and_quadrature),
                                       name='quadr_motion')
                quadr_motion.inputs.multicol = True
                workflow.connect(norm_motion, 'out_file', quadr_motion,
                                 'in_file')

                # scale motion derivatives and get quadratures
                quadr_motion_deriv = pe.Node(interface=Function(
                    input_names=['in_file', 'multicol'],
                    output_names=['out_file', 'out_quadr_file'],
                    function=scale_and_quadrature),
                                             name='quadr_motion_deriv')
                quadr_motion_deriv.inputs.multicol = True
                workflow.connect(deriv_motion, 'out_file', quadr_motion_deriv,
                                 'in_file')

                # -------- CSF regression signals ---------------

                # threshold and erode CSF mask
                erode_CSF_mask = pe.Node(fsl.ImageMaths(),
                                         name='erode_CSF_mask')
                erode_CSF_mask.inputs.args = '-thr 0.5 -kernel boxv 3 -ero'
                erode_CSF_mask.inputs.in_file = anat_CSF

                # register CSF mask to functional image space
                reg_CSF_mask = pe.Node(fsl.preprocess.ApplyXFM(),
                                       name='register_CSF_mask')
                reg_CSF_mask.inputs.apply_xfm = True
                workflow.connect(tstat2, 'out_file', reg_CSF_mask, 'reference')
                workflow.connect(invmat, 'out_file', reg_CSF_mask,
                                 'in_matrix_file')

                # inverse lesion mask and remove it from CSF mask #remove this if you don't have a lesion mask
                inverse_lesion_mask = pe.Node(fsl.ImageMaths(),
                                              name='inverse_lesion_mask')
                inverse_lesion_mask.inputs.args = '-add 1 -rem 2'
                inverse_lesion_mask.inputs.in_file = lesion_mask_file
                rem_lesion = pe.Node(fsl.ImageMaths(), name='remove_lesion')
                workflow.connect(erode_CSF_mask, 'out_file', rem_lesion,
                                 'in_file')
                workflow.connect(inverse_lesion_mask, 'out_file', rem_lesion,
                                 'mask_file')
                '''
			# Transform lesion mask to MNI152 space #remove if lesion masks are already in MNI152 space
		        warp_lesion = pe.Node(interface=fsl.ApplyWarp(), name='warp_lesion')
		        warp_lesion.inputs.ref_file = MNI_brain
		        warp_lesion.inputs.field_file = anat2MNI_fieldwarp
			warp_lesion.inputs.in_file = lesion_mask_file
			warp_lesion.inputs.out_file = anat_path + subj +'/'+ s + '/anat/nonlinear_reg/lesion_seg_warp.nii.gz'
			warp_lesion.run()
			'''

                # inverse old lesion mask and remove it from CSF mask #remove this if you don't have a lesion mask
                if old_lesion_mask_file:
                    inverse_old_lesion_mask = pe.Node(
                        fsl.ImageMaths(), name='inverse_old_lesion_mask')
                    inverse_old_lesion_mask.inputs.args = '-add 1 -rem 3'
                    #inverse_old_lesion_mask.inputs.in_file = old_lesion_mask_file[0]
                    inverse_old_lesion_mask.inputs.in_file = old_lesion_mask_file
                    rem_old_lesion = pe.Node(fsl.ImageMaths(),
                                             name='remove_old_lesion')
                    workflow.connect(rem_lesion, 'out_file', rem_old_lesion,
                                     'in_file')
                    workflow.connect(inverse_old_lesion_mask, 'out_file',
                                     rem_old_lesion, 'mask_file')
                    workflow.connect(rem_old_lesion, 'out_file', reg_CSF_mask,
                                     'in_file')
                    '''
			    # Transform old lesion mask to MNI152 space #remove if lesion masks are already in MNI152 space
		            warp_old_lesion = pe.Node(interface=fsl.ApplyWarp(), name='warp_old_lesion')
		            warp_old_lesion.inputs.ref_file = MNI_brain
		            warp_old_lesion.inputs.field_file = anat2MNI_fieldwarp
			    warp_old_lesion.inputs.in_file = old_lesion_mask_file
			    warp_old_lesion.inputs.out_file = anat_path + subj +'/'+ s + '/anat/nonlinear_reg/old_lesion_seg_warp.nii.gz'
			    warp_old_lesion.run()
			    '''

                else:
                    workflow.connect(rem_lesion, 'out_file', reg_CSF_mask,
                                     'in_file')

                # threshold CSF mask and intersect with functional brain mask
                thr_CSF_mask = pe.Node(fsl.ImageMaths(),
                                       name='threshold_CSF_mask')
                thr_CSF_mask.inputs.args = '-thr 0.25'
                workflow.connect(reg_CSF_mask, 'out_file', thr_CSF_mask,
                                 'in_file')
                workflow.connect(erode_brain, 'out_file', thr_CSF_mask,
                                 'mask_file')

                # extract CSF values
                get_CSF_noise = pe.Node(fsl.ImageMeants(),
                                        name='get_CSF_noise')
                workflow.connect(skullstrip, 'out_file', get_CSF_noise,
                                 'in_file')
                workflow.connect(thr_CSF_mask, 'out_file', get_CSF_noise,
                                 'mask')

                # compute CSF noise derivatives
                deriv_CSF = pe.Node(afni.OneDToolPy(), name='deriv_CSF')
                deriv_CSF.inputs.set_nruns = 1
                deriv_CSF.inputs.derivative = True
                if overwrite: deriv_CSF.inputs.args = '-overwrite'
                deriv_CSF.inputs.out_file = 'CSF_derivatives.txt'
                workflow.connect(get_CSF_noise, 'out_file', deriv_CSF,
                                 'in_file')

                # scale SCF noise and get quadratures
                quadr_CSF = pe.Node(interface=Function(
                    input_names=['in_file', 'multicol'],
                    output_names=['out_file', 'out_quadr_file'],
                    function=scale_and_quadrature),
                                    name='quadr_CSF')
                quadr_CSF.inputs.multicol = False
                workflow.connect(get_CSF_noise, 'out_file', quadr_CSF,
                                 'in_file')

                # scale CSF noise derivatives and get quadratures
                quadr_CSF_deriv = pe.Node(interface=Function(
                    input_names=['in_file', 'multicol'],
                    output_names=['out_file', 'out_quadr_file'],
                    function=scale_and_quadrature),
                                          name='quadr_CSF_deriv')
                quadr_CSF_deriv.inputs.multicol = False
                workflow.connect(deriv_CSF, 'out_file', quadr_CSF_deriv,
                                 'in_file')

                # -------- WM regression signals -----------------

                # threshold and erode WM mask
                erode_WM_mask = pe.Node(fsl.ImageMaths(), name='erode_WM_mask')
                erode_WM_mask.inputs.args = '-thr 0.5 -kernel boxv 7 -ero'
                erode_WM_mask.inputs.in_file = anat_WM

                # registrer WM mask to functional image space
                reg_WM_mask = pe.Node(fsl.preprocess.ApplyXFM(),
                                      name='register_WM_mask')
                reg_WM_mask.inputs.apply_xfm = True
                workflow.connect(tstat2, 'out_file', reg_WM_mask, 'reference')
                workflow.connect(invmat, 'out_file', reg_WM_mask,
                                 'in_matrix_file')
                workflow.connect(erode_WM_mask, 'out_file', reg_WM_mask,
                                 'in_file')

                # create inverse nonlinear registration MNI2anat
                invwarp = pe.Node(fsl.InvWarp(output_type='NIFTI_GZ'),
                                  name='invwarp')
                invwarp.inputs.warp = anat2MNI_fieldwarp
                invwarp.inputs.reference = anat

                # transform ventricle mask to functional space
                reg_ventricles = pe.Node(fsl.ApplyWarp(),
                                         name='register_ventricle_mask')
                reg_ventricles.inputs.in_file = ventricle_mask
                workflow.connect(tstat2, 'out_file', reg_ventricles,
                                 'ref_file')
                workflow.connect(invwarp, 'inverse_warp', reg_ventricles,
                                 'field_file')
                workflow.connect(invmat, 'out_file', reg_ventricles, 'postmat')

                # threshold WM mask and intersect with functional brain mask
                thr_WM_mask = pe.Node(fsl.ImageMaths(),
                                      name='threshold_WM_mask')
                thr_WM_mask.inputs.args = '-thr 0.25'
                workflow.connect(reg_WM_mask, 'out_file', thr_WM_mask,
                                 'in_file')
                workflow.connect(erode_brain, 'out_file', thr_WM_mask,
                                 'mask_file')

                # remove ventricles from WM mask
                exclude_ventricles = pe.Node(fsl.ImageMaths(),
                                             name='exclude_ventricles')
                workflow.connect(thr_WM_mask, 'out_file', exclude_ventricles,
                                 'in_file')
                workflow.connect(reg_ventricles, 'out_file',
                                 exclude_ventricles, 'mask_file')

                # check that WM is collected from both hemispheres
                check_WM_bilat = pe.Node(interface=Function(
                    input_names=['in_file'],
                    output_names=['errors'],
                    function=check_bilateralism),
                                         name='check_WM_bilateralism')
                workflow.connect(exclude_ventricles, 'out_file',
                                 check_WM_bilat, 'in_file')

                # extract WM values
                get_WM_noise = pe.Node(fsl.ImageMeants(), name='get_WM_noise')
                workflow.connect(skullstrip, 'out_file', get_WM_noise,
                                 'in_file')
                workflow.connect(exclude_ventricles, 'out_file', get_WM_noise,
                                 'mask')

                # compute WM noise derivatives
                deriv_WM = pe.Node(afni.OneDToolPy(), name='deriv_WM')
                deriv_WM.inputs.set_nruns = 1
                deriv_WM.inputs.derivative = True
                if overwrite: deriv_WM.inputs.args = '-overwrite'
                deriv_WM.inputs.out_file = 'WM_derivatives.txt'
                workflow.connect(get_WM_noise, 'out_file', deriv_WM, 'in_file')

                # scale WM noise and get quadratures
                quadr_WM = pe.Node(interface=Function(
                    input_names=['in_file', 'multicol'],
                    output_names=['out_file', 'out_quadr_file'],
                    function=scale_and_quadrature),
                                   name='quadr_WM')
                quadr_WM.inputs.multicol = False
                workflow.connect(get_WM_noise, 'out_file', quadr_WM, 'in_file')

                # scale WM noise derivatives and get quadratures
                quadr_WM_deriv = pe.Node(interface=Function(
                    input_names=['in_file', 'multicol'],
                    output_names=['out_file', 'out_quadr_file'],
                    function=scale_and_quadrature),
                                         name='quadr_WM_deriv')
                quadr_WM_deriv.inputs.multicol = False
                workflow.connect(deriv_WM, 'out_file', quadr_WM_deriv,
                                 'in_file')

                # ---------- global regression signals ----------------

                if global_reg:
                    # register anatomical whole brain mask to functional image space
                    reg_glob_mask = pe.Node(fsl.preprocess.ApplyXFM(),
                                            name='register_global_mask')
                    reg_glob_mask.inputs.apply_xfm = True
                    reg_glob_mask.inputs.in_file = anat
                    workflow.connect(tstat2, 'out_file', reg_glob_mask,
                                     'reference')
                    workflow.connect(invmat, 'out_file', reg_glob_mask,
                                     'in_matrix_file')

                    # threshold anatomical brain mask and intersect with functional brain mask
                    thr_glob_mask = pe.Node(fsl.ImageMaths(),
                                            name='threshold_global_mask')
                    thr_glob_mask.inputs.args = '-thr -0.1'
                    workflow.connect(reg_glob_mask, 'out_file', thr_glob_mask,
                                     'in_file')
                    workflow.connect(erode_brain, 'out_file', thr_glob_mask,
                                     'mask_file')

                    # extract global signal values
                    get_glob_noise = pe.Node(fsl.ImageMeants(),
                                             name='get_global_noise')
                    workflow.connect(skullstrip, 'out_file', get_glob_noise,
                                     'in_file')
                    workflow.connect(thr_glob_mask, 'out_file', get_glob_noise,
                                     'mask')

                    # compute global noise derivative
                    deriv_glob = pe.Node(afni.OneDToolPy(),
                                         name='deriv_global')
                    deriv_glob.inputs.set_nruns = 1
                    deriv_glob.inputs.derivative = True
                    if overwrite: deriv_glob.inputs.args = '-overwrite'
                    deriv_glob.inputs.out_file = 'global_derivatives.txt'
                    workflow.connect(get_glob_noise, 'out_file', deriv_glob,
                                     'in_file')

                    # scale global noise and get quadratures
                    quadr_glob = pe.Node(interface=Function(
                        input_names=['in_file', 'multicol'],
                        output_names=['out_file', 'out_quadr_file'],
                        function=scale_and_quadrature),
                                         name='quadr_glob')
                    quadr_glob.inputs.multicol = False
                    workflow.connect(get_glob_noise, 'out_file', quadr_glob,
                                     'in_file')

                    # scale global noise derivatives and get quadratures
                    quadr_glob_deriv = pe.Node(interface=Function(
                        input_names=['in_file', 'multicol'],
                        output_names=['out_file', 'out_quadr_file'],
                        function=scale_and_quadrature),
                                               name='quadr_glob_deriv')
                    quadr_glob_deriv.inputs.multicol = False
                    workflow.connect(deriv_glob, 'out_file', quadr_glob_deriv,
                                     'in_file')

                # ---------- regression matrix ----------

                # create bandpass regressors, can not be easily implemented to workflow
                get_bandpass = pe.Node(interface=Function(
                    input_names=['minf', 'maxf', 'example_file', 'tr'],
                    output_names=['out_tuple'],
                    function=bandpass),
                                       name='bandpass_regressors')
                get_bandpass.inputs.minf = myminf
                get_bandpass.inputs.maxf = mymaxf
                get_bandpass.inputs.tr = mytr
                workflow.connect(norm_motion, 'out_file', get_bandpass,
                                 'example_file')

                # concatenate regressor time series
                cat_reg_name = 'cat_regressors'
                if global_reg: cat_reg_name = cat_reg_name + '_global'
                cat_reg = pe.Node(interface=Function(
                    input_names=[
                        'mot', 'motd', 'motq', 'motdq', 'CSF', 'CSFd', 'CSFq',
                        'CSFdq', 'WM', 'WMd', 'WMq', 'WMdq', 'include_global',
                        'glob', 'globd', 'globq', 'globdq'
                    ],
                    output_names=['reg_file_args'],
                    function=concatenate_regressors),
                                  name=cat_reg_name)
                cat_reg.inputs.include_global = global_reg
                workflow.connect(quadr_motion, 'out_file', cat_reg, 'mot')
                workflow.connect(quadr_motion_deriv, 'out_file', cat_reg,
                                 'motd')
                workflow.connect(quadr_motion, 'out_quadr_file', cat_reg,
                                 'motq')
                workflow.connect(quadr_motion_deriv, 'out_quadr_file', cat_reg,
                                 'motdq')
                workflow.connect(quadr_CSF, 'out_file', cat_reg, 'CSF')
                workflow.connect(quadr_CSF_deriv, 'out_file', cat_reg, 'CSFd')
                workflow.connect(quadr_CSF, 'out_quadr_file', cat_reg, 'CSFq')
                workflow.connect(quadr_CSF_deriv, 'out_quadr_file', cat_reg,
                                 'CSFdq')
                workflow.connect(quadr_WM, 'out_file', cat_reg, 'WM')
                workflow.connect(quadr_WM_deriv, 'out_file', cat_reg, 'WMd')
                workflow.connect(quadr_WM, 'out_quadr_file', cat_reg, 'WMq')
                workflow.connect(quadr_WM_deriv, 'out_quadr_file', cat_reg,
                                 'WMdq')
                if global_reg:
                    workflow.connect(quadr_glob, 'out_file', cat_reg, 'glob')
                    workflow.connect(quadr_glob_deriv, 'out_file', cat_reg,
                                     'globd')
                    workflow.connect(quadr_glob, 'out_quadr_file', cat_reg,
                                     'globq')
                    workflow.connect(quadr_glob_deriv, 'out_quadr_file',
                                     cat_reg, 'globdq')
                else:
                    cat_reg.inputs.glob = None
                    cat_reg.inputs.globd = None
                    cat_reg.inputs.globq = None
                    cat_reg.inputs.globdq = None

# create regression matrix
                deconvolve_name = 'deconvolve'
                if global_reg: deconvolve_name = deconvolve_name + '_global'
                deconvolve = pe.Node(afni.Deconvolve(), name=deconvolve_name)
                deconvolve.inputs.polort = 2  # contstant, linear and quadratic background signals removed
                deconvolve.inputs.fout = True
                deconvolve.inputs.tout = True
                deconvolve.inputs.x1D_stop = True
                deconvolve.inputs.force_TR = mytr
                workflow.connect(cat_reg, 'reg_file_args', deconvolve, 'args')
                workflow.connect(get_bandpass, 'out_tuple', deconvolve,
                                 'ortvec')
                workflow.connect([(skullstrip, deconvolve,
                                   [(('out_file', str2list), 'in_files')])])

                # regress out motion and other unwanted signals
                tproject_name = 'tproject'
                if global_reg: tproject_name = tproject_name + '_global'
                tproject = pe.Node(afni.TProject(outputtype="NIFTI_GZ"),
                                   name=tproject_name)
                tproject.inputs.TR = mytr
                tproject.inputs.polort = 0  # use matrix created with 3dDeconvolve, higher order polynomials not needed
                tproject.inputs.cenmode = 'NTRP'  # interpolate removed time points
                workflow.connect(get_censor, 'out_file', tproject, 'censor')
                workflow.connect(skullstrip, 'out_file', tproject, 'in_file')
                workflow.connect(automask, 'out_file', tproject, 'mask')
                workflow.connect(deconvolve, 'x1D', tproject, 'ort')

                # Transform all images
                warpall_name = 'warpall'
                if global_reg: warpall_name = warpall_name + '_global'
                warpall = pe.Node(interface=fsl.ApplyWarp(), name=warpall_name)
                warpall.inputs.ref_file = MNI_brain
                warpall.inputs.field_file = anat2MNI_fieldwarp
                workflow.connect(mean2anat, 'out_matrix_file', warpall,
                                 'premat')
                workflow.connect(tproject, 'out_file', warpall, 'in_file')
                workflow.connect(warpall, 'out_file', outputnode,
                                 'result_func')

                # Run workflow
                workflow.write_graph()
                workflow.run()

        print "FUNCTIONAL PREPROCESSING DONE! Results in ", results_path + subj + '/' + s
    except:
        print "Error with patient: ", subj
        traceback.print_exc()