def hmc_mcflirt(name='fMRI_HMC_mcflirt'): """ An :abbr:`HMC (head motion correction)` for functional scans using FSL MCFLIRT """ 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') mcflirt = pe.Node(fsl.MCFLIRT(save_plots=True, save_rms=True, save_mats=True), name='MCFLIRT') fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False), name='ComputeFD') workflow.connect([ (inputnode, mcflirt, [('in_file', 'in_file')]), (inputnode, fdnode, [('fd_radius', 'radius')]), (mcflirt, fdnode, [('par_file', 'in_plots')]), (mcflirt, outputnode, [('out_file', 'out_file')]), (fdnode, outputnode, [('out_file', 'out_fd')]), ]) return workflow
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') mcflirt.interface.estimated_memory_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 init_bold_confs_wf(mem_gb, 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``, ``white_matter``, ``global_signal``) #. DVARS - original and standardized variants (``dvars``, ``std_dvars``) #. Framewise displacement, based on head-motion parameters (``framewise_displacement``) #. Temporal CompCor (``t_comp_cor_XX``) #. Anatomical CompCor (``a_comp_cor_XX``) #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (``cosine_XX``) #. Non-steady-state volumes (``non_steady_state_XX``) #. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) 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, 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 metadata : dict BIDS metadata for BOLD file name : str Name of workflow (default: ``bold_confs_wf``) **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 skip_vols number of non steady state volumes 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 **Outputs** confounds_file TSV of all aggregated confounds rois_report Reportlet visualizing white-matter/CSF mask used for aCompCor, the ROI for tCompCor and the BOLD brain mask. """ workflow = Workflow(name=name) workflow.__desc__ = """\ Several confounding time-series were calculated based on the *preprocessed BOLD*: framewise displacement (FD), DVARS and three region-wise global signals. FD and DVARS are calculated for each functional run, both using their implementations in *Nipype* [following the definitions by @power_fd_dvars]. The three global signals are extracted within the CSF, the WM, and the whole-brain masks. Additionally, a set of physiological regressors were extracted to allow for component-based noise correction [*CompCor*, @compcor]. Principal components are estimated after high-pass filtering the *preprocessed BOLD* time-series (using a discrete cosine filter with 128s cut-off) for the two *CompCor* variants: temporal (tCompCor) and anatomical (aCompCor). Six tCompCor components are then calculated from the top 5% variable voxels within a mask covering the subcortical regions. This subcortical mask is obtained by heavily eroding the brain mask, which ensures it does not include cortical GM regions. For aCompCor, six components are calculated within the intersection of the aforementioned mask and the union of CSF and WM masks calculated in T1w space, after their projection to the native space of each functional run (using the inverse BOLD-to-T1w transformation). The head-motion estimates calculated in the correction step were also placed within the corresponding confounds file. """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold', 'bold_mask', 'movpar_file', 'skip_vols', 't1_mask', 't1_tpms', 't1_bold_xform' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=['confounds_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_nstd=True, save_std=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 tcompcor = pe.Node(TCompCor(components_file='tcompcor.tsv', header_prefix='t_comp_cor_', pre_filter='cosine', save_pre_filter=True, percentile_threshold=.05), name="tcompcor", mem_gb=mem_gb) acompcor = pe.Node(ACompCor(components_file='acompcor.tsv', header_prefix='a_comp_cor_', 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( class_labels=["csf", "white_matter", "global_signal"]), name="signals", mem_gb=mem_gb) # Arrange confounds add_dvars_header = pe.Node(AddTSVHeader(columns=["dvars"]), name="add_dvars_header", mem_gb=0.01, run_without_submitting=True) add_std_dvars_header = pe.Node(AddTSVHeader(columns=["std_dvars"]), name="add_std_dvars_header", mem_gb=0.01, run_without_submitting=True) add_motion_headers = pe.Node(AddTSVHeader( columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", 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(colors=['r', 'b', 'magenta'], generate_report=True), name='rois_plot', mem_gb=mem_gb) ds_report_bold_rois = pe.Node(DerivativesDataSink(suffix='rois'), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) def _pick_csf(files): return files[0] def _pick_wm(files): return files[-1] 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')]), # tCompCor (inputnode, tcompcor, [('bold', 'realigned_file')]), (inputnode, tcompcor, [('skip_vols', 'ignore_initial_volumes')]), (tcc_tfm, tcc_msk, [('output_image', 'roi_file')]), (tcc_msk, tcompcor, [('out', 'mask_files')]), # aCompCor (inputnode, acompcor, [('bold', 'realigned_file')]), (inputnode, acompcor, [('skip_vols', '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_motion_headers, [('movpar_file', 'in_file')]), (dvars, add_dvars_header, [('out_nstd', 'in_file')]), (dvars, add_std_dvars_header, [('out_std', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_motion_headers, concat, [('out_file', 'motion')]), (add_dvars_header, concat, [('out_file', 'dvars')]), (add_std_dvars_header, concat, [('out_file', 'std_dvars')]), # Set outputs (concat, outputnode, [('confounds_file', 'confounds_file')]), (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, ds_report_bold_rois, [('out_report', 'in_file')]), ]) return workflow
def init_bold_confs_wf( mem_gb, metadata, regressors_all_comps, regressors_dvars_th, regressors_fd_th, freesurfer=False, name="bold_confs_wf", ): """ Build a workflow to generate and write out confounding signals. 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``, ``white_matter``, ``global_signal``) #. DVARS - original and standardized variants (``dvars``, ``std_dvars``) #. Framewise displacement, based on head-motion parameters (``framewise_displacement``) #. Temporal CompCor (``t_comp_cor_XX``) #. Anatomical CompCor (``a_comp_cor_XX``) #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (``cosine_XX``) #. Non-steady-state volumes (``non_steady_state_XX``) #. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) 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 Graph .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.confounds import init_bold_confs_wf wf = init_bold_confs_wf( mem_gb=1, metadata={}, regressors_all_comps=False, regressors_dvars_th=1.5, regressors_fd_th=0.5, ) Parameters ---------- mem_gb : :obj:`float` Size of BOLD file in GB - please note that this size should be calculated after resamplings that may extend the FoV metadata : :obj:`dict` BIDS metadata for BOLD file name : :obj:`str` Name of workflow (default: ``bold_confs_wf``) regressors_all_comps : :obj:`bool` Indicates whether CompCor decompositions should return all components instead of the minimal number of components necessary to explain 50 percent of the variance in the decomposition mask. regressors_dvars_th : :obj:`float` Criterion for flagging DVARS outliers regressors_fd_th : :obj:`float` Criterion for flagging framewise displacement outliers 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 rmsd_file Framewise displacement as measured by ``fsl_motion_outliers``. skip_vols number of non steady state volumes t1w_mask Mask of the skull-stripped template image t1w_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 Outputs ------- confounds_file TSV of all aggregated confounds rois_report Reportlet visualizing white-matter/CSF mask used for aCompCor, the ROI for tCompCor and the BOLD brain mask. confounds_metadata Confounds metadata dictionary. """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.confounds import ExpandModel, SpikeRegressors from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces.images import SignalExtraction from niworkflows.interfaces.masks import ROIsPlot from niworkflows.interfaces.nibabel import ApplyMask, Binarize from niworkflows.interfaces.patches import ( RobustACompCor as ACompCor, RobustTCompCor as TCompCor, ) from niworkflows.interfaces.plotting import (CompCorVariancePlot, ConfoundsCorrelationPlot) from niworkflows.interfaces.utils import (AddTSVHeader, TSV2JSON, DictMerge) from ...interfaces.confounds import aCompCorMasks gm_desc = ( "dilating a GM mask extracted from the FreeSurfer's *aseg* segmentation" if freesurfer else "thresholding the corresponding partial volume map at 0.05") workflow = Workflow(name=name) workflow.__desc__ = f"""\ Several confounding time-series were calculated based on the *preprocessed BOLD*: framewise displacement (FD), DVARS and three region-wise global signals. FD was computed using two formulations following Power (absolute sum of relative motions, @power_fd_dvars) and Jenkinson (relative root mean square displacement between affines, @mcflirt). FD and DVARS are calculated for each functional run, both using their implementations in *Nipype* [following the definitions by @power_fd_dvars]. The three global signals are extracted within the CSF, the WM, and the whole-brain masks. Additionally, a set of physiological regressors were extracted to allow for component-based noise correction [*CompCor*, @compcor]. Principal components are estimated after high-pass filtering the *preprocessed BOLD* time-series (using a discrete cosine filter with 128s cut-off) for the two *CompCor* variants: temporal (tCompCor) and anatomical (aCompCor). tCompCor components are then calculated from the top 2% variable voxels within the brain mask. For aCompCor, three probabilistic masks (CSF, WM and combined CSF+WM) are generated in anatomical space. The implementation differs from that of Behzadi et al. in that instead of eroding the masks by 2 pixels on BOLD space, the aCompCor masks are subtracted a mask of pixels that likely contain a volume fraction of GM. This mask is obtained by {gm_desc}, and it ensures components are not extracted from voxels containing a minimal fraction of GM. Finally, these masks are resampled into BOLD space and binarized by thresholding at 0.99 (as in the original implementation). Components are also calculated separately within the WM and CSF masks. For each CompCor decomposition, the *k* components with the largest singular values are retained, such that the retained components' time series are sufficient to explain 50 percent of variance across the nuisance mask (CSF, WM, combined, or temporal). The remaining components are dropped from consideration. The head-motion estimates calculated in the correction step were also placed within the corresponding confounds file. The confound time series derived from head motion estimates and global signals were expanded with the inclusion of temporal derivatives and quadratic terms for each [@confounds_satterthwaite_2013]. Frames that exceeded a threshold of {regressors_fd_th} mm FD or {regressors_dvars_th} standardised DVARS were annotated as motion outliers. """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold', 'bold_mask', 'movpar_file', 'rmsd_file', 'skip_vols', 't1w_mask', 't1w_tpms', 't1_bold_xform' ]), name='inputnode') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'confounds_file', 'confounds_metadata', 'acompcor_masks', 'tcompcor_mask' ]), name='outputnode') # DVARS dvars = pe.Node(nac.ComputeDVARS(save_nstd=True, save_std=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) # Generate aCompCor probseg maps acc_masks = pe.Node(aCompCorMasks(is_aseg=freesurfer), name="acc_masks") # Resample probseg maps in BOLD space via T1w-to-BOLD transform acc_msk_tfm = pe.MapNode(ApplyTransforms(interpolation='Gaussian', float=False), iterfield=["input_image"], name='acc_msk_tfm', mem_gb=0.1) acc_msk_brain = pe.MapNode(ApplyMask(), name="acc_msk_brain", iterfield=["in_file"]) acc_msk_bin = pe.MapNode(Binarize(thresh_low=0.99), name='acc_msk_bin', iterfield=["in_file"]) acompcor = pe.Node(ACompCor(components_file='acompcor.tsv', header_prefix='a_comp_cor_', pre_filter='cosine', save_pre_filter=True, save_metadata=True, mask_names=['CSF', 'WM', 'combined'], merge_method='none', failure_mode='NaN'), name="acompcor", mem_gb=mem_gb) tcompcor = pe.Node(TCompCor(components_file='tcompcor.tsv', header_prefix='t_comp_cor_', pre_filter='cosine', save_pre_filter=True, save_metadata=True, percentile_threshold=.02, failure_mode='NaN'), name="tcompcor", mem_gb=mem_gb) # Set number of components if regressors_all_comps: acompcor.inputs.num_components = 'all' tcompcor.inputs.num_components = 'all' else: acompcor.inputs.variance_threshold = 0.5 tcompcor.inputs.variance_threshold = 0.5 # Set TR if present if 'RepetitionTime' in metadata: tcompcor.inputs.repetition_time = metadata['RepetitionTime'] acompcor.inputs.repetition_time = metadata['RepetitionTime'] # Global and segment regressors signals_class_labels = [ "global_signal", "csf", "white_matter", "csf_wm", "tcompcor", ] merge_rois = pe.Node(niu.Merge(3, ravel_inputs=True), name='merge_rois', run_without_submitting=True) signals = pe.Node(SignalExtraction(class_labels=signals_class_labels), name="signals", mem_gb=mem_gb) # Arrange confounds add_dvars_header = pe.Node(AddTSVHeader(columns=["dvars"]), name="add_dvars_header", mem_gb=0.01, run_without_submitting=True) add_std_dvars_header = pe.Node(AddTSVHeader(columns=["std_dvars"]), name="add_std_dvars_header", mem_gb=0.01, run_without_submitting=True) add_motion_headers = pe.Node(AddTSVHeader( columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", mem_gb=0.01, run_without_submitting=True) add_rmsd_header = pe.Node(AddTSVHeader(columns=["rmsd"]), name="add_rmsd_header", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # CompCor metadata tcc_metadata_fmt = pe.Node(TSV2JSON( index_column='component', drop_columns=['mask'], output=None, additional_metadata={'Method': 'tCompCor'}, enforce_case=True), name='tcc_metadata_fmt') acc_metadata_fmt = pe.Node(TSV2JSON( index_column='component', output=None, additional_metadata={'Method': 'aCompCor'}, enforce_case=True), name='acc_metadata_fmt') mrg_conf_metadata = pe.Node(niu.Merge(3), name='merge_confound_metadata', run_without_submitting=True) mrg_conf_metadata.inputs.in3 = { label: { 'Method': 'Mean' } for label in signals_class_labels } mrg_conf_metadata2 = pe.Node(DictMerge(), name='merge_confound_metadata2', run_without_submitting=True) # Expand model to include derivatives and quadratics model_expand = pe.Node( ExpandModel(model_formula='(dd1(rps + wm + csf + gsr))^^2 + others'), name='model_expansion') # Add spike regressors spike_regress = pe.Node(SpikeRegressors(fd_thresh=regressors_fd_th, dvars_thresh=regressors_dvars_th), name='spike_regressors') # Generate reportlet (ROIs) mrg_compcor = pe.Node(niu.Merge(2, ravel_inputs=True), name='mrg_compcor', run_without_submitting=True) rois_plot = pe.Node(ROIsPlot(colors=['b', 'magenta'], generate_report=True), name='rois_plot', mem_gb=mem_gb) ds_report_bold_rois = pe.Node(DerivativesDataSink( desc='rois', datatype="figures", dismiss_entities=("echo", )), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Generate reportlet (CompCor) mrg_cc_metadata = pe.Node(niu.Merge(2), name='merge_compcor_metadata', run_without_submitting=True) compcor_plot = pe.Node(CompCorVariancePlot( variance_thresholds=(0.5, 0.7, 0.9), metadata_sources=['tCompCor', 'aCompCor']), name='compcor_plot') ds_report_compcor = pe.Node(DerivativesDataSink( desc='compcorvar', datatype="figures", dismiss_entities=("echo", )), name='ds_report_compcor', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Generate reportlet (Confound correlation) conf_corr_plot = pe.Node(ConfoundsCorrelationPlot( reference_column='global_signal', max_dim=20), name='conf_corr_plot') ds_report_conf_corr = pe.Node(DerivativesDataSink( desc='confoundcorr', datatype="figures", dismiss_entities=("echo", )), name='ds_report_conf_corr', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) def _last(inlist): return inlist[-1] def _select_cols(table): import pandas as pd return [ col for col in pd.read_table(table, nrows=2).columns if not col.startswith(("a_comp_cor_", "t_comp_cor_", "std_dvars")) ] workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('bold', 'in_file'), ('bold_mask', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), # aCompCor (inputnode, acompcor, [("bold", "realigned_file"), ("skip_vols", "ignore_initial_volumes")]), (inputnode, acc_masks, [("t1w_tpms", "in_vfs"), (("bold", _get_zooms), "bold_zooms")]), (inputnode, acc_msk_tfm, [("t1_bold_xform", "transforms"), ("bold_mask", "reference_image")]), (inputnode, acc_msk_brain, [("bold_mask", "in_mask")]), (acc_masks, acc_msk_tfm, [("out_masks", "input_image")]), (acc_msk_tfm, acc_msk_brain, [("output_image", "in_file")]), (acc_msk_brain, acc_msk_bin, [("out_file", "in_file")]), (acc_msk_bin, acompcor, [("out_file", "mask_files")]), # tCompCor (inputnode, tcompcor, [("bold", "realigned_file"), ("skip_vols", "ignore_initial_volumes"), ("bold_mask", "mask_files")]), # Global signals extraction (constrained by anatomy) (inputnode, signals, [('bold', 'in_file')]), (inputnode, merge_rois, [('bold_mask', 'in1')]), (acc_msk_bin, merge_rois, [('out_file', 'in2')]), (tcompcor, merge_rois, [('high_variance_masks', 'in3')]), (merge_rois, signals, [('out', 'label_files')]), # Collate computed confounds together (inputnode, add_motion_headers, [('movpar_file', 'in_file')]), (inputnode, add_rmsd_header, [('rmsd_file', 'in_file')]), (dvars, add_dvars_header, [('out_nstd', 'in_file')]), (dvars, add_std_dvars_header, [('out_std', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_motion_headers, concat, [('out_file', 'motion')]), (add_rmsd_header, concat, [('out_file', 'rmsd')]), (add_dvars_header, concat, [('out_file', 'dvars')]), (add_std_dvars_header, concat, [('out_file', 'std_dvars')]), # Confounds metadata (tcompcor, tcc_metadata_fmt, [('metadata_file', 'in_file')]), (acompcor, acc_metadata_fmt, [('metadata_file', 'in_file')]), (tcc_metadata_fmt, mrg_conf_metadata, [('output', 'in1')]), (acc_metadata_fmt, mrg_conf_metadata, [('output', 'in2')]), (mrg_conf_metadata, mrg_conf_metadata2, [('out', 'in_dicts')]), # Expand the model with derivatives, quadratics, and spikes (concat, model_expand, [('confounds_file', 'confounds_file')]), (model_expand, spike_regress, [('confounds_file', 'confounds_file')]), # Set outputs (spike_regress, outputnode, [('confounds_file', 'confounds_file')]), (mrg_conf_metadata2, outputnode, [('out_dict', 'confounds_metadata')]), (tcompcor, outputnode, [("high_variance_masks", "tcompcor_mask")]), (acc_msk_bin, outputnode, [("out_file", "acompcor_masks")]), (inputnode, rois_plot, [('bold', 'in_file'), ('bold_mask', 'in_mask')]), (tcompcor, mrg_compcor, [('high_variance_masks', 'in1')]), (acc_msk_bin, mrg_compcor, [(('out_file', _last), 'in2')]), (mrg_compcor, rois_plot, [('out', 'in_rois')]), (rois_plot, ds_report_bold_rois, [('out_report', 'in_file')]), (tcompcor, mrg_cc_metadata, [('metadata_file', 'in1')]), (acompcor, mrg_cc_metadata, [('metadata_file', 'in2')]), (mrg_cc_metadata, compcor_plot, [('out', 'metadata_files')]), (compcor_plot, ds_report_compcor, [('out_file', 'in_file')]), (concat, conf_corr_plot, [('confounds_file', 'confounds_file'), (('confounds_file', _select_cols), 'columns') ]), (conf_corr_plot, ds_report_conf_corr, [('out_file', 'in_file')]), ]) return workflow
def init_bold_confs_wf( mem_gb, metadata, regressors_all_comps, regressors_dvars_th, regressors_fd_th, name="bold_confs_wf", ): """ Build a workflow to generate and write out confounding signals. 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``, ``white_matter``, ``global_signal``) #. DVARS - original and standardized variants (``dvars``, ``std_dvars``) #. Framewise displacement, based on head-motion parameters (``framewise_displacement``) #. Temporal CompCor (``t_comp_cor_XX``) #. Anatomical CompCor (``a_comp_cor_XX``) #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (``cosine_XX``) #. Non-steady-state volumes (``non_steady_state_XX``) #. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) 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 Graph .. workflow:: :graph2use: orig :simple_form: yes from fmriprep.workflows.bold.confounds import init_bold_confs_wf wf = init_bold_confs_wf( mem_gb=1, metadata={}, regressors_all_comps=False, regressors_dvars_th=1.5, regressors_fd_th=0.5, ) Parameters ---------- mem_gb : :obj:`float` Size of BOLD file in GB - please note that this size should be calculated after resamplings that may extend the FoV metadata : :obj:`dict` BIDS metadata for BOLD file name : :obj:`str` Name of workflow (default: ``bold_confs_wf``) regressors_all_comps : :obj:`bool` Indicates whether CompCor decompositions should return all components instead of the minimal number of components necessary to explain 50 percent of the variance in the decomposition mask. regressors_dvars_th : :obj:`float` Criterion for flagging DVARS outliers regressors_fd_th : :obj:`float` Criterion for flagging framewise displacement outliers 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 rmsd_file Framewise displacement as measured by ``fsl_motion_outliers``. skip_vols number of non steady state volumes t1w_mask Mask of the skull-stripped template image t1w_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 Outputs ------- confounds_file TSV of all aggregated confounds rois_report Reportlet visualizing white-matter/CSF mask used for aCompCor, the ROI for tCompCor and the BOLD brain mask. confounds_metadata Confounds metadata dictionary. """ from niworkflows.engine.workflows import LiterateWorkflow as Workflow from niworkflows.interfaces.confounds import ExpandModel, SpikeRegressors from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms from niworkflows.interfaces.images import SignalExtraction from niworkflows.interfaces.masks import ROIsPlot from niworkflows.interfaces.patches import ( RobustACompCor as ACompCor, RobustTCompCor as TCompCor, ) from niworkflows.interfaces.plotting import (CompCorVariancePlot, ConfoundsCorrelationPlot) from niworkflows.interfaces.utils import (TPM2ROI, AddTPMs, AddTSVHeader, TSV2JSON, DictMerge) workflow = Workflow(name=name) workflow.__desc__ = """\ Several confounding time-series were calculated based on the *preprocessed BOLD*: framewise displacement (FD), DVARS and three region-wise global signals. FD was computed using two formulations following Power (absolute sum of relative motions, @power_fd_dvars) and Jenkinson (relative root mean square displacement between affines, @mcflirt). FD and DVARS are calculated for each functional run, both using their implementations in *Nipype* [following the definitions by @power_fd_dvars]. The three global signals are extracted within the CSF, the WM, and the whole-brain masks. Additionally, a set of physiological regressors were extracted to allow for component-based noise correction [*CompCor*, @compcor]. Principal components are estimated after high-pass filtering the *preprocessed BOLD* time-series (using a discrete cosine filter with 128s cut-off) for the two *CompCor* variants: temporal (tCompCor) and anatomical (aCompCor). tCompCor components are then calculated from the top 5% variable voxels within a mask covering the subcortical regions. This subcortical mask is obtained by heavily eroding the brain mask, which ensures it does not include cortical GM regions. For aCompCor, components are calculated within the intersection of the aforementioned mask and the union of CSF and WM masks calculated in T1w space, after their projection to the native space of each functional run (using the inverse BOLD-to-T1w transformation). Components are also calculated separately within the WM and CSF masks. For each CompCor decomposition, the *k* components with the largest singular values are retained, such that the retained components' time series are sufficient to explain 50 percent of variance across the nuisance mask (CSF, WM, combined, or temporal). The remaining components are dropped from consideration. The head-motion estimates calculated in the correction step were also placed within the corresponding confounds file. The confound time series derived from head motion estimates and global signals were expanded with the inclusion of temporal derivatives and quadratic terms for each [@confounds_satterthwaite_2013]. Frames that exceeded a threshold of {fd} mm FD or {dv} standardised DVARS were annotated as motion outliers. """.format(fd=regressors_fd_th, dv=regressors_dvars_th) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold', 'bold_mask', 'movpar_file', 'rmsd_file', 'skip_vols', 't1w_mask', 't1w_tpms', 't1_bold_xform' ]), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['confounds_file', 'confounds_metadata']), name='outputnode') # Get masks ready in T1w space acc_tpm = pe.Node( AddTPMs(indices=[1, 2]), # BIDS convention (WM=1, CSF=2) name='acc_tpm') # 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_nstd=True, save_std=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 mrg_lbl_cc = pe.Node(niu.Merge(3), name='merge_rois_cc', run_without_submitting=True) tcompcor = pe.Node(TCompCor(components_file='tcompcor.tsv', header_prefix='t_comp_cor_', pre_filter='cosine', save_pre_filter=True, save_metadata=True, percentile_threshold=.05, failure_mode='NaN'), name="tcompcor", mem_gb=mem_gb) acompcor = pe.Node(ACompCor(components_file='acompcor.tsv', header_prefix='a_comp_cor_', pre_filter='cosine', save_pre_filter=True, save_metadata=True, mask_names=['combined', 'CSF', 'WM'], merge_method='none', failure_mode='NaN'), name="acompcor", mem_gb=mem_gb) # Set number of components if regressors_all_comps: acompcor.inputs.num_components = 'all' tcompcor.inputs.num_components = 'all' else: acompcor.inputs.variance_threshold = 0.5 tcompcor.inputs.variance_threshold = 0.5 # Set TR if present if 'RepetitionTime' in metadata: tcompcor.inputs.repetition_time = metadata['RepetitionTime'] acompcor.inputs.repetition_time = metadata['RepetitionTime'] # Global and segment regressors signals_class_labels = ["csf", "white_matter", "global_signal"] mrg_lbl = pe.Node(niu.Merge(3), name='merge_rois', run_without_submitting=True) signals = pe.Node(SignalExtraction(class_labels=signals_class_labels), name="signals", mem_gb=mem_gb) # Arrange confounds add_dvars_header = pe.Node(AddTSVHeader(columns=["dvars"]), name="add_dvars_header", mem_gb=0.01, run_without_submitting=True) add_std_dvars_header = pe.Node(AddTSVHeader(columns=["std_dvars"]), name="add_std_dvars_header", mem_gb=0.01, run_without_submitting=True) add_motion_headers = pe.Node(AddTSVHeader( columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", mem_gb=0.01, run_without_submitting=True) add_rmsd_header = pe.Node(AddTSVHeader(columns=["rmsd"]), name="add_rmsd_header", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # CompCor metadata tcc_metadata_fmt = pe.Node(TSV2JSON( index_column='component', drop_columns=['mask'], output=None, additional_metadata={'Method': 'tCompCor'}, enforce_case=True), name='tcc_metadata_fmt') acc_metadata_fmt = pe.Node(TSV2JSON( index_column='component', output=None, additional_metadata={'Method': 'aCompCor'}, enforce_case=True), name='acc_metadata_fmt') mrg_conf_metadata = pe.Node(niu.Merge(3), name='merge_confound_metadata', run_without_submitting=True) mrg_conf_metadata.inputs.in3 = { label: { 'Method': 'Mean' } for label in signals_class_labels } mrg_conf_metadata2 = pe.Node(DictMerge(), name='merge_confound_metadata2', run_without_submitting=True) # Expand model to include derivatives and quadratics model_expand = pe.Node( ExpandModel(model_formula='(dd1(rps + wm + csf + gsr))^^2 + others'), name='model_expansion') # Add spike regressors spike_regress = pe.Node(SpikeRegressors(fd_thresh=regressors_fd_th, dvars_thresh=regressors_dvars_th), name='spike_regressors') # Generate reportlet (ROIs) mrg_compcor = pe.Node(niu.Merge(2), name='merge_compcor', run_without_submitting=True) rois_plot = pe.Node(ROIsPlot(colors=['b', 'magenta'], generate_report=True), name='rois_plot', mem_gb=mem_gb) ds_report_bold_rois = pe.Node(DerivativesDataSink( desc='rois', datatype="figures", dismiss_entities=("echo", )), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Generate reportlet (CompCor) mrg_cc_metadata = pe.Node(niu.Merge(2), name='merge_compcor_metadata', run_without_submitting=True) compcor_plot = pe.Node(CompCorVariancePlot( variance_thresholds=(0.5, 0.7, 0.9), metadata_sources=['tCompCor', 'aCompCor']), name='compcor_plot') ds_report_compcor = pe.Node(DerivativesDataSink( desc='compcorvar', datatype="figures", dismiss_entities=("echo", )), name='ds_report_compcor', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Generate reportlet (Confound correlation) conf_corr_plot = pe.Node(ConfoundsCorrelationPlot( reference_column='global_signal', max_dim=70), name='conf_corr_plot') ds_report_conf_corr = pe.Node(DerivativesDataSink( desc='confoundcorr', datatype="figures", dismiss_entities=("echo", )), name='ds_report_conf_corr', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) def _pick_csf(files): return files[2] # after smriprep#189, this is BIDS-compliant. def _pick_wm(files): return files[1] # after smriprep#189, this is BIDS-compliant. workflow.connect([ # Massage ROIs (in T1w space) (inputnode, acc_tpm, [('t1w_tpms', 'in_files')]), (inputnode, csf_roi, [(('t1w_tpms', _pick_csf), 'in_tpm'), ('t1w_mask', 'in_mask')]), (inputnode, wm_roi, [(('t1w_tpms', _pick_wm), 'in_tpm'), ('t1w_mask', 'in_mask')]), (inputnode, acc_roi, [('t1w_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')]), # tCompCor (inputnode, tcompcor, [('bold', 'realigned_file')]), (inputnode, tcompcor, [('skip_vols', 'ignore_initial_volumes')]), (tcc_tfm, tcc_msk, [('output_image', 'roi_file')]), (tcc_msk, tcompcor, [('out', 'mask_files')]), # aCompCor (inputnode, acompcor, [('bold', 'realigned_file')]), (inputnode, acompcor, [('skip_vols', 'ignore_initial_volumes')]), (acc_tfm, acc_msk, [('output_image', 'roi_file')]), (acc_msk, mrg_lbl_cc, [('out', 'in1')]), (csf_msk, mrg_lbl_cc, [('out', 'in2')]), (wm_msk, mrg_lbl_cc, [('out', 'in3')]), (mrg_lbl_cc, 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_motion_headers, [('movpar_file', 'in_file')]), (inputnode, add_rmsd_header, [('rmsd_file', 'in_file')]), (dvars, add_dvars_header, [('out_nstd', 'in_file')]), (dvars, add_std_dvars_header, [('out_std', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_motion_headers, concat, [('out_file', 'motion')]), (add_rmsd_header, concat, [('out_file', 'rmsd')]), (add_dvars_header, concat, [('out_file', 'dvars')]), (add_std_dvars_header, concat, [('out_file', 'std_dvars')]), # Confounds metadata (tcompcor, tcc_metadata_fmt, [('metadata_file', 'in_file')]), (acompcor, acc_metadata_fmt, [('metadata_file', 'in_file')]), (tcc_metadata_fmt, mrg_conf_metadata, [('output', 'in1')]), (acc_metadata_fmt, mrg_conf_metadata, [('output', 'in2')]), (mrg_conf_metadata, mrg_conf_metadata2, [('out', 'in_dicts')]), # Expand the model with derivatives, quadratics, and spikes (concat, model_expand, [('confounds_file', 'confounds_file')]), (model_expand, spike_regress, [('confounds_file', 'confounds_file')]), # Set outputs (spike_regress, outputnode, [('confounds_file', 'confounds_file')]), (mrg_conf_metadata2, outputnode, [('out_dict', 'confounds_metadata')]), (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, ds_report_bold_rois, [('out_report', 'in_file')]), (tcompcor, mrg_cc_metadata, [('metadata_file', 'in1')]), (acompcor, mrg_cc_metadata, [('metadata_file', 'in2')]), (mrg_cc_metadata, compcor_plot, [('out', 'metadata_files')]), (compcor_plot, ds_report_compcor, [('out_file', 'in_file')]), (concat, conf_corr_plot, [('confounds_file', 'confounds_file')]), (conf_corr_plot, ds_report_conf_corr, [('out_file', 'in_file')]), ]) return workflow
def init_discover_wf(bold_file_size_gb, 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. Saves the confounds in a file ('outputnode.confounds_file')''' inputnode = pe.Node(utility.IdentityInterface( fields=['fmri_file', 'movpar_file', 't1_tpms', 'epi_mask']), name='inputnode') outputnode = pe.Node(utility.IdentityInterface( fields=['confounds_file', 'acompcor_report', 'tcompcor_report']), name='outputnode') # 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')]), ]) return workflow
def init_bold_confs_wf( mem_gb, metadata, #regressors_all_comps, #regressors_dvars_th, #regressors_fd_th, name="bold_confs_wf", ): """ Build a workflow to generate and write out confounding signals. 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``, ``white_matter``, ``global_signal``) #. DVARS - original and standardized variants (``dvars``, ``std_dvars``) #. Framewise displacement, based on head-motion parameters (``framewise_displacement``) #. Temporal CompCor (``t_comp_cor_XX``) #. Anatomical CompCor (``a_comp_cor_XX``) #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (``cosine_XX``) #. Non-steady-state volumes (``non_steady_state_XX``) #. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) 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 Graph .. workflow:: :graph2use: orig :simple_form: yes from aslprep.workflows.bold.confounds import init_bold_confs_wf wf = init_bold_confs_wf( mem_gb=1, metadata={}, regressors_all_comps=False, regressors_dvars_th=1.5, regressors_fd_th=0.5, ) Parameters ---------- mem_gb : :obj:`float` Size of BOLD file in GB - please note that this size should be calculated after resamplings that may extend the FoV metadata : :obj:`dict` BIDS metadata for BOLD file name : :obj:`str` Name of workflow (default: ``bold_confs_wf``) regressors_all_comps : :obj:`bool` Indicates whether CompCor decompositions should return all components instead of the minimal number of components necessary to explain 50 percent of the variance in the decomposition mask. regressors_dvars_th : :obj:`float` Criterion for flagging DVARS outliers regressors_fd_th : :obj:`float` Criterion for flagging framewise displacement outliers 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 skip_vols number of non steady state volumes t1w_mask Mask of the skull-stripped template image t1w_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 Outputs ------- confounds_file TSV of all aggregated confounds rois_report Reportlet visualizing white-matter/CSF mask used for aCompCor, the ROI for tCompCor and the BOLD brain mask. confounds_metadata Confounds metadata dictionary. """ workflow = Workflow(name=name) workflow.__desc__ = """\ Several confounding time-series were calculated based on the *preprocessed ASL*: framewise displacement (FD) and DVARS. FD and DVARS are calculated for each ASL run, both using their implementations in *Nipype* [following the definitions by @power_fd_dvars]. The head-motion estimates calculated in the correction step were also placed within the corresponding confounds file. """ inputnode = pe.Node(niu.IdentityInterface( fields=['bold', 'bold_mask', 'movpar_file', 'skip_vols', 't1w_mask', 't1w_tpms', 't1_bold_xform']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['confounds_file', 'confounds_metadata']), name='outputnode') # DVARS dvars = pe.Node(nac.ComputeDVARS(save_nstd=True, save_std=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) # Global and segment regressors #signals_class_labels = ["csf", "white_matter", "global_signal"] # Arrange confounds add_dvars_header = pe.Node( AddTSVHeader(columns=["dvars"]), name="add_dvars_header", mem_gb=0.01, run_without_submitting=True) add_std_dvars_header = pe.Node( AddTSVHeader(columns=["std_dvars"]), name="add_std_dvars_header", mem_gb=0.01, run_without_submitting=True) add_motion_headers = pe.Node( AddTSVHeader(columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) rois_plot = pe.Node(ROIsPlot(colors=['b', 'magenta'], generate_report=True), name='rois_plot', mem_gb=mem_gb) ds_report_bold_rois = pe.Node( DerivativesDataSink(desc='rois', keep_dtype=True), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Expand model to include derivatives and quadratics workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('bold', 'in_file'), ('bold_mask', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), # Collate computed confounds together (inputnode, add_motion_headers, [('movpar_file', 'in_file')]), (dvars, add_dvars_header, [('out_nstd', 'in_file')]), (dvars, add_std_dvars_header, [('out_std', 'in_file')]), (fdisp, concat, [('out_file', 'fd')]), (add_motion_headers, concat, [('out_file', 'motion')]), (add_dvars_header, concat, [('out_file', 'dvars')]), (add_std_dvars_header, concat, [('out_file', 'std_dvars')]), # Expand the model with derivatives, quadratics, and spikes # Set outputs (concat, outputnode, [('confounds_file', 'confounds_file')]), ]) return workflow
def fmri_qc_workflow(name='fMRIQC', settings=None): """ The fMRI qc workflow """ if settings is None: settings = {} workflow = pe.Workflow(name=name) deriv_dir = op.abspath(op.join(settings['output_dir'], 'derivatives')) if not op.exists(deriv_dir): os.makedirs(deriv_dir) # Read FD radius, or default it fd_radius = settings.get('fd_radius', 50.) # Define workflow, inputs and outputs inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bids_dir', 'subject_id', 'session_id', 'run_id', 'site_name', 'start_idx', 'stop_idx' ]), name='inputnode') get_idx = pe.Node(niu.Function( input_names=['in_file', 'start_idx', 'stop_idx'], function=fmri_getidx, output_names=['start_idx', 'stop_idx']), name='get_idx') outputnode = pe.Node(niu.IdentityInterface(fields=[ 'qc', 'mosaic', 'out_group', 'out_movpar', 'out_dvars', 'out_fd' ]), name='outputnode') # 0. Get data datasource = pe.Node(niu.Function(input_names=[ 'bids_dir', 'data_type', 'subject_id', 'session_id', 'run_id' ], output_names=['out_file'], function=bids_getfile), name='datasource') datasource.inputs.data_type = 'func' # Workflow -------------------------------------------------------- # 1. HMC: head motion correct hmcwf = hmc_mcflirt() if settings.get('hmc_afni', False): hmcwf = hmc_afni( st_correct=settings.get('correct_slice_timing', False)) hmcwf.inputs.inputnode.fd_radius = fd_radius mean = pe.Node( afp.TStat( # 2. Compute mean fmri options='-mean', outputtype='NIFTI_GZ'), name='mean') bmw = fmri_bmsk_workflow( # 3. Compute brain mask use_bet=settings.get('use_bet', False)) # Compute TSNR using nipype implementation tsnr = pe.Node(nac.TSNR(), name='compute_tsnr') # Compute DVARS dvnode = pe.Node(nac.ComputeDVARS(remove_zerovariance=True, save_plot=True, save_all=True, figdpi=200, figformat='pdf'), name='ComputeDVARS') fdnode = pe.Node(nac.FramewiseDisplacement(normalize=True, save_plot=True, radius=fd_radius, figdpi=200), name='ComputeFD') # AFNI quality measures fwhm = pe.Node(afp.FWHMx(combine=True, detrend=True), name='smoothness') # fwhm.inputs.acf = True # add when AFNI >= 16 outliers = pe.Node(afp.OutlierCount(fraction=True, out_file='ouliers.out'), name='outliers') quality = pe.Node(afp.QualityIndex(automask=True), out_file='quality.out', name='quality') measures = pe.Node(FunctionalQC(), name='measures') # Link images that should be reported dsreport = pe.Node(nio.DataSink(base_directory=settings['report_dir'], parameterization=True), name='dsreport') dsreport.inputs.container = 'func' dsreport.inputs.substitutions = [ ('_data', ''), ('fd_power_2012', 'plot_fd'), ('tsnr.nii.gz', 'mosaic_TSNR.nii.gz'), ('mean.nii.gz', 'mosaic_TSNR_mean.nii.gz'), ('stdev.nii.gz', 'mosaic_TSNR_stdev.nii.gz') ] dsreport.inputs.regexp_substitutions = [ ('_u?(sub-[\\w\\d]*)\\.([\\w\\d_]*)(?:\\.([\\w\\d_-]*))+', '\\1_ses-\\2_\\3'), ('sub-[^/.]*_dvars_std', 'plot_dvars'), ('sub-[^/.]*_mask', 'mask'), ('sub-[^/.]*_mcf_tstat', 'mosaic_epi_mean') ] workflow.connect([ (inputnode, datasource, [('bids_dir', 'bids_dir'), ('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (inputnode, get_idx, [('start_idx', 'start_idx'), ('stop_idx', 'stop_idx')]), (datasource, get_idx, [('out_file', 'in_file')]), (datasource, hmcwf, [('out_file', 'inputnode.in_file')]), (get_idx, hmcwf, [('start_idx', 'inputnode.start_idx'), ('stop_idx', 'inputnode.stop_idx')]), (hmcwf, bmw, [('outputnode.out_file', 'inputnode.in_file')]), (hmcwf, mean, [('outputnode.out_file', 'in_file')]), (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]), (hmcwf, fdnode, [('outputnode.out_movpar', 'in_plots')]), (mean, fwhm, [('out_file', 'in_file')]), (bmw, fwhm, [('outputnode.out_file', 'mask')]), (hmcwf, outliers, [('outputnode.out_file', 'in_file')]), (bmw, outliers, [('outputnode.out_file', 'mask')]), (hmcwf, quality, [('outputnode.out_file', 'in_file')]), (hmcwf, dvnode, [('outputnode.out_file', 'in_file')]), (bmw, dvnode, [('outputnode.out_file', 'in_mask')]), (mean, measures, [('out_file', 'in_epi')]), (hmcwf, measures, [('outputnode.out_file', 'in_hmc')]), (bmw, measures, [('outputnode.out_file', 'in_mask')]), (tsnr, measures, [('tsnr_file', 'in_tsnr')]), (dvnode, measures, [('out_all', 'in_dvars')]), (fdnode, measures, [('out_file', 'in_fd')]), (fdnode, outputnode, [('out_file', 'out_fd')]), (dvnode, outputnode, [('out_all', 'out_dvars')]), (hmcwf, outputnode, [('outputnode.out_movpar', 'out_movpar')]), (mean, dsreport, [('out_file', '@meanepi')]), (tsnr, dsreport, [('tsnr_file', '@tsnr'), ('stddev_file', '@tsnr_std'), ('mean_file', '@tsnr_mean')]), (bmw, dsreport, [('outputnode.out_file', '@mask')]), (fdnode, dsreport, [('out_figure', '@fdplot')]), (dvnode, dsreport, [('fig_std', '@dvars')]), ]) # Format name out_name = pe.Node(niu.Function( input_names=['subid', 'sesid', 'runid', 'prefix', 'out_path'], output_names=['out_file'], function=bids_path), name='FormatName') out_name.inputs.out_path = deriv_dir out_name.inputs.prefix = 'func' # Save to JSON file datasink = pe.Node(nio.JSONFileSink(), name='datasink') datasink.inputs.qc_type = 'func' workflow.connect([ (inputnode, out_name, [('subject_id', 'subid'), ('session_id', 'sesid'), ('run_id', 'runid')]), (inputnode, datasink, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (fwhm, datasink, [(('fwhm', fwhm_dict), 'fwhm')]), (outliers, datasink, [(('out_file', _parse_tout), 'outlier')]), (quality, datasink, [(('out_file', _parse_tqual), 'quality')]), (measures, datasink, [('summary', 'summary'), ('spacing', 'spacing'), ('size', 'size'), ('fber', 'fber'), ('efc', 'efc'), ('snr', 'snr'), ('gsr', 'gsr'), ('m_tsnr', 'm_tsnr'), ('fd', 'fd'), ('dvars', 'dvars'), ('gcor', 'gcor')]), (out_name, datasink, [('out_file', 'out_file')]), (datasink, outputnode, [('out_file', 'out_file')]) ]) return workflow
def mc_workflow_afni(reference_vol="mid", FD_mode="Power", SinkTag="func_preproc", wf_name="motion_correction_afni"): from nipype.interfaces.afni import preprocess import sys import os import nipype import nipype.pipeline as pe import nipype.interfaces.utility as utility import PUMI.func_preproc.info.info_get as info_get import nipype.interfaces.io as io import nipype.algorithms.confounds as conf import PUMI.utils.utils_math as utils_math import PUMI.utils.utils_convert as utils_convert import PUMI.utils.globals as globals import PUMI.utils.QC as qc SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag) if not os.path.exists(SinkDir): os.makedirs(SinkDir) QCDir = os.path.abspath(globals._SinkDir_ + "/" + globals._QCDir_) if not os.path.exists(QCDir): os.makedirs(QCDir) # Basic interface class generates identity mappings inputspec = pe.Node(utility.IdentityInterface( fields=['func', 'ref_vol', 'save_plots', 'stats_imgs']), name='inputspec') inputspec.inputs.save_plots = True inputspec.inputs.stats_imgs = True inputspec.inputs.ref_vol = reference_vol # extract reference volume refvol = pe.MapNode(utility.Function(input_names=['refvol', 'func'], output_names=['refvol'], function=getRefVol), iterfield=['func'], name='getRefVol') if (reference_vol == "mean"): func_motion_correct1 = pe.MapNode(interface=preprocess.Volreg(), iterfield=["in_file", "basefile"], name='mc_afni_init') func_motion_correct1.inputs.args = '-Fourier -twopass' func_motion_correct1.inputs.zpad = 4 func_motion_correct1.inputs.outputtype = 'NIFTI_GZ' # extract reference volume refvol2 = pe.MapNode(utility.Function(input_names=['refvol', 'func'], output_names=['refvol'], function=getRefVol), iterfield=['func'], name='getRefVol2') func_motion_correct = pe.MapNode(interface=preprocess.Volreg(), iterfield=["in_file", "basefile"], name='mc_afni') func_motion_correct.inputs.args = '-Fourier -twopass' func_motion_correct.inputs.zpad = 4 func_motion_correct.inputs.outputtype = 'NIFTI_GZ' myqc = qc.timecourse2png("timeseries", tag="010_motioncorr") # Calculate Friston24 parameters calc_friston = pe.MapNode(utility.Function( input_names=['in_file'], output_names=['out_file'], function=calc_friston_twenty_four), iterfield=['in_file'], name='calc_friston') if FD_mode == "Power": calculate_FD = pe.MapNode(conf.FramewiseDisplacement( parameter_source='AFNI', save_plot=True), iterfield=['in_file'], name='calculate_FD_Power') elif FD_mode == "Jenkinson": calculate_FD = pe.MapNode(utility.Function(input_names=['in_file'], output_names=['out_file'], function=calculate_FD_J), iterfield=['in_file'], name='calculate_FD_Jenkinson') # compute mean and max FD meanFD = pe.MapNode(interface=utils_math.Txt2meanTxt, iterfield=['in_file'], name='meanFD') meanFD.inputs.axis = 0 # global mean meanFD.inputs.header = True # global mean maxFD = pe.MapNode(interface=utils_math.Txt2maxTxt, iterfield=['in_file'], name='maxFD') maxFD.inputs.axis = 0 # global mean maxFD.inputs.header = True # global mean pop_FD = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FD') pop_FDmax = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FDmax') # save data out with Datasink ds_fd = pe.Node(interface=io.DataSink(), name='ds_pop_fd') ds_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD.txt")] ds_fd.inputs.base_directory = SinkDir # save data out with Datasink ds_fd_max = pe.Node(interface=io.DataSink(), name='ds_pop_fd_max') ds_fd_max.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD_max.txt")] ds_fd_max.inputs.base_directory = SinkDir # Save outputs which are important ds_qc_fd = pe.Node(interface=io.DataSink(), name='ds_qc_fd') ds_qc_fd.inputs.base_directory = QCDir ds_qc_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_FD.pdf")] # Basic interface class generates identity mappings outputspec = pe.Node(utility.IdentityInterface(fields=[ 'func_out_file', 'first24_file', 'mat_file', 'mc_par_file', 'FD_file' ]), name='outputspec') # save data out with Datasink ds_nii = pe.Node(interface=io.DataSink(), name='ds_nii') ds_nii.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".nii.gz")] ds_nii.inputs.base_directory = SinkDir # save data out with Datasink ds_text = pe.Node(interface=io.DataSink(), name='ds_txt') ds_text.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".txt")] ds_text.inputs.base_directory = SinkDir # TODO_ready set the proper images which has to be saved in a the datasink specified directory # Create a workflow to connect all those nodes analysisflow = nipype.Workflow(wf_name) analysisflow.connect(inputspec, 'func', refvol, 'func') analysisflow.connect(inputspec, 'ref_vol', refvol, 'refvol') if (reference_vol == "mean"): analysisflow.connect(inputspec, 'func', func_motion_correct1, 'in_file') analysisflow.connect(refvol, 'refvol', func_motion_correct1, 'basefile') analysisflow.connect(func_motion_correct1, 'out_file', refvol2, 'func') analysisflow.connect(inputspec, 'ref_vol', refvol2, 'refvol') analysisflow.connect(inputspec, 'func', func_motion_correct, 'in_file') analysisflow.connect(refvol2, 'refvol', func_motion_correct, 'basefile') else: analysisflow.connect(inputspec, 'func', func_motion_correct, 'in_file') analysisflow.connect(refvol, 'refvol', func_motion_correct, 'basefile') analysisflow.connect(func_motion_correct, 'oned_file', calc_friston, 'in_file') analysisflow.connect(func_motion_correct, 'oned_file', calculate_FD, 'in_file') analysisflow.connect(func_motion_correct, 'out_file', outputspec, 'func_out_file') analysisflow.connect(func_motion_correct, 'oned_matrix_save', outputspec, 'mat_file') analysisflow.connect(func_motion_correct, 'oned_file', outputspec, 'mc_par_file') analysisflow.connect(func_motion_correct, 'out_file', ds_nii, 'mc_func') analysisflow.connect(func_motion_correct, 'oned_file', ds_text, 'mc_par') # analysisflow.connect(func_motion_correct, 'variance_img', ds, 'mc.@variance_img') analysisflow.connect(calc_friston, 'out_file', outputspec, 'first24_file') analysisflow.connect(calc_friston, 'out_file', ds_text, 'mc_first24') analysisflow.connect(calculate_FD, 'out_file', outputspec, 'FD_file') analysisflow.connect(func_motion_correct, 'out_file', myqc, 'inputspec.func') # pop-level mean FD analysisflow.connect(calculate_FD, 'out_file', meanFD, 'in_file') analysisflow.connect(calculate_FD, 'out_file', ds_text, 'mc_fd') analysisflow.connect(meanFD, 'mean_file', pop_FD, 'in_list') analysisflow.connect(pop_FD, 'txt_file', ds_fd, 'pop') analysisflow.connect(calculate_FD, 'out_figure', ds_qc_fd, 'FD') analysisflow.connect(calculate_FD, 'out_file', maxFD, 'in_file') analysisflow.connect(maxFD, 'max_file', pop_FDmax, 'in_list') analysisflow.connect(pop_FDmax, 'txt_file', ds_fd_max, 'pop') return analysisflow
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, 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``) 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, 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 metadata : dict BIDS metadata for BOLD file name : str Name of workflow (default: ``bold_confs_wf``) **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 **Outputs** confounds_file TSV of all aggregated confounds rois_report Reportlet visualizing white-matter/CSF mask used for aCompCor, the ROI for tCompCor and the BOLD brain mask. """ inputnode = pe.Node(niu.IdentityInterface( fields=['bold', 'bold_mask', 'movpar_file', 't1_mask', 't1_tpms', 't1_bold_xform']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['confounds_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(TCompCor( components_file='tcompcor.tsv', pre_filter='cosine', save_pre_filter=True, percentile_threshold=.05), name="tcompcor", mem_gb=mem_gb) acompcor = pe.Node(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(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(colors=['r', 'b', 'magenta'], generate_report=True), name='rois_plot') ds_report_bold_rois = pe.Node( DerivativesDataSink(suffix='rois'), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) 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')]), (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, ds_report_bold_rois, [('out_report', 'in_file')]), ]) return workflow
def hmc_afni(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() """ 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')]), ]) get_mean_RPI = pe.Node(afni.TStat(options='-mean', outputtype='NIFTI_GZ'), name='get_mean_RPI') # calculate hmc parameters hmc = pe.Node(afni.Volreg(args='-Fourier -twopass', zpad=4, outputtype='NIFTI_GZ'), name='motion_correct') get_mean_motion = get_mean_RPI.clone('get_mean_motion') hmc_A = hmc.clone('motion_correct_A') hmc_A.inputs.md1d_file = 'max_displacement.1D' # Compute the frame-wise displacement fdnode = pe.Node(nac.FramewiseDisplacement(normalize=False, parameter_source="AFNI"), name='ComputeFD') workflow.connect([ (inputnode, fdnode, [('fd_radius', 'radius')]), (get_mean_RPI, hmc, [('out_file', 'basefile')]), (hmc, get_mean_motion, [('out_file', 'in_file')]), (get_mean_motion, hmc_A, [('out_file', 'basefile')]), (hmc_A, outputnode, [('out_file', 'out_file')]), (hmc_A, 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, get_mean_RPI, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), (deoblique_node, hmc_A, [('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, get_mean_RPI, [('out_file', 'in_file')]), (despike_node, hmc, [('out_file', 'in_file')]), (despike_node, hmc_A, [('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, get_mean_RPI, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), (deoblique_node, hmc_A, [('out_file', 'in_file')]), ]) elif st_correct: workflow.connect([ (drop_trs, st_corr, [('out_file', 'in_file')]), (st_corr, get_mean_RPI, [('out_file', 'in_file')]), (st_corr, hmc, [('out_file', 'in_file')]), (st_corr, hmc_A, [('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, get_mean_RPI, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), (deoblique_node, hmc_A, [('out_file', 'in_file')]), ]) elif despike: workflow.connect([ (drop_trs, despike_node, [('out_file', 'in_file')]), (despike_node, get_mean_RPI, [('out_file', 'in_file')]), (despike_node, hmc, [('out_file', 'in_file')]), (despike_node, hmc_A, [('out_file', 'in_file')]), ]) elif deoblique: workflow.connect([ (drop_trs, deoblique_node, [('out_file', 'in_file')]), (deoblique_node, get_mean_RPI, [('out_file', 'in_file')]), (deoblique_node, hmc, [('out_file', 'in_file')]), (deoblique_node, hmc_A, [('out_file', 'in_file')]), ]) else: workflow.connect([ (drop_trs, get_mean_RPI, [('out_file', 'in_file')]), (drop_trs, hmc, [('out_file', 'in_file')]), (drop_trs, hmc_A, [('out_file', 'in_file')]), ]) return workflow
def init_bold_confs_wf( out_dir, out_path_base, source_file, mem_gb, regressors_all_comps, regressors_dvars_th, regressors_fd_th, dt=None, work_dir=None, 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``, ``white_matter``, ``global_signal``) #. DVARS - original and standardized variants (``dvars``, ``std_dvars``) #. Framewise displacement, based on head-motion parameters (``framewise_displacement``) #. Temporal CompCor (``t_comp_cor_XX``) #. Anatomical CompCor (``a_comp_cor_XX``) #. Cosine basis set for high-pass filtering w/ 0.008 Hz cut-off (``cosine_XX``) #. Non-steady-state volumes (``non_steady_state_XX``) #. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) 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, regressors_all_comps=False, regressors_dvars_th=1.5, regressors_fd_th=0.5, dt=2.0, ) **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 regressors_all_comps: bool Indicates whether CompCor decompositions should return all components instead of the minimal number of components necessary to explain 50 percent of the variance in the decomposition mask. regressors_dvars_th Criterion for flagging DVARS outliers regressors_fd_th Criterion for flagging framewise displacement outliers dt: float repetition time name : str Name of workflow (default: ``bold_confs_wf``) **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 skip_vols number of non steady state volumes csf_mask csk mask in MNI 2mm space wm_mask wm mask in MNI 2mm space cortical_gm_mask gm mask in MNI 2mm space **Outputs** confounds_file TSV of all aggregated confounds confounds_metadata Confounds metadata dictionary. """ DerivativesDataSink.out_path_base = out_path_base workflow = Workflow(name=name, base_dir=work_dir) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bold', 'bold_mask', 'movpar_file', 'skip_vols', 'csf_mask', 'wm_mask', 'cortical_gm_mask' ]), name='inputnode') outputnode = pe.Node( niu.IdentityInterface(fields=['confounds_file', 'confounds_metadata']), name='outputnode') # create tcc mask: fslmaths cortical_gm_mask -dilD -mul -1 -add bold_mask -bin tcc_roi = pe.Node(fsl.utils.ImageMaths(op_string='-dilD -mul -1 -add', args='-bin'), name='tcc_roi') # create acc mask fslmaths wm_mask -add csf_mask acc_roi = pe.Node(fsl.utils.ImageMaths(op_string='-add'), name='acc_roi') # 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_nstd=True, save_std=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 mrg_lbl_cc = pe.Node(niu.Merge(3), name='merge_rois_cc', run_without_submitting=True) tcompcor = pe.Node(TCompCor(components_file='tcompcor.tsv', header_prefix='t_comp_cor_', pre_filter='cosine', save_pre_filter=True, save_metadata=True, percentile_threshold=.05, failure_mode='NaN'), name="tcompcor", mem_gb=mem_gb) acompcor = pe.Node(ACompCor(components_file='acompcor.tsv', header_prefix='a_comp_cor_', pre_filter='cosine', save_pre_filter=True, save_metadata=True, mask_names=['combined', 'CSF', 'WM'], merge_method='none', failure_mode='NaN'), name="acompcor", mem_gb=mem_gb) # Set number of components if regressors_all_comps: acompcor.inputs.num_components = 'all' tcompcor.inputs.num_components = 'all' else: acompcor.inputs.variance_threshold = 0.5 tcompcor.inputs.variance_threshold = 0.5 # Set TR if present if dt: tcompcor.inputs.repetition_time = dt acompcor.inputs.repetition_time = dt # Global and segment regressors mrg_lbl = pe.Node(niu.Merge(3), name='merge_rois', run_without_submitting=True) signals = pe.Node(SignalExtraction( class_labels=["csf", "white_matter", "global_signal"]), name="signals", mem_gb=mem_gb) # Arrange confounds add_dvars_header = pe.Node(AddTSVHeader(columns=["dvars"]), name="add_dvars_header", mem_gb=0.01, run_without_submitting=True) add_std_dvars_header = pe.Node(AddTSVHeader(columns=["std_dvars"]), name="add_std_dvars_header", mem_gb=0.01, run_without_submitting=True) add_motion_headers = pe.Node(AddTSVHeader( columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # CompCor metadata tcc_metadata_fmt = pe.Node(TSV2JSON( index_column='component', drop_columns=['mask'], output=None, additional_metadata={'Method': 'tCompCor'}, enforce_case=True), name='tcc_metadata_fmt') acc_metadata_fmt = pe.Node(TSV2JSON( index_column='component', output=None, additional_metadata={'Method': 'aCompCor'}, enforce_case=True), name='acc_metadata_fmt') mrg_conf_metadata = pe.Node(niu.Merge(2), name='merge_confound_metadata', run_without_submitting=True) mrg_conf_metadata2 = pe.Node(DictMerge(), name='merge_confound_metadata2', run_without_submitting=True) # Expand model to include derivatives and quadratics model_expand = pe.Node( ExpandModel(model_formula='(dd1(rps + wm + csf + gsr))^^2 + others'), name='model_expansion') # Add spike regressors spike_regress = pe.Node(SpikeRegressors(fd_thresh=regressors_fd_th, dvars_thresh=regressors_dvars_th), name='spike_regressors') # Generate reportlet (ROIs) mrg_compcor = pe.Node(niu.Merge(2), name='merge_compcor', run_without_submitting=True) rois_plot = pe.Node(ROIsPlot(colors=['b', 'magenta'], generate_report=True), name='rois_plot', mem_gb=mem_gb) ds_report_bold_rois = pe.Node(DerivativesDataSink(base_directory=out_dir, desc='rois', source_file=source_file, suffix='reportlet', keep_dtype=True), name='ds_report_bold_rois', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Generate reportlet (CompCor) mrg_cc_metadata = pe.Node(niu.Merge(2), name='merge_compcor_metadata', run_without_submitting=True) compcor_plot = pe.Node( CompCorVariancePlot(metadata_sources=['tCompCor', 'aCompCor']), name='compcor_plot') ds_report_compcor = pe.Node(DerivativesDataSink(base_directory=out_dir, desc='compcorvar', source_file=source_file, keep_dtype=True), name='ds_report_compcor', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) # Generate reportlet (Confound correlation) conf_corr_plot = pe.Node(ConfoundsCorrelationPlot( reference_column='global_signal', max_dim=70), name='conf_corr_plot') ds_report_conf_corr = pe.Node(DerivativesDataSink(base_directory=out_dir, desc='confoundcorr', source_file=source_file, keep_dtype=True), name='ds_report_conf_corr', run_without_submitting=True, mem_gb=DEFAULT_MEMORY_MIN_GB) workflow.connect([ # generate tcc and acc rois (inputnode, tcc_roi, [('cortical_gm_mask', 'in_file'), ('bold_mask', 'in_file2')]), (inputnode, acc_roi, [('wm_mask', 'in_file'), ('csf_mask', 'in_file2')]), # 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')]), # tCompCor (inputnode, tcompcor, [('bold', 'realigned_file')]), (inputnode, tcompcor, [('skip_vols', 'ignore_initial_volumes')]), (tcc_roi, tcc_msk, [('out_file', 'roi_file')]), (tcc_msk, tcompcor, [('out', 'mask_files')]), # aCompCor (inputnode, acompcor, [('bold', 'realigned_file')]), (inputnode, acompcor, [('skip_vols', 'ignore_initial_volumes')]), (acc_roi, acc_msk, [('out_file', 'roi_file')]), (acc_msk, mrg_lbl_cc, [('out', 'in1')]), (inputnode, mrg_lbl_cc, [('csf_mask', 'in2')]), (inputnode, mrg_lbl_cc, [('wm_mask', 'in3')]), (mrg_lbl_cc, acompcor, [('out', 'mask_files')]), # Global signals extraction (constrained by anatomy) (inputnode, signals, [('bold', 'in_file')]), (inputnode, csf_msk, [('csf_mask', 'roi_file')]), (csf_msk, mrg_lbl, [('out', 'in1')]), (inputnode, wm_msk, [('wm_mask', '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_motion_headers, [('movpar_file', 'in_file')]), (dvars, add_dvars_header, [('out_nstd', 'in_file')]), (dvars, add_std_dvars_header, [('out_std', 'in_file')]), (signals, concat, [('out_file', 'signals')]), (fdisp, concat, [('out_file', 'fd')]), (tcompcor, concat, [('components_file', 'tcompcor'), ('pre_filter_file', 'cos_basis')]), (acompcor, concat, [('components_file', 'acompcor')]), (add_motion_headers, concat, [('out_file', 'motion')]), (add_dvars_header, concat, [('out_file', 'dvars')]), (add_std_dvars_header, concat, [('out_file', 'std_dvars')]), # Confounds metadata (tcompcor, tcc_metadata_fmt, [('metadata_file', 'in_file')]), (acompcor, acc_metadata_fmt, [('metadata_file', 'in_file')]), (tcc_metadata_fmt, mrg_conf_metadata, [('output', 'in1')]), (acc_metadata_fmt, mrg_conf_metadata, [('output', 'in2')]), (mrg_conf_metadata, mrg_conf_metadata2, [('out', 'in_dicts')]), # Expand the model with derivatives, quadratics, and spikes (concat, model_expand, [('confounds_file', 'confounds_file')]), (model_expand, spike_regress, [('confounds_file', 'confounds_file')]), # Set outputs (spike_regress, outputnode, [('confounds_file', 'confounds_file')]), (mrg_conf_metadata2, outputnode, [('out_dict', 'confounds_metadata')]), (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, ds_report_bold_rois, [('out_report', 'in_file')]), (tcompcor, mrg_cc_metadata, [('metadata_file', 'in1')]), (acompcor, mrg_cc_metadata, [('metadata_file', 'in2')]), (mrg_cc_metadata, compcor_plot, [('out', 'metadata_files')]), (compcor_plot, ds_report_compcor, [('out_file', 'in_file')]), (concat, conf_corr_plot, [('confounds_file', 'confounds_file')]), (conf_corr_plot, ds_report_conf_corr, [('out_file', 'in_file')]), ]) return workflow
flirt_T1toMNI.inputs.reference = $FSLDIR/data/standard/MNI152_T1_2mm_brain.nii.gz #Wraps the executable command ``convert_xfm``. fsl_convert_xfm = pe.MapNode(interface = fsl.ConvertXFM(), name='fsl_convert_xfm', iterfield = ['in_file', 'in_file2']) fsl_convert_xfm.inputs.concat_xfm = True #Wraps the executable command ``flirt``. flirt_EPItoMNI = pe.MapNode(interface = fsl.FLIRT(), name='flirt_EPItoMNI', iterfield = ['in_file', 'in_matrix_file']) flirt_EPItoMNI.inputs.reference = $FSLDIR/data/standard/MNI152_T1_2mm_brain.nii.gz #Wraps the executable command ``3dBlurToFWHM``. afni_blur_to_fwhm = pe.Node(interface = afni.BlurToFWHM(), name='afni_blur_to_fwhm') afni_blur_to_fwhm.inputs.fwhm = 6 #Calculate the :abbr:`FD (framewise displacement)` as in [Power2012]_. confounds_framewise_displacement = pe.MapNode(interface = confounds.FramewiseDisplacement(), name='confounds_framewise_displacement', iterfield = ['in_file']) confounds_framewise_displacement.inputs.parameter_source = 'FSL' #Wraps the executable command ``fast``. fsl_fast = pe.MapNode(interface = fsl.FAST(), name='fsl_fast', iterfield = ['in_files']) fsl_fast.inputs.number_classes = 3 #Anatomical compcor: for inputs and outputs, see CompCor. confounds_acomp_cor = pe.MapNode(interface = confounds.ACompCor(), name='confounds_acomp_cor', iterfield = ['realigned_file', 'mask_files']) confounds_acomp_cor.inputs.merge_method = 'union' #Generic datasink module to store structured outputs io_data_sink = pe.MapNode(interface = io.DataSink(), name='io_data_sink', iterfield = ['smoothedEPI', 'framewiseDisplacement', 'mcflirtPar', 'aCompCorComponents']) io_data_sink.inputs.base_directory = out_dir io_data_sink.inputs.parameterization = True
def init_asl_confs_wf( mem_gb, metadata, name="asl_confs_wf", ): """ Build a workflow to generate and write out confounding signals. This workflow calculates confounds for a asl 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: #. DVARS - original and standardized variants (``dvars``, ``std_dvars``) #. Framewise displacement, based on head-motion parameters (``framewise_displacement``) #. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) Workflow Graph .. workflow:: :graph2use: orig :simple_form: yes from aslprep.workflows.asl.confounds import init_asl_confs_wf wf = init_asl_confs_wf( mem_gb=1, metadata={}, ) Parameters ---------- mem_gb : :obj:`float` Size of asl file in GB - please note that this size should be calculated after resamplings that may extend the FoV metadata : :obj:`dict` BIDS metadata for asl file name : :obj:`str` Name of workflow (default: ``asl_confs_wf``) Inputs ------ asl asl image, after the prescribed corrections (STC, HMC and SDC) when available. asl_mask asl series mask movpar_file SPM-formatted motion parameters file skip_vols number of non steady state volumes t1w_mask Mask of the skull-stripped template image t1w_tpms List of tissue probability maps in T1w space t1_asl_xform Affine matrix that maps the T1w space into alignment with the native asl space Outputs ------- confounds_file TSV of all aggregated confounds confounds_metadata Confounds metadata dictionary. """ workflow = Workflow(name=name) workflow.__desc__ = """\ Several confounding time-series were calculated based on the *preprocessed ASL*: framewise displacement (FD) and DVARS. FD and DVARS are calculated for each ASL run, both using their implementations in *Nipype* [following the definitions by @power_fd_dvars]. The head-motion estimates calculated in the correction step were also placed within the corresponding confounds file. """ inputnode = pe.Node(niu.IdentityInterface( fields=['asl', 'asl_mask', 'movpar_file', 'skip_vols', 't1w_mask', 't1w_tpms', 't1_asl_xform']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['confounds_file', 'confounds_metadata']), name='outputnode') # DVARS dvars = pe.Node(nac.ComputeDVARS(save_nstd=True, save_std=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) # Global and segment regressors #signals_class_labels = ["csf", "white_matter", "global_signal"] # Arrange confounds add_dvars_header = pe.Node( AddTSVHeader(columns=["dvars"]), name="add_dvars_header", mem_gb=0.01, run_without_submitting=True) add_std_dvars_header = pe.Node( AddTSVHeader(columns=["std_dvars"]), name="add_std_dvars_header", mem_gb=0.01, run_without_submitting=True) add_motion_headers = pe.Node( AddTSVHeader(columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) # Expand model to include derivatives and quadratics workflow.connect([ # connect inputnode to each non-anatomical confound node (inputnode, dvars, [('asl', 'in_file'), ('asl_mask', 'in_mask')]), (inputnode, fdisp, [('movpar_file', 'in_file')]), # Collate computed confounds together (inputnode, add_motion_headers, [('movpar_file', 'in_file')]), (dvars, add_dvars_header, [('out_nstd', 'in_file')]), (dvars, add_std_dvars_header, [('out_std', 'in_file')]), (fdisp, concat, [('out_file', 'fd')]), (add_motion_headers, concat, [('out_file', 'motion')]), (add_dvars_header, concat, [('out_file', 'dvars')]), (add_std_dvars_header, concat, [('out_file', 'std_dvars')]), # Expand the model with derivatives, quadratics, and spikes # Set outputs (concat, outputnode, [('confounds_file', 'confounds_file')]), ]) return workflow
def init_dwi_confs_wf(mem_gb, metadata, impute_slice_threshold, name="dwi_confs_wf"): """ This workflow calculates confounds for a dwi 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: 1. Framewise displacement, based on head-motion parameters (``framewise_displacement``) 2. Estimated head-motion parameters, in mm and rad (``trans_x``, ``trans_y``, ``trans_z``, ``rot_x``, ``rot_y``, ``rot_z``) .. workflow:: :graph2use: orig :simple_form: yes from qsiprep.workflows.dwi.confounds import init_dwi_confs_wf wf = init_dwi_confs_wf( mem_gb=1, metadata={}, impute_slice_threshold=0) **Parameters** mem_gb : float Size of dwi file in GB - please note that this size should be calculated after resamplings that may extend the FoV metadata : dict BIDS metadata for dwi file name : str Name of workflow (default: ``dwi_confs_wf``) **Inputs** sliceqc_file dwi image, after the prescribed corrections (STC, HMC and SDC) when available. motion_params spm motion params **Outputs** confounds_file TSV of all aggregated confounds rois_report Reportlet visualizing white-matter/CSF mask used for aCompCor, the ROI for tCompCor and the dwi brain mask. """ workflow = Workflow(name=name) workflow.__desc__ = """\ Several confounding time-series were calculated based on the *preprocessed dwi*: framewise displacement (FD) using the implementation in *Nipype* [following the definitions by @power_fd_dvars]. The head-motion estimates calculated in the correction step were also placed within the corresponding confounds file. Slicewise cross correlation was also calculated. """ inputnode = pe.Node(niu.IdentityInterface( fields=['sliceqc_file', 'motion_params', 'bval_files', 'bvec_files', 'original_files']), name='inputnode') outputnode = pe.Node(niu.IdentityInterface( fields=['confounds_file', 'imputed_images']), name='outputnode') # Frame displacement fdisp = pe.Node(nac.FramewiseDisplacement(parameter_source="SPM"), name="fdisp", mem_gb=mem_gb) add_motion_headers = pe.Node( AddTSVHeader(columns=["trans_x", "trans_y", "trans_z", "rot_x", "rot_y", "rot_z"]), name="add_motion_headers", mem_gb=0.01, run_without_submitting=True) concat = pe.Node(GatherConfounds(), name="concat", mem_gb=0.01, run_without_submitting=True) workflow.connect([ (inputnode, fdisp, [('motion_params', 'in_file')]), # Collate computed confounds together (inputnode, add_motion_headers, [('motion_params', 'in_file')]), (fdisp, concat, [('out_file', 'fd')]), (add_motion_headers, concat, [('out_file', 'motion')]), (inputnode, concat, [('sliceqc_file', 'sliceqc_file'), ('bval_files', 'original_bvals'), ('bvec_files', 'original_bvecs'), ('original_files', 'original_files')]), # Set outputs (concat, outputnode, [('confounds_file', 'confounds_file')]), ]) return workflow
def mc_workflow_fsl(reference_vol="mid", FD_mode="Power", SinkTag="func_preproc", wf_name="motion_correction_fsl"): """ Modified version of CPAC.func_preproc.func_preproc and CPAC.generate_motion_statistics.generate_motion_statistics: `source: https://fcp-indi.github.io/docs/developer/_modules/CPAC/func_preproc/func_preproc.html` `source: https://fcp-indi.github.io/docs/developer/_modules/CPAC/generate_motion_statistics/generate_motion_statistics.html` Use FSL MCFLIRT to do the motion correction of the 4D functional data and use the 6df rigid body motion parameters to calculate friston24 parameters for later nuissance regression step. Workflow inputs: :param func: The reoriented functional file. :param reference_vol: Either "first", "mid", "last", "mean", or the index of the volume which the rigid body registration (motion correction) will use as reference. default: "mid" :param FD_mode Eiher "Power" or "Jenkinson" :param SinkDir: :param SinkTag: The output directory in which the returned images (see workflow outputs) could be found in a subdirectory directory specific for this workflow.. Workflow outputs: :return: mc_workflow - workflow Balint Kincses [email protected] 2018 """ # TODO_ready nipype has the ability to calculate FD: the function from CPAC calculates it correctly # import relevant packages import sys import os import nipype import nipype.pipeline as pe import nipype.interfaces.utility as utility import nipype.interfaces.fsl as fsl import nipype.algorithms.confounds as conf import PUMI.func_preproc.info.info_get as info_get import nipype.interfaces.io as io import PUMI.utils.utils_math as utils_math import PUMI.utils.utils_convert as utils_convert import PUMI.utils.globals as globals import PUMI.utils.QC as qc SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag) if not os.path.exists(SinkDir): os.makedirs(SinkDir) QCDir = os.path.abspath(globals._SinkDir_ + "/" + globals._QCDir_) if not os.path.exists(QCDir): os.makedirs(QCDir) # Basic interface class generates identity mappings inputspec = pe.Node(utility.IdentityInterface( fields=['func', 'ref_vol', 'save_plots', 'stats_imgs']), name='inputspec') inputspec.inputs.save_plots = True inputspec.inputs.stats_imgs = True inputspec.inputs.ref_vol = reference_vol # todo_ready: make parametrizable: the reference_vol variable is an argumentum of the mc_workflow # extract reference volume refvol = pe.MapNode(utility.Function(input_names=['refvol', 'func'], output_names=['refvol'], function=getRefVol), iterfield=['func'], name='getRefVol') # Wraps command **mcflirt** mcflirt = pe.MapNode( interface=fsl.MCFLIRT( interpolation="spline", stats_imgs=False), # stages=4), #stages 4: more accurate but slow iterfield=['in_file', 'ref_file'], # , 'ref_vol'], # make parametrizable name='mcflirt') if (reference_vol == "mean"): mcflirt = pe.MapNode( interface=fsl.MCFLIRT(interpolation="spline", stats_imgs=False), # stages=4), #stages 4: more accurate but slow iterfield=['in_file'], # , 'ref_vol'], # make parametrizable name='mcflirt') mcflirt.inputs.mean_vol = True else: mcflirt = pe.MapNode( interface=fsl.MCFLIRT(interpolation="spline", stats_imgs=False), # stages=4), #stages 4: more accurate but slow iterfield=['in_file', 'ref_file'], # , 'ref_vol'], # make parametrizable name='mcflirt') mcflirt.inputs.dof = 6 mcflirt.inputs.save_mats = True mcflirt.inputs.save_plots = True mcflirt.inputs.save_rms = True mcflirt.inputs.stats_imgs = False myqc = qc.timecourse2png("timeseries", tag="010_motioncorr") # Calculate Friston24 parameters calc_friston = pe.MapNode(utility.Function( input_names=['in_file'], output_names=['out_file'], function=calc_friston_twenty_four), iterfield=['in_file'], name='calc_friston') # Calculate FD based on Power's method if FD_mode == "Power": calculate_FD = pe.MapNode(conf.FramewiseDisplacement( parameter_source='FSL', save_plot=True), iterfield=['in_file'], name='calculate_FD_Power') elif FD_mode == "Jenkinson": calculate_FD = pe.MapNode(utility.Function(input_names=['in_file'], output_names=['out_file'], function=calculate_FD_J), iterfield=['in_file'], name='calculate_FD_Jenkinson') # compute mean and max FD meanFD = pe.MapNode(interface=utils_math.Txt2meanTxt, iterfield=['in_file'], name='meanFD') meanFD.inputs.axis = 0 # global mean meanFD.inputs.header = True # global mean maxFD = pe.MapNode(interface=utils_math.Txt2maxTxt, iterfield=['in_file'], name='maxFD') maxFD.inputs.axis = 0 # global mean maxFD.inputs.header = True pop_FD = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FD') pop_FDmax = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FDmax') # save data out with Datasink ds_fd = pe.Node(interface=io.DataSink(), name='ds_pop_fd') ds_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD.txt")] ds_fd.inputs.base_directory = SinkDir # save data out with Datasink ds_fd_max = pe.Node(interface=io.DataSink(), name='ds_pop_fd_max') ds_fd_max.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD_max.txt")] ds_fd_max.inputs.base_directory = SinkDir plot_motion_rot = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion_rot', iterfield=['in_file']) plot_motion_rot.inputs.plot_type = 'rotations' plot_motion_tra = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion_trans', iterfield=['in_file']) plot_motion_tra.inputs.plot_type = 'translations' # Basic interface class generates identity mappings outputspec = pe.Node(utility.IdentityInterface(fields=[ 'func_out_file', 'first24_file', 'mat_file', 'mc_par_file', 'FD_file' ]), name='outputspec') # save data out with Datasink ds_nii = pe.Node(interface=io.DataSink(), name='ds_nii') ds_nii.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".nii.gz")] ds_nii.inputs.base_directory = SinkDir # save data out with Datasink ds_text = pe.Node(interface=io.DataSink(), name='ds_txt') ds_text.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".txt")] ds_text.inputs.base_directory = SinkDir # Save outputs which are important ds_qc_fd = pe.Node(interface=io.DataSink(), name='ds_qc_fd') ds_qc_fd.inputs.base_directory = QCDir ds_qc_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_FD.pdf")] # Save outputs which are important ds_qc_rot = pe.Node(interface=io.DataSink(), name='ds_qc_rot') ds_qc_rot.inputs.base_directory = QCDir ds_qc_rot.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_rot.png")] # Save outputs which are important ds_qc_tra = pe.Node(interface=io.DataSink(), name='ds_qc_tra') ds_qc_tra.inputs.base_directory = QCDir ds_qc_tra.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_trans.png")] #TODO_ready set the proper images which has to be saved in a the datasink specified directory # Create a workflow to connect all those nodes analysisflow = nipype.Workflow(wf_name) analysisflow.connect(inputspec, 'func', mcflirt, 'in_file') analysisflow.connect(inputspec, 'func', refvol, 'func') analysisflow.connect(inputspec, 'ref_vol', refvol, 'refvol') if (reference_vol != "mean"): analysisflow.connect(refvol, 'refvol', mcflirt, 'ref_file') analysisflow.connect(mcflirt, 'par_file', calc_friston, 'in_file') analysisflow.connect(mcflirt, 'par_file', calculate_FD, 'in_file') analysisflow.connect(mcflirt, 'out_file', outputspec, 'func_out_file') analysisflow.connect(mcflirt, 'mat_file', outputspec, 'mat_file') analysisflow.connect(mcflirt, 'par_file', outputspec, 'mc_par_file') analysisflow.connect(mcflirt, 'out_file', ds_nii, 'mc_func') analysisflow.connect(mcflirt, 'par_file', ds_text, 'mc_par') #analysisflow.connect(mcflirt, 'std_img', ds, 'mc.@std_img') analysisflow.connect(mcflirt, 'rms_files', ds_text, 'mc_rms') #analysisflow.connect(mcflirt, 'variance_img', ds, 'mc.@variance_img') analysisflow.connect(calc_friston, 'out_file', outputspec, 'first24_file') analysisflow.connect(calc_friston, 'out_file', ds_text, 'mc_first24') analysisflow.connect(calculate_FD, 'out_file', outputspec, 'FD_file') analysisflow.connect(mcflirt, 'par_file', plot_motion_rot, 'in_file') analysisflow.connect(mcflirt, 'par_file', plot_motion_tra, 'in_file') analysisflow.connect(plot_motion_rot, 'out_file', ds_qc_rot, 'motion_correction') analysisflow.connect(plot_motion_tra, 'out_file', ds_qc_tra, 'motion_correction') analysisflow.connect(mcflirt, 'out_file', myqc, 'inputspec.func') # pop-level mean FD analysisflow.connect(calculate_FD, 'out_file', meanFD, 'in_file') analysisflow.connect(calculate_FD, 'out_file', ds_text, 'mc_fd') analysisflow.connect(calculate_FD, 'out_figure', ds_qc_fd, 'FD') analysisflow.connect(meanFD, 'mean_file', pop_FD, 'in_list') analysisflow.connect(pop_FD, 'txt_file', ds_fd, 'pop') analysisflow.connect(calculate_FD, 'out_file', maxFD, 'in_file') analysisflow.connect(maxFD, 'max_file', pop_FDmax, 'in_list') analysisflow.connect(pop_FDmax, 'txt_file', ds_fd_max, 'pop') return analysisflow