def init_single_subject_wf( opts: ArgumentParser, layout: BIDSLayout, run_uuid: str, work_dir: str, output_dir: str, name: str, subject_id: str, reportlets_dir: str, ): import nilearn if name in ('single_subject_wf', 'single_subject_test_wf'): # for documentation purposes subject_data = { 'bold': ['/completely/made/up/path/sub-01_task-nback_bold.nii.gz'] } else: subject_data = collect_data(layout, subject_id, None)[0] workflow = Workflow(name=name) workflow.base_dir = work_dir workflow.__desc__ = """ Results included in this manuscript come from processing performed using *atlasTransform* {atlasTransform_ver} (@atlasTransform1; @atlasTransform; RRID:some.id), which is based on *Nipype* {nipype_ver} (@nipype1; @nipype2; RRID:SCR_002502). """.format(atlasTransform_ver=__version__, nipype_ver=nipype_ver) workflow.__postdesc__ = """ Many internal operations of *atlasTransform* use *Nilearn* {nilearn_ver} [@nilearn, RRID:SCR_001362]. ### Copyright Waiver The above boilerplate text was automatically generated by fMRIPrep with the express intention that users should copy and paste this text into their manuscripts *unchanged*. It is released under the [CC0]\ (https://creativecommons.org/publicdomain/zero/1.0/) license. ### References """.format(nilearn_ver=nilearn.version.__version__) inputnode = pe.Node(niu.IdentityInterface(fields=['subjects_dir']), name='inputnode') # # # require_masks = opts.source_format == 'bold' # bidssrc = pe.Node(BIDSPlusDataGrabber(subject_data=subject_data, require_masks=False), # name='bidssrc') # # # bids_info = pe.Node(BIDSInfo( # # bids_dir=layout.root, bids_validate=False), name='bids_info') # # summary = pe.Node(SubjectSummary(), # name='summary', run_without_submitting=True) # # about = pe.Node(AboutSummary(version=__version__, # command=' '.join(sys.argv)), # name='about', run_without_submitting=True) # # ds_report_summary = pe.Node( # DerivativesDataSink(base_directory=reportlets_dir, # desc='summary', keep_dtype=True), # name='ds_report_summary', run_without_submitting=True) # # ds_report_about = pe.Node( # DerivativesDataSink(base_directory=reportlets_dir, # desc='about', keep_dtype=True), # name='ds_report_about', run_without_submitting=True) # Preprocessing of T1w (includes registration to MNI) # workflow.connect([ # # (bidssrc, bids_info, [('bold', 'in_file')]), # (inputnode, summary, [('subjects_dir', 'subjects_dir')]), # (bidssrc, summary, [('t1w', 't1w'), # ('t2w', 't2w'), # ('bold', 'bold')]), # # (bids_info, summary, [('subject', 'subject_id')]), # (bidssrc, ds_report_summary, [('bold', 'source_file')]), # # (summary, ds_report_summary, [('out_report', 'in_file')]), # (bidssrc, ds_report_about, [('bold', 'source_file')]), # (about, ds_report_about, [('out_report', 'in_file')]), # ]) # Overwrite ``out_path_base`` of smriprep's DataSinks for node in workflow.list_node_names(): if node.split('.')[-1].startswith('ds_'): workflow.get_node(node).interface.out_path_base = 'atlasTransform' for i in range(len(subject_data[opts.source])): transform_wf = init_atlas_transform_workflow( nifti=subject_data[opts.source][i], atlas_name=opts.atlas_name, options=opts, bids_dir=str(layout.root), name='atlas_transform_%d_wf' % i) workflow.connect([ (inputnode, transform_wf, [('subjects_dir', 'inputnode.subjects_dir')]), ]) # outputs_wf = init_datasink_wf(bids_root=str(layout.root), output_dir=str(opts.output_dir), atlas_name=opts.atlas_name, name='ds_%d_wf' % i) # # workflow.connect([(transform_wf,outputs_wf,[('outputnode.transformed','inputnode.source_file')])]) # # outputs_wf.inputs.source_file = subject_data['csv'][i] if subject_data.__contains__('mask') else subject_data['bold'][i] # # workflow.connect([(transform_wf, outputs_wf, [ # ('outputnode.transformed', 'inputnode.transformed') # ])]) return workflow
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