def main(sourcedata, derivatives, tmp_dir, subject=None, session=None, run=None): print(subject, session, run) layout = BIDSLayout(sourcedata) derivatives_layout = BIDSLayout('/derivatives/spynoza', validate=False) cortex_l = get_derivative(derivatives, 'nighres', 'anat', subject, 'dseg', session='anat', space='average', description='cortex', hemi='left') cortex_r = get_derivative(derivatives, 'nighres', 'anat', subject, 'dseg', session='anat', space='average', description='cortex', hemi='left') mask = derivatives_layout.get(subject=subject, session=session, suffix='mask', return_type='file') mask = sorted(mask) assert (len(mask) == 1) mask = mask[0] bold = derivatives_layout.get(subject=subject, session=session, suffix='preproc', return_type='file') bold = sorted(bold) print('BOLD: {}'.format(bold)) print('MASK: {}'.format(mask)) inputnode = pe.Node( niu.IdentityInterface(fields=['cortex_l', 'cortex_r', 'bold', 'mask']), name='inputnode') inputnode.inputs.cortex_l = cortex_l inputnode.inputs.cortex_r = cortex_r inputnode.inputs.bold = bold inputnode.inputs.mask = mask get_masks = pe.MapNode(niu.Function( function=get_brain_regions_cruise, input_names=['cortex_l', 'cortex_r', 'type'], output_names=['out']), iterfield=['type'], name='get_masks') get_masks.inputs.type = ['csf', 'wm'] wf = pe.Workflow(name='get_confounds_{}_{}'.format(subject, session), base_dir='/workflow_folders') wf.connect(inputnode, 'cortex_l', get_masks, 'cortex_l') wf.connect(inputnode, 'cortex_r', get_masks, 'cortex_r') resampler = pe.MapNode(niu.Function( function=resample_img, input_names=['input_image', 'ref_image', 'interpolation'], output_names=['resampled_image'], ), iterfield=['input_image'], name='resampler') wf.connect(inputnode, ('bold', pickfirst), resampler, 'ref_image') wf.connect(get_masks, 'out', resampler, 'input_image') compcorr = pe.MapNode(ACompCor(merge_method='union'), iterfield=['realigned_file'], name='acompcorr') wf.connect(resampler, 'resampled_image', compcorr, 'mask_files') wf.connect(inputnode, 'bold', compcorr, 'realigned_file') dvars = pe.MapNode(ComputeDVARS(), iterfield=['in_file'], name='dvars') wf.connect(inputnode, 'mask', dvars, 'in_mask') wf.connect(inputnode, 'bold', dvars, 'in_file') add_header = pe.MapNode(AddTSVHeader(columns=["dvars"]), iterfield=['in_file'], name="add_header_dvars") wf.connect(dvars, 'out_std', add_header, 'in_file') concat = pe.MapNode(GatherConfounds(), iterfield=['acompcor', 'dvars'], name="concat") wf.connect(add_header, 'out_file', concat, 'dvars') wf.connect(compcorr, 'components_file', concat, 'acompcor') ds_confounds = pe.MapNode(DerivativesDataSink(out_path_base='spynoza', suffix='confounds_compcor', base_directory=derivatives), iterfield=['in_file', 'source_file'], name='ds_reg_report') wf.connect(inputnode, 'bold', ds_confounds, 'source_file') wf.connect(concat, 'confounds_file', ds_confounds, 'in_file') wf.run(plugin='MultiProc', plugin_args={'n_procs': 10})
def create_workflow(files, target_file, subject_id, TR, slice_times, norm_threshold=1, num_components=5, vol_fwhm=None, surf_fwhm=None, lowpass_freq=-1, highpass_freq=-1, subjects_dir=None, sink_directory=os.getcwd(), target_subject=['fsaverage3', 'fsaverage4'], name='resting'): wf = Workflow(name=name) # Rename files in case they are named identically name_unique = MapNode(Rename(format_string='rest_%(run)02d'), iterfield=['in_file', 'run'], name='rename') name_unique.inputs.keep_ext = True name_unique.inputs.run = list(range(1, len(files) + 1)) name_unique.inputs.in_file = files realign = Node(nipy.SpaceTimeRealigner(), name="spacetime_realign") realign.inputs.slice_times = slice_times realign.inputs.tr = TR realign.inputs.slice_info = 2 realign.plugin_args = {'sbatch_args': '-c%d' % 4} # Compute TSNR on realigned data regressing polynomials up to order 2 tsnr = MapNode(TSNR(regress_poly=2), iterfield=['in_file'], name='tsnr') wf.connect(realign, "out_file", tsnr, "in_file") # Compute the median image across runs calc_median = Node(CalculateMedian(), name='median') wf.connect(tsnr, 'detrended_file', calc_median, 'in_files') """Segment and Register """ registration = create_reg_workflow(name='registration') wf.connect(calc_median, 'median_file', registration, 'inputspec.mean_image') registration.inputs.inputspec.subject_id = subject_id registration.inputs.inputspec.subjects_dir = subjects_dir registration.inputs.inputspec.target_image = target_file """Quantify TSNR in each freesurfer ROI """ get_roi_tsnr = MapNode(fs.SegStats(default_color_table=True), iterfield=['in_file'], name='get_aparc_tsnr') get_roi_tsnr.inputs.avgwf_txt_file = True wf.connect(tsnr, 'tsnr_file', get_roi_tsnr, 'in_file') wf.connect(registration, 'outputspec.aparc', get_roi_tsnr, 'segmentation_file') """Use :class:`nipype.algorithms.rapidart` to determine which of the images in the functional series are outliers based on deviations in intensity or movement. """ art = Node(interface=ArtifactDetect(), name="art") art.inputs.use_differences = [True, True] art.inputs.use_norm = True art.inputs.norm_threshold = norm_threshold art.inputs.zintensity_threshold = 9 art.inputs.mask_type = 'spm_global' art.inputs.parameter_source = 'NiPy' """Here we are connecting all the nodes together. Notice that we add the merge node only if you choose to use 4D. Also `get_vox_dims` function is passed along the input volume of normalise to set the optimal voxel sizes. """ wf.connect([(name_unique, realign, [('out_file', 'in_file')]), (realign, art, [('out_file', 'realigned_files')]), (realign, art, [('par_file', 'realignment_parameters')]), ]) def selectindex(files, idx): import numpy as np from nipype.utils.filemanip import filename_to_list, list_to_filename return list_to_filename(np.array(filename_to_list(files))[idx].tolist()) mask = Node(fsl.BET(), name='getmask') mask.inputs.mask = True wf.connect(calc_median, 'median_file', mask, 'in_file') # get segmentation in normalized functional space def merge_files(in1, in2): out_files = filename_to_list(in1) out_files.extend(filename_to_list(in2)) return out_files # filter some noise # Compute motion regressors motreg = Node(Function(input_names=['motion_params', 'order', 'derivatives'], output_names=['out_files'], function=motion_regressors, imports=imports), name='getmotionregress') wf.connect(realign, 'par_file', motreg, 'motion_params') # Create a filter to remove motion and art confounds createfilter1 = Node(Function(input_names=['motion_params', 'comp_norm', 'outliers', 'detrend_poly'], output_names=['out_files'], function=build_filter1, imports=imports), name='makemotionbasedfilter') createfilter1.inputs.detrend_poly = 2 wf.connect(motreg, 'out_files', createfilter1, 'motion_params') wf.connect(art, 'norm_files', createfilter1, 'comp_norm') wf.connect(art, 'outlier_files', createfilter1, 'outliers') filter1 = MapNode(fsl.GLM(out_f_name='F_mcart.nii.gz', out_pf_name='pF_mcart.nii.gz', demean=True), iterfield=['in_file', 'design', 'out_res_name'], name='filtermotion') wf.connect(realign, 'out_file', filter1, 'in_file') wf.connect(realign, ('out_file', rename, '_filtermotart'), filter1, 'out_res_name') wf.connect(createfilter1, 'out_files', filter1, 'design') createfilter2 = MapNode(ACompCor(), iterfield=['realigned_file', 'extra_regressors'], name='makecompcorrfilter') createfilter2.inputs.components_file = 'noise_components.txt' createfilter2.inputs.num_components = num_components wf.connect(createfilter1, 'out_files', createfilter2, 'extra_regressors') wf.connect(filter1, 'out_res', createfilter2, 'realigned_file') wf.connect(registration, ('outputspec.segmentation_files', selectindex, [0, 2]), createfilter2, 'mask_file') filter2 = MapNode(fsl.GLM(out_f_name='F.nii.gz', out_pf_name='pF.nii.gz', demean=True), iterfield=['in_file', 'design', 'out_res_name'], name='filter_noise_nosmooth') wf.connect(filter1, 'out_res', filter2, 'in_file') wf.connect(filter1, ('out_res', rename, '_cleaned'), filter2, 'out_res_name') wf.connect(createfilter2, 'components_file', filter2, 'design') wf.connect(mask, 'mask_file', filter2, 'mask') bandpass = Node(Function(input_names=['files', 'lowpass_freq', 'highpass_freq', 'fs'], output_names=['out_files'], function=bandpass_filter, imports=imports), name='bandpass_unsmooth') bandpass.inputs.fs = 1. / TR bandpass.inputs.highpass_freq = highpass_freq bandpass.inputs.lowpass_freq = lowpass_freq wf.connect(filter2, 'out_res', bandpass, 'files') """Smooth the functional data using :class:`nipype.interfaces.fsl.IsotropicSmooth`. """ smooth = MapNode(interface=fsl.IsotropicSmooth(), name="smooth", iterfield=["in_file"]) smooth.inputs.fwhm = vol_fwhm wf.connect(bandpass, 'out_files', smooth, 'in_file') collector = Node(Merge(2), name='collect_streams') wf.connect(smooth, 'out_file', collector, 'in1') wf.connect(bandpass, 'out_files', collector, 'in2') """ Transform the remaining images. First to anatomical and then to target """ warpall = MapNode(ants.ApplyTransforms(), iterfield=['input_image'], name='warpall') warpall.inputs.input_image_type = 3 warpall.inputs.interpolation = 'Linear' warpall.inputs.invert_transform_flags = [False, False] warpall.terminal_output = 'file' warpall.inputs.reference_image = target_file warpall.inputs.args = '--float' warpall.inputs.num_threads = 2 warpall.plugin_args = {'sbatch_args': '-c%d' % 2} # transform to target wf.connect(collector, 'out', warpall, 'input_image') wf.connect(registration, 'outputspec.transforms', warpall, 'transforms') mask_target = Node(fsl.ImageMaths(op_string='-bin'), name='target_mask') wf.connect(registration, 'outputspec.anat2target', mask_target, 'in_file') maskts = MapNode(fsl.ApplyMask(), iterfield=['in_file'], name='ts_masker') wf.connect(warpall, 'output_image', maskts, 'in_file') wf.connect(mask_target, 'out_file', maskts, 'mask_file') # map to surface # extract aparc+aseg ROIs # extract subcortical ROIs # extract target space ROIs # combine subcortical and cortical rois into a single cifti file ####### # Convert aparc to subject functional space # Sample the average time series in aparc ROIs sampleaparc = MapNode(freesurfer.SegStats(default_color_table=True), iterfield=['in_file', 'summary_file', 'avgwf_txt_file'], name='aparc_ts') sampleaparc.inputs.segment_id = ([8] + list(range(10, 14)) + [17, 18, 26, 47] + list(range(49, 55)) + [58] + list(range(1001, 1036)) + list(range(2001, 2036))) wf.connect(registration, 'outputspec.aparc', sampleaparc, 'segmentation_file') wf.connect(collector, 'out', sampleaparc, 'in_file') def get_names(files, suffix): """Generate appropriate names for output files """ from nipype.utils.filemanip import (split_filename, filename_to_list, list_to_filename) import os out_names = [] for filename in files: path, name, _ = split_filename(filename) out_names.append(os.path.join(path, name + suffix)) return list_to_filename(out_names) wf.connect(collector, ('out', get_names, '_avgwf.txt'), sampleaparc, 'avgwf_txt_file') wf.connect(collector, ('out', get_names, '_summary.stats'), sampleaparc, 'summary_file') # Sample the time series onto the surface of the target surface. Performs # sampling into left and right hemisphere target = Node(IdentityInterface(fields=['target_subject']), name='target') target.iterables = ('target_subject', filename_to_list(target_subject)) samplerlh = MapNode(freesurfer.SampleToSurface(), iterfield=['source_file'], name='sampler_lh') samplerlh.inputs.sampling_method = "average" samplerlh.inputs.sampling_range = (0.1, 0.9, 0.1) samplerlh.inputs.sampling_units = "frac" samplerlh.inputs.interp_method = "trilinear" samplerlh.inputs.smooth_surf = surf_fwhm # samplerlh.inputs.cortex_mask = True samplerlh.inputs.out_type = 'niigz' samplerlh.inputs.subjects_dir = subjects_dir samplerrh = samplerlh.clone('sampler_rh') samplerlh.inputs.hemi = 'lh' wf.connect(collector, 'out', samplerlh, 'source_file') wf.connect(registration, 'outputspec.out_reg_file', samplerlh, 'reg_file') wf.connect(target, 'target_subject', samplerlh, 'target_subject') samplerrh.set_input('hemi', 'rh') wf.connect(collector, 'out', samplerrh, 'source_file') wf.connect(registration, 'outputspec.out_reg_file', samplerrh, 'reg_file') wf.connect(target, 'target_subject', samplerrh, 'target_subject') # Combine left and right hemisphere to text file combiner = MapNode(Function(input_names=['left', 'right'], output_names=['out_file'], function=combine_hemi, imports=imports), iterfield=['left', 'right'], name="combiner") wf.connect(samplerlh, 'out_file', combiner, 'left') wf.connect(samplerrh, 'out_file', combiner, 'right') # Sample the time series file for each subcortical roi ts2txt = MapNode(Function(input_names=['timeseries_file', 'label_file', 'indices'], output_names=['out_file'], function=extract_subrois, imports=imports), iterfield=['timeseries_file'], name='getsubcortts') ts2txt.inputs.indices = [8] + list(range(10, 14)) + [17, 18, 26, 47] +\ list(range(49, 55)) + [58] ts2txt.inputs.label_file = \ os.path.abspath(('OASIS-TRT-20_jointfusion_DKT31_CMA_labels_in_MNI152_' '2mm_v2.nii.gz')) wf.connect(maskts, 'out_file', ts2txt, 'timeseries_file') ###### substitutions = [('_target_subject_', ''), ('_filtermotart_cleaned_bp_trans_masked', ''), ('_filtermotart_cleaned_bp', ''), ] substitutions += [("_smooth%d" % i, "") for i in range(11)[::-1]] substitutions += [("_ts_masker%d" % i, "") for i in range(11)[::-1]] substitutions += [("_getsubcortts%d" % i, "") for i in range(11)[::-1]] substitutions += [("_combiner%d" % i, "") for i in range(11)[::-1]] substitutions += [("_filtermotion%d" % i, "") for i in range(11)[::-1]] substitutions += [("_filter_noise_nosmooth%d" % i, "") for i in range(11)[::-1]] substitutions += [("_makecompcorfilter%d" % i, "") for i in range(11)[::-1]] substitutions += [("_get_aparc_tsnr%d/" % i, "run%d_" % (i + 1)) for i in range(11)[::-1]] substitutions += [("T1_out_brain_pve_0_maths_warped", "compcor_csf"), ("T1_out_brain_pve_1_maths_warped", "compcor_gm"), ("T1_out_brain_pve_2_maths_warped", "compcor_wm"), ("output_warped_image_maths", "target_brain_mask"), ("median_brain_mask", "native_brain_mask"), ("corr_", "")] regex_subs = [('_combiner.*/sar', '/smooth/'), ('_combiner.*/ar', '/unsmooth/'), ('_aparc_ts.*/sar', '/smooth/'), ('_aparc_ts.*/ar', '/unsmooth/'), ('_getsubcortts.*/sar', '/smooth/'), ('_getsubcortts.*/ar', '/unsmooth/'), ('series/sar', 'series/smooth/'), ('series/ar', 'series/unsmooth/'), ('_inverse_transform./', ''), ] # Save the relevant data into an output directory datasink = Node(interface=DataSink(), name="datasink") datasink.inputs.base_directory = sink_directory datasink.inputs.container = subject_id datasink.inputs.substitutions = substitutions datasink.inputs.regexp_substitutions = regex_subs # (r'(/_.*(\d+/))', r'/run\2') wf.connect(realign, 'par_file', datasink, 'resting.qa.motion') wf.connect(art, 'norm_files', datasink, 'resting.qa.art.@norm') wf.connect(art, 'intensity_files', datasink, 'resting.qa.art.@intensity') wf.connect(art, 'outlier_files', datasink, 'resting.qa.art.@outlier_files') wf.connect(registration, 'outputspec.segmentation_files', datasink, 'resting.mask_files') wf.connect(registration, 'outputspec.anat2target', datasink, 'resting.qa.ants') wf.connect(mask, 'mask_file', datasink, 'resting.mask_files.@brainmask') wf.connect(mask_target, 'out_file', datasink, 'resting.mask_files.target') wf.connect(filter1, 'out_f', datasink, 'resting.qa.compmaps.@mc_F') wf.connect(filter1, 'out_pf', datasink, 'resting.qa.compmaps.@mc_pF') wf.connect(filter2, 'out_f', datasink, 'resting.qa.compmaps') wf.connect(filter2, 'out_pf', datasink, 'resting.qa.compmaps.@p') wf.connect(registration, 'outputspec.min_cost_file', datasink, 'resting.qa.mincost') wf.connect(tsnr, 'tsnr_file', datasink, 'resting.qa.tsnr.@map') wf.connect([(get_roi_tsnr, datasink, [('avgwf_txt_file', 'resting.qa.tsnr'), ('summary_file', 'resting.qa.tsnr.@summary')])]) wf.connect(bandpass, 'out_files', datasink, 'resting.timeseries.@bandpassed') wf.connect(smooth, 'out_file', datasink, 'resting.timeseries.@smoothed') wf.connect(createfilter1, 'out_files', datasink, 'resting.regress.@regressors') wf.connect(createfilter2, 'components_file', datasink, 'resting.regress.@compcorr') wf.connect(maskts, 'out_file', datasink, 'resting.timeseries.target') wf.connect(sampleaparc, 'summary_file', datasink, 'resting.parcellations.aparc') wf.connect(sampleaparc, 'avgwf_txt_file', datasink, 'resting.parcellations.aparc.@avgwf') wf.connect(ts2txt, 'out_file', datasink, 'resting.parcellations.grayo.@subcortical') datasink2 = Node(interface=DataSink(), name="datasink2") datasink2.inputs.base_directory = sink_directory datasink2.inputs.container = subject_id datasink2.inputs.substitutions = substitutions datasink2.inputs.regexp_substitutions = regex_subs # (r'(/_.*(\d+/))', r'/run\2') wf.connect(combiner, 'out_file', datasink2, 'resting.parcellations.grayo.@surface') return wf
def create_compcor_workflow(name='compcor'): """ Creates A/T compcor workflow. """ input_node = pe.Node(interface=IdentityInterface(fields=[ 'in_file', 'fast_files', 'highres2epi_mat', 'n_comp_tcompcor', 'n_comp_acompcor', 'output_directory', 'sub_id' ]), name='inputspec') output_node = pe.Node(interface=IdentityInterface( fields=['tcompcor_file', 'acompcor_file', 'epi_mask']), name='outputspec') extract_task = pe.MapNode(interface=Extract_task, iterfield=['in_file'], name='extract_task') rename_acompcor = pe.MapNode(interface=Rename( format_string='task-%(task)s_acompcor.tsv', keepext=True), iterfield=['task', 'in_file'], name='rename_acompcor') datasink = pe.Node(DataSink(), name='sinker') datasink.inputs.parameterization = False average_func = pe.MapNode(interface=fsl.maths.MeanImage(dimension='T'), name='average_func', iterfield=['in_file']) epi_mask = pe.MapNode(interface=fsl.BET(frac=.3, mask=True, no_output=True, robust=True), iterfield=['in_file'], name='epi_mask') wm2epi = pe.MapNode(fsl.ApplyXFM(interp='nearestneighbour'), iterfield=['reference'], name='wm2epi') csf2epi = pe.MapNode(fsl.ApplyXFM(interp='nearestneighbour'), iterfield=['reference'], name='csf2epi') erode_csf = pe.MapNode(interface=Erode_mask, name='erode_csf', iterfield=['epi_mask', 'in_file']) erode_csf.inputs.erosion_mm = 0 erode_csf.inputs.epi_mask_erosion_mm = 30 erode_wm = pe.MapNode(interface=Erode_mask, name='erode_wm', iterfield=['epi_mask', 'in_file']) erode_wm.inputs.erosion_mm = 6 erode_wm.inputs.epi_mask_erosion_mm = 10 merge_wm_and_csf_masks = pe.MapNode(Merge(2), name='merge_wm_and_csf_masks', iterfield=['in1', 'in2']) # This should be fit on the 30mm eroded mask from CSF tcompcor = pe.MapNode(TCompCor(components_file='tcomcor_comps.txt'), iterfield=['realigned_file', 'mask_files'], name='tcompcor') # WM + CSF mask acompcor = pe.MapNode(ACompCor(components_file='acompcor_comps.txt', merge_method='union'), iterfield=['realigned_file', 'mask_files'], name='acompcor') compcor_wf = pe.Workflow(name=name) compcor_wf.connect(input_node, 'in_file', extract_task, 'in_file') compcor_wf.connect(extract_task, 'task_name', rename_acompcor, 'task') compcor_wf.connect(acompcor, 'components_file', rename_acompcor, 'in_file') compcor_wf.connect(input_node, 'sub_id', datasink, 'container') compcor_wf.connect(input_node, 'output_directory', datasink, 'base_directory') compcor_wf.connect(input_node, ('fast_files', pick_wm), wm2epi, 'in_file') compcor_wf.connect(epi_mask, 'mask_file', wm2epi, 'reference') compcor_wf.connect(input_node, 'highres2epi_mat', wm2epi, 'in_matrix_file') compcor_wf.connect(input_node, ('fast_files', pick_csf), csf2epi, 'in_file') compcor_wf.connect(epi_mask, 'mask_file', csf2epi, 'reference') compcor_wf.connect(input_node, 'highres2epi_mat', csf2epi, 'in_matrix_file') compcor_wf.connect(input_node, 'n_comp_tcompcor', tcompcor, 'num_components') compcor_wf.connect(input_node, 'n_comp_acompcor', acompcor, 'num_components') compcor_wf.connect(input_node, 'in_file', average_func, 'in_file') compcor_wf.connect(average_func, 'out_file', epi_mask, 'in_file') compcor_wf.connect(epi_mask, 'mask_file', erode_csf, 'epi_mask') compcor_wf.connect(epi_mask, 'mask_file', erode_wm, 'epi_mask') compcor_wf.connect(wm2epi, 'out_file', erode_wm, 'in_file') compcor_wf.connect(csf2epi, 'out_file', erode_csf, 'in_file') compcor_wf.connect(erode_wm, 'roi_eroded', merge_wm_and_csf_masks, 'in1') compcor_wf.connect(erode_csf, 'roi_eroded', merge_wm_and_csf_masks, 'in2') compcor_wf.connect(merge_wm_and_csf_masks, 'out', acompcor, 'mask_files') compcor_wf.connect(input_node, 'in_file', acompcor, 'realigned_file') compcor_wf.connect(input_node, 'in_file', tcompcor, 'realigned_file') compcor_wf.connect(erode_csf, 'epi_mask_eroded', tcompcor, 'mask_files') #compcor_wf.connect(tcompcor, 'components_file', output_node, 'acompcor_file') #compcor_wf.connect(acompcor, 'components_file', output_node, 'tcompcor_file') compcor_wf.connect(epi_mask, 'mask_file', output_node, 'epi_mask') compcor_wf.connect(rename_acompcor, 'out_file', datasink, 'acompcor_file') #compcor_wf.connect(tcompcor, 'components_file', combine_files, 'tcomp') #compcor_wf.connect(acompcor, 'components_file', combine_files, 'acomp') #compcor_wf.connect(combine_files, 'out_file', datasink, 'confounds') return compcor_wf