def fmri_qc_workflow(name='funcMRIQC'): """ Initialize the (f)MRIQC workflow. .. workflow:: import os.path as op from mriqc.workflows.functional import fmri_qc_workflow from mriqc.testing import mock_config with mock_config(): wf = fmri_qc_workflow() """ from nipype.interfaces.afni import TStat from nipype.algorithms.confounds import TSNR, NonSteadyStateDetector from niworkflows.interfaces.utils import SanitizeImage workflow = pe.Workflow(name=name) mem_gb = config.workflow.biggest_file_gb dataset = config.workflow.inputs.get("bold", []) config.loggers.workflow.info(f"""\ Building functional MRIQC workflow for files: {', '.join(dataset)}.""") # Define workflow, inputs and outputs # 0. Get data, put it in RAS orientation inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') inputnode.iterables = [('in_file', dataset)] outputnode = pe.Node(niu.IdentityInterface( fields=['qc', 'mosaic', 'out_group', 'out_dvars', 'out_fd']), name='outputnode') non_steady_state_detector = pe.Node(NonSteadyStateDetector(), name="non_steady_state_detector") sanitize = pe.Node(SanitizeImage(), name="sanitize", mem_gb=mem_gb * 4.0) sanitize.inputs.max_32bit = config.execution.float32 # Workflow -------------------------------------------------------- # 1. HMC: head motion correct hmcwf = hmc() # Set HMC settings hmcwf.inputs.inputnode.fd_radius = config.workflow.fd_radius # 2. Compute mean fmri mean = pe.Node(TStat(options='-mean', outputtype='NIFTI_GZ'), name='mean', mem_gb=mem_gb * 1.5) skullstrip_epi = fmri_bmsk_workflow() # EPI to MNI registration ema = epi_mni_align() # Compute TSNR using nipype implementation tsnr = pe.Node(TSNR(), name='compute_tsnr', mem_gb=mem_gb * 2.5) # 7. Compute IQMs iqmswf = compute_iqms() # Reports repwf = individual_reports() workflow.connect([ (inputnode, iqmswf, [('in_file', 'inputnode.in_file')]), (inputnode, sanitize, [('in_file', 'in_file')]), (inputnode, non_steady_state_detector, [('in_file', 'in_file')]), (non_steady_state_detector, sanitize, [('n_volumes_to_discard', 'n_volumes_to_discard')]), (sanitize, hmcwf, [('out_file', 'inputnode.in_file')]), (mean, skullstrip_epi, [('out_file', 'inputnode.in_file')]), (hmcwf, mean, [('outputnode.out_file', 'in_file')]), (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]), (mean, ema, [('out_file', 'inputnode.epi_mean')]), (skullstrip_epi, ema, [('outputnode.out_file', 'inputnode.epi_mask')]), (sanitize, iqmswf, [('out_file', 'inputnode.in_ras')]), (mean, iqmswf, [('out_file', 'inputnode.epi_mean')]), (hmcwf, iqmswf, [('outputnode.out_file', 'inputnode.hmc_epi'), ('outputnode.out_fd', 'inputnode.hmc_fd')]), (skullstrip_epi, iqmswf, [('outputnode.out_file', 'inputnode.brainmask')]), (tsnr, iqmswf, [('tsnr_file', 'inputnode.in_tsnr')]), (sanitize, repwf, [('out_file', 'inputnode.in_ras')]), (mean, repwf, [('out_file', 'inputnode.epi_mean')]), (tsnr, repwf, [('stddev_file', 'inputnode.in_stddev')]), (skullstrip_epi, repwf, [('outputnode.out_file', 'inputnode.brainmask') ]), (hmcwf, repwf, [('outputnode.out_fd', 'inputnode.hmc_fd'), ('outputnode.out_file', 'inputnode.hmc_epi')]), (ema, repwf, [('outputnode.epi_parc', 'inputnode.epi_parc'), ('outputnode.report', 'inputnode.mni_report')]), (non_steady_state_detector, iqmswf, [('n_volumes_to_discard', 'inputnode.exclude_index')]), (iqmswf, repwf, [('outputnode.out_file', 'inputnode.in_iqms'), ('outputnode.out_dvars', 'inputnode.in_dvars'), ('outputnode.outliers', 'inputnode.outliers')]), (hmcwf, outputnode, [('outputnode.out_fd', 'out_fd')]), ]) if config.workflow.fft_spikes_detector: workflow.connect([ (iqmswf, repwf, [('outputnode.out_spikes', 'inputnode.in_spikes'), ('outputnode.out_fft', 'inputnode.in_fft')]), ]) if config.workflow.ica: from niworkflows.interfaces import segmentation as nws melodic = pe.Node(nws.MELODICRPT(no_bet=True, no_mask=True, no_mm=True, compress_report=False, generate_report=True), name="ICA", mem_gb=max(mem_gb * 5, 8)) workflow.connect([(sanitize, melodic, [('out_file', 'in_files')]), (skullstrip_epi, melodic, [('outputnode.out_file', 'report_mask')]), (melodic, repwf, [('out_report', 'inputnode.ica_report')])]) # Upload metrics if not config.execution.no_sub: from ..interfaces.webapi import UploadIQMs upldwf = pe.Node(UploadIQMs(), name='UploadMetrics') upldwf.inputs.url = config.execution.webapi_url upldwf.inputs.strict = config.execution.upload_strict if config.execution.webapi_port: upldwf.inputs.port = config.execution.webapi_port workflow.connect([ (iqmswf, upldwf, [('outputnode.out_file', 'in_iqms')]), ]) return workflow
def fmri_qc_workflow(dataset, settings, name='funcMRIQC'): """ The fMRI qc workflow .. workflow:: import os.path as op from mriqc.workflows.functional import fmri_qc_workflow datadir = op.abspath('data') wf = fmri_qc_workflow([op.join(datadir, 'sub-001/func/sub-001_task-rest_bold.nii.gz')], settings={'bids_dir': datadir, 'output_dir': op.abspath('out')}) """ workflow = pe.Workflow(name=name) biggest_file_gb = settings.get("biggest_file_size_gb", 1) # Define workflow, inputs and outputs # 0. Get data, put it in RAS orientation inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') WFLOGGER.info( 'Building fMRI QC workflow, datasets list: %s', sorted([d.replace(settings['bids_dir'] + '/', '') for d in dataset])) inputnode.iterables = [('in_file', dataset)] meta = pe.Node(ReadSidecarJSON(), name='metadata') outputnode = pe.Node(niu.IdentityInterface( fields=['qc', 'mosaic', 'out_group', 'out_dvars', 'out_fd']), name='outputnode') reorient_and_discard = pe.Node(niu.Function( input_names=['in_file', 'float32'], output_names=['exclude_index', 'out_file'], function=reorient_and_discard_non_steady), name='reorient_and_discard') reorient_and_discard.inputs.float32 = settings.get("float32", DEFAULTS['float32']) reorient_and_discard.interface.estimated_memory_gb = 4.0 * biggest_file_gb # Workflow -------------------------------------------------------- # 1. HMC: head motion correct if settings.get('hmc_fsl', False): hmcwf = hmc_mcflirt(settings) else: hmcwf = hmc_afni(settings, st_correct=settings.get('correct_slice_timing', False), despike=settings.get('despike', False), deoblique=settings.get('deoblique', False), start_idx=settings.get('start_idx', None), stop_idx=settings.get('stop_idx', None)) # Set HMC settings hmcwf.inputs.inputnode.fd_radius = settings.get('fd_radius', DEFAULT_FD_RADIUS) mean = pe.Node( afni.TStat( # 2. Compute mean fmri options='-mean', outputtype='NIFTI_GZ'), name='mean') mean.interface.estimated_memory_gb = biggest_file_gb * 1.5 skullstrip_epi = fmri_bmsk_workflow(use_bet=True) # EPI to MNI registration ema = epi_mni_align(settings) # Compute TSNR using nipype implementation tsnr = pe.Node(nac.TSNR(), name='compute_tsnr') tsnr.interface.estimated_memory_gb = biggest_file_gb * 4.5 # 7. Compute IQMs iqmswf = compute_iqms(settings) # Reports repwf = individual_reports(settings) # Upload metrics upldwf = upload_wf(settings) workflow.connect([ (inputnode, meta, [('in_file', 'in_file')]), (inputnode, reorient_and_discard, [('in_file', 'in_file')]), (reorient_and_discard, hmcwf, [('out_file', 'inputnode.in_file')]), (mean, skullstrip_epi, [('out_file', 'inputnode.in_file')]), (hmcwf, mean, [('outputnode.out_file', 'in_file')]), (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]), (mean, ema, [('out_file', 'inputnode.epi_mean')]), (skullstrip_epi, ema, [('outputnode.out_file', 'inputnode.epi_mask')]), (meta, iqmswf, [('subject_id', 'inputnode.subject_id'), ('session_id', 'inputnode.session_id'), ('task_id', 'inputnode.task_id'), ('acq_id', 'inputnode.acq_id'), ('rec_id', 'inputnode.rec_id'), ('run_id', 'inputnode.run_id'), ('out_dict', 'inputnode.metadata')]), (reorient_and_discard, iqmswf, [('out_file', 'inputnode.orig')]), (mean, iqmswf, [('out_file', 'inputnode.epi_mean')]), (hmcwf, iqmswf, [('outputnode.out_file', 'inputnode.hmc_epi'), ('outputnode.out_fd', 'inputnode.hmc_fd')]), (skullstrip_epi, iqmswf, [('outputnode.out_file', 'inputnode.brainmask')]), (tsnr, iqmswf, [('tsnr_file', 'inputnode.in_tsnr')]), (reorient_and_discard, repwf, [('out_file', 'inputnode.orig')]), (mean, repwf, [('out_file', 'inputnode.epi_mean')]), (tsnr, repwf, [('stddev_file', 'inputnode.in_stddev')]), (skullstrip_epi, repwf, [('outputnode.out_file', 'inputnode.brainmask') ]), (hmcwf, repwf, [('outputnode.out_fd', 'inputnode.hmc_fd'), ('outputnode.out_file', 'inputnode.hmc_epi')]), (ema, repwf, [('outputnode.epi_parc', 'inputnode.epi_parc'), ('outputnode.report', 'inputnode.mni_report')]), (reorient_and_discard, repwf, [('exclude_index', 'inputnode.exclude_index')]), (iqmswf, repwf, [('outputnode.out_file', 'inputnode.in_iqms'), ('outputnode.out_dvars', 'inputnode.in_dvars'), ('outputnode.outliers', 'inputnode.outliers')]), (iqmswf, upldwf, [('outputnode.out_file', 'inputnode.in_iqms')]), (hmcwf, outputnode, [('outputnode.out_fd', 'out_fd')]), ]) if settings.get('fft_spikes_detector', False): workflow.connect([ (iqmswf, repwf, [('outputnode.out_spikes', 'inputnode.in_spikes'), ('outputnode.out_fft', 'inputnode.in_fft')]), ]) if settings.get('ica', False): melodic = pe.Node(nws.MELODICRPT(no_bet=True, no_mask=True, no_mm=True, generate_report=True), name="ICA") melodic.interface.estimated_memory_gb = biggest_file_gb * 5 workflow.connect([ (reorient_and_discard, melodic, [('out_file', 'in_files')]), (skullstrip_epi, melodic, [('outputnode.out_file', 'report_mask') ]), (melodic, repwf, [('out_report', 'inputnode.ica_report')]) ]) return workflow
def fmri_qc_workflow(dataset, settings, name='funcMRIQC'): """ The fMRI qc workflow .. workflow:: import os.path as op from mriqc.workflows.functional import fmri_qc_workflow datadir = op.abspath('data') wf = fmri_qc_workflow([op.join(datadir, 'sub-001/func/sub-001_task-rest_bold.nii.gz')], settings={'bids_dir': datadir, 'output_dir': op.abspath('out'), 'no_sub': True}) """ workflow = pe.Workflow(name=name) biggest_file_gb = settings.get("biggest_file_size_gb", 1) # Define workflow, inputs and outputs # 0. Get data, put it in RAS orientation inputnode = pe.Node(niu.IdentityInterface(fields=['in_file']), name='inputnode') WFLOGGER.info( 'Building fMRI QC workflow, datasets list: %s', sorted([d.replace(settings['bids_dir'] + '/', '') for d in dataset])) inputnode.iterables = [('in_file', dataset)] outputnode = pe.Node(niu.IdentityInterface( fields=['qc', 'mosaic', 'out_group', 'out_dvars', 'out_fd']), name='outputnode') non_steady_state_detector = pe.Node(nac.NonSteadyStateDetector(), name="non_steady_state_detector") sanitize = pe.Node(niutils.SanitizeImage(), name="sanitize", mem_gb=biggest_file_gb * 4.0) sanitize.inputs.max_32bit = settings.get("float32", DEFAULTS['float32']) # Workflow -------------------------------------------------------- # 1. HMC: head motion correct if settings.get('hmc_fsl', False): hmcwf = hmc_mcflirt(settings) else: hmcwf = hmc_afni(settings, st_correct=settings.get('correct_slice_timing', False), despike=settings.get('despike', False), deoblique=settings.get('deoblique', False), start_idx=settings.get('start_idx', None), stop_idx=settings.get('stop_idx', None)) # Set HMC settings hmcwf.inputs.inputnode.fd_radius = settings.get('fd_radius', DEFAULT_FD_RADIUS) mean = pe.Node( afni.TStat( # 2. Compute mean fmri options='-mean', outputtype='NIFTI_GZ'), name='mean', mem_gb=biggest_file_gb * 1.5) skullstrip_epi = fmri_bmsk_workflow(use_bet=True) # EPI to MNI registration ema = epi_mni_align(settings) # Compute TSNR using nipype implementation tsnr = pe.Node(nac.TSNR(), name='compute_tsnr', mem_gb=biggest_file_gb * 4.5) # 7. Compute IQMs iqmswf = compute_iqms(settings) # Reports repwf = individual_reports(settings) workflow.connect([ (inputnode, iqmswf, [('in_file', 'inputnode.in_file')]), (inputnode, sanitize, [('in_file', 'in_file')]), (inputnode, non_steady_state_detector, [('in_file', 'in_file')]), (non_steady_state_detector, sanitize, [('n_volumes_to_discard', 'n_volumes_to_discard')]), (sanitize, hmcwf, [('out_file', 'inputnode.in_file')]), (mean, skullstrip_epi, [('out_file', 'inputnode.in_file')]), (hmcwf, mean, [('outputnode.out_file', 'in_file')]), (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]), (mean, ema, [('out_file', 'inputnode.epi_mean')]), (skullstrip_epi, ema, [('outputnode.out_file', 'inputnode.epi_mask')]), (sanitize, iqmswf, [('out_file', 'inputnode.in_ras')]), (mean, iqmswf, [('out_file', 'inputnode.epi_mean')]), (hmcwf, iqmswf, [('outputnode.out_file', 'inputnode.hmc_epi'), ('outputnode.out_fd', 'inputnode.hmc_fd')]), (skullstrip_epi, iqmswf, [('outputnode.out_file', 'inputnode.brainmask')]), (tsnr, iqmswf, [('tsnr_file', 'inputnode.in_tsnr')]), (sanitize, repwf, [('out_file', 'inputnode.in_ras')]), (mean, repwf, [('out_file', 'inputnode.epi_mean')]), (tsnr, repwf, [('stddev_file', 'inputnode.in_stddev')]), (skullstrip_epi, repwf, [('outputnode.out_file', 'inputnode.brainmask') ]), (hmcwf, repwf, [('outputnode.out_fd', 'inputnode.hmc_fd'), ('outputnode.out_file', 'inputnode.hmc_epi')]), (ema, repwf, [('outputnode.epi_parc', 'inputnode.epi_parc'), ('outputnode.report', 'inputnode.mni_report')]), (non_steady_state_detector, iqmswf, [('n_volumes_to_discard', 'inputnode.exclude_index')]), (iqmswf, repwf, [('outputnode.out_file', 'inputnode.in_iqms'), ('outputnode.out_dvars', 'inputnode.in_dvars'), ('outputnode.outliers', 'inputnode.outliers')]), (hmcwf, outputnode, [('outputnode.out_fd', 'out_fd')]), ]) if settings.get('fft_spikes_detector', False): workflow.connect([ (iqmswf, repwf, [('outputnode.out_spikes', 'inputnode.in_spikes'), ('outputnode.out_fft', 'inputnode.in_fft')]), ]) if settings.get('ica', False): melodic = pe.Node(nws.MELODICRPT(no_bet=True, no_mask=True, no_mm=True, generate_report=True), name="ICA", mem_gb=biggest_file_gb * 5) workflow.connect([(sanitize, melodic, [('out_file', 'in_files')]), (skullstrip_epi, melodic, [('outputnode.out_file', 'report_mask')]), (melodic, repwf, [('out_report', 'inputnode.ica_report')])]) # Upload metrics if not settings.get('no_sub', False): from ..interfaces.webapi import UploadIQMs upldwf = pe.Node(UploadIQMs(), name='UploadMetrics') upldwf.inputs.url = settings.get('webapi_url') if settings.get('webapi_port'): upldwf.inputs.port = settings.get('webapi_port') upldwf.inputs.email = settings.get('email') upldwf.inputs.strict = settings.get('upload_strict', False) workflow.connect([ (iqmswf, upldwf, [('outputnode.out_file', 'in_iqms')]), ]) return workflow
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