def gen_sig2noise_img(in4d, outdir): startdir = os.getcwd() os.chdir(outdir) tsnr = misc.TSNR() tsnr.inputs.in_file = in4d tsnrout = tsnr.run() ## there is no valid return code for tsnr #if tsnrout.runtime.returncode == 0: # return tsnrout.outputs.tsnr_file #else: # print tsnrout.runtime.stderr # return None os.chdir(startdir) return tsnrout.outputs.tsnr_file
def fmri_qc(name='fmri_qc'): inputnode = pe.Node( utility.IdentityInterface( fields=['motion','realigned','mask','grey']), name='inputspec') n_tsnr = pe.Node(algmisc.TSNR(), name='tsnr') def tsnr_stats(tsnr,mask,grey=None): import nibabel as nb, numpy as np tsnr=nb.load(tsnr).get_data() mask=np.logical_and( nb.load(mask).get_data()>0, np.logical_not(np.logical_or(np.isinf(tsnr),np.isnan(tsnr)))) grey_tsnr_mean = np.nan grey_tsnr_std = np.nan if grey is not None: grey=np.logical_and(nb.load(grey).get_data()>0, mask) grey_tsnr_mean = tsnr[grey].mean() grey_tsnr_std = tsnr[grey].std() brain_tsnr_mean = tsnr[mask].mean() brain_tsnr_std = tsnr[mask].std() return brain_tsnr_mean,brain_tsnr_std,grey_tsnr_mean,grey_tsnr_std n_tsnr_stats = pe.Node( utility.Function( input_names=['tsnr','mask','grey'], output_names=['brain_tsnr_mean','brain_tsnr_std', 'grey_tsnr_mean','grey_tsnr_std'], function=tsnr_stats), name='tsnr_stats') w=pe.Workflow(name=name) w.connect([ (inputnode,n_tsnr,[('realigned','in_file')]), (n_tsnr,n_tsnr_stats,[('tsnr_file','tsnr')]), (inputnode,n_tsnr_stats,[('mask','mask'), ('grey','grey')]), ]) return w
despike = Node(afni.Despike(), name='despike') despike.inputs.outputtype = 'NIFTI' #Outputs: out_file #Slice timing corrected (gets timing from header) st_corr = Node(spm.SliceTiming(), name='slicetiming_correction') st_corr.inputs.ref_slice = 1 #Outputs: timecorrected_files #Realignment using SPM <--- Maybe just estimate and apply all transforms at the end? realign = Node(spm.Realign(), name='realign') realign.inputs.register_to_mean = False realign.inputs.quality = 1.0 #Outputs: realignment_parameters, reliced epi images (motion corrected) tsnr = Node(misc.TSNR(), name='tsnr') tsnr.inputs.regress_poly = 2 #Outputs: detrended_file, mean_file, stddev_file, tsnr_file smooth = Node(spm.Smooth(), name='smooth') smooth.inputs.fwhm = fwhm ####Anatomical preprocessing#### #dcmstack - Convert dicoms to nii (with embeded metadata) anat_stack = Node(dcmstack.DcmStack(), name='anatstack') anat_stack.inputs.embed_meta = True anat_stack.inputs.out_format = 'anat' anat_stack.inputs.out_ext = '.nii' #Outputs: out_file
def fmri_qc_workflow(name='fMRIQC', settings=None): """ The fMRI qc workflow """ if settings is None: settings = {} workflow = pe.Workflow(name=name) deriv_dir = op.abspath('./derivatives') if 'work_dir' in settings.keys(): deriv_dir = op.abspath(op.join(settings['work_dir'], 'derivatives')) if not op.exists(deriv_dir): os.makedirs(deriv_dir) # Read FD radius, or default it fd_radius = settings.get('fd_radius', 80.) # Define workflow, inputs and outputs inputnode = pe.Node(niu.IdentityInterface(fields=[ 'bids_root', 'subject_id', 'session_id', 'run_id', 'site_name', 'start_idx', 'stop_idx' ]), name='inputnode') get_idx = pe.Node(niu.Function( input_names=['in_file', 'start_idx', 'stop_idx'], function=fmri_getidx, output_names=['start_idx', 'stop_idx']), name='get_idx') outputnode = pe.Node(niu.IdentityInterface( fields=['qc', 'mosaic', 'out_group', 'out_movpar', 'out_dvars']), name='outputnode') # 0. Get data datasource = pe.Node(niu.Function(input_names=[ 'bids_root', 'data_type', 'subject_id', 'session_id', 'run_id' ], output_names=['out_file'], function=bids_getfile), name='datasource') datasource.inputs.data_type = 'func' # Workflow -------------------------------------------------------- # 1. HMC: head motion correct hmcwf = hmc_mcflirt() if settings.get('hmc_afni', False): hmcwf = hmc_afni( st_correct=settings.get('correct_slice_timing', False)) hmcwf.inputs.inputnode.fd_radius = fd_radius mean = pe.Node( afp.TStat( # 2. Compute mean fmri options='-mean', outputtype='NIFTI_GZ'), name='mean') bmw = fmri_bmsk_workflow( # 3. Compute brain mask use_bet=settings.get('use_bet', False)) # Compute TSNR using nipype implementation tsnr = pe.Node(nam.TSNR(), name='compute_tsnr') # Compute DVARS dvnode = pe.Node(ComputeDVARS(), name='ComputeDVARS') # AFNI quality measures fwhm = pe.Node(afp.FWHMx(combine=True, detrend=True), name='smoothness') # fwhm.inputs.acf = True # add when AFNI >= 16 outliers = pe.Node(afp.OutlierCount(fraction=True, out_file='ouliers.out'), name='outliers') quality = pe.Node(afp.QualityIndex(automask=True), out_file='quality.out', name='quality') measures = pe.Node(FunctionalQC(), name='measures') # Plots plot_mean = pe.Node(PlotMosaic(title='Mean fMRI'), name='plot_mean') plot_tsnr = pe.Node(PlotMosaic(title='tSNR volume'), name='plot_tSNR') plot_fd = pe.Node(PlotFD(), name='plot_fd') plot_fd.inputs.fd_radius = fd_radius merg = pe.Node(niu.Merge(3), name='plot_metadata') workflow.connect([ (inputnode, datasource, [('bids_root', 'bids_root'), ('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (inputnode, get_idx, [('start_idx', 'start_idx'), ('stop_idx', 'stop_idx')]), (datasource, get_idx, [('out_file', 'in_file')]), (inputnode, merg, [('session_id', 'in1'), ('run_id', 'in2'), ('site_name', 'in3')]), (datasource, hmcwf, [('out_file', 'inputnode.in_file')]), (get_idx, hmcwf, [('start_idx', 'inputnode.start_idx'), ('stop_idx', 'inputnode.stop_idx')]), (hmcwf, bmw, [('outputnode.out_file', 'inputnode.in_file')]), (hmcwf, mean, [('outputnode.out_file', 'in_file')]), (hmcwf, tsnr, [('outputnode.out_file', 'in_file')]), (mean, plot_mean, [('out_file', 'in_file')]), (tsnr, plot_tsnr, [('tsnr_file', 'in_file')]), (hmcwf, plot_fd, [('outputnode.out_movpar', 'in_file')]), (inputnode, plot_mean, [('subject_id', 'subject')]), (inputnode, plot_tsnr, [('subject_id', 'subject')]), (inputnode, plot_fd, [('subject_id', 'subject')]), (merg, plot_mean, [('out', 'metadata')]), (merg, plot_tsnr, [('out', 'metadata')]), (merg, plot_fd, [('out', 'metadata')]), (mean, fwhm, [('out_file', 'in_file')]), (bmw, fwhm, [('outputnode.out_file', 'mask')]), (hmcwf, outliers, [('outputnode.out_file', 'in_file')]), (bmw, outliers, [('outputnode.out_file', 'mask')]), (hmcwf, quality, [('outputnode.out_file', 'in_file')]), (hmcwf, dvnode, [('outputnode.out_file', 'in_file')]), (bmw, dvnode, [('outputnode.out_file', 'in_mask')]), (mean, measures, [('out_file', 'in_epi')]), (hmcwf, measures, [('outputnode.out_file', 'in_hmc'), ('outputnode.out_movpar', 'fd_movpar')]), (bmw, measures, [('outputnode.out_file', 'in_mask')]), (tsnr, measures, [('tsnr_file', 'in_tsnr')]), (dvnode, measures, [('out_file', 'in_dvars')]), (dvnode, outputnode, [('out_file', 'out_dvars')]), (hmcwf, outputnode, [('outputnode.out_movpar', 'out_movpar')]), ]) if settings.get('mosaic_mask', False): workflow.connect(bmw, 'outputnode.out_file', plot_mean, 'in_mask') workflow.connect(bmw, 'outputnode.out_file', plot_tsnr, 'in_mask') # Save mean mosaic to well-formed path mvmean = pe.Node(niu.Rename( format_string='meanepi_%(subject_id)s_%(session_id)s_%(run_id)s', keep_ext=True), name='rename_mean_mosaic') dsmean = pe.Node(nio.DataSink(base_directory=settings['work_dir'], parameterization=False), name='ds_mean') workflow.connect([(inputnode, mvmean, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (plot_mean, mvmean, [('out_file', 'in_file')]), (mvmean, dsmean, [('out_file', '@mosaic')])]) # Save tSNR mosaic to well-formed path mvtsnr = pe.Node(niu.Rename( format_string='tsnr_%(subject_id)s_%(session_id)s_%(run_id)s', keep_ext=True), name='rename_tsnr_mosaic') dstsnr = pe.Node(nio.DataSink(base_directory=settings['work_dir'], parameterization=False), name='ds_tsnr') workflow.connect([(inputnode, mvtsnr, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (plot_tsnr, mvtsnr, [('out_file', 'in_file')]), (mvtsnr, dstsnr, [('out_file', '@mosaic')])]) # Save FD plot to well-formed path mvfd = pe.Node(niu.Rename( format_string='fd_%(subject_id)s_%(session_id)s_%(run_id)s', keep_ext=True), name='rename_fd_mosaic') dsfd = pe.Node(nio.DataSink(base_directory=settings['work_dir'], parameterization=False), name='ds_fd') workflow.connect([(inputnode, mvfd, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (plot_fd, mvfd, [('out_file', 'in_file')]), (mvfd, dsfd, [('out_file', '@mosaic')])]) # Format name out_name = pe.Node(niu.Function( input_names=['subid', 'sesid', 'runid', 'prefix', 'out_path'], output_names=['out_file'], function=bids_path), name='FormatName') out_name.inputs.out_path = deriv_dir out_name.inputs.prefix = 'func' # Save to JSON file datasink = pe.Node(nio.JSONFileSink(), name='datasink') datasink.inputs.qc_type = 'func' workflow.connect([ (inputnode, out_name, [('subject_id', 'subid'), ('session_id', 'sesid'), ('run_id', 'runid')]), (inputnode, datasink, [('subject_id', 'subject_id'), ('session_id', 'session_id'), ('run_id', 'run_id')]), (plot_mean, datasink, [('out_file', 'mean_plot')]), (plot_tsnr, datasink, [('out_file', 'tsnr_plot')]), (plot_fd, datasink, [('out_file', 'fd_plot')]), (fwhm, datasink, [(('fwhm', fwhm_dict), 'fwhm')]), (outliers, datasink, [(('out_file', _parse_tout), 'outlier')]), (quality, datasink, [(('out_file', _parse_tqual), 'quality')]), (measures, datasink, [('summary', 'summary'), ('spacing', 'spacing'), ('size', 'size'), ('fber', 'fber'), ('efc', 'efc'), ('snr', 'snr'), ('gsr', 'gsr'), ('m_tsnr', 'm_tsnr'), ('fd_stats', 'fd_stats'), ('dvars', 'dvars'), ('gcor', 'gcor')]), (out_name, datasink, [('out_file', 'out_file')]), (datasink, outputnode, [('out_file', 'out_file')]) ]) return workflow
def create_qc_pipeline(working_dir, ds_dir, name='qc'): # initiate workflow qc_wf = Workflow(name=name) qc_wf.base_dir = os.path.join(working_dir, 'LeiCA_resting', 'rsfMRI_preprocessing') # set fsl output fsl.FSLCommand.set_default_output_type('NIFTI_GZ') # I/O NODES inputnode = Node(util.IdentityInterface(fields=[ 'subject_id', 'par_moco', 'outlier_files', 'epi_deskulled', 't1w_brain', 'mean_epi_structSpace', 'mean_epi_MNIspace', 'struct_MNIspace', 'struct_brain_mask', 'brain_mask_epiSpace', 'struct_2_MNI_warp', 'rs_preprocessed' ]), name='inputnode') outputnode = Node(util.IdentityInterface(fields=['']), name='outputnode') ds = Node(nio.DataSink(base_directory=ds_dir), name='ds') ds.inputs.substitutions = [('_TR_id_', 'TR_')] # CALCULATED POWER FD FD_power = Node(util.Function(input_names=['in_file'], output_names=['out_file'], function=calculate_FD_P), name='FD_power') qc_wf.connect(inputnode, 'par_moco', FD_power, 'in_file') qc_wf.connect(FD_power, 'out_file', ds, 'QC.FD.FD_ts') mean_FD_power = Node(util.Function( input_names=['in_file'], output_names=['mean_FD_power', 'out_file'], function=calculate_mean_FD_fct), name='mean_FD_power') qc_wf.connect(FD_power, 'out_file', mean_FD_power, 'in_file') qc_wf.connect(mean_FD_power, 'out_file', ds, 'QC.FD.FD_mean') # EXTRACT NUMBER OF SPIKES (OUTLIERS) def get_n_spikes_fct(outliers_file): import numpy as np try: spikes = np.atleast_1d(np.genfromtxt(outliers_file)) except IOError: spikes = np.empty((0)) n_spikes = len(spikes) return n_spikes get_n_spikes = Node(util.Function(input_names=['outliers_file'], output_names=['n_spikes'], function=get_n_spikes_fct), name='get_n_spikes') qc_wf.connect(inputnode, 'outlier_files', get_n_spikes, 'outliers_file') # CALCULATE TSNR tsnr_uglyname = Node(misc.TSNR(), name='tsnr_uglyname') qc_wf.connect(inputnode, 'epi_deskulled', tsnr_uglyname, 'in_file') tsnr = Node(niu.Rename(format_string='tsnr', keep_ext=True), name='tsnr') qc_wf.connect(tsnr_uglyname, 'tsnr_file', tsnr, 'in_file') qc_wf.connect(tsnr, 'out_file', ds, 'QC.tsnr') # GET MEAN TSNR IN BRAIN_MASK def get_values_inside_a_mask(main_file, mask_file, out_file): import nibabel as nb import numpy as np import os out_file = os.path.join(os.getcwd(), out_file + '.npy') main_nii = nb.load(main_file) main_data = main_nii.get_data() nan_mask = np.logical_not(np.isnan(main_data)) mask = nb.load(mask_file).get_data() > 0 data = main_data[np.logical_and(nan_mask, mask)] np.save(out_file, data) median_data = np.median(data) return (out_file, median_data) get_tsnr = Node(util.Function( input_names=['main_file', 'mask_file', 'out_file'], output_names=['out_file', 'median_data'], function=get_values_inside_a_mask), name='get_tsnr') get_tsnr.inputs.out_file = 'tsnr' qc_wf.connect(tsnr, 'out_file', get_tsnr, 'main_file') qc_wf.connect(inputnode, 'brain_mask_epiSpace', get_tsnr, 'mask_file') qc_wf.connect(get_tsnr, 'out_file', ds, 'QC.tsnr.@out_file') ## CREATE SLICES OVERLAY slices_epi_structSpace = Node(util.Function( input_names=['in_file', 'in_file2'], output_names=['out_file'], function=fsl_slices_fct), name='slices_epi_structSpace') qc_wf.connect(inputnode, 'mean_epi_structSpace', slices_epi_structSpace, 'in_file') qc_wf.connect(inputnode, 't1w_brain', slices_epi_structSpace, 'in_file2') qc_wf.connect(slices_epi_structSpace, 'out_file', ds, 'QC.slices.epi_structSpace') slices_epi_MNIspace = Node(util.Function( input_names=['in_file', 'in_file2'], output_names=['out_file'], function=fsl_slices_fct), name='slices_epi_MNIspace') slices_epi_MNIspace.inputs.in_file2 = fsl.Info.standard_image( 'MNI152_T1_2mm_brain.nii.gz') qc_wf.connect(inputnode, 'mean_epi_MNIspace', slices_epi_MNIspace, 'in_file') qc_wf.connect(slices_epi_MNIspace, 'out_file', ds, 'QC.slices.epi_MNIspace') slices_struct_MNIspace = Node(util.Function( input_names=['in_file', 'in_file2'], output_names=['out_file'], function=fsl_slices_fct), name='slices_struct_MNIspace') qc_wf.connect(inputnode, 'struct_MNIspace', slices_struct_MNIspace, 'in_file') slices_struct_MNIspace.inputs.in_file2 = fsl.Info.standard_image( 'MNI152_T1_2mm_brain.nii.gz') qc_wf.connect(slices_struct_MNIspace, 'out_file', ds, 'QC.slices.struct_MNIspace') # CREATE BRAIN MASK IN MNI SPACE struct_brain_mask_MNIspace = Node(fsl.ApplyWarp(), name='struct_brain_mask_MNIspace', interp='nn') struct_brain_mask_MNIspace.inputs.ref_file = fsl.Info.standard_image( 'MNI152_T1_2mm_brain.nii.gz') struct_brain_mask_MNIspace.inputs.out_file = 'struct_brain_mask_MNIspace.nii.gz' qc_wf.connect(inputnode, 'struct_brain_mask', struct_brain_mask_MNIspace, 'in_file') qc_wf.connect(inputnode, 'struct_2_MNI_warp', struct_brain_mask_MNIspace, 'field_file') # CREATE STRUCT_BRAIN IN MNI SPACE struct_brain_MNIspace = Node(fsl.ApplyWarp(), name='struct_brain_MNIspace') struct_brain_MNIspace.inputs.ref_file = fsl.Info.standard_image( 'MNI152_T1_2mm_brain.nii.gz') struct_brain_MNIspace.inputs.out_file = 'struct_MNIspace.nii.gz' qc_wf.connect(inputnode, 't1w_brain', struct_brain_MNIspace, 'in_file') qc_wf.connect(inputnode, 'struct_2_MNI_warp', struct_brain_MNIspace, 'field_file') def similarity_to_file_fct(similarity): import os import numpy as np out_file = os.path.join(os.getcwd(), 'similarity.txt') np.savetxt(out_file, np.array(similarity)) return out_file # CALCULATE SIMILARITY FOR QC similarity_epi_struct = Node(interface=Similarity(metric='nmi'), name='similarity_epi_struct') qc_wf.connect(inputnode, 'mean_epi_structSpace', similarity_epi_struct, 'volume1') qc_wf.connect(inputnode, 't1w_brain', similarity_epi_struct, 'volume2') qc_wf.connect(inputnode, 'struct_brain_mask', similarity_epi_struct, 'mask1') qc_wf.connect(inputnode, 'struct_brain_mask', similarity_epi_struct, 'mask2') similarity_epi_struct_txt = Node(util.Function( input_names=['similarity'], output_names=['out_file'], function=similarity_to_file_fct), name='similarity_epi_struct_txt') qc_wf.connect(similarity_epi_struct, 'similarity', similarity_epi_struct_txt, 'similarity') qc_wf.connect(similarity_epi_struct_txt, 'out_file', ds, 'QC.similarity.epi_struct') similarity_struct_MNI = Node(interface=Similarity(metric='nmi'), name='similarity_struct_MNI') qc_wf.connect(struct_brain_MNIspace, 'out_file', similarity_struct_MNI, 'volume1') similarity_struct_MNI.inputs.volume2 = fsl.Info.standard_image( 'MNI152_T1_2mm_brain.nii.gz') qc_wf.connect(struct_brain_mask_MNIspace, 'out_file', similarity_struct_MNI, 'mask1') qc_wf.connect(struct_brain_mask_MNIspace, 'out_file', similarity_struct_MNI, 'mask2') similarity_struct_MNI_txt = Node(util.Function( input_names=['similarity'], output_names=['out_file'], function=similarity_to_file_fct), name='similarity_struct_MNI_txt') qc_wf.connect(similarity_struct_MNI, 'similarity', similarity_struct_MNI_txt, 'similarity') qc_wf.connect(similarity_struct_MNI_txt, 'out_file', ds, 'QC.similarity.struct_MNI') def list_to_num(lst): return lst[0] def create_qc_df_fct(subject_id, similarity_epi_struct, similarity_struct_MNI, mean_FD_Power, n_spikes, median_tsnr): import pandas as pd import os out_file_df = os.path.join(os.getcwd(), 'qc_values.pkl') out_file_txt = os.path.join(os.getcwd(), 'qc_values.txt') data = [ subject_id, similarity_epi_struct, similarity_struct_MNI, mean_FD_Power, n_spikes, median_tsnr ] header = [ 'subject_id', 'similarity_epi_struct', 'similarity_struct_MNI', 'mean_FD_Power', 'n_spikes', 'median_tsnr' ] df = pd.DataFrame([data], columns=header) df = df.set_index(df.subject_id) df.to_pickle(out_file_df) df.to_csv(out_file_txt, sep='\t') return (out_file_df, out_file_txt) create_qc_df = Node(util.Function( input_names=[ 'subject_id', 'similarity_epi_struct', 'similarity_struct_MNI', 'mean_FD_Power', 'n_spikes', 'median_tsnr' ], output_names=['out_file_df', 'out_file_txt'], function=create_qc_df_fct), name='create_qc_df') qc_wf.connect(inputnode, 'subject_id', create_qc_df, 'subject_id') qc_wf.connect(similarity_epi_struct, ('similarity', list_to_num), create_qc_df, 'similarity_epi_struct') qc_wf.connect(similarity_struct_MNI, ('similarity', list_to_num), create_qc_df, 'similarity_struct_MNI') qc_wf.connect(mean_FD_power, 'mean_FD_power', create_qc_df, 'mean_FD_Power') qc_wf.connect(get_n_spikes, 'n_spikes', create_qc_df, 'n_spikes') qc_wf.connect(get_tsnr, 'median_data', create_qc_df, 'median_tsnr') qc_wf.connect(create_qc_df, 'out_file_df', ds, 'QC.df.@df') qc_wf.connect(create_qc_df, 'out_file_txt', ds, 'QC.df.@txt') qc_wf.write_graph(dotfilename=qc_wf.name, graph2use='flat', format='pdf') return qc_wf
r12.append(get_rigid_matrix(rigid_params, args.package)[:-1].ravel()) r12 = numpy.asarray(r12) numpy.savetxt(r12_file, r12) fd_jenkinson(r12_file, rmax=80., out_file=fd_file) # > computes the time-course SNR for a time series, # typically you want to run this on a realigned time-series. funcrealign_file = os.path.join(subjectdir, "nonan_" + os.path.basename(args.funcrealign)) im = nibabel.load(args.funcrealign) data_array = im.get_data() data_array[numpy.isnan(data_array)] = 0 nibabel.save(im, funcrealign_file) cwd = os.getcwd() os.chdir(subjectdir) tsnr = nam.TSNR() tsnr.inputs.in_file = funcrealign_file tsnr.run() tsnr = tsnr.aggregate_outputs() os.chdir(cwd) for out_name in ["tsnr_file", "mean_file", "stddev_file"]: path = getattr(tsnr, out_name) tmp_path = os.path.join(subjectdir, "tmp_" + os.path.basename(path)) shutil.copyfile(path, tmp_path) im = nibabel.load(tmp_path) data_array = im.get_data() data_array[numpy.isnan(data_array)] = 0 nibabel.save(im, path) os.remove(tmp_path) # > compute functional temporal scores from QAP library
def getbtthresh(medianvals): return [0.75 * val for val in medianvals] preprocessing.connect(medianval, ('out_stat', getbtthresh), smooth, 'brightness_threshold') else: smooth=pe.Node(interface=fsl.utils.Smooth(), name="smooth") smooth.inputs.fwhm=6 # Turn off smoothing for now - we will do it later on the constrast images #preprocessing.connect(rescale,'out_file',smooth,'in_file') #preprocessing.connect(smooth,'smoothed_file',datasink,'smooth') fqc=pe.Node(interface=FunctionalQC(),name='fqc') tsnr = pe.Node(nam.TSNR(), name='compute_tsnr') preprocessing.connect(mcflirt, 'out_file',tsnr,'in_file') preprocessing.connect(mcflirt,'mean_img',fqc,'in_epi') preprocessing.connect(mcflirt, 'out_file',fqc,'in_hmc') preprocessing.connect(bet_func, 'mask_file',fqc,'in_mask') preprocessing.connect(tsnr, 'tsnr_file',fqc,'in_tsnr') preprocessing.connect(mcflirt, 'par_file',fqc,'fd_movpar') preprocessing.connect(fqc,'dvars',datasink,'mriqc.dvars') preprocessing.connect(fqc,'summary',datasink,'mriqc.summary') preprocessing.connect(fqc,'fd_stats',datasink,'mriqc.fd') preprocessing.connect(tsnr,'tsnr_file',datasink,'mriqc.tsnr')
def qap_functional_temporal_workflow(workflow, resource_pool, config): # resource pool should have: # functional_brain_mask # func_motion_correct # coordinate_transformation import os import sys import nipype.interfaces.io as nio import nipype.pipeline.engine as pe import nipype.interfaces.utility as niu import nipype.algorithms.misc as nam from qap_workflows_utils import qap_functional_temporal from temporal_qc import fd_jenkinson from qap.viz.interfaces import PlotMosaic, PlotFD def _getfirst(inlist): if isinstance(inlist, list): return inlist[0] return inlist # if 'mean_functional' not in resource_pool.keys(): # from functional_preproc import mean_functional_workflow # workflow, resource_pool = \ # mean_functional_workflow(workflow, resource_pool, config) if 'functional_brain_mask' not in resource_pool.keys(): from functional_preproc import functional_brain_mask_workflow workflow, resource_pool = \ functional_brain_mask_workflow(workflow, resource_pool, config) if ('func_motion_correct' not in resource_pool.keys()) or \ ('coordinate_transformation' not in resource_pool.keys() and 'mcflirt_rel_rms' not in resource_pool.keys()): from functional_preproc import func_motion_correct_workflow workflow, resource_pool = \ func_motion_correct_workflow(workflow, resource_pool, config) fd = pe.Node(niu.Function(input_names=['in_file'], output_names=['out_file'], function=fd_jenkinson), name='generate_FD_file') if 'mcflirt_rel_rms' in resource_pool.keys(): fd.inputs.in_file = resource_pool['mcflirt_rel_rms'] else: if len(resource_pool['coordinate_transformation']) == 2: node, out_file = resource_pool['coordinate_transformation'] workflow.connect(node, out_file, fd, 'in_file') else: fd.inputs.in_file = resource_pool['coordinate_transformation'] temporal = pe.Node(niu.Function(input_names=[ 'func_motion_correct', 'func_brain_mask', 'tsnr_volume', 'fd_file', 'subject_id', 'session_id', 'scan_id', 'site_name' ], output_names=['qc'], function=qap_functional_temporal), name='qap_functional_temporal') temporal.inputs.subject_id = config['subject_id'] temporal.inputs.session_id = config['session_id'] temporal.inputs.scan_id = config['scan_id'] workflow.connect(fd, 'out_file', temporal, 'fd_file') if 'site_name' in config.keys(): temporal.inputs.site_name = config['site_name'] tsnr = pe.Node(nam.TSNR(), name='compute_tsnr') if len(resource_pool['func_motion_correct']) == 2: node, out_file = resource_pool['func_motion_correct'] workflow.connect(node, out_file, tsnr, 'in_file') workflow.connect(node, out_file, temporal, 'func_motion_correct') else: from workflow_utils import check_input_resources check_input_resources(resource_pool, 'func_motion_correct') input_file = resource_pool['func_motion_correct'] tsnr.inputs.in_file = input_file temporal.inputs.func_motion_correct = input_file if len(resource_pool['functional_brain_mask']) == 2: node, out_file = resource_pool['functional_brain_mask'] workflow.connect(node, out_file, temporal, 'func_brain_mask') else: temporal.inputs.func_brain_mask = \ resource_pool['functional_brain_mask'] # Write mosaic and FD plot if config.get('write_report', False): plot = pe.Node(PlotMosaic(), name='plot_mosaic') plot.inputs.subject = config['subject_id'] metadata = [config['session_id'], config['scan_id']] if 'site_name' in config.keys(): metadata.append(config['site_name']) plot.inputs.metadata = metadata plot.inputs.title = 'tSNR volume' workflow.connect(tsnr, 'tsnr_file', plot, 'in_file') # Enable this if we want masks # if len(resource_pool['functional_brain_mask']) == 2: # node, out_file = resource_pool['functional_brain_mask'] # workflow.connect(node, out_file, plot, 'in_mask') # else: # plot.inputs.in_mask = resource_pool['functional_brain_mask'] resource_pool['qap_mosaic'] = (plot, 'out_file') fdplot = pe.Node(PlotFD(), name='plot_fd') fdplot.inputs.subject = config['subject_id'] fdplot.inputs.metadata = metadata workflow.connect(fd, 'out_file', fdplot, 'in_file') resource_pool['qap_fd'] = (fdplot, 'out_file') out_csv = op.join(config['output_directory'], 'qap_functional_temporal.csv') temporal_to_csv = pe.Node(nam.AddCSVRow(in_file=out_csv), name='qap_functional_temporal_to_csv') workflow.connect(tsnr, 'tsnr_file', temporal, 'tsnr_volume') workflow.connect(temporal, 'qc', temporal_to_csv, '_outputs') resource_pool['qap_functional_temporal'] = (temporal_to_csv, 'csv_file') return workflow, resource_pool
fslmaths = MeanImage() fslmaths.inputs.in_file = 'corr_rest_roi.nii.gz' fslmaths.inputs.out_file = 'mean_corr_rest_roi.nii.gz' fslmaths.inputs.dimension = 'T' fslmaths.run() # Step#4 get binary mask & skull stripped imag img_StMoco = os.path.abspath('corr_rest_roi.nii.gz') btr = fsl.BET() btr.inputs.in_file = img_StMoco btr.inputs.mask = True btr.run() # Step#5 tsnr calculation on realigned image tsnr = misc.TSNR() tsnr.inputs.in_file = 'corr_rest_roi.nii.gz' tsnr.inputs.mean_file = 'corr_rest_roi_tsnr_mean.nii.gz' tsnr.inputs.tsnr_file = 'corr_rest_roi_tsnr.nii.gz' tsnr.inputs.stddev_file = 'corr_rest_roi_tsnr_stddev.nii.gz' tsnr.run() # (optional) registration from restYY -->> restXX if len(sys.argv) > 3: # e.g. dayXX = 'd01' dayXX = sys.argv[3] # create work_dir for transform matrices dir_trf = 'transforms2rest' + dayXX[1:] work_dir_trf = os.path.join(data_dir, subject_id, 'preprocessed/func', dir_trf)
def create_moco_pipeline(name='motion_correction'): # initiate workflow moco = Workflow(name='motion_correction') # set fsl output fsl.FSLCommand.set_default_output_type('NIFTI_GZ') # inputnode inputnode = Node(util.IdentityInterface(fields=['epi']), name='inputnode') # outputnode outputnode = Node(util.IdentityInterface(fields=[ 'epi_moco', 'par_moco', 'mat_moco', 'rms_moco', 'epi_mean', 'rotplot', 'transplot', 'dispplots', 'tsnr_file' ]), name='outputnode') # mcflirt motion correction to 1st volume mcflirt = Node(fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True, ref_vol=1, out_file='rest_realigned.nii.gz'), name='mcflirt') # plot motion parameters rotplotter = Node(fsl.PlotMotionParams(in_source='fsl', plot_type='rotations', out_file='rotation_plot.png'), name='rotplotter') transplotter = Node(fsl.PlotMotionParams(in_source='fsl', plot_type='translations', out_file='translation_plot.png'), name='transplotter') dispplotter = MapNode(interface=fsl.PlotMotionParams( in_source='fsl', plot_type='displacement', ), name='dispplotter', iterfield=['in_file']) dispplotter.iterables = ('plot_type', ['displacement']) # calculate tmean tmean = Node(fsl.maths.MeanImage(dimension='T', out_file='rest_realigned_mean.nii.gz'), name='tmean') # calculate tsnr tsnr = Node(misc.TSNR(), name='tsnr') # create connections moco.connect([(inputnode, mcflirt, [('epi', 'in_file')]), (mcflirt, tmean, [('out_file', 'in_file')]), (mcflirt, rotplotter, [('par_file', 'in_file')]), (mcflirt, transplotter, [('par_file', 'in_file')]), (mcflirt, dispplotter, [('rms_files', 'in_file')]), (tmean, outputnode, [('out_file', 'epi_mean')]), (mcflirt, outputnode, [('out_file', 'epi_moco'), ('par_file', 'par_moco'), ('mat_file', 'mat_moco'), ('rms_files', 'rms_moco')]), (rotplotter, outputnode, [('out_file', 'rotplot')]), (transplotter, outputnode, [('out_file', 'transplot')]), (dispplotter, outputnode, [('out_file', 'dispplots')]), (mcflirt, tsnr, [('out_file', 'in_file')]), (tsnr, outputnode, [('tsnr_file', 'tsnr_file')])]) return moco
def min_func_preproc(subject, sessions, data_dir, fs_dir, wd, sink, TR, EPI_resolution): #initiate min func preproc workflow wf = pe.Workflow(name='MPP') wf.base_dir = wd wf.config['execution']['crashdump_dir'] = wf.base_dir + "/crash_files" ## set fsl output type to nii.gz fsl.FSLCommand.set_default_output_type('NIFTI_GZ') # I/O nodes inputnode = pe.Node(util.IdentityInterface(fields=['subjid', 'fs_dir']), name='inputnode') inputnode.inputs.subjid = subject inputnode.inputs.fs_dir = fs_dir ds = pe.Node(nio.DataSink(base_directory=sink, parameterization=False), name='sink') ds.inputs.substitutions = [('moco.nii.gz.par', 'moco.par'), ('moco.nii.gz_', 'moco_')] #infosource to interate over sessions: COND, EXT1, EXT2 sessions_infosource = pe.Node(util.IdentityInterface(fields=['session']), name='session') sessions_infosource.iterables = [('session', sessions)] #select files templates = { 'func_data': '{session}/func_data.nii.gz', 'T1_brain': 'T1/T1_brain.nii.gz', 'wmedge': 'T1/MASKS/aparc_aseg.WMedge.nii.gz' } selectfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir), name='selectfiles') wf.connect(sessions_infosource, 'session', selectfiles, 'session') wf.connect(sessions_infosource, 'session', ds, 'container') ########################################################################## ######################## START ###################################### ########################################################################## ########################################################################### ######################## No. 3 ###################################### #change the data type to float fsl_float = pe.Node(fsl.maths.MathsCommand(output_datatype='float'), name='fsl_float') wf.connect(selectfiles, 'func_data', fsl_float, 'in_file') ########################################################################### ######################## No. 4 ###################################### #get FD from fsl_motion_outliers FD = pe.Node(fsl.MotionOutliers(out_file='func_data_FD_outliers.txt', out_metric_values='func_data_FD.txt', metric='fd'), name='FD') wf.connect(fsl_float, 'out_file', FD, 'in_file') wf.connect(FD, 'out_metric_values', ds, 'QC.@FD') wf.connect(FD, 'out_file', ds, 'QC.@FDoutliers') ########################################################################### ######################## No. 5 ###################################### #slice timing correction: sequential ascending slicetimer = pe.Node( fsl.SliceTimer( index_dir=False, interleaved=False, #slice_direction=3, #z direction time_repetition=TR, out_file='func_data_stc.nii.gz'), name='slicetimer') wf.connect(fsl_float, 'out_file', slicetimer, 'in_file') wf.connect(slicetimer, 'slice_time_corrected_file', ds, 'TEMP.@slicetimer') ########################################################################### ######################## No. 6 ###################################### #do realignment to the middle or first volume mcflirt = pe.Node(fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True, ref_vol=1, out_file='func_data_stc_moco.nii.gz'), name='mcflirt') wf.connect(slicetimer, 'slice_time_corrected_file', mcflirt, 'in_file') wf.connect(mcflirt, 'out_file', ds, 'TEMP.@mcflirt') wf.connect(mcflirt, 'par_file', ds, 'MOCO.@par_file') wf.connect(mcflirt, 'rms_files', ds, 'MOCO.@rms_files') wf.connect(mcflirt, 'mat_file', ds, 'MOCO_MAT.@mcflirt') # plot motion parameters rotplotter = pe.Node(fsl.PlotMotionParams(in_source='fsl', plot_type='rotations', out_file='rotation.png'), name='rotplotter') transplotter = pe.Node(fsl.PlotMotionParams(in_source='fsl', plot_type='translations', out_file='translation.png'), name='transplotter') dispplotter = pe.Node( interface=fsl.PlotMotionParams(in_source='fsl', plot_type='displacement', out_file='displacement.png'), name='dispplotter') wf.connect(mcflirt, 'par_file', rotplotter, 'in_file') wf.connect(mcflirt, 'par_file', transplotter, 'in_file') wf.connect(mcflirt, 'rms_files', dispplotter, 'in_file') wf.connect(rotplotter, 'out_file', ds, 'PLOTS.@rotplot') wf.connect(transplotter, 'out_file', ds, 'PLOTS.@transplot') wf.connect(dispplotter, 'out_file', ds, 'PLOTS.@disppplot') #calculate tSNR and the mean moco_Tmean = pe.Node(fsl.maths.MathsCommand(args='-Tmean', out_file='moco_Tmean.nii.gz'), name='moco_Tmean') moco_Tstd = pe.Node(fsl.maths.MathsCommand(args='-Tstd', out_file='moco_Tstd.nii.gz'), name='moco_Tstd') tSNR0 = pe.Node(fsl.maths.MultiImageMaths(op_string='-div %s', out_file='moco_tSNR.nii.gz'), name='moco_tSNR') wf.connect(mcflirt, 'out_file', moco_Tmean, 'in_file') wf.connect(mcflirt, 'out_file', moco_Tstd, 'in_file') wf.connect(moco_Tmean, 'out_file', tSNR0, 'in_file') wf.connect(moco_Tstd, 'out_file', tSNR0, 'operand_files') wf.connect(moco_Tmean, 'out_file', ds, 'TEMP.@moco_Tmean') wf.connect(moco_Tstd, 'out_file', ds, 'TEMP.@moco_Tstd') wf.connect(tSNR0, 'out_file', ds, 'TEMP.@moco_Tsnr') ########################################################################### ######################## No. 7 ###################################### #bias field correction of mean epi for better coregistration bias = pe.Node( fsl.FAST( img_type=2, #restored_image='epi_Tmeanrestored.nii.gz', output_biascorrected=True, out_basename='moco_Tmean', no_pve=True, probability_maps=False), name='bias') wf.connect(moco_Tmean, 'out_file', bias, 'in_files') wf.connect(bias, 'restored_image', ds, 'TEMP.@restored_image') #co-registration to anat using FS BBregister and mean EPI bbregister = pe.Node(fs.BBRegister( subject_id=subject, subjects_dir=fs_dir, contrast_type='t2', init='fsl', out_fsl_file='func2anat.mat', out_reg_file='func2anat.dat', registered_file='moco_Tmean_restored2anat.nii.gz', epi_mask=True), name='bbregister') wf.connect(bias, 'restored_image', bbregister, 'source_file') wf.connect(bbregister, 'registered_file', ds, 'TEMP.@registered_file') wf.connect(bbregister, 'out_fsl_file', ds, 'COREG.@out_fsl_file') wf.connect(bbregister, 'out_reg_file', ds, 'COREG.@out_reg_file') wf.connect(bbregister, 'min_cost_file', ds, 'COREG.@min_cost_file') #inverse func2anat mat inverseXFM = pe.Node(fsl.ConvertXFM(invert_xfm=True, out_file='anat2func.mat'), name='inverseXFM') wf.connect(bbregister, 'out_fsl_file', inverseXFM, 'in_file') wf.connect(inverseXFM, 'out_file', ds, 'COREG.@out_fsl_file_inv') #plot the corregistration quality slicer = pe.Node(fsl.Slicer(middle_slices=True, out_file='func2anat.png'), name='slicer') wf.connect(selectfiles, 'wmedge', slicer, 'image_edges') wf.connect(bbregister, 'registered_file', slicer, 'in_file') wf.connect(slicer, 'out_file', ds, 'PLOTS.@func2anat') ########################################################################### ######################## No. 8 ###################################### #MOCO and COREGISTRATION #resample T1 to EPI resolution to use it as a reference image resample_T1 = pe.Node( fsl.FLIRT(datatype='float', apply_isoxfm=EPI_resolution, out_file='T1_brain_EPI.nii.gz'), #interp='nearestneighbour'),keep spline so it looks nicer name='resample_T1') wf.connect(selectfiles, 'T1_brain', resample_T1, 'in_file') wf.connect(selectfiles, 'T1_brain', resample_T1, 'reference') wf.connect(resample_T1, 'out_file', ds, 'COREG.@resample_T1') #concate matrices (moco and func2anat) volume-wise concat_xfm = pe.MapNode(fsl.ConvertXFM(concat_xfm=True), iterfield=['in_file'], name='concat_xfm') wf.connect(mcflirt, 'mat_file', concat_xfm, 'in_file') wf.connect(bbregister, 'out_fsl_file', concat_xfm, 'in_file2') wf.connect(concat_xfm, 'out_file', ds, 'MOCO2ANAT_MAT.@concat_out') #split func_data split = pe.Node(fsl.Split(dimension='t'), name='split') wf.connect(slicetimer, 'slice_time_corrected_file', split, 'in_file') #motion correction and corregistration in one interpolation step flirt = pe.MapNode(fsl.FLIRT(apply_xfm=True, interp='spline', datatype='float'), iterfield=['in_file', 'in_matrix_file'], name='flirt') wf.connect(split, 'out_files', flirt, 'in_file') wf.connect(resample_T1, 'out_file', flirt, 'reference') wf.connect(concat_xfm, 'out_file', flirt, 'in_matrix_file') #merge the files to have 4d dataset motion corrected and co-registerd to T1 merge = pe.Node(fsl.Merge(dimension='t', merged_file='func_data_stc_moco2anat.nii.gz'), name='merge') wf.connect(flirt, 'out_file', merge, 'in_files') wf.connect(merge, 'merged_file', ds, 'TEMP.@merged') ########################################################################### ######################## No. 9 ###################################### #run BET on co-registered EPI in 1mm and get the mask bet = pe.Node(fsl.BET(mask=True, functional=True, out_file='moco_Tmean_restored2anat_BET.nii.gz'), name='bet') wf.connect(bbregister, 'registered_file', bet, 'in_file') wf.connect(bet, 'out_file', ds, 'TEMP.@func_data_example') wf.connect(bet, 'mask_file', ds, 'TEMP.@func_data_mask') #resample BET mask to EPI resolution resample_mask = pe.Node(fsl.FLIRT( datatype='int', apply_isoxfm=EPI_resolution, interp='nearestneighbour', out_file='prefiltered_func_data_mask.nii.gz'), name='resample_mask') wf.connect(bet, 'mask_file', resample_mask, 'in_file') wf.connect(resample_T1, 'out_file', resample_mask, 'reference') wf.connect(resample_mask, 'out_file', ds, '@mask') #apply the mask to 4D data to get rid of the "eyes and the rest" mask4D = pe.Node(fsl.maths.ApplyMask(), name='mask') wf.connect(merge, 'merged_file', mask4D, 'in_file') wf.connect(resample_mask, 'out_file', mask4D, 'mask_file') ########################################################################### ######################## No. 10 ###################################### #get the values necessary for intensity normalization median = pe.Node(fsl.utils.ImageStats(op_string='-k %s -p 50'), name='median') wf.connect(resample_mask, 'out_file', median, 'mask_file') wf.connect(mask4D, 'out_file', median, 'in_file') #compute the scaling factor def get_factor(val): factor = 10000 / val return factor get_scaling_factor = pe.Node(util.Function(input_names=['val'], output_names=['out_val'], function=get_factor), name='scaling_factor') #normalize the 4D func data with one scaling factor multiplication = pe.Node(fsl.maths.BinaryMaths( operation='mul', out_file='prefiltered_func_data.nii.gz'), name='multiplication') wf.connect(median, 'out_stat', get_scaling_factor, 'val') wf.connect(get_scaling_factor, 'out_val', multiplication, 'operand_value') wf.connect(mask4D, 'out_file', multiplication, 'in_file') wf.connect(multiplication, 'out_file', ds, '@prefiltered_func_data') ########################################################################### ######################## No. 11 ###################################### #calculate tSNR and the mean of the new prefiltered and detrend dataset tsnr_detrend = pe.Node(misc.TSNR( regress_poly=1, detrended_file='prefiltered_func_data_detrend.nii.gz', mean_file='prefiltered_func_data_detrend_Tmean.nii.gz', tsnr_file='prefiltered_func_data_detrend_tSNR.nii.gz'), name='tsnr_detrend') wf.connect(multiplication, 'out_file', tsnr_detrend, 'in_file') wf.connect(tsnr_detrend, 'tsnr_file', ds, 'QC.@tsnr_detrend') wf.connect(tsnr_detrend, 'mean_file', ds, 'QC.@detrend_mean_file') wf.connect(tsnr_detrend, 'detrended_file', ds, '@detrend_file') #resample the EPI mask to original EPI dimensions convert2func = pe.Node(fsl.FLIRT(apply_xfm=True, interp='nearestneighbour', out_file='func_data_mask2func.nii.gz'), name='conver2func') wf.connect(resample_mask, 'out_file', convert2func, 'in_file') wf.connect(bias, 'restored_image', convert2func, 'reference') wf.connect(inverseXFM, 'out_file', convert2func, 'in_matrix_file') wf.connect(convert2func, 'out_file', ds, 'QC.@inv') ########################################################################### ######################## RUN ###################################### wf.write_graph(dotfilename='wf.dot', graph2use='colored', format='pdf', simple_form=True) wf.run(plugin='MultiProc', plugin_args={'n_procs': 2}) #wf.run() return