def hmc_mcflirt(settings, name='fMRI_HMC_mcflirt'): """ An :abbr:`HMC (head motion correction)` for functional scans using FSL MCFLIRT .. workflow:: from mriqc.workflows.functional import hmc_mcflirt wf = hmc_mcflirt({'biggest_file_size_gb': 1}) """ workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_fd']), name='outputnode') gen_ref = pe.Node(nwr.EstimateReferenceImage(mc_method="AFNI"), name="gen_ref") mcflirt = pe.Node(fsl.MCFLIRT(save_plots=True, interpolation='sinc'), name='MCFLIRT', mem_gb=settings['biggest_file_size_gb'] * 2.5) fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False, parameter_source="FSL"), name='ComputeFD') workflow.connect([ (inputnode, gen_ref, [('in_file', 'in_file')]), (gen_ref, mcflirt, [('ref_image', 'ref_file')]), (inputnode, mcflirt, [('in_file', 'in_file')]), (inputnode, fdnode, [('fd_radius', 'radius')]), (mcflirt, fdnode, [('par_file', 'in_file')]), (mcflirt, outputnode, [('out_file', 'out_file')]), (fdnode, outputnode, [('out_file', 'out_fd')]), ]) return workflow
def hmc_afni(settings, name='fMRI_HMC_afni', st_correct=False, despike=False, deoblique=False, start_idx=None, stop_idx=None): """ A :abbr:`HMC (head motion correction)` workflow for functional scans .. workflow:: from mriqc.workflows.functional import hmc_afni wf = hmc_afni({'biggest_file_size_gb': 1}) """ biggest_file_gb = settings.get("biggest_file_size_gb", 1) workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface( fields=['in_file', 'fd_radius', 'start_idx', 'stop_idx']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['out_file', 'out_fd']), name='outputnode') if (start_idx is not None) or (stop_idx is not None): drop_trs = pe.Node(afni.Calc(expr='a', outputtype='NIFTI_GZ'), name='drop_trs') workflow.connect([ (inputnode, drop_trs, [('in_file', 'in_file_a'), ('start_idx', 'start_idx'), ('stop_idx', 'stop_idx')]), ]) else: drop_trs = pe.Node(niu.IdentityInterface(fields=['out_file']), name='drop_trs') workflow.connect([ (inputnode, drop_trs, [('in_file', 'out_file')]), ]) gen_ref = pe.Node(nwr.EstimateReferenceImage(mc_method="AFNI"), name="gen_ref") # calculate hmc parameters hmc = pe.Node(afni.Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'), name='motion_correct', mem_gb=biggest_file_gb * 2.5) # Compute the frame-wise displacement fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False, parameter_source="AFNI"), name='ComputeFD') workflow.connect([ (inputnode, fdnode, [('fd_radius', 'radius')]), (gen_ref, hmc, [('ref_image', 'basefile')]), (hmc, outputnode, [('out_file', 'out_file')]), (hmc, fdnode, [('oned_file', 'in_file')]), (fdnode, outputnode, [('out_file', 'out_fd')]), ]) # Slice timing correction, despiking, and deoblique st_corr = pe.Node(afni.TShift(outputtype='NIFTI_GZ'), name='TimeShifts') deoblique_node = pe.Node(afni.Refit(deoblique=True), name='deoblique') despike_node = pe.Node(afni.Despike(outputtype='NIFTI_GZ'), name='despike') if st_correct and despike and deoblique: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, despike_node, [('out_file', 'in_file')]), (despike_node, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) elif st_correct and despike: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, despike_node, [('out_file', 'in_file')]), (despike_node, gen_ref, [('out_file', 'in_file')]), (despike_node, hmc, [('out_file', 'in_file')]), ]) elif st_correct and deoblique: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) elif st_correct: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, gen_ref, [('out_file', 'in_file')]), (st_corr, hmc, [('out_file', 'in_file')]), ]) elif despike and deoblique: workflow.connect([ (drop_trs, despike_node, [('out_file', 'in_file')]), (despike_node, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) elif despike: workflow.connect([ (drop_trs, despike_node, [('out_file', 'in_file')]), (despike_node, gen_ref, [('out_file', 'in_file')]), (despike_node, hmc, [('out_file', 'in_file')]), ]) elif deoblique: workflow.connect([ (drop_trs, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, gen_ref, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), ]) else: workflow.connect([ (drop_trs, gen_ref, [('out_file', 'in_file')]), (drop_trs, hmc, [('out_file', 'in_file')]), ]) return workflow
def init_bold_confs_wf(mem_gb, use_aroma, ignore_aroma_err, metadata, name="bold_confs_wf"): """ This workflow calculates confounds for a BOLD series, and aggregates them into a :abbr:`TSV (tab-separated value)` file, for use as nuisance regressors in a :abbr:`GLM (general linear model)`. The following confounds are calculated, with column headings in parentheses: #. Region-wise average signal (``CSF``, ``WhiteMatter``, ``GlobalSignal``) #. DVARS - standard, nonstandard, and voxel-wise standard variants (``stdDVARS``, ``non-stdDVARS``, ``vx-wisestdDVARS``) #. Framewise displacement, based on MCFLIRT motion parameters (``FramewiseDisplacement``) #. Temporal CompCor (``tCompCorXX``) #. Anatomical CompCor (``aCompCorXX``) #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (``CosineXX``) #. Non-steady-state volumes (``NonSteadyStateXX``) #. Estimated head-motion parameters, in mm and rad (``X``, ``Y``, ``Z``, ``RotX``, ``RotY``, ``RotZ``) #. ICA-AROMA-identified noise components, if enabled (``AROMAAggrCompXX``) Prior to estimating aCompCor and tCompCor, non-steady-state volumes are censored and high-pass filtered using a :abbr:`DCT (discrete cosine transform)` basis. The cosine basis, as well as one regressor per censored volume, are included for convenience. .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.confounds import init_bold_confs_wf wf = init_bold_confs_wf( mem_gb=1, use_aroma=True, ignore_aroma_err=True, metadata={}) **Parameters** mem_gb : float Size of BOLD file in GB - please note that this size should be calculated after resamplings that may extend the FoV use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series ignore_aroma_err : bool Do not fail on ICA-AROMA errors metadata : dict BIDS metadata for BOLD file **Inputs** bold BOLD image, after the prescribed corrections (STC, HMC and SDC) when available. bold_mask BOLD series mask movpar_file SPM-formatted motion parameters file t1_mask Mask of the skull-stripped template image t1_tpms List of tissue probability maps in T1w space t1_bold_xform Affine matrix that maps the T1w space into alignment with the native BOLD space bold_mni BOLD image resampled in MNI space (only if ``use_aroma`` enabled) bold_mask_mni Brain mask corresponding to the BOLD image resampled in MNI space (only if ``use_aroma`` enabled) **Outputs** confounds_file TSV of all aggregated confounds confounds_list List of calculated confounds for reporting acompcor_report Reportlet visualizing white-matter/CSF mask used for aCompCor tcompcor_report Reportlet visualizing ROI identified in tCompCor ica_aroma_report Reportlet visualizing MELODIC ICs, with ICA-AROMA signal/noise labels 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 **Subworkflows** * :py:func:`~fmriprep.workflows.bold.confounds.init_ica_aroma_wf` """ inputnode = pe.Node(niu.IdentityInterface( fields=['bold', 'bold_mask', 'movpar_file', 't1_mask', 't1_tpms', 't1_bold_xform', 'bold_mni', 'bold_mask_mni']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['confounds_file', 'confounds_list', 'rois_report', 'ica_aroma_report', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file']), name='outputnode') # Get masks ready in T1w space acc_tpm = pe.Node(AddTPMs(indices=[0, 2]), name='tpms_add_csf_wm') # acc stands for aCompCor csf_roi = pe.Node(TPM2ROI(erode_mm=0, mask_erode_mm=30), name='csf_roi') wm_roi = pe.Node(TPM2ROI( erode_prop=0.6, mask_erode_prop=0.6**3), # 0.6 = radius; 0.6^3 = volume name='wm_roi') acc_roi = pe.Node(TPM2ROI( erode_prop=0.6, mask_erode_prop=0.6**3), # 0.6 = radius; 0.6^3 = volume name='acc_roi') # Map ROIs in T1w space into BOLD space csf_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True), name='csf_tfm', mem_gb=0.1) wm_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True), name='wm_tfm', mem_gb=0.1) acc_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True), name='acc_tfm', mem_gb=0.1) tcc_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True), name='tcc_tfm', mem_gb=0.1) # Ensure ROIs don't go off-limits (reduced FoV) csf_msk = pe.Node(niu.Function(function=_maskroi), name='csf_msk') wm_msk = pe.Node(niu.Function(function=_maskroi), name='wm_msk') acc_msk = pe.Node(niu.Function(function=_maskroi), name='acc_msk') tcc_msk = pe.Node(niu.Function(function=_maskroi), name='tcc_msk') # DVARS dvars = pe.Node(nac.ComputeDVARS(save_all=True, remove_zerovariance=True), name="dvars", mem_gb=mem_gb) # Frame displacement fdisp = pe.Node(nac.FramewiseDisplacement(parameter_source="SPM"), name="fdisp", mem_gb=mem_gb) # a/t-CompCor non_steady_state = pe.Node(nac.NonSteadyStateDetector(), name='non_steady_state') tcompcor = pe.Node(nac.TCompCor( components_file='tcompcor.tsv', pre_filter='cosine', save_pre_filter=True, percentile_threshold=.05), name="tcompcor", mem_gb=mem_gb) acompcor = pe.Node(nac.ACompCor( components_file='acompcor.tsv', pre_filter='cosine', save_pre_filter=True), name="acompcor", mem_gb=mem_gb) # Set TR if present if 'RepetitionTime' in metadata: tcompcor.inputs.repetition_time = metadata['RepetitionTime'] acompcor.inputs.repetition_time = metadata['RepetitionTime'] # Global and segment regressors mrg_lbl = pe.Node(niu.Merge(3), name='merge_rois', run_without_submitting=True) signals = pe.Node(SignalExtraction( detrend=True, class_labels=["CSF", "WhiteMatter", "GlobalSignal"]), name="signals", mem_gb=mem_gb) # Arrange confounds add_header = pe.Node(AddTSVHeader(columns=["X", "Y", "Z", "RotX", "RotY", "RotZ"]), name="add_header", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # Generate reportlet mrg_compcor = pe.Node(niu.Merge(2), name='merge_compcor', run_without_submitting=True) rois_plot = pe.Node(ROIsPlot(compress_report=True, colors=['r', 'b', 'magenta'], generate_report=True), name='rois_plot') def _pick_csf(files): return files[0] def _pick_wm(files): return files[-1] workflow = pe.Workflow(name=name) workflow.connect([ # Massage ROIs (in T1w space) (inputnode, acc_tpm, [('t1_tpms', 'in_files')]), (inputnode, csf_roi, [(('t1_tpms', _pick_csf), 'in_tpm'), ('t1_mask', 'in_mask')]), (inputnode, wm_roi, [(('t1_tpms', _pick_wm), 'in_tpm'), ('t1_mask', 'in_mask')]), (inputnode, acc_roi, [('t1_mask', 'in_mask')]), (acc_tpm, acc_roi, [('out_file', 'in_tpm')]), # Map ROIs to BOLD (inputnode, csf_tfm, [('bold_mask', 'reference_image'), ('t1_bold_xform', 'transforms')]), (csf_roi, csf_tfm, [('roi_file', 'input_image')]), (inputnode, wm_tfm, [('bold_mask', 'reference_image'), ('t1_bold_xform', 'transforms')]), (wm_roi, wm_tfm, [('roi_file', 'input_image')]), (inputnode, acc_tfm, [('bold_mask', 'reference_image'), ('t1_bold_xform', 'transforms')]), (acc_roi, acc_tfm, [('roi_file', 'input_image')]), (inputnode, tcc_tfm, [('bold_mask', 'reference_image'), ('t1_bold_xform', 'transforms')]), (csf_roi, tcc_tfm, [('eroded_mask', 'input_image')]), # Mask ROIs with bold_mask (inputnode, csf_msk, [('bold_mask', 'in_mask')]), (inputnode, wm_msk, [('bold_mask', 'in_mask')]), (inputnode, acc_msk, [('bold_mask', 'in_mask')]), (inputnode, tcc_msk, [('bold_mask', 'in_mask')]), # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('bold', 'in_file'), ('bold_mask', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), # Calculate nonsteady state (inputnode, non_steady_state, [('bold', 'in_file')]), # tCompCor (inputnode, tcompcor, [('bold', 'realigned_file')]), (non_steady_state, tcompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (tcc_tfm, tcc_msk, [('output_image', 'roi_file')]), (tcc_msk, tcompcor, [('out', 'mask_files')]), # aCompCor (inputnode, acompcor, [('bold', 'realigned_file')]), (non_steady_state, acompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (acc_tfm, acc_msk, [('output_image', 'roi_file')]), (acc_msk, acompcor, [('out', 'mask_files')]), # Global signals extraction (constrained by anatomy) (inputnode, signals, [('bold', 'in_file')]), (csf_tfm, csf_msk, [('output_image', 'roi_file')]), (csf_msk, mrg_lbl, [('out', 'in1')]), (wm_tfm, wm_msk, [('output_image', 'roi_file')]), (wm_msk, mrg_lbl, [('out', 'in2')]), (inputnode, mrg_lbl, [('bold_mask', 'in3')]), (mrg_lbl, signals, [('out', 'label_files')]), # Collate computed confounds together (inputnode, add_header, [('movpar_file', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (dvars, concat, [('out_all', 'dvars')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_header, concat, [('out_file', 'motion')]), # Set outputs (concat, outputnode, [('confounds_file', 'confounds_file'), ('confounds_list', 'confounds_list')]), (inputnode, rois_plot, [('bold', 'in_file'), ('bold_mask', 'in_mask')]), (tcompcor, mrg_compcor, [('high_variance_masks', 'in1')]), (acc_msk, mrg_compcor, [('out', 'in2')]), (mrg_compcor, rois_plot, [('out', 'in_rois')]), (rois_plot, outputnode, [('out_report', 'rois_report')]), ]) if use_aroma: # ICA-AROMA ica_aroma_wf = init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=ignore_aroma_err) workflow.connect([ (inputnode, ica_aroma_wf, [('bold_mni', 'inputnode.bold_mni'), ('bold_mask_mni', 'inputnode.bold_mask_mni'), ('movpar_file', 'inputnode.movpar_file')]), (ica_aroma_wf, concat, [('outputnode.aroma_confounds', 'aroma')]), (ica_aroma_wf, outputnode, [('outputnode.out_report', 'ica_aroma_report'), ('outputnode.aroma_noise_ics', 'aroma_noise_ics'), ('outputnode.melodic_mix', 'melodic_mix'), ('outputnode.nonaggr_denoised_file', 'nonaggr_denoised_file')]) ]) return workflow
def init_bold_confs_wf(bold_file_size_gb, use_aroma, ignore_aroma_err, metadata, name="bold_confs_wf"): """ This workflow calculates confounds for a BOLD series, and aggregates them into a :abbr:`TSV (tab-separated value)` file, for use as nuisance regressors in a :abbr:`GLM (general linear model)`. The following confounds are calculated, with column headings in parentheses: #. White matter / global signals (WhiteMatter, GlobalSignal) #. DVARS - standard, nonstandard, and voxel-wise standard variants (stdDVARS, non-stdDVARS, vx-wisestdDVARS) #. Framewise displacement, based on MCFLIRT motion parameters (FramewiseDisplacement) #. tCompCor #. aCompCor #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (CosineXX) #. Non-steady-state volumes (NonSteadyStateXX) #. MCFLIRT motion parameters, in mm and rad (X, Y, Z, RotX, RotY, RotZ) #. ICA-AROMA-identified noise components, if enabled (AROMAAggrCompXX) Prior to estimating aCompCor and tCompCor, non-steady-state volumes are censored and high-pass filtered using a :abbr:`DCT (discrete cosine transform)` basis. The cosine basis, as well as one regressor per censored volume, are included for convenience. .. workflow:: :graph2use: orig :simpleform: yes from fmriprep.workflows.confounds import init_bold_confs_wf wf = init_bold_confs_wf(bold_file_size_gb=1, use_aroma=True, ignore_aroma_err=True, metadata={}) Parameters bold_file_size_gb : float Size of BOLD file in GB use_aroma : bool Perform ICA-AROMA on MNI-resampled functional series ignore_aroma_err : bool Do not fail on ICA-AROMA errors metadata : dict BIDS metadata for BOLD file Inputs bold_t1 BOLD image, resampled in T1w space movpar_file SPM-formatted motion parameters file t1_mask Mask of the skull-stripped template image t1_tpms List of tissue probability maps in T1w space bold_mask_t1 BOLD series mask in T1w space bold_mni BOLD series, resampled to template space bold_mask_mni BOLD series mask in template space Outputs confounds_file TSV of all aggregated confounds confounds_list List of calculated confounds for reporting acompcor_report Reportlet visualizing white-matter/CSF mask used for aCompCor tcompcor_report Reportlet visualizing ROI identified in tCompCor ica_aroma_report Reportlet visualizing MELODIC ICs, with ICA-AROMA signal/noise labels 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 """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold_t1', 'movpar_file', 't1_mask', 't1_tpms', 'bold_mask_t1', 'bold_mni', 'bold_mask_mni' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'confounds_file', 'confounds_list', 'acompcor_report', 'tcompcor_report', 'ica_aroma_report', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file' ]), name='outputnode') # ICA-AROMA if use_aroma: ica_aroma_wf = init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=ignore_aroma_err) # DVARS dvars = pe.Node(nac.ComputeDVARS(save_all=True, remove_zerovariance=True), name="dvars", mem_gb=bold_file_size_gb * 3) # Frame displacement fdisp = pe.Node(nac.FramewiseDisplacement(parameter_source="SPM"), name="fdisp", mem_gb=bold_file_size_gb * 3) # CompCor non_steady_state = pe.Node(nac.NonSteadyStateDetector(), name='non_steady_state') tcompcor = pe.Node(TCompCorRPT(components_file='tcompcor.tsv', generate_report=True, pre_filter='cosine', save_pre_filter=True, percentile_threshold=.05), name="tcompcor", mem_gb=bold_file_size_gb * 3) acompcor = pe.Node(ACompCorRPT(components_file='acompcor.tsv', pre_filter='cosine', save_pre_filter=True, generate_report=True), name="acompcor", mem_gb=bold_file_size_gb * 3) csf_roi = pe.Node(TPM2ROI(erode_mm=0, mask_erode_mm=30), name='csf_roi') wm_roi = pe.Node(TPM2ROI(erode_mm=6, mask_erode_mm=10), name='wm_roi') merge_rois = pe.Node(niu.Merge(2), name='merge_rois', run_without_submitting=True, mem_gb=0.01) combine_rois = pe.Node(CombineROIs(), name='combine_rois') concat_rois = pe.Node(ConcatROIs(), name='concat_rois') # Global and segment regressors signals = pe.Node(SignalExtraction( detrend=True, class_labels=["WhiteMatter", "GlobalSignal"]), name="signals", mem_gb=bold_file_size_gb * 3) # Arrange confounds add_header = pe.Node( AddTSVHeader(columns=["X", "Y", "Z", "RotX", "RotY", "RotZ"]), name="add_header", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # Set TR if present if 'RepetitionTime' in metadata: tcompcor.inputs.repetition_time = metadata['RepetitionTime'] acompcor.inputs.repetition_time = metadata['RepetitionTime'] def _pick_csf(files): return files[0] def _pick_wm(files): return files[2] workflow = pe.Workflow(name=name) workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('bold_t1', 'in_file'), ('bold_mask_t1', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), (inputnode, non_steady_state, [('bold_t1', 'in_file')]), (inputnode, tcompcor, [('bold_t1', 'realigned_file')]), (non_steady_state, tcompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (non_steady_state, acompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (inputnode, csf_roi, [(('t1_tpms', _pick_csf), 't1_tpm'), ('t1_mask', 't1_mask'), ('bold_mask_t1', 'bold_mask')]), (csf_roi, tcompcor, [('eroded_mask', 'mask_files')]), (inputnode, wm_roi, [(('t1_tpms', _pick_wm), 't1_tpm'), ('t1_mask', 't1_mask'), ('bold_mask_t1', 'bold_mask')]), (csf_roi, merge_rois, [('roi_file', 'in1')]), (wm_roi, merge_rois, [('roi_file', 'in2')]), (merge_rois, combine_rois, [('out', 'in_files')]), (inputnode, combine_rois, [('bold_t1', 'ref_header')]), # anatomical confound: aCompCor. (inputnode, acompcor, [('bold_t1', 'realigned_file')]), (combine_rois, acompcor, [('out_file', 'mask_files')]), (wm_roi, concat_rois, [('roi_file', 'in_file')]), (inputnode, concat_rois, [('bold_mask_t1', 'in_mask')]), (inputnode, concat_rois, [('bold_t1', 'ref_header')]), # anatomical confound: signal extraction (concat_rois, signals, [('out_file', 'label_files')]), (inputnode, signals, [('bold_t1', 'in_file')]), # connect the confound nodes to the concatenate node (inputnode, add_header, [('movpar_file', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (dvars, concat, [('out_all', 'dvars')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_header, concat, [('out_file', 'motion')]), (concat, outputnode, [('confounds_file', 'confounds_file'), ('confounds_list', 'confounds_list')]), (acompcor, outputnode, [('out_report', 'acompcor_report')]), (tcompcor, outputnode, [('out_report', 'tcompcor_report')]), ]) if use_aroma: workflow.connect([ (inputnode, ica_aroma_wf, [('bold_mni', 'inputnode.bold_mni'), ('bold_mask_mni', 'inputnode.bold_mask_mni'), ('movpar_file', 'inputnode.movpar_file')]), (ica_aroma_wf, concat, [('outputnode.aroma_confounds', 'aroma')]), (ica_aroma_wf, outputnode, [('outputnode.out_report', 'ica_aroma_report'), ('outputnode.aroma_noise_ics', 'aroma_noise_ics'), ('outputnode.melodic_mix', 'melodic_mix'), ('outputnode.nonaggr_denoised_file', 'nonaggr_denoised_file')]) ]) return workflow
def init_discover_wf(bold_file_size_gb, use_aroma, ignore_aroma_err, name="discover_wf"): ''' All input fields are required. Calculates global regressor and tCompCor from motion-corrected fMRI ('inputnode.fmri_file'). Calculates DVARS from the fMRI and an EPI brain mask ('inputnode.epi_mask') Calculates frame displacement from MCFLIRT movement parameters ('inputnode.movpar_file') Calculates segment regressors and aCompCor from the fMRI and a white matter/gray matter/CSF segmentation ('inputnode.t1_seg'), after applying the transform to the images. Transforms should be fsl-formatted. Calculates noise components identified from ICA_AROMA (if ``use_aroma=True``) Saves the confounds in a file ('outputnode.confounds_file')''' inputnode = pe.Node(utility.IdentityInterface(fields=[ 'fmri_file', 'movpar_file', 't1_tpms', 'epi_mask', 'epi_mni', 'epi_mask_mni' ]), name='inputnode') outputnode = pe.Node(utility.IdentityInterface(fields=[ 'confounds_file', 'acompcor_report', 'tcompcor_report', 'ica_aroma_report', 'aroma_noise_ics', 'melodic_mix' ]), name='outputnode') # ICA-AROMA if use_aroma: ica_aroma_wf = init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=ignore_aroma_err) # DVARS dvars = pe.Node(confounds.ComputeDVARS(save_all=True, remove_zerovariance=True), name="dvars") dvars.interface.estimated_memory_gb = bold_file_size_gb * 3 # Frame displacement frame_displace = pe.Node( confounds.FramewiseDisplacement(parameter_source="SPM"), name="frame_displace") frame_displace.interface.estimated_memory_gb = bold_file_size_gb * 3 # CompCor tcompcor = pe.Node(TCompCorRPT(components_file='tcompcor.tsv', generate_report=True, percentile_threshold=.05), name="tcompcor") tcompcor.interface.estimated_memory_gb = bold_file_size_gb * 3 CSF_roi = pe.Node(utility.Function( function=prepare_roi_from_probtissue, output_names=['roi_file', 'eroded_mask']), name='CSF_roi') CSF_roi.inputs.erosion_mm = 0 CSF_roi.inputs.epi_mask_erosion_mm = 30 WM_roi = pe.Node(utility.Function(function=prepare_roi_from_probtissue, output_names=['roi_file', 'eroded_mask']), name='WM_roi') WM_roi.inputs.erosion_mm = 6 WM_roi.inputs.epi_mask_erosion_mm = 10 def concat_rois_func(in_WM, in_mask, ref_header): import os import nibabel as nb from nilearn.image import resample_to_img WM_nii = nb.load(in_WM) mask_nii = nb.load(in_mask) # we have to do this explicitly because of potential differences in # qform_code between the two files that prevent SignalExtraction to do # the concatenation concat_nii = nb.funcs.concat_images([ resample_to_img(WM_nii, mask_nii, interpolation='nearest'), mask_nii ]) concat_nii = nb.Nifti1Image(concat_nii.get_data(), nb.load(ref_header).affine, nb.load(ref_header).header) concat_nii.to_filename("concat.nii.gz") return os.path.abspath("concat.nii.gz") concat_rois = pe.Node(utility.Function(function=concat_rois_func), name='concat_rois') # Global and segment regressors signals = pe.Node(SignalExtraction( detrend=True, class_labels=["WhiteMatter", "GlobalSignal"]), name="signals") signals.interface.estimated_memory_gb = bold_file_size_gb * 3 def combine_rois(in_CSF, in_WM, ref_header): import os import numpy as np import nibabel as nb CSF_nii = nb.load(in_CSF) CSF_data = CSF_nii.get_data() WM_nii = nb.load(in_WM) WM_data = WM_nii.get_data() combined = np.zeros_like(WM_data) combined[WM_data != 0] = 1 combined[CSF_data != 0] = 1 # we have to do this explicitly because of potential differences in # qform_code between the two files that prevent aCompCor to work new_nii = nb.Nifti1Image(combined, nb.load(ref_header).affine, nb.load(ref_header).header) new_nii.to_filename("logical_or.nii.gz") return os.path.abspath("logical_or.nii.gz") combine_rois = pe.Node(utility.Function(function=combine_rois), name='combine_rois') acompcor = pe.Node(ACompCorRPT(components_file='acompcor.tsv', generate_report=True), name="acompcor") acompcor.interface.estimated_memory_gb = bold_file_size_gb * 3 # misc utilities concat = pe.Node(utility.Function(function=_gather_confounds), name="concat") def pick_csf(files): return files[0] def pick_wm(files): return files[2] def add_header_func(in_file): import numpy as np import pandas as pd import os from sys import version_info PY3 = version_info[0] > 2 data = np.loadtxt(in_file) df = pd.DataFrame(data, columns=["X", "Y", "Z", "RotX", "RotY", "RotZ"]) df.to_csv("motion.tsv", sep="\t" if PY3 else '\t'.encode(), index=None) return os.path.abspath("motion.tsv") add_header = pe.Node(utility.Function(function=add_header_func), name="add_header") workflow = pe.Workflow(name=name) workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('fmri_file', 'in_file'), ('epi_mask', 'in_mask')]), (inputnode, frame_displace, [('movpar_file', 'in_file')]), (inputnode, tcompcor, [('fmri_file', 'realigned_file')]), (inputnode, CSF_roi, [(('t1_tpms', pick_csf), 'in_file')]), (inputnode, CSF_roi, [('epi_mask', 'epi_mask')]), (CSF_roi, tcompcor, [('eroded_mask', 'mask_files')]), (inputnode, WM_roi, [(('t1_tpms', pick_wm), 'in_file')]), (inputnode, WM_roi, [('epi_mask', 'epi_mask')]), (CSF_roi, combine_rois, [('roi_file', 'in_CSF')]), (WM_roi, combine_rois, [('roi_file', 'in_WM')]), (inputnode, combine_rois, [('fmri_file', 'ref_header')]), # anatomical confound: aCompCor. (inputnode, acompcor, [('fmri_file', 'realigned_file')]), (combine_rois, acompcor, [('out', 'mask_files')]), (WM_roi, concat_rois, [('roi_file', 'in_WM')]), (inputnode, concat_rois, [('epi_mask', 'in_mask')]), (inputnode, concat_rois, [('fmri_file', 'ref_header')]), # anatomical confound: signal extraction (concat_rois, signals, [('out', 'label_files')]), (inputnode, signals, [('fmri_file', 'in_file')]), # connect the confound nodes to the concatenate node (signals, concat, [('out_file', 'signals')]), (dvars, concat, [('out_all', 'dvars')]), (frame_displace, concat, [('out_file', 'frame_displace')]), (tcompcor, concat, [('components_file', 'tcompcor')]), (acompcor, concat, [('components_file', 'acompcor')]), (inputnode, add_header, [('movpar_file', 'in_file')]), (add_header, concat, [('out', 'motion')]), (concat, outputnode, [('out', 'confounds_file')]), (acompcor, outputnode, [('out_report', 'acompcor_report')]), (tcompcor, outputnode, [('out_report', 'tcompcor_report')]), ]) if use_aroma: workflow.connect([(inputnode, ica_aroma_wf, [ ('epi_mni', 'inputnode.epi_mni'), ('epi_mask_mni', 'inputnode.epi_mask_mni'), ('movpar_file', 'inputnode.movpar_file') ]), (ica_aroma_wf, concat, [('outputnode.aroma_confounds', 'aroma')]), (ica_aroma_wf, outputnode, [('outputnode.out_report', 'ica_aroma_report'), ('outputnode.aroma_noise_ics', 'aroma_noise_ics'), ('outputnode.melodic_mix', 'melodic_mix')])]) return workflow
def init_bold_confs_wf(bold_file_size_gb, use_aroma, ignore_aroma_err, metadata, name="bold_confs_wf"): ''' All input fields are required. Calculates global regressor and tCompCor from motion-corrected fMRI ('inputnode.fmri_file'). Calculates DVARS from the fMRI and an EPI brain mask ('inputnode.bold_mask') Calculates frame displacement from MCFLIRT movement parameters ('inputnode.movpar_file') Calculates segment regressors and aCompCor from the fMRI and a white matter/gray matter/CSF segmentation ('inputnode.t1_seg'), after applying the transform to the images. Transforms should be fsl-formatted. Calculates noise components identified from ICA-AROMA (if ``use_aroma=True``) Saves the confounds in a file ('outputnode.confounds_file')''' inputnode = pe.Node(niu.IdentityInterface(fields=[ 'fmri_file', 'movpar_file', 't1_mask', 't1_tpms', 'bold_mask', 'bold_mni', 'bold_mask_mni' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'confounds_file', 'confounds_list', 'acompcor_report', 'tcompcor_report', 'ica_aroma_report', 'aroma_noise_ics', 'melodic_mix', 'nonaggr_denoised_file' ]), name='outputnode') # ICA-AROMA if use_aroma: ica_aroma_wf = init_ica_aroma_wf(name='ica_aroma_wf', ignore_aroma_err=ignore_aroma_err) # DVARS dvars = pe.Node(nac.ComputeDVARS(save_all=True, remove_zerovariance=True), name="dvars", mem_gb=bold_file_size_gb * 3) # Frame displacement fdisp = pe.Node(nac.FramewiseDisplacement(parameter_source="SPM"), name="fdisp", mem_gb=bold_file_size_gb * 3) # CompCor non_steady_state = pe.Node(nac.NonSteadyStateDetector(), name='non_steady_state') tcompcor = pe.Node(TCompCorRPT(components_file='tcompcor.tsv', generate_report=True, pre_filter='cosine', save_pre_filter=True, percentile_threshold=.05), name="tcompcor", mem_gb=bold_file_size_gb * 3) acompcor = pe.Node(ACompCorRPT(components_file='acompcor.tsv', pre_filter='cosine', save_pre_filter=True, generate_report=True), name="acompcor", mem_gb=bold_file_size_gb * 3) csf_roi = pe.Node(TPM2ROI(erode_mm=0, mask_erode_mm=30), name='csf_roi') wm_roi = pe.Node(TPM2ROI(erode_mm=6, mask_erode_mm=10), name='wm_roi') merge_rois = pe.Node(niu.Merge(2), name='merge_rois', run_without_submit=True, mem_gb=0.01) combine_rois = pe.Node(CombineROIs(), name='combine_rois') concat_rois = pe.Node(ConcatROIs(), name='concat_rois') # Global and segment regressors signals = pe.Node(SignalExtraction( detrend=True, class_labels=["WhiteMatter", "GlobalSignal"]), name="signals", mem_gb=bold_file_size_gb * 3) # Arrange confounds add_header = pe.Node( AddTSVHeader(columns=["X", "Y", "Z", "RotX", "RotY", "RotZ"]), name="add_header", mem_gb=0.01, run_without_submit=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submit=True) # Set TR if present if 'RepetitionTime' in metadata: tcompcor.inputs.repetition_time = metadata['RepetitionTime'] acompcor.inputs.repetition_time = metadata['RepetitionTime'] def _pick_csf(files): return files[0] def _pick_wm(files): return files[2] workflow = pe.Workflow(name=name) workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('fmri_file', 'in_file'), ('bold_mask', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), (inputnode, non_steady_state, [('fmri_file', 'in_file')]), (inputnode, tcompcor, [('fmri_file', 'realigned_file')]), (non_steady_state, tcompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (non_steady_state, acompcor, [('n_volumes_to_discard', 'ignore_initial_volumes')]), (inputnode, csf_roi, [(('t1_tpms', _pick_csf), 't1_tpm'), ('t1_mask', 't1_mask'), ('bold_mask', 'bold_mask')]), (csf_roi, tcompcor, [('eroded_mask', 'mask_files')]), (inputnode, wm_roi, [(('t1_tpms', _pick_wm), 't1_tpm'), ('t1_mask', 't1_mask'), ('bold_mask', 'bold_mask')]), (csf_roi, merge_rois, [('roi_file', 'in1')]), (wm_roi, merge_rois, [('roi_file', 'in2')]), (merge_rois, combine_rois, [('out', 'in_files')]), (inputnode, combine_rois, [('fmri_file', 'ref_header')]), # anatomical confound: aCompCor. (inputnode, acompcor, [('fmri_file', 'realigned_file')]), (combine_rois, acompcor, [('out_file', 'mask_files')]), (wm_roi, concat_rois, [('roi_file', 'in_file')]), (inputnode, concat_rois, [('bold_mask', 'in_mask')]), (inputnode, concat_rois, [('fmri_file', 'ref_header')]), # anatomical confound: signal extraction (concat_rois, signals, [('out_file', 'label_files')]), (inputnode, signals, [('fmri_file', 'in_file')]), # connect the confound nodes to the concatenate node (inputnode, add_header, [('movpar_file', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (dvars, concat, [('out_all', 'dvars')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_header, concat, [('out_file', 'motion')]), (concat, outputnode, [('confounds_file', 'confounds_file'), ('confounds_list', 'confounds_list')]), (acompcor, outputnode, [('out_report', 'acompcor_report')]), (tcompcor, outputnode, [('out_report', 'tcompcor_report')]), ]) if use_aroma: workflow.connect([ (inputnode, ica_aroma_wf, [('bold_mni', 'inputnode.bold_mni'), ('bold_mask_mni', 'inputnode.bold_mask_mni'), ('movpar_file', 'inputnode.movpar_file')]), (ica_aroma_wf, concat, [('outputnode.aroma_confounds', 'aroma')]), (ica_aroma_wf, outputnode, [('outputnode.out_report', 'ica_aroma_report'), ('outputnode.aroma_noise_ics', 'aroma_noise_ics'), ('outputnode.melodic_mix', 'melodic_mix'), ('outputnode.nonaggr_denoised_file', 'nonaggr_denoised_file')]) ]) return workflow