def rapidart_fmri_artifact_detection(): art = ArtifactDetect() art.inputs.use_differences = [True, False] art.inputs.use_norm = True art.inputs.zintensity_threshold = 2 art.inputs.norm_threshold = 1 art.inputs.mask_type = 'file' art.inputs.parameter_source = 'NiPy' return art
def set_art_params(self, zintensity=3, norm_threshold=2, abs_mvmt=3): if self.analysis: self.analysis = type('', (), {})() self.analysis.art = Node(ArtifactDetect(norm_threshold=2, zintensity_threshold=2, mask_type='spm_global', parameter_source='FSL', use_differences=[True, False], save_plot=True, plot_type='svg'), name="art")
def test_ArtifactDetect_outputs(): output_map = dict(displacement_files=dict(), intensity_files=dict(), mask_files=dict(), norm_files=dict(), outlier_files=dict(), plot_files=dict(), statistic_files=dict(), ) outputs = ArtifactDetect.output_spec() for key, metadata in output_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(outputs.traits()[key], metakey), value
def test_ArtifactDetect_outputs(): output_map = dict(displacement_files=dict(), outlier_files=dict(), mask_files=dict(), intensity_files=dict(), norm_files=dict(), statistic_files=dict(), plot_files=dict(), ) outputs = ArtifactDetect.output_spec() for key, metadata in output_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(outputs.traits()[key], metakey), value
def test_ArtifactDetect_inputs(): input_map = dict(ignore_exception=dict(nohash=True, usedefault=True, ), plot_type=dict(usedefault=True, ), mask_type=dict(mandatory=True, ), mask_threshold=dict(), parameter_source=dict(mandatory=True, ), translation_threshold=dict(mandatory=True, xor=['norm_threshold'], ), global_threshold=dict(usedefault=True, ), bound_by_brainmask=dict(usedefault=True, ), realigned_files=dict(mandatory=True, ), rotation_threshold=dict(mandatory=True, xor=['norm_threshold'], ), save_plot=dict(usedefault=True, ), use_differences=dict(minlen=2, maxlen=2, usedefault=True, ), norm_threshold=dict(mandatory=True, xor=['rotation_threshold', 'translation_threshold'], ), zintensity_threshold=dict(mandatory=True, ), use_norm=dict(requires=['norm_threshold'], usedefault=True, ), mask_file=dict(), intersect_mask=dict(), realignment_parameters=dict(mandatory=True, ), ) inputs = ArtifactDetect.input_spec() for key, metadata in input_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(inputs.traits()[key], metakey), value
def test_ArtifactDetect_inputs(): input_map = dict(bound_by_brainmask=dict(usedefault=True, ), global_threshold=dict(usedefault=True, ), ignore_exception=dict(nohash=True, usedefault=True, ), intersect_mask=dict(), mask_file=dict(), mask_threshold=dict(), mask_type=dict(mandatory=True, ), norm_threshold=dict(mandatory=True, xor=['rotation_threshold', 'translation_threshold'], ), parameter_source=dict(mandatory=True, ), plot_type=dict(usedefault=True, ), realigned_files=dict(mandatory=True, ), realignment_parameters=dict(mandatory=True, ), rotation_threshold=dict(mandatory=True, xor=['norm_threshold'], ), save_plot=dict(usedefault=True, ), translation_threshold=dict(mandatory=True, xor=['norm_threshold'], ), use_differences=dict(maxlen=2, minlen=2, usedefault=True, ), use_norm=dict(requires=['norm_threshold'], usedefault=True, ), zintensity_threshold=dict(mandatory=True, ), ) inputs = ArtifactDetect.input_spec() for key, metadata in input_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(inputs.traits()[key], metakey), value
def preprocessing(*argu): argu = argu[0] json_file = argu[1] with open(json_file, 'r') as jsonfile: info = json.load(jsonfile, object_pairs_hook=OrderedDict) subject_list = info["subject_list"] experiment_dir = info["experiment_dir"] output_dir = 'datasink' working_dir = 'workingdir' task_list = info["task_list"] fwhm = [*map(int, info["fwhm"])] TR = float(info["TR"]) iso_size = 4 slice_list = [*map(int, info["slice order"])] # ExtractROI - skip dummy scans extract = Node(ExtractROI(t_min=int(info["dummy scans"]), t_size=-1, output_type='NIFTI'), name="extract") slicetime = Node(SliceTiming(num_slices=len(slice_list), ref_slice=int(median(slice_list)), slice_order=slice_list, time_repetition=TR, time_acquisition=TR - (TR / len(slice_list))), name="slicetime") mcflirt = Node(MCFLIRT(mean_vol=True, save_plots=True, output_type='NIFTI'), name="mcflirt") # Smooth - image smoothing smooth = Node(Smooth(), name="smooth") smooth.iterables = ("fwhm", fwhm) # Artifact Detection - determines outliers in functional images art = Node(ArtifactDetect(norm_threshold=2, zintensity_threshold=3, mask_type='spm_global', parameter_source='FSL', use_differences=[True, False], plot_type='svg'), name="art") # BET - Skullstrip anatomical Image bet_anat = Node(BET(frac=0.5, robust=True, output_type='NIFTI_GZ'), name="bet_anat") # FAST - Image Segmentation segmentation = Node(FAST(output_type='NIFTI_GZ'), name="segmentation", mem_gb=4) # Select WM segmentation file from segmentation output def get_wm(files): return files[-1] # Threshold - Threshold WM probability image threshold = Node(Threshold(thresh=0.5, args='-bin', output_type='NIFTI_GZ'), name="threshold") # FLIRT - pre-alignment of functional images to anatomical images coreg_pre = Node(FLIRT(dof=6, output_type='NIFTI_GZ'), name="coreg_pre") # FLIRT - coregistration of functional images to anatomical images with BBR coreg_bbr = Node(FLIRT(dof=6, cost='bbr', schedule=opj(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch'), output_type='NIFTI_GZ'), name="coreg_bbr") # Apply coregistration warp to functional images applywarp = Node(FLIRT(interp='spline', apply_isoxfm=iso_size, output_type='NIFTI'), name="applywarp") # Apply coregistration warp to mean file applywarp_mean = Node(FLIRT(interp='spline', apply_isoxfm=iso_size, output_type='NIFTI_GZ'), name="applywarp_mean") # Create a coregistration workflow coregwf = Workflow(name='coregwf') coregwf.base_dir = opj(experiment_dir, working_dir) # Connect all components of the coregistration workflow coregwf.connect([ (bet_anat, segmentation, [('out_file', 'in_files')]), (segmentation, threshold, [(('partial_volume_files', get_wm), 'in_file')]), (bet_anat, coreg_pre, [('out_file', 'reference')]), (threshold, coreg_bbr, [('out_file', 'wm_seg')]), (coreg_pre, coreg_bbr, [('out_matrix_file', 'in_matrix_file')]), (coreg_bbr, applywarp, [('out_matrix_file', 'in_matrix_file')]), (bet_anat, applywarp, [('out_file', 'reference')]), (coreg_bbr, applywarp_mean, [('out_matrix_file', 'in_matrix_file')]), (bet_anat, applywarp_mean, [('out_file', 'reference')]), ]) # Infosource - a function free node to iterate over the list of subject names infosource = Node(IdentityInterface(fields=['subject_id', 'task_name']), name="infosource") infosource.iterables = [('subject_id', subject_list), ('task_name', task_list)] # SelectFiles - to grab the data (alternativ to DataGrabber) anat_file = opj('sub-{subject_id}', 'anat', 'sub-{subject_id}_T1w.nii.gz') func_file = opj('sub-{subject_id}', 'func', 'sub-{subject_id}_task-{task_name}_bold.nii.gz') templates = {'anat': anat_file, 'func': func_file} selectfiles = Node(SelectFiles(templates, base_directory=info["base directory"]), name="selectfiles") # Datasink - creates output folder for important outputs datasink = Node(DataSink(base_directory=experiment_dir, container=output_dir), name="datasink") ## Use the following DataSink output substitutions substitutions = [ ('_subject_id_', 'sub-'), ('_task_name_', '/task-'), ('_fwhm_', 'fwhm-'), ('_roi', ''), ('_mcf', ''), ('_st', ''), ('_flirt', ''), ('.nii_mean_reg', '_mean'), ('.nii.par', '.par'), ] subjFolders = [('fwhm-%s/' % f, 'fwhm-%s_' % f) for f in fwhm] substitutions.extend(subjFolders) datasink.inputs.substitutions = substitutions # Create a preprocessing workflow preproc = Workflow(name='preproc') preproc.base_dir = opj(experiment_dir, working_dir) # Connect all components of the preprocessing workflow preproc.connect([ (infosource, selectfiles, [('subject_id', 'subject_id'), ('task_name', 'task_name')]), (selectfiles, extract, [('func', 'in_file')]), (extract, slicetime, [('roi_file', 'in_files')]), (slicetime, mcflirt, [('timecorrected_files', 'in_file')]), (selectfiles, coregwf, [('anat', 'bet_anat.in_file'), ('anat', 'coreg_bbr.reference')]), (mcflirt, coregwf, [('mean_img', 'coreg_pre.in_file'), ('mean_img', 'coreg_bbr.in_file'), ('mean_img', 'applywarp_mean.in_file')]), (mcflirt, coregwf, [('out_file', 'applywarp.in_file')]), (coregwf, smooth, [('applywarp.out_file', 'in_files')]), (mcflirt, datasink, [('par_file', 'preproc.@par')]), (smooth, datasink, [('smoothed_files', 'preproc.@smooth')]), (coregwf, datasink, [('applywarp_mean.out_file', 'preproc.@mean')]), (coregwf, art, [('applywarp.out_file', 'realigned_files')]), (mcflirt, art, [('par_file', 'realignment_parameters')]), (coregwf, datasink, [('coreg_bbr.out_matrix_file', 'preproc.@mat_file'), ('bet_anat.out_file', 'preproc.@brain')]), (art, datasink, [('outlier_files', 'preproc.@outlier_files'), ('plot_files', 'preproc.@plot_files')]), ]) # Create preproc output graph# Creat # Create preproc.write_graph(graph2use='colored', format='png', simple_form=True) # Visualize the graph img1 = imread(opj(preproc.base_dir, 'preproc', 'graph.png')) plt.imshow(img1) plt.xticks([]), plt.yticks([]) plt.show() # Visualize the detailed graph# Visua # Visual preproc.write_graph(graph2use='flat', format='png', simple_form=True) img2 = imread(opj(preproc.base_dir, 'preproc', 'graph_detailed.png')) plt.imshow(img2) plt.xticks([]), plt.yticks([]) plt.show() print("Workflow all set. Check the workflow image :)") response = input('Should run the workflow? Enter yes or no :') if response == 'yes': preproc.run('MultiProc', plugin_args={'n_procs': 10}) elif response == 'no': print('Exits the program since you entered no') else: raise RuntimeError('Should enter either yes or no')
def Couple_Preproc_Pipeline(base_dir=None, output_dir=None, subject_id=None, spm_path=None): """ Create a preprocessing workflow for the Couples Conflict Study using nipype Args: base_dir: path to data folder where raw subject folder is located output_dir: path to where key output files should be saved subject_id: subject_id (str) spm_path: path to spm folder Returns: workflow: a nipype workflow that can be run """ from nipype.interfaces.dcm2nii import Dcm2nii from nipype.interfaces.fsl import Merge, TOPUP, ApplyTOPUP import nipype.interfaces.io as nio import nipype.interfaces.utility as util from nipype.interfaces.utility import Merge as Merge_List from nipype.pipeline.engine import Node, Workflow from nipype.interfaces.fsl.maths import UnaryMaths from nipype.interfaces.nipy.preprocess import Trim from nipype.algorithms.rapidart import ArtifactDetect from nipype.interfaces import spm from nipype.interfaces.spm import Normalize12 from nipype.algorithms.misc import Gunzip from nipype.interfaces.nipy.preprocess import ComputeMask import nipype.interfaces.matlab as mlab from nltools.utils import get_resource_path, get_vox_dims, get_n_volumes from nltools.interfaces import Plot_Coregistration_Montage, PlotRealignmentParameters, Create_Covariates import os import glob ######################################## ## Setup Paths and Nodes ######################################## # Specify Paths canonical_file = os.path.join(spm_path, 'canonical', 'single_subj_T1.nii') template_file = os.path.join(spm_path, 'tpm', 'TPM.nii') # Set the way matlab should be called mlab.MatlabCommand.set_default_matlab_cmd("matlab -nodesktop -nosplash") mlab.MatlabCommand.set_default_paths(spm_path) # Get File Names for different types of scans. Parse into separate processing streams datasource = Node(interface=nio.DataGrabber( infields=['subject_id'], outfields=['struct', 'ap', 'pa']), name='datasource') datasource.inputs.base_directory = base_dir datasource.inputs.template = '*' datasource.inputs.field_template = { 'struct': '%s/Study*/t1w_32ch_mpr_08mm*', 'ap': '%s/Study*/distortion_corr_32ch_ap*', 'pa': '%s/Study*/distortion_corr_32ch_pa*' } datasource.inputs.template_args = { 'struct': [['subject_id']], 'ap': [['subject_id']], 'pa': [['subject_id']] } datasource.inputs.subject_id = subject_id datasource.inputs.sort_filelist = True # iterate over functional scans to define paths scan_file_list = glob.glob( os.path.join(base_dir, subject_id, 'Study*', '*')) func_list = [s for s in scan_file_list if "romcon_ap_32ch_mb8" in s] func_list = [s for s in func_list if "SBRef" not in s] # Exclude sbref for now. func_source = Node(interface=util.IdentityInterface(fields=['scan']), name="func_source") func_source.iterables = ('scan', func_list) # Create Separate Converter Nodes for each different type of file. (dist corr scans need to be done before functional) ap_dcm2nii = Node(interface=Dcm2nii(), name='ap_dcm2nii') ap_dcm2nii.inputs.gzip_output = True ap_dcm2nii.inputs.output_dir = '.' ap_dcm2nii.inputs.date_in_filename = False pa_dcm2nii = Node(interface=Dcm2nii(), name='pa_dcm2nii') pa_dcm2nii.inputs.gzip_output = True pa_dcm2nii.inputs.output_dir = '.' pa_dcm2nii.inputs.date_in_filename = False f_dcm2nii = Node(interface=Dcm2nii(), name='f_dcm2nii') f_dcm2nii.inputs.gzip_output = True f_dcm2nii.inputs.output_dir = '.' f_dcm2nii.inputs.date_in_filename = False s_dcm2nii = Node(interface=Dcm2nii(), name='s_dcm2nii') s_dcm2nii.inputs.gzip_output = True s_dcm2nii.inputs.output_dir = '.' s_dcm2nii.inputs.date_in_filename = False ######################################## ## Setup Nodes for distortion correction ######################################## # merge output files into list merge_to_file_list = Node(interface=Merge_List(2), infields=['in1', 'in2'], name='merge_to_file_list') # fsl merge AP + PA files (depends on direction) merger = Node(interface=Merge(dimension='t'), name='merger') merger.inputs.output_type = 'NIFTI_GZ' # use topup to create distortion correction map topup = Node(interface=TOPUP(), name='topup') topup.inputs.encoding_file = os.path.join(get_resource_path(), 'epi_params_APPA_MB8.txt') topup.inputs.output_type = "NIFTI_GZ" topup.inputs.config = 'b02b0.cnf' # apply topup to all functional images apply_topup = Node(interface=ApplyTOPUP(), name='apply_topup') apply_topup.inputs.in_index = [1] apply_topup.inputs.encoding_file = os.path.join(get_resource_path(), 'epi_params_APPA_MB8.txt') apply_topup.inputs.output_type = "NIFTI_GZ" apply_topup.inputs.method = 'jac' apply_topup.inputs.interp = 'spline' # Clear out Zeros from spline interpolation using absolute value. abs_maths = Node(interface=UnaryMaths(), name='abs_maths') abs_maths.inputs.operation = 'abs' ######################################## ## Preprocessing ######################################## # Trim - remove first 10 TRs n_vols = 10 trim = Node(interface=Trim(), name='trim') trim.inputs.begin_index = n_vols #Realignment - 6 parameters - realign to first image of very first series. realign = Node(interface=spm.Realign(), name="realign") realign.inputs.register_to_mean = True #Coregister - 12 parameters coregister = Node(interface=spm.Coregister(), name="coregister") coregister.inputs.jobtype = 'estwrite' #Plot Realignment plot_realign = Node(interface=PlotRealignmentParameters(), name="plot_realign") #Artifact Detection art = Node(interface=ArtifactDetect(), name="art") art.inputs.use_differences = [True, False] art.inputs.use_norm = True art.inputs.norm_threshold = 1 art.inputs.zintensity_threshold = 3 art.inputs.mask_type = 'file' art.inputs.parameter_source = 'SPM' # Gunzip - unzip the functional and structural images gunzip_struc = Node(Gunzip(), name="gunzip_struc") gunzip_func = Node(Gunzip(), name="gunzip_func") # Normalize - normalizes functional and structural images to the MNI template normalize = Node(interface=Normalize12(jobtype='estwrite', tpm=template_file), name="normalize") #Plot normalization Check plot_normalization_check = Node(interface=Plot_Coregistration_Montage(), name="plot_normalization_check") plot_normalization_check.inputs.canonical_img = canonical_file #Create Mask compute_mask = Node(interface=ComputeMask(), name="compute_mask") #remove lower 5% of histogram of mean image compute_mask.inputs.m = .05 #Smooth #implicit masking (.im) = 0, dtype = 0 smooth = Node(interface=spm.Smooth(), name="smooth") smooth.inputs.fwhm = 6 #Create Covariate matrix make_cov = Node(interface=Create_Covariates(), name="make_cov") # Create a datasink to clean up output files datasink = Node(interface=nio.DataSink(), name='datasink') datasink.inputs.base_directory = output_dir datasink.inputs.container = subject_id ######################################## # Create Workflow ######################################## workflow = Workflow(name='Preprocessed') workflow.base_dir = os.path.join(base_dir, subject_id) workflow.connect([ (datasource, ap_dcm2nii, [('ap', 'source_dir')]), (datasource, pa_dcm2nii, [('pa', 'source_dir')]), (datasource, s_dcm2nii, [('struct', 'source_dir')]), (func_source, f_dcm2nii, [('scan', 'source_dir')]), (ap_dcm2nii, merge_to_file_list, [('converted_files', 'in1')]), (pa_dcm2nii, merge_to_file_list, [('converted_files', 'in2')]), (merge_to_file_list, merger, [('out', 'in_files')]), (merger, topup, [('merged_file', 'in_file')]), (topup, apply_topup, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar')]), (f_dcm2nii, trim, [('converted_files', 'in_file')]), (trim, apply_topup, [('out_file', 'in_files')]), (apply_topup, abs_maths, [('out_corrected', 'in_file')]), (abs_maths, gunzip_func, [('out_file', 'in_file')]), (gunzip_func, realign, [('out_file', 'in_files')]), (s_dcm2nii, gunzip_struc, [('converted_files', 'in_file')]), (gunzip_struc, coregister, [('out_file', 'source')]), (coregister, normalize, [('coregistered_source', 'image_to_align')]), (realign, coregister, [('mean_image', 'target'), ('realigned_files', 'apply_to_files')]), (realign, normalize, [(('mean_image', get_vox_dims), 'write_voxel_sizes')]), (coregister, normalize, [('coregistered_files', 'apply_to_files')]), (normalize, smooth, [('normalized_files', 'in_files')]), (realign, compute_mask, [('mean_image', 'mean_volume')]), (compute_mask, art, [('brain_mask', 'mask_file')]), (realign, art, [('realignment_parameters', 'realignment_parameters'), ('realigned_files', 'realigned_files')]), (realign, plot_realign, [('realignment_parameters', 'realignment_parameters')]), (normalize, plot_normalization_check, [('normalized_files', 'wra_img') ]), (realign, make_cov, [('realignment_parameters', 'realignment_parameters')]), (art, make_cov, [('outlier_files', 'spike_id')]), (normalize, datasink, [('normalized_files', 'structural.@normalize')]), (coregister, datasink, [('coregistered_source', 'structural.@struct') ]), (topup, datasink, [('out_fieldcoef', 'distortion.@fieldcoef')]), (topup, datasink, [('out_movpar', 'distortion.@movpar')]), (smooth, datasink, [('smoothed_files', 'functional.@smooth')]), (plot_realign, datasink, [('plot', 'functional.@plot_realign')]), (plot_normalization_check, datasink, [('plot', 'functional.@plot_normalization')]), (make_cov, datasink, [('covariates', 'functional.@covariates')]) ]) return workflow
name="motion_correction") # SliceTimer - correct for slice wise acquisition slicetimer = Node(SliceTimer(index_dir=False, interleaved=True, output_type='NIFTI', time_repetition=TR), name="slice_timing_correction") # Smooth - image smoothing smooth = Node(spm.Smooth(fwhm=fwhm), name="smooth") # Artifact Detection - determines outliers in functional images art = Node(ArtifactDetect(norm_threshold=2, zintensity_threshold=3, mask_type='spm_global', parameter_source='FSL', use_differences=[True, False], plot_type='svg'), name="artifact_detection") extract_confounds_ws_csf = Node(ExtractConfounds(out_file='ev_without_gs.csv'), name='extract_confounds_ws_csf') extract_confounds_gs = Node(ExtractConfounds(out_file='ev_with_gs.csv', delimiter=','), name='extract_confounds_global_signal') signal_extraction = Node(SignalExtraction( time_series_out_file='time_series.csv', correlation_matrix_out_file='correlation_matrix.png', atlas_identifier='cort-maxprob-thr25-2mm',
def run_dmriprep(dwi_file, bvec_file, bval_file, subjects_dir, working_dir, out_dir): """ Runs dmriprep for acquisitions with just one PE direction. """ from glob import glob import nibabel as nib import nipype.interfaces.freesurfer as fs import nipype.interfaces.fsl as fsl import nipype.interfaces.io as nio import nipype.interfaces.utility as niu import nipype.pipeline.engine as pe import numpy as np from nipype.algorithms.rapidart import ArtifactDetect from nipype.interfaces.dipy import DTI from nipype.interfaces.fsl.utils import AvScale from nipype.utils.filemanip import fname_presuffix from nipype.workflows.dmri.fsl.epi import create_dmri_preprocessing wf = create_dmri_preprocessing(name='dmriprep', use_fieldmap=False, fieldmap_registration=False) wf.inputs.inputnode.ref_num = 0 wf.inputs.inputnode.in_file = dwi_file wf.inputs.inputnode.in_bvec = bvec_file dwi_fname = op.split(dwi_file)[1].split(".nii.gz")[0] bids_sub_name = dwi_fname.split("_")[0] assert bids_sub_name.startswith("sub-") # inputnode = wf.get_node("inputnode") outputspec = wf.get_node("outputnode") # QC: FLIRT translation and rotation parameters flirt = wf.get_node("motion_correct.coregistration") # flirt.inputs.save_mats = True get_tensor = pe.Node(DTI(), name="dipy_tensor") wf.connect(outputspec, "dmri_corrected", get_tensor, "in_file") # wf.connect(inputspec2,"bvals", get_tensor, "in_bval") get_tensor.inputs.in_bval = bval_file wf.connect(outputspec, "bvec_rotated", get_tensor, "in_bvec") scale_tensor = pe.Node(name='scale_tensor', interface=fsl.BinaryMaths()) scale_tensor.inputs.operation = 'mul' scale_tensor.inputs.operand_value = 1000 wf.connect(get_tensor, 'out_file', scale_tensor, 'in_file') fslroi = pe.Node(fsl.ExtractROI(t_min=0, t_size=1), name="fslroi") wf.connect(outputspec, "dmri_corrected", fslroi, "in_file") bbreg = pe.Node(fs.BBRegister(contrast_type="t2", init="fsl", out_fsl_file=True, subjects_dir=subjects_dir, epi_mask=True), name="bbreg") # wf.connect(inputspec2,"fsid", bbreg,"subject_id") bbreg.inputs.subject_id = 'freesurfer' # bids_sub_name wf.connect(fslroi, "roi_file", bbreg, "source_file") voltransform = pe.Node(fs.ApplyVolTransform(inverse=True), iterfield=['source_file', 'reg_file'], name='transform') voltransform.inputs.subjects_dir = subjects_dir vt2 = voltransform.clone("transform_aparcaseg") vt2.inputs.interp = "nearest" def binarize_aparc(aparc_aseg): img = nib.load(aparc_aseg) data, aff = img.get_data(), img.affine outfile = fname_presuffix(aparc_aseg, suffix="bin.nii.gz", newpath=op.abspath("."), use_ext=False) nib.Nifti1Image((data > 0).astype(float), aff).to_filename(outfile) return outfile # wf.connect(inputspec2, "mask_nii", voltransform, "target_file") create_mask = pe.Node(niu.Function(input_names=["aparc_aseg"], output_names=["outfile"], function=binarize_aparc), name="bin_aparc") def get_aparc_aseg(subjects_dir, sub): return op.join(subjects_dir, sub, "mri", "aparc+aseg.mgz") create_mask.inputs.aparc_aseg = get_aparc_aseg(subjects_dir, 'freesurfer') wf.connect(create_mask, "outfile", voltransform, "target_file") wf.connect(fslroi, "roi_file", voltransform, "source_file") wf.connect(bbreg, "out_reg_file", voltransform, "reg_file") vt2.inputs.target_file = get_aparc_aseg(subjects_dir, 'freesurfer') # wf.connect(inputspec2, "aparc_aseg", vt2, "target_file") wf.connect(fslroi, "roi_file", vt2, "source_file") wf.connect(bbreg, "out_reg_file", vt2, "reg_file") # AK (2017): THIS NODE MIGHT NOT BE NECESSARY # AK (2018) doesn't know why she said that above.. threshold2 = pe.Node(fs.Binarize(min=0.5, out_type='nii.gz', dilate=1), iterfield=['in_file'], name='threshold2') wf.connect(voltransform, "transformed_file", threshold2, "in_file") # wf.connect(getmotion, "motion_params", datasink, "dti.@motparams") def get_flirt_motion_parameters(flirt_out_mats): def get_params(A): """This is a copy of spm's spm_imatrix where we already know the rotations and translations matrix, shears and zooms (as outputs from fsl FLIRT/avscale) Let A = the 4x4 rotation and translation matrix R = [ c5*c6, c5*s6, s5] [-s4*s5*c6-c4*s6, -s4*s5*s6+c4*c6, s4*c5] [-c4*s5*c6+s4*s6, -c4*s5*s6-s4*c6, c4*c5] """ def rang(b): a = min(max(b, -1), 1) return a Ry = np.arcsin(A[0, 2]) # Rx = np.arcsin(A[1, 2] / np.cos(Ry)) # Rz = np.arccos(A[0, 1] / np.sin(Ry)) if (abs(Ry) - np.pi / 2)**2 < 1e-9: Rx = 0 Rz = np.arctan2(-rang(A[1, 0]), rang(-A[2, 0] / A[0, 2])) else: c = np.cos(Ry) Rx = np.arctan2(rang(A[1, 2] / c), rang(A[2, 2] / c)) Rz = np.arctan2(rang(A[0, 1] / c), rang(A[0, 0] / c)) rotations = [Rx, Ry, Rz] translations = [A[0, 3], A[1, 3], A[2, 3]] return rotations, translations motion_params = open(op.abspath('motion_parameters.par'), 'w') for mat in flirt_out_mats: res = AvScale(mat_file=mat).run() A = np.asarray(res.outputs.rotation_translation_matrix) rotations, translations = get_params(A) for i in rotations + translations: motion_params.write('%f ' % i) motion_params.write('\n') motion_params.close() motion_params = op.abspath('motion_parameters.par') return motion_params getmotion = pe.Node(niu.Function(input_names=["flirt_out_mats"], output_names=["motion_params"], function=get_flirt_motion_parameters), name="get_motion_parameters", iterfield="flirt_out_mats") wf.connect(flirt, "out_matrix_file", getmotion, "flirt_out_mats") art = pe.Node(interface=ArtifactDetect(), name="art") art.inputs.use_differences = [True, True] art.inputs.save_plot = False art.inputs.use_norm = True art.inputs.norm_threshold = 3 art.inputs.zintensity_threshold = 9 art.inputs.mask_type = 'spm_global' art.inputs.parameter_source = 'FSL' wf.connect(getmotion, "motion_params", art, "realignment_parameters") wf.connect(outputspec, "dmri_corrected", art, "realigned_files") datasink = pe.Node(nio.DataSink(), name="sinker") datasink.inputs.base_directory = out_dir datasink.inputs.substitutions = [ ("vol0000_flirt_merged.nii.gz", dwi_fname + '.nii.gz'), ("stats.vol0000_flirt_merged.txt", dwi_fname + ".art.json"), ("motion_parameters.par", dwi_fname + ".motion.txt"), ("_rotated.bvec", ".bvec"), ("aparc+aseg_warped_out", dwi_fname.replace("_dwi", "_aparc+aseg")), ("art.vol0000_flirt_merged_outliers.txt", dwi_fname + ".outliers.txt"), ("vol0000_flirt_merged", dwi_fname), ("_roi_bbreg_freesurfer", "_register"), ("aparc+asegbin_warped_thresh", dwi_fname.replace("_dwi", "_mask")), ("derivatives/dmriprep", "derivatives/{}/dmriprep".format(bids_sub_name)) ] wf.connect(art, "statistic_files", datasink, "dmriprep.art.@artstat") wf.connect(art, "outlier_files", datasink, "dmriprep.art.@artoutlier") wf.connect(outputspec, "dmri_corrected", datasink, "dmriprep.dwi.@corrected") wf.connect(outputspec, "bvec_rotated", datasink, "dmriprep.dwi.@rotated") wf.connect(getmotion, "motion_params", datasink, "dmriprep.art.@motion") wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@tensor") wf.connect(get_tensor, "fa_file", datasink, "dmriprep.dti.@fa") wf.connect(get_tensor, "md_file", datasink, "dmriprep.dti.@md") wf.connect(get_tensor, "ad_file", datasink, "dmriprep.dti.@ad") wf.connect(get_tensor, "rd_file", datasink, "dmriprep.dti.@rd") wf.connect(get_tensor, "out_file", datasink, "dmriprep.dti.@scaled_tensor") wf.connect(bbreg, "min_cost_file", datasink, "dmriprep.reg.@mincost") wf.connect(bbreg, "out_fsl_file", datasink, "dmriprep.reg.@fslfile") wf.connect(bbreg, "out_reg_file", datasink, "dmriprep.reg.@reg") wf.connect(threshold2, "binary_file", datasink, "dmriprep.anat.@mask") # wf.connect(vt2, "transformed_file", datasink, "dwi.@aparc_aseg") convert = pe.Node(fs.MRIConvert(out_type="niigz"), name="convert2nii") wf.connect(vt2, "transformed_file", convert, "in_file") wf.connect(convert, "out_file", datasink, "dmriprep.anat.@aparc_aseg") wf.base_dir = working_dir wf.run() copyfile( bval_file, op.join(out_dir, bids_sub_name, "dmriprep", "dwi", op.split(bval_file)[1])) dmri_corrected = glob(op.join(out_dir, '*/dmriprep/dwi', '*.nii.gz'))[0] bvec_rotated = glob(op.join(out_dir, '*/dmriprep/dwi', '*.bvec'))[0] bval_file = glob(op.join(out_dir, '*/dmriprep/dwi', '*.bval'))[0] art_file = glob(op.join(out_dir, '*/dmriprep/art', '*.art.json'))[0] motion_file = glob(op.join(out_dir, '*/dmriprep/art', '*.motion.txt'))[0] outlier_file = glob(op.join(out_dir, '*/dmriprep/art', '*.outliers.txt'))[0] return dmri_corrected, bvec_rotated, art_file, motion_file, outlier_file
interleaved_order = list(range(1, number_of_slices + 1, 2)) + list( range(2, number_of_slices + 1, 2)) sliceTiming = Node(SliceTiming(num_slices=number_of_slices, time_repetition=TR, time_acquisition=TR - TR / number_of_slices, slice_order=interleaved_order, ref_slice=number_of_slices // 2), name="sliceTiming") # Realign - correct for motion realign = Node(Realign(register_to_mean=True), name="realign") # Artifact Detection - determine which of the images in the functional series # are outliers. This is based on deviation in intensity or movement. art = Node(ArtifactDetect(norm_threshold=1, zintensity_threshold=3, mask_type='file', parameter_source='SPM', use_differences=[True, False]), name="art") #Gunzip - unzip anatomical gunzip2 = Node(Gunzip(), name="gunzip2") gunzip = Node(Gunzip(), name="gunzip") sliceTiming = Node(SliceTiming(num_slices=number_of_slices, time_repetition=TR, time_acquisition=TR - TR / number_of_slices, slice_order=interleaved_order, ref_slice=19), name="sliceTiming")
def TV_Preproc_Pipeline(base_dir=None, output_dir=None, subject_id=None, spm_path=None): """ Create a preprocessing workflow for the Couples Conflict Study using nipype Args: base_dir: path to data folder where raw subject folder is located output_dir: path to where key output files should be saved subject_id: subject_id (str) spm_path: path to spm folder Returns: workflow: a nipype workflow that can be run """ import nipype.interfaces.io as nio import nipype.interfaces.utility as util from nipype.interfaces.utility import Merge as Merge_List from nipype.pipeline.engine import Node, Workflow from nipype.interfaces.fsl.maths import UnaryMaths from nipype.interfaces.nipy.preprocess import Trim from nipype.algorithms.rapidart import ArtifactDetect from nipype.interfaces import spm from nipype.interfaces.spm import Normalize12 from nipype.algorithms.misc import Gunzip from nipype.interfaces.nipy.preprocess import ComputeMask import nipype.interfaces.matlab as mlab from nltools.utils import get_resource_path, get_vox_dims, get_n_volumes from nltools.interfaces import Plot_Coregistration_Montage, PlotRealignmentParameters, Create_Covariates, Plot_Quality_Control import os import glob ######################################## ## Setup Paths and Nodes ######################################## # Specify Paths canonical_file = os.path.join(spm_path, 'canonical', 'single_subj_T1.nii') template_file = os.path.join(spm_path, 'tpm', 'TPM.nii') # Set the way matlab should be called mlab.MatlabCommand.set_default_matlab_cmd("matlab -nodesktop -nosplash") mlab.MatlabCommand.set_default_paths(spm_path) # Get File Names for different types of scans. Parse into separate processing streams datasource = Node(interface=nio.DataGrabber(infields=['subject_id'], outfields=['struct', 'func']), name='datasource') datasource.inputs.base_directory = base_dir datasource.inputs.template = '*' datasource.inputs.field_template = { 'struct': '%s/T1.nii.gz', 'func': '%s/*ep*.nii.gz' } datasource.inputs.template_args = { 'struct': [['subject_id']], 'func': [['subject_id']] } datasource.inputs.subject_id = subject_id datasource.inputs.sort_filelist = True # iterate over functional scans to define paths func_source = Node(interface=util.IdentityInterface(fields=['scan']), name="func_source") func_source.iterables = ('scan', glob.glob( os.path.join(base_dir, subject_id, '*ep*nii.gz'))) ######################################## ## Preprocessing ######################################## # Trim - remove first 5 TRs n_vols = 5 trim = Node(interface=Trim(), name='trim') trim.inputs.begin_index = n_vols #Realignment - 6 parameters - realign to first image of very first series. realign = Node(interface=spm.Realign(), name="realign") realign.inputs.register_to_mean = True #Coregister - 12 parameters coregister = Node(interface=spm.Coregister(), name="coregister") coregister.inputs.jobtype = 'estwrite' #Plot Realignment plot_realign = Node(interface=PlotRealignmentParameters(), name="plot_realign") #Artifact Detection art = Node(interface=ArtifactDetect(), name="art") art.inputs.use_differences = [True, False] art.inputs.use_norm = True art.inputs.norm_threshold = 1 art.inputs.zintensity_threshold = 3 art.inputs.mask_type = 'file' art.inputs.parameter_source = 'SPM' # Gunzip - unzip the functional and structural images gunzip_struc = Node(Gunzip(), name="gunzip_struc") gunzip_func = Node(Gunzip(), name="gunzip_func") # Normalize - normalizes functional and structural images to the MNI template normalize = Node(interface=Normalize12(jobtype='estwrite', tpm=template_file), name="normalize") #Plot normalization Check plot_normalization_check = Node(interface=Plot_Coregistration_Montage(), name="plot_normalization_check") plot_normalization_check.inputs.canonical_img = canonical_file #Create Mask compute_mask = Node(interface=ComputeMask(), name="compute_mask") #remove lower 5% of histogram of mean image compute_mask.inputs.m = .05 #Smooth #implicit masking (.im) = 0, dtype = 0 smooth = Node(interface=spm.Smooth(), name="smooth") smooth.inputs.fwhm = 6 #Create Covariate matrix make_cov = Node(interface=Create_Covariates(), name="make_cov") #Plot Quality Control Check quality_control = Node(interface=Plot_Quality_Control(), name='quality_control') # Create a datasink to clean up output files datasink = Node(interface=nio.DataSink(), name='datasink') datasink.inputs.base_directory = output_dir datasink.inputs.container = subject_id ######################################## # Create Workflow ######################################## workflow = Workflow(name='Preprocessed') workflow.base_dir = os.path.join(base_dir, subject_id) workflow.connect([ (datasource, gunzip_struc, [('struct', 'in_file')]), (func_source, trim, [('scan', 'in_file')]), (trim, gunzip_func, [('out_file', 'in_file')]), (gunzip_func, realign, [('out_file', 'in_files')]), (realign, quality_control, [('realigned_files', 'dat_img')]), (gunzip_struc, coregister, [('out_file', 'source')]), (coregister, normalize, [('coregistered_source', 'image_to_align')]), (realign, coregister, [('mean_image', 'target'), ('realigned_files', 'apply_to_files')]), (realign, normalize, [(('mean_image', get_vox_dims), 'write_voxel_sizes')]), (coregister, normalize, [('coregistered_files', 'apply_to_files')]), (normalize, smooth, [('normalized_files', 'in_files')]), (realign, compute_mask, [('mean_image', 'mean_volume')]), (compute_mask, art, [('brain_mask', 'mask_file')]), (realign, art, [('realignment_parameters', 'realignment_parameters'), ('realigned_files', 'realigned_files')]), (realign, plot_realign, [('realignment_parameters', 'realignment_parameters')]), (normalize, plot_normalization_check, [('normalized_files', 'wra_img') ]), (realign, make_cov, [('realignment_parameters', 'realignment_parameters')]), (art, make_cov, [('outlier_files', 'spike_id')]), (normalize, datasink, [('normalized_files', 'structural.@normalize')]), (coregister, datasink, [('coregistered_source', 'structural.@struct') ]), (smooth, datasink, [('smoothed_files', 'functional.@smooth')]), (plot_realign, datasink, [('plot', 'functional.@plot_realign')]), (plot_normalization_check, datasink, [('plot', 'functional.@plot_normalization')]), (make_cov, datasink, [('covariates', 'functional.@covariates')]), (quality_control, datasink, [('plot', 'functional.@quality_control')]) ]) return workflow
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(interface=spm.Realign(), name="realign") realign.inputs.jobtype = 'estwrite' num_slices = len(slice_times) slice_timing = Node(interface=spm.SliceTiming(), name="slice_timing") slice_timing.inputs.num_slices = num_slices slice_timing.inputs.time_repetition = TR slice_timing.inputs.time_acquisition = TR - TR / float(num_slices) slice_timing.inputs.slice_order = (np.argsort(slice_times) + 1).tolist() slice_timing.inputs.ref_slice = int(num_slices / 2) # Comute TSNR on realigned data regressing polynomials upto order 2 tsnr = MapNode(TSNR(regress_poly=2), iterfield=['in_file'], name='tsnr') wf.connect(slice_timing, 'timecorrected_files', tsnr, 'in_file') # Compute the median image across runs calc_median = Node(Function(input_names=['in_files'], output_names=['median_file'], function=median, imports=imports), 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 """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 = 'SPM' """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_files')]), (realign, slice_timing, [('realigned_files', 'in_files')]), (slice_timing, art, [('timecorrected_files', 'realigned_files')]), (realign, art, [('realignment_parameters', '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, 'realignment_parameters', 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', out_pf_name='pF_mcart.nii', demean=True), iterfield=['in_file', 'design', 'out_res_name'], name='filtermotion') wf.connect(slice_timing, 'timecorrected_files', filter1, 'in_file') wf.connect(slice_timing, ('timecorrected_files', rename, '_filtermotart'), filter1, 'out_res_name') wf.connect(createfilter1, 'out_files', filter1, 'design') createfilter2 = MapNode(Function(input_names=[ 'realigned_file', 'mask_file', 'num_components', 'extra_regressors' ], output_names=['out_files'], function=extract_noise_components, imports=imports), iterfield=['realigned_file', 'extra_regressors'], name='makecompcorrfilter') 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', out_pf_name='pF.nii', 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, 'out_files', 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.spm.Smooth`. """ smooth = Node(interface=spm.Smooth(), name="smooth") smooth.inputs.fwhm = vol_fwhm wf.connect(bandpass, 'out_files', smooth, 'in_files') collector = Node(Merge(2), name='collect_streams') wf.connect(smooth, 'smoothed_files', 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.inputs.terminal_output = 'file' warpall.inputs.reference_image = target_file warpall.inputs.args = '--float' warpall.inputs.num_threads = 1 # 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) out_names = [] for filename in files: _, name, _ = split_filename(filename) out_names.append(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', '')] regex_subs = [ ('_ts_masker.*/sar', '/smooth/'), ('_ts_masker.*/ar', '/unsmooth/'), ('_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, 'realignment_parameters', 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(bandpass, 'out_files', datasink, 'resting.timeseries.@bandpassed') wf.connect(smooth, 'smoothed_files', datasink, 'resting.timeseries.@smoothed') wf.connect(createfilter1, 'out_files', datasink, 'resting.regress.@regressors') wf.connect(createfilter2, 'out_files', 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_workflow(files, subject_id, n_vol=0, despike=True, TR=None, slice_times=None, slice_thickness=None, fieldmap_images=[], norm_threshold=1, num_components=6, vol_fwhm=None, surf_fwhm=None, lowpass_freq=-1, highpass_freq=-1, sink_directory=os.getcwd(), FM_TEdiff=2.46, FM_sigma=2, FM_echo_spacing=.7, target_subject=['fsaverage3', 'fsaverage4'], name='resting'): wf = Workflow(name=name) # Skip starting volumes remove_vol = MapNode(fsl.ExtractROI(t_min=n_vol, t_size=-1), iterfield=['in_file'], name="remove_volumes") remove_vol.inputs.in_file = files # Run AFNI's despike. This is always run, however, whether this is fed to # realign depends on the input configuration despiker = MapNode(afni.Despike(outputtype='NIFTI_GZ'), iterfield=['in_file'], name='despike') #despiker.plugin_args = {'qsub_args': '-l nodes=1:ppn='} wf.connect(remove_vol, 'roi_file', despiker, 'in_file') # Run Nipy joint slice timing and realignment algorithm realign = Node(nipy.SpaceTimeRealigner(), name='realign') realign.inputs.tr = TR realign.inputs.slice_times = slice_times realign.inputs.slice_info = 2 if despike: wf.connect(despiker, 'out_file', realign, 'in_file') else: wf.connect(remove_vol, 'roi_file', realign, 'in_file') # Comute TSNR on realigned data regressing polynomials upto 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(Function(input_names=['in_files'], output_names=['median_file'], function=median, imports=imports), name='median') wf.connect(tsnr, 'detrended_file', calc_median, 'in_files') # Coregister the median to the surface register = Node(freesurfer.BBRegister(), name='bbregister') register.inputs.subject_id = subject_id register.inputs.init = 'fsl' register.inputs.contrast_type = 't2' register.inputs.out_fsl_file = True register.inputs.epi_mask = True # Compute fieldmaps and unwarp using them if fieldmap_images: fieldmap = Node(interface=EPIDeWarp(), name='fieldmap_unwarp') fieldmap.inputs.tediff = FM_TEdiff fieldmap.inputs.esp = FM_echo_spacing fieldmap.inputs.sigma = FM_sigma fieldmap.inputs.mag_file = fieldmap_images[0] fieldmap.inputs.dph_file = fieldmap_images[1] wf.connect(calc_median, 'median_file', fieldmap, 'exf_file') dewarper = MapNode(interface=fsl.FUGUE(), iterfield=['in_file'], name='dewarper') wf.connect(tsnr, 'detrended_file', dewarper, 'in_file') wf.connect(fieldmap, 'exf_mask', dewarper, 'mask_file') wf.connect(fieldmap, 'vsm_file', dewarper, 'shift_in_file') wf.connect(fieldmap, 'exfdw', register, 'source_file') else: wf.connect(calc_median, 'median_file', register, 'source_file') # Get the subject's freesurfer source directory fssource = Node(FreeSurferSource(), name='fssource') fssource.inputs.subject_id = subject_id fssource.inputs.subjects_dir = os.environ['SUBJECTS_DIR'] # Extract wm+csf, brain masks by eroding freesurfer labels and then # transform the masks into the space of the median wmcsf = Node(freesurfer.Binarize(), name='wmcsfmask') mask = wmcsf.clone('anatmask') wmcsftransform = Node(freesurfer.ApplyVolTransform(inverse=True, interp='nearest'), name='wmcsftransform') wmcsftransform.inputs.subjects_dir = os.environ['SUBJECTS_DIR'] wmcsf.inputs.wm_ven_csf = True wmcsf.inputs.match = [4, 5, 14, 15, 24, 31, 43, 44, 63] wmcsf.inputs.binary_file = 'wmcsf.nii.gz' wmcsf.inputs.erode = int(np.ceil(slice_thickness)) wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), wmcsf, 'in_file') if fieldmap_images: wf.connect(fieldmap, 'exf_mask', wmcsftransform, 'source_file') else: wf.connect(calc_median, 'median_file', wmcsftransform, 'source_file') wf.connect(register, 'out_reg_file', wmcsftransform, 'reg_file') wf.connect(wmcsf, 'binary_file', wmcsftransform, 'target_file') mask.inputs.binary_file = 'mask.nii.gz' mask.inputs.dilate = int(np.ceil(slice_thickness)) + 1 mask.inputs.erode = int(np.ceil(slice_thickness)) mask.inputs.min = 0.5 wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), mask, 'in_file') masktransform = wmcsftransform.clone("masktransform") if fieldmap_images: wf.connect(fieldmap, 'exf_mask', masktransform, 'source_file') else: wf.connect(calc_median, 'median_file', masktransform, 'source_file') wf.connect(register, 'out_reg_file', masktransform, 'reg_file') wf.connect(mask, 'binary_file', masktransform, 'target_file') # Compute Art outliers art = Node(interface=ArtifactDetect(use_differences=[True, False], use_norm=True, norm_threshold=norm_threshold, zintensity_threshold=3, parameter_source='NiPy', bound_by_brainmask=True, save_plot=False, mask_type='file'), name="art") if fieldmap_images: wf.connect(dewarper, 'unwarped_file', art, 'realigned_files') else: wf.connect(tsnr, 'detrended_file', art, 'realigned_files') wf.connect(realign, 'par_file', art, 'realignment_parameters') wf.connect(masktransform, 'transformed_file', art, 'mask_file') # 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'], output_names=['out_files'], function=build_filter1, imports=imports), name='makemotionbasedfilter') wf.connect(motreg, 'out_files', createfilter1, 'motion_params') wf.connect(art, 'norm_files', createfilter1, 'comp_norm') wf.connect(art, 'outlier_files', createfilter1, 'outliers') # Filter the motion and art confounds filter1 = MapNode(fsl.GLM(out_res_name='timeseries.nii.gz', demean=True), iterfield=['in_file', 'design'], name='filtermotion') if fieldmap_images: wf.connect(dewarper, 'unwarped_file', filter1, 'in_file') else: wf.connect(tsnr, 'detrended_file', filter1, 'in_file') wf.connect(createfilter1, 'out_files', filter1, 'design') wf.connect(masktransform, 'transformed_file', filter1, 'mask') # Create a filter to remove noise components based on white matter and CSF createfilter2 = MapNode(Function( input_names=['realigned_file', 'mask_file', 'num_components'], output_names=['out_files'], function=extract_noise_components, imports=imports), iterfield=['realigned_file'], name='makecompcorrfilter') createfilter2.inputs.num_components = num_components wf.connect(filter1, 'out_res', createfilter2, 'realigned_file') wf.connect(masktransform, 'transformed_file', createfilter2, 'mask_file') # Filter noise components filter2 = MapNode(fsl.GLM(out_res_name='timeseries_cleaned.nii.gz', demean=True), iterfield=['in_file', 'design'], name='filtercompcorr') wf.connect(filter1, 'out_res', filter2, 'in_file') wf.connect(createfilter2, 'out_files', filter2, 'design') wf.connect(masktransform, 'transformed_file', filter2, 'mask') # Smoothing using surface and volume smoothing smooth = MapNode(freesurfer.Smooth(), iterfield=['in_file'], name='smooth') smooth.inputs.proj_frac_avg = (0.1, 0.9, 0.1) if surf_fwhm is None: surf_fwhm = 5 * slice_thickness smooth.inputs.surface_fwhm = surf_fwhm if vol_fwhm is None: vol_fwhm = 2 * slice_thickness smooth.inputs.vol_fwhm = vol_fwhm wf.connect(filter2, 'out_res', smooth, 'in_file') wf.connect(register, 'out_reg_file', smooth, 'reg_file') # Bandpass filter the data bandpass = MapNode(fsl.TemporalFilter(), iterfield=['in_file'], name='bandpassfilter') if highpass_freq < 0: bandpass.inputs.highpass_sigma = -1 else: bandpass.inputs.highpass_sigma = 1. / (2 * TR * highpass_freq) if lowpass_freq < 0: bandpass.inputs.lowpass_sigma = -1 else: bandpass.inputs.lowpass_sigma = 1. / (2 * TR * lowpass_freq) wf.connect(smooth, 'smoothed_file', bandpass, 'in_file') # Convert aparc to subject functional space aparctransform = wmcsftransform.clone("aparctransform") if fieldmap_images: wf.connect(fieldmap, 'exf_mask', aparctransform, 'source_file') else: wf.connect(calc_median, 'median_file', aparctransform, 'source_file') wf.connect(register, 'out_reg_file', aparctransform, 'reg_file') wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), aparctransform, 'target_file') # Sample the average time series in aparc ROIs sampleaparc = MapNode(freesurfer.SegStats(avgwf_txt_file=True, default_color_table=True), iterfield=['in_file'], name='aparc_ts') sampleaparc.inputs.segment_id = ([8] + range(10, 14) + [17, 18, 26, 47] + range(49, 55) + [58] + range(1001, 1036) + range(2001, 2036)) wf.connect(aparctransform, 'transformed_file', sampleaparc, 'segmentation_file') wf.connect(bandpass, 'out_file', sampleaparc, 'in_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.cortex_mask = True samplerlh.inputs.out_type = 'niigz' samplerlh.inputs.subjects_dir = os.environ['SUBJECTS_DIR'] samplerrh = samplerlh.clone('sampler_rh') samplerlh.inputs.hemi = 'lh' wf.connect(bandpass, 'out_file', samplerlh, 'source_file') wf.connect(register, 'out_reg_file', samplerlh, 'reg_file') wf.connect(target, 'target_subject', samplerlh, 'target_subject') samplerrh.set_input('hemi', 'rh') wf.connect(bandpass, 'out_file', samplerrh, 'source_file') wf.connect(register, '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') # Compute registration between the subject's structural and MNI template # This is currently set to perform a very quick registration. However, the # registration can be made significantly more accurate for cortical # structures by increasing the number of iterations # All parameters are set using the example from: # https://github.com/stnava/ANTs/blob/master/Scripts/newAntsExample.sh reg = Node(ants.Registration(), name='antsRegister') reg.inputs.output_transform_prefix = "output_" reg.inputs.transforms = ['Translation', 'Rigid', 'Affine', 'SyN'] reg.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.1, ), (0.2, 3.0, 0.0)] # reg.inputs.number_of_iterations = ([[10000, 111110, 11110]]*3 + # [[100, 50, 30]]) reg.inputs.number_of_iterations = [[100, 100, 100]] * 3 + [[100, 20, 10]] reg.inputs.dimension = 3 reg.inputs.write_composite_transform = True reg.inputs.collapse_output_transforms = False reg.inputs.metric = ['Mattes'] * 3 + [['Mattes', 'CC']] reg.inputs.metric_weight = [1] * 3 + [[0.5, 0.5]] reg.inputs.radius_or_number_of_bins = [32] * 3 + [[32, 4]] reg.inputs.sampling_strategy = ['Regular'] * 3 + [[None, None]] reg.inputs.sampling_percentage = [0.3] * 3 + [[None, None]] reg.inputs.convergence_threshold = [1.e-8] * 3 + [-0.01] reg.inputs.convergence_window_size = [20] * 3 + [5] reg.inputs.smoothing_sigmas = [[4, 2, 1]] * 3 + [[1, 0.5, 0]] reg.inputs.sigma_units = ['vox'] * 4 reg.inputs.shrink_factors = [[6, 4, 2]] + [[3, 2, 1]] * 2 + [[4, 2, 1]] reg.inputs.use_estimate_learning_rate_once = [True] * 4 reg.inputs.use_histogram_matching = [False] * 3 + [True] reg.inputs.output_warped_image = 'output_warped_image.nii.gz' reg.inputs.fixed_image = \ os.path.abspath('OASIS-30_Atropos_template_in_MNI152_2mm.nii.gz') reg.inputs.num_threads = 4 reg.plugin_args = {'qsub_args': '-l nodes=1:ppn=4'} # Convert T1.mgz to nifti for using with ANTS convert = Node(freesurfer.MRIConvert(out_type='niigz'), name='convert2nii') wf.connect(fssource, 'T1', convert, 'in_file') # Mask the T1.mgz file with the brain mask computed earlier maskT1 = Node(fsl.BinaryMaths(operation='mul'), name='maskT1') wf.connect(mask, 'binary_file', maskT1, 'operand_file') wf.connect(convert, 'out_file', maskT1, 'in_file') wf.connect(maskT1, 'out_file', reg, 'moving_image') # Convert the BBRegister transformation to ANTS ITK format convert2itk = MapNode(C3dAffineTool(), iterfield=['transform_file', 'source_file'], name='convert2itk') convert2itk.inputs.fsl2ras = True convert2itk.inputs.itk_transform = True wf.connect(register, 'out_fsl_file', convert2itk, 'transform_file') if fieldmap_images: wf.connect(fieldmap, 'exf_mask', convert2itk, 'source_file') else: wf.connect(calc_median, 'median_file', convert2itk, 'source_file') wf.connect(convert, 'out_file', convert2itk, 'reference_file') # Concatenate the affine and ants transforms into a list pickfirst = lambda x: x[0] merge = MapNode(Merge(2), iterfield=['in2'], name='mergexfm') wf.connect(convert2itk, 'itk_transform', merge, 'in2') wf.connect(reg, ('composite_transform', pickfirst), merge, 'in1') # Apply the combined transform to the time series file sample2mni = MapNode(ants.ApplyTransforms(), iterfield=['input_image', 'transforms'], name='sample2mni') sample2mni.inputs.input_image_type = 3 sample2mni.inputs.interpolation = 'BSpline' sample2mni.inputs.invert_transform_flags = [False, False] sample2mni.inputs.reference_image = \ os.path.abspath('OASIS-30_Atropos_template_in_MNI152_2mm.nii.gz') sample2mni.inputs.terminal_output = 'file' wf.connect(bandpass, 'out_file', sample2mni, 'input_image') wf.connect(merge, 'out', sample2mni, 'transforms') # 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] + range(10, 14) + [17, 18, 26, 47] +\ range(49, 55) + [58] ts2txt.inputs.label_file = \ os.path.abspath(('OASIS-TRT-20_jointfusion_DKT31_CMA_labels_in_MNI152_' '2mm.nii.gz')) wf.connect(sample2mni, 'output_image', ts2txt, 'timeseries_file') # 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 = [('_target_subject_', '')] datasink.inputs.regexp_substitutions = (r'(/_.*(\d+/))', r'/run\2') wf.connect(despiker, 'out_file', datasink, 'resting.qa.despike') wf.connect(realign, 'par_file', datasink, 'resting.qa.motion') wf.connect(tsnr, 'tsnr_file', datasink, 'resting.qa.tsnr') wf.connect(tsnr, 'mean_file', datasink, 'resting.qa.tsnr.@mean') wf.connect(tsnr, 'stddev_file', datasink, 'resting.qa.@tsnr_stddev') if fieldmap_images: wf.connect(fieldmap, 'exf_mask', datasink, 'resting.reference') else: wf.connect(calc_median, 'median_file', datasink, 'resting.reference') 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(mask, 'binary_file', datasink, 'resting.mask') wf.connect(masktransform, 'transformed_file', datasink, 'resting.mask.@transformed_file') wf.connect(register, 'out_reg_file', datasink, 'resting.registration.bbreg') wf.connect(reg, ('composite_transform', pickfirst), datasink, 'resting.registration.ants') wf.connect(register, 'min_cost_file', datasink, 'resting.qa.bbreg.@mincost') wf.connect(smooth, 'smoothed_file', datasink, 'resting.timeseries.fullpass') wf.connect(bandpass, 'out_file', datasink, 'resting.timeseries.bandpassed') wf.connect(sample2mni, 'output_image', datasink, 'resting.timeseries.mni') wf.connect(createfilter1, 'out_files', datasink, 'resting.regress.@regressors') wf.connect(createfilter2, 'out_files', datasink, 'resting.regress.@compcorr') 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 = [('_target_subject_', '')] datasink2.inputs.regexp_substitutions = (r'(/_.*(\d+/))', r'/run\2') wf.connect(combiner, 'out_file', datasink2, 'resting.parcellations.grayo.@surface') return wf
# structural is the 'in_file', output is 'binary_file' binarize_struct = Node(Binarize(dilate=mask_dilation, erode=mask_erosion, min=1), name='binarize_struct') # apply the binary mask to the functional data # functional is 'in_file', binary mask is 'mask_file', output is 'out_file' mask_func = Node(ApplyMask(), name='mask_func') # Artifact detection for scrubbing/motion assessment art = Node( ArtifactDetect( mask_type='file', parameter_source='FSL', norm_threshold= 0.5, #mutually exclusive with rotation and translation thresh zintensity_threshold=3, use_differences=[True, False]), name='art') def converthex_xform(orig_xform): from numpy import genfromtxt, savetxt from os.path import abspath orig_matrix = genfromtxt(orig_xform, delimiter=' ', dtype=None, skip_header=0) new_xform = 'brainmask_out_flirt.mat'
def run(base_dir): template = '/home/brainlab/Desktop/Rudas/Data/Parcellation/TPM.nii' matlab_cmd = '/home/brainlab/Desktop/Rudas/Tools/spm12_r7487/spm12/run_spm12.sh /home/brainlab/Desktop/Rudas/Tools/MCR/v713/ script' spm.SPMCommand.set_mlab_paths(matlab_cmd=matlab_cmd, use_mcr=True) print('SPM version: ' + str(spm.SPMCommand().version)) structural_dir = '/home/brainlab/Desktop/Rudas/Data/Propofol/Structurals/' experiment_dir = opj(base_dir, 'output/') output_dir = 'datasink' working_dir = 'workingdir' ''' subject_list = ['2014_05_02_02CB', '2014_05_16_16RA', '2014_05_30_30AQ', '2014_07_04_04HD'] ''' subject_list = [ '2014_05_02_02CB', '2014_05_16_16RA', '2014_05_30_30AQ', '2014_07_04_04HD', '2014_07_04_04SG', '2014_08_13_13CA', '2014_10_08_08BC', '2014_10_08_08VR', '2014_10_22_22CY', '2014_10_22_22TK', '2014_11_17_17EK', '2014_11_17_17NA', '2014_11_19_19SA', '2014_11_19_AK', '2014_11_25.25JK', '2014_11_27_27HF', '2014_12_10_10JR' ] # list of subject identifiers fwhm = 8 # Smoothing widths to apply (Gaussian kernel size) TR = 2 # Repetition time init_volume = 0 # Firts volumen identification which will use in the pipeline iso_size = 2 # Isometric resample of functional images to voxel size (in mm) # ExtractROI - skip dummy scans extract = Node(ExtractROI(t_min=init_volume, t_size=-1, output_type='NIFTI'), name="extract") # MCFLIRT - motion correction mcflirt = Node(MCFLIRT(mean_vol=True, save_plots=True, output_type='NIFTI'), name="motion_correction") # SliceTimer - correct for slice wise acquisition slicetimer = Node(SliceTimer(index_dir=False, interleaved=True, output_type='NIFTI', time_repetition=TR), name="slice_timing_correction") # Smooth - image smoothing smooth = Node(spm.Smooth(fwhm=fwhm), name="smooth") n4bias = Node(N4Bias(out_file='t1_n4bias.nii.gz'), name='n4bias') descomposition = Node(Descomposition(n_components=20, low_pass=0.1, high_pass=0.01, tr=TR), name='descomposition') # Artifact Detection - determines outliers in functional images art = Node(ArtifactDetect(norm_threshold=2, zintensity_threshold=3, mask_type='spm_global', parameter_source='FSL', use_differences=[True, False], plot_type='svg'), name="artifact_detection") extract_confounds_ws_csf = Node( ExtractConfounds(out_file='ev_without_gs.csv'), name='extract_confounds_ws_csf') extract_confounds_gs = Node(ExtractConfounds(out_file='ev_with_gs.csv', delimiter=','), name='extract_confounds_global_signal') signal_extraction = Node(SignalExtraction( time_series_out_file='time_series.csv', correlation_matrix_out_file='correlation_matrix.png', atlas_identifier='cort-maxprob-thr25-2mm', tr=TR, plot=True), name='signal_extraction') art_remotion = Node(ArtifacRemotion(out_file='fmri_art_removed.nii'), name='artifact_remotion') # BET - Skullstrip anatomical anf funtional images bet_t1 = Node(BET(frac=0.5, robust=True, mask=True, output_type='NIFTI_GZ'), name="bet_t1") # FAST - Image Segmentation segmentation = Node(FAST(output_type='NIFTI'), name="segmentation") # Normalize - normalizes functional and structural images to the MNI template normalize_fmri = Node(Normalize12(jobtype='estwrite', tpm=template, write_voxel_sizes=[2, 2, 2], write_bounding_box=[[-90, -126, -72], [90, 90, 108]]), name="normalize_fmri") gunzip = Node(Gunzip(), name="gunzip") normalize_t1 = Node(Normalize12( jobtype='estwrite', tpm=template, write_voxel_sizes=[iso_size, iso_size, iso_size], write_bounding_box=[[-90, -126, -72], [90, 90, 108]]), name="normalize_t1") normalize_masks = Node(Normalize12( jobtype='estwrite', tpm=template, write_voxel_sizes=[iso_size, iso_size, iso_size], write_bounding_box=[[-90, -126, -72], [90, 90, 108]]), name="normalize_masks") # Threshold - Threshold WM probability image threshold = Node(Threshold(thresh=0.5, args='-bin', output_type='NIFTI_GZ'), name="wm_mask_threshold") # FLIRT - pre-alignment of functional images to anatomical images coreg_pre = Node(FLIRT(dof=6, output_type='NIFTI_GZ'), name="linear_warp_estimation") # FLIRT - coregistration of functional images to anatomical images with BBR coreg_bbr = Node(FLIRT(dof=6, cost='bbr', schedule=opj(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch'), output_type='NIFTI_GZ'), name="nonlinear_warp_estimation") # Apply coregistration warp to functional images applywarp = Node(FLIRT(interp='spline', apply_isoxfm=iso_size, output_type='NIFTI'), name="registration_fmri") # Apply coregistration warp to mean file applywarp_mean = Node(FLIRT(interp='spline', apply_isoxfm=iso_size, output_type='NIFTI_GZ'), name="registration_mean_fmri") # Infosource - a function free node to iterate over the list of subject names infosource = Node(IdentityInterface(fields=['subject_id']), name="infosource") infosource.iterables = [('subject_id', subject_list)] # SelectFiles - to grab the data (alternativ to DataGrabber) anat_file = opj(structural_dir, '{subject_id}', 't1.nii') func_file = opj('{subject_id}', 'fmri.nii') templates = {'anat': anat_file, 'func': func_file} selectfiles = Node(SelectFiles(templates, base_directory=base_dir), name="selectfiles") # Datasink - creates output folder for important outputs datasink = Node(DataSink(base_directory=experiment_dir, container=output_dir), name="datasink") # Create a coregistration workflow coregwf = Workflow(name='coreg_fmri_to_t1') coregwf.base_dir = opj(experiment_dir, working_dir) # Create a preprocessing workflow preproc = Workflow(name='preproc') preproc.base_dir = opj(experiment_dir, working_dir) # Connect all components of the coregistration workflow coregwf.connect([ (bet_t1, n4bias, [('out_file', 'in_file')]), (n4bias, segmentation, [('out_file', 'in_files')]), (segmentation, threshold, [(('partial_volume_files', get_latest), 'in_file')]), (n4bias, coreg_pre, [('out_file', 'reference')]), (threshold, coreg_bbr, [('out_file', 'wm_seg')]), (coreg_pre, coreg_bbr, [('out_matrix_file', 'in_matrix_file')]), (coreg_bbr, applywarp, [('out_matrix_file', 'in_matrix_file')]), (n4bias, applywarp, [('out_file', 'reference')]), (coreg_bbr, applywarp_mean, [('out_matrix_file', 'in_matrix_file')]), (n4bias, applywarp_mean, [('out_file', 'reference')]), ]) ## Use the following DataSink output substitutions substitutions = [('_subject_id_', 'sub-')] # ('_fwhm_', 'fwhm-'), # ('_roi', ''), # ('_mcf', ''), # ('_st', ''), # ('_flirt', ''), # ('.nii_mean_reg', '_mean'), # ('.nii.par', '.par'), # ] #subjFolders = [('fwhm-%s/' % f, 'fwhm-%s_' % f) for f in fwhm] #substitutions.extend(subjFolders) datasink.inputs.substitutions = substitutions # Connect all components of the preprocessing workflow preproc.connect([ (infosource, selectfiles, [('subject_id', 'subject_id')]), (selectfiles, extract, [('func', 'in_file')]), (extract, mcflirt, [('roi_file', 'in_file')]), (mcflirt, slicetimer, [('out_file', 'in_file')]), (selectfiles, coregwf, [('anat', 'bet_t1.in_file'), ('anat', 'nonlinear_warp_estimation.reference') ]), (mcflirt, coregwf, [('mean_img', 'linear_warp_estimation.in_file'), ('mean_img', 'nonlinear_warp_estimation.in_file'), ('mean_img', 'registration_mean_fmri.in_file')]), (slicetimer, coregwf, [('slice_time_corrected_file', 'registration_fmri.in_file')]), (coregwf, art, [('registration_fmri.out_file', 'realigned_files')]), (mcflirt, art, [('par_file', 'realignment_parameters')]), (art, art_remotion, [('outlier_files', 'outlier_files')]), (coregwf, art_remotion, [('registration_fmri.out_file', 'in_file')]), (coregwf, gunzip, [('n4bias.out_file', 'in_file')]), (selectfiles, normalize_fmri, [('anat', 'image_to_align')]), (art_remotion, normalize_fmri, [('out_file', 'apply_to_files')]), (selectfiles, normalize_t1, [('anat', 'image_to_align')]), (gunzip, normalize_t1, [('out_file', 'apply_to_files')]), (selectfiles, normalize_masks, [('anat', 'image_to_align')]), (coregwf, normalize_masks, [(('segmentation.partial_volume_files', get_wm_csf), 'apply_to_files')]), (normalize_fmri, smooth, [('normalized_files', 'in_files')]), (smooth, extract_confounds_ws_csf, [('smoothed_files', 'in_file')]), (normalize_masks, extract_confounds_ws_csf, [('normalized_files', 'list_mask')]), (mcflirt, extract_confounds_ws_csf, [('par_file', 'file_concat')]), #(smooth, extract_confounds_gs, [('smoothed_files', 'in_file')]), #(normalize_t1, extract_confounds_gs, [(('normalized_files',change_to_list), 'list_mask')]), #(extract_confounds_ws_csf, extract_confounds_gs, [('out_file', 'file_concat')]), (smooth, signal_extraction, [('smoothed_files', 'in_file')]), #(extract_confounds_gs, signal_extraction, [('out_file', 'confounds_file')]), (extract_confounds_ws_csf, signal_extraction, [('out_file', 'confounds_file')]), #(smooth, descomposition, [('smoothed_files', 'in_file')]), #(extract_confounds_ws_csf, descomposition, [('out_file', 'confounds_file')]), #(extract_confounds_gs, datasink, [('out_file', 'preprocessing.@confounds_with_gs')]), (extract_confounds_ws_csf, datasink, [('out_file', 'preprocessing.@confounds_without_gs')]), (smooth, datasink, [('smoothed_files', 'preprocessing.@smoothed')]), (normalize_fmri, datasink, [('normalized_files', 'preprocessing.@fmri_normalized')]), (normalize_t1, datasink, [('normalized_files', 'preprocessing.@t1_normalized')]), (normalize_masks, datasink, [('normalized_files', 'preprocessing.@masks_normalized')]), (signal_extraction, datasink, [('time_series_out_file', 'preprocessing.@time_serie')]), (signal_extraction, datasink, [('correlation_matrix_out_file', 'preprocessing.@correlation_matrix')]), (signal_extraction, datasink, [('fmri_cleaned_out_file', 'preprocessing.@fmri_cleaned_out_file')]), #(descomposition, datasink, [('out_file', 'preprocessing.@descomposition')]), #(descomposition, datasink, [('plot_files', 'preprocessing.@descomposition_plot_files')]) ]) preproc.write_graph(graph2use='colored', format='png', simple_form=True) preproc.run()
def wfmaker(project_dir, raw_dir, subject_id, task_name='', apply_trim=False, apply_dist_corr=False, apply_smooth=False, apply_filter=False, mni_template='2mm', apply_n4=True, ants_threads=8, readable_crash_files=False): """ This function returns a "standard" workflow based on requested settings. Assumes data is in the following directory structure in BIDS format: *Work flow steps*: 1) EPI Distortion Correction (FSL; optional) 2) Trimming (nipy) 3) Realignment/Motion Correction (FSL) 4) Artifact Detection (rapidART/python) 5) Brain Extraction + N4 Bias Correction (ANTs) 6) Coregistration (rigid) (ANTs) 7) Normalization to MNI (non-linear) (ANTs) 8) Low-pass filtering (nilearn; optional) 8) Smoothing (FSL; optional) 9) Downsampling to INT16 precision to save space (nibabel) Args: project_dir (str): full path to the root of project folder, e.g. /my/data/myproject. All preprocessed data will be placed under this foler and the raw_dir folder will be searched for under this folder raw_dir (str): folder name for raw data, e.g. 'raw' which would be automatically converted to /my/data/myproject/raw subject_id (str/int): subject ID to process. Can be either a subject ID string e.g. 'sid-0001' or an integer to index the entire list of subjects in raw_dir, e.g. 0, which would process the first subject apply_trim (int/bool; optional): number of volumes to trim from the beginning of each functional run; default is None task_name (str; optional): which functional task runs to process; default is all runs apply_dist_corr (bool; optional): look for fmap files and perform distortion correction; default False smooth (int/list; optional): smoothing to perform in FWHM mm; if a list is provided will create outputs for each smoothing kernel separately; default False apply_filter (float/list; optional): low-pass/high-freq filtering cut-offs in Hz; if a list is provided will create outputs for each filter cut-off separately. With high temporal resolution scans .25Hz is a decent value to capture respitory artifacts; default None/False mni_template (str; optional): which mm resolution template to use, e.g. '3mm'; default '2mm' apply_n4 (bool; optional): perform N4 Bias Field correction on the anatomical image; default true ants_threads (int; optional): number of threads ANTs should use for its processes; default 8 readable_crash_files (bool; optional): should nipype crash files be saved as txt? This makes them easily readable, but sometimes interferes with nipype's ability to use cached results of successfully run nodes (i.e. picking up where it left off after bugs are fixed); default False Examples: >>> from cosanlab_preproc.wfmaker import wfmaker >>> # Create workflow that performs no distortion correction, trims first 5 TRs, no filtering, 6mm smoothing, and normalizes to 2mm MNI space. Run it with 16 cores. >>> >>> workflow = wfmaker( project_dir = '/data/project', raw_dir = 'raw', apply_trim = 5) >>> >>> workflow.run('MultiProc',plugin_args = {'n_procs': 16}) >>> >>> # Create workflow that performs distortion correction, trims first 25 TRs, no filtering and filtering .25hz, 6mm and 8mm smoothing, and normalizes to 3mm MNI space. Run it serially (will be super slow!). >>> >>> workflow = wfmaker( project_dir = '/data/project', raw_dir = 'raw', apply_trim = 25, apply_dist_corr = True, apply_filter = [0, .25], apply_smooth = [6.0, 8.0], mni = '3mm') >>> >>> workflow.run() """ ################## ### PATH SETUP ### ################## if mni_template not in ['1mm', '2mm', '3mm']: raise ValueError("MNI template must be: 1mm, 2mm, or 3mm") data_dir = os.path.join(project_dir, raw_dir) output_dir = os.path.join(project_dir, 'preprocessed') output_final_dir = os.path.join(output_dir, 'final') output_interm_dir = os.path.join(output_dir, 'intermediate') log_dir = os.path.join(project_dir, 'logs', 'nipype') if not os.path.exists(output_final_dir): os.makedirs(output_final_dir) if not os.path.exists(output_interm_dir): os.makedirs(output_interm_dir) if not os.path.exists(log_dir): os.makedirs(log_dir) # Set MNI template MNItemplate = os.path.join(get_resource_path(), 'MNI152_T1_' + mni_template + '_brain.nii.gz') MNImask = os.path.join(get_resource_path(), 'MNI152_T1_' + mni_template + '_brain_mask.nii.gz') MNItemplatehasskull = os.path.join(get_resource_path(), 'MNI152_T1_' + mni_template + '.nii.gz') # Set ANTs files bet_ants_template = os.path.join(get_resource_path(), 'OASIS_template.nii.gz') bet_ants_prob_mask = os.path.join( get_resource_path(), 'OASIS_BrainCerebellumProbabilityMask.nii.gz') bet_ants_registration_mask = os.path.join( get_resource_path(), 'OASIS_BrainCerebellumRegistrationMask.nii.gz') ################################# ### NIPYPE IMPORTS AND CONFIG ### ################################# # Update nipype global config because workflow.config[] = ..., doesn't seem to work # Can't store nipype config/rc file in container anyway so set them globaly before importing and setting up workflow as suggested here: http://nipype.readthedocs.io/en/latest/users/config_file.html#config-file from nipype import config if readable_crash_files: cfg = dict(execution={'crashfile_format': 'txt'}) config.update_config(cfg) config.update_config( {'logging': { 'log_directory': log_dir, 'log_to_file': True }}) from nipype import logging logging.update_logging(config) # Now import everything else from nipype.interfaces.io import DataSink from nipype.interfaces.utility import Merge, IdentityInterface from nipype.pipeline.engine import Node, Workflow from nipype.interfaces.nipy.preprocess import ComputeMask from nipype.algorithms.rapidart import ArtifactDetect from nipype.interfaces.ants.segmentation import BrainExtraction, N4BiasFieldCorrection from nipype.interfaces.ants import Registration, ApplyTransforms from nipype.interfaces.fsl import MCFLIRT, TOPUP, ApplyTOPUP from nipype.interfaces.fsl.maths import MeanImage from nipype.interfaces.fsl import Merge as MERGE from nipype.interfaces.fsl.utils import Smooth from nipype.interfaces.nipy.preprocess import Trim from .interfaces import Plot_Coregistration_Montage, Plot_Quality_Control, Plot_Realignment_Parameters, Create_Covariates, Down_Sample_Precision, Create_Encoding_File, Filter_In_Mask ################## ### INPUT NODE ### ################## layout = BIDSLayout(data_dir) # Dartmouth subjects are named with the sub- prefix, handle whether we receive an integer identifier for indexing or the full subject id with prefixg if isinstance(subject_id, six.string_types): subId = subject_id[4:] elif isinstance(subject_id, int): subId = layout.get_subjects()[subject_id] subject_id = 'sub-' + subId else: raise TypeError("subject_id should be a string or integer") #Get anat file location anat = layout.get(subject=subId, type='T1w', extensions='.nii.gz')[0].filename #Get functional file locations if task_name: funcs = [ f.filename for f in layout.get(subject=subId, type='bold', task=task_name, extensions='.nii.gz') ] else: funcs = [ f.filename for f in layout.get( subject=subId, type='bold', extensions='.nii.gz') ] #Turn functional file list into interable Node func_scans = Node(IdentityInterface(fields=['scan']), name='func_scans') func_scans.iterables = ('scan', funcs) #Get TR for use in filtering below; we're assuming all BOLD runs have the same TR tr_length = layout.get_metadata(funcs[0])['RepetitionTime'] ##################################### ## TRIM ## ##################################### if apply_trim: trim = Node(Trim(), name='trim') trim.inputs.begin_index = apply_trim ##################################### ## DISTORTION CORRECTION ## ##################################### if apply_dist_corr: #Get fmap file locations fmaps = [ f.filename for f in layout.get( subject=subId, modality='fmap', extensions='.nii.gz') ] if not fmaps: raise IOError( "Distortion Correction requested but field map scans not found..." ) #Get fmap metadata totalReadoutTimes, measurements, fmap_pes = [], [], [] for i, fmap in enumerate(fmaps): # Grab total readout time for each fmap totalReadoutTimes.append( layout.get_metadata(fmap)['TotalReadoutTime']) # Grab measurements (for some reason pyBIDS doesn't grab dcm_meta... fields from side-car json file and json.load, doesn't either; so instead just read the header using nibabel to determine number of scans) measurements.append(nib.load(fmap).header['dim'][4]) # Get phase encoding direction fmap_pe = layout.get_metadata(fmap)["PhaseEncodingDirection"] fmap_pes.append(fmap_pe) encoding_file_writer = Node(interface=Create_Encoding_File(), name='create_encoding') encoding_file_writer.inputs.totalReadoutTimes = totalReadoutTimes encoding_file_writer.inputs.fmaps = fmaps encoding_file_writer.inputs.fmap_pes = fmap_pes encoding_file_writer.inputs.measurements = measurements encoding_file_writer.inputs.file_name = 'encoding_file.txt' merge_to_file_list = Node(interface=Merge(2), infields=['in1', 'in2'], name='merge_to_file_list') merge_to_file_list.inputs.in1 = fmaps[0] merge_to_file_list.inputs.in1 = fmaps[1] #Merge AP and PA distortion correction scans merger = Node(interface=MERGE(dimension='t'), name='merger') merger.inputs.output_type = 'NIFTI_GZ' merger.inputs.in_files = fmaps merger.inputs.merged_file = 'merged_epi.nii.gz' #Create distortion correction map topup = Node(interface=TOPUP(), name='topup') topup.inputs.output_type = 'NIFTI_GZ' #Apply distortion correction to other scans apply_topup = Node(interface=ApplyTOPUP(), name='apply_topup') apply_topup.inputs.output_type = 'NIFTI_GZ' apply_topup.inputs.method = 'jac' apply_topup.inputs.interp = 'spline' ################################### ### REALIGN ### ################################### realign_fsl = Node(MCFLIRT(), name="realign") realign_fsl.inputs.cost = 'mutualinfo' realign_fsl.inputs.mean_vol = True realign_fsl.inputs.output_type = 'NIFTI_GZ' realign_fsl.inputs.save_mats = True realign_fsl.inputs.save_rms = True realign_fsl.inputs.save_plots = True ################################### ### MEAN EPIs ### ################################### #For coregistration after realignment mean_epi = Node(MeanImage(), name='mean_epi') mean_epi.inputs.dimension = 'T' #For after normalization is done to plot checks mean_norm_epi = Node(MeanImage(), name='mean_norm_epi') mean_norm_epi.inputs.dimension = 'T' ################################### ### MASK, ART, COV CREATION ### ################################### compute_mask = Node(ComputeMask(), name='compute_mask') compute_mask.inputs.m = .05 art = Node(ArtifactDetect(), name='art') art.inputs.use_differences = [True, False] art.inputs.use_norm = True art.inputs.norm_threshold = 1 art.inputs.zintensity_threshold = 3 art.inputs.mask_type = 'file' art.inputs.parameter_source = 'FSL' make_cov = Node(Create_Covariates(), name='make_cov') ################################ ### N4 BIAS FIELD CORRECTION ### ################################ if apply_n4: n4_correction = Node(N4BiasFieldCorrection(), name='n4_correction') n4_correction.inputs.copy_header = True n4_correction.inputs.save_bias = False n4_correction.inputs.num_threads = ants_threads n4_correction.inputs.input_image = anat ################################### ### BRAIN EXTRACTION ### ################################### brain_extraction_ants = Node(BrainExtraction(), name='brain_extraction') brain_extraction_ants.inputs.dimension = 3 brain_extraction_ants.inputs.use_floatingpoint_precision = 1 brain_extraction_ants.inputs.num_threads = ants_threads brain_extraction_ants.inputs.brain_probability_mask = bet_ants_prob_mask brain_extraction_ants.inputs.keep_temporary_files = 1 brain_extraction_ants.inputs.brain_template = bet_ants_template brain_extraction_ants.inputs.extraction_registration_mask = bet_ants_registration_mask brain_extraction_ants.inputs.out_prefix = 'bet' ################################### ### COREGISTRATION ### ################################### coregistration = Node(Registration(), name='coregistration') coregistration.inputs.float = False coregistration.inputs.output_transform_prefix = "meanEpi2highres" coregistration.inputs.transforms = ['Rigid'] coregistration.inputs.transform_parameters = [(0.1, ), (0.1, )] coregistration.inputs.number_of_iterations = [[1000, 500, 250, 100]] coregistration.inputs.dimension = 3 coregistration.inputs.num_threads = ants_threads coregistration.inputs.write_composite_transform = True coregistration.inputs.collapse_output_transforms = True coregistration.inputs.metric = ['MI'] coregistration.inputs.metric_weight = [1] coregistration.inputs.radius_or_number_of_bins = [32] coregistration.inputs.sampling_strategy = ['Regular'] coregistration.inputs.sampling_percentage = [0.25] coregistration.inputs.convergence_threshold = [1e-08] coregistration.inputs.convergence_window_size = [10] coregistration.inputs.smoothing_sigmas = [[3, 2, 1, 0]] coregistration.inputs.sigma_units = ['mm'] coregistration.inputs.shrink_factors = [[4, 3, 2, 1]] coregistration.inputs.use_estimate_learning_rate_once = [True] coregistration.inputs.use_histogram_matching = [False] coregistration.inputs.initial_moving_transform_com = True coregistration.inputs.output_warped_image = True coregistration.inputs.winsorize_lower_quantile = 0.01 coregistration.inputs.winsorize_upper_quantile = 0.99 ################################### ### NORMALIZATION ### ################################### # Settings Explanations # Only a few key settings are worth adjusting and most others relate to how ANTs optimizer starts or iterates and won't make a ton of difference # Brian Avants referred to these settings as the last "best tested" when he was aligning fMRI data: https://github.com/ANTsX/ANTsRCore/blob/master/R/antsRegistration.R#L275 # Things that matter the most: # smoothing_sigmas: # how much gaussian smoothing to apply when performing registration, probably want the upper limit of this to match the resolution that the data is collected at e.g. 3mm # Old settings [[3,2,1,0]]*3 # shrink_factors # The coarseness with which to do registration # Old settings [[8,4,2,1]] * 3 # >= 8 may result is some problems causing big chunks of cortex with little fine grain spatial structure to be moved to other parts of cortex # Other settings # transform_parameters: # how much regularization to do for fitting that transformation # for syn this pertains to both the gradient regularization term, and the flow, and elastic terms. Leave the syn settings alone as they seem to be the most well tested across published data sets # radius_or_number_of_bins # This is the bin size for MI metrics and 32 is probably adequate for most use cases. Increasing this might increase precision (e.g. to 64) but takes exponentially longer # use_histogram_matching # Use image intensity distribution to guide registration # Leave it on for within modality registration (e.g. T1 -> MNI), but off for between modality registration (e.g. EPI -> T1) # convergence_threshold # threshold for optimizer # convergence_window_size # how many samples should optimizer average to compute threshold? # sampling_strategy # what strategy should ANTs use to initialize the transform. Regular here refers to approximately random sampling around the center of the image mass normalization = Node(Registration(), name='normalization') normalization.inputs.float = False normalization.inputs.collapse_output_transforms = True normalization.inputs.convergence_threshold = [1e-06, 1e-06, 1e-07] normalization.inputs.convergence_window_size = [10] normalization.inputs.dimension = 3 normalization.inputs.fixed_image = MNItemplate normalization.inputs.initial_moving_transform_com = True normalization.inputs.metric = ['MI', 'MI', 'CC'] normalization.inputs.metric_weight = [1.0] * 3 normalization.inputs.number_of_iterations = [[1000, 500, 250, 100], [1000, 500, 250, 100], [100, 70, 50, 20]] normalization.inputs.num_threads = ants_threads normalization.inputs.output_transform_prefix = 'anat2template' normalization.inputs.output_inverse_warped_image = True normalization.inputs.output_warped_image = True normalization.inputs.radius_or_number_of_bins = [32, 32, 4] normalization.inputs.sampling_percentage = [0.25, 0.25, 1] normalization.inputs.sampling_strategy = ['Regular', 'Regular', 'None'] normalization.inputs.shrink_factors = [[4, 3, 2, 1]] * 3 normalization.inputs.sigma_units = ['vox'] * 3 normalization.inputs.smoothing_sigmas = [[2, 1], [2, 1], [3, 2, 1, 0]] normalization.inputs.transforms = ['Rigid', 'Affine', 'SyN'] normalization.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.1, 3.0, 0.0)] normalization.inputs.use_histogram_matching = True normalization.inputs.winsorize_lower_quantile = 0.005 normalization.inputs.winsorize_upper_quantile = 0.995 normalization.inputs.write_composite_transform = True ################################### ### APPLY TRANSFORMS AND SMOOTH ### ################################### merge_transforms = Node(Merge(2), iterfield=['in2'], name='merge_transforms') # Used for epi -> mni, via (coreg + norm) apply_transforms = Node(ApplyTransforms(), iterfield=['input_image'], name='apply_transforms') apply_transforms.inputs.input_image_type = 3 apply_transforms.inputs.float = False apply_transforms.inputs.num_threads = 12 apply_transforms.inputs.environ = {} apply_transforms.inputs.interpolation = 'BSpline' apply_transforms.inputs.invert_transform_flags = [False, False] apply_transforms.inputs.reference_image = MNItemplate # Used for t1 segmented -> mni, via (norm) apply_transform_seg = Node(ApplyTransforms(), name='apply_transform_seg') apply_transform_seg.inputs.input_image_type = 3 apply_transform_seg.inputs.float = False apply_transform_seg.inputs.num_threads = 12 apply_transform_seg.inputs.environ = {} apply_transform_seg.inputs.interpolation = 'MultiLabel' apply_transform_seg.inputs.invert_transform_flags = [False] apply_transform_seg.inputs.reference_image = MNItemplate ################################### ### PLOTS ### ################################### plot_realign = Node(Plot_Realignment_Parameters(), name="plot_realign") plot_qa = Node(Plot_Quality_Control(), name="plot_qa") plot_normalization_check = Node(Plot_Coregistration_Montage(), name="plot_normalization_check") plot_normalization_check.inputs.canonical_img = MNItemplatehasskull ############################################ ### FILTER, SMOOTH, DOWNSAMPLE PRECISION ### ############################################ #Use cosanlab_preproc for down sampling down_samp = Node(Down_Sample_Precision(), name="down_samp") #Use FSL for smoothing if apply_smooth: smooth = Node(Smooth(), name='smooth') if isinstance(apply_smooth, list): smooth.iterables = ("fwhm", apply_smooth) elif isinstance(apply_smooth, int) or isinstance(apply_smooth, float): smooth.inputs.fwhm = apply_smooth else: raise ValueError("apply_smooth must be a list or int/float") #Use cosanlab_preproc for low-pass filtering if apply_filter: lp_filter = Node(Filter_In_Mask(), name='lp_filter') lp_filter.inputs.mask = MNImask lp_filter.inputs.sampling_rate = tr_length lp_filter.inputs.high_pass_cutoff = 0 if isinstance(apply_filter, list): lp_filter.iterables = ("low_pass_cutoff", apply_filter) elif isinstance(apply_filter, int) or isinstance(apply_filter, float): lp_filter.inputs.low_pass_cutoff = apply_filter else: raise ValueError("apply_filter must be a list or int/float") ################### ### OUTPUT NODE ### ################### #Collect all final outputs in the output dir and get rid of file name additions datasink = Node(DataSink(), name='datasink') datasink.inputs.base_directory = output_final_dir datasink.inputs.container = subject_id # Remove substitutions data_dir_parts = data_dir.split('/')[1:] prefix = ['_scan_'] + data_dir_parts + [subject_id] + ['func'] func_scan_names = [os.path.split(elem)[-1] for elem in funcs] to_replace = [] for elem in func_scan_names: bold_name = elem.split(subject_id + '_')[-1] bold_name = bold_name.split('.nii.gz')[0] to_replace.append(('..'.join(prefix + [elem]), bold_name)) datasink.inputs.substitutions = to_replace ##################### ### INIT WORKFLOW ### ##################### workflow = Workflow(name=subId) workflow.base_dir = output_interm_dir ############################ ######### PART (1a) ######### # func -> discorr -> trim -> realign # OR # func -> trim -> realign # OR # func -> discorr -> realign # OR # func -> realign ############################ if apply_dist_corr: workflow.connect([(encoding_file_writer, topup, [('encoding_file', 'encoding_file')]), (encoding_file_writer, apply_topup, [('encoding_file', 'encoding_file')]), (merger, topup, [('merged_file', 'in_file')]), (func_scans, apply_topup, [('scan', 'in_files')]), (topup, apply_topup, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar')])]) if apply_trim: # Dist Corr + Trim workflow.connect([(apply_topup, trim, [('out_corrected', 'in_file') ]), (trim, realign_fsl, [('out_file', 'in_file')])]) else: # Dist Corr + No Trim workflow.connect([(apply_topup, realign_fsl, [('out_corrected', 'in_file')])]) else: if apply_trim: # No Dist Corr + Trim workflow.connect([(func_scans, trim, [('scan', 'in_file')]), (trim, realign_fsl, [('out_file', 'in_file')])]) else: # No Dist Corr + No Trim workflow.connect([ (func_scans, realign_fsl, [('scan', 'in_file')]), ]) ############################ ######### PART (1n) ######### # anat -> N4 -> bet # OR # anat -> bet ############################ if apply_n4: workflow.connect([(n4_correction, brain_extraction_ants, [('output_image', 'anatomical_image')])]) else: brain_extraction_ants.inputs.anatomical_image = anat ########################################## ############### PART (2) ################# # realign -> coreg -> mni (via t1) # t1 -> mni # covariate creation # plot creation ########################################### workflow.connect([ (realign_fsl, plot_realign, [('par_file', 'realignment_parameters')]), (realign_fsl, plot_qa, [('out_file', 'dat_img')]), (realign_fsl, art, [('out_file', 'realigned_files'), ('par_file', 'realignment_parameters')]), (realign_fsl, mean_epi, [('out_file', 'in_file')]), (realign_fsl, make_cov, [('par_file', 'realignment_parameters')]), (mean_epi, compute_mask, [('out_file', 'mean_volume')]), (compute_mask, art, [('brain_mask', 'mask_file')]), (art, make_cov, [('outlier_files', 'spike_id')]), (art, plot_realign, [('outlier_files', 'outliers')]), (plot_qa, make_cov, [('fd_outliers', 'fd_outliers')]), (brain_extraction_ants, coregistration, [('BrainExtractionBrain', 'fixed_image')]), (mean_epi, coregistration, [('out_file', 'moving_image')]), (brain_extraction_ants, normalization, [('BrainExtractionBrain', 'moving_image')]), (coregistration, merge_transforms, [('composite_transform', 'in2')]), (normalization, merge_transforms, [('composite_transform', 'in1')]), (merge_transforms, apply_transforms, [('out', 'transforms')]), (realign_fsl, apply_transforms, [('out_file', 'input_image')]), (apply_transforms, mean_norm_epi, [('output_image', 'in_file')]), (normalization, apply_transform_seg, [('composite_transform', 'transforms')]), (brain_extraction_ants, apply_transform_seg, [('BrainExtractionSegmentation', 'input_image')]), (mean_norm_epi, plot_normalization_check, [('out_file', 'wra_img')]) ]) ################################################## ################### PART (3) ##################### # epi (in mni) -> filter -> smooth -> down sample # OR # epi (in mni) -> filter -> down sample # OR # epi (in mni) -> smooth -> down sample # OR # epi (in mni) -> down sample ################################################### if apply_filter: workflow.connect([(apply_transforms, lp_filter, [('output_image', 'in_file')])]) if apply_smooth: # Filtering + Smoothing workflow.connect([(lp_filter, smooth, [('out_file', 'in_file')]), (smooth, down_samp, [('smoothed_file', 'in_file') ])]) else: # Filtering + No Smoothing workflow.connect([(lp_filter, down_samp, [('out_file', 'in_file')]) ]) else: if apply_smooth: # No Filtering + Smoothing workflow.connect([ (apply_transforms, smooth, [('output_image', 'in_file')]), (smooth, down_samp, [('smoothed_file', 'in_file')]) ]) else: # No Filtering + No Smoothing workflow.connect([(apply_transforms, down_samp, [('output_image', 'in_file')])]) ########################################## ############### PART (4) ################# # down sample -> save # plots -> save # covs -> save # t1 (in mni) -> save # t1 segmented masks (in mni) -> save ########################################## workflow.connect([ (down_samp, datasink, [('out_file', 'functional.@down_samp')]), (plot_realign, datasink, [('plot', 'functional.@plot_realign')]), (plot_qa, datasink, [('plot', 'functional.@plot_qa')]), (plot_normalization_check, datasink, [('plot', 'functional.@plot_normalization')]), (make_cov, datasink, [('covariates', 'functional.@covariates')]), (normalization, datasink, [('warped_image', 'structural.@normanat')]), (apply_transform_seg, datasink, [('output_image', 'structural.@normanatseg')]) ]) if not os.path.exists(os.path.join(output_dir, 'pipeline.png')): workflow.write_graph(dotfilename=os.path.join(output_dir, 'pipeline'), format='png') print(f"Creating workflow for subject: {subject_id}") if ants_threads == 8: print( f"ANTs will utilize the default of {ants_threads} threads for parallel processing." ) else: print( f"ANTs will utilize the user-requested {ants_threads} threads for parallel processing." ) return workflow
def builder(subject_id, subId, project_dir, data_dir, output_dir, output_final_dir, output_interm_dir, layout, anat=None, funcs=None, fmaps=None, task_name='', session=None, apply_trim=False, apply_dist_corr=False, apply_smooth=False, apply_filter=False, mni_template='2mm', apply_n4=True, ants_threads=8, readable_crash_files=False, write_logs=True): """ Core function that returns a workflow. See wfmaker for more details. Args: subject_id: name of subject folder for final outputted sub-folder name subId: abbreviate name of subject for intermediate outputted sub-folder name project_dir: full path to root of project data_dir: full path to raw data files output_dir: upper level output dir (others will be nested within this) output_final_dir: final preprocessed sub-dir name output_interm_dir: intermediate preprcess sub-dir name layout: BIDS layout instance """ ################## ### PATH SETUP ### ################## if session is not None: session = int(session) if session < 10: session = '0' + str(session) else: session = str(session) # Set MNI template MNItemplate = os.path.join(get_resource_path(), 'MNI152_T1_' + mni_template + '_brain.nii.gz') MNImask = os.path.join(get_resource_path(), 'MNI152_T1_' + mni_template + '_brain_mask.nii.gz') MNItemplatehasskull = os.path.join(get_resource_path(), 'MNI152_T1_' + mni_template + '.nii.gz') # Set ANTs files bet_ants_template = os.path.join(get_resource_path(), 'OASIS_template.nii.gz') bet_ants_prob_mask = os.path.join( get_resource_path(), 'OASIS_BrainCerebellumProbabilityMask.nii.gz') bet_ants_registration_mask = os.path.join( get_resource_path(), 'OASIS_BrainCerebellumRegistrationMask.nii.gz') ################################# ### NIPYPE IMPORTS AND CONFIG ### ################################# # Update nipype global config because workflow.config[] = ..., doesn't seem to work # Can't store nipype config/rc file in container anyway so set them globaly before importing and setting up workflow as suggested here: http://nipype.readthedocs.io/en/latest/users/config_file.html#config-file # Create subject's intermediate directory before configuring nipype and the workflow because that's where we'll save log files in addition to intermediate files if not os.path.exists(os.path.join(output_interm_dir, subId, 'logs')): os.makedirs(os.path.join(output_interm_dir, subId, 'logs')) log_dir = os.path.join(output_interm_dir, subId, 'logs') from nipype import config if readable_crash_files: cfg = dict(execution={'crashfile_format': 'txt'}) config.update_config(cfg) config.update_config({ 'logging': { 'log_directory': log_dir, 'log_to_file': write_logs }, 'execution': { 'crashdump_dir': log_dir } }) from nipype import logging logging.update_logging(config) # Now import everything else from nipype.interfaces.io import DataSink from nipype.interfaces.utility import Merge, IdentityInterface from nipype.pipeline.engine import Node, Workflow from nipype.interfaces.nipy.preprocess import ComputeMask from nipype.algorithms.rapidart import ArtifactDetect from nipype.interfaces.ants.segmentation import BrainExtraction, N4BiasFieldCorrection from nipype.interfaces.ants import Registration, ApplyTransforms from nipype.interfaces.fsl import MCFLIRT, TOPUP, ApplyTOPUP from nipype.interfaces.fsl.maths import MeanImage from nipype.interfaces.fsl import Merge as MERGE from nipype.interfaces.fsl.utils import Smooth from nipype.interfaces.nipy.preprocess import Trim from .interfaces import Plot_Coregistration_Montage, Plot_Quality_Control, Plot_Realignment_Parameters, Create_Covariates, Down_Sample_Precision, Create_Encoding_File, Filter_In_Mask ################## ### INPUT NODE ### ################## # Turn functional file list into interable Node func_scans = Node(IdentityInterface(fields=['scan']), name='func_scans') func_scans.iterables = ('scan', funcs) # Get TR for use in filtering below; we're assuming all BOLD runs have the same TR tr_length = layout.get_metadata(funcs[0])['RepetitionTime'] ##################################### ## TRIM ## ##################################### if apply_trim: trim = Node(Trim(), name='trim') trim.inputs.begin_index = apply_trim ##################################### ## DISTORTION CORRECTION ## ##################################### if apply_dist_corr: # Get fmap file locations fmaps = [ f.filename for f in layout.get( subject=subId, modality='fmap', extensions='.nii.gz') ] if not fmaps: raise IOError( "Distortion Correction requested but field map scans not found..." ) # Get fmap metadata totalReadoutTimes, measurements, fmap_pes = [], [], [] for i, fmap in enumerate(fmaps): # Grab total readout time for each fmap totalReadoutTimes.append( layout.get_metadata(fmap)['TotalReadoutTime']) # Grab measurements (for some reason pyBIDS doesn't grab dcm_meta... fields from side-car json file and json.load, doesn't either; so instead just read the header using nibabel to determine number of scans) measurements.append(nib.load(fmap).header['dim'][4]) # Get phase encoding direction fmap_pe = layout.get_metadata(fmap)["PhaseEncodingDirection"] fmap_pes.append(fmap_pe) encoding_file_writer = Node(interface=Create_Encoding_File(), name='create_encoding') encoding_file_writer.inputs.totalReadoutTimes = totalReadoutTimes encoding_file_writer.inputs.fmaps = fmaps encoding_file_writer.inputs.fmap_pes = fmap_pes encoding_file_writer.inputs.measurements = measurements encoding_file_writer.inputs.file_name = 'encoding_file.txt' merge_to_file_list = Node(interface=Merge(2), infields=['in1', 'in2'], name='merge_to_file_list') merge_to_file_list.inputs.in1 = fmaps[0] merge_to_file_list.inputs.in1 = fmaps[1] # Merge AP and PA distortion correction scans merger = Node(interface=MERGE(dimension='t'), name='merger') merger.inputs.output_type = 'NIFTI_GZ' merger.inputs.in_files = fmaps merger.inputs.merged_file = 'merged_epi.nii.gz' # Create distortion correction map topup = Node(interface=TOPUP(), name='topup') topup.inputs.output_type = 'NIFTI_GZ' # Apply distortion correction to other scans apply_topup = Node(interface=ApplyTOPUP(), name='apply_topup') apply_topup.inputs.output_type = 'NIFTI_GZ' apply_topup.inputs.method = 'jac' apply_topup.inputs.interp = 'spline' ################################### ### REALIGN ### ################################### realign_fsl = Node(MCFLIRT(), name="realign") realign_fsl.inputs.cost = 'mutualinfo' realign_fsl.inputs.mean_vol = True realign_fsl.inputs.output_type = 'NIFTI_GZ' realign_fsl.inputs.save_mats = True realign_fsl.inputs.save_rms = True realign_fsl.inputs.save_plots = True ################################### ### MEAN EPIs ### ################################### # For coregistration after realignment mean_epi = Node(MeanImage(), name='mean_epi') mean_epi.inputs.dimension = 'T' # For after normalization is done to plot checks mean_norm_epi = Node(MeanImage(), name='mean_norm_epi') mean_norm_epi.inputs.dimension = 'T' ################################### ### MASK, ART, COV CREATION ### ################################### compute_mask = Node(ComputeMask(), name='compute_mask') compute_mask.inputs.m = .05 art = Node(ArtifactDetect(), name='art') art.inputs.use_differences = [True, False] art.inputs.use_norm = True art.inputs.norm_threshold = 1 art.inputs.zintensity_threshold = 3 art.inputs.mask_type = 'file' art.inputs.parameter_source = 'FSL' make_cov = Node(Create_Covariates(), name='make_cov') ################################ ### N4 BIAS FIELD CORRECTION ### ################################ if apply_n4: n4_correction = Node(N4BiasFieldCorrection(), name='n4_correction') n4_correction.inputs.copy_header = True n4_correction.inputs.save_bias = False n4_correction.inputs.num_threads = ants_threads n4_correction.inputs.input_image = anat ################################### ### BRAIN EXTRACTION ### ################################### brain_extraction_ants = Node(BrainExtraction(), name='brain_extraction') brain_extraction_ants.inputs.dimension = 3 brain_extraction_ants.inputs.use_floatingpoint_precision = 1 brain_extraction_ants.inputs.num_threads = ants_threads brain_extraction_ants.inputs.brain_probability_mask = bet_ants_prob_mask brain_extraction_ants.inputs.keep_temporary_files = 1 brain_extraction_ants.inputs.brain_template = bet_ants_template brain_extraction_ants.inputs.extraction_registration_mask = bet_ants_registration_mask brain_extraction_ants.inputs.out_prefix = 'bet' ################################### ### COREGISTRATION ### ################################### coregistration = Node(Registration(), name='coregistration') coregistration.inputs.float = False coregistration.inputs.output_transform_prefix = "meanEpi2highres" coregistration.inputs.transforms = ['Rigid'] coregistration.inputs.transform_parameters = [(0.1, ), (0.1, )] coregistration.inputs.number_of_iterations = [[1000, 500, 250, 100]] coregistration.inputs.dimension = 3 coregistration.inputs.num_threads = ants_threads coregistration.inputs.write_composite_transform = True coregistration.inputs.collapse_output_transforms = True coregistration.inputs.metric = ['MI'] coregistration.inputs.metric_weight = [1] coregistration.inputs.radius_or_number_of_bins = [32] coregistration.inputs.sampling_strategy = ['Regular'] coregistration.inputs.sampling_percentage = [0.25] coregistration.inputs.convergence_threshold = [1e-08] coregistration.inputs.convergence_window_size = [10] coregistration.inputs.smoothing_sigmas = [[3, 2, 1, 0]] coregistration.inputs.sigma_units = ['mm'] coregistration.inputs.shrink_factors = [[4, 3, 2, 1]] coregistration.inputs.use_estimate_learning_rate_once = [True] coregistration.inputs.use_histogram_matching = [False] coregistration.inputs.initial_moving_transform_com = True coregistration.inputs.output_warped_image = True coregistration.inputs.winsorize_lower_quantile = 0.01 coregistration.inputs.winsorize_upper_quantile = 0.99 ################################### ### NORMALIZATION ### ################################### # Settings Explanations # Only a few key settings are worth adjusting and most others relate to how ANTs optimizer starts or iterates and won't make a ton of difference # Brian Avants referred to these settings as the last "best tested" when he was aligning fMRI data: https://github.com/ANTsX/ANTsRCore/blob/master/R/antsRegistration.R#L275 # Things that matter the most: # smoothing_sigmas: # how much gaussian smoothing to apply when performing registration, probably want the upper limit of this to match the resolution that the data is collected at e.g. 3mm # Old settings [[3,2,1,0]]*3 # shrink_factors # The coarseness with which to do registration # Old settings [[8,4,2,1]] * 3 # >= 8 may result is some problems causing big chunks of cortex with little fine grain spatial structure to be moved to other parts of cortex # Other settings # transform_parameters: # how much regularization to do for fitting that transformation # for syn this pertains to both the gradient regularization term, and the flow, and elastic terms. Leave the syn settings alone as they seem to be the most well tested across published data sets # radius_or_number_of_bins # This is the bin size for MI metrics and 32 is probably adequate for most use cases. Increasing this might increase precision (e.g. to 64) but takes exponentially longer # use_histogram_matching # Use image intensity distribution to guide registration # Leave it on for within modality registration (e.g. T1 -> MNI), but off for between modality registration (e.g. EPI -> T1) # convergence_threshold # threshold for optimizer # convergence_window_size # how many samples should optimizer average to compute threshold? # sampling_strategy # what strategy should ANTs use to initialize the transform. Regular here refers to approximately random sampling around the center of the image mass normalization = Node(Registration(), name='normalization') normalization.inputs.float = False normalization.inputs.collapse_output_transforms = True normalization.inputs.convergence_threshold = [1e-06] normalization.inputs.convergence_window_size = [10] normalization.inputs.dimension = 3 normalization.inputs.fixed_image = MNItemplate normalization.inputs.initial_moving_transform_com = True normalization.inputs.metric = ['MI', 'MI', 'CC'] normalization.inputs.metric_weight = [1.0] * 3 normalization.inputs.number_of_iterations = [[1000, 500, 250, 100], [1000, 500, 250, 100], [100, 70, 50, 20]] normalization.inputs.num_threads = ants_threads normalization.inputs.output_transform_prefix = 'anat2template' normalization.inputs.output_inverse_warped_image = True normalization.inputs.output_warped_image = True normalization.inputs.radius_or_number_of_bins = [32, 32, 4] normalization.inputs.sampling_percentage = [0.25, 0.25, 1] normalization.inputs.sampling_strategy = ['Regular', 'Regular', 'None'] normalization.inputs.shrink_factors = [[8, 4, 2, 1]] * 3 normalization.inputs.sigma_units = ['vox'] * 3 normalization.inputs.smoothing_sigmas = [[3, 2, 1, 0]] * 3 normalization.inputs.transforms = ['Rigid', 'Affine', 'SyN'] normalization.inputs.transform_parameters = [(0.1, ), (0.1, ), (0.1, 3.0, 0.0)] normalization.inputs.use_histogram_matching = True normalization.inputs.winsorize_lower_quantile = 0.005 normalization.inputs.winsorize_upper_quantile = 0.995 normalization.inputs.write_composite_transform = True # NEW SETTINGS (need to be adjusted; specifically shink_factors and smoothing_sigmas need to be the same length) # normalization = Node(Registration(), name='normalization') # normalization.inputs.float = False # normalization.inputs.collapse_output_transforms = True # normalization.inputs.convergence_threshold = [1e-06, 1e-06, 1e-07] # normalization.inputs.convergence_window_size = [10] # normalization.inputs.dimension = 3 # normalization.inputs.fixed_image = MNItemplate # normalization.inputs.initial_moving_transform_com = True # normalization.inputs.metric = ['MI', 'MI', 'CC'] # normalization.inputs.metric_weight = [1.0]*3 # normalization.inputs.number_of_iterations = [[1000, 500, 250, 100], # [1000, 500, 250, 100], # [100, 70, 50, 20]] # normalization.inputs.num_threads = ants_threads # normalization.inputs.output_transform_prefix = 'anat2template' # normalization.inputs.output_inverse_warped_image = True # normalization.inputs.output_warped_image = True # normalization.inputs.radius_or_number_of_bins = [32, 32, 4] # normalization.inputs.sampling_percentage = [0.25, 0.25, 1] # normalization.inputs.sampling_strategy = ['Regular', # 'Regular', # 'None'] # normalization.inputs.shrink_factors = [[4, 3, 2, 1]]*3 # normalization.inputs.sigma_units = ['vox']*3 # normalization.inputs.smoothing_sigmas = [[2, 1], [2, 1], [3, 2, 1, 0]] # normalization.inputs.transforms = ['Rigid', 'Affine', 'SyN'] # normalization.inputs.transform_parameters = [(0.1,), # (0.1,), # (0.1, 3.0, 0.0)] # normalization.inputs.use_histogram_matching = True # normalization.inputs.winsorize_lower_quantile = 0.005 # normalization.inputs.winsorize_upper_quantile = 0.995 # normalization.inputs.write_composite_transform = True ################################### ### APPLY TRANSFORMS AND SMOOTH ### ################################### merge_transforms = Node(Merge(2), iterfield=['in2'], name='merge_transforms') # Used for epi -> mni, via (coreg + norm) apply_transforms = Node(ApplyTransforms(), iterfield=['input_image'], name='apply_transforms') apply_transforms.inputs.input_image_type = 3 apply_transforms.inputs.float = False apply_transforms.inputs.num_threads = 12 apply_transforms.inputs.environ = {} apply_transforms.inputs.interpolation = 'BSpline' apply_transforms.inputs.invert_transform_flags = [False, False] apply_transforms.inputs.reference_image = MNItemplate # Used for t1 segmented -> mni, via (norm) apply_transform_seg = Node(ApplyTransforms(), name='apply_transform_seg') apply_transform_seg.inputs.input_image_type = 3 apply_transform_seg.inputs.float = False apply_transform_seg.inputs.num_threads = 12 apply_transform_seg.inputs.environ = {} apply_transform_seg.inputs.interpolation = 'MultiLabel' apply_transform_seg.inputs.invert_transform_flags = [False] apply_transform_seg.inputs.reference_image = MNItemplate ################################### ### PLOTS ### ################################### plot_realign = Node(Plot_Realignment_Parameters(), name="plot_realign") plot_qa = Node(Plot_Quality_Control(), name="plot_qa") plot_normalization_check = Node(Plot_Coregistration_Montage(), name="plot_normalization_check") plot_normalization_check.inputs.canonical_img = MNItemplatehasskull ############################################ ### FILTER, SMOOTH, DOWNSAMPLE PRECISION ### ############################################ # Use cosanlab_preproc for down sampling down_samp = Node(Down_Sample_Precision(), name="down_samp") # Use FSL for smoothing if apply_smooth: smooth = Node(Smooth(), name='smooth') if isinstance(apply_smooth, list): smooth.iterables = ("fwhm", apply_smooth) elif isinstance(apply_smooth, int) or isinstance(apply_smooth, float): smooth.inputs.fwhm = apply_smooth else: raise ValueError("apply_smooth must be a list or int/float") # Use cosanlab_preproc for low-pass filtering if apply_filter: lp_filter = Node(Filter_In_Mask(), name='lp_filter') lp_filter.inputs.mask = MNImask lp_filter.inputs.sampling_rate = tr_length lp_filter.inputs.high_pass_cutoff = 0 if isinstance(apply_filter, list): lp_filter.iterables = ("low_pass_cutoff", apply_filter) elif isinstance(apply_filter, int) or isinstance(apply_filter, float): lp_filter.inputs.low_pass_cutoff = apply_filter else: raise ValueError("apply_filter must be a list or int/float") ################### ### OUTPUT NODE ### ################### # Collect all final outputs in the output dir and get rid of file name additions datasink = Node(DataSink(), name='datasink') if session: datasink.inputs.base_directory = os.path.join(output_final_dir, subject_id) datasink.inputs.container = 'ses-' + session else: datasink.inputs.base_directory = output_final_dir datasink.inputs.container = subject_id # Remove substitutions data_dir_parts = data_dir.split('/')[1:] if session: prefix = ['_scan_'] + data_dir_parts + [subject_id] + [ 'ses-' + session ] + ['func'] else: prefix = ['_scan_'] + data_dir_parts + [subject_id] + ['func'] func_scan_names = [os.path.split(elem)[-1] for elem in funcs] to_replace = [] for elem in func_scan_names: bold_name = elem.split(subject_id + '_')[-1] bold_name = bold_name.split('.nii.gz')[0] to_replace.append(('..'.join(prefix + [elem]), bold_name)) datasink.inputs.substitutions = to_replace ##################### ### INIT WORKFLOW ### ##################### # If we have sessions provide the full path to the subject's intermediate directory # and only rely on workflow init to create the session container *within* that directory # Otherwise just point to the intermediate directory and let the workflow init create the subject container within the intermediate directory if session: workflow = Workflow(name='ses_' + session) workflow.base_dir = os.path.join(output_interm_dir, subId) else: workflow = Workflow(name=subId) workflow.base_dir = output_interm_dir ############################ ######### PART (1a) ######### # func -> discorr -> trim -> realign # OR # func -> trim -> realign # OR # func -> discorr -> realign # OR # func -> realign ############################ if apply_dist_corr: workflow.connect([(encoding_file_writer, topup, [('encoding_file', 'encoding_file')]), (encoding_file_writer, apply_topup, [('encoding_file', 'encoding_file')]), (merger, topup, [('merged_file', 'in_file')]), (func_scans, apply_topup, [('scan', 'in_files')]), (topup, apply_topup, [('out_fieldcoef', 'in_topup_fieldcoef'), ('out_movpar', 'in_topup_movpar')])]) if apply_trim: # Dist Corr + Trim workflow.connect([(apply_topup, trim, [('out_corrected', 'in_file') ]), (trim, realign_fsl, [('out_file', 'in_file')])]) else: # Dist Corr + No Trim workflow.connect([(apply_topup, realign_fsl, [('out_corrected', 'in_file')])]) else: if apply_trim: # No Dist Corr + Trim workflow.connect([(func_scans, trim, [('scan', 'in_file')]), (trim, realign_fsl, [('out_file', 'in_file')])]) else: # No Dist Corr + No Trim workflow.connect([ (func_scans, realign_fsl, [('scan', 'in_file')]), ]) ############################ ######### PART (1n) ######### # anat -> N4 -> bet # OR # anat -> bet ############################ if apply_n4: workflow.connect([(n4_correction, brain_extraction_ants, [('output_image', 'anatomical_image')])]) else: brain_extraction_ants.inputs.anatomical_image = anat ########################################## ############### PART (2) ################# # realign -> coreg -> mni (via t1) # t1 -> mni # covariate creation # plot creation ########################################### workflow.connect([ (realign_fsl, plot_realign, [('par_file', 'realignment_parameters')]), (realign_fsl, plot_qa, [('out_file', 'dat_img')]), (realign_fsl, art, [('out_file', 'realigned_files'), ('par_file', 'realignment_parameters')]), (realign_fsl, mean_epi, [('out_file', 'in_file')]), (realign_fsl, make_cov, [('par_file', 'realignment_parameters')]), (mean_epi, compute_mask, [('out_file', 'mean_volume')]), (compute_mask, art, [('brain_mask', 'mask_file')]), (art, make_cov, [('outlier_files', 'spike_id')]), (art, plot_realign, [('outlier_files', 'outliers')]), (plot_qa, make_cov, [('fd_outliers', 'fd_outliers')]), (brain_extraction_ants, coregistration, [('BrainExtractionBrain', 'fixed_image')]), (mean_epi, coregistration, [('out_file', 'moving_image')]), (brain_extraction_ants, normalization, [('BrainExtractionBrain', 'moving_image')]), (coregistration, merge_transforms, [('composite_transform', 'in2')]), (normalization, merge_transforms, [('composite_transform', 'in1')]), (merge_transforms, apply_transforms, [('out', 'transforms')]), (realign_fsl, apply_transforms, [('out_file', 'input_image')]), (apply_transforms, mean_norm_epi, [('output_image', 'in_file')]), (normalization, apply_transform_seg, [('composite_transform', 'transforms')]), (brain_extraction_ants, apply_transform_seg, [('BrainExtractionSegmentation', 'input_image')]), (mean_norm_epi, plot_normalization_check, [('out_file', 'wra_img')]) ]) ################################################## ################### PART (3) ##################### # epi (in mni) -> filter -> smooth -> down sample # OR # epi (in mni) -> filter -> down sample # OR # epi (in mni) -> smooth -> down sample # OR # epi (in mni) -> down sample ################################################### if apply_filter: workflow.connect([(apply_transforms, lp_filter, [('output_image', 'in_file')])]) if apply_smooth: # Filtering + Smoothing workflow.connect([(lp_filter, smooth, [('out_file', 'in_file')]), (smooth, down_samp, [('smoothed_file', 'in_file') ])]) else: # Filtering + No Smoothing workflow.connect([(lp_filter, down_samp, [('out_file', 'in_file')]) ]) else: if apply_smooth: # No Filtering + Smoothing workflow.connect([ (apply_transforms, smooth, [('output_image', 'in_file')]), (smooth, down_samp, [('smoothed_file', 'in_file')]) ]) else: # No Filtering + No Smoothing workflow.connect([(apply_transforms, down_samp, [('output_image', 'in_file')])]) ########################################## ############### PART (4) ################# # down sample -> save # plots -> save # covs -> save # t1 (in mni) -> save # t1 segmented masks (in mni) -> save # realignment parms -> save ########################################## workflow.connect([ (down_samp, datasink, [('out_file', 'functional.@down_samp')]), (plot_realign, datasink, [('plot', 'functional.@plot_realign')]), (plot_qa, datasink, [('plot', 'functional.@plot_qa')]), (plot_normalization_check, datasink, [('plot', 'functional.@plot_normalization')]), (make_cov, datasink, [('covariates', 'functional.@covariates')]), (normalization, datasink, [('warped_image', 'structural.@normanat')]), (apply_transform_seg, datasink, [('output_image', 'structural.@normanatseg')]), (realign_fsl, datasink, [('par_file', 'functional.@motionparams')]) ]) if not os.path.exists(os.path.join(output_dir, 'pipeline.png')): workflow.write_graph(dotfilename=os.path.join(output_dir, 'pipeline'), format='png') print(f"Creating workflow for subject: {subject_id}") if ants_threads != 8: print( f"ANTs will utilize the user-requested {ants_threads} threads for parallel processing." ) return workflow
range(2, number_of_slices + 1, 2)) print(interleaved_order) sliceTiming = Node(SliceTiming(num_slices=number_of_slices, time_repetition=TR, time_acquisition=TR - TR / number_of_slices, slice_order=interleaved_order, ref_slice=19), name="sliceTiming") # Realign - correct for motion realign = Node(Realign(register_to_mean=True), name="realign") # Artifact Detection - determine which of the images in the functional series # are outliers. This is based on deviation in intensity or movement. art = Node(ArtifactDetect(norm_threshold=1, zintensity_threshold=3, mask_type='spm_global', parameter_source='SPM'), name="art") # Smooth - to smooth the images with a given kernel smooth = Node(Smooth(fwhm=smoothing_size), name="smooth") # Create a preprocessing workflow preproc = Workflow(name='preproc') preproc.base_dir = opj(experiment_dir, working_dir) # Connect all components of the preprocessing workflow preproc.connect([ (gunzip, sliceTiming, [('out_file', 'in_files')]), (sliceTiming, realign, [('timecorrected_files', 'in_files')]), (realign, art, [('realigned_files', 'realigned_files'),
def create_resting_preproc(name='restpreproc'): """Create a "resting" time series preprocessing workflow The noise removal is based on Behzadi et al. (2007) Parameters ---------- name : name of workflow (default: restpreproc) Inputs:: inputspec.func : functional run (filename or list of filenames) Outputs:: outputspec.noise_mask_file : voxels used for PCA to derive noise components outputspec.filtered_file : bandpass filtered and noise-reduced time series Example ------- >>> TR = 3.0 >>> wf = create_resting_preproc() >>> wf.inputs.inputspec.func = 'f3.nii' >>> wf.inputs.inputspec.num_noise_components = 6 >>> wf.inputs.inputspec.highpass_sigma = 100/(2*TR) >>> wf.inputs.inputspec.lowpass_sigma = 12.5/(2*TR) >>> wf.run() # doctest: +SKIP """ restpreproc = pe.Workflow(name=name) # Define nodes inputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'func', 'num_noise_components', 'highpass_sigma', 'lowpass_sigma' ]), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'noise_mask_file', 'filtered_file', 'motion_rms_files', 'motion_par_file', 'realigned_file', 'mask_file', 'outlier_files', 'intensity_files', 'outlier_plots' ]), name='outputspec') slicetimer = pe.Node(fsl.SliceTimer(), name='slicetimer') realigner = create_realign_flow() art_detector = pe.Node(ArtifactDetect(), name='art_detector') art_detector.inputs.parameter_source = 'FSL' art_detector.inputs.mask_type = 'spm_global' art_detector.inputs.global_threshold = .5 art_detector.inputs.norm_threshold = .6 art_detector.inputs.use_differences = [True, True] ## [Movement, Intensity] art_detector.inputs.zintensity_threshold = 3 art_detector.inputs.intersect_mask = True '''Mask smoother node, added by Pablo Polosecki to use EPI mask''' mask_smoother = pe.Node(util.Function(input_names=['vol_in'], output_names=['out_vol'], function=morph_open_close), name='mask_smoother') tsnr = pe.Node(TSNR(regress_poly=2), name='tsnr') getthresh = pe.Node(interface=fsl.ImageStats(op_string='-k %s -p 98'), name='getthreshold') threshold_stddev = pe.Node(fsl.Threshold(), name='threshold') ''' Mask conjunction, to limit noisy voxels to those inside brain mask''' conj_masker = pe.Node(fsl.BinaryMaths(operation='mul'), name='conj_masker') compcor = pe.Node(util.Function( input_names=['realigned_file', 'noise_mask_file', 'num_components'], output_names=['noise_components'], function=extract_noise_components), name='compcorr') # cat_regressors = pe.Node(util.Function(input_names=['file1', # 'file2'], # output_names=['out_fn'], # function=concatetante_reg_files), # name='cat_regressors') remove_noise = pe.Node(fsl.FilterRegressor(filter_all=True), name='remove_noise') bandpass_filter = pe.Node(fsl.TemporalFilter(), name='bandpass_filter') # Define connections restpreproc.connect(inputnode, 'func', slicetimer, 'in_file') restpreproc.connect(slicetimer, 'slice_time_corrected_file', realigner, 'inputspec.func') restpreproc.connect(realigner, 'outputspec.realigned_file', tsnr, 'in_file') restpreproc.connect(tsnr, 'stddev_file', threshold_stddev, 'in_file') restpreproc.connect(tsnr, 'stddev_file', getthresh, 'in_file') restpreproc.connect(mask_smoother, 'out_vol', getthresh, 'mask_file') restpreproc.connect(getthresh, 'out_stat', threshold_stddev, 'thresh') restpreproc.connect(realigner, 'outputspec.realigned_file', compcor, 'realigned_file') restpreproc.connect(inputnode, 'num_noise_components', compcor, 'num_components') restpreproc.connect(tsnr, 'detrended_file', remove_noise, 'in_file') # Combiinng compcorr with motion regressors: #restpreproc.connect(compcor, 'noise_components', # cat_regressors, 'file1') #restpreproc.connect(realigner, 'outputspec.par_file', # cat_regressors, 'file2') #restpreproc.connect(cat_regressors, 'out_fn', # remove_noise, 'design_file') restpreproc.connect(compcor, 'noise_components', remove_noise, 'design_file') restpreproc.connect(inputnode, 'highpass_sigma', bandpass_filter, 'highpass_sigma') restpreproc.connect(inputnode, 'lowpass_sigma', bandpass_filter, 'lowpass_sigma') restpreproc.connect(remove_noise, 'out_file', bandpass_filter, 'in_file') restpreproc.connect(conj_masker, 'out_file', outputnode, 'noise_mask_file') restpreproc.connect(bandpass_filter, 'out_file', outputnode, 'filtered_file') restpreproc.connect(realigner, 'outputspec.rms_files', outputnode, 'motion_rms_files') restpreproc.connect(realigner, 'outputspec.par_file', outputnode, 'motion_par_file') restpreproc.connect(realigner, 'outputspec.realigned_file', outputnode, 'realigned_file') restpreproc.connect(realigner, 'outputspec.realigned_file', art_detector, 'realigned_files') restpreproc.connect(realigner, 'outputspec.par_file', art_detector, 'realignment_parameters') restpreproc.connect(art_detector, 'mask_files', mask_smoother, 'vol_in') restpreproc.connect(mask_smoother, 'out_vol', outputnode, 'mask_file') restpreproc.connect(art_detector, 'outlier_files', outputnode, 'outlier_files') restpreproc.connect(art_detector, 'intensity_files', outputnode, 'intensity_files') #restpreproc.connect(art_detector, 'plot_files', # outputnode, 'outlier_plots') restpreproc.connect(mask_smoother, 'out_vol', conj_masker, 'in_file') restpreproc.connect(threshold_stddev, 'out_file', conj_masker, 'operand_file') restpreproc.connect(conj_masker, 'out_file', compcor, 'noise_mask_file') return restpreproc
def run(self): matlab_cmd = self.paths['spm_path'] + ' ' + self.paths[ 'mcr_path'] + '/ script' spm.SPMCommand.set_mlab_paths(matlab_cmd=matlab_cmd, use_mcr=True) print(matlab_cmd) print('SPM version: ' + str(spm.SPMCommand().version)) experiment_dir = opj(self.paths['input_path'], 'output/') output_dir = 'datasink' working_dir = 'workingdir' subject_list = self.subject_list # list of subject identifiers fwhm = self.parameters[ 'fwhm'] # Smoothing widths to apply (Gaussian kernel size) tr = self.parameters['tr'] # Repetition time init_volume = self.parameters[ 'init_volume'] # Firts volumen identification which will use in the pipeline iso_size = self.parameters[ 'iso_size'] # Isometric resample of functional images to voxel size (in mm) low_pass = self.parameters['low_pass'] high_pass = self.parameters['high_pass'] t1_relative_path = self.paths['t1_relative_path'] fmri_relative_path = self.paths['fmri_relative_path'] # ExtractROI - skip dummy scans extract = Node(ExtractROI(t_min=init_volume, t_size=-1, output_type='NIFTI'), name="extract") #FSL # MCFLIRT - motion correction mcflirt = Node(MCFLIRT(mean_vol=True, save_plots=True, output_type='NIFTI'), name="motion_correction") #FSL # SliceTimer - correct for slice wise acquisition slicetimer = Node(SliceTimer(index_dir=False, interleaved=True, output_type='NIFTI', time_repetition=tr), name="slice_timing_correction") #FSL # Smooth - image smoothing denoise = Node(Denoise(), name="denoising") #Interfaces with dipy smooth = Node(spm.Smooth(fwhm=fwhm), name="smooth") #SPM n4bias = Node(N4Bias(out_file='t1_n4bias.nii.gz'), name='n4bias') #Interface with SimpleITK descomposition = Node(Descomposition(n_components=20, low_pass=0.1, high_pass=0.01, tr=tr), name='descomposition') #Interface with nilearn # Artifact Detection - determines outliers in functional images art = Node(ArtifactDetect(norm_threshold=2, zintensity_threshold=3, mask_type='spm_global', parameter_source='FSL', use_differences=[True, False], plot_type='svg'), name="artifact_detection") #Rapidart extract_confounds_ws_csf = Node( ExtractConfounds(out_file='ev_without_gs.csv'), name='extract_confounds_ws_csf') #Interfece extract_confounds_gs = Node(ExtractConfounds(out_file='ev_with_gs.csv', delimiter=','), name='extract_confounds_global_signal') signal_extraction = Node(SignalExtraction( time_series_out_file='time_series.csv', correlation_matrix_out_file='correlation_matrix.png', labels_parcellation_path=self.paths['labels_parcellation_path'], mask_mni_path=self.paths['mask_mni_path'], tr=tr, low_pass=low_pass, high_pass=high_pass, plot=False), name='signal_extraction') signal_extraction.iterables = [('image_parcellation_path', self.paths['image_parcellation_path'])] art_remotion = Node( ArtifacRemotion(out_file='fmri_art_removed.nii'), name='artifact_remotion') #This interface requires implementation # BET - Skullstrip anatomical anf funtional images bet_t1 = Node(BET(frac=0.5, robust=True, mask=True, output_type='NIFTI_GZ'), name="bet_t1") #FSL # FAST - Image Segmentation segmentation = Node(FAST(output_type='NIFTI'), name="segmentation") #FSL # Normalize - normalizes functional and structural images to the MNI template normalize_fmri = Node(Normalize12( jobtype='estwrite', tpm=self.paths['template_spm_path'], write_voxel_sizes=[iso_size, iso_size, iso_size], write_bounding_box=[[-90, -126, -72], [90, 90, 108]]), name="normalize_fmri") #SPM gunzip = Node(Gunzip(), name="gunzip") normalize_t1 = Node(Normalize12( jobtype='estwrite', tpm=self.paths['template_spm_path'], write_voxel_sizes=[iso_size, iso_size, iso_size], write_bounding_box=[[-90, -126, -72], [90, 90, 108]]), name="normalize_t1") normalize_masks = Node(Normalize12( jobtype='estwrite', tpm=self.paths['template_spm_path'], write_voxel_sizes=[iso_size, iso_size, iso_size], write_bounding_box=[[-90, -126, -72], [90, 90, 108]]), name="normalize_masks") # Threshold - Threshold WM probability image threshold = Node(Threshold(thresh=0.5, args='-bin', output_type='NIFTI_GZ'), name="wm_mask_threshold") # FLIRT - pre-alignment of functional images to anatomical images coreg_pre = Node(FLIRT(dof=6, output_type='NIFTI_GZ'), name="linear_warp_estimation") # FLIRT - coregistration of functional images to anatomical images with BBR coreg_bbr = Node(FLIRT(dof=6, cost='bbr', schedule=opj(os.getenv('FSLDIR'), 'etc/flirtsch/bbr.sch'), output_type='NIFTI_GZ'), name="nonlinear_warp_estimation") # Apply coregistration warp to functional images applywarp = Node(FLIRT(interp='spline', apply_isoxfm=iso_size, output_type='NIFTI'), name="registration_fmri") # Apply coregistration warp to mean file applywarp_mean = Node(FLIRT(interp='spline', apply_isoxfm=iso_size, output_type='NIFTI_GZ'), name="registration_mean_fmri") # Infosource - a function free node to iterate over the list of subject names infosource = Node(IdentityInterface(fields=['subject_id']), name="infosource") infosource.iterables = [('subject_id', subject_list)] # SelectFiles - to grab the data (alternativ to DataGrabber) anat_file = opj('{subject_id}', t1_relative_path) func_file = opj('{subject_id}', fmri_relative_path) #anat_file = opj('{subject_id}/anat/', 'data.nii') #func_file = opj('{subject_id}/func/', 'data.nii') templates = {'anat': anat_file, 'func': func_file} selectfiles = Node(SelectFiles( templates, base_directory=self.paths['input_path']), name="selectfiles") # Datasink - creates output folder for important outputs datasink = Node(DataSink(base_directory=experiment_dir, container=output_dir), name="datasink") # Create a coregistration workflow coregwf = Workflow(name='coreg_fmri_to_t1') coregwf.base_dir = opj(experiment_dir, working_dir) # Create a preprocessing workflow preproc = Workflow(name='preproc') preproc.base_dir = opj(experiment_dir, working_dir) # Connect all components of the coregistration workflow coregwf.connect([ (bet_t1, n4bias, [('out_file', 'in_file')]), (n4bias, segmentation, [('out_file', 'in_files')]), (segmentation, threshold, [(('partial_volume_files', get_latest), 'in_file')]), (n4bias, coreg_pre, [('out_file', 'reference')]), (threshold, coreg_bbr, [('out_file', 'wm_seg')]), (coreg_pre, coreg_bbr, [('out_matrix_file', 'in_matrix_file')]), (coreg_bbr, applywarp, [('out_matrix_file', 'in_matrix_file')]), (n4bias, applywarp, [('out_file', 'reference')]), (coreg_bbr, applywarp_mean, [('out_matrix_file', 'in_matrix_file') ]), (n4bias, applywarp_mean, [('out_file', 'reference')]), ]) ## Use the following DataSink output substitutions substitutions = [('_subject_id_', 'sub-')] # ('_fwhm_', 'fwhm-'), # ('_roi', ''), # ('_mcf', ''), # ('_st', ''), # ('_flirt', ''), # ('.nii_mean_reg', '_mean'), # ('.nii.par', '.par'), # ] # subjFolders = [('fwhm-%s/' % f, 'fwhm-%s_' % f) for f in fwhm] # substitutions.extend(subjFolders) datasink.inputs.substitutions = substitutions # Connect all components of the preprocessing workflow preproc.connect([ (infosource, selectfiles, [('subject_id', 'subject_id')]), (selectfiles, extract, [('func', 'in_file')]), (extract, mcflirt, [('roi_file', 'in_file')]), (mcflirt, slicetimer, [('out_file', 'in_file')]), (selectfiles, denoise, [('anat', 'in_file')]), (denoise, coregwf, [('out_file', 'bet_t1.in_file'), ('out_file', 'nonlinear_warp_estimation.reference')]), (mcflirt, coregwf, [('mean_img', 'linear_warp_estimation.in_file'), ('mean_img', 'nonlinear_warp_estimation.in_file'), ('mean_img', 'registration_mean_fmri.in_file')]), (slicetimer, coregwf, [('slice_time_corrected_file', 'registration_fmri.in_file')]), (coregwf, art, [('registration_fmri.out_file', 'realigned_files') ]), (mcflirt, art, [('par_file', 'realignment_parameters')]), (art, art_remotion, [('outlier_files', 'outlier_files')]), (coregwf, art_remotion, [('registration_fmri.out_file', 'in_file') ]), (coregwf, gunzip, [('n4bias.out_file', 'in_file')]), (selectfiles, normalize_fmri, [('anat', 'image_to_align')]), (art_remotion, normalize_fmri, [('out_file', 'apply_to_files')]), (selectfiles, normalize_t1, [('anat', 'image_to_align')]), (gunzip, normalize_t1, [('out_file', 'apply_to_files')]), (selectfiles, normalize_masks, [('anat', 'image_to_align')]), (coregwf, normalize_masks, [(('segmentation.partial_volume_files', get_wm_csf), 'apply_to_files')]), (normalize_fmri, smooth, [('normalized_files', 'in_files')]), (smooth, extract_confounds_ws_csf, [('smoothed_files', 'in_file') ]), (normalize_masks, extract_confounds_ws_csf, [('normalized_files', 'list_mask')]), (mcflirt, extract_confounds_ws_csf, [('par_file', 'file_concat')]), (art, extract_confounds_ws_csf, [('outlier_files', 'outlier_files') ]), # (smooth, extract_confounds_gs, [('smoothed_files', 'in_file')]), # (normalize_t1, extract_confounds_gs, [(('normalized_files',change_to_list), 'list_mask')]), # (extract_confounds_ws_csf, extract_confounds_gs, [('out_file', 'file_concat')]), (smooth, signal_extraction, [('smoothed_files', 'in_file')]), # (extract_confounds_gs, signal_extraction, [('out_file', 'confounds_file')]), (extract_confounds_ws_csf, signal_extraction, [('out_file', 'confounds_file')]), #(smooth, descomposition, [('smoothed_files', 'in_file')]), #(extract_confounds_ws_csf, descomposition, [('out_file', 'confounds_file')]), # (extract_confounds_gs, datasink, [('out_file', 'preprocessing.@confounds_with_gs')]), (denoise, datasink, [('out_file', 'preprocessing.@t1_denoised')]), (extract_confounds_ws_csf, datasink, [('out_file', 'preprocessing.@confounds_without_gs')]), (smooth, datasink, [('smoothed_files', 'preprocessing.@smoothed') ]), (normalize_fmri, datasink, [('normalized_files', 'preprocessing.@fmri_normalized')]), (normalize_t1, datasink, [('normalized_files', 'preprocessing.@t1_normalized')]), (normalize_masks, datasink, [('normalized_files', 'preprocessing.@masks_normalized')]), (signal_extraction, datasink, [('time_series_out_file', 'preprocessing.@time_serie')]), (signal_extraction, datasink, [('correlation_matrix_out_file', 'preprocessing.@correlation_matrix')]) ]) #(signal_extraction, datasink, # [('fmri_cleaned_out_file', 'preprocessing.@fmri_cleaned_out_file')])]) #, #(descomposition, datasink, [('out_file', 'preprocessing.@descomposition')]), #(descomposition, datasink, [('plot_files', 'preprocessing.@descomposition_plot_files')]) #]) preproc.write_graph(graph2use='colored', format='png', simple_form=True) preproc.run()