def cbf_outlier_detection_initialize(): od_preproc = pe.Workflow(name='od_preproc') od_preproc.base_dir = (os.getcwd()) od_inputnode = pe.Node(interface=util.IdentityInterface( fields=['cl_mcf', 'cl_mcf_par', 'm0_mask']), name='od_inputnode') # Artifact Detection with od_cl_art = pe.Node(interface=ra.ArtifactDetect( use_differences=[True, False], use_norm=True, norm_threshold=1, zintensity_threshold=3, parameter_source='FSL', mask_type='file'), name="od_cl_art") od_outputnode = pe.Node(interface=util.IdentityInterface( fields=['od_plot', 'od_outliers', 'od_intensity', 'od_statistic']), name='od_outputnode') od_preproc.connect([ (od_inputnode, od_cl_art, [('cl_mcf', 'realigned_files')]), (od_inputnode, od_cl_art, [('cl_mcf_par', 'realignment_parameters')]), (od_inputnode, od_cl_art, [('m0_mask', 'mask_file')]), (od_cl_art, od_outputnode, [['outlier_files', 'od_outliers']]), (od_cl_art, od_outputnode, [['intensity_files', 'od_intensity']]), (od_cl_art, od_outputnode, [['plot_files', 'od_plot']]), (od_cl_art, od_outputnode, [['statistic_files', 'od_statistic']]) ]) return od_preproc
def test_ad_get_affine_matrix(): ad = ra.ArtifactDetect() matrix = ad._get_affine_matrix(np.array([0])) yield assert_equal, matrix, np.eye(4) # test translation params = [1, 2, 3] matrix = ad._get_affine_matrix(params) out = np.eye(4) out[0:3, 3] = params yield assert_equal, matrix, out # test rotation params = np.array([0, 0, 0, np.pi / 2, np.pi / 2, np.pi / 2]) matrix = ad._get_affine_matrix(params) out = np.array([0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]).reshape( (4, 4)) yield assert_almost_equal, matrix, out # test scaling params = np.array([0, 0, 0, 0, 0, 0, 1, 2, 3]) matrix = ad._get_affine_matrix(params) out = np.array([1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1]).reshape( (4, 4)) yield assert_equal, matrix, out # test shear params = np.array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 3]) matrix = ad._get_affine_matrix(params) out = np.array([1, 1, 2, 0, 0, 1, 3, 0, 0, 0, 1, 0, 0, 0, 0, 1]).reshape( (4, 4)) yield assert_equal, matrix, out
def art_mean_workflow(name="take_mean_art"): """Calculates mean image after running art w/ norm = 0.5, z=2 Parameters ---------- name : name of workflow. Default = 'take_mean_art' Inputs ------ inputspec.realigned_files : inputspec.parameter_source : inputspec.realignment_parameters : Outputs ------- outputspec.mean_image : Returns ------- workflow : mean image workflow """ # define workflow import nipype.pipeline.engine as pe import nipype.interfaces.utility as util import nipype.algorithms.rapidart as ra # rapid artifact detection wkflw = pe.Workflow(name=name) # define nodes inputspec = pe.Node(util.IdentityInterface(fields=[ 'realigned_files', 'parameter_source', 'realignment_parameters' ]), name='inputspec') meanimg = pe.Node(util.Function(input_names=['image', 'art_file'], output_names=['mean_image'], function=weight_mean), name='weighted_mean') ad = pe.Node(ra.ArtifactDetect(), name='strict_artifact_detect') outputspec = pe.Node(util.IdentityInterface(fields=['mean_image']), name='outputspec') # inputs ad.inputs.zintensity_threshold = 2 ad.inputs.norm_threshold = 0.5 ad.inputs.use_differences = [True, False] ad.inputs.mask_type = 'spm_global' # connections wkflw.connect(inputspec, 'parameter_source', ad, 'parameter_source') wkflw.connect(inputspec, 'realigned_files', ad, 'realigned_files') wkflw.connect(inputspec, 'realignment_parameters', ad, 'realignment_parameters') wkflw.connect(ad, 'outlier_files', meanimg, 'art_file') wkflw.connect(inputspec, 'realigned_files', meanimg, 'image') wkflw.connect(meanimg, 'mean_image', outputspec, 'mean_image') return wkflw
def test_ad_get_norm(): ad = ra.ArtifactDetect() params = np.array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, np.pi / 4, np.pi / 4, np.pi / 4, 0, 0, 0, -np.pi / 4, -np.pi / 4, -np.pi / 4 ]).reshape((3, 6)) norm = ad._calc_norm(params, False) yield assert_almost_equal, norm, np.array( [18.86436316, 37.74610158, 31.29780829]) norm = ad._calc_norm(params, True) yield assert_almost_equal, norm, np.array([0., 143.72192614, 173.92527131])
def test_ad_output_filenames(): ad = ra.ArtifactDetect() outputdir = '/tmp' f = 'motion.nii' outlierfile, intensityfile, statsfile, normfile, plotfile = ad._get_output_filenames( f, outputdir) yield assert_equal, outlierfile, '/tmp/art.motion_outliers.txt' yield assert_equal, intensityfile, '/tmp/global_intensity.motion.txt' yield assert_equal, statsfile, '/tmp/stats.motion.txt' yield assert_equal, normfile, '/tmp/norm.motion.txt' yield assert_equal, plotfile, '/tmp/plot.motion.png'
def run_artdetect(file4d, param_file, thresh=4, param_source='SPM'): startdir = os.getcwd() pth, _ = os.path.split(param_file) os.chdir(pth) ad = rapidart.ArtifactDetect() ad.inputs.realigned_files = file4d ad.inputs.realignment_parameters = param_file ad.inputs.parameter_source = param_source ad.inputs.norm_threshold = 1 ad.inputs.mask_type = 'spm_global' ad.inputs.use_differences = [True, False] ad.inputs.zintensity_threshold = thresh adout = ad.run() os.chdir(startdir) return adout
def run_artdetect(file4d, param_file): startdir = os.getcwd() pth, _ = os.path.split(file4d) os.chdir(pth) ad = rapidart.ArtifactDetect() ad.inputs.realigned_files = file4d ad.inputs.realignment_parameters = param_file ad.inputs.parameter_source = 'SPM' ad.inputs.norm_threshold = 3 ad.inputs.use_differences = [True, False] ad.inputs.zintensity_threshold = 5 ad.inputs.mask_type = 'thresh' ad.inputs.mask_threshold = -100 ad.inputs.save_plot = False adout = ad.run() os.chdir(startdir) return adout
def test_artifactdetect(): input_map = dict( intersect_mask=dict(), mask_file=dict(), mask_threshold=dict(), mask_type=dict(), norm_threshold=dict(), parameter_source=dict(mandatory=True, ), realigned_files=dict(mandatory=True, ), realignment_parameters=dict(), rotation_threshold=dict(), translation_threshold=dict(), use_differences=dict(usedefault=True, ), use_norm=dict(usedefault=True, ), zintensity_threshold=dict(), ) instance = ra.ArtifactDetect() for key, metadata in input_map.items(): for metakey, value in metadata.items(): yield assert_equal, getattr(instance.inputs.traits()[key], metakey), value
def analyze_openfmri_dataset(data_dir, subject=None, model_id=None, task_id=None, output_dir=None): """Analyzes an open fmri dataset Parameters ---------- data_dir : str Path to the base data directory work_dir : str Nipype working directory (defaults to cwd) """ """ Load nipype workflows """ preproc = create_featreg_preproc(whichvol='first') modelfit = create_modelfit_workflow() fixed_fx = create_fixed_effects_flow() registration = create_reg_workflow() """ Remove the plotting connection so that plot iterables don't propagate to the model stage """ preproc.disconnect(preproc.get_node('plot_motion'), 'out_file', preproc.get_node('outputspec'), 'motion_plots') """ Set up openfmri data specific components """ subjects = sorted([path.split(os.path.sep)[-1] for path in glob(os.path.join(data_dir, 'sub*'))]) infosource = pe.Node(niu.IdentityInterface(fields=['subject_id', 'model_id', 'task_id']), name='infosource') if subject is None: infosource.iterables = [('subject_id', subjects), ('model_id', [model_id]), ('task_id', [task_id])] else: infosource.iterables = [('subject_id', [subjects[subjects.index(subject)]]), ('model_id', [model_id]), ('task_id', [task_id])] subjinfo = pe.Node(niu.Function(input_names=['subject_id', 'base_dir', 'task_id', 'model_id'], output_names=['run_id', 'conds', 'TR'], function=get_subjectinfo), name='subjectinfo') subjinfo.inputs.base_dir = data_dir """ Return data components as anat, bold and behav """ datasource = pe.Node(nio.DataGrabber(infields=['subject_id', 'run_id', 'task_id', 'model_id'], outfields=['anat', 'bold', 'behav', 'contrasts']), name='datasource') datasource.inputs.base_directory = data_dir datasource.inputs.template = '*' datasource.inputs.field_template = {'anat': '%s/anatomy/highres001.nii.gz', 'bold': '%s/BOLD/task%03d_r*/bold.nii.gz', 'behav': ('%s/model/model%03d/onsets/task%03d_' 'run%03d/cond*.txt'), 'contrasts': ('models/model%03d/' 'task_contrasts.txt')} datasource.inputs.template_args = {'anat': [['subject_id']], 'bold': [['subject_id', 'task_id']], 'behav': [['subject_id', 'model_id', 'task_id', 'run_id']], 'contrasts': [['model_id']]} datasource.inputs.sort_filelist = True """ Create meta workflow """ wf = pe.Workflow(name='openfmri') wf.connect(infosource, 'subject_id', subjinfo, 'subject_id') wf.connect(infosource, 'model_id', subjinfo, 'model_id') wf.connect(infosource, 'task_id', subjinfo, 'task_id') wf.connect(infosource, 'subject_id', datasource, 'subject_id') wf.connect(infosource, 'model_id', datasource, 'model_id') wf.connect(infosource, 'task_id', datasource, 'task_id') wf.connect(subjinfo, 'run_id', datasource, 'run_id') wf.connect([(datasource, preproc, [('bold', 'inputspec.func')]), ]) def get_highpass(TR, hpcutoff): return hpcutoff / (2 * TR) gethighpass = pe.Node(niu.Function(input_names=['TR', 'hpcutoff'], output_names=['highpass'], function=get_highpass), name='gethighpass') wf.connect(subjinfo, 'TR', gethighpass, 'TR') wf.connect(gethighpass, 'highpass', preproc, 'inputspec.highpass') """ Setup a basic set of contrasts, a t-test per condition """ def get_contrasts(contrast_file, task_id, conds): import numpy as np contrast_def = np.genfromtxt(contrast_file, dtype=object) if len(contrast_def.shape) == 1: contrast_def = contrast_def[None, :] contrasts = [] for row in contrast_def: if row[0] != 'task%03d' % task_id: continue con = [row[1], 'T', ['cond%03d' % (i + 1) for i in range(len(conds))], row[2:].astype(float).tolist()] contrasts.append(con) # add auto contrasts for each column for i, cond in enumerate(conds): con = [cond, 'T', ['cond%03d' % (i + 1)], [1]] contrasts.append(con) return contrasts contrastgen = pe.Node(niu.Function(input_names=['contrast_file', 'task_id', 'conds'], output_names=['contrasts'], function=get_contrasts), name='contrastgen') art = pe.MapNode(interface=ra.ArtifactDetect(use_differences=[True, False], use_norm=True, norm_threshold=1, zintensity_threshold=3, parameter_source='FSL', mask_type='file'), iterfield=['realigned_files', 'realignment_parameters', 'mask_file'], name="art") modelspec = pe.Node(interface=model.SpecifyModel(), name="modelspec") modelspec.inputs.input_units = 'secs' wf.connect(subjinfo, 'TR', modelspec, 'time_repetition') wf.connect(datasource, 'behav', modelspec, 'event_files') wf.connect(subjinfo, 'TR', modelfit, 'inputspec.interscan_interval') wf.connect(subjinfo, 'conds', contrastgen, 'conds') wf.connect(datasource, 'contrasts', contrastgen, 'contrast_file') wf.connect(infosource, 'task_id', contrastgen, 'task_id') wf.connect(contrastgen, 'contrasts', modelfit, 'inputspec.contrasts') wf.connect([(preproc, art, [('outputspec.motion_parameters', 'realignment_parameters'), ('outputspec.realigned_files', 'realigned_files'), ('outputspec.mask', 'mask_file')]), (preproc, modelspec, [('outputspec.highpassed_files', 'functional_runs'), ('outputspec.motion_parameters', 'realignment_parameters')]), (art, modelspec, [('outlier_files', 'outlier_files')]), (modelspec, modelfit, [('session_info', 'inputspec.session_info')]), (preproc, modelfit, [('outputspec.highpassed_files', 'inputspec.functional_data')]) ]) """ Reorder the copes so that now it combines across runs """ def sort_copes(files): numelements = len(files[0]) outfiles = [] for i in range(numelements): outfiles.insert(i, []) for j, elements in enumerate(files): outfiles[i].append(elements[i]) return outfiles def num_copes(files): return len(files) pickfirst = lambda x: x[0] wf.connect([(preproc, fixed_fx, [(('outputspec.mask', pickfirst), 'flameo.mask_file')]), (modelfit, fixed_fx, [(('outputspec.copes', sort_copes), 'inputspec.copes'), ('outputspec.dof_file', 'inputspec.dof_files'), (('outputspec.varcopes', sort_copes), 'inputspec.varcopes'), (('outputspec.copes', num_copes), 'l2model.num_copes'), ]) ]) wf.connect(preproc, 'outputspec.mean', registration, 'inputspec.mean_image') wf.connect(datasource, 'anat', registration, 'inputspec.anatomical_image') registration.inputs.inputspec.target_image = fsl.Info.standard_image('MNI152_T1_2mm.nii.gz') def merge_files(copes, varcopes): out_files = [] splits = [] out_files.extend(copes) splits.append(len(copes)) out_files.extend(varcopes) splits.append(len(varcopes)) return out_files, splits mergefunc = pe.Node(niu.Function(input_names=['copes', 'varcopes'], output_names=['out_files', 'splits'], function=merge_files), name='merge_files') wf.connect([(fixed_fx.get_node('outputspec'), mergefunc, [('copes', 'copes'), ('varcopes', 'varcopes'), ])]) wf.connect(mergefunc, 'out_files', registration, 'inputspec.source_files') def split_files(in_files, splits): copes = in_files[:splits[1]] varcopes = in_files[splits[1]:] return copes, varcopes splitfunc = pe.Node(niu.Function(input_names=['in_files', 'splits'], output_names=['copes', 'varcopes'], function=split_files), name='split_files') wf.connect(mergefunc, 'splits', splitfunc, 'splits') wf.connect(registration, 'outputspec.transformed_files', splitfunc, 'in_files') """ Connect to a datasink """ def get_subs(subject_id, conds, model_id, task_id): subs = [('_subject_id_%s_' % subject_id, '')] subs.append(('_model_id_%d' % model_id, 'model%03d' %model_id)) subs.append(('task_id_%d/' % task_id, '/task%03d_' % task_id)) subs.append(('bold_dtype_mcf_mask_smooth_mask_gms_tempfilt_mean_warp_warp', 'mean')) for i in range(len(conds)): subs.append(('_flameo%d/cope1.' % i, 'cope%02d.' % (i + 1))) subs.append(('_flameo%d/varcope1.' % i, 'varcope%02d.' % (i + 1))) subs.append(('_flameo%d/zstat1.' % i, 'zstat%02d.' % (i + 1))) subs.append(('_flameo%d/tstat1.' % i, 'tstat%02d.' % (i + 1))) subs.append(('_flameo%d/res4d.' % i, 'res4d%02d.' % (i + 1))) subs.append(('_warpall%d/cope1_warp_warp.' % i, 'cope%02d.' % (i + 1))) subs.append(('_warpall%d/varcope1_warp_warp.' % (len(conds) + i), 'varcope%02d.' % (i + 1))) return subs subsgen = pe.Node(niu.Function(input_names=['subject_id', 'conds', 'model_id', 'task_id'], output_names=['substitutions'], function=get_subs), name='subsgen') datasink = pe.Node(interface=nio.DataSink(), name="datasink") wf.connect(infosource, 'subject_id', datasink, 'container') wf.connect(infosource, 'subject_id', subsgen, 'subject_id') wf.connect(infosource, 'model_id', subsgen, 'model_id') wf.connect(infosource, 'task_id', subsgen, 'task_id') wf.connect(contrastgen, 'contrasts', subsgen, 'conds') wf.connect(subsgen, 'substitutions', datasink, 'substitutions') wf.connect([(fixed_fx.get_node('outputspec'), datasink, [('res4d', 'res4d'), ('copes', 'copes'), ('varcopes', 'varcopes'), ('zstats', 'zstats'), ('tstats', 'tstats')]) ]) wf.connect([(splitfunc, datasink, [('copes', 'copes.mni'), ('varcopes', 'varcopes.mni'), ])]) wf.connect(registration, 'outputspec.transformed_mean', datasink, 'mean.mni') """ Set processing parameters """ hpcutoff = 120. preproc.inputs.inputspec.fwhm = 6.0 gethighpass.inputs.hpcutoff = hpcutoff modelspec.inputs.high_pass_filter_cutoff = hpcutoff modelfit.inputs.inputspec.bases = {'dgamma': {'derivs': True}} modelfit.inputs.inputspec.model_serial_correlations = True modelfit.inputs.inputspec.film_threshold = 1000 datasink.inputs.base_directory = output_dir return wf
def fsl_run_level_wf( model, step, bids_dir, output_dir, work_dir, subject_id, database_path, smoothing_fwhm=None, smoothing_level=None, smoothing_type=None, use_rapidart=False, detrend_poly=None, align_volumes=None, smooth_autocorrelations=False, despike=False, name="fsl_run_level_wf", ): """Generate run level workflow for a given model.""" bids_dir = Path(bids_dir) work_dir = Path(work_dir) workflow = pe.Workflow(name=name) level = step["Level"] dimensionality = 3 # Nipype FSL.SUSAN Default if smoothing_type == "inp": dimensionality = 2 workflow.__desc__ = "" (work_dir / model["Name"]).mkdir(exist_ok=True) include_entities = {} if "Input" in model: if "Include" in model["Input"]: include_entities = model["Input"]["Include"] include_entities.update({"subject": subject_id}) getter = pe.Node( BIDSGet( database_path=database_path, fixed_entities=include_entities, align_volumes=align_volumes, ), name="func_select", ) get_info = pe.MapNode( GetRunModelInfo(model=step, detrend_poly=detrend_poly), iterfield=[ "metadata_file", "regressor_file", "events_file", "entities" ], name=f"get_{level}_info", ) despiker = pe.MapNode( afni.Despike(outputtype="NIFTI_GZ"), iterfield=["in_file"], name="despiker", ) realign_runs = pe.MapNode( fsl.MCFLIRT(output_type="NIFTI_GZ", interpolation="sinc"), iterfield=["in_file", "ref_file"], name="func_realign", ) wrangle_volumes = pe.MapNode( IdentityInterface(fields=["functional_file"]), iterfield=["functional_file"], name="wrangle_volumes", ) specify_model = pe.MapNode( modelgen.SpecifyModel(high_pass_filter_cutoff=-1.0, input_units="secs"), iterfield=["functional_runs", "subject_info", "time_repetition"], name=f"model_{level}_specify", ) fit_model = pe.MapNode( IdentityInterface( fields=[ "session_info", "interscan_interval", "contrasts", "functional_data" ], mandatory_inputs=True, ), iterfield=[ "functional_data", "session_info", "interscan_interval", "contrasts" ], name=f"model_{level}_fit", ) first_level_design = pe.MapNode( fsl.Level1Design( bases={"dgamma": { "derivs": False }}, model_serial_correlations=False, ), iterfield=["session_info", "interscan_interval", "contrasts"], name=f"model_{level}_design", ) generate_model = pe.MapNode( fsl.FEATModel(output_type="NIFTI_GZ"), iterfield=["fsf_file", "ev_files"], name=f"model_{level}_generate", ) estimate_model = pe.MapNode( fsl.FILMGLS( threshold=0.0, # smooth_autocorr=True output_type="NIFTI_GZ", results_dir="results", smooth_autocorr=False, autocorr_noestimate=True, ), iterfield=["design_file", "in_file", "tcon_file"], name=f"model_{level}_estimate", ) if smooth_autocorrelations: first_level_design.inputs.model_serial_correlations = True estimate_model.inputs.smooth_autocorr = True estimate_model.inputs.autocorr_noestimate = False calculate_p = pe.MapNode( fsl.ImageMaths(output_type="NIFTI_GZ", op_string="-ztop", suffix="_pval"), iterfield=["in_file"], name=f"model_{level}_caculate_p", ) image_pattern = ("[sub-{subject}/][ses-{session}/]" "[sub-{subject}_][ses-{session}_]" "task-{task}_[acq-{acquisition}_]" "[rec-{reconstruction}_][run-{run}_]" "[echo-{echo}_][space-{space}_]contrast-{contrast}_" "stat-{stat<effect|variance|z|p|t|F>}_statmap.nii.gz") run_rapidart = pe.MapNode( ra.ArtifactDetect( use_differences=[True, False], use_norm=True, zintensity_threshold=3, norm_threshold=1, bound_by_brainmask=True, mask_type="file", parameter_source="FSL", ), iterfield=["realignment_parameters", "realigned_files", "mask_file"], name="rapidart_run", ) reshape_rapidart = pe.MapNode( Function( input_names=[ "run_info", "functional_file", "outlier_file", "contrast_entities" ], output_names=["run_info", "contrast_entities"], function=utils.reshape_ra, ), iterfield=[ "run_info", "functional_file", "outlier_file", "contrast_entities" ], name="reshape_rapidart", ) mean_img = pe.MapNode( fsl.ImageMaths(output_type="NIFTI_GZ", op_string="-Tmean", suffix="_mean"), iterfield=["in_file", "mask_file"], name="smooth_susan_avgimg", ) median_img = pe.MapNode( fsl.ImageStats(output_type="NIFTI_GZ", op_string="-k %s -p 50"), iterfield=["in_file", "mask_file"], name="smooth_susan_medimg", ) merge = pe.Node(Merge(2, axis="hstack"), name="smooth_merge") run_susan = pe.MapNode( fsl.SUSAN(output_type="NIFTI_GZ"), iterfield=["in_file", "brightness_threshold", "usans"], name="smooth_susan", ) mask_functional = pe.MapNode(ApplyMask(), iterfield=["in_file", "mask_file"], name="mask_functional") # Exists solely to correct undesirable behavior of FSL # that results in loss of constant columns correct_matrices = pe.MapNode( Function( input_names=["design_matrix"], output_names=["design_matrix"], function=utils.correct_matrix, ), iterfield=["design_matrix"], run_without_submitting=True, name=f"correct_{level}_matrices", ) collate = pe.Node( MergeAll( fields=[ "effect_maps", "variance_maps", "zscore_maps", "pvalue_maps", "tstat_maps", "contrast_metadata", ], check_lengths=True, ), name=f"collate_{level}", ) collate_outputs = pe.Node( CollateWithMetadata( fields=[ "effect_maps", "variance_maps", "zscore_maps", "pvalue_maps", "tstat_maps" ], field_to_metadata_map={ "effect_maps": { "stat": "effect" }, "variance_maps": { "stat": "variance" }, "zscore_maps": { "stat": "z" }, "pvalue_maps": { "stat": "p" }, "tstat_maps": { "stat": "t" }, }, ), name=f"collate_{level}_outputs", ) plot_matrices = pe.MapNode( PlotMatrices(output_dir=output_dir, database_path=database_path), iterfield=["mat_file", "con_file", "entities", "run_info"], run_without_submitting=True, name=f"plot_{level}_matrices", ) ds_contrast_maps = pe.MapNode( BIDSDataSink(base_directory=output_dir, path_patterns=image_pattern), iterfield=["entities", "in_file"], run_without_submitting=True, name=f"ds_{level}_contrast_maps", ) wrangle_outputs = pe.Node( IdentityInterface(fields=["contrast_metadata", "contrast_maps"]), name=f"wrangle_{level}_outputs", ) # Setup connections among nodes workflow.connect([( getter, get_info, [ ("metadata_files", "metadata_file"), ("events_files", "events_file"), ("regressor_files", "regressor_file"), ("entities", "entities"), ], )]) if align_volumes and despike: workflow.connect([ (getter, despiker, [("functional_files", "in_file")]), (despiker, realign_runs, [("out_file", "in_file")]), (getter, realign_runs, [("reference_files", "ref_file")]), ( realign_runs, wrangle_volumes, [("out_file", "functional_file")], ), ]) elif align_volumes and not despike: workflow.connect([ ( getter, realign_runs, [("functional_files", "in_file"), ("reference_files", "ref_file")], ), ( realign_runs, wrangle_volumes, [("out_file", "functional_file")], ), ]) elif despike: workflow.connect([ (getter, despiker, [("functional_files", "in_file")]), (despiker, wrangle_volumes, [("out_file", "functional_file")]), ]) else: workflow.connect([(getter, wrangle_volumes, [("functional_files", "functional_file")])]) if use_rapidart: workflow.connect([ (get_info, run_rapidart, [("motion_parameters", "realignment_parameters")]), (getter, run_rapidart, [("mask_files", "mask_file")]), ( wrangle_volumes, run_rapidart, [("functional_file", "realigned_files")], ), ( run_rapidart, reshape_rapidart, [("outlier_files", "outlier_file")], ), ( get_info, reshape_rapidart, [("run_info", "run_info"), ("contrast_entities", "contrast_entities")], ), (wrangle_volumes, reshape_rapidart, [("functional_file", "functional_file")]), ( reshape_rapidart, specify_model, [("run_info", "subject_info")], ), (reshape_rapidart, plot_matrices, [("run_info", "run_info")]), (reshape_rapidart, collate, [("contrast_entities", "contrast_metadata")]), ]) else: workflow.connect([ (get_info, specify_model, [("run_info", "subject_info")]), (get_info, plot_matrices, [("run_info", "run_info")]), ( get_info, collate, [("contrast_entities", "contrast_metadata")], ), ]) if smoothing_level == "l1" or smoothing_level == "run": run_susan.inputs.fwhm = smoothing_fwhm run_susan.inputs.dimension = dimensionality estimate_model.inputs.mask_size = smoothing_fwhm workflow.connect([ (wrangle_volumes, mean_img, [("functional_file", "in_file")]), ( wrangle_volumes, median_img, [("functional_file", "in_file")], ), (getter, mean_img, [("mask_files", "mask_file")]), (getter, median_img, [("mask_files", "mask_file")]), (mean_img, merge, [("out_file", "in1")]), (median_img, merge, [("out_stat", "in2")]), (wrangle_volumes, run_susan, [("functional_file", "in_file")]), ( median_img, run_susan, [( ("out_stat", utils.get_btthresh), "brightness_threshold", )], ), (merge, run_susan, [(("out", utils.get_usans), "usans")]), (getter, mask_functional, [("mask_files", "mask_file")]), (run_susan, mask_functional, [("smoothed_file", "in_file")]), ( mask_functional, specify_model, [("out_file", "functional_runs")], ), ( mask_functional, fit_model, [("out_file", "functional_data")], ), ]) else: workflow.connect([ (getter, mask_functional, [("mask_files", "mask_file")]), ( wrangle_volumes, mask_functional, [("functional_file", "in_file")], ), ( mask_functional, specify_model, [("out_file", "functional_runs")], ), ( mask_functional, fit_model, [("out_file", "functional_data")], ), ]) workflow.connect([ ( get_info, specify_model, [("repetition_time", "time_repetition")], ), (specify_model, fit_model, [("session_info", "session_info")]), ( get_info, fit_model, [("repetition_time", "interscan_interval"), ("run_contrasts", "contrasts")], ), ( fit_model, first_level_design, [ ("interscan_interval", "interscan_interval"), ("session_info", "session_info"), ("contrasts", "contrasts"), ], ), (first_level_design, generate_model, [("fsf_files", "fsf_file")]), (first_level_design, generate_model, [("ev_files", "ev_files")]), ]) if detrend_poly: workflow.connect([ ( generate_model, correct_matrices, [("design_file", "design_matrix")], ), ( correct_matrices, plot_matrices, [("design_matrix", "mat_file")], ), ( correct_matrices, estimate_model, [("design_matrix", "design_file")], ), ]) else: workflow.connect([ (generate_model, plot_matrices, [("design_file", "mat_file")]), ( generate_model, estimate_model, [("design_file", "design_file")], ), ]) workflow.connect([ (getter, plot_matrices, [("entities", "entities")]), (generate_model, plot_matrices, [("con_file", "con_file")]), (fit_model, estimate_model, [("functional_data", "in_file")]), (generate_model, estimate_model, [("con_file", "tcon_file")]), ( estimate_model, calculate_p, [(("zstats", utils.flatten), "in_file")], ), ( estimate_model, collate, [ ("copes", "effect_maps"), ("varcopes", "variance_maps"), ("zstats", "zscore_maps"), ("tstats", "tstat_maps"), ], ), (calculate_p, collate, [("out_file", "pvalue_maps")]), ( collate, collate_outputs, [ ("effect_maps", "effect_maps"), ("variance_maps", "variance_maps"), ("zscore_maps", "zscore_maps"), ("pvalue_maps", "pvalue_maps"), ("tstat_maps", "tstat_maps"), ("contrast_metadata", "metadata"), ], ), ( collate_outputs, ds_contrast_maps, [("out", "in_file"), ("metadata", "entities")], ), ( collate_outputs, wrangle_outputs, [("metadata", "contrast_metadata"), ("out", "contrast_maps")], ), ]) return workflow
def create_spm_preproc_func_pipeline(data_dir=None, subject_id=None, task_list=None): ############################### ## Set up Nodes ############################### ds = Node(nio.DataGrabber(infields=['subject_id', 'task_id'], outfields=['func', 'struc']), name='datasource') ds.inputs.base_directory = os.path.abspath(data_dir + '/' + subject_id) ds.inputs.template = '*' ds.inputs.sort_filelist = True ds.inputs.template_args = {'func': [['task_id']], 'struc': []} ds.inputs.field_template = { 'func': 'Functional/Raw/%s/func.nii', 'struc': 'Structural/SPGR/spgr.nii' } ds.inputs.subject_id = subject_id ds.inputs.task_id = task_list ds.iterables = ('task_id', task_list) # ds.run().outputs #show datafiles # #Setup Data Sinker for writing output files # datasink = Node(nio.DataSink(), name='sinker') # datasink.inputs.base_directory = '/path/to/output' # workflow.connect(realigner, 'realignment_parameters', datasink, 'motion.@par') # datasink.inputs.substitutions = [('_variable', 'variable'),('file_subject_', '')] #Get Timing Acquisition for slice timing tr = 2 ta = Node(interface=util.Function(input_names=['tr', 'n_slices'], output_names=['ta'], function=get_ta), name="ta") ta.inputs.tr = tr #Slice Timing: sequential ascending slice_timing = Node(interface=spm.SliceTiming(), name="slice_timing") slice_timing.inputs.time_repetition = tr slice_timing.inputs.ref_slice = 1 #Realignment - 6 parameters - realign to first image of very first series. realign = Node(interface=spm.Realign(), name="realign") realign.inputs.register_to_mean = True #Plot Realignment plot_realign = Node(interface=PlotRealignmentParameters(), name="plot_realign") #Artifact Detection art = Node(interface=ra.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' #Coregister - 12 parameters, cost function = 'nmi', fwhm 7, interpolate, don't mask #anatomical to functional mean across all available data. coregister = Node(interface=spm.Coregister(), name="coregister") coregister.inputs.jobtype = 'estimate' # Segment structural, gray/white/csf,mni, segment = Node(interface=spm.Segment(), name="segment") segment.inputs.save_bias_corrected = True #Normalize - structural to MNI - then apply this to the coregistered functionals normalize = Node(interface=spm.Normalize(), name="normalize") normalize.inputs.template = os.path.abspath(t1_template_file) #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") fwhmlist = [0, 5, 8] smooth.iterables = ('fwhm', fwhmlist) #Create Covariate matrix make_covariates = Node(interface=Create_Covariates(), name="make_covariates") ############################### ## Create Pipeline ############################### Preprocessed = Workflow(name="Preprocessed") Preprocessed.base_dir = os.path.abspath(data_dir + '/' + subject_id + '/Functional') Preprocessed.connect([ (ds, ta, [(('func', get_n_slices), "n_slices")]), (ta, slice_timing, [("ta", "time_acquisition")]), (ds, slice_timing, [ ('func', 'in_files'), (('func', get_n_slices), "num_slices"), (('func', get_slice_order), "slice_order"), ]), (slice_timing, realign, [('timecorrected_files', 'in_files')]), (realign, compute_mask, [('mean_image', 'mean_volume')]), (realign, coregister, [('mean_image', 'target')]), (ds, coregister, [('struc', 'source')]), (coregister, segment, [('coregistered_source', 'data')]), (segment, normalize, [ ('transformation_mat', 'parameter_file'), ('bias_corrected_image', 'source'), ]), (realign, normalize, [('realigned_files', 'apply_to_files'), (('realigned_files', get_vox_dims), 'write_voxel_sizes')]), (normalize, smooth, [('normalized_files', 'in_files')]), (compute_mask, art, [('brain_mask', 'mask_file')]), (realign, art, [('realignment_parameters', 'realignment_parameters')]), (realign, art, [('realigned_files', 'realigned_files')]), (realign, plot_realign, [('realignment_parameters', 'realignment_parameters')]), (normalize, plot_normalization_check, [('normalized_files', 'wra_img') ]), (realign, make_covariates, [('realignment_parameters', 'realignment_parameters')]), (art, make_covariates, [('outlier_files', 'spike_id')]), ]) return Preprocessed
level1_workflow = pe.Workflow(name='level1flow') preproc = create_featreg_preproc(whichvol='first') modelfit = create_modelfit_workflow() fixed_fx = create_fixed_effects_flow() """ Add artifact detection and model specification nodes between the preprocessing and modelfitting workflows. """ art = pe.MapNode( ra.ArtifactDetect(use_differences=[True, False], use_norm=True, norm_threshold=1, zintensity_threshold=3, parameter_source='FSL', mask_type='file'), iterfield=['realigned_files', 'realignment_parameters', 'mask_file'], name="art") modelspec = pe.Node(model.SpecifyModel(), name="modelspec") level1_workflow.connect([ (preproc, art, [('outputspec.motion_parameters', 'realignment_parameters'), ('outputspec.realigned_files', 'realigned_files'), ('outputspec.mask', 'mask_file')]), (preproc, modelspec, [('outputspec.highpassed_files', 'functional_runs'), ('outputspec.motion_parameters', 'realignment_parameters')]), (art, modelspec, [('outlier_files', 'outlier_files')]),
def create_denoise_pipeline(name='denoise'): # workflow denoise = Workflow(name='denoise') # Define nodes inputnode = Node(interface=util.IdentityInterface(fields=[ 'anat_brain', 'brain_mask', 'epi2anat_dat', 'unwarped_mean', 'epi_coreg', 'moco_par', 'highpass_sigma', 'lowpass_sigma', 'tr' ]), name='inputnode') outputnode = Node(interface=util.IdentityInterface(fields=[ 'wmcsf_mask', 'brain_mask_resamp', 'brain_mask2epi', 'combined_motion', 'outlier_files', 'intensity_files', 'outlier_stats', 'outlier_plots', 'mc_regressor', 'mc_F', 'mc_pF', 'comp_regressor', 'comp_F', 'comp_pF', 'normalized_file' ]), name='outputnode') # run fast to get tissue probability classes fast = Node(fsl.FAST(), name='fast') denoise.connect([(inputnode, fast, [('anat_brain', 'in_files')])]) # functions to select tissue classes 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()) def selectsingle(files, idx): return files[idx] # resample tissue classes resample_tissue = MapNode(afni.Resample(resample_mode='NN', outputtype='NIFTI_GZ'), iterfield=['in_file'], name='resample_tissue') denoise.connect([ (inputnode, resample_tissue, [('epi_coreg', 'master')]), (fast, resample_tissue, [(('partial_volume_files', selectindex, [0, 2]), 'in_file')]), ]) # binarize tissue classes binarize_tissue = MapNode( fsl.ImageMaths(op_string='-nan -thr 0.99 -ero -bin'), iterfield=['in_file'], name='binarize_tissue') denoise.connect([ (resample_tissue, binarize_tissue, [('out_file', 'in_file')]), ]) # combine tissue classes to noise mask wmcsf_mask = Node(fsl.BinaryMaths(operation='add', out_file='wmcsf_mask_lowres.nii.gz'), name='wmcsf_mask') denoise.connect([(binarize_tissue, wmcsf_mask, [(('out_file', selectsingle, 0), 'in_file'), (('out_file', selectsingle, 1), 'operand_file')]), (wmcsf_mask, outputnode, [('out_file', 'wmcsf_mask')])]) # resample brain mask resample_brain = Node(afni.Resample( resample_mode='NN', outputtype='NIFTI_GZ', out_file='T1_brain_mask_lowres.nii.gz'), name='resample_brain') denoise.connect([(inputnode, resample_brain, [('brain_mask', 'in_file'), ('epi_coreg', 'master')]), (resample_brain, outputnode, [('out_file', 'brain_mask_resamp')])]) # project brain mask into original epi space fpr quality assessment brainmask2epi = Node(fs.ApplyVolTransform( interp='nearest', inverse=True, transformed_file='T1_brain_mask2epi.nii.gz', ), name='brainmask2epi') denoise.connect([ (inputnode, brainmask2epi, [('brain_mask', 'target_file'), ('epi2anat_dat', 'reg_file'), ('unwarped_mean', 'source_file')]), (brainmask2epi, outputnode, [('transformed_file', 'brain_mask2epi')]) ]) # perform artefact detection artefact = Node(ra.ArtifactDetect(save_plot=True, use_norm=True, parameter_source='FSL', mask_type='file', norm_threshold=1, zintensity_threshold=3, use_differences=[True, False]), name='artefact') artefact.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([ (inputnode, artefact, [('epi_coreg', 'realigned_files'), ('moco_par', 'realignment_parameters')]), (resample_brain, artefact, [('out_file', 'mask_file')]), (artefact, outputnode, [('norm_files', 'combined_motion'), ('outlier_files', 'outlier_files'), ('intensity_files', 'intensity_files'), ('statistic_files', 'outlier_stats'), ('plot_files', 'outlier_plots')]) ]) # Compute motion regressors motreg = Node(util.Function( input_names=['motion_params', 'order', 'derivatives'], output_names=['out_files'], function=motion_regressors), name='getmotionregress') motreg.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([(inputnode, motreg, [('moco_par', 'motion_params')])]) # Create a filter to remove motion and art confounds createfilter1 = Node(util.Function( input_names=['motion_params', 'comp_norm', 'outliers', 'detrend_poly'], output_names=['out_files'], function=build_filter1), name='makemotionbasedfilter') createfilter1.inputs.detrend_poly = 2 createfilter1.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([ (motreg, createfilter1, [('out_files', 'motion_params')]), ( artefact, createfilter1, [ #('norm_files', 'comp_norm'), ('outlier_files', 'outliers') ]), (createfilter1, outputnode, [('out_files', 'mc_regressor')]) ]) # regress out motion and art confounds filter1 = Node(fsl.GLM(out_f_name='F_mcart.nii.gz', out_pf_name='pF_mcart.nii.gz', out_res_name='rest_mc_denoised.nii.gz', demean=True), name='filtermotion') filter1.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([(inputnode, filter1, [('epi_coreg', 'in_file')]), (createfilter1, filter1, [(('out_files', list_to_filename), 'design')]), (filter1, outputnode, [('out_f', 'mc_F'), ('out_pf', 'mc_pF')])]) # create filter with compcor components createfilter2 = Node(util.Function(input_names=[ 'realigned_file', 'mask_file', 'num_components', 'extra_regressors' ], output_names=['out_files'], function=extract_noise_components), name='makecompcorfilter') createfilter2.inputs.num_components = 6 createfilter2.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([ (createfilter1, createfilter2, [(('out_files', list_to_filename), 'extra_regressors')]), (filter1, createfilter2, [('out_res', 'realigned_file')]), (wmcsf_mask, createfilter2, [('out_file', 'mask_file')]), (createfilter2, outputnode, [('out_files', 'comp_regressor')]), ]) # regress compcor and other noise components filter2 = Node(fsl.GLM(out_f_name='F_noise.nii.gz', out_pf_name='pF_noise.nii.gz', out_res_name='rest2anat_denoised.nii.gz', demean=True), name='filternoise') filter2.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([(filter1, filter2, [('out_res', 'in_file')]), (createfilter2, filter2, [('out_files', 'design')]), (resample_brain, filter2, [('out_file', 'mask')]), (filter2, outputnode, [('out_f', 'comp_F'), ('out_pf', 'comp_pF')])]) # bandpass filter denoised file bandpass_filter = Node( fsl.TemporalFilter(out_file='rest_denoised_bandpassed.nii.gz'), name='bandpass_filter') bandpass_filter.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([(inputnode, bandpass_filter, [('highpass_sigma', 'highpass_sigma'), ('lowpass_sigma', 'lowpass_sigma')]), (filter2, bandpass_filter, [('out_res', 'in_file')])]) # time-normalize scans normalize_time = Node(util.Function(input_names=['in_file', 'tr'], output_names=['out_file'], function=time_normalizer), name='normalize_time') normalize_time.plugin_args = {'submit_specs': 'request_memory = 17000'} denoise.connect([ (inputnode, normalize_time, [('tr', 'tr')]), (bandpass_filter, normalize_time, [('out_file', 'in_file')]), (normalize_time, outputnode, [('out_file', 'normalized_file')]) ]) return denoise
def create_spm_preproc(name='preproc'): """Create an spm preprocessing workflow with freesurfer registration and artifact detection. The workflow realigns and smooths and registers the functional images with the subject's freesurfer space. Example ------- >>> preproc = create_spm_preproc() >>> preproc.base_dir = '.' >>> preproc.inputs.inputspec.fwhm = 6 >>> preproc.inputs.inputspec.subject_id = 's1' >>> preproc.inputs.inputspec.subjects_dir = '.' >>> preproc.inputs.inputspec.functionals = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.norm_threshold = 1 >>> preproc.inputs.inputspec.zintensity_threshold = 3 Inputs:: inputspec.functionals : functional runs use 4d nifti inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects dir inputspec.fwhm : smoothing fwhm inputspec.norm_threshold : norm threshold for outliers inputspec.zintensity_threshold : intensity threshold in z-score Outputs:: outputspec.realignment_parameters : realignment parameter files outputspec.smoothed_files : smoothed functional files outputspec.outlier_files : list of outliers outputspec.outlier_stats : statistics of outliers outputspec.outlier_plots : images of outliers outputspec.mask_file : binary mask file in reference image space outputspec.reg_file : registration file that maps reference image to freesurfer space outputspec.reg_cost : cost of registration (useful for detecting misalignment) """ """ Initialize the workflow """ workflow = pe.Workflow(name=name) """ Define the inputs to this workflow """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'functionals', 'subject_id', 'subjects_dir', 'fwhm', 'norm_threshold', 'zintensity_threshold' ]), name='inputspec') """ Setup the processing nodes and create the mask generation and coregistration workflow """ poplist = lambda x: x.pop() realign = pe.Node(spm.Realign(), name='realign') workflow.connect(inputnode, 'functionals', realign, 'in_files') maskflow = create_getmask_flow() workflow.connect([(inputnode, maskflow, [('subject_id', 'inputspec.subject_id'), ('subjects_dir', 'inputspec.subjects_dir')])]) maskflow.inputs.inputspec.contrast_type = 't2' workflow.connect(realign, 'mean_image', maskflow, 'inputspec.source_file') smooth = pe.Node(spm.Smooth(), name='smooth') workflow.connect(inputnode, 'fwhm', smooth, 'fwhm') workflow.connect(realign, 'realigned_files', smooth, 'in_files') artdetect = pe.Node(ra.ArtifactDetect(mask_type='file', parameter_source='SPM', use_differences=[True, False], use_norm=True, save_plot=True), name='artdetect') workflow.connect([(inputnode, artdetect, [('norm_threshold', 'norm_threshold'), ('zintensity_threshold', 'zintensity_threshold')])]) workflow.connect([(realign, artdetect, [ ('realigned_files', 'realigned_files'), ('realignment_parameters', 'realignment_parameters') ])]) workflow.connect(maskflow, ('outputspec.mask_file', poplist), artdetect, 'mask_file') """ Define the outputs of the workflow and connect the nodes to the outputnode """ outputnode = pe.Node(niu.IdentityInterface(fields=[ "realignment_parameters", "smoothed_files", "mask_file", "reg_file", "reg_cost", 'outlier_files', 'outlier_stats', 'outlier_plots' ]), name="outputspec") workflow.connect([ (maskflow, outputnode, [("outputspec.reg_file", "reg_file")]), (maskflow, outputnode, [("outputspec.reg_cost", "reg_cost")]), (maskflow, outputnode, [(("outputspec.mask_file", poplist), "mask_file")]), (realign, outputnode, [('realignment_parameters', 'realignment_parameters')]), (smooth, outputnode, [('smoothed_files', 'smoothed_files')]), (artdetect, outputnode, [('outlier_files', 'outlier_files'), ('statistic_files', 'outlier_stats'), ('plot_files', 'outlier_plots')]) ]) return workflow
def create_prep(name='preproc'): """ Base preprocessing workflow for task and resting state fMRI Parameters ---------- name : name of workflow. Default = 'preproc' Inputs ------ inputspec.fssubject_id : inputspec.fssubject_dir : inputspec.func : inputspec.highpass : inputspec.num_noise_components : inputspec.ad_normthresh : inputspec.ad_zthresh : inputspec.tr : inputspec.interleaved : inputspec.sliceorder : inputspec.compcor_select : inputspec.highpass_sigma : inputspec.lowpass_sigma : inputspec.reg_params : inputspec.FM_TEdiff : inputspec.FM_Echo_spacing : inputspec.FM_sigma : Outputs ------- outputspec.reference : outputspec.motion_parameters : outputspec.realigned_files : outputspec.mask : outputspec.smoothed_files : outputspec.highpassed_files : outputspec.mean : outputspec.combined_motion : outputspec.outlier_files : outputspec.mask : outputspec.reg_cost : outputspec.reg_file : outputspec.noise_components : outputspec.tsnr_file : outputspec.stddev_file : outputspec.filter_file : outputspec.scaled_files : outputspec.z_img : outputspec.motion_plots : outputspec.FM_unwarped_mean : outputspec.FM_unwarped_epi : Returns ------- workflow : preprocessing workflow """ import nipype.interfaces.fsl as fsl # fsl import nipype.algorithms.rapidart as ra # rapid artifact detection from nipype.workflows.smri.freesurfer.utils import create_getmask_flow from modular_nodes import create_mod_smooth, mod_realign, mod_despike import nipype.pipeline.engine as pe import nipype.interfaces.utility as util preproc = pe.Workflow(name=name) # Compcorr node compcor = create_compcorr() # Input node inputnode = pe.Node(util.IdentityInterface(fields=[ 'fssubject_id', 'fssubject_dir', 'func', 'highpass', 'num_noise_components', 'ad_normthresh', 'ad_zthresh', 'tr', 'do_slicetime', 'sliceorder', 'compcor_select', 'highpass_freq', 'lowpass_freq', 'reg_params', 'FM_TEdiff', 'FM_Echo_spacing', 'FM_sigma', 'motion_correct_node', 'smooth_type', 'surface_fwhm', 'filter_type', 'timepoints_to_remove', 'do_whitening', 'regress_before_PCA', 'realign_parameters', 'do_despike', 'anatomical' ]), name='inputspec') # Separate input node for FWHM inputnode_fwhm = pe.Node(util.IdentityInterface(fields=['fwhm']), name='fwhm_input') # strip ids strip_rois = pe.MapNode(fsl.ExtractROI(), name='extractroi', iterfield='in_file') strip_rois.inputs.t_size = -1 preproc.connect(inputnode, 'timepoints_to_remove', strip_rois, 't_min') # convert BOLD images to float img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') #afni despike despike = pe.MapNode(util.Function(input_names=['in_file', "do_despike"], output_names=["out_file"], function=mod_despike), name="despike", iterfield=["in_file"]) preproc.connect(inputnode, "do_despike", despike, "do_despike") # define the motion correction node #motion_correct = pe.Node(interface=FmriRealign4d(), # name='realign') motion_correct = pe.Node(util.Function( input_names=[ 'node', 'in_file', 'tr', 'do_slicetime', 'sliceorder', "parameters" ], output_names=['out_file', 'par_file', 'parameter_source'], function=mod_realign), name="mod_realign") preproc.connect(inputnode, 'motion_correct_node', motion_correct, 'node') # construct motion plots #plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), # name='plot_motion', # iterfield=['in_file']) # rapidArt for artifactual timepoint detection ad = pe.Node(ra.ArtifactDetect(save_plot=False), name='artifactdetect') # extract the mean volume if the first functional run meanfunc = art_mean_workflow() # generate a freesurfer workflow that will return the mask getmask = create_getmask_flow() # create a SUSAN smoothing workflow, and smooth each run with # 75% of the median value for each run as the brightness # threshold. smooth = create_mod_smooth(name="modular_smooth", separate_masks=False) preproc.connect(inputnode, 'smooth_type', smooth, 'inputnode.smooth_type') # choose susan function """ The following node selects smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ choosesusan = pe.Node(util.Function( input_names=['fwhm', 'motion_files', 'smoothed_files'], output_names=['cor_smoothed_files'], function=choose_susan), name='select_smooth') # scale the median value of each run to 10,000 meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file', 'op_string'], name='scale_median') # determine the median value of the MASKED functional runs medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield=['in_file'], name='compute_median_val') # temporal highpass filtering highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') # Calculate the z-score of output zscore = pe.MapNode(interface=util.Function( input_names=['image', 'outliers'], output_names=['z_img'], function=z_image), name='z_score', iterfield=['image', 'outliers']) # declare some node inputs... #plot_motion.iterables = ('plot_type', ['rotations', 'translations']) #ad.inputs.parameter_source = 'FSL' meanfunc.inputs.inputspec.parameter_source = 'FSL' ad.inputs.mask_type = 'file' ad.inputs.use_differences = [True, False] getmask.inputs.inputspec.contrast_type = 't2' getmask.inputs.register.out_fsl_file = True fssource = getmask.get_node('fssource') # make connections... preproc.connect(inputnode, 'fssubject_id', getmask, 'inputspec.subject_id') preproc.connect(inputnode, 'ad_normthresh', ad, 'norm_threshold') preproc.connect(inputnode, 'ad_zthresh', ad, 'zintensity_threshold') preproc.connect(inputnode, 'tr', motion_correct, 'tr') preproc.connect(inputnode, 'realign_parameters', motion_correct, 'parameters') preproc.connect(motion_correct, 'parameter_source', ad, 'parameter_source') preproc.connect(inputnode, 'do_slicetime', motion_correct, 'do_slicetime') preproc.connect(inputnode, 'sliceorder', motion_correct, 'sliceorder') preproc.connect(inputnode, 'compcor_select', compcor, 'inputspec.selector') preproc.connect(inputnode, 'fssubject_dir', getmask, 'inputspec.subjects_dir') #preproc.connect(inputnode, 'func', # img2float, 'in_file') preproc.connect(inputnode, 'func', strip_rois, 'in_file') preproc.connect(strip_rois, 'roi_file', img2float, 'in_file') preproc.connect(img2float, 'out_file', despike, "in_file") preproc.connect(despike, "out_file", motion_correct, 'in_file') #preproc.connect(motion_correct, 'par_file', # plot_motion, 'in_file') preproc.connect(motion_correct, 'out_file', meanfunc, 'inputspec.realigned_files') preproc.connect(motion_correct, 'par_file', meanfunc, 'inputspec.realignment_parameters') preproc.connect(meanfunc, 'outputspec.mean_image', getmask, 'inputspec.source_file') preproc.connect(inputnode, 'num_noise_components', compcor, 'inputspec.num_components') preproc.connect(inputnode, 'regress_before_PCA', compcor, 'inputspec.regress_before_PCA') preproc.connect(motion_correct, 'out_file', compcor, 'inputspec.realigned_file') preproc.connect(meanfunc, 'outputspec.mean_image', compcor, 'inputspec.mean_file') preproc.connect(fssource, 'aseg', compcor, 'inputspec.fsaseg_file') preproc.connect(getmask, ('outputspec.reg_file', pickfirst), compcor, 'inputspec.reg_file') preproc.connect(ad, 'outlier_files', compcor, 'inputspec.outlier_files') preproc.connect(motion_correct, 'par_file', compcor, 'inputspec.realignment_parameters') preproc.connect(motion_correct, 'out_file', ad, 'realigned_files') preproc.connect(motion_correct, 'par_file', ad, 'realignment_parameters') preproc.connect(getmask, ('outputspec.mask_file', pickfirst), ad, 'mask_file') preproc.connect(getmask, ('outputspec.mask_file', pickfirst), medianval, 'mask_file') preproc.connect(inputnode_fwhm, 'fwhm', smooth, 'inputnode.fwhm') preproc.connect(motion_correct, 'out_file', smooth, 'inputnode.in_files') preproc.connect(getmask, ('outputspec.mask_file', pickfirst), smooth, 'inputnode.mask_file') preproc.connect(getmask, ('outputspec.reg_file', pickfirst), smooth, 'inputnode.reg_file') preproc.connect(inputnode, 'surface_fwhm', smooth, 'inputnode.surface_fwhm') preproc.connect(inputnode, 'fssubject_dir', smooth, 'inputnode.surf_dir') preproc.connect(smooth, 'outputnode.smoothed_files', choosesusan, 'smoothed_files') preproc.connect(motion_correct, 'out_file', choosesusan, 'motion_files') preproc.connect(inputnode_fwhm, 'fwhm', choosesusan, 'fwhm') preproc.connect(choosesusan, 'cor_smoothed_files', meanscale, 'in_file') preproc.connect(choosesusan, 'cor_smoothed_files', medianval, 'in_file') preproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') preproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') preproc.connect(meanscale, 'out_file', highpass, 'in_file') preproc.connect(highpass, 'out_file', zscore, 'image') preproc.connect(ad, 'outlier_files', zscore, 'outliers') # create output node outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'mean', 'motion_parameters', 'realigned_files', 'smoothed_files', 'highpassed_files', 'combined_motion', 'outlier_files', 'outlier_stat_files', 'mask', 'reg_cost', 'reg_file', 'reg_fsl_file', 'noise_components', 'tsnr_file', 'stddev_file', 'tsnr_detrended', 'filter_file', 'scaled_files', 'unmasked_fullspectrum', 'z_img', 'motion_plots', 'FM_unwarped_epi', 'FM_unwarped_mean', 'vsm_file', 'bandpassed_file', 'intensity_files', 'noise_mask', 'csf_mask' ]), name='outputspec') # make output connection preproc.connect(meanfunc, 'outputspec.mean_image', outputnode, 'mean') preproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') preproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') preproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') preproc.connect(ad, 'norm_files', outputnode, 'combined_motion') preproc.connect(ad, 'outlier_files', outputnode, 'outlier_files') preproc.connect(ad, 'intensity_files', outputnode, 'intensity_files') preproc.connect(ad, 'statistic_files', outputnode, 'outlier_stat_files') preproc.connect(compcor, 'outputspec.noise_components', outputnode, 'noise_components') preproc.connect(compcor, 'outputspec.noise_mask', outputnode, 'noise_mask') preproc.connect(compcor, 'outputspec.csf_mask', outputnode, 'csf_mask') preproc.connect(getmask, 'outputspec.mask_file', outputnode, 'mask') preproc.connect(getmask, 'register.out_fsl_file', outputnode, 'reg_fsl_file') preproc.connect(getmask, 'outputspec.reg_file', outputnode, 'reg_file') preproc.connect(getmask, 'outputspec.reg_cost', outputnode, 'reg_cost') preproc.connect(choosesusan, 'cor_smoothed_files', outputnode, 'smoothed_files') preproc.connect(compcor, 'outputspec.tsnr_file', outputnode, 'tsnr_file') preproc.connect(compcor, 'outputspec.stddev_file', outputnode, 'stddev_file') preproc.connect(compcor, 'outputspec.tsnr_detrended', outputnode, 'tsnr_detrended') preproc.connect(zscore, 'z_img', outputnode, 'z_img') #preproc.connect(plot_motion,'out_file', # outputnode,'motion_plots') return preproc
def create_spm_preproc(c, name='preproc'): """Create an spm preprocessing workflow with freesurfer registration and artifact detection. The workflow realigns and smooths and registers the functional images with the subject's freesurfer space. Example ------- >>> preproc = create_spm_preproc() >>> preproc.base_dir = '.' >>> preproc.inputs.inputspec.fwhm = 6 >>> preproc.inputs.inputspec.subject_id = 's1' >>> preproc.inputs.inputspec.subjects_dir = '.' >>> preproc.inputs.inputspec.functionals = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.norm_threshold = 1 >>> preproc.inputs.inputspec.zintensity_threshold = 3 Inputs:: inputspec.functionals : functional runs use 4d nifti inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects dir inputspec.fwhm : smoothing fwhm inputspec.norm_threshold : norm threshold for outliers inputspec.zintensity_threshold : intensity threshold in z-score Outputs:: outputspec.realignment_parameters : realignment parameter files outputspec.smoothed_files : smoothed functional files outputspec.outlier_files : list of outliers outputspec.outlier_stats : statistics of outliers outputspec.outlier_plots : images of outliers outputspec.mask_file : binary mask file in reference image space outputspec.reg_file : registration file that maps reference image to freesurfer space outputspec.reg_cost : cost of registration (useful for detecting misalignment) """ from nipype.workflows.smri.freesurfer.utils import create_getmask_flow import nipype.algorithms.rapidart as ra import nipype.interfaces.spm as spm import nipype.interfaces.utility as niu import nipype.pipeline.engine as pe import nipype.interfaces.io as nio """ Initialize the workflow """ workflow = pe.Workflow(name=name) """ Define the inputs to this workflow """ inputnode = pe.Node(niu.IdentityInterface(fields=[ 'functionals', 'subject_id', 'subjects_dir', 'fwhm', 'norm_threshold', 'zintensity_threshold', 'tr', 'do_slicetime', 'sliceorder', 'node', 'csf_prob', 'wm_prob', 'gm_prob' ]), name='inputspec') """ Setup the processing nodes and create the mask generation and coregistration workflow """ poplist = lambda x: x.pop() #realign = pe.Node(spm.Realign(), name='realign') sym_func = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_link'], function=do_symlink), name='func_symlink') realign = pe.Node(niu.Function( input_names=['node', 'in_file', 'tr', 'do_slicetime', 'sliceorder'], output_names=['out_file', 'par_file'], function=mod_realign), name="mod_realign") mean = art_mean_workflow() workflow.connect(realign, 'out_file', mean, 'inputspec.realigned_files') workflow.connect(realign, 'par_file', mean, 'inputspec.realignment_parameters') mean.inputs.inputspec.parameter_source = 'FSL' # Modular realign puts it in FSL format for consistency #workflow.connect(inputnode, 'functionals', realign, 'in_file') workflow.connect(inputnode, 'functionals', sym_func, 'in_file') workflow.connect(sym_func, 'out_link', realign, 'in_file') workflow.connect(inputnode, 'tr', realign, 'tr') workflow.connect(inputnode, 'do_slicetime', realign, 'do_slicetime') workflow.connect(inputnode, 'sliceorder', realign, 'sliceorder') workflow.connect(inputnode, 'node', realign, 'node') maskflow = create_getmask_flow() workflow.connect([(inputnode, maskflow, [('subject_id', 'inputspec.subject_id'), ('subjects_dir', 'inputspec.subjects_dir')])]) maskflow.inputs.inputspec.contrast_type = 't2' workflow.connect(mean, 'outputspec.mean_image', maskflow, 'inputspec.source_file') smooth = pe.Node(spm.Smooth(), name='smooth') normalize = pe.Node(spm.Normalize(jobtype='write'), name='normalize') normalize_struct = normalize.clone('normalize_struct') segment = pe.Node(spm.Segment(csf_output_type=[True, True, False], gm_output_type=[True, True, False], wm_output_type=[True, True, False]), name='segment') mergefunc = lambda in1, in2, in3: [in1, in2, in3] # merge = pe.Node(niu.Merge(),name='merge') merge = pe.Node(niu.Function(input_names=['in1', 'in2', 'in3'], output_names=['out'], function=mergefunc), name='merge') workflow.connect(inputnode, 'csf_prob', merge, 'in3') workflow.connect(inputnode, 'wm_prob', merge, 'in2') workflow.connect(inputnode, 'gm_prob', merge, 'in1') #workflow.connect(merge,'out', segment,'tissue_prob_maps') sym_prob = sym_func.clone('sym_prob') workflow.connect(merge, 'out', sym_prob, 'in_file') workflow.connect(sym_prob, 'out_link', segment, 'tissue_prob_maps') workflow.connect(maskflow, ('outputspec.mask_file', pickfirst), segment, 'mask_image') workflow.connect(inputnode, 'fwhm', smooth, 'fwhm') #sym_brain = sym_func.clone('sym_brain') #workflow.connect(realign, 'mean_image', normalize, 'source') #workflow.connect(maskflow,'fssource.brain',segment,'data') fssource = maskflow.get_node('fssource') import nipype.interfaces.freesurfer as fs convert_brain = pe.Node(interface=fs.ApplyVolTransform(inverse=True), name='convert') workflow.connect(fssource, 'brain', convert_brain, 'target_file') workflow.connect(maskflow, ('outputspec.reg_file', pickfirst), convert_brain, 'reg_file') workflow.connect(mean, 'outputspec.mean_image', convert_brain, 'source_file') convert2nii = pe.Node(fs.MRIConvert(in_type='mgz', out_type='nii'), name='convert2nii') workflow.connect(convert_brain, 'transformed_file', convert2nii, 'in_file') workflow.connect(convert2nii, 'out_file', segment, 'data') workflow.connect(segment, 'transformation_mat', normalize, 'parameter_file') workflow.connect(segment, 'transformation_mat', normalize_struct, 'parameter_file') workflow.connect(convert2nii, 'out_file', normalize_struct, 'apply_to_files') workflow.connect(realign, 'out_file', normalize, 'apply_to_files') #normalize.inputs.template='/software/spm8/templates/EPI.nii' workflow.connect(normalize, 'normalized_files', smooth, 'in_files') #workflow.connect(realign, 'realigned_files', smooth, 'in_files') artdetect = pe.Node(ra.ArtifactDetect(mask_type='file', parameter_source='FSL', use_differences=[True, False], use_norm=True, save_plot=True), name='artdetect') workflow.connect([(inputnode, artdetect, [('norm_threshold', 'norm_threshold'), ('zintensity_threshold', 'zintensity_threshold')])]) workflow.connect([(realign, artdetect, [('out_file', 'realigned_files'), ('par_file', 'realignment_parameters')])]) workflow.connect(maskflow, ('outputspec.mask_file', poplist), artdetect, 'mask_file') """ Define the outputs of the workflow and connect the nodes to the outputnode """ outputnode = pe.Node(niu.IdentityInterface(fields=[ "realignment_parameters", "smoothed_files", "mask_file", "mean_image", "reg_file", "reg_cost", 'outlier_files', 'outlier_stats', 'outlier_plots', 'norm_components', 'mod_csf', 'unmod_csf', 'mod_wm', 'unmod_wm', 'mod_gm', 'unmod_gm', 'mean', 'normalized_struct', 'struct_in_functional_space', 'normalization_parameters', 'reverse_normalize_parameters' ]), name="outputspec") workflow.connect([ (maskflow, outputnode, [("outputspec.reg_file", "reg_file")]), (maskflow, outputnode, [("outputspec.reg_cost", "reg_cost")]), (maskflow, outputnode, [(("outputspec.mask_file", poplist), "mask_file")]), (realign, outputnode, [('par_file', 'realignment_parameters')]), (smooth, outputnode, [('smoothed_files', 'smoothed_files')]), (artdetect, outputnode, [('outlier_files', 'outlier_files'), ('statistic_files', 'outlier_stats'), ('plot_files', 'outlier_plots'), ('norm_files', 'norm_components')]) ]) workflow.connect(segment, 'modulated_csf_image', outputnode, 'mod_csf') workflow.connect(segment, 'modulated_wm_image', outputnode, 'mod_wm') workflow.connect(segment, 'modulated_gm_image', outputnode, 'mod_gm') workflow.connect(segment, 'normalized_csf_image', outputnode, 'unmod_csf') workflow.connect(segment, 'normalized_wm_image', outputnode, 'unmod_wm') workflow.connect(segment, 'normalized_gm_image', outputnode, 'unmod_gm') workflow.connect(mean, 'outputspec.mean_image', outputnode, 'mean') workflow.connect(normalize_struct, 'normalized_files', outputnode, 'normalized_struct') workflow.connect(segment, 'transformation_mat', outputnode, 'normalization_parameters') workflow.connect(segment, 'inverse_transformation_mat', outputnode, 'reverse_normalize_parameters') workflow.connect(convert2nii, 'out_file', outputnode, 'struct_in_functional_space') workflow.inputs.inputspec.fwhm = c.fwhm workflow.inputs.inputspec.subjects_dir = c.surf_dir workflow.inputs.inputspec.norm_threshold = c.norm_thresh workflow.inputs.inputspec.zintensity_threshold = c.z_thresh workflow.inputs.inputspec.node = c.motion_correct_node workflow.inputs.inputspec.tr = c.TR workflow.inputs.inputspec.do_slicetime = c.do_slicetiming workflow.inputs.inputspec.sliceorder = c.SliceOrder workflow.inputs.inputspec.csf_prob = c.csf_prob workflow.inputs.inputspec.gm_prob = c.grey_prob workflow.inputs.inputspec.wm_prob = c.white_prob workflow.base_dir = c.working_dir workflow.config = {'execution': {'crashdump_dir': c.crash_dir}} datagrabber = get_dataflow(c) workflow.connect(datagrabber, 'func', inputnode, 'functionals') infosource = pe.Node(niu.IdentityInterface(fields=['subject_id']), name='subject_names') if not c.test_mode: infosource.iterables = ('subject_id', c.subjects) else: infosource.iterables = ('subject_id', c.subjects[:1]) workflow.connect(infosource, 'subject_id', inputnode, 'subject_id') workflow.connect(infosource, 'subject_id', datagrabber, 'subject_id') sub = lambda x: [('_subject_id_%s' % x, '')] sinker = pe.Node(nio.DataSink(), name='sinker') workflow.connect(infosource, 'subject_id', sinker, 'container') workflow.connect(infosource, ('subject_id', sub), sinker, 'substitutions') sinker.inputs.base_directory = c.sink_dir outputspec = workflow.get_node('outputspec') workflow.connect(outputspec, 'realignment_parameters', sinker, 'spm_preproc.realignment_parameters') workflow.connect(outputspec, 'smoothed_files', sinker, 'spm_preproc.smoothed_outputs') workflow.connect(outputspec, 'outlier_files', sinker, 'spm_preproc.art.@outlier_files') workflow.connect(outputspec, 'outlier_stats', sinker, 'spm_preproc.art.@outlier_stats') workflow.connect(outputspec, 'outlier_plots', sinker, 'spm_preproc.art.@outlier_plots') workflow.connect(outputspec, 'norm_components', sinker, 'spm_preproc.art.@norm') workflow.connect(outputspec, 'reg_file', sinker, 'spm_preproc.bbreg.@reg_file') workflow.connect(outputspec, 'reg_cost', sinker, 'spm_preproc.bbreg.@reg_cost') workflow.connect(outputspec, 'mask_file', sinker, 'spm_preproc.mask.@mask_file') workflow.connect(outputspec, 'mod_csf', sinker, 'spm_preproc.segment.mod.@csf') workflow.connect(outputspec, 'mod_wm', sinker, 'spm_preproc.segment.mod.@wm') workflow.connect(outputspec, 'mod_gm', sinker, 'spm_preproc.segment.mod.@gm') workflow.connect(outputspec, 'unmod_csf', sinker, 'spm_preproc.segment.unmod.@csf') workflow.connect(outputspec, 'unmod_wm', sinker, 'spm_preproc.segment.unmod.@wm') workflow.connect(outputspec, 'unmod_gm', sinker, 'spm_preproc.segment.unmod.@gm') workflow.connect(outputspec, 'mean', sinker, 'spm_preproc.mean') workflow.connect(outputspec, 'normalized_struct', sinker, 'spm_preproc.normalized_struct') workflow.connect(outputspec, 'normalization_parameters', sinker, 'spm_preproc.normalization_parameters.@forward') workflow.connect(outputspec, 'reverse_normalize_parameters', sinker, 'spm_preproc.normalization_parameters.@reverse') workflow.connect(outputspec, 'struct_in_functional_space', sinker, 'spm_preproc.struct_in_func_space') return workflow
def analyze_openfmri_dataset(data_dir, subject=None, model_id=None, work_dir=None): """Analyzes an open fmri dataset Parameters ---------- data_dir : str Path to the base data directory work_dir : str Nipype working directory (defaults to cwd) """ """ Load nipype workflows """ preproc = create_featreg_preproc(whichvol='first') modelfit = create_modelfit_workflow() fixed_fx = create_fixed_effects_flow() """ Remove the plotting connection so that plot iterables don't propagate to the model stage """ preproc.disconnect(preproc.get_node('plot_motion'), 'out_file', preproc.get_node('outputspec'), 'motion_plots') """ Set up openfmri data specific components """ subjects = [path.split(os.path.sep)[-1] for path in glob(os.path.join(data_dir, 'sub*'))] infosource = pe.Node(niu.IdentityInterface(fields=['subject_id', 'model_id']), name='infosource') if subject is None: infosource.iterables = [('subject_id', subjects), ('model_id', [model_id])] else: infosource.iterables = [('subject_id', [subjects[subjects.index(subject)]]), ('model_id', [model_id])] subjinfo = pe.Node(niu.Function(input_names=['subject_id', 'base_dir', 'task_id', 'model_id'], output_names=['run_id', 'conds', 'TR'], function=get_subjectinfo), name='subjectinfo') subjinfo.inputs.base_dir = data_dir """ Return data components as anat, bold and behav """ datasource = pe.Node(nio.DataGrabber(infields=['subject_id', 'run_id', 'model_id'], outfields=['anat', 'bold', 'behav']), name='datasource') datasource.inputs.base_directory = data_dir datasource.inputs.template = '*' datasource.inputs.field_template = {'anat': '%s/anatomy/highres001.nii.gz', 'bold': '%s/BOLD/task001_r*/bold.nii.gz', 'behav': ('%s/model/model%03d/onsets/task001_' 'run%03d/cond*.txt')} datasource.inputs.template_args = {'anat': [['subject_id']], 'bold': [['subject_id']], 'behav': [['subject_id', 'model_id', 'run_id']]} datasource.inputs.sorted = True """ Create meta workflow """ wf = pe.Workflow(name='openfmri') wf.connect(infosource, 'subject_id', subjinfo, 'subject_id') wf.connect(infosource, 'model_id', subjinfo, 'model_id') wf.connect(infosource, 'subject_id', datasource, 'subject_id') wf.connect(infosource, 'model_id', datasource, 'model_id') wf.connect(subjinfo, 'run_id', datasource, 'run_id') wf.connect([(datasource, preproc, [('bold', 'inputspec.func')]), ]) def get_highpass(TR, hpcutoff): return hpcutoff / (2 * TR) gethighpass = pe.Node(niu.Function(input_names=['TR', 'hpcutoff'], output_names=['highpass'], function=get_highpass), name='gethighpass') wf.connect(subjinfo, 'TR', gethighpass, 'TR') wf.connect(gethighpass, 'highpass', preproc, 'inputspec.highpass') """ Setup a basic set of contrasts, a t-test per condition """ def get_contrasts(base_dir, model_id, conds): import numpy as np import os contrast_file = os.path.join(base_dir, 'models', 'model%03d' % model_id, 'task_contrasts.txt') contrast_def = np.genfromtxt(contrast_file, dtype=object) contrasts = [] for row in contrast_def: con = [row[0], 'T', ['cond%03d' % i for i in range(len(conds))], row[1:].astype(float).tolist()] contrasts.append(con) return contrasts contrastgen = pe.Node(niu.Function(input_names=['base_dir', 'model_id', 'conds'], output_names=['contrasts'], function=get_contrasts), name='contrastgen') contrastgen.inputs.base_dir = data_dir art = pe.MapNode(interface=ra.ArtifactDetect(use_differences=[True, False], use_norm=True, norm_threshold=1, zintensity_threshold=3, parameter_source='FSL', mask_type='file'), iterfield=['realigned_files', 'realignment_parameters', 'mask_file'], name="art") modelspec = pe.Node(interface=model.SpecifyModel(), name="modelspec") modelspec.inputs.input_units = 'secs' wf.connect(subjinfo, 'TR', modelspec, 'time_repetition') wf.connect(datasource, 'behav', modelspec, 'event_files') wf.connect(subjinfo, 'TR', modelfit, 'inputspec.interscan_interval') wf.connect(subjinfo, 'conds', contrastgen, 'conds') wf.connect(infosource, 'model_id', contrastgen, 'model_id') wf.connect(contrastgen, 'contrasts', modelfit, 'inputspec.contrasts') wf.connect([(preproc, art, [('outputspec.motion_parameters', 'realignment_parameters'), ('outputspec.realigned_files', 'realigned_files'), ('outputspec.mask', 'mask_file')]), (preproc, modelspec, [('outputspec.highpassed_files', 'functional_runs'), ('outputspec.motion_parameters', 'realignment_parameters')]), (art, modelspec, [('outlier_files', 'outlier_files')]), (modelspec, modelfit, [('session_info', 'inputspec.session_info')]), (preproc, modelfit, [('outputspec.highpassed_files', 'inputspec.functional_data')]) ]) """ Reorder the copes so that now it combines across runs """ def sort_copes(files): numelements = len(files[0]) outfiles = [] for i in range(numelements): outfiles.insert(i, []) for j, elements in enumerate(files): outfiles[i].append(elements[i]) return outfiles def num_copes(files): return len(files) pickfirst = lambda x: x[0] wf.connect([(preproc, fixed_fx, [(('outputspec.mask', pickfirst), 'flameo.mask_file')]), (modelfit, fixed_fx, [(('outputspec.copes', sort_copes), 'inputspec.copes'), ('outputspec.dof_file', 'inputspec.dof_files'), (('outputspec.varcopes', sort_copes), 'inputspec.varcopes'), (('outputspec.copes', num_copes), 'l2model.num_copes'), ]) ]) """ Connect to a datasink """ def get_subs(subject_id, conds): subs = [('_subject_id_%s/' % subject_id, '')] for i in range(len(conds)): subs.append(('_flameo%d/cope1.' % i, 'cope%02d.' % (i + 1))) subs.append(('_flameo%d/varcope1.' % i, 'varcope%02d.' % (i + 1))) subs.append(('_flameo%d/zstat1.' % i, 'zstat%02d.' % (i + 1))) subs.append(('_flameo%d/tstat1.' % i, 'tstat%02d.' % (i + 1))) subs.append(('_flameo%d/res4d.' % i, 'res4d%02d.' % (i + 1))) return subs subsgen = pe.Node(niu.Function(input_names=['subject_id', 'conds'], output_names=['substitutions'], function=get_subs), name='subsgen') datasink = pe.Node(interface=nio.DataSink(), name="datasink") wf.connect(infosource, 'subject_id', datasink, 'container') wf.connect(infosource, 'subject_id', subsgen, 'subject_id') wf.connect(subjinfo, 'conds', subsgen, 'conds') wf.connect(subsgen, 'substitutions', datasink, 'substitutions') wf.connect([(fixed_fx.get_node('outputspec'), datasink, [('res4d', 'res4d'), ('copes', 'copes'), ('varcopes', 'varcopes'), ('zstats', 'zstats'), ('tstats', 'tstats')]) ]) """ Set processing parameters """ hpcutoff = 120. subjinfo.inputs.task_id = 1 preproc.inputs.inputspec.fwhm = 6.0 gethighpass.inputs.hpcutoff = hpcutoff modelspec.inputs.high_pass_filter_cutoff = hpcutoff modelfit.inputs.inputspec.bases = {'dgamma': {'derivs': True}} modelfit.inputs.inputspec.model_serial_correlations = True modelfit.inputs.inputspec.film_threshold = 1000 if work_dir is None: work_dir = os.path.join(os.getcwd(), 'working') wf.base_dir = work_dir datasink.inputs.base_directory = os.path.join(work_dir, 'output') wf.config['execution'] = dict(crashdump_dir=os.path.join(work_dir, 'crashdumps'), stop_on_first_crash=True) wf.run('MultiProc', plugin_args={'n_procs': 2})
def create_workflow(func_runs, subject_id, subjects_dir, fwhm, slice_times, highpass_frequency, lowpass_frequency, TR, sink_directory, use_fsl_bp, num_components, whichvol, name='wmaze'): wf = pe.Workflow(name=name) datasource = pe.Node(nio.DataGrabber(infields=['subject_id', 'run'], outfields=['func']), name='datasource') datasource.inputs.subject_id = subject_id datasource.inputs.run = func_runs datasource.inputs.template = '/home/data/madlab/data/mri/wmaze/%s/bold/bold_%03d/bold.nii.gz' datasource.inputs.sort_filelist = True # Rename files in case they are named identically name_unique = pe.MapNode(util.Rename(format_string='wmaze_%(run)02d'), iterfield = ['in_file', 'run'], name='rename') name_unique.inputs.keep_ext = True name_unique.inputs.run = func_runs wf.connect(datasource, 'func', name_unique, 'in_file') # Define the outputs for the preprocessing workflow output_fields = ['reference', 'motion_parameters', 'motion_parameters_plusDerivs', 'motionandoutlier_noise_file', 'noise_components', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'bandpassed_files', 'reg_file', 'reg_cost', 'reg_fsl_file', 'artnorm_files', 'artoutlier_files', 'artdisplacement_files', 'tsnr_file'] outputnode = pe.Node(util.IdentityInterface(fields=output_fields), name='outputspec') # Convert functional images to float representation img2float = pe.MapNode(fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') wf.connect(name_unique, 'out_file', img2float, 'in_file') # Run AFNI's despike. This is always run, however, whether this is fed to # realign depends on the input configuration despiker = pe.MapNode(afni.Despike(outputtype='NIFTI_GZ'), iterfield=['in_file'], name='despike') num_threads = 4 despiker.inputs.environ = {'OMP_NUM_THREADS': '%d' % num_threads} despiker.plugin_args = {'bsub_args': '-n %d' % num_threads} despiker.plugin_args = {'bsub_args': '-R "span[hosts=1]"'} wf.connect(img2float, 'out_file', despiker, 'in_file') # Extract the first volume of the first run as the reference extractref = pe.Node(fsl.ExtractROI(t_size=1), iterfield=['in_file'], name = "extractref") wf.connect(despiker, ('out_file', pickfirst), extractref, 'in_file') wf.connect(despiker, ('out_file', pickvol, 0, whichvol), extractref, 't_min') wf.connect(extractref, 'roi_file', outputnode, 'reference') if slice_times is not None: # Simultaneous motion and slice timing correction with Nipy algorithm motion_correct = pe.Node(nipy.SpaceTimeRealigner(), name='motion_correct') motion_correct.inputs.tr = TR motion_correct.inputs.slice_times = slice_times motion_correct.inputs.slice_info = 2 motion_correct.plugin_args = {'bsub_args': '-n %s' %os.environ['MKL_NUM_THREADS']} motion_correct.plugin_args = {'bsub_args': '-R "span[hosts=1]"'} wf.connect(despiker, 'out_file', motion_correct, 'in_file') wf.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') wf.connect(motion_correct, 'out_file', outputnode, 'realigned_files') else: # Motion correct functional runs to the reference (1st volume of 1st run) motion_correct = pe.MapNode(fsl.MCFLIRT(save_mats = True, save_plots = True, interpolation = 'sinc'), name = 'motion_correct', iterfield = ['in_file']) wf.connect(despiker, 'out_file', motion_correct, 'in_file') wf.connect(extractref, 'roi_file', motion_correct, 'ref_file') wf.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') wf.connect(motion_correct, 'out_file', outputnode, 'realigned_files') # Compute TSNR on realigned data regressing polynomials upto order 2 tsnr = pe.MapNode(TSNR(regress_poly=2), iterfield=['in_file'], name='tsnr') wf.connect(motion_correct, 'out_file', tsnr, 'in_file') wf.connect(tsnr, 'tsnr_file', outputnode, 'tsnr_file') # Plot the estimated motion parameters plot_motion = pe.MapNode(fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) wf.connect(motion_correct, 'par_file', plot_motion, 'in_file') wf.connect(plot_motion, 'out_file', outputnode, 'motion_plots') # Register a source file to fs space and create a brain mask in source space fssource = pe.Node(nio.FreeSurferSource(), name ='fssource') fssource.inputs.subject_id = subject_id fssource.inputs.subjects_dir = subjects_dir # Extract aparc+aseg brain mask and binarize fs_threshold = pe.Node(fs.Binarize(min=0.5, out_type='nii'), name ='fs_threshold') wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), fs_threshold, 'in_file') # Calculate the transformation matrix from EPI space to FreeSurfer space # using the BBRegister command fs_register = pe.MapNode(fs.BBRegister(init='fsl'), iterfield=['source_file'], name ='fs_register') fs_register.inputs.contrast_type = 't2' fs_register.inputs.out_fsl_file = True fs_register.inputs.subject_id = subject_id fs_register.inputs.subjects_dir = subjects_dir wf.connect(extractref, 'roi_file', fs_register, 'source_file') wf.connect(fs_register, 'out_reg_file', outputnode, 'reg_file') wf.connect(fs_register, 'min_cost_file', outputnode, 'reg_cost') wf.connect(fs_register, 'out_fsl_file', outputnode, 'reg_fsl_file') # Extract wm+csf, brain masks by eroding freesurfer lables wmcsf = pe.MapNode(fs.Binarize(), iterfield=['match', 'binary_file', 'erode'], name='wmcsfmask') #wmcsf.inputs.wm_ven_csf = True wmcsf.inputs.match = [[2, 41], [4, 5, 14, 15, 24, 31, 43, 44, 63]] wmcsf.inputs.binary_file = ['wm.nii.gz', 'csf.nii.gz'] wmcsf.inputs.erode = [2, 2] #int(np.ceil(slice_thickness)) wf.connect(fssource, ('aparc_aseg', get_aparc_aseg), wmcsf, 'in_file') # Now transform the wm and csf masks to 1st volume of 1st run wmcsftransform = pe.MapNode(fs.ApplyVolTransform(inverse=True, interp='nearest'), iterfield=['target_file'], name='wmcsftransform') wmcsftransform.inputs.subjects_dir = subjects_dir wf.connect(extractref, 'roi_file', wmcsftransform, 'source_file') wf.connect(fs_register, ('out_reg_file', pickfirst), wmcsftransform, 'reg_file') wf.connect(wmcsf, 'binary_file', wmcsftransform, 'target_file') # Transform the binarized aparc+aseg file to the 1st volume of 1st run space fs_voltransform = pe.MapNode(fs.ApplyVolTransform(inverse=True), iterfield = ['source_file', 'reg_file'], name='fs_transform') fs_voltransform.inputs.subjects_dir = subjects_dir wf.connect(extractref, 'roi_file', fs_voltransform, 'source_file') wf.connect(fs_register, 'out_reg_file', fs_voltransform, 'reg_file') wf.connect(fs_threshold, 'binary_file', fs_voltransform, 'target_file') # Dilate the binarized mask by 1 voxel that is now in the EPI space fs_threshold2 = pe.MapNode(fs.Binarize(min=0.5, out_type='nii'), iterfield=['in_file'], name='fs_threshold2') fs_threshold2.inputs.dilate = 1 wf.connect(fs_voltransform, 'transformed_file', fs_threshold2, 'in_file') wf.connect(fs_threshold2, 'binary_file', outputnode, 'mask_file') # Use RapidART to detect motion/intensity outliers art = pe.MapNode(ra.ArtifactDetect(use_differences = [True, False], use_norm = True, zintensity_threshold = 3, norm_threshold = 1, bound_by_brainmask=True, mask_type = "file"), iterfield=["realignment_parameters","realigned_files"], name="art") if slice_times is not None: art.inputs.parameter_source = "NiPy" else: art.inputs.parameter_source = "FSL" wf.connect(motion_correct, 'par_file', art, 'realignment_parameters') wf.connect(motion_correct, 'out_file', art, 'realigned_files') wf.connect(fs_threshold2, ('binary_file', pickfirst), art, 'mask_file') wf.connect(art, 'norm_files', outputnode, 'artnorm_files') wf.connect(art, 'outlier_files', outputnode, 'artoutlier_files') wf.connect(art, 'displacement_files', outputnode, 'artdisplacement_files') # Compute motion regressors (save file with 1st and 2nd derivatives) motreg = pe.Node(util.Function(input_names=['motion_params', 'order', 'derivatives'], output_names=['out_files'], function=motion_regressors, imports=imports), name='getmotionregress') wf.connect(motion_correct, 'par_file', motreg, 'motion_params') wf.connect(motreg, 'out_files', outputnode, 'motion_parameters_plusDerivs') # Create a filter text file to remove motion (+ derivatives), art confounds, # and 1st, 2nd, and 3rd order legendre polynomials. createfilter1 = pe.Node(util.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 = 3 wf.connect(motreg, 'out_files', createfilter1, 'motion_params') wf.connect(art, 'norm_files', createfilter1, 'comp_norm') wf.connect(art, 'outlier_files', createfilter1, 'outliers') wf.connect(createfilter1, 'out_files', outputnode, 'motionandoutlier_noise_file') # Create a filter to remove noise components based on white matter and CSF createfilter2 = pe.MapNode(util.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(motion_correct, 'out_file', createfilter2, 'realigned_file') wf.connect(wmcsftransform, 'transformed_file', createfilter2, 'mask_file') wf.connect(createfilter2, 'out_files', outputnode, 'noise_components') # Mask the functional runs with the extracted mask maskfunc = pe.MapNode(fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name = 'maskfunc') wf.connect(motion_correct, 'out_file', maskfunc, 'in_file') wf.connect(fs_threshold2, ('binary_file', pickfirst), maskfunc, 'in_file2') # Smooth each run using SUSAn with the brightness threshold set to 75% # of the median value for each run and a mask constituting the mean functional smooth_median = pe.MapNode(fsl.ImageStats(op_string='-k %s -p 50'), iterfield = ['in_file'], name='smooth_median') wf.connect(maskfunc, 'out_file', smooth_median, 'in_file') wf.connect(fs_threshold2, ('binary_file', pickfirst), smooth_median, 'mask_file') smooth_meanfunc = pe.MapNode(fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='smooth_meanfunc') wf.connect(maskfunc, 'out_file', smooth_meanfunc, 'in_file') smooth_merge = pe.Node(util.Merge(2, axis='hstack'), name='smooth_merge') wf.connect(smooth_meanfunc, 'out_file', smooth_merge, 'in1') wf.connect(smooth_median, 'out_stat', smooth_merge, 'in2') smooth = pe.MapNode(fsl.SUSAN(), iterfield=['in_file', 'brightness_threshold', 'usans'], name='smooth') smooth.inputs.fwhm=fwhm wf.connect(maskfunc, 'out_file', smooth, 'in_file') wf.connect(smooth_median, ('out_stat', getbtthresh), smooth, 'brightness_threshold') wf.connect(smooth_merge, ('out', getusans), smooth, 'usans') # Mask the smoothed data with the dilated mask maskfunc2 = pe.MapNode(fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file'], name='maskfunc2') wf.connect(smooth, 'smoothed_file', maskfunc2, 'in_file') wf.connect(fs_threshold2, ('binary_file', pickfirst), maskfunc2, 'in_file2') wf.connect(maskfunc2, 'out_file', outputnode, 'smoothed_files') # Band-pass filter the timeseries if use_fsl_bp == 'True': determine_bp_sigmas = pe.Node(util.Function(input_names=['tr', 'highpass_freq', 'lowpass_freq'], output_names = ['out_sigmas'], function=calc_fslbp_sigmas), name='determine_bp_sigmas') determine_bp_sigmas.inputs.tr = float(TR) determine_bp_sigmas.inputs.highpass_freq = float(highpass_frequency) determine_bp_sigmas.inputs.lowpass_freq = float(lowpass_frequency) bandpass = pe.MapNode(fsl.ImageMaths(suffix='_tempfilt'), iterfield=["in_file"], name="bandpass") wf.connect(determine_bp_sigmas, ('out_sigmas', highpass_operand), bandpass, 'op_string') wf.connect(maskfunc2, 'out_file', bandpass, 'in_file') wf.connect(bandpass, 'out_file', outputnode, 'bandpassed_files') else: bandpass = pe.Node(util.Function(input_names=['files', 'lowpass_freq', 'highpass_freq', 'fs'], output_names=['out_files'], function=bandpass_filter, imports=imports), name='bandpass') bandpass.inputs.fs = 1./TR if highpass_frequency < 0: bandpass.inputs.highpass_freq = -1 else: bandpass.inputs.highpass_freq = highpass_frequency if lowpass_frequency < 0: bandpass.inputs.lowpass_freq = -1 else: bandpass.inputs.lowpass_freq = lowpass_frequency wf.connect(maskfunc2, 'out_file', bandpass, 'files') wf.connect(bandpass, 'out_files', outputnode, 'bandpassed_files') # Save the relevant data into an output directory datasink = pe.Node(nio.DataSink(), name="datasink") datasink.inputs.base_directory = sink_directory datasink.inputs.container = subject_id wf.connect(outputnode, 'reference', datasink, 'ref') wf.connect(outputnode, 'motion_parameters', datasink, 'motion') wf.connect(outputnode, 'realigned_files', datasink, 'func.realigned') wf.connect(outputnode, 'motion_plots', datasink, 'motion.@plots') wf.connect(outputnode, 'mask_file', datasink, 'ref.@mask') wf.connect(outputnode, 'smoothed_files', datasink, 'func.smoothed_fullspectrum') wf.connect(outputnode, 'bandpassed_files', datasink, 'func.smoothed_bandpassed') wf.connect(outputnode, 'reg_file', datasink, 'bbreg.@reg') wf.connect(outputnode, 'reg_cost', datasink, 'bbreg.@cost') wf.connect(outputnode, 'reg_fsl_file', datasink, 'bbreg.@regfsl') wf.connect(outputnode, 'artnorm_files', datasink, 'art.@norm_files') wf.connect(outputnode, 'artoutlier_files', datasink, 'art.@outlier_files') wf.connect(outputnode, 'artdisplacement_files', datasink, 'art.@displacement_files') wf.connect(outputnode, 'motion_parameters_plusDerivs', datasink, 'noise.@motionplusDerivs') wf.connect(outputnode, 'motionandoutlier_noise_file', datasink, 'noise.@motionplusoutliers') wf.connect(outputnode, 'noise_components', datasink, 'compcor') wf.connect(outputnode, 'tsnr_file', datasink, 'tsnr') return wf
# Tip 11: Add any arguments to the Node as a field in the object (e.g., coreg). Nipype doesn't seem to register inputs in the Node definition... or at least the _report.rst makes it seem like the arguments may not be communicated. # Tip 12: Look at the _report/_report.rst and pyscript_{node_variable} to figure out what ran. import nipype.interfaces.spm as spm import nipype.algorithms.rapidart as ra # artifact detection # SPM Realign (Estimate & Reslice - Reslice only the mean image) realign = Node(interface=spm.Realign(), name="realign") realign.inputs.jobtype = 'estwrite' realign.inputs.register_to_mean = True realign.inputs.write_which = [ 0, 1 ] # first field is for "reslice all"; 2nd is "reslice mean" # ART evaluation of realignment parameters art = Node(interface=ra.ArtifactDetect(), name='art') art.inputs.norm_threshold = 1 art.inputs.zintensity_threshold = 5 art.inputs.mask_type = 'spm_global' art.inputs.parameter_source = 'SPM' art.inputs.use_differences = [ True, False ] # Reflects use difference for motion, not intensity art.inputs.use_norm = True # SPM Coregister (Estimate Only) coreg = Node(interface=spm.Coregister(), name="coregister") coreg.inputs.jobtype = 'estimate' # SPM Normalise (Estimate & Reslice) norm12 = Node(interface=spm.Normalize12(), name="normalize")
(selectfiles, fix_header_wmmask, [('wm_mask', 'data_file'), ('unwarped_file', 'header_file')]), ]) # merge images into list masklist = Node(util.Merge(2), name='masklist') preproc_nuisance_regress.connect([ (fix_header_csfmask, masklist, [('out_file', 'in1')]), (fix_header_wmmask, masklist, [('out_file', 'in2')]), ]) # perform artefact detection artefact = Node(ra.ArtifactDetect(save_plot=True, use_norm=True, parameter_source='NiPy', mask_type='file', norm_threshold=1, zintensity_threshold=3, use_differences=[True, False]), name='artefact') preproc_nuisance_regress.connect([ (selectfiles, artefact, [('unwarped_file', 'realigned_files'), ('slicemoco_par_file', 'realignment_parameters') ]), (selectfiles, artefact, [('gm_mask', 'mask_file')]), ]) # calculate motion regressors motreg = Node(util.Function(
preproc_wf.connect(fs_voltransform, 'transformed_file', fs_threshold2, 'in_file') preproc_wf.connect(fs_threshold2, 'binary_file', outputspec, 'mask_file') # Mask the functional runs with the extracted mask maskfunc = pe.MapNode(fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name='maskfunc') preproc_wf.connect(motion_correct, 'out_file', maskfunc, 'in_file') preproc_wf.connect(fs_threshold2, ('binary_file', pickfirst), maskfunc, 'in_file2') # Use RapidART to detect motion/intensity outliers art = pe.MapNode(ra.ArtifactDetect(use_differences=[True, False], use_norm=True, zintensity_threshold=3, norm_threshold=1, bound_by_brainmask=True, mask_type='file'), iterfield=['realignment_parameters', 'realigned_files'], name='art') art.inputs.parameter_source = 'NiPy' preproc_wf.connect(motion_correct, 'par_file', art, 'realignment_parameters') preproc_wf.connect(motion_correct, 'out_file', art, 'realigned_files') preproc_wf.connect(fs_threshold2, ('binary_file', pickfirst), art, 'mask_file') preproc_wf.connect(art, 'norm_files', outputspec, 'artnorm_files') preproc_wf.connect(art, 'outlier_files', outputspec, 'artoutlier_files') preproc_wf.connect(art, 'displacement_files', outputspec, 'artdisplacement_files') # Compute motion regressors (save file with 1st and 2nd derivatives) motreg = pe.Node(util.Function(
columns=['trans_x', 'trans_y', 'trans_z', 'rot_x', 'rot_y', 'rot_z'], header=False, index=False) return os.path.abspath('mcparams.tsv') mcparams = pe.Node(niu.Function(input_names=['confounds_file'], output_names=['realignment_parameters'], function=get_mcparams), 'mcparams') # Rapidart node # In[15]: art = pe.Node(ra.ArtifactDetect(), 'art') art.inputs.parameter_source = 'SPM' art.inputs.norm_threshold = 1 art.inputs.use_differences = [True, False] art.inputs.zintensity_threshold = 3 art.inputs.mask_type = 'file' # **ModelGrabber:** Grab model specification info (util) # In[16]: def ModelGrabber(contrasts_file, events_file, confounds_file): from os import environ import numpy as np import pandas as pd
def analyze_openfmri_dataset(data_dir, subject=None, model_id=None, task_id=None, output_dir=None, subj_prefix='*', hpcutoff=120., use_derivatives=True, fwhm=6.0, subjects_dir=None, target=None): """Analyzes an open fmri dataset Parameters ---------- data_dir : str Path to the base data directory work_dir : str Nipype working directory (defaults to cwd) """ """ Load nipype workflows """ preproc = create_featreg_preproc(whichvol='first') modelfit = create_modelfit_workflow() fixed_fx = create_fixed_effects_flow() if subjects_dir: registration = create_fs_reg_workflow() else: registration = create_reg_workflow() """ Remove the plotting connection so that plot iterables don't propagate to the model stage """ preproc.disconnect(preproc.get_node('plot_motion'), 'out_file', preproc.get_node('outputspec'), 'motion_plots') """ Set up openfmri data specific components """ subjects = sorted([path.split(os.path.sep)[-1] for path in glob(os.path.join(data_dir, subj_prefix))]) infosource = pe.Node(niu.IdentityInterface(fields=['subject_id', 'model_id', 'task_id']), name='infosource') if len(subject) == 0: infosource.iterables = [('subject_id', subjects), ('model_id', [model_id]), ('task_id', task_id)] else: infosource.iterables = [('subject_id', [subjects[subjects.index(subj)] for subj in subject]), ('model_id', [model_id]), ('task_id', task_id)] subjinfo = pe.Node(niu.Function(input_names=['subject_id', 'base_dir', 'task_id', 'model_id'], output_names=['run_id', 'conds', 'TR'], function=get_subjectinfo), name='subjectinfo') subjinfo.inputs.base_dir = data_dir """ Return data components as anat, bold and behav """ contrast_file = os.path.join(data_dir, 'models', 'model%03d' % model_id, 'task_contrasts.txt') has_contrast = os.path.exists(contrast_file) if has_contrast: datasource = pe.Node(nio.DataGrabber(infields=['subject_id', 'run_id', 'task_id', 'model_id'], outfields=['anat', 'bold', 'behav', 'contrasts']), name='datasource') else: datasource = pe.Node(nio.DataGrabber(infields=['subject_id', 'run_id', 'task_id', 'model_id'], outfields=['anat', 'bold', 'behav']), name='datasource') datasource.inputs.base_directory = data_dir datasource.inputs.template = '*' if has_contrast: datasource.inputs.field_template = {'anat': '%s/anatomy/T1_001.nii.gz', 'bold': '%s/BOLD/task%03d_r*/bold.nii.gz', 'behav': ('%s/model/model%03d/onsets/task%03d_' 'run%03d/cond*.txt'), 'contrasts': ('models/model%03d/' 'task_contrasts.txt')} datasource.inputs.template_args = {'anat': [['subject_id']], 'bold': [['subject_id', 'task_id']], 'behav': [['subject_id', 'model_id', 'task_id', 'run_id']], 'contrasts': [['model_id']]} else: datasource.inputs.field_template = {'anat': '%s/anatomy/T1_001.nii.gz', 'bold': '%s/BOLD/task%03d_r*/bold.nii.gz', 'behav': ('%s/model/model%03d/onsets/task%03d_' 'run%03d/cond*.txt')} datasource.inputs.template_args = {'anat': [['subject_id']], 'bold': [['subject_id', 'task_id']], 'behav': [['subject_id', 'model_id', 'task_id', 'run_id']]} datasource.inputs.sort_filelist = True """ Create meta workflow """ wf = pe.Workflow(name='openfmri') wf.connect(infosource, 'subject_id', subjinfo, 'subject_id') wf.connect(infosource, 'model_id', subjinfo, 'model_id') wf.connect(infosource, 'task_id', subjinfo, 'task_id') wf.connect(infosource, 'subject_id', datasource, 'subject_id') wf.connect(infosource, 'model_id', datasource, 'model_id') wf.connect(infosource, 'task_id', datasource, 'task_id') wf.connect(subjinfo, 'run_id', datasource, 'run_id') wf.connect([(datasource, preproc, [('bold', 'inputspec.func')]), ]) def get_highpass(TR, hpcutoff): return hpcutoff / (2 * TR) gethighpass = pe.Node(niu.Function(input_names=['TR', 'hpcutoff'], output_names=['highpass'], function=get_highpass), name='gethighpass') wf.connect(subjinfo, 'TR', gethighpass, 'TR') wf.connect(gethighpass, 'highpass', preproc, 'inputspec.highpass') """ Setup a basic set of contrasts, a t-test per condition """ def get_contrasts(contrast_file, task_id, conds): import numpy as np import os contrast_def = [] if os.path.exists(contrast_file): with open(contrast_file, 'rt') as fp: contrast_def.extend([np.array(row.split()) for row in fp.readlines() if row.strip()]) contrasts = [] for row in contrast_def: if row[0] != 'task%03d' % task_id: continue con = [row[1], 'T', ['cond%03d' % (i + 1) for i in range(len(conds))], row[2:].astype(float).tolist()] contrasts.append(con) # add auto contrasts for each column for i, cond in enumerate(conds): con = [cond, 'T', ['cond%03d' % (i + 1)], [1]] contrasts.append(con) return contrasts contrastgen = pe.Node(niu.Function(input_names=['contrast_file', 'task_id', 'conds'], output_names=['contrasts'], function=get_contrasts), name='contrastgen') art = pe.MapNode(interface=ra.ArtifactDetect(use_differences=[True, False], use_norm=True, norm_threshold=1, zintensity_threshold=3, parameter_source='FSL', mask_type='file'), iterfield=['realigned_files', 'realignment_parameters', 'mask_file'], name="art") modelspec = pe.Node(interface=model.SpecifyModel(), name="modelspec") modelspec.inputs.input_units = 'secs' def check_behav_list(behav, run_id, conds): from nipype.external import six import numpy as np num_conds = len(conds) if isinstance(behav, six.string_types): behav = [behav] behav_array = np.array(behav).flatten() num_elements = behav_array.shape[0] return behav_array.reshape(num_elements/num_conds, num_conds).tolist() reshape_behav = pe.Node(niu.Function(input_names=['behav', 'run_id', 'conds'], output_names=['behav'], function=check_behav_list), name='reshape_behav') wf.connect(subjinfo, 'TR', modelspec, 'time_repetition') wf.connect(datasource, 'behav', reshape_behav, 'behav') wf.connect(subjinfo, 'run_id', reshape_behav, 'run_id') wf.connect(subjinfo, 'conds', reshape_behav, 'conds') wf.connect(reshape_behav, 'behav', modelspec, 'event_files') wf.connect(subjinfo, 'TR', modelfit, 'inputspec.interscan_interval') wf.connect(subjinfo, 'conds', contrastgen, 'conds') if has_contrast: wf.connect(datasource, 'contrasts', contrastgen, 'contrast_file') else: contrastgen.inputs.contrast_file = '' wf.connect(infosource, 'task_id', contrastgen, 'task_id') wf.connect(contrastgen, 'contrasts', modelfit, 'inputspec.contrasts') wf.connect([(preproc, art, [('outputspec.motion_parameters', 'realignment_parameters'), ('outputspec.realigned_files', 'realigned_files'), ('outputspec.mask', 'mask_file')]), (preproc, modelspec, [('outputspec.highpassed_files', 'functional_runs'), ('outputspec.motion_parameters', 'realignment_parameters')]), (art, modelspec, [('outlier_files', 'outlier_files')]), (modelspec, modelfit, [('session_info', 'inputspec.session_info')]), (preproc, modelfit, [('outputspec.highpassed_files', 'inputspec.functional_data')]) ]) # Comute TSNR on realigned data regressing polynomials upto order 2 tsnr = MapNode(TSNR(regress_poly=2), iterfield=['in_file'], name='tsnr') wf.connect(preproc, "outputspec.realigned_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') """ Reorder the copes so that now it combines across runs """ def sort_copes(copes, varcopes, contrasts): import numpy as np if not isinstance(copes, list): copes = [copes] varcopes = [varcopes] num_copes = len(contrasts) n_runs = len(copes) all_copes = np.array(copes).flatten() all_varcopes = np.array(varcopes).flatten() outcopes = all_copes.reshape(len(all_copes)/num_copes, num_copes).T.tolist() outvarcopes = all_varcopes.reshape(len(all_varcopes)/num_copes, num_copes).T.tolist() return outcopes, outvarcopes, n_runs cope_sorter = pe.Node(niu.Function(input_names=['copes', 'varcopes', 'contrasts'], output_names=['copes', 'varcopes', 'n_runs'], function=sort_copes), name='cope_sorter') pickfirst = lambda x: x[0] wf.connect(contrastgen, 'contrasts', cope_sorter, 'contrasts') wf.connect([(preproc, fixed_fx, [(('outputspec.mask', pickfirst), 'flameo.mask_file')]), (modelfit, cope_sorter, [('outputspec.copes', 'copes')]), (modelfit, cope_sorter, [('outputspec.varcopes', 'varcopes')]), (cope_sorter, fixed_fx, [('copes', 'inputspec.copes'), ('varcopes', 'inputspec.varcopes'), ('n_runs', 'l2model.num_copes')]), (modelfit, fixed_fx, [('outputspec.dof_file', 'inputspec.dof_files'), ]) ]) wf.connect(calc_median, 'median_file', registration, 'inputspec.mean_image') if subjects_dir: wf.connect(infosource, 'subject_id', registration, 'inputspec.subject_id') registration.inputs.inputspec.subjects_dir = subjects_dir registration.inputs.inputspec.target_image = fsl.Info.standard_image('MNI152_T1_2mm_brain.nii.gz') if target: registration.inputs.inputspec.target_image = target else: wf.connect(datasource, 'anat', registration, 'inputspec.anatomical_image') registration.inputs.inputspec.target_image = fsl.Info.standard_image('MNI152_T1_2mm.nii.gz') registration.inputs.inputspec.target_image_brain = fsl.Info.standard_image('MNI152_T1_2mm_brain.nii.gz') registration.inputs.inputspec.config_file = 'T1_2_MNI152_2mm' def merge_files(copes, varcopes, zstats): out_files = [] splits = [] out_files.extend(copes) splits.append(len(copes)) out_files.extend(varcopes) splits.append(len(varcopes)) out_files.extend(zstats) splits.append(len(zstats)) return out_files, splits mergefunc = pe.Node(niu.Function(input_names=['copes', 'varcopes', 'zstats'], output_names=['out_files', 'splits'], function=merge_files), name='merge_files') wf.connect([(fixed_fx.get_node('outputspec'), mergefunc, [('copes', 'copes'), ('varcopes', 'varcopes'), ('zstats', 'zstats'), ])]) wf.connect(mergefunc, 'out_files', registration, 'inputspec.source_files') def split_files(in_files, splits): copes = in_files[:splits[0]] varcopes = in_files[splits[0]:(splits[0] + splits[1])] zstats = in_files[(splits[0] + splits[1]):] return copes, varcopes, zstats splitfunc = pe.Node(niu.Function(input_names=['in_files', 'splits'], output_names=['copes', 'varcopes', 'zstats'], function=split_files), name='split_files') wf.connect(mergefunc, 'splits', splitfunc, 'splits') wf.connect(registration, 'outputspec.transformed_files', splitfunc, 'in_files') if subjects_dir: get_roi_mean = pe.MapNode(fs.SegStats(default_color_table=True), iterfield=['in_file'], name='get_aparc_means') get_roi_mean.inputs.avgwf_txt_file = True wf.connect(fixed_fx.get_node('outputspec'), 'copes', get_roi_mean, 'in_file') wf.connect(registration, 'outputspec.aparc', get_roi_mean, 'segmentation_file') get_roi_tsnr = pe.MapNode(fs.SegStats(default_color_table=True), iterfield=['in_file'], name='get_aparc_tsnr') get_roi_tsnr.inputs.avgwf_txt_file = True wf.connect(tsnr, 'tsnr_file', get_roi_tsnr, 'in_file') wf.connect(registration, 'outputspec.aparc', get_roi_tsnr, 'segmentation_file') """ Connect to a datasink """ def get_subs(subject_id, conds, run_id, model_id, task_id): subs = [('_subject_id_%s_' % subject_id, '')] subs.append(('_model_id_%d' % model_id, 'model%03d' %model_id)) subs.append(('task_id_%d/' % task_id, '/task%03d_' % task_id)) subs.append(('bold_dtype_mcf_mask_smooth_mask_gms_tempfilt_mean_warp', 'mean')) subs.append(('bold_dtype_mcf_mask_smooth_mask_gms_tempfilt_mean_flirt', 'affine')) for i in range(len(conds)): subs.append(('_flameo%d/cope1.' % i, 'cope%02d.' % (i + 1))) subs.append(('_flameo%d/varcope1.' % i, 'varcope%02d.' % (i + 1))) subs.append(('_flameo%d/zstat1.' % i, 'zstat%02d.' % (i + 1))) subs.append(('_flameo%d/tstat1.' % i, 'tstat%02d.' % (i + 1))) subs.append(('_flameo%d/res4d.' % i, 'res4d%02d.' % (i + 1))) subs.append(('_warpall%d/cope1_warp.' % i, 'cope%02d.' % (i + 1))) subs.append(('_warpall%d/varcope1_warp.' % (len(conds) + i), 'varcope%02d.' % (i + 1))) subs.append(('_warpall%d/zstat1_warp.' % (2 * len(conds) + i), 'zstat%02d.' % (i + 1))) subs.append(('_warpall%d/cope1_trans.' % i, 'cope%02d.' % (i + 1))) subs.append(('_warpall%d/varcope1_trans.' % (len(conds) + i), 'varcope%02d.' % (i + 1))) subs.append(('_warpall%d/zstat1_trans.' % (2 * len(conds) + i), 'zstat%02d.' % (i + 1))) subs.append(('__get_aparc_means%d/' % i, '/cope%02d_' % (i + 1))) for i, run_num in enumerate(run_id): subs.append(('__get_aparc_tsnr%d/' % i, '/run%02d_' % run_num)) subs.append(('__art%d/' % i, '/run%02d_' % run_num)) subs.append(('__dilatemask%d/' % i, '/run%02d_' % run_num)) subs.append(('__realign%d/' % i, '/run%02d_' % run_num)) subs.append(('__modelgen%d/' % i, '/run%02d_' % run_num)) subs.append(('/model%03d/task%03d/' % (model_id, task_id), '/')) subs.append(('/model%03d/task%03d_' % (model_id, task_id), '/')) subs.append(('_bold_dtype_mcf_bet_thresh_dil', '_mask')) subs.append(('_output_warped_image', '_anat2target')) subs.append(('median_flirt_brain_mask', 'median_brain_mask')) subs.append(('median_bbreg_brain_mask', 'median_brain_mask')) return subs subsgen = pe.Node(niu.Function(input_names=['subject_id', 'conds', 'run_id', 'model_id', 'task_id'], output_names=['substitutions'], function=get_subs), name='subsgen') wf.connect(subjinfo, 'run_id', subsgen, 'run_id') datasink = pe.Node(interface=nio.DataSink(), name="datasink") wf.connect(infosource, 'subject_id', datasink, 'container') wf.connect(infosource, 'subject_id', subsgen, 'subject_id') wf.connect(infosource, 'model_id', subsgen, 'model_id') wf.connect(infosource, 'task_id', subsgen, 'task_id') wf.connect(contrastgen, 'contrasts', subsgen, 'conds') wf.connect(subsgen, 'substitutions', datasink, 'substitutions') wf.connect([(fixed_fx.get_node('outputspec'), datasink, [('res4d', 'res4d'), ('copes', 'copes'), ('varcopes', 'varcopes'), ('zstats', 'zstats'), ('tstats', 'tstats')]) ]) wf.connect([(modelfit.get_node('modelgen'), datasink, [('design_cov', 'qa.model'), ('design_image', 'qa.model.@matrix_image'), ('design_file', 'qa.model.@matrix'), ])]) wf.connect([(preproc, datasink, [('outputspec.motion_parameters', 'qa.motion'), ('outputspec.motion_plots', 'qa.motion.plots'), ('outputspec.mask', 'qa.mask')])]) wf.connect(registration, 'outputspec.mean2anat_mask', datasink, 'qa.mask.mean2anat') wf.connect(art, 'norm_files', datasink, 'qa.art.@norm') wf.connect(art, 'intensity_files', datasink, 'qa.art.@intensity') wf.connect(art, 'outlier_files', datasink, 'qa.art.@outlier_files') wf.connect(registration, 'outputspec.anat2target', datasink, 'qa.anat2target') wf.connect(tsnr, 'tsnr_file', datasink, 'qa.tsnr.@map') if subjects_dir: wf.connect(registration, 'outputspec.min_cost_file', datasink, 'qa.mincost') wf.connect([(get_roi_tsnr, datasink, [('avgwf_txt_file', 'qa.tsnr'), ('summary_file', 'qa.tsnr.@summary')])]) wf.connect([(get_roi_mean, datasink, [('avgwf_txt_file', 'copes.roi'), ('summary_file', 'copes.roi.@summary')])]) wf.connect([(splitfunc, datasink, [('copes', 'copes.mni'), ('varcopes', 'varcopes.mni'), ('zstats', 'zstats.mni'), ])]) wf.connect(calc_median, 'median_file', datasink, 'mean') wf.connect(registration, 'outputspec.transformed_mean', datasink, 'mean.mni') wf.connect(registration, 'outputspec.func2anat_transform', datasink, 'xfm.mean2anat') wf.connect(registration, 'outputspec.anat2target_transform', datasink, 'xfm.anat2target') """ Set processing parameters """ preproc.inputs.inputspec.fwhm = fwhm gethighpass.inputs.hpcutoff = hpcutoff modelspec.inputs.high_pass_filter_cutoff = hpcutoff modelfit.inputs.inputspec.bases = {'dgamma': {'derivs': use_derivatives}} modelfit.inputs.inputspec.model_serial_correlations = True modelfit.inputs.inputspec.film_threshold = 1000 datasink.inputs.base_directory = output_dir return wf
def create_spm_preproc(c, name='preproc'): """ """ from nipype.workflows.smri.freesurfer.utils import create_getmask_flow import nipype.algorithms.rapidart as ra import nipype.interfaces.spm as spm import nipype.interfaces.utility as niu import nipype.pipeline.engine as pe import nipype.interfaces.io as nio import nipype.interfaces.freesurfer as fs workflow = pe.Workflow(name=name) inputnode = pe.Node(niu.IdentityInterface(fields=[ 'functionals', 'subject_id', 'subjects_dir', 'fwhm', 'norm_threshold', 'zintensity_threshold', 'tr', 'do_slicetime', 'sliceorder', 'parameters', 'node', 'csf_prob', 'wm_prob', 'gm_prob' ]), name='inputspec') poplist = lambda x: x.pop() sym_func = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_link'], function=do_symlink), name='func_symlink') # REALIGN realign = pe.Node(niu.Function( input_names=[ 'node', 'in_file', 'tr', 'do_slicetime', 'sliceorder', 'parameters' ], output_names=['out_file', 'par_file', 'parameter_source'], function=mod_realign), name="mod_realign") workflow.connect(inputnode, 'parameters', realign, 'parameters') workflow.connect(inputnode, 'functionals', realign, 'in_file') workflow.connect(inputnode, 'tr', realign, 'tr') workflow.connect(inputnode, 'do_slicetime', realign, 'do_slicetime') workflow.connect(inputnode, 'sliceorder', realign, 'sliceorder') workflow.connect(inputnode, 'node', realign, 'node') # TAKE MEAN IMAGE mean = art_mean_workflow() workflow.connect(realign, 'out_file', mean, 'inputspec.realigned_files') workflow.connect(realign, 'par_file', mean, 'inputspec.realignment_parameters') workflow.connect(realign, 'parameter_source', mean, 'inputspec.parameter_source') # CREATE BRAIN MASK maskflow = create_getmask_flow() workflow.connect([(inputnode, maskflow, [('subject_id', 'inputspec.subject_id'), ('subjects_dir', 'inputspec.subjects_dir')])]) maskflow.inputs.inputspec.contrast_type = 't2' workflow.connect(mean, 'outputspec.mean_image', maskflow, 'inputspec.source_file') # SEGMENT segment = pe.Node(spm.Segment(csf_output_type=[True, True, False], gm_output_type=[True, True, False], wm_output_type=[True, True, False]), name='segment') mergefunc = lambda in1, in2, in3: [in1, in2, in3] merge = pe.Node(niu.Function(input_names=['in1', 'in2', 'in3'], output_names=['out'], function=mergefunc), name='merge') workflow.connect(inputnode, 'csf_prob', merge, 'in3') workflow.connect(inputnode, 'wm_prob', merge, 'in2') workflow.connect(inputnode, 'gm_prob', merge, 'in1') sym_prob = sym_func workflow.connect(merge, 'out', sym_prob, 'in_file') workflow.connect(sym_prob, 'out_link', segment, 'tissue_prob_maps') xform_mask = pe.Node(fs.ApplyVolTransform(fs_target=True), name='transform_mask') workflow.connect(maskflow, ('outputspec.reg_file', pickfirst), xform_mask, 'reg_file') workflow.connect(maskflow, ('outputspec.mask_file', pickfirst), xform_mask, 'source_file') workflow.connect(xform_mask, "transformed_file", segment, 'mask_image') fssource = maskflow.get_node('fssource') convert2nii = pe.Node(fs.MRIConvert(in_type='mgz', out_type='nii'), name='convert2nii') workflow.connect(fssource, 'brain', convert2nii, 'in_file') workflow.connect(convert2nii, 'out_file', segment, 'data') # NORMALIZE normalize = pe.MapNode(spm.Normalize(jobtype='write'), name='normalize', iterfield=['apply_to_files']) normalize_struct = normalize.clone('normalize_struct') normalize_mask = normalize.clone('normalize_mask') workflow.connect(segment, 'transformation_mat', normalize, 'parameter_file') workflow.connect(segment, 'transformation_mat', normalize_mask, 'parameter_file') workflow.connect(segment, 'transformation_mat', normalize_struct, 'parameter_file') workflow.connect(convert2nii, 'out_file', normalize_struct, 'apply_to_files') workflow.connect(xform_mask, "transformed_file", normalize_mask, 'apply_to_files') xform_image = pe.MapNode(fs.ApplyVolTransform(fs_target=True), name='xform_image', iterfield=['source_file']) workflow.connect(maskflow, ('outputspec.reg_file', pickfirst), xform_image, 'reg_file') workflow.connect(realign, 'out_file', xform_image, "source_file") workflow.connect(xform_image, "transformed_file", normalize, "apply_to_files") #SMOOTH smooth = pe.Node(spm.Smooth(), name='smooth') workflow.connect(inputnode, 'fwhm', smooth, 'fwhm') workflow.connect(normalize, 'normalized_files', smooth, 'in_files') # ART artdetect = pe.Node(ra.ArtifactDetect(mask_type='file', use_differences=[True, False], use_norm=True, save_plot=True), name='artdetect') workflow.connect(realign, 'parameter_source', artdetect, 'parameter_source') workflow.connect([(inputnode, artdetect, [('norm_threshold', 'norm_threshold'), ('zintensity_threshold', 'zintensity_threshold')])]) workflow.connect([(realign, artdetect, [('out_file', 'realigned_files'), ('par_file', 'realignment_parameters')])]) workflow.connect(maskflow, ('outputspec.mask_file', poplist), artdetect, 'mask_file') # OUTPUTS outputnode = pe.Node(niu.IdentityInterface(fields=[ "realignment_parameters", "smoothed_files", "mask_file", "mean_image", "reg_file", "reg_cost", 'outlier_files', 'outlier_stats', 'outlier_plots', 'norm_components', 'mod_csf', 'unmod_csf', 'mod_wm', 'unmod_wm', 'mod_gm', 'unmod_gm', 'mean', 'normalized_struct', 'normalization_parameters', 'reverse_normalize_parameters' ]), name="outputspec") workflow.connect([ (maskflow, outputnode, [("outputspec.reg_file", "reg_file")]), (maskflow, outputnode, [("outputspec.reg_cost", "reg_cost")]), (realign, outputnode, [('par_file', 'realignment_parameters')]), (smooth, outputnode, [('smoothed_files', 'smoothed_files')]), (artdetect, outputnode, [('outlier_files', 'outlier_files'), ('statistic_files', 'outlier_stats'), ('plot_files', 'outlier_plots'), ('norm_files', 'norm_components')]) ]) workflow.connect(normalize_mask, "normalized_files", outputnode, "mask_file") workflow.connect(segment, 'modulated_csf_image', outputnode, 'mod_csf') workflow.connect(segment, 'modulated_wm_image', outputnode, 'mod_wm') workflow.connect(segment, 'modulated_gm_image', outputnode, 'mod_gm') workflow.connect(segment, 'normalized_csf_image', outputnode, 'unmod_csf') workflow.connect(segment, 'normalized_wm_image', outputnode, 'unmod_wm') workflow.connect(segment, 'normalized_gm_image', outputnode, 'unmod_gm') workflow.connect(mean, 'outputspec.mean_image', outputnode, 'mean') workflow.connect(normalize_struct, 'normalized_files', outputnode, 'normalized_struct') workflow.connect(segment, 'transformation_mat', outputnode, 'normalization_parameters') workflow.connect(segment, 'inverse_transformation_mat', outputnode, 'reverse_normalize_parameters') # CONNECT TO CONFIG workflow.inputs.inputspec.fwhm = c.fwhm workflow.inputs.inputspec.subjects_dir = c.surf_dir workflow.inputs.inputspec.norm_threshold = c.norm_thresh workflow.inputs.inputspec.zintensity_threshold = c.z_thresh workflow.inputs.inputspec.node = c.motion_correct_node workflow.inputs.inputspec.tr = c.TR workflow.inputs.inputspec.do_slicetime = c.do_slicetiming workflow.inputs.inputspec.sliceorder = c.SliceOrder workflow.inputs.inputspec.csf_prob = c.csf_prob workflow.inputs.inputspec.gm_prob = c.grey_prob workflow.inputs.inputspec.wm_prob = c.white_prob workflow.inputs.inputspec.parameters = {"order": c.order} workflow.base_dir = c.working_dir workflow.config = {'execution': {'crashdump_dir': c.crash_dir}} datagrabber = get_dataflow(c) workflow.connect(datagrabber, 'func', inputnode, 'functionals') infosource = pe.Node(niu.IdentityInterface(fields=['subject_id']), name='subject_names') if not c.test_mode: infosource.iterables = ('subject_id', c.subjects) else: infosource.iterables = ('subject_id', c.subjects[:1]) workflow.connect(infosource, 'subject_id', inputnode, 'subject_id') workflow.connect(infosource, 'subject_id', datagrabber, 'subject_id') sub = lambda x: [('_subject_id_%s' % x, '')] sinker = pe.Node(nio.DataSink(), name='sinker') workflow.connect(infosource, 'subject_id', sinker, 'container') workflow.connect(infosource, ('subject_id', sub), sinker, 'substitutions') sinker.inputs.base_directory = c.sink_dir outputspec = workflow.get_node('outputspec') workflow.connect(outputspec, 'realignment_parameters', sinker, 'spm_preproc.realignment_parameters') workflow.connect(outputspec, 'smoothed_files', sinker, 'spm_preproc.smoothed_outputs') workflow.connect(outputspec, 'outlier_files', sinker, 'spm_preproc.art.@outlier_files') workflow.connect(outputspec, 'outlier_stats', sinker, 'spm_preproc.art.@outlier_stats') workflow.connect(outputspec, 'outlier_plots', sinker, 'spm_preproc.art.@outlier_plots') workflow.connect(outputspec, 'norm_components', sinker, 'spm_preproc.art.@norm') workflow.connect(outputspec, 'reg_file', sinker, 'spm_preproc.bbreg.@reg_file') workflow.connect(outputspec, 'reg_cost', sinker, 'spm_preproc.bbreg.@reg_cost') workflow.connect(outputspec, 'mask_file', sinker, 'spm_preproc.mask.@mask_file') workflow.connect(outputspec, 'mod_csf', sinker, 'spm_preproc.segment.mod.@csf') workflow.connect(outputspec, 'mod_wm', sinker, 'spm_preproc.segment.mod.@wm') workflow.connect(outputspec, 'mod_gm', sinker, 'spm_preproc.segment.mod.@gm') workflow.connect(outputspec, 'unmod_csf', sinker, 'spm_preproc.segment.unmod.@csf') workflow.connect(outputspec, 'unmod_wm', sinker, 'spm_preproc.segment.unmod.@wm') workflow.connect(outputspec, 'unmod_gm', sinker, 'spm_preproc.segment.unmod.@gm') workflow.connect(outputspec, 'mean', sinker, 'spm_preproc.mean') workflow.connect(outputspec, 'normalized_struct', sinker, 'spm_preproc.normalized_struct') workflow.connect(outputspec, 'normalization_parameters', sinker, 'spm_preproc.normalization_parameters.@forward') workflow.connect(outputspec, 'reverse_normalize_parameters', sinker, 'spm_preproc.normalization_parameters.@reverse') return workflow
""" preproc = pe.Workflow(name='preproc') """Use :class:`nipype.interfaces.spm.Realign` for motion correction and register all images to the mean image. """ realign = pe.Node(interface=spm.Realign(), name="realign") realign.inputs.register_to_mean = True """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 = pe.Node(interface=ra.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' """Skull strip structural images using :class:`nipype.interfaces.fsl.BET`. """ skullstrip = pe.Node(interface=fsl.BET(), name="skullstrip") skullstrip.inputs.mask = True """Use :class:`nipype.interfaces.spm.Coregister` to perform a rigid body registration of the functional data to the structural data. """
def create_art_workflow(name="art", make_movie=True): # Define the workflow inputs inputnode = pe.Node(util.IdentityInterface(fields=[ "raw_timeseries", "realigned_timeseries", "mask_file", "realignment_parameters" ]), name="inputs") # Use RapidART to detect motion/intensity outliers art = pe.MapNode( ra.ArtifactDetect(use_differences=[True, False], use_norm=True, zintensity_threshold=3, norm_threshold=1, parameter_source="FSL", mask_type="file"), iterfield=["realignment_parameters", "realigned_files", "mask_file"], name="art") # Plot a timeseries of the global mean intensity art_plot_inputs = ["intensity_file", "outlier_file"] plotmean = pe.MapNode(util.Function(input_names=art_plot_inputs, output_names="intensity_plot", function=write_art_plot), iterfield=art_plot_inputs, name="plotmean") # Use our very own ts_movie script to generate a movie of the timeseries if make_movie: tsmovie = pe.MapNode(TimeSeriesMovie(ref_type="mean"), iterfield=["in_file", "plot_file"], name="tsmovie") # Rename the outputs moviename = pe.MapNode( util.Rename(format_string="timeseries_movie.gif"), iterfield=["in_file"], name="moviename") outliername = pe.MapNode(util.Rename(format_string="outlier_volumes.txt"), iterfield=["in_file"], name="outliername") # Define the workflow outputs out_fields = ["outlier_volumes", "intensity_plot"] if make_movie: out_fields.append("timeseries_movie") outputnode = pe.Node(util.IdentityInterface(fields=out_fields), name="outputs") # Define and connect the workflow artifact = pe.Workflow(name=name) artifact.connect([ (inputnode, art, [("realignment_parameters", "realignment_parameters"), ("realigned_timeseries", "realigned_files"), ("mask_file", "mask_file")]), (art, plotmean, [("intensity_files", "intensity_file"), ("outlier_files", "outlier_file")]), (art, outliername, [("outlier_files", "in_file")]), (outliername, outputnode, [("out_file", "outlier_volumes")]), (plotmean, outputnode, [("intensity_plot", "intensity_plot")]), ]) if make_movie: artifact.connect([ (inputnode, tsmovie, [("raw_timeseries", "in_file")]), (art, tsmovie, [("intensity_files", "plot_file")]), (tsmovie, moviename, [("out_file", "in_file")]), (moviename, outputnode, [("out_file", "timeseries_movie")]), ]) return artifact
iterfield = ['in_file']) motion_correct.inputs.in_file = func_files psb6351_wf.connect(extractref, 'roi_file', motion_correct, 'ref_file') psb6351_wf.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') psb6351_wf.connect(motion_correct, 'out_file', outputnode, 'realigned_files') ''' ############################### # ADD RAPIDART DETECTION HERE # ############################### # Evaluate the number of outliers that are detected # when using zintensity_thresholds of 1, 2, 3, 4 # and when using norm_threshold of 2, 1, 0.5, 0.2 ############################### AD = pe.MapNode(rapidart.ArtifactDetect(), iterfield=['realigned_files', 'realignment_parameters'], name='AD') AD.inputs.mask_type = 'spm_global' AD.inputs.norm_threshold = 2.0 AD.inputs.parameter_source = 'AFNI' #AD.inputs.realigned_files = 'motion_func' #AD.inputs.realignment_parameters = 'motion_par' AD.inputs.zintensity_threshold = 1.0 AD.inputs.global_threshold = 9.0 #AD.inputs.use_norm = 'TRUE' psb6351_wf.connect(volreg, 'out_file', AD, 'realigned_files') psb6351_wf.connect(volreg, 'oned_file', AD, 'realignment_parameters') # Below is the command that runs AFNI's 3dTshift command
def test_ad_init(): ad = ra.ArtifactDetect(use_differences=[True, False]) yield assert_true, ad.inputs.use_differences[0] yield assert_false, ad.inputs.use_differences[1]
""" preproc = pe.Workflow(name='preproc') """Use :class:`nipype.interfaces.spm.Realign` for motion correction and register all images to the mean image. """ realign = pe.Node(spm.Realign(), name="realign") realign.inputs.register_to_mean = True """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 = pe.Node(ra.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' """Skull strip structural images using :class:`nipype.interfaces.fsl.BET`. """ skullstrip = pe.Node(fsl.BET(), name="skullstrip") skullstrip.inputs.mask = True """Use :class:`nipype.interfaces.spm.Coregister` to perform a rigid body registration of the functional data to the structural data. """
def create_workflow(): featpreproc = pe.Workflow(name="featpreproc") featpreproc.base_dir = os.path.join(ds_root, 'workingdirs') # =================================================================== # _____ _ # |_ _| | | # | | _ __ _ __ _ _| |_ # | | | '_ \| '_ \| | | | __| # _| |_| | | | |_) | |_| | |_ # |_____|_| |_| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Specify variables inputnode = pe.Node( niu.IdentityInterface(fields=[ 'funcs', 'subject_id', 'session_id', 'fwhm', # smoothing 'highpass' ]), name="inputspec") # SelectFiles templates = { 'ref_manual_fmapmask': # was: manual_fmapmask 'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_magnitude1_res-1x1x1_manualmask.nii.gz', 'ref_fmap_magnitude': 'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_magnitude1_res-1x1x1_reference.nii.gz', 'ref_fmap_phasediff': 'derivatives/resampled-isotropic-1mm/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_phasediff_res-1x1x1_preproc' '.nii.gz', # 'manualweights': # 'manual-masks/sub-eddy/ses-20170511/func/' # 'sub-eddy_ses-20170511_task-curvetracing_run-01_frame-50_bold' # '_res-1x1x1_manualweights.nii.gz', 'ref_func': # was: manualmask_func_ref 'derivatives/manual-masks/sub-eddy/ses-20170607/func/' 'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_' 'res-1x1x1_fnirt_reference.nii.gz', 'ref_funcmask': # was: manualmask 'derivatives/manual-masks/sub-eddy/ses-20170607/func/' 'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_' 'res-1x1x1_fnirt_mask.nii.gz', 'ref_t1': 'derivatives/manual-masks/sub-eddy/ses-20170511/anat/' 'sub-eddy_ses-20170511_T1w_res-1x1x1_reference.nii.gz', 'ref_t1mask': 'derivatives/manual-masks/sub-eddy/ses-20170511/anat/' 'sub-eddy_ses-20170511_T1w_res-1x1x1_manualmask.nii.gz', # 'funcs': # 'resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/func/' # # 'sub-{subject_id}_ses-{session_id}*_bold_res-1x1x1_preproc' # 'sub-{subject_id}_ses-{session_id}*run-01_bold_res-1x1x1_preproc' # # '.nii.gz', # '_nvol10.nii.gz', 'fmap_phasediff': 'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}_phasediff_res-1x1x1_preproc' '.nii.gz', 'fmap_magnitude': 'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}_magnitude1_res-1x1x1_preproc' '.nii.gz', # 'fmap_mask': # 'transformed-manual-fmap-mask/sub-{subject_id}/ses-{session_id}/fmap/' # 'sub-{subject_id}_ses-{session_id}_' # 'magnitude1_res-1x1x1_preproc.nii.gz', } inputfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir), name="input_files") featpreproc.connect([(inputnode, inputfiles, [ ('subject_id', 'subject_id'), ('session_id', 'session_id'), ])]) # =================================================================== # ____ _ _ # / __ \ | | | | # | | | |_ _| |_ _ __ _ _| |_ # | | | | | | | __| '_ \| | | | __| # | |__| | |_| | |_| |_) | |_| | |_ # \____/ \__,_|\__| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Output Files # Datasink outputfiles = pe.Node(nio.DataSink(base_directory=ds_root, container='derivatives/featpreproc', parameterization=True), name="output_files") # Use the following DataSink output substitutions # each tuple is only matched once per file outputfiles.inputs.substitutions = [ ('/_mc_method_afni3dAllinSlices/', '/'), ('/_mc_method_afni3dAllinSlices/', '/'), # needs to appear twice ('/oned_file/', '/'), ('/out_file/', '/'), ('/oned_matrix_save/', '/'), ('subject_id_', 'sub-'), ('session_id_', 'ses-'), ] # Put result into a BIDS-like format outputfiles.inputs.regexp_substitutions = [ (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1'), (r'/_addmean[0-9]+/', r'/func/'), (r'/_funcbrains[0-9]+/', r'/func/'), (r'/_maskfunc[0-9]+/', r'/func/'), (r'/_mc[0-9]+/', r'/func/'), (r'/_meanfunc[0-9]+/', r'/func/'), (r'/_outliers[0-9]+/', r'/func/'), (r'_run_id_[0-9][0-9]', r''), ] outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'motion_parameters', 'motion_corrected', 'motion_plots', 'motion_outlier_files', 'mask', 'smoothed_files', 'highpassed_files', 'mean', 'func_unwarp', 'ref_func', 'ref_funcmask', 'ref_t1', 'ref_t1mask', ]), name='outputspec') # =================================================================== # _____ _ _ _ # | __ (_) | (_) # | |__) | _ __ ___| |_ _ __ ___ # | ___/ | '_ \ / _ \ | | '_ \ / _ \ # | | | | |_) | __/ | | | | | __/ # |_| |_| .__/ \___|_|_|_| |_|\___| # | | # |_| # =================================================================== # ~|~ _ _ _ _ |` _ _ _ _ _ _ _ _| _ # | | (_|| |_\~|~(_)| | | | | | |(_|_\|<_\ # # Transform manual skull-stripped masks to multiple images # -------------------------------------------------------- # should just be used as input to motion correction, # after mc, all functionals should be aligned to reference transmanmask_mc = transform_manualmask.create_workflow() # - - - - - - Connections - - - - - - - featpreproc.connect([(inputfiles, transmanmask_mc, [ ('subject_id', 'in.subject_id'), ('session_id', 'in.session_id'), ])]) featpreproc.connect(inputfiles, 'ref_funcmask', transmanmask_mc, 'in.manualmask') featpreproc.connect(inputnode, 'funcs', transmanmask_mc, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', transmanmask_mc, 'in.manualmask_func_ref') # fieldmaps not being used if False: trans_fmapmask = transmanmask_mc.clone('trans_fmapmask') featpreproc.connect(inputfiles, 'ref_manual_fmapmask', trans_fmapmask, 'in.manualmask') featpreproc.connect(inputfiles, 'fmap_magnitude', trans_fmapmask, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', trans_fmapmask, 'in.manualmask_func_ref') # |\/| _ _|_. _ _ _ _ _ _ _ __|_. _ _ # | |(_) | |(_)| | (_(_)| | (/_(_ | |(_)| | # # Perform motion correction, using some pipeline # -------------------------------------------------------- # mc = motioncorrection_workflow.create_workflow_afni() # Register an image from the functionals to the reference image median_func = pe.MapNode( interface=fsl.maths.MedianImage(dimension="T"), name='median_func', iterfield=('in_file'), ) pre_mc = motioncorrection_workflow.create_workflow_allin_slices( name='premotioncorrection') featpreproc.connect([ (inputnode, median_func, [ ('funcs', 'in_file'), ]), (median_func, pre_mc, [ ('out_file', 'in.funcs'), ]), ( inputfiles, pre_mc, [ # median func image will be used a reference / base ('ref_func', 'in.ref_func'), ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, pre_mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (pre_mc, outputnode, [ ('mc.out_file', 'pre_motion_corrected'), ('mc.oned_file', 'pre_motion_parameters.oned_file'), ('mc.oned_matrix_save', 'pre_motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('pre_motion_corrected', 'pre_motion_corrected.out_file'), ('pre_motion_parameters.oned_file', 'pre_motion_corrected.oned_file' ), # warp parameters in ASCII (.1D) ('pre_motion_parameters.oned_matrix_save', 'pre_motion_corrected.oned_matrix_save' ), # transformation matrices for each sub-brick ]), ]) mc = motioncorrection_workflow.create_workflow_allin_slices( name='motioncorrection', iterfield=('in_file', 'ref_file', 'in_weight_file')) # - - - - - - Connections - - - - - - - featpreproc.connect([ (inputnode, mc, [ ('funcs', 'in.funcs'), ]), ( pre_mc, mc, [ # the median image realigned to the reference functional will serve as reference # this way motion correction is done to an image more similar to the functionals ('mc.out_file', 'in.ref_func'), ]), ( inputfiles, mc, [ # Check and make sure the ref func mask is close enough to the registered median # image. ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (mc, outputnode, [ ('mc.out_file', 'motion_corrected'), ('mc.oned_file', 'motion_parameters.oned_file'), ('mc.oned_matrix_save', 'motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('motion_corrected', 'motion_corrected.out_file'), ('motion_parameters.oned_file', 'motion_corrected.oned_file' ), # warp parameters in ASCII (.1D) ('motion_parameters.oned_matrix_save', 'motion_corrected.oned_matrix_save' ), # transformation matrices for each sub-brick ]), ]) # |~. _ | _| _ _ _ _ _ _ _ _ _ __|_. _ _ # |~|(/_|(_|| | |(_||_) (_(_)| | (/_(_ | |(_)| | # | # Unwarp EPI distortions # -------------------------------------------------------- # Performing motion correction to a reference that is undistorted, # so b0_unwarp is currently not needed if False: b0_unwarp = undistort_workflow.create_workflow() featpreproc.connect([ ( inputfiles, b0_unwarp, [ # ('subject_id', 'in.subject_id'), # ('session_id', 'in.session_id'), ('fmap_phasediff', 'in.fmap_phasediff'), ('fmap_magnitude', 'in.fmap_magnitude'), ]), (mc, b0_unwarp, [ ('mc.out_file', 'in.funcs'), ]), (transmanmask_mc, b0_unwarp, [ ('funcreg.out_file', 'in.funcmasks'), ]), (trans_fmapmask, b0_unwarp, [('funcreg.out_file', 'in.fmap_mask') ]), (b0_unwarp, outputfiles, [ ('out.funcs', 'func_unwarp.funcs'), ('out.funcmasks', 'func_unwarp.funcmasks'), ]), (b0_unwarp, outputnode, [ ('out.funcs', 'func_unwarp.funcs'), ('out.funcmasks', 'mask'), ]), ]) # undistort the reference images if False: b0_unwarp_ref = b0_unwarp.clone('b0_unwarp_ref') featpreproc.connect([ ( inputfiles, b0_unwarp_ref, [ # ('subject_id', 'in.subject_id'), # ('session_id', 'in.session_id'), ('ref_fmap_phasediff', 'in.fmap_phasediff'), ('ref_fmap_magnitude', 'in.fmap_magnitude'), ('ref_manual_fmapmask', 'in.fmap_mask'), ('ref_func', 'in.funcs'), ('ref_funcmask', 'in.funcmasks'), ]), (b0_unwarp_ref, outputfiles, [ ('out.funcs', 'func_unwarp_ref.func'), ('out.funcmasks', 'func_unwarp_ref.funcmask'), ]), (b0_unwarp_ref, outputnode, [ ('out.funcs', 'ref_func'), ('out.funcmasks', 'ref_mask'), ]), ]) else: featpreproc.connect([ (inputfiles, outputfiles, [ ('ref_func', 'reference/func'), ('ref_funcmask', 'reference/func_mask'), ]), (inputfiles, outputnode, [ ('ref_func', 'ref_func'), ('ref_funcmask', 'ref_funcmask'), ]), ]) # |~) _ _ . __|_ _ _ _|_ _ |~) _ |` _ _ _ _ _ _ # |~\(/_(_||_\ | (/_| | (_) |~\(/_~|~(/_| (/_| |(_(/_ # _| # Register all functionals to common reference # -------------------------------------------------------- if False: # this is now done during motion correction # FLIRT cost: intermodal: corratio, intramodal: least squares and normcorr reg_to_ref = pe.MapNode( # intra-modal # some runs need to be scaled along the anterior-posterior direction interface=fsl.FLIRT(dof=12, cost='normcorr'), name='reg_to_ref', iterfield=('in_file', 'in_weight'), ) refEPI_to_refT1 = pe.Node( # some runs need to be scaled along the anterior-posterior direction interface=fsl.FLIRT(dof=12, cost='corratio'), name='refEPI_to_refT1', ) # combine func -> ref_func and ref_func -> ref_T1 reg_to_refT1 = pe.MapNode( interface=fsl.ConvertXFM(concat_xfm=True), name='reg_to_refT1', iterfield=('in_file'), ) reg_funcs = pe.MapNode( interface=fsl.preprocess.ApplyXFM(), name='reg_funcs', iterfield=('in_file', 'in_matrix_file'), ) reg_funcmasks = pe.MapNode(interface=fsl.preprocess.ApplyXFM(), name='reg_funcmasks', iterfield=('in_file', 'in_matrix_file')) def deref_list(x): assert len(x) == 1 return x[0] featpreproc.connect([ ( b0_unwarp, reg_to_ref, # --> reg_to_ref, (A) [ ('out.funcs', 'in_file'), ('out.funcmasks', 'in_weight'), ]), (b0_unwarp_ref, reg_to_ref, [ (('out.funcs', deref_list), 'reference'), (('out.funcmasks', deref_list), 'ref_weight'), ]), ( b0_unwarp_ref, refEPI_to_refT1, # --> refEPI_to_refT1 (B) [ (('out.funcs', deref_list), 'in_file'), (('out.funcmasks', deref_list), 'in_weight'), ]), (inputfiles, refEPI_to_refT1, [ ('ref_t1', 'reference'), ('ref_t1mask', 'ref_weight'), ]), ( reg_to_ref, reg_to_refT1, # --> reg_to_refT1 (A*B) [ ('out_matrix_file', 'in_file'), ]), (refEPI_to_refT1, reg_to_refT1, [ ('out_matrix_file', 'in_file2'), ]), ( reg_to_refT1, reg_funcs, # --> reg_funcs [ # ('out_matrix_file', 'in_matrix_file'), ('out_file', 'in_matrix_file'), ]), (b0_unwarp, reg_funcs, [ ('out.funcs', 'in_file'), ]), (b0_unwarp_ref, reg_funcs, [ (('out.funcs', deref_list), 'reference'), ]), ( reg_to_refT1, reg_funcmasks, # --> reg_funcmasks [ # ('out_matrix_file', 'in_matrix_file'), ('out_file', 'in_matrix_file'), ]), (b0_unwarp, reg_funcmasks, [ ('out.funcmasks', 'in_file'), ]), (b0_unwarp_ref, reg_funcmasks, [ (('out.funcs', deref_list), 'reference'), ]), (reg_funcs, outputfiles, [ ('out_file', 'common_ref.func'), ]), (reg_funcmasks, outputfiles, [ ('out_file', 'common_ref.funcmask'), ]), ]) # |\/| _ _|_. _ _ _ _|_|. _ _ _ # | |(_) | |(_)| | (_)|_|| ||(/_| _\ # # -------------------------------------------------------- # Apply brain masks to functionals # -------------------------------------------------------- # Dilate mask """ Dilate the mask """ if False: dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(reg_funcmasks, 'out_file', dilatemask, 'in_file') else: dilatemask = pe.Node(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), name='dilatemask') featpreproc.connect(inputfiles, 'ref_funcmask', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputfiles, 'dilate_mask') funcbrains = pe.MapNode(fsl.BinaryMaths(operation='mul'), iterfield=('in_file', 'operand_file'), name='funcbrains') featpreproc.connect([ (mc, funcbrains, [ ('mc.out_file', 'in_file'), ]), (dilatemask, funcbrains, [ ('out_file', 'operand_file'), ]), (funcbrains, outputfiles, [ ('out_file', 'funcbrains'), ]), ]) # Detect motion outliers # -------------------------------------------------------- import nipype.algorithms.rapidart as ra outliers = pe.MapNode( ra.ArtifactDetect( mask_type='file', # trying to "disable" `norm_threshold`: use_norm=True, norm_threshold=10.0, # combines translations in mm and rotations # use_norm=Undefined, # translation_threshold=1.0, # translation in mm # rotation_threshold=0.02, # rotation in radians zintensity_threshold=3.0, # z-score parameter_source='AFNI', save_plot=True), iterfield=('realigned_files', 'realignment_parameters', 'mask_file'), name='outliers') featpreproc.connect([ ( mc, outliers, [ # ('mc.par_file', 'realignment_parameters'), ('mc.oned_file', 'realignment_parameters'), ]), (funcbrains, outliers, [ ('out_file', 'realigned_files'), ]), (dilatemask, outliers, [ ('out_file', 'mask_file'), ]), ( outliers, outputfiles, [ ('outlier_files', 'motion_outliers.@outlier_files'), ('plot_files', 'motion_outliers.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]), (mc, outputnode, [ ('mc.oned_file', 'motion_parameters'), ]), ( outliers, outputnode, [ ('outlier_files', 'motion_outlier_files'), ('plot_files', 'motion_plots.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]) ]) """ Determine the 2nd and 98th percentile intensities of each functional run """ getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield=['in_file'], name='getthreshold') if False: featpreproc.connect(b0_unwarp, 'out.funcs', getthresh, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', getthresh, 'in_file') """ Threshold the first run of functional data at 10% of the 98th percentile """ threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') if False: featpreproc.connect(b0_unwarp, 'out.funcs', threshold, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ def getthreshop(thresh): return ['-thr %.10f -Tmin -bin' % (0.1 * val[1]) for val in thresh] featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield=['in_file', 'mask_file'], name='medianval') if False: featpreproc.connect(b0_unwarp, 'out.funcs', medianval, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') # (~ _ _ _|_. _ | (~ _ _ _ _ _|_|_ . _ _ # _)|_)(_| | |(_|| _)| | |(_)(_) | | ||| |(_| # | _| # Spatial smoothing (SUSAN) # -------------------------------------------------------- # create_susan_smooth takes care of calculating the mean and median # functional, applying mask to functional, and running the smoothing smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') # featpreproc.connect(b0_unwarp, 'out.funcs', smooth, 'inputnode.in_files') if False: featpreproc.connect(reg_funcs, 'out_file', smooth, 'inputnode.in_files') else: featpreproc.connect(mc, 'mc.out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') # ------------------------------------------------------- # The below is from workflows/fmri/fsl/preprocess.py """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') tolist = lambda x: [x] def chooseindex(fwhm): if fwhm < 1: return [0] else: return [1] # maskfunc2 is the functional data before SUSAN if False: featpreproc.connect(b0_unwarp, ('out.funcs', tolist), concatnode, 'in1') else: featpreproc.connect(mc, ('mc.out_file', tolist), concatnode, 'in1') # maskfunc3 is the functional data after SUSAN featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(), name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputfiles, 'smoothed_files') """ Scale the median value of the run is set to 10000. """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file', 'op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') # |_|. _ |_ _ _ _ _ # | ||(_|| ||_)(_|_\_\ # _| | # Temporal filtering # -------------------------------------------------------- highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') highpass_operand = lambda x: '-bptf %.10f -1' % x featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 if version < 507: featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') else: """ Add back the mean removed by the highpass filter operation as of FSL 5.0.7 """ meanfunc4 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc4') featpreproc.connect(meanscale, 'out_file', meanfunc4, 'in_file') addmean = pe.MapNode(interface=fsl.BinaryMaths(operation='add'), iterfield=['in_file', 'operand_file'], name='addmean') featpreproc.connect(highpass, 'out_file', addmean, 'in_file') featpreproc.connect(meanfunc4, 'out_file', addmean, 'operand_file') featpreproc.connect(addmean, 'out_file', outputnode, 'highpassed_files') """ Generate a mean functional image from the first run """ meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputfiles, 'mean') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean_highpassed') featpreproc.connect(outputnode, 'highpassed_files', outputfiles, 'highpassed_files') return (featpreproc)