Пример #1
0
#Wraps command **fslstats**
NodeHash_2fd0bda0 = pe.MapNode(interface = fsl.ImageStats(), name = 'NodeName_2fd0bda0', iterfield = ['in_file', 'mask_file'])
NodeHash_2fd0bda0.inputs.op_string = '-p 50'

#Wraps command **fslmaths**
NodeHash_ffd7a90 = pe.MapNode(interface = fsl.ApplyMask(), name = 'NodeName_ffd7a90', iterfield = ['in_file', 'mask_file'])

#Wraps command **fslmaths**
NodeHash_255ee520 = pe.MapNode(interface = fsl.MeanImage(), name = 'NodeName_255ee520', iterfield = ['in_file'])

#Custom interface wrapping function Getusan
NodeHash_12d6d9d0 = pe.MapNode(interface = firstlevelhelpers.Getusan, name = 'NodeName_12d6d9d0', iterfield = ['brightness_thresh', 'in_file'])

#Wraps command **susan**
NodeHash_f58d230 = pe.MapNode(interface = fsl.SUSAN(), name = 'NodeName_f58d230', iterfield = ['brightness_threshold', 'in_file', 'usans'])
NodeHash_f58d230.inputs.dimension = 3
NodeHash_f58d230.inputs.fwhm = 5
NodeHash_f58d230.inputs.use_median = 1

#Wraps command **fslmaths**
NodeHash_10dc3c10 = pe.MapNode(interface = fsl.ApplyMask(), name = 'NodeName_10dc3c10', iterfield = ['in_file', 'mask_file'])

#Custom interface wrapping function Getinormscale
NodeHash_1c9d2280 = pe.MapNode(interface = firstlevelhelpers.Getinormscale, name = 'NodeName_1c9d2280', iterfield = ['medianval'])

#Wraps command **fslmaths**
NodeHash_2ee27f90 = pe.MapNode(interface = fsl.BinaryMaths(), name = 'NodeName_2ee27f90', iterfield = ['in_file', 'operand_value'])
NodeHash_2ee27f90.inputs.operation = 'mul'

#Wraps command **fslmaths**
Пример #2
0
def denoise(subject, sessions, data_dir, wd, sink, TR):

    #initiate min func preproc workflow
    wf = pe.Workflow(name='DENOISE_aCompCor')
    wf.base_dir = wd
    wf.config['execution']['crashdump_dir'] = wf.base_dir + "/crash_files"

    ## set fsl output type to nii.gz
    fsl.FSLCommand.set_default_output_type('NIFTI_GZ')

    # I/O nodes
    inputnode = pe.Node(util.IdentityInterface(fields=['subjid']),
                        name='inputnode')
    inputnode.inputs.subjid = subject

    ds = pe.Node(nio.DataSink(base_directory=sink, parameterization=False),
                 name='sink')

    #infosource to interate over sessions: COND, EXT1, EXT2
    sessions_infosource = pe.Node(util.IdentityInterface(fields=['session']),
                                  name='session')
    sessions_infosource.iterables = [('session', sessions)]

    #select files
    templates = {
        'prefiltered': 'MPP/{subject}/{session}/prefiltered_func_data.nii.gz',
        'prefiltered_detrend':
        'MPP/{subject}/{session}/prefiltered_func_data_detrend.nii.gz',
        'prefiltered_detrend_Tmean':
        'MPP/{subject}/{session}/QC/prefiltered_func_data_detrend_Tmean.nii.gz',
        'prefiltered_mask':
        'MPP/{subject}/{session}/prefiltered_func_data_mask.nii.gz',
        'WM_msk': 'MASKS/{subject}/aparc_asec.WMmask_ero2EPI.nii.gz',
        'CSF_msk': 'MASKS/{subject}/aparc_asec.CSFmask_ero0EPI.nii.gz',
        'motion_par': 'MPP/{subject}/{session}/MOCO/func_data_stc_moco.par'
    }

    selectfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir),
                          name='selectfiles')

    wf.connect(inputnode, 'subjid', selectfiles, 'subject')
    wf.connect(sessions_infosource, 'session', selectfiles, 'session')
    wf.connect(sessions_infosource, 'session', ds, 'container')

    ##########################################################################
    ########################    START   ######################################
    ##########################################################################

    ###########################################################################
    ########################    No. 1  ######################################
    #the script outputs only std DVARS
    DVARS = pe.Node(util.Function(
        input_names=['in_file', 'in_mask', 'out_std_name'],
        output_names=['out_std', 'out_nstd', 'out_vx_std'],
        function=compute_dvars),
                    name='DVARS')

    DVARS.inputs.out_std_name = 'stdDVARS_pre.txt'
    wf.connect(selectfiles, 'prefiltered_detrend', DVARS, 'in_file')
    wf.connect(selectfiles, 'prefiltered_mask', DVARS, 'in_mask')
    wf.connect(DVARS, 'out_std', ds, 'QC.@DVARS')

    ###########################################################################
    ########################    No. 2   ######################################
    # DEMAN and DETREND the data, which are used to get nuisance regressors

    def run_demean_detrend(in_file):
        import nibabel as nb
        import numpy as np
        import os
        from scipy.signal import detrend

        img = nb.load(in_file)
        imgseries = img.get_data().astype(np.float32)
        imgseries_new = detrend(imgseries, type='linear')

        new = nb.nifti1.Nifti1Image(imgseries_new,
                                    header=img.get_header(),
                                    affine=img.get_affine())
        out_file = os.path.join(os.getcwd(),
                                'prefiltered_func_data_demean_detrend.nii.gz')
        new.to_filename(out_file)
        del imgseries, imgseries_new, new
        return out_file

    demean_detrend = pe.Node(util.Function(input_names=['in_file'],
                                           output_names=['out_file'],
                                           function=run_demean_detrend),
                             name='demean_detrend')

    wf.connect(selectfiles, 'prefiltered', demean_detrend, 'in_file')
    wf.connect(demean_detrend, 'out_file', ds, 'TEMP.@demean_detrend')
    ###########################################################################
    ########################    No. 3A   ######################################
    #PREPARE WM_CSF MASK

    WM_CSF_msk = pe.Node(fsl.BinaryMaths(operation='add'), name='wm_csf_msk')

    wf.connect(selectfiles, 'WM_msk', WM_CSF_msk, 'in_file')
    wf.connect(selectfiles, 'CSF_msk', WM_CSF_msk, 'operand_file')

    #take the coverage of the masks from functional data (essentially multiply by the mask from functional data)
    func_msk = pe.Node(fsl.BinaryMaths(operation='mul',
                                       out_file='WM_CSFmsk.nii.gz'),
                       name='func_masking')

    wf.connect(WM_CSF_msk, 'out_file', func_msk, 'in_file')
    wf.connect(selectfiles, 'prefiltered_mask', func_msk, 'operand_file')
    wf.connect(func_msk, 'out_file', ds, 'TEMP.@masks')

    ###########################################################################
    ########################    No. 3B   ######################################
    #PREPARE MOTION REGRESSSORS FRISTON 24 AND TRENDS

    friston24 = pe.Node(util.Function(input_names=['in_file'],
                                      output_names=['out_file'],
                                      function=calc_friston_twenty_four),
                        name='friston24')

    wf.connect(selectfiles, 'motion_par', friston24, 'in_file')
    wf.connect(friston24, 'out_file', ds, 'TEMP.@friston24')

    # linear and quadratic trends
    trends = pe.Node(util.Function(input_names=['nr_vols'],
                                   output_names=['out_file'],
                                   function=calc_trends),
                     name='trends')

    def get_nr_vols(in_file):
        import nibabel as nb
        img = nb.load(in_file)
        return img.shape[3]

    wf.connect(demean_detrend, ('out_file', get_nr_vols), trends, 'nr_vols')
    wf.connect(trends, 'out_file', ds, 'TEMP.@trends')

    ###########################################################################
    ########################    No. 3C   ######################################
    #aCOMP_COR
    aCompCor = pe.Node(util.Function(input_names=['in_file', 'in_mask'],
                                     output_names=['out_file'],
                                     function=calc_compcor),
                       name='aCompCor')

    wf.connect(demean_detrend, 'out_file', aCompCor, 'in_file')
    wf.connect(func_msk, 'out_file', aCompCor, 'in_mask')
    wf.connect(aCompCor, 'out_file', ds, 'TEMP.@aCompCor')

    ###########################################################################
    ########################    No. 4   ######################################
    #PREP the nuisance model

    #A is with Global Signal, and B is CompCor
    def mergetxt(filelist, fname):
        import pandas as pd
        import os
        for n, f in enumerate(filelist):
            if n == 0:
                data = pd.read_csv(f, header=None, sep='\t')
            else:
                data_new = pd.read_csv(f, header=None, sep='\t')
                data = pd.concat([data, data_new], axis=1)

        out_file = os.path.join(os.getcwd(), 'nuisance' + fname + '.mat')
        data.to_csv(out_file, index=False, header=None, sep='\t')
        return out_file

    merge_nuisance = pe.Node(util.Merge(3),
                             infields=['in1', 'in2', 'in3'],
                             name='merge_nuisance')

    wf.connect(aCompCor, 'out_file', merge_nuisance, 'in1')
    wf.connect(friston24, 'out_file', merge_nuisance, 'in2')
    wf.connect(trends, 'out_file', merge_nuisance, 'in3')

    nuisance_txt = pe.Node(util.Function(input_names=['filelist', 'fname'],
                                         output_names=['out_file'],
                                         function=mergetxt),
                           name='nuisance_txt')

    nuisance_txt.inputs.fname = '_model'
    wf.connect(merge_nuisance, 'out', nuisance_txt, 'filelist')
    wf.connect(nuisance_txt, 'out_file', ds, 'TEMP.@nuisance_txt')

    ###########################################################################
    ########################    No. 5   ######################################
    #run nuisance regression on prefiltered raw data

    regression = pe.Node(fsl.GLM(demean=True), name='regression')

    regression.inputs.out_res_name = 'residuals.nii.gz'
    regression.inputs.out_f_name = 'residuals_fstats.nii.gz'
    regression.inputs.out_pf_name = 'residuals_pstats.nii.gz'
    regression.inputs.out_z_name = 'residuals_zstats.nii.gz'

    wf.connect(nuisance_txt, 'out_file', regression, 'design')
    wf.connect(selectfiles, 'prefiltered', regression, 'in_file')
    wf.connect(selectfiles, 'prefiltered_mask', regression, 'mask')

    wf.connect(regression, 'out_f', ds, 'REGRESSION.@out_f_name')
    wf.connect(regression, 'out_pf', ds, 'REGRESSION.@out_pf_name')
    wf.connect(regression, 'out_z', ds, 'REGRESSION.@out_z_name')

    ########################   FIX HEADER TR AFTER FSL_GLM   #################
    fixhd = pe.Node(fsl.utils.CopyGeom(), name='fixhd')

    wf.connect(regression, 'out_res', fixhd, 'dest_file')
    wf.connect(selectfiles, 'prefiltered', fixhd, 'in_file')
    wf.connect(fixhd, 'out_file', ds, 'REGRESSION.@res_out')

    ###########################################################################
    ########################    No. 6   ######################################
    #apply HP FILTER of 0.01Hz
    #100/1.96/2 = 25.51
    hp_filter = pe.Node(fsl.maths.TemporalFilter(
        highpass_sigma=25.51, out_file='residuals_hp01.nii.gz'),
                        name='highpass')

    wf.connect(fixhd, 'out_file', hp_filter, 'in_file')
    wf.connect(hp_filter, 'out_file', ds, 'TEMP.@hp')

    #add the mean back for smoothing
    addmean = pe.Node(fsl.BinaryMaths(
        operation='add', out_file='filtered_func_data_hp01.nii.gz'),
                      name='addmean')

    wf.connect(hp_filter, 'out_file', addmean, 'in_file')
    wf.connect(selectfiles, 'prefiltered_detrend_Tmean', addmean,
               'operand_file')
    wf.connect(addmean, 'out_file', ds, '@out')

    ###########################################################################
    ########################    No. 7   ######################################
    ## COMPUTE POST DVARS
    DVARSpost = pe.Node(util.Function(
        input_names=['in_file', 'in_mask', 'out_std_name'],
        output_names=['out_std', 'out_nstd', 'out_vx_std'],
        function=compute_dvars),
                        name='DVARSpost')

    DVARSpost.inputs.out_std_name = 'stdDVARS_post.txt'

    wf.connect(addmean, 'out_file', DVARSpost, 'in_file')
    wf.connect(selectfiles, 'prefiltered_mask', DVARSpost, 'in_mask')
    wf.connect(DVARSpost, 'out_std', ds, 'QC.@DVARSpost')

    ###########################################################################
    ########################    No. 8   ######################################
    #SMOOTHING of 6fwhm

    merge_datasets = pe.Node(util.Merge(2),
                             infields=['in1', 'in2'],
                             name='merge_datasets')

    wf.connect(addmean, 'out_file', merge_datasets, 'in1')
    wf.connect(selectfiles, 'prefiltered_detrend', merge_datasets, 'in2')

    median = pe.MapNode(fsl.utils.ImageStats(op_string='-k %s -p 50'),
                        name='median',
                        iterfield=['in_file'])

    wf.connect(merge_datasets, 'out', median, 'in_file')
    wf.connect(selectfiles, 'prefiltered_mask', median, 'mask_file')

    smooth = pe.MapNode(
        fsl.SUSAN(fwhm=6.0),
        name='smooth',
        iterfield=['in_file', 'brightness_threshold', 'usans', 'out_file'])
    smooth.inputs.out_file = [
        'filtered_func_data_hp01_sm6fwhm.nii.gz',
        'prefiltered_func_data_detrend_sm6fwhm.nii.gz'
    ]

    merge_usans = pe.MapNode(util.Merge(2),
                             infields=['in1', 'in2'],
                             name='merge_usans',
                             iterfield=['in2'])

    wf.connect(selectfiles, 'prefiltered_detrend_Tmean', merge_usans, 'in1')
    wf.connect(median, 'out_stat', merge_usans, 'in2')

    def getbtthresh(medianvals):
        return [0.75 * val for val in medianvals]

    def getusans(x):
        return [[tuple([val[0], 0.75 * val[1]])] for val in x]

    wf.connect(merge_datasets, 'out', smooth, 'in_file')
    wf.connect(median, ('out_stat', getbtthresh), smooth,
               'brightness_threshold')
    wf.connect(merge_usans, ('out', getusans), smooth, 'usans')
    wf.connect(smooth, 'smoothed_file', ds, '@smoothout')

    ###########################################################################
    ########################    RUN   ######################################
    wf.write_graph(dotfilename='wf.dot',
                   graph2use='colored',
                   format='pdf',
                   simple_form=True)
    wf.run(plugin='MultiProc', plugin_args={'n_procs': 2})
    #wf.run()
    return
Пример #3
0
def init_ica_aroma_wf(metadata,
                      mem_gb,
                      omp_nthreads,
                      name='ica_aroma_wf',
                      susan_fwhm=6.0,
                      err_on_aroma_warn=False,
                      aroma_melodic_dim=-200,
                      use_fieldwarp=True):
    """
    This workflow wraps `ICA-AROMA`_ to identify and remove motion-related
    independent components from a BOLD time series.

    The following steps are performed:

    #. Remove non-steady state volumes from the bold series.
    #. Smooth data using FSL `susan`, with a kernel width FWHM=6.0mm.
    #. Run FSL `melodic` outside of ICA-AROMA to generate the report
    #. Run ICA-AROMA
    #. Aggregate identified motion components (aggressive) to TSV
    #. Return ``classified_motion_ICs`` and ``melodic_mix`` for user to complete
       non-aggressive denoising in T1w space
    #. Calculate ICA-AROMA-identified noise components
       (columns named ``AROMAAggrCompXX``)

    Additionally, non-aggressive denoising is performed on the BOLD series
    resampled into MNI space.

    There is a current discussion on whether other confounds should be extracted
    before or after denoising `here <http://nbviewer.jupyter.org/github/poldracklab/\
    fmriprep-notebooks/blob/922e436429b879271fa13e76767a6e73443e74d9/issue-817_\
    aroma_confounds.ipynb>`__.

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

        from fmriprep.workflows.bold.confounds import init_ica_aroma_wf
        wf = init_ica_aroma_wf(metadata={'RepetitionTime': 1.0},
                               mem_gb=3,
                               omp_nthreads=1)

    **Parameters**

        standard_spaces : str
            Spatial normalization template used as target when that
            registration step was previously calculated with
            :py:func:`~fmriprep.workflows.bold.registration.init_bold_reg_wf`.
            The template must be one of the MNI templates (fMRIPrep uses
            ``MNI152NLin2009cAsym`` by default).
        metadata : dict
            BIDS metadata for BOLD file
        mem_gb : float
            Size of BOLD file in GB
        omp_nthreads : int
            Maximum number of threads an individual process may use
        name : str
            Name of workflow (default: ``bold_tpl_trans_wf``)
        susan_fwhm : float
            Kernel width (FWHM in mm) for the smoothing step with
            FSL ``susan`` (default: 6.0mm)
        use_fieldwarp : bool
            Include SDC warp in single-shot transform from BOLD to MNI
        err_on_aroma_warn : bool
            Do not fail on ICA-AROMA errors
        aroma_melodic_dim: int
            Set the dimensionality of the MELODIC ICA decomposition.
            Negative numbers set a maximum on automatic dimensionality estimation.
            Positive numbers set an exact number of components to extract.
            (default: -200, i.e., estimate <=200 components)

    **Inputs**

        itk_bold_to_t1
            Affine transform from ``ref_bold_brain`` to T1 space (ITK format)
        anat2std_xfm
            ANTs-compatible affine-and-warp transform file
        name_source
            BOLD series NIfTI file
            Used to recover original information lost during processing
        skip_vols
            number of non steady state volumes
        bold_split
            Individual 3D BOLD volumes, not motion corrected
        bold_mask
            BOLD series mask in template space
        hmc_xforms
            List of affine transforms aligning each volume to ``ref_image`` in ITK format
        fieldwarp
            a :abbr:`DFM (displacements field map)` in ITK format
        movpar_file
            SPM-formatted motion parameters file

    **Outputs**

        aroma_confounds
            TSV of confounds identified as noise by ICA-AROMA
        aroma_noise_ics
            CSV of noise components identified by ICA-AROMA
        melodic_mix
            FSL MELODIC mixing matrix
        nonaggr_denoised_file
            BOLD series with non-aggressive ICA-AROMA denoising applied

    .. _ICA-AROMA: https://github.com/maartenmennes/ICA-AROMA

    """
    workflow = Workflow(name=name)
    workflow.__postdesc__ = """\
Automatic removal of motion artifacts using independent component analysis
[ICA-AROMA, @aroma] was performed on the *preprocessed BOLD on MNI space*
time-series after removal of non-steady state volumes and spatial smoothing
with an isotropic, Gaussian kernel of 6mm FWHM (full-width half-maximum).
Corresponding "non-aggresively" denoised runs were produced after such
smoothing.
Additionally, the "aggressive" noise-regressors were collected and placed
in the corresponding confounds file.
"""

    inputnode = pe.Node(niu.IdentityInterface(fields=[
        'bold_std',
        'bold_mask_std',
        'movpar_file',
        'name_source',
        'skip_vols',
        'templates',
    ]),
                        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'aroma_confounds', 'aroma_noise_ics', 'melodic_mix',
        'nonaggr_denoised_file'
    ]),
                         name='outputnode')

    select_std = pe.Node(KeySelect(fields=['bold_mask_std', 'bold_std']),
                         name='select_std',
                         run_without_submitting=True)
    select_std.inputs.key = 'MNI152NLin6Asym'

    rm_non_steady_state = pe.Node(niu.Function(function=_remove_volumes,
                                               output_names=['bold_cut']),
                                  name='rm_nonsteady')

    calc_median_val = pe.Node(fsl.ImageStats(op_string='-k %s -p 50'),
                              name='calc_median_val')
    calc_bold_mean = pe.Node(fsl.MeanImage(), name='calc_bold_mean')

    def _getusans_func(image, thresh):
        return [tuple([image, thresh])]

    getusans = pe.Node(niu.Function(function=_getusans_func,
                                    output_names=['usans']),
                       name='getusans',
                       mem_gb=0.01)

    smooth = pe.Node(fsl.SUSAN(fwhm=susan_fwhm), name='smooth')

    # melodic node
    melodic = pe.Node(fsl.MELODIC(no_bet=True,
                                  tr_sec=float(metadata['RepetitionTime']),
                                  mm_thresh=0.5,
                                  out_stats=True,
                                  dim=aroma_melodic_dim),
                      name="melodic")

    # ica_aroma node
    ica_aroma = pe.Node(ICA_AROMARPT(denoise_type='nonaggr',
                                     generate_report=True,
                                     TR=metadata['RepetitionTime']),
                        name='ica_aroma')

    add_non_steady_state = pe.Node(niu.Function(function=_add_volumes,
                                                output_names=['bold_add']),
                                   name='add_nonsteady')

    # extract the confound ICs from the results
    ica_aroma_confound_extraction = pe.Node(
        ICAConfounds(err_on_aroma_warn=err_on_aroma_warn),
        name='ica_aroma_confound_extraction')

    ds_report_ica_aroma = pe.Node(DerivativesDataSink(desc='aroma',
                                                      keep_dtype=True),
                                  name='ds_report_ica_aroma',
                                  run_without_submitting=True,
                                  mem_gb=DEFAULT_MEMORY_MIN_GB)

    def _getbtthresh(medianval):
        return 0.75 * medianval

    # connect the nodes
    workflow.connect([
        (inputnode, select_std, [('templates', 'keys'),
                                 ('bold_std', 'bold_std'),
                                 ('bold_mask_std', 'bold_mask_std')]),
        (inputnode, ica_aroma, [('movpar_file', 'motion_parameters')]),
        (inputnode, rm_non_steady_state, [('skip_vols', 'skip_vols')]),
        (select_std, rm_non_steady_state, [('bold_std', 'bold_file')]),
        (select_std, calc_median_val, [('bold_mask_std', 'mask_file')]),
        (rm_non_steady_state, calc_median_val, [('bold_cut', 'in_file')]),
        (rm_non_steady_state, calc_bold_mean, [('bold_cut', 'in_file')]),
        (calc_bold_mean, getusans, [('out_file', 'image')]),
        (calc_median_val, getusans, [('out_stat', 'thresh')]),
        # Connect input nodes to complete smoothing
        (rm_non_steady_state, smooth, [('bold_cut', 'in_file')]),
        (getusans, smooth, [('usans', 'usans')]),
        (calc_median_val, smooth, [(('out_stat', _getbtthresh),
                                    'brightness_threshold')]),
        # connect smooth to melodic
        (smooth, melodic, [('smoothed_file', 'in_files')]),
        (select_std, melodic, [('bold_mask_std', 'mask')]),
        # connect nodes to ICA-AROMA
        (smooth, ica_aroma, [('smoothed_file', 'in_file')]),
        (select_std, ica_aroma, [('bold_mask_std', 'report_mask'),
                                 ('bold_mask_std', 'mask')]),
        (melodic, ica_aroma, [('out_dir', 'melodic_dir')]),
        # generate tsvs from ICA-AROMA
        (ica_aroma, ica_aroma_confound_extraction, [('out_dir', 'in_directory')
                                                    ]),
        (inputnode, ica_aroma_confound_extraction, [('skip_vols', 'skip_vols')
                                                    ]),
        # output for processing and reporting
        (ica_aroma_confound_extraction,
         outputnode, [('aroma_confounds', 'aroma_confounds'),
                      ('aroma_noise_ics', 'aroma_noise_ics'),
                      ('melodic_mix', 'melodic_mix')]),
        (ica_aroma, add_non_steady_state, [('nonaggr_denoised_file',
                                            'bold_cut_file')]),
        (select_std, add_non_steady_state, [('bold_std', 'bold_file')]),
        (inputnode, add_non_steady_state, [('skip_vols', 'skip_vols')]),
        (add_non_steady_state, outputnode, [('bold_add',
                                             'nonaggr_denoised_file')]),
        (ica_aroma, ds_report_ica_aroma, [('out_report', 'in_file')]),
    ])

    return workflow
Пример #4
0
def create_susan_smooth(name="susan_smooth", separate_masks=True):
    """Create a SUSAN smoothing workflow

    Parameters
    ----------

    ::

        name : name of workflow (default: susan_smooth)
        separate_masks : separate masks for each run

    Inputs::

        inputnode.in_files : functional runs (filename or list of filenames)
        inputnode.fwhm : fwhm for smoothing with SUSAN (float or list of floats)
        inputnode.mask_file : mask used for estimating SUSAN thresholds (but not for smoothing)

    Outputs::

        outputnode.smoothed_files : functional runs (filename or list of filenames)

    Example
    -------

    >>> smooth = create_susan_smooth()
    >>> smooth.inputs.inputnode.in_files = 'f3.nii'
    >>> smooth.inputs.inputnode.fwhm = 5
    >>> smooth.inputs.inputnode.mask_file = 'mask.nii'
    >>> smooth.run() # doctest: +SKIP

    """

    # replaces the functionality of a "for loop"
    def cartesian_product(fwhms, in_files, usans, btthresh):
        from nipype.utils.filemanip import ensure_list
        # ensure all inputs are lists
        in_files = ensure_list(in_files)
        fwhms = [fwhms] if isinstance(fwhms, (int, float)) else fwhms
        # create cartesian product lists (s_<name> = single element of list)
        cart_in_file = [
            s_in_file for s_in_file in in_files for s_fwhm in fwhms
        ]
        cart_fwhm = [s_fwhm for s_in_file in in_files for s_fwhm in fwhms]
        cart_usans = [s_usans for s_usans in usans for s_fwhm in fwhms]
        cart_btthresh = [
            s_btthresh for s_btthresh in btthresh for s_fwhm in fwhms
        ]

        return cart_in_file, cart_fwhm, cart_usans, cart_btthresh

    susan_smooth = pe.Workflow(name=name)
    """
    Set up a node to define all inputs required for the preprocessing workflow

    """

    inputnode = pe.Node(interface=util.IdentityInterface(
        fields=['in_files', 'fwhm', 'mask_file']),
                        name='inputnode')
    """
    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
    """

    multi_inputs = pe.Node(util.Function(function=cartesian_product,
                                         output_names=[
                                             'cart_in_file', 'cart_fwhm',
                                             'cart_usans', 'cart_btthresh'
                                         ]),
                           name='multi_inputs')

    smooth = pe.MapNode(
        interface=fsl.SUSAN(),
        iterfield=['in_file', 'brightness_threshold', 'usans', 'fwhm'],
        name='smooth')
    """
    Determine the median value of the functional runs using the mask
    """

    if separate_masks:
        median = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                            iterfield=['in_file', 'mask_file'],
                            name='median')
    else:
        median = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                            iterfield=['in_file'],
                            name='median')
    susan_smooth.connect(inputnode, 'in_files', median, 'in_file')
    susan_smooth.connect(inputnode, 'mask_file', median, 'mask_file')
    """
    Mask the motion corrected functional runs with the dilated mask
    """

    if separate_masks:
        mask = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                   op_string='-mas'),
                          iterfield=['in_file', 'in_file2'],
                          name='mask')
    else:
        mask = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                   op_string='-mas'),
                          iterfield=['in_file'],
                          name='mask')
    susan_smooth.connect(inputnode, 'in_files', mask, 'in_file')
    susan_smooth.connect(inputnode, 'mask_file', mask, 'in_file2')
    """
    Determine the mean image from each functional run
    """

    meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                   suffix='_mean'),
                          iterfield=['in_file'],
                          name='meanfunc2')
    susan_smooth.connect(mask, 'out_file', meanfunc, 'in_file')
    """
    Merge the median values with the mean functional images into a coupled list
    """

    merge = pe.Node(interface=util.Merge(2, axis='hstack'), name='merge')
    susan_smooth.connect(meanfunc, 'out_file', merge, 'in1')
    susan_smooth.connect(median, 'out_stat', merge, 'in2')
    """
    Define a function to get the brightness threshold for SUSAN
    """

    susan_smooth.connect([
        (inputnode, multi_inputs, [('in_files', 'in_files'),
                                   ('fwhm', 'fwhms')]),
        (median, multi_inputs, [(('out_stat', getbtthresh), 'btthresh')]),
        (merge, multi_inputs, [(('out', getusans), 'usans')]),
        (multi_inputs, smooth, [('cart_in_file', 'in_file'),
                                ('cart_fwhm', 'fwhm'),
                                ('cart_btthresh', 'brightness_threshold'),
                                ('cart_usans', 'usans')]),
    ])

    outputnode = pe.Node(
        interface=util.IdentityInterface(fields=['smoothed_files']),
        name='outputnode')

    susan_smooth.connect(smooth, 'smoothed_file', outputnode, 'smoothed_files')

    return susan_smooth
Пример #5
0
preproc_wf.connect(maskfunc, 'out_file', smooth_median, 'in_file')
preproc_wf.connect(fs_threshold2, ('binary_file', pickfirst), smooth_median,
                   'mask_file')

smooth_meanfunc = pe.MapNode(fsl.ImageMaths(op_string='-Tmean',
                                            suffix='_mean'),
                             iterfield=['in_file'],
                             name='susan_smooth_meanfunc')
preproc_wf.connect(maskfunc, 'out_file', smooth_meanfunc, 'in_file')

smooth_merge = pe.Node(util.Merge(2, axis='hstack'), name='susan_smooth_merge')
preproc_wf.connect(smooth_meanfunc, 'out_file', smooth_merge, 'in1')
preproc_wf.connect(smooth_median, 'out_stat', smooth_merge, 'in2')

susan_smooth = pe.MapNode(
    fsl.SUSAN(),
    iterfield=['in_file', 'brightness_threshold', 'usans'],
    name='susan_smooth')
susan_smooth.inputs.fwhm = 6.
preproc_wf.connect(maskfunc, 'out_file', susan_smooth, 'in_file')
preproc_wf.connect(smooth_median, ('out_stat', getbtthresh), susan_smooth,
                   'brightness_threshold')
preproc_wf.connect(smooth_merge, ('out', getusans), susan_smooth, 'usans')

# Mask the smoothed data with the dilated mask
maskfunc2 = pe.MapNode(fsl.ImageMaths(suffix='_mask', op_string='-mas'),
                       iterfield=['in_file'],
                       name='susan_mask')
preproc_wf.connect(susan_smooth, 'smoothed_file', maskfunc2, 'in_file')
preproc_wf.connect(fs_threshold2, ('binary_file', pickfirst), maskfunc2,
                   'in_file2')
Пример #6
0
                 ('sub-{subject_id}' + '_task-' + taskName + '_events.tsv'))
}

# Create SelectFiles node
sf = Node(SelectFiles(templates, sort_filelist=True), name='sf')
sf.iterables = [('subject_id', subject_list)]

###########
#
# FMRI PREPROCESSING NODES
#
###########

# smoothing with SUSAN
susan = Node(
    fsl.SUSAN(brightness_threshold=2000.0,
              fwhm=6.0),  # smoothing filter width (6mm, isotropic)
    name='susan')

# masking the fMRI with a brain mask
applymask = Node(fsl.ApplyMask(), name='applymask')

###########
#
# NODE TO CALL EVENT INFO FUNCTION
#
###########
taskevents = Node(interface=Function(
    input_names=['fileEvent'],
    output_names=['subject_info', 'contrast_list'],
    function=TaskEvents),
                  name='taskevents')
Пример #7
0
def create_susan_smooth(name="susan_smooth", separate_masks=True):
    """Create a SUSAN smoothing workflow

    Parameters
    ----------

    ::

        name : name of workflow (default: susan_smooth)
        separate_masks : separate masks for each run

    Inputs::

        inputnode.in_files : functional runs (filename or list of filenames)
        inputnode.fwhm : fwhm for smoothing with SUSAN
        inputnode.mask_file : mask used for estimating SUSAN thresholds (but not for smoothing)

    Outputs::

        outputnode.smoothed_files : functional runs (filename or list of filenames)

    Example
    -------

    >>> smooth = create_susan_smooth()
    >>> smooth.inputs.inputnode.in_files = 'f3.nii'
    >>> smooth.inputs.inputnode.fwhm = 5
    >>> smooth.inputs.inputnode.mask_file = 'mask.nii'
    >>> smooth.run() # doctest: +SKIP

    """

    susan_smooth = pe.Workflow(name=name)
    """
    Set up a node to define all inputs required for the preprocessing workflow

    """

    inputnode = pe.Node(interface=util.IdentityInterface(
        fields=['in_files', 'fwhm', 'mask_file']),
                        name='inputnode')
    """
    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 = pe.MapNode(interface=fsl.SUSAN(),
                        iterfield=['in_file', 'brightness_threshold', 'usans'],
                        name='smooth')
    """
    Determine the median value of the functional runs using the mask
    """

    if separate_masks:
        median = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                            iterfield=['in_file', 'mask_file'],
                            name='median')
    else:
        median = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'),
                            iterfield=['in_file'],
                            name='median')
    susan_smooth.connect(inputnode, 'in_files', median, 'in_file')
    susan_smooth.connect(inputnode, 'mask_file', median, 'mask_file')
    """
    Mask the motion corrected functional runs with the dilated mask
    """

    if separate_masks:
        mask = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                   op_string='-mas'),
                          iterfield=['in_file', 'in_file2'],
                          name='mask')
    else:
        mask = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                   op_string='-mas'),
                          iterfield=['in_file'],
                          name='mask')
    susan_smooth.connect(inputnode, 'in_files', mask, 'in_file')
    susan_smooth.connect(inputnode, 'mask_file', mask, 'in_file2')
    """
    Determine the mean image from each functional run
    """

    meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                   suffix='_mean'),
                          iterfield=['in_file'],
                          name='meanfunc2')
    susan_smooth.connect(mask, 'out_file', meanfunc, 'in_file')
    """
    Merge the median values with the mean functional images into a coupled list
    """

    merge = pe.Node(interface=util.Merge(2, axis='hstack'), name='merge')
    susan_smooth.connect(meanfunc, 'out_file', merge, 'in1')
    susan_smooth.connect(median, 'out_stat', merge, 'in2')
    """
    Define a function to get the brightness threshold for SUSAN
    """
    susan_smooth.connect(inputnode, 'fwhm', smooth, 'fwhm')
    susan_smooth.connect(inputnode, 'in_files', smooth, 'in_file')
    susan_smooth.connect(median, ('out_stat', getbtthresh), smooth,
                         'brightness_threshold')
    susan_smooth.connect(merge, ('out', getusans), smooth, 'usans')

    outputnode = pe.Node(
        interface=util.IdentityInterface(fields=['smoothed_files']),
        name='outputnode')

    susan_smooth.connect(smooth, 'smoothed_file', outputnode, 'smoothed_files')

    return susan_smooth
                               name='NodeName_19cad2d0',
                               iterfield=['in_file', 'mask_file'])

#Wraps command **fslmaths**
NodeHash_1ba62de0 = pe.MapNode(interface=fsl.MeanImage(),
                               name='NodeName_1ba62de0',
                               iterfield=['in_file'])

#Custom interface wrapping function Getusan
NodeHash_1b45a190 = pe.MapNode(interface=firstlevelhelpers.Getusan,
                               name='NodeName_1b45a190',
                               iterfield=['brightness_thresh', 'in_file'])

#Wraps command **susan**
NodeHash_1be88ee0 = pe.MapNode(
    interface=fsl.SUSAN(),
    name='NodeName_1be88ee0',
    iterfield=['brightness_threshold', 'in_file', 'usans'])
NodeHash_1be88ee0.inputs.dimension = 3
NodeHash_1be88ee0.inputs.fwhm = 5
NodeHash_1be88ee0.inputs.use_median = 1

#Wraps command **fslmaths**
NodeHash_1becb6b0 = pe.MapNode(interface=fsl.ApplyMask(),
                               name='NodeName_1becb6b0',
                               iterfield=['in_file', 'mask_file'])

#Custom interface wrapping function Getinormscale
NodeHash_1dce7160 = pe.MapNode(interface=firstlevelhelpers.Getinormscale,
                               name='NodeName_1dce7160',
                               iterfield=['medianval'])
Пример #9
0
"""
Merge the median values with the mean functional images into a coupled list
"""

mergenode = pe.Node(interface=util.Merge(2, axis='hstack'),
                    name='merge')
preproc.connect(meanfunc2, 'out_file', mergenode, 'in1')
preproc.connect(medianval, 'out_stat', mergenode, 'in2')


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

smooth = pe.MapNode(interface=fsl.SUSAN(),
                    iterfield=['in_file', 'brightness_threshold', 'usans'],
                    name='smooth')

"""
Define a function to get the brightness threshold for SUSAN
"""


def getbtthresh(medianvals):
    return [0.75 * val for val in medianvals]


def getusans(x):
    return [[tuple([val[0], 0.75 * val[1]])] for val in x]
Пример #10
0
def init_ica_aroma_wf(
    mem_gb,
    metadata,
    omp_nthreads,
    aroma_melodic_dim=-200,
    err_on_aroma_warn=False,
    name="ica_aroma_wf",
    susan_fwhm=6.0,
    use_fieldwarp=True,
):
    """
    Build a workflow that runs `ICA-AROMA`_.

    This workflow wraps `ICA-AROMA`_ to identify and remove motion-related
    independent components from a BOLD time series.

    The following steps are performed:

    #. Remove non-steady state volumes from the bold series.
    #. Smooth data using FSL `susan`, with a kernel width FWHM=6.0mm.
    #. Run FSL `melodic` outside of ICA-AROMA to generate the report
    #. Run ICA-AROMA
    #. Aggregate identified motion components (aggressive) to TSV
    #. Return ``classified_motion_ICs`` and ``melodic_mix`` for user to complete
       non-aggressive denoising in T1w space
    #. Calculate ICA-AROMA-identified noise components
       (columns named ``AROMAAggrCompXX``)

    Additionally, non-aggressive denoising is performed on the BOLD series
    resampled into MNI space.

    There is a current discussion on whether other confounds should be extracted
    before or after denoising `here
    <http://nbviewer.jupyter.org/github/nipreps/fmriprep-notebooks/blob/922e436429b879271fa13e76767a6e73443e74d9/issue-817_aroma_confounds.ipynb>`__.

    .. _ICA-AROMA: https://github.com/maartenmennes/ICA-AROMA

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

            from fprodents.workflows.bold.confounds import init_ica_aroma_wf
            wf = init_ica_aroma_wf(
                mem_gb=3,
                metadata={'RepetitionTime': 1.0},
                omp_nthreads=1)

    Parameters
    ----------
    metadata : :obj:`dict`
        BIDS metadata for BOLD file
    mem_gb : :obj:`float`
        Size of BOLD file in GB
    omp_nthreads : :obj:`int`
        Maximum number of threads an individual process may use
    name : :obj:`str`
        Name of workflow (default: ``bold_tpl_trans_wf``)
    susan_fwhm : :obj:`float`
        Kernel width (FWHM in mm) for the smoothing step with
        FSL ``susan`` (default: 6.0mm)
    use_fieldwarp : :obj:`bool`
        Include SDC warp in single-shot transform from BOLD to MNI
    err_on_aroma_warn : :obj:`bool`
        Do not fail on ICA-AROMA errors
    aroma_melodic_dim : :obj:`int`
        Set the dimensionality of the MELODIC ICA decomposition.
        Negative numbers set a maximum on automatic dimensionality estimation.
        Positive numbers set an exact number of components to extract.
        (default: -200, i.e., estimate <=200 components)

    Inputs
    ------
    itk_bold_to_t1
        Affine transform from ``ref_bold_brain`` to T1 space (ITK format)
    anat2std_xfm
        ANTs-compatible affine-and-warp transform file
    name_source
        BOLD series NIfTI file
        Used to recover original information lost during processing
    skip_vols
        number of non steady state volumes
    bold_split
        Individual 3D BOLD volumes, not motion corrected
    bold_mask
        BOLD series mask in template space
    hmc_xforms
        List of affine transforms aligning each volume to ``ref_image`` in ITK format
    fieldwarp
        a :abbr:`DFM (displacements field map)` in ITK format
    movpar_file
        SPM-formatted motion parameters file

    Outputs
    -------
    aroma_confounds
        TSV of confounds identified as noise by ICA-AROMA
    aroma_noise_ics
        CSV of noise components identified by ICA-AROMA
    melodic_mix
        FSL MELODIC mixing matrix
    nonaggr_denoised_file
        BOLD series with non-aggressive ICA-AROMA denoising applied

    """
    from niworkflows.engine.workflows import LiterateWorkflow as Workflow
    from niworkflows.interfaces.segmentation import ICA_AROMARPT
    from niworkflows.interfaces.utility import KeySelect, TSV2JSON

    workflow = Workflow(name=name)
    workflow.__postdesc__ = """\
Automatic removal of motion artifacts using independent component analysis
[ICA-AROMA, @aroma] was performed on the *preprocessed BOLD on MNI space*
time-series after removal of non-steady state volumes and spatial smoothing
with an isotropic, Gaussian kernel of 6mm FWHM (full-width half-maximum).
Corresponding "non-aggresively" denoised runs were produced after such
smoothing.
Additionally, the "aggressive" noise-regressors were collected and placed
in the corresponding confounds file.
"""

    inputnode = pe.Node(
        niu.IdentityInterface(fields=[
            "bold_std",
            "bold_mask_std",
            "movpar_file",
            "name_source",
            "skip_vols",
            "spatial_reference",
        ]),
        name="inputnode",
    )

    outputnode = pe.Node(
        niu.IdentityInterface(fields=[
            "aroma_confounds",
            "aroma_noise_ics",
            "melodic_mix",
            "nonaggr_denoised_file",
            "aroma_metadata",
        ]),
        name="outputnode",
    )

    # extract out to BOLD base
    select_std = pe.Node(
        KeySelect(fields=["bold_mask_std", "bold_std"]),
        name="select_std",
        run_without_submitting=True,
    )
    select_std.inputs.key = "MNI152NLin6Asym_res-2"

    rm_non_steady_state = pe.Node(
        niu.Function(function=_remove_volumes, output_names=["bold_cut"]),
        name="rm_nonsteady",
    )

    calc_median_val = pe.Node(fsl.ImageStats(op_string="-k %s -p 50"),
                              name="calc_median_val")
    calc_bold_mean = pe.Node(fsl.MeanImage(), name="calc_bold_mean")

    def _getusans_func(image, thresh):
        return [tuple([image, thresh])]

    getusans = pe.Node(
        niu.Function(function=_getusans_func, output_names=["usans"]),
        name="getusans",
        mem_gb=0.01,
    )

    smooth = pe.Node(fsl.SUSAN(fwhm=susan_fwhm), name="smooth")

    # melodic node
    melodic = pe.Node(
        fsl.MELODIC(
            no_bet=True,
            tr_sec=float(metadata["RepetitionTime"]),
            mm_thresh=0.5,
            out_stats=True,
            dim=aroma_melodic_dim,
        ),
        name="melodic",
    )

    # ica_aroma node
    ica_aroma = pe.Node(
        ICA_AROMARPT(
            denoise_type="nonaggr",
            generate_report=True,
            TR=metadata["RepetitionTime"],
            args="-np",
        ),
        name="ica_aroma",
    )

    add_non_steady_state = pe.Node(
        niu.Function(function=_add_volumes, output_names=["bold_add"]),
        name="add_nonsteady",
    )

    # extract the confound ICs from the results
    ica_aroma_confound_extraction = pe.Node(
        ICAConfounds(err_on_aroma_warn=err_on_aroma_warn),
        name="ica_aroma_confound_extraction",
    )

    ica_aroma_metadata_fmt = pe.Node(
        TSV2JSON(
            index_column="IC",
            output=None,
            enforce_case=True,
            additional_metadata={
                "Method": {
                    "Name": "ICA-AROMA",
                    "Version": getenv("AROMA_VERSION", "n/a"),
                }
            },
        ),
        name="ica_aroma_metadata_fmt",
    )

    ds_report_ica_aroma = pe.Node(
        DerivativesDataSink(desc="aroma",
                            datatype="figures",
                            dismiss_entities=("echo", )),
        name="ds_report_ica_aroma",
        run_without_submitting=True,
        mem_gb=DEFAULT_MEMORY_MIN_GB,
    )

    def _getbtthresh(medianval):
        return 0.75 * medianval

    # connect the nodes
    # fmt:off
    workflow.connect([
        (inputnode, select_std, [('spatial_reference', 'keys'),
                                 ('bold_std', 'bold_std'),
                                 ('bold_mask_std', 'bold_mask_std')]),
        (inputnode, ica_aroma, [('movpar_file', 'motion_parameters')]),
        (inputnode, rm_non_steady_state, [('skip_vols', 'skip_vols')]),
        (select_std, rm_non_steady_state, [('bold_std', 'bold_file')]),
        (select_std, calc_median_val, [('bold_mask_std', 'mask_file')]),
        (rm_non_steady_state, calc_median_val, [('bold_cut', 'in_file')]),
        (rm_non_steady_state, calc_bold_mean, [('bold_cut', 'in_file')]),
        (calc_bold_mean, getusans, [('out_file', 'image')]),
        (calc_median_val, getusans, [('out_stat', 'thresh')]),
        # Connect input nodes to complete smoothing
        (rm_non_steady_state, smooth, [('bold_cut', 'in_file')]),
        (getusans, smooth, [('usans', 'usans')]),
        (calc_median_val, smooth, [(('out_stat', _getbtthresh),
                                    'brightness_threshold')]),
        # connect smooth to melodic
        (smooth, melodic, [('smoothed_file', 'in_files')]),
        (select_std, melodic, [('bold_mask_std', 'mask')]),
        # connect nodes to ICA-AROMA
        (smooth, ica_aroma, [('smoothed_file', 'in_file')]),
        (select_std, ica_aroma, [('bold_mask_std', 'report_mask'),
                                 ('bold_mask_std', 'mask')]),
        (melodic, ica_aroma, [('out_dir', 'melodic_dir')]),
        # generate tsvs from ICA-AROMA
        (ica_aroma, ica_aroma_confound_extraction, [('out_dir', 'in_directory')
                                                    ]),
        (inputnode, ica_aroma_confound_extraction, [('skip_vols', 'skip_vols')
                                                    ]),
        (ica_aroma_confound_extraction, ica_aroma_metadata_fmt,
         [('aroma_metadata', 'in_file')]),
        # output for processing and reporting
        (ica_aroma_confound_extraction,
         outputnode, [('aroma_confounds', 'aroma_confounds'),
                      ('aroma_noise_ics', 'aroma_noise_ics'),
                      ('melodic_mix', 'melodic_mix')]),
        (ica_aroma_metadata_fmt, outputnode, [('output', 'aroma_metadata')]),
        (ica_aroma, add_non_steady_state, [('nonaggr_denoised_file',
                                            'bold_cut_file')]),
        (select_std, add_non_steady_state, [('bold_std', 'bold_file')]),
        (inputnode, add_non_steady_state, [('skip_vols', 'skip_vols')]),
        (add_non_steady_state, outputnode, [('bold_add',
                                             'nonaggr_denoised_file')]),
        (ica_aroma, ds_report_ica_aroma, [('out_report', 'in_file')]),
    ])
    # fmt:on

    return workflow
#runinfo.inputs.thr = thr # set threshold of scrubbing

## adding node for the saveScrub functions
svScrub = pe.Node(util.Function(
    input_names = ['regressors_file', 'thr'], output_names = ['perFile'],
    function = saveScrub), name = 'svScrub'
    )

svScrub.inputs.thr = thr
# %%
skip = pe.Node(interface=fsl.ExtractROI(), name = 'skip')
skip.inputs.t_min = removeTR
skip.inputs.t_size = lastTR

# %%
susan =  pe.Node(interface=fsl.SUSAN(), name = 'susan') #create_susan_smooth()
susan.inputs.fwhm = fwhm
susan.inputs.brightness_threshold = 1000.0
# %%
modelfit = pe.Workflow(name='fsl_fit', base_dir= output_dir)
"""
Use :class:`nipype.algorithms.modelgen.SpecifyModel` to generate design information.
"""

modelspec = pe.Node(interface=model.SpecifyModel(),
                    name="modelspec")

modelspec.inputs.input_units = 'scans'
modelspec.inputs.time_repetition = tr
modelspec.inputs.high_pass_filter_cutoff= 120
"""
Пример #12
0
def init_ica_aroma_wf(
    dt,
    aroma_melodic_dim=-200,
    err_on_aroma_warn=False,
    susan_fwhm=6.0,
    name='ica_aroma_wf',
):
    """
    Build a workflow that runs `ICA-AROMA`_.

    This workflow wraps `ICA-AROMA`_ to identify and remove motion-related
    independent components from a BOLD time series.

    The following steps are performed:

    #. Remove non-steady state volumes from the bold series.
    #. Smooth data using FSL `susan`, with a kernel width FWHM=6.0mm.
    #. Run FSL `melodic` outside of ICA-AROMA to generate the report
    #. Run ICA-AROMA
    #. Aggregate identified motion components (aggressive) to TSV
    #. Return ``classified_motion_ICs`` and ``melodic_mix`` for user to complete
       non-aggressive denoising in T1w space
    #. Calculate ICA-AROMA-identified noise components
       (columns named ``AROMAAggrCompXX``)

    There is a current discussion on whether other confounds should be extracted
    before or after denoising `here
    <http://nbviewer.jupyter.org/github/nipreps/fmriprep-notebooks/blob/922e436429b879271fa13e76767a6e73443e74d9/issue-817_aroma_confounds.ipynb>`__.

    .. _ICA-AROMA: https://github.com/maartenmennes/ICA-AROMA

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

            from ecp.workflows.confounds import init_ica_aroma_wf
            wf = init_ica_aroma_wf(
                dt=1.0)

    Parameters
    ----------
    dt : :obj:`float`
        bold repetition time
    aroma_melodic_dim : :obj:`int`
        Set the dimensionality of the MELODIC ICA decomposition.
        Negative numbers set a maximum on automatic dimensionality estimation.
        Positive numbers set an exact number of components to extract.
        (default: -200, i.e., estimate <=200 components)
    err_on_aroma_warn : :obj:`bool`
        Do not fail on ICA-AROMA errors
    susan_fwhm : :obj:`float`
        Kernel width (FWHM in mm) for the smoothing step with
        FSL ``susan`` (default: 6.0mm)
    name : :obj:`str`
        Name of workflow (default: ``ica_aroma_wf``)

    Inputs
    ------
    bold_std
        BOLD series NIfTI file in MNI152NLin6Asym space
    bold_mask_std
        BOLD mask for MNI152NLin6Asym space
    movpar_file
        movement parameter file
    skip_vols
        number of non steady state volumes
        
    Outputs
    -------
    aroma_confounds
        TSV of confounds identified as noise by ICA-AROMA
    aroma_noise_ics
        CSV of noise components identified by ICA-AROMA
    melodic_mix
        FSL MELODIC mixing matrix
    aroma_metatdata
        metadata
    out_report
        aroma out report

    """
    from niworkflows.engine.workflows import LiterateWorkflow as Workflow
    from niworkflows.interfaces.segmentation import ICA_AROMARPT
    from niworkflows.interfaces.utility import KeySelect
    from niworkflows.interfaces.utils import TSV2JSON

    workflow = Workflow(name=name)
    workflow.__postdesc__ = """\
Automatic removal of motion artifacts using independent component analysis
[ICA-AROMA, @aroma] was performed on the *preprocessed BOLD on MNI space*
time-series after removal of non-steady state volumes and spatial smoothing
with an isotropic, Gaussian kernel of 6mm FWHM (full-width half-maximum).
The "aggressive" noise-regressors were collected and placed
in the corresponding confounds file.
"""

    inputnode = pe.Node(niu.IdentityInterface(fields=[
        'bold_std',
        'bold_mask_std',
        'movpar_file',
        'skip_vols',
    ]),
                        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'aroma_confounds', 'aroma_noise_ics', 'melodic_mix', 'aroma_metadata',
        'out_report'
    ]),
                         name='outputnode')

    # extract out to BOLD base
    rm_non_steady_state = pe.Node(Trim(), name='rm_nonsteady')
    trim_movement = pe.Node(TrimMovement(), name='trim_movement')

    calc_median_val = pe.Node(fsl.ImageStats(op_string='-k %s -p 50'),
                              name='calc_median_val')
    calc_bold_mean = pe.Node(fsl.MeanImage(), name='calc_bold_mean')

    def _getusans_func(image, thresh):
        return [tuple([image, thresh])]

    getusans = pe.Node(niu.Function(function=_getusans_func,
                                    output_names=['usans']),
                       name='getusans',
                       mem_gb=0.01)

    smooth = pe.Node(fsl.SUSAN(fwhm=susan_fwhm), name='smooth')

    # melodic node
    melodic = pe.Node(fsl.MELODIC(no_bet=True,
                                  tr_sec=dt,
                                  mm_thresh=0.5,
                                  out_stats=True,
                                  dim=aroma_melodic_dim),
                      name="melodic")

    # ica_aroma node
    ica_aroma = pe.Node(ICA_AROMARPT(denoise_type='no',
                                     generate_report=True,
                                     TR=dt,
                                     args='-np'),
                        name='ica_aroma')

    # extract the confound ICs from the results
    ica_aroma_confound_extraction = pe.Node(
        ICAConfounds(err_on_aroma_warn=err_on_aroma_warn),
        name='ica_aroma_confound_extraction')

    ica_aroma_metadata_fmt = pe.Node(TSV2JSON(index_column='IC',
                                              output=None,
                                              enforce_case=True,
                                              additional_metadata={
                                                  'Method': {
                                                      'Name':
                                                      'ICA-AROMA',
                                                      'Version':
                                                      getenv(
                                                          'AROMA_VERSION',
                                                          'n/a')
                                                  }
                                              }),
                                     name='ica_aroma_metadata_fmt')

    def _getbtthresh(medianval):
        return 0.75 * medianval

    # connect the nodes
    workflow.connect([
        (inputnode, ica_aroma, [('movpar_file', 'motion_parameters')]),
        (inputnode, rm_non_steady_state, [('skip_vols', 'begin_index')]),
        (inputnode, rm_non_steady_state, [('bold_std', 'in_file')]),
        (inputnode, calc_median_val, [('bold_mask_std', 'mask_file')]),
        (inputnode, trim_movement, [('movpar_file', 'movpar_file')]),
        (inputnode, trim_movement, [('skip_vols', 'skip_vols')]),
        (rm_non_steady_state, calc_median_val, [('out_file', 'in_file')]),
        (rm_non_steady_state, calc_bold_mean, [('out_file', 'in_file')]),
        (calc_bold_mean, getusans, [('out_file', 'image')]),
        (calc_median_val, getusans, [('out_stat', 'thresh')]),
        # Connect input nodes to complete smoothing
        (rm_non_steady_state, smooth, [('out_file', 'in_file')]),
        (getusans, smooth, [('usans', 'usans')]),
        (calc_median_val, smooth, [(('out_stat', _getbtthresh),
                                    'brightness_threshold')]),
        # connect smooth to melodic
        (smooth, melodic, [('smoothed_file', 'in_files')]),
        (inputnode, melodic, [('bold_mask_std', 'mask')]),
        # connect nodes to ICA-AROMA
        (smooth, ica_aroma, [('smoothed_file', 'in_file')]),
        (inputnode, ica_aroma, [('bold_mask_std', 'report_mask'),
                                ('bold_mask_std', 'mask')]),
        (melodic, ica_aroma, [('out_dir', 'melodic_dir')]),
        # generate tsvs from ICA-AROMA
        (ica_aroma, ica_aroma_confound_extraction, [('out_dir', 'in_directory')
                                                    ]),
        (inputnode, ica_aroma_confound_extraction, [('skip_vols', 'skip_vols')
                                                    ]),
        (ica_aroma_confound_extraction, ica_aroma_metadata_fmt,
         [('aroma_metadata', 'in_file')]),
        # output for processing and reporting
        (ica_aroma_confound_extraction,
         outputnode, [('aroma_confounds', 'aroma_confounds'),
                      ('aroma_noise_ics', 'aroma_noise_ics'),
                      ('melodic_mix', 'melodic_mix')]),
        (ica_aroma_metadata_fmt, outputnode, [('output', 'aroma_metadata')]),
        (ica_aroma, outputnode, [('out_report', 'out_report')]),
    ])

    return workflow
Пример #13
0
# Calculate the mean functional
smooth_meanfunc = pe.MapNode(fsl.ImageMaths(op_string='-Tmean',
                                            suffix='_mean'),
                             iterfield=['in_file'],
                             name='smooth_meanfunc')
psb6351_wf.connect(maskfunc, 'out_file', smooth_meanfunc, 'in_file')

smooth_merge = pe.Node(util.Merge(2, axis='hstack'),
                       name='smooth_merge')
psb6351_wf.connect(smooth_meanfunc, 'out_file', smooth_merge, 'in1')
psb6351_wf.connect(smooth_median, 'out_stat', smooth_merge, 'in2')

# Below is the code for smoothing using the susan algorithm from FSL that
# limits smoothing based on different tissue classes
smooth = pe.MapNode(fsl.SUSAN(),
                    iterfield=['in_file', 'brightness_threshold', 'usans', 'fwhm'],
                    name='smooth')
smooth.inputs.fwhm=[2.0, 4.0, 6.0, 8.0, 10.0, 12.0]
psb6351_wf.connect(maskfunc, 'out_file', smooth, 'in_file')
psb6351_wf.connect(smooth_median, ('out_stat', getbtthresh), smooth, 'brightness_threshold')
psb6351_wf.connect(smooth_merge, ('out', getusans), smooth, 'usans')

# Below is the node that collects all the data and saves
# the outputs that I am interested in. Here in this node
# I use the substitutions input combined with the earlier
# function to get rid of nesting
datasink = pe.Node(nio.DataSink(), name="datasink")
datasink.inputs.base_directory = os.path.join(base_dir, 'derivatives/preproc')
datasink.inputs.container = f'sub-{sids[0]}'
psb6351_wf.connect(tshifter, 'out_file', datasink, 'sltime_corr')
Пример #14
0
                       name='maskfunc2')
                       
#Determine the mean image from each functional run
meanfunc2 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',
                                                suffix='_mean'),
                       iterfield=['in_file'],
                       name='meanfunc2')

#Merge the median values with the mean functional images into a coupled list
mergenode = pe.Node(interface=util.Merge(2, axis='hstack'),
                    name='merge')

#Smooth each run using SUSAN 
#brightness threshold set to xx% of the median value for each run by function 'getbtthresh'
#and a mask constituting the mean functional
smooth = pe.MapNode(interface=fsl.SUSAN(fwhm = 5.),
                    iterfield=['in_file', 'brightness_threshold','usans'],
                    name='smooth'
                    )

#Mask the smoothed data with the dilated mask
maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask',
                                                op_string='-mas'),
                       iterfield=['in_file'],
                       name='maskfunc3')

#Scale each volume of the run so that the median value of the run is set to 10000
intnorm = pe.MapNode(interface=fsl.ImageMaths(suffix='_intnorm'),
                     iterfield=['in_file','op_string'],
                     name='intnorm')
Пример #15
0
def init_ica_aroma_wf(template,
                      metadata,
                      mem_gb,
                      omp_nthreads,
                      name='ica_aroma_wf',
                      susan_fwhm=6.0,
                      ignore_aroma_err=False,
                      aroma_melodic_dim=None,
                      use_fieldwarp=True):
    """
    This workflow wraps `ICA-AROMA`_ to identify and remove motion-related
    independent components from a BOLD time series.

    The following steps are performed:

    #. Smooth data using FSL `susan`, with a kernel width FWHM=6.0mm.
    #. Run FSL `melodic` outside of ICA-AROMA to generate the report
    #. Run ICA-AROMA
    #. Aggregate identified motion components (aggressive) to TSV
    #. Return ``classified_motion_ICs`` and ``melodic_mix`` for user to complete
       non-aggressive denoising in T1w space
    #. Calculate ICA-AROMA-identified noise components
       (columns named ``AROMAAggrCompXX``)

    Additionally, non-aggressive denoising is performed on the BOLD series
    resampled into MNI space.

    There is a current discussion on whether other confounds should be extracted
    before or after denoising `here <http://nbviewer.jupyter.org/github/poldracklab/\
    fmriprep-notebooks/blob/922e436429b879271fa13e76767a6e73443e74d9/issue-817_\
    aroma_confounds.ipynb>`__.

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

        from fmriprep.workflows.bold.confounds import init_ica_aroma_wf
        wf = init_ica_aroma_wf(template='MNI152NLin2009cAsym',
                               metadata={'RepetitionTime': 1.0},
                               mem_gb=3,
                               omp_nthreads=1)

    **Parameters**

        template : str
            Spatial normalization template used as target when that
            registration step was previously calculated with
            :py:func:`~fmriprep.workflows.bold.registration.init_bold_reg_wf`.
            The template must be one of the MNI templates (fMRIPrep uses
            ``MNI152NLin2009cAsym`` by default).
        metadata : dict
            BIDS metadata for BOLD file
        mem_gb : float
            Size of BOLD file in GB
        omp_nthreads : int
            Maximum number of threads an individual process may use
        name : str
            Name of workflow (default: ``bold_mni_trans_wf``)
        susan_fwhm : float
            Kernel width (FWHM in mm) for the smoothing step with
            FSL ``susan`` (default: 6.0mm)
        use_fieldwarp : bool
            Include SDC warp in single-shot transform from BOLD to MNI
        ignore_aroma_err : bool
            Do not fail on ICA-AROMA errors
        aroma_melodic_dim: int or None
            Set the dimensionality of the Melodic ICA decomposition
            If None, MELODIC automatically estimates dimensionality.


    **Inputs**

        bold_mni
            BOLD series, resampled to template space
        movpar_file
            SPM-formatted motion parameters file
        bold_mask_mni
            BOLD series mask in template space

    **Outputs**

        aroma_confounds
            TSV of confounds identified as noise by ICA-AROMA
        aroma_noise_ics
            CSV of noise components identified by ICA-AROMA
        melodic_mix
            FSL MELODIC mixing matrix
        nonaggr_denoised_file
            BOLD series with non-aggressive ICA-AROMA denoising applied

    .. _ICA-AROMA: https://github.com/rhr-pruim/ICA-AROMA

    """
    workflow = Workflow(name=name)
    workflow.__postdesc__ = """\
Automatic removal of motion artifacts using independent component analysis
[ICA-AROMA, @aroma] was performed on the *preprocessed BOLD on MNI space*
time-series after a spatial smoothing with an isotropic, Gaussian kernel
of 6mm FWHM (full-width half-maximum).
Corresponding "non-aggresively" denoised runs were produced after such
smoothing.
Additionally, the "aggressive" noise-regressors were collected and placed
in the corresponding confounds file.
"""

    inputnode = pe.Node(niu.IdentityInterface(fields=[
        'itk_bold_to_t1', 't1_2_mni_forward_transform', 'name_source',
        'bold_split', 'bold_mask', 'hmc_xforms', 'fieldwarp', 'movpar_file'
    ]),
                        name='inputnode')

    outputnode = pe.Node(niu.IdentityInterface(fields=[
        'aroma_confounds', 'aroma_noise_ics', 'melodic_mix',
        'nonaggr_denoised_file'
    ]),
                         name='outputnode')

    bold_mni_trans_wf = init_bold_mni_trans_wf(template=template,
                                               mem_gb=mem_gb,
                                               omp_nthreads=omp_nthreads,
                                               template_out_grid=os.path.join(
                                                   get_mni_icbm152_linear(),
                                                   '2mm_T1.nii.gz'),
                                               use_compression=False,
                                               use_fieldwarp=use_fieldwarp,
                                               name='bold_mni_trans_wf')
    bold_mni_trans_wf.__desc__ = None

    calc_median_val = pe.Node(fsl.ImageStats(op_string='-k %s -p 50'),
                              name='calc_median_val')
    calc_bold_mean = pe.Node(fsl.MeanImage(), name='calc_bold_mean')

    def _getusans_func(image, thresh):
        return [tuple([image, thresh])]

    getusans = pe.Node(niu.Function(function=_getusans_func,
                                    output_names=['usans']),
                       name='getusans',
                       mem_gb=0.01)

    smooth = pe.Node(fsl.SUSAN(fwhm=susan_fwhm), name='smooth')

    # melodic node
    melodic = pe.Node(fsl.MELODIC(no_bet=True,
                                  tr_sec=float(metadata['RepetitionTime']),
                                  mm_thresh=0.5,
                                  out_stats=True),
                      name="melodic")

    if aroma_melodic_dim is not None:
        melodic.inputs.dim = aroma_melodic_dim

    # ica_aroma node
    ica_aroma = pe.Node(ICA_AROMARPT(denoise_type='nonaggr',
                                     generate_report=True,
                                     TR=metadata['RepetitionTime']),
                        name='ica_aroma')

    # extract the confound ICs from the results
    ica_aroma_confound_extraction = pe.Node(
        ICAConfounds(ignore_aroma_err=ignore_aroma_err),
        name='ica_aroma_confound_extraction')

    ds_report_ica_aroma = pe.Node(DerivativesDataSink(suffix='ica_aroma'),
                                  name='ds_report_ica_aroma',
                                  run_without_submitting=True,
                                  mem_gb=DEFAULT_MEMORY_MIN_GB)

    def _getbtthresh(medianval):
        return 0.75 * medianval

    # connect the nodes
    workflow.connect([
        (inputnode, bold_mni_trans_wf,
         [('name_source', 'inputnode.name_source'),
          ('bold_split', 'inputnode.bold_split'),
          ('bold_mask', 'inputnode.bold_mask'),
          ('hmc_xforms', 'inputnode.hmc_xforms'),
          ('itk_bold_to_t1', 'inputnode.itk_bold_to_t1'),
          ('t1_2_mni_forward_transform',
           'inputnode.t1_2_mni_forward_transform'),
          ('fieldwarp', 'inputnode.fieldwarp')]),
        (inputnode, ica_aroma, [('movpar_file', 'motion_parameters')]),
        (bold_mni_trans_wf, calc_median_val,
         [('outputnode.bold_mni', 'in_file'),
          ('outputnode.bold_mask_mni', 'mask_file')]),
        (bold_mni_trans_wf, calc_bold_mean, [('outputnode.bold_mni', 'in_file')
                                             ]),
        (calc_bold_mean, getusans, [('out_file', 'image')]),
        (calc_median_val, getusans, [('out_stat', 'thresh')]),
        # Connect input nodes to complete smoothing
        (bold_mni_trans_wf, smooth, [('outputnode.bold_mni', 'in_file')]),
        (getusans, smooth, [('usans', 'usans')]),
        (calc_median_val, smooth, [(('out_stat', _getbtthresh),
                                    'brightness_threshold')]),
        # connect smooth to melodic
        (smooth, melodic, [('smoothed_file', 'in_files')]),
        (bold_mni_trans_wf, melodic, [('outputnode.bold_mask_mni', 'mask')]),
        # connect nodes to ICA-AROMA
        (smooth, ica_aroma, [('smoothed_file', 'in_file')]),
        (bold_mni_trans_wf, ica_aroma, [('outputnode.bold_mask_mni',
                                         'report_mask')]),
        (melodic, ica_aroma, [('out_dir', 'melodic_dir')]),
        # generate tsvs from ICA-AROMA
        (ica_aroma, ica_aroma_confound_extraction, [('out_dir', 'in_directory')
                                                    ]),
        # output for processing and reporting
        (ica_aroma_confound_extraction,
         outputnode, [('aroma_confounds', 'aroma_confounds'),
                      ('aroma_noise_ics', 'aroma_noise_ics'),
                      ('melodic_mix', 'melodic_mix')]),
        # TODO change melodic report to reflect noise and non-noise components
        (ica_aroma, outputnode, [('nonaggr_denoised_file',
                                  'nonaggr_denoised_file')]),
        (ica_aroma, ds_report_ica_aroma, [('out_report', 'in_file')]),
    ])

    return workflow
Пример #16
0
def create_workflow(func_runs,
                    subject_id,
                    subjects_dir,
                    fwhm,
                    slice_times,
                    highpass_frequency,
                    lowpass_frequency,
                    TR,
                    sink_directory,
                    use_fsl_bp,
                    num_components,
                    whichvol,
                    name='wmaze'):

    wf = pe.Workflow(name=name)

    datasource = pe.Node(nio.DataGrabber(infields=['subject_id', 'run'],
                                         outfields=['func']),
                         name='datasource')
    datasource.inputs.subject_id = subject_id
    datasource.inputs.run = func_runs
    datasource.inputs.template = '/home/data/madlab/data/mri/wmaze/%s/rsfmri/rest_run%03d/rest.nii.gz'
    datasource.inputs.sort_filelist = True

    # Rename files in case they are named identically
    name_unique = pe.MapNode(util.Rename(format_string='wmaze_rest_%(run)02d'),
                             iterfield=['in_file', 'run'],
                             name='rename')
    name_unique.inputs.keep_ext = True
    name_unique.inputs.run = func_runs
    wf.connect(datasource, 'func', name_unique, 'in_file')

    # Define the outputs for the preprocessing workflow
    output_fields = [
        'reference', 'motion_parameters', 'motion_parameters_plusDerivs',
        'motionandoutlier_noise_file', 'noise_components', 'realigned_files',
        'motion_plots', 'mask_file', 'smoothed_files', 'reg_file', 'reg_cost',
        'reg_fsl_file', 'artnorm_files', 'artoutlier_files',
        'artdisplacement_files', 'tsnr_file'
    ]

    outputnode = pe.Node(util.IdentityInterface(fields=output_fields),
                         name='outputspec')

    # Convert functional images to float representation
    img2float = pe.MapNode(fsl.ImageMaths(out_data_type='float',
                                          op_string='',
                                          suffix='_dtype'),
                           iterfield=['in_file'],
                           name='img2float')
    wf.connect(name_unique, 'out_file', img2float, 'in_file')

    # Run AFNI's despike. This is always run, however, whether this is fed to
    # realign depends on the input configuration
    despiker = pe.MapNode(afni.Despike(outputtype='NIFTI_GZ'),
                          iterfield=['in_file'],
                          name='despike')
    num_threads = 4
    despiker.inputs.environ = {'OMP_NUM_THREADS': '%d' % num_threads}
    despiker.plugin_args = {'bsub_args': '-n %d' % num_threads}
    despiker.plugin_args = {'bsub_args': '-R "span[hosts=1]"'}
    wf.connect(img2float, 'out_file', despiker, 'in_file')

    # Extract the first volume of the first run as the reference
    extractref = pe.Node(fsl.ExtractROI(t_size=1),
                         iterfield=['in_file'],
                         name="extractref")
    wf.connect(despiker, ('out_file', pickfirst), extractref, 'in_file')
    wf.connect(despiker, ('out_file', pickvol, 0, whichvol), extractref,
               't_min')
    wf.connect(extractref, 'roi_file', outputnode, 'reference')

    if slice_times is not None:
        # Simultaneous motion and slice timing correction with Nipy algorithm
        motion_correct = pe.Node(nipy.SpaceTimeRealigner(),
                                 name='motion_correct')
        motion_correct.inputs.tr = TR
        motion_correct.inputs.slice_times = slice_times
        motion_correct.inputs.slice_info = 2
        motion_correct.plugin_args = {
            'bsub_args': '-n %s' % os.environ['OMP_NUM_THREADS']
        }
        motion_correct.plugin_args = {'bsub_args': '-R "span[hosts=1]"'}
        wf.connect(despiker, 'out_file', motion_correct, 'in_file')
        wf.connect(motion_correct, 'par_file', outputnode, 'motion_parameters')
        wf.connect(motion_correct, 'out_file', outputnode, 'realigned_files')
    else:
        # Motion correct functional runs to the reference (1st volume of 1st run)
        motion_correct = pe.MapNode(fsl.MCFLIRT(save_mats=True,
                                                save_plots=True,
                                                interpolation='sinc'),
                                    name='motion_correct',
                                    iterfield=['in_file'])
        wf.connect(despiker, 'out_file', motion_correct, 'in_file')
        wf.connect(extractref, 'roi_file', motion_correct, 'ref_file')
        wf.connect(motion_correct, 'par_file', outputnode, 'motion_parameters')
        wf.connect(motion_correct, 'out_file', outputnode, 'realigned_files')

    # Compute TSNR on realigned data regressing polynomials upto order 2
    tsnr = pe.MapNode(TSNR(regress_poly=2), iterfield=['in_file'], name='tsnr')
    wf.connect(motion_correct, 'out_file', tsnr, 'in_file')
    wf.connect(tsnr, 'tsnr_file', outputnode, 'tsnr_file')

    # Plot the estimated motion parameters
    plot_motion = pe.MapNode(fsl.PlotMotionParams(in_source='fsl'),
                             name='plot_motion',
                             iterfield=['in_file'])
    plot_motion.iterables = ('plot_type', ['rotations', 'translations'])
    wf.connect(motion_correct, 'par_file', plot_motion, 'in_file')
    wf.connect(plot_motion, 'out_file', outputnode, 'motion_plots')

    # Register a source file to fs space and create a brain mask in source space
    fssource = pe.Node(nio.FreeSurferSource(), name='fssource')
    fssource.inputs.subject_id = subject_id
    fssource.inputs.subjects_dir = subjects_dir

    # Extract aparc+aseg brain mask and binarize
    fs_threshold = pe.Node(fs.Binarize(min=0.5, out_type='nii'),
                           name='fs_threshold')
    wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), fs_threshold,
               'in_file')

    # Calculate the transformation matrix from EPI space to FreeSurfer space
    # using the BBRegister command
    fs_register = pe.MapNode(fs.BBRegister(init='fsl'),
                             iterfield=['source_file'],
                             name='fs_register')
    fs_register.inputs.contrast_type = 't2'
    fs_register.inputs.out_fsl_file = True
    fs_register.inputs.subject_id = subject_id
    fs_register.inputs.subjects_dir = subjects_dir
    wf.connect(extractref, 'roi_file', fs_register, 'source_file')
    wf.connect(fs_register, 'out_reg_file', outputnode, 'reg_file')
    wf.connect(fs_register, 'min_cost_file', outputnode, 'reg_cost')
    wf.connect(fs_register, 'out_fsl_file', outputnode, 'reg_fsl_file')

    # Extract wm+csf, brain masks by eroding freesurfer lables
    wmcsf = pe.MapNode(fs.Binarize(),
                       iterfield=['match', 'binary_file', 'erode'],
                       name='wmcsfmask')
    #wmcsf.inputs.wm_ven_csf = True
    wmcsf.inputs.match = [[2, 41], [4, 5, 14, 15, 24, 31, 43, 44, 63]]
    wmcsf.inputs.binary_file = ['wm.nii.gz', 'csf.nii.gz']
    wmcsf.inputs.erode = [2, 2]  #int(np.ceil(slice_thickness))
    wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), wmcsf, 'in_file')

    # Now transform the wm and csf masks to 1st volume of 1st run
    wmcsftransform = pe.MapNode(fs.ApplyVolTransform(inverse=True,
                                                     interp='nearest'),
                                iterfield=['target_file'],
                                name='wmcsftransform')
    wmcsftransform.inputs.subjects_dir = subjects_dir
    wf.connect(extractref, 'roi_file', wmcsftransform, 'source_file')
    wf.connect(fs_register, ('out_reg_file', pickfirst), wmcsftransform,
               'reg_file')
    wf.connect(wmcsf, 'binary_file', wmcsftransform, 'target_file')

    # Transform the binarized aparc+aseg file to the 1st volume of 1st run space
    fs_voltransform = pe.MapNode(fs.ApplyVolTransform(inverse=True),
                                 iterfield=['source_file', 'reg_file'],
                                 name='fs_transform')
    fs_voltransform.inputs.subjects_dir = subjects_dir
    wf.connect(extractref, 'roi_file', fs_voltransform, 'source_file')
    wf.connect(fs_register, 'out_reg_file', fs_voltransform, 'reg_file')
    wf.connect(fs_threshold, 'binary_file', fs_voltransform, 'target_file')

    # Dilate the binarized mask by 1 voxel that is now in the EPI space
    fs_threshold2 = pe.MapNode(fs.Binarize(min=0.5, out_type='nii'),
                               iterfield=['in_file'],
                               name='fs_threshold2')
    fs_threshold2.inputs.dilate = 1
    wf.connect(fs_voltransform, 'transformed_file', fs_threshold2, 'in_file')
    wf.connect(fs_threshold2, ('binary_file', pickfirst), outputnode,
               'mask_file')

    # Use RapidART to detect motion/intensity outliers
    art = pe.MapNode(ra.ArtifactDetect(use_differences=[True, False],
                                       use_norm=True,
                                       zintensity_threshold=3,
                                       norm_threshold=1,
                                       bound_by_brainmask=True,
                                       mask_type="file"),
                     iterfield=["realignment_parameters", "realigned_files"],
                     name="art")
    if slice_times is not None:
        art.inputs.parameter_source = "NiPy"
    else:
        art.inputs.parameter_source = "FSL"
    wf.connect(motion_correct, 'par_file', art, 'realignment_parameters')
    wf.connect(motion_correct, 'out_file', art, 'realigned_files')
    wf.connect(fs_threshold2, ('binary_file', pickfirst), art, 'mask_file')
    wf.connect(art, 'norm_files', outputnode, 'artnorm_files')
    wf.connect(art, 'outlier_files', outputnode, 'artoutlier_files')
    wf.connect(art, 'displacement_files', outputnode, 'artdisplacement_files')

    # Compute motion regressors (save file with 1st and 2nd derivatives)
    motreg = pe.Node(util.Function(
        input_names=['motion_params', 'order', 'derivatives'],
        output_names=['out_files'],
        function=motion_regressors,
        imports=imports),
                     name='getmotionregress')
    wf.connect(motion_correct, 'par_file', motreg, 'motion_params')
    wf.connect(motreg, 'out_files', outputnode, 'motion_parameters_plusDerivs')

    # Create a filter text file to remove motion (+ derivatives), art confounds,
    # and 1st, 2nd, and 3rd order legendre polynomials.
    createfilter1 = pe.Node(util.Function(
        input_names=['motion_params', 'comp_norm', 'outliers', 'detrend_poly'],
        output_names=['out_files'],
        function=build_filter1,
        imports=imports),
                            name='makemotionbasedfilter')
    createfilter1.inputs.detrend_poly = 3
    wf.connect(motreg, 'out_files', createfilter1, 'motion_params')
    wf.connect(art, 'norm_files', createfilter1, 'comp_norm')
    wf.connect(art, 'outlier_files', createfilter1, 'outliers')
    wf.connect(createfilter1, 'out_files', outputnode,
               'motionandoutlier_noise_file')

    filter1 = pe.MapNode(fsl.GLM(out_f_name='F_mcart.nii.gz',
                                 out_pf_name='pF_mcart.nii.gz',
                                 demean=True),
                         iterfield=['in_file', 'design', 'out_res_name'],
                         name='filtermotion')
    wf.connect(motion_correct, 'out_file', filter1, 'in_file')
    wf.connect(motion_correct, ('out_file', rename, '_filtermotart'), filter1,
               'out_res_name')
    wf.connect(createfilter1, 'out_files', filter1, 'design')

    # Create a filter to remove noise components based on white matter and CSF
    createfilter2 = pe.MapNode(
        util.Function(input_names=[
            'realigned_file', 'mask_file', 'num_components', 'extra_regressors'
        ],
                      output_names=['out_files'],
                      function=extract_noise_components,
                      imports=imports),
        iterfield=['realigned_file', 'extra_regressors'],
        name='makecompcorrfilter')
    createfilter2.inputs.num_components = num_components
    wf.connect(createfilter1, 'out_files', createfilter2, 'extra_regressors')
    wf.connect(motion_correct, 'out_file', createfilter2, 'realigned_file')
    wf.connect(wmcsftransform, 'transformed_file', createfilter2, 'mask_file')
    wf.connect(createfilter2, 'out_files', outputnode, 'noise_components')

    filter2 = pe.MapNode(fsl.GLM(out_f_name='F.nii.gz',
                                 out_pf_name='pF.nii.gz',
                                 demean=True),
                         iterfield=['in_file', 'design', 'out_res_name'],
                         name='filter_noise_nosmooth')
    wf.connect(filter1, 'out_res', filter2, 'in_file')
    wf.connect(filter1, ('out_res', rename, '_cleaned'), filter2,
               'out_res_name')
    wf.connect(createfilter2, 'out_files', filter2, 'design')
    wf.connect(fs_threshold2, ('binary_file', pickfirst), filter2, 'mask')

    # Band-pass filter the timeseries
    if use_fsl_bp == 'True':
        determine_bp_sigmas = pe.Node(util.Function(
            input_names=['tr', 'highpass_freq', 'lowpass_freq'],
            output_names=['out_sigmas'],
            function=calc_fslbp_sigmas),
                                      name='determine_bp_sigmas')
        determine_bp_sigmas.inputs.tr = float(TR)
        determine_bp_sigmas.inputs.highpass_freq = float(highpass_frequency)
        determine_bp_sigmas.inputs.lowpass_freq = float(lowpass_frequency)

        bandpass = pe.MapNode(fsl.ImageMaths(suffix='_tempfilt'),
                              iterfield=["in_file"],
                              name="bandpass")
        wf.connect(determine_bp_sigmas, ('out_sigmas', highpass_operand),
                   bandpass, 'op_string')
        wf.connect(filter2, 'out_res', bandpass, 'in_file')
        wf.connect(bandpass, 'out_file', outputnode, 'bandpassed_files')
    else:
        bandpass = pe.Node(util.Function(
            input_names=['files', 'lowpass_freq', 'highpass_freq', 'fs'],
            output_names=['out_files'],
            function=bandpass_filter,
            imports=imports),
                           name='bandpass')
        bandpass.inputs.fs = 1. / TR
        if highpass_frequency < 0:
            bandpass.inputs.highpass_freq = -1
        else:
            bandpass.inputs.highpass_freq = highpass_frequency
        if lowpass_frequency < 0:
            bandpass.inputs.lowpass_freq = -1
        else:
            bandpass.inputs.lowpass_freq = lowpass_frequency
        wf.connect(filter2, 'out_res', bandpass, 'files')
        wf.connect(bandpass, 'out_files', outputnode, 'bandpassed_files')

    # Smooth each run using SUSAn with the brightness threshold set to 75%
    # of the median value for each run and a mask constituting the mean functional
    smooth_median = pe.MapNode(fsl.ImageStats(op_string='-k %s -p 50'),
                               iterfield=['in_file'],
                               name='smooth_median')
    wf.connect(motion_correct, 'out_file', smooth_median, 'in_file')
    wf.connect(fs_threshold2, ('binary_file', pickfirst), smooth_median,
               'mask_file')

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

    smooth_merge = pe.Node(util.Merge(2, axis='hstack'), name='smooth_merge')
    wf.connect(smooth_meanfunc, 'out_file', smooth_merge, 'in1')
    wf.connect(smooth_median, 'out_stat', smooth_merge, 'in2')

    smooth = pe.MapNode(fsl.SUSAN(),
                        iterfield=['in_file', 'brightness_threshold', 'usans'],
                        name='smooth')
    smooth.inputs.fwhm = fwhm
    wf.connect(bandpass, 'out_file', smooth, 'in_file')
    wf.connect(smooth_median, ('out_stat', getbtthresh), smooth,
               'brightness_threshold')
    wf.connect(smooth_merge, ('out', getusans), smooth, 'usans')
    wf.connect(smooth, 'smoothed_file', outputnode, 'smoothed_files')

    # Save the relevant data into an output directory
    datasink = pe.Node(nio.DataSink(), name="datasink")
    datasink.inputs.base_directory = sink_directory
    datasink.inputs.container = subject_id
    wf.connect(outputnode, 'reference', datasink, 'wmaze_rest.ref')
    wf.connect(outputnode, 'motion_parameters', datasink, 'wmaze_rest.motion')
    wf.connect(outputnode, 'realigned_files', datasink,
               'wmaze_rest.func.realigned')
    wf.connect(outputnode, 'motion_plots', datasink,
               'wmaze_rest.motion.@plots')
    wf.connect(outputnode, 'mask_file', datasink, 'wmaze_rest.ref.@mask')
    wf.connect(outputnode, 'smoothed_files', datasink,
               'wmaze_rest.func.smoothed_bandpassed')
    wf.connect(outputnode, 'reg_file', datasink, 'wmaze_rest.bbreg.@reg')
    wf.connect(outputnode, 'reg_cost', datasink, 'wmaze_rest.bbreg.@cost')
    wf.connect(outputnode, 'reg_fsl_file', datasink,
               'wmaze_rest.bbreg.@regfsl')
    wf.connect(outputnode, 'artnorm_files', datasink,
               'wmaze_rest.art.@norm_files')
    wf.connect(outputnode, 'artoutlier_files', datasink,
               'wmaze_rest.art.@outlier_files')
    wf.connect(outputnode, 'artdisplacement_files', datasink,
               'wmaze_rest.art.@displacement_files')
    wf.connect(outputnode, 'motion_parameters_plusDerivs', datasink,
               'wmaze_rest.noise.@motionplusDerivs')
    wf.connect(outputnode, 'motionandoutlier_noise_file', datasink,
               'wmaze_rest.noise.@motionplusoutliers')
    wf.connect(outputnode, 'noise_components', datasink, 'wmaze_rest.compcor')
    wf.connect(outputnode, 'tsnr_file', datasink, 'wmaze_rest.tsnr')

    return wf