def init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=False): ''' From: https://github.com/rhr-pruim/ICA-AROMA Description: ICA-AROMA (i.e. ‘ICA-based Automatic Removal Of Motion Artifacts’) concerns a data-driven method to identify and remove motion-related independent components from fMRI data. Preconditions/Assumptions: The input fmri bold file is in standard space (for ease of interfacing with the original ICA-AROMA code) Steps: 1) smooth data using SUSAN 2) run melodic outside of ICA_AROMA to generate the report 3) run ICA_AROMA 4) print identified motion components (aggressive) to tsv 5) pass classified_motion_ICs and melodic_mix for user to complete nonaggr denoising ''' workflow = pe.Workflow(name=name) inputnode = pe.Node(utility.IdentityInterface( fields=['epi_mni', 'movpar_file', 'epi_mask_mni']), name='inputnode') outputnode = pe.Node(utility.IdentityInterface(fields=[ 'aroma_confounds', 'out_report', 'aroma_noise_ics', 'melodic_mix' ]), name='outputnode') # helper function to get # smoothing node (SUSAN) # functions to help set SUSAN def getbtthresh(medianval): return 0.75 * medianval def getusans_func(image, thresh): return [tuple([image, thresh])] calc_median_val = pe.Node(fsl.ImageStats(op_string='-k %s -p 50'), name='calc_median_val') calc_epi_mean = pe.Node(fsl.MeanImage(), name='calc_epi_mean') brightness_threshold = pe.Node(utility.Function(function=getbtthresh, input_names=['medianval'], output_names=['thresh']), name='brightness_threshold') getusans = pe.Node(utility.Function(function=getusans_func, input_names=['image', 'thresh'], output_names=['usans']), name='getusans') smooth = pe.Node(fsl.SUSAN(fwhm=6.0), name='smooth') # melodic node melodic = pe.Node(nws.MELODICRPT(no_bet=True, no_mm=True, generate_report=True), name="melodic") # ica_aroma node ica_aroma = pe.Node(aroma.ICA_AROMA(denoise_type='no'), name='ica_aroma') # extract the confound ICs from the results ica_aroma_confound_extraction = pe.Node( utility.Function( function=get_ica_confounds, input_names=['ica_out_dir', 'ignore_aroma_err'], output_names=['aroma_confounds', 'aroma_noise_ics', 'melodic_mix']), name='ica_aroma_confound_extraction') ica_aroma_confound_extraction.inputs.ignore_aroma_err = ignore_aroma_err # connect the nodes workflow.connect([ # Connect input nodes to complete smoothing (inputnode, calc_median_val, [('epi_mni', 'in_file'), ('epi_mask_mni', 'mask_file')]), (calc_median_val, brightness_threshold, [('out_stat', 'medianval')]), (inputnode, calc_epi_mean, [('epi_mni', 'in_file')]), (calc_epi_mean, getusans, [('out_file', 'image')]), (calc_median_val, getusans, [('out_stat', 'thresh')]), (inputnode, smooth, [('epi_mni', 'in_file')]), (getusans, smooth, [('usans', 'usans')]), (brightness_threshold, smooth, [('thresh', 'brightness_threshold')]), # connect smooth to melodic (smooth, melodic, [('smoothed_file', 'in_files')]), (inputnode, melodic, [('epi_mask_mni', 'report_mask'), ('epi_mask_mni', 'mask')]), # connect nodes to ICA-AROMA (smooth, ica_aroma, [('smoothed_file', 'in_file')]), (inputnode, ica_aroma, [('movpar_file', 'motion_parameters')]), (melodic, ica_aroma, [('out_dir', 'melodic_dir')]), # geneerate tsvs from ICA_AROMA (ica_aroma, ica_aroma_confound_extraction, [('out_dir', 'ica_out_dir')] ), # 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 (melodic, outputnode, [('out_report', 'out_report')]), ]) return workflow
def init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=False): ''' 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 SUSAN #. Run 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 Additionally, non-aggressive denoising is performed on the BOLD series resampled into MNI space. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.confounds import init_ica_aroma_wf wf = init_ica_aroma_wf() **Parameters** ignore_aroma_err : bool Do not fail on ICA-AROMA errors **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 out_report Reportlet visualizing MELODIC ICs, with ICA-AROMA signal/noise labels .. _ICA-AROMA: https://github.com/rhr-pruim/ICA-AROMA ''' workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['bold_mni', 'movpar_file', 'bold_mask_mni']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['aroma_confounds', 'out_report', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file']), name='outputnode') 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=6.0), name='smooth') # melodic node melodic = pe.Node(fsl.MELODIC(no_bet=True, no_mm=True), name="melodic") # ica_aroma node ica_aroma = pe.Node(ICA_AROMARPT(denoise_type='nonaggr', generate_report=True), 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') def _getbtthresh(medianval): return 0.75 * medianval # connect the nodes workflow.connect([ # Connect input nodes to complete smoothing (inputnode, calc_median_val, [('bold_mni', 'in_file'), ('bold_mask_mni', 'mask_file')]), (inputnode, calc_bold_mean, [('bold_mni', 'in_file')]), (calc_bold_mean, getusans, [('out_file', 'image')]), (calc_median_val, getusans, [('out_stat', 'thresh')]), (inputnode, smooth, [('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')]), (inputnode, melodic, [('bold_mask_mni', 'mask')]), # connect nodes to ICA-AROMA (smooth, ica_aroma, [('smoothed_file', 'in_file')]), (inputnode, ica_aroma, [('bold_mask_mni', 'report_mask'), ('movpar_file', 'motion_parameters')]), (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, [('out_report', 'out_report'), ('nonaggr_denoised_file', 'nonaggr_denoised_file')]), ]) return workflow
def init_ica_aroma_wf(template, metadata, mem_gb, omp_nthreads, name='ica_aroma_wf', 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 SUSAN #. Run 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. .. 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``) 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 = pe.Workflow(name=name) 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' ) 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=6.0), 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