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(confounds.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 MCORRECTOR(): #--- 1) Import modules import nipype.pipeline.engine as pe import os os.system('clear') from glob import glob import nipype.interfaces.fsl as fsl import nipype.interfaces.utility as util #--- 2) Record intial working directory INITDIR=os.getcwd(); #--- 3) Prompt user for directory containing DICOM FILES NIFTIFILE=raw_input('Please drag in the nifti\n file you wish to motion correct\n(ensure there is no blank space at the end)\n') os.system('clear') print '---\n' NIFTIFILE=NIFTIFILE.strip('\'"') NIFTIDIR=os.path.split(NIFTIFILE)[0] #--- 3) Move to directory os.chdir(NIFTIDIR) #--- 4) Set up motion correction node motion_correct = pe.Node(interface=fsl.MCFLIRT(save_mats=True,save_plots=True,interpolation='spline'),name='MCORRECTED') motion_correct.inputs.in_file=NIFTIFILE #--- 5) Set up plotting node plot_motion = pe.Node(interface=fsl.PlotMotionParams(in_source='fsl'),name='PLOTTED') plot_motion.iterables = ('plot_type', ['rotations', 'translations']) #--- 5) Utility output node outputnode = pe.Node(interface=util.IdentityInterface(fields=['mcorrected_files']),name='outputnode') #--- 6) Set up workflow workflow = pe.Workflow(name='MCORRECTOR') workflow.base_dir = NIFTIDIR #--- 7) Connect nodes. workflow.connect(motion_correct, 'par_file', plot_motion, 'in_file') workflow.connect(motion_correct,'out_file', outputnode,'mcorrected_files') workflow.write_graph(graph2use='exec') #--- 8) Run workflow result=workflow.run() print "Node completed. Returning to intital directory\n" os.chdir(INITDIR)
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
def FUNCPIPE(): #--- 1) Import modules import os # system functions os.system('clear') import nipype.interfaces.dcm2nii as dcm2nii import nipype.interfaces.io as nio # Data i/o import nipype.interfaces.fsl as fsl # fsl import nipype.interfaces.utility as util # utility import nipype.pipeline.engine as pe # pypeline engine import nipype.interfaces.fsl.utils as fslu import nipype.interfaces.fsl.preprocess as fslp from nipype.interfaces import afni as afni from nipype.interfaces.utility import Function import matplotlib from nilearn import plotting from nilearn import image import os import nipype.interfaces.fsl as fsl # fsl import nipype.interfaces.utility as util # utility import nipype.pipeline.engine as pe # pypeline engine import nipype.interfaces.freesurfer as fs # freesurfer tolist = lambda x: [x] highpass_operand = lambda x:'-bptf %.10f -1'%x #--- 2) Prompt user for directory containing DICOM FILES INITDIR=os.getcwd(); #--- 3) Prompt user for inputs. DICOMDIR=raw_input('Please drag in the directory of\nDICOM files you wish to pre-process\n(ensure there is no blank space at the end)\n') os.system('clear') print ('---\n') DICOMDIR=DICOMDIR.strip('\'"') frac=float(input('Please enter the fractional anisotropy threshold [0 - 1] \n')) os.system('clear') print ('---\n') grad=float(input('Please enter the threshold gradient [-1 - 1] \n')) os.system('clear') print ('---\n') FWHM=float(input('Please enter the FWHM of the smoother (mm) \n')) os.system('clear') print ('---\n') HIGHPASS=float(input('Please enter the High Pass filter cutoff (s)\n')) os.system('clear') print ('---\n') TR=float(input('Please enter the TR (s)\n')) os.system('clear') print ('---\n') #--- 4) Define workflow and input node. workflow = pe.Workflow(name='FUNCPIPE') inputnode = pe.Node(interface=util.IdentityInterface(fields=['fwhm','highpass','TR']),name='inputspec') inputnode.inputs.fwhm=FWHM inputnode.inputs.TR=TR inputnode.inputs.highpass=float(HIGHPASS/(inputnode.inputs.TR*2.5)) #--- 5) Move to directory os.chdir(DICOMDIR) #--- 6) Set up converter node for conversion to nifti converter=pe.Node(interface=dcm2nii.Dcm2nii(),name='CONVERTED') converter.inputs.source_dir=DICOMDIR converter.inputs.gzip_output=bool(1) #--- 7) Set up realigner node to match orientation of MNI 152 realigner=pe.Node(interface=fslu.Reorient2Std(),name='REORIENTED') realigner.inputs.output_type='NIFTI_GZ' workflow.connect(converter,'converted_files',realigner,'in_file') #--- 8) Set up a slice timing node slicetimer=pe.Node(interface=fslp.SliceTimer(),name='SLICETIMED') slicetimer.inputs.interleaved = True workflow.connect(inputnode, 'TR', slicetimer, 'time_repetition') workflow.connect(realigner, 'out_file', slicetimer, 'in_file') #--- 9) Convert to float. img2float = pe.Node(interface=fsl.ImageMaths(out_data_type='float',op_string='',suffix='_dtype'),name='IMG2FLOATED') workflow.connect(slicetimer,'slice_time_corrected_file',img2float,'in_file') #--- 10) Motion correct. motion_correct = pe.Node(interface=fsl.MCFLIRT(save_mats=True,save_plots=True,interpolation='spline'),name='MCORRECTED') workflow.connect(img2float, 'out_file', motion_correct, 'in_file') #--- 11) Despike despiker=pe.Node(interface=afni.Despike(),name='DESPIKED') despiker.inputs.outputtype = 'NIFTI_GZ' workflow.connect(motion_correct,'out_file',despiker,'in_file') #--- 12) Plot motion. plot_motion = pe.Node(interface=fsl.PlotMotionParams(in_source='fsl'),name='MOTIONPLOTTED') plot_motion.iterables = ('plot_type', ['rotations', 'translations']) workflow.connect(motion_correct, 'par_file', plot_motion, 'in_file') #--- 13) Extract extracter=pe.Node(interface=fsl.BET(),name='EXTRACTED') extracter.inputs.frac=float(frac) extracter.inputs.vertical_gradient=float(grad) extracter.inputs.mask=bool(1) extracter.inputs.functional=bool(1) workflow.connect(despiker, 'out_file', extracter, 'in_file') #--- 14) Smooth smoother=pe.MapNode(interface=afni.BlurInMask(),name='SMOOTHED',iterfield=['fwhm']) smoother.inputs.outputtype='NIFTI_GZ' workflow.connect(inputnode, 'fwhm', smoother, 'fwhm') workflow.connect(extracter, 'out_file', smoother, 'in_file') workflow.connect(extracter, 'mask_file', smoother, 'mask') #--- 15) Highpass filter # Filtering node highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'),name='HIGHPASSED',iterfield=['in_file']) workflow.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') workflow.connect(smoother, 'out_file', highpass, 'in_file') #--- 16) Mean functional volume # Need to add back the mean removed by FSL meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean',suffix='_mean'),name='meanfunc',iterfield=['in_file']) workflow.connect(smoother, 'out_file', meanfunc, 'in_file') #--- 17) Add mean back to highpassed data (FINAL OUTPUT) addmean = pe.MapNode(interface=fsl.BinaryMaths(operation='add'),name='PREPROCESSED',iterfield=['in_file','operand_file']) workflow.connect(highpass, 'out_file', addmean, 'in_file') workflow.connect(meanfunc, 'out_file', addmean, 'operand_file') outputnode = pe.Node(interface=util.IdentityInterface(fields=['highpassed_files']),name='outputnode') workflow.connect(addmean, 'out_file', outputnode, 'highpassed_files') # Utility function for plotting extraction def bplot(in_file,in_file2,in_file3): from nilearn import image from nilearn import plotting import matplotlib niftifiledim=len(image.load_img(in_file).shape) firstim=image.index_img(in_file, 0) firstim2=image.index_img(in_file2, 0) display=plotting.plot_anat(firstim2) display.add_contours(firstim,filled=True, alpha=0.5,levels=[0.2], colors='b') display.add_edges(in_file3) matplotlib.pyplot.show() return niftifiledim #--- 18) Show extraction showextract= pe.Node(Function(input_names=['in_file','in_file2','in_file3'],output_names=['niftifiledim'],function=bplot),name='SHOWEXTRACT') workflow.connect(despiker,'out_file', showextract,'in_file2') workflow.connect(extracter,'out_file', showextract,'in_file') workflow.connect(extracter,'mask_file', showextract,'in_file3') # Utility function for plotting extraction def splot(in_file): from nilearn import image from nilearn import plotting import matplotlib niftifiledim=len(image.load_img(in_file).shape) firstim=image.index_img(in_file, 0) display=plotting.plot_anat(firstim,display_mode='z',cut_coords=10) matplotlib.pyplot.show() return niftifiledim #--- 19) Show smoothing showsmooth= pe.MapNode(Function(input_names=['in_file'],output_names=['niftifiledim'],function=splot),iterfield=['in_file'],name='SHOWSMOOTH') workflow.connect(smoother,'out_file', showsmooth,'in_file') #--- 20) Mean functional volume (for plotting stats) meanfunc2 = pe.Node(interface=fsl.ImageMaths(op_string='-Tmean',suffix='_mean'),name='MEANFUNCTIONAL') workflow.connect(extracter, 'out_file', meanfunc2, 'in_file') workflow.connect(meanfunc2, 'out_file', outputnode, 'mean_functional_volume') #--- 21) Plot workflow workflow.base_dir = DICOMDIR workflow.write_graph(graph2use='exec') #--- 22) Plot workflow result=workflow.run() #--- 23) Show plots #--- 24) Return to initial working directory print ("Workflow completed. Returning to intital directory\n") os.chdir(INITDIR)
realign_flow = Workflow(name='realign_flow') realign_inputnode = Node(IdentityInterface(fields=['func']), name='inputspec') realign_outputnode = Node(IdentityInterface(fields=[ 'realigned_file', 'transformation_matrices', 'motion_parameters', 'displacement_parameters', 'motion_plots' ]), name='outputspec') realigner = Node(fsl.MCFLIRT(save_mats=True, stats_imgs=True, save_plots=True), name='realigner') splitter = Node(fsl.Split(dimension='t'), name='splitter') warper = MapNode(fsl.ApplyWarp(interp='spline'), iterfield=['in_file', 'premat'], name='warper') joiner = Node(fsl.Merge(dimension='t'), name='joiner') plot_motion = Node(fsl.PlotMotionParams(in_source='fsl'), name='plot_motion') plot_motion.iterables = ('plot_type', ['rotations', 'translations']) realign_flow.connect(realign_inputnode, 'func', realigner, 'in_file') realign_flow.connect(realign_inputnode, ('func', select_volume, 'middle'), realigner, 'ref_vol') realign_flow.connect(realigner, 'out_file', splitter, 'in_file') realign_flow.connect(realigner, 'mat_file', warper, 'premat') realign_flow.connect(realigner, 'variance_img', warper, 'ref_file') realign_flow.connect(realigner, 'par_file', plot_motion, 'in_file') realign_flow.connect(splitter, 'out_files', warper, 'in_file') realign_flow.connect(warper, 'out_file', joiner, 'in_files') realign_flow.connect(joiner, 'merged_file', realign_outputnode, 'realigned_file') realign_flow.connect(realigner, 'mat_file', realign_outputnode, 'transformation_matrices')
def func_preproc_fsl(wf_name='func_preproc'): featpreproc = pe.Workflow(name=wf_name) """ Set up a node to define all inputs required for the preprocessing workflow """ inputnode = pe.Node(interface=util.IdentityInterface( fields=['in_file', 'anat_brain'], mandatory_inputs=True), name='inputspec') # preprocessed_func: upscaled, brain-extracted and motion corrected functional data outputnode = pe.Node(interface=util.IdentityInterface( fields=[ 'preprocessed_func', 'example_func', 'func_brain_mask', 'motion_plots', 'func2anat_mat' ], mandatory_inputs=True), name='outputspec') """ Reorient data to match Paxinos """ reorient = pe.MapNode(interface=fsl.utils.SwapDimensions(new_dims=("RL", "AP", "IS")), name='reorient', iterfield=['in_file']) featpreproc.connect(inputnode, 'in_file', reorient, 'in_file') """ Upscale data to human range """ upscaler = mine.upscale() featpreproc.connect(reorient, 'out_file', upscaler, 'inputspec.in_file') """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(upscaler, 'outputspec.out_file', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file', 't_min'], name='extractref') featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickmiddle), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'example_func') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats=True, save_plots=True), name='motioncorr', iterfield=['in_file', 'ref_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['displacement', 'rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """ Extract the mean volume of the first functional run """ meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file') """ estimate func2anat matrix """ f2a = pe.MapNode(util.Function(input_names=['in_file', 'like'], output_names=['out_mat'], function=myfsl.utils.run_resample), name='func2anat', iterfield=['in_file', 'like']) featpreproc.connect(meanfunc, 'out_file', f2a, 'in_file') featpreproc.connect(inputnode, 'anat_brain', f2a, 'like') featpreproc.connect(f2a, 'out_mat', outputnode, 'func2anat_mat') """ Invert func2anat matrix """ invertmat = pe.MapNode(interface=fsl.ConvertXFM(invert_xfm=True), iterfield=['in_file'], name='invertmat') featpreproc.connect(f2a, 'out_mat', invertmat, 'in_file') """ Resample the anatomical brain mask to the space of functional image """ resamplemask = pe.MapNode( interface=fsl.ApplyXfm(interp='nearestneighbour'), iterfield=['reference', 'in_file', 'in_matrix_file'], name='resamplemask') featpreproc.connect(inputnode, 'anat_brain', resamplemask, 'in_file') featpreproc.connect(meanfunc, 'out_file', resamplemask, 'reference') featpreproc.connect(invertmat, 'out_file', resamplemask, 'in_matrix_file') """ Mask the functional runs with the resampled mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(resamplemask, 'out_file', maskfunc, 'in_file2') """ 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') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') """ Threshold the first run of the 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') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') featpreproc.connect(threshold, 'out_file', outputnode, 'func_brain_mask') #""" #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') #featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') #featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') #""" #Dilate the mask #""" #dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', # op_string='-dilF'), # iterfield=['in_file'], # name='dilatemask') #featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') """ Mask the motion corrected functional runs with the dilated mask """ maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') featpreproc.connect(threshold, 'out_file', maskfunc2, 'in_file2') #featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2') featpreproc.connect(maskfunc2, 'out_file', outputnode, 'preprocessed_func') return featpreproc
def mc_workflow_fsl(reference_vol="mid", FD_mode="Power", SinkTag="func_preproc", wf_name="motion_correction_fsl"): """ Modified version of CPAC.func_preproc.func_preproc and CPAC.generate_motion_statistics.generate_motion_statistics: `source: https://fcp-indi.github.io/docs/developer/_modules/CPAC/func_preproc/func_preproc.html` `source: https://fcp-indi.github.io/docs/developer/_modules/CPAC/generate_motion_statistics/generate_motion_statistics.html` Use FSL MCFLIRT to do the motion correction of the 4D functional data and use the 6df rigid body motion parameters to calculate friston24 parameters for later nuissance regression step. Workflow inputs: :param func: The reoriented functional file. :param reference_vol: Either "first", "mid", "last", "mean", or the index of the volume which the rigid body registration (motion correction) will use as reference. default: "mid" :param FD_mode Eiher "Power" or "Jenkinson" :param SinkDir: :param SinkTag: The output directory in which the returned images (see workflow outputs) could be found in a subdirectory directory specific for this workflow.. Workflow outputs: :return: mc_workflow - workflow Balint Kincses [email protected] 2018 """ # TODO_ready nipype has the ability to calculate FD: the function from CPAC calculates it correctly # import relevant packages import sys import os import nipype import nipype.pipeline as pe import nipype.interfaces.utility as utility import nipype.interfaces.fsl as fsl import nipype.algorithms.confounds as conf import PUMI.func_preproc.info.info_get as info_get import nipype.interfaces.io as io import PUMI.utils.utils_math as utils_math import PUMI.utils.utils_convert as utils_convert import PUMI.utils.globals as globals import PUMI.utils.QC as qc SinkDir = os.path.abspath(globals._SinkDir_ + "/" + SinkTag) if not os.path.exists(SinkDir): os.makedirs(SinkDir) QCDir = os.path.abspath(globals._SinkDir_ + "/" + globals._QCDir_) if not os.path.exists(QCDir): os.makedirs(QCDir) # Basic interface class generates identity mappings inputspec = pe.Node(utility.IdentityInterface( fields=['func', 'ref_vol', 'save_plots', 'stats_imgs']), name='inputspec') inputspec.inputs.save_plots = True inputspec.inputs.stats_imgs = True inputspec.inputs.ref_vol = reference_vol # todo_ready: make parametrizable: the reference_vol variable is an argumentum of the mc_workflow # extract reference volume refvol = pe.MapNode(utility.Function(input_names=['refvol', 'func'], output_names=['refvol'], function=getRefVol), iterfield=['func'], name='getRefVol') # Wraps command **mcflirt** mcflirt = pe.MapNode( interface=fsl.MCFLIRT( interpolation="spline", stats_imgs=False), # stages=4), #stages 4: more accurate but slow iterfield=['in_file', 'ref_file'], # , 'ref_vol'], # make parametrizable name='mcflirt') if (reference_vol == "mean"): mcflirt = pe.MapNode( interface=fsl.MCFLIRT(interpolation="spline", stats_imgs=False), # stages=4), #stages 4: more accurate but slow iterfield=['in_file'], # , 'ref_vol'], # make parametrizable name='mcflirt') mcflirt.inputs.mean_vol = True else: mcflirt = pe.MapNode( interface=fsl.MCFLIRT(interpolation="spline", stats_imgs=False), # stages=4), #stages 4: more accurate but slow iterfield=['in_file', 'ref_file'], # , 'ref_vol'], # make parametrizable name='mcflirt') mcflirt.inputs.dof = 6 mcflirt.inputs.save_mats = True mcflirt.inputs.save_plots = True mcflirt.inputs.save_rms = True mcflirt.inputs.stats_imgs = False myqc = qc.timecourse2png("timeseries", tag="010_motioncorr") # Calculate Friston24 parameters calc_friston = pe.MapNode(utility.Function( input_names=['in_file'], output_names=['out_file'], function=calc_friston_twenty_four), iterfield=['in_file'], name='calc_friston') # Calculate FD based on Power's method if FD_mode == "Power": calculate_FD = pe.MapNode(conf.FramewiseDisplacement( parameter_source='FSL', save_plot=True), iterfield=['in_file'], name='calculate_FD_Power') elif FD_mode == "Jenkinson": calculate_FD = pe.MapNode(utility.Function(input_names=['in_file'], output_names=['out_file'], function=calculate_FD_J), iterfield=['in_file'], name='calculate_FD_Jenkinson') # compute mean and max FD meanFD = pe.MapNode(interface=utils_math.Txt2meanTxt, iterfield=['in_file'], name='meanFD') meanFD.inputs.axis = 0 # global mean meanFD.inputs.header = True # global mean maxFD = pe.MapNode(interface=utils_math.Txt2maxTxt, iterfield=['in_file'], name='maxFD') maxFD.inputs.axis = 0 # global mean maxFD.inputs.header = True pop_FD = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FD') pop_FDmax = pe.Node(interface=utils_convert.List2TxtFileOpen, name='pop_FDmax') # save data out with Datasink ds_fd = pe.Node(interface=io.DataSink(), name='ds_pop_fd') ds_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD.txt")] ds_fd.inputs.base_directory = SinkDir # save data out with Datasink ds_fd_max = pe.Node(interface=io.DataSink(), name='ds_pop_fd_max') ds_fd_max.inputs.regexp_substitutions = [("(\/)[^\/]*$", "FD_max.txt")] ds_fd_max.inputs.base_directory = SinkDir plot_motion_rot = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion_rot', iterfield=['in_file']) plot_motion_rot.inputs.plot_type = 'rotations' plot_motion_tra = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion_trans', iterfield=['in_file']) plot_motion_tra.inputs.plot_type = 'translations' # Basic interface class generates identity mappings outputspec = pe.Node(utility.IdentityInterface(fields=[ 'func_out_file', 'first24_file', 'mat_file', 'mc_par_file', 'FD_file' ]), name='outputspec') # save data out with Datasink ds_nii = pe.Node(interface=io.DataSink(), name='ds_nii') ds_nii.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".nii.gz")] ds_nii.inputs.base_directory = SinkDir # save data out with Datasink ds_text = pe.Node(interface=io.DataSink(), name='ds_txt') ds_text.inputs.regexp_substitutions = [("(\/)[^\/]*$", ".txt")] ds_text.inputs.base_directory = SinkDir # Save outputs which are important ds_qc_fd = pe.Node(interface=io.DataSink(), name='ds_qc_fd') ds_qc_fd.inputs.base_directory = QCDir ds_qc_fd.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_FD.pdf")] # Save outputs which are important ds_qc_rot = pe.Node(interface=io.DataSink(), name='ds_qc_rot') ds_qc_rot.inputs.base_directory = QCDir ds_qc_rot.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_rot.png")] # Save outputs which are important ds_qc_tra = pe.Node(interface=io.DataSink(), name='ds_qc_tra') ds_qc_tra.inputs.base_directory = QCDir ds_qc_tra.inputs.regexp_substitutions = [("(\/)[^\/]*$", "_trans.png")] #TODO_ready set the proper images which has to be saved in a the datasink specified directory # Create a workflow to connect all those nodes analysisflow = nipype.Workflow(wf_name) analysisflow.connect(inputspec, 'func', mcflirt, 'in_file') analysisflow.connect(inputspec, 'func', refvol, 'func') analysisflow.connect(inputspec, 'ref_vol', refvol, 'refvol') if (reference_vol != "mean"): analysisflow.connect(refvol, 'refvol', mcflirt, 'ref_file') analysisflow.connect(mcflirt, 'par_file', calc_friston, 'in_file') analysisflow.connect(mcflirt, 'par_file', calculate_FD, 'in_file') analysisflow.connect(mcflirt, 'out_file', outputspec, 'func_out_file') analysisflow.connect(mcflirt, 'mat_file', outputspec, 'mat_file') analysisflow.connect(mcflirt, 'par_file', outputspec, 'mc_par_file') analysisflow.connect(mcflirt, 'out_file', ds_nii, 'mc_func') analysisflow.connect(mcflirt, 'par_file', ds_text, 'mc_par') #analysisflow.connect(mcflirt, 'std_img', ds, 'mc.@std_img') analysisflow.connect(mcflirt, 'rms_files', ds_text, 'mc_rms') #analysisflow.connect(mcflirt, 'variance_img', ds, 'mc.@variance_img') analysisflow.connect(calc_friston, 'out_file', outputspec, 'first24_file') analysisflow.connect(calc_friston, 'out_file', ds_text, 'mc_first24') analysisflow.connect(calculate_FD, 'out_file', outputspec, 'FD_file') analysisflow.connect(mcflirt, 'par_file', plot_motion_rot, 'in_file') analysisflow.connect(mcflirt, 'par_file', plot_motion_tra, 'in_file') analysisflow.connect(plot_motion_rot, 'out_file', ds_qc_rot, 'motion_correction') analysisflow.connect(plot_motion_tra, 'out_file', ds_qc_tra, 'motion_correction') analysisflow.connect(mcflirt, 'out_file', myqc, 'inputspec.func') # pop-level mean FD analysisflow.connect(calculate_FD, 'out_file', meanFD, 'in_file') analysisflow.connect(calculate_FD, 'out_file', ds_text, 'mc_fd') analysisflow.connect(calculate_FD, 'out_figure', ds_qc_fd, 'FD') analysisflow.connect(meanFD, 'mean_file', pop_FD, 'in_list') analysisflow.connect(pop_FD, 'txt_file', ds_fd, 'pop') analysisflow.connect(calculate_FD, 'out_file', maxFD, 'in_file') analysisflow.connect(maxFD, 'max_file', pop_FDmax, 'in_list') analysisflow.connect(pop_FDmax, 'txt_file', ds_fd_max, 'pop') return analysisflow
def create_fsl_fs_preproc(name='preproc', highpass=True, whichvol='middle'): """Create a FEAT preprocessing workflow together with freesurfer Parameters ---------- :: name : name of workflow (default: preproc) highpass : boolean (default: True) whichvol : which volume of the first run to register to ('first', 'middle', 'mean') Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) inputspec.subject_id : freesurfer subject id inputspec.subjects_dir : freesurfer subjects dir Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask_file : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.reg_file : bbregister registration files outputspec.reg_cost : bbregister registration cost files Example ------- >>> preproc = create_fsl_fs_preproc(whichvol='first') >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.subjects_dir = '.' >>> preproc.inputs.inputspec.subject_id = 's1' >>> preproc.inputs.inputspec.fwhm = 6 >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ if highpass: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'subject_id', 'subjects_dir', 'highpass']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'highpassed_files', 'reg_file', 'reg_cost' ]), name='outputspec') else: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'subject_id', 'subjects_dir' ]), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask_file', 'smoothed_files', 'reg_file', 'reg_cost' ]), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ if whichvol != 'mean': extract_ref = pe.Node(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file'], name = 'extractref') featpreproc.connect(img2float, ('out_file', pickfirst), extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True, save_plots = True, interpolation = 'sinc'), name='realign', iterfield = ['in_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') else: motion_correct.inputs.mean_vol = True featpreproc.connect(motion_correct, 'mean_img', outputnode, 'reference') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """Get the mask from subject for each run """ maskflow = create_getmask_flow() featpreproc.connect([(inputnode, maskflow, [('subject_id','inputspec.subject_id'), ('subjects_dir', 'inputspec.subjects_dir')])]) maskflow.inputs.inputspec.contrast_type = 't2' if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', maskflow, 'inputspec.source_file') else: featpreproc.connect(motion_correct, ('mean_img', pickfirst), maskflow, 'inputspec.source_file') """ Mask the functional runs with the extracted mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name = 'maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_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 consituting the mean functional """ smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), smooth, 'inputnode.mask_file') """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') featpreproc.connect(maskfunc, ('out_file', tolist), concatnode, 'in1') 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', outputnode, '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') """ 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'], name='medianval') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), medianval, 'mask_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') featpreproc.connect(maskflow, ('outputspec.mask_file', pickfirst), outputnode, 'mask_file') featpreproc.connect(maskflow, 'outputspec.reg_file', outputnode, 'reg_file') featpreproc.connect(maskflow, 'outputspec.reg_cost', outputnode, 'reg_cost') return featpreproc
def create_moco_pipeline(working_dir, ds_dir, name='motion_correction'): """ Workflow for motion correction to 1st volume based on https://github.com/NeuroanatomyAndConnectivity/pipelines/blob/master/src/lsd_lemon/func_preproc/moco.py """ # initiate workflow moco_wf = Workflow(name=name) moco_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=['epi', 'vols_to_drop']), name='inputnode') outputnode = Node(util.IdentityInterface(fields=[ 'epi_moco', 'par_moco', 'mat_moco', 'rms_moco', 'initial_mean_epi_moco', 'rotplot', 'transplot', 'dispplots', 'tsnr_file', 'epi_mask' ]), name='outputnode') ds = Node(nio.DataSink(base_directory=ds_dir), name='ds') ds.inputs.substitutions = [('_TR_id_', 'TR_')] # REMOVE FIRST VOLUMES drop_vols = Node(util.Function(input_names=['in_file', 't_min'], output_names=['out_file'], function=strip_rois_func), name='remove_vol') moco_wf.connect(inputnode, 'epi', drop_vols, 'in_file') moco_wf.connect(inputnode, 'vols_to_drop', drop_vols, 't_min') # MCFILRT MOCO TO 1st VOLUME mcflirt = Node(fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True, ref_vol=0, out_file='rest_realigned.nii.gz'), name='mcflirt') moco_wf.connect(drop_vols, 'out_file', mcflirt, 'in_file') moco_wf.connect([(mcflirt, ds, [('par_file', 'realign.par.@par'), ('mat_file', 'realign.MAT.@mat'), ('rms_files', 'realign.plots.@rms')])]) moco_wf.connect([(mcflirt, outputnode, [('out_file', 'epi_moco'), ('par_file', 'par_moco'), ('mat_file', 'mat_moco'), ('rms_files', 'rms_moco')])]) # CREATE MEAN EPI (INTENSITY NORMALIZED) initial_mean_epi_moco = Node(fsl.maths.MeanImage( dimension='T', out_file='initial_mean_epi_moco.nii.gz'), name='initial_mean_epi_moco') moco_wf.connect(mcflirt, 'out_file', initial_mean_epi_moco, 'in_file') moco_wf.connect(initial_mean_epi_moco, 'out_file', outputnode, 'initial_mean_epi_moco') moco_wf.connect(initial_mean_epi_moco, 'out_file', ds, 'QC.initial_mean_epi_moco') # PLOT MOTION PARAMETERS rotplotter = Node(fsl.PlotMotionParams(in_source='fsl', plot_type='rotations', out_file='rotation_plot.png'), name='rotplotter') moco_wf.connect(mcflirt, 'par_file', rotplotter, 'in_file') moco_wf.connect(rotplotter, 'out_file', ds, 'realign.plots.@rotplot') transplotter = Node(fsl.PlotMotionParams(in_source='fsl', plot_type='translations', out_file='translation_plot.png'), name='transplotter') moco_wf.connect(mcflirt, 'par_file', transplotter, 'in_file') moco_wf.connect(transplotter, 'out_file', ds, 'realign.plots.@transplot') dispplotter = MapNode(interface=fsl.PlotMotionParams( in_source='fsl', plot_type='displacement'), name='dispplotter', iterfield=['in_file']) dispplotter.iterables = ('plot_type', ['displacement']) moco_wf.connect(mcflirt, 'rms_files', dispplotter, 'in_file') moco_wf.connect(dispplotter, 'out_file', ds, 'realign.plots.@dispplots') moco_wf.write_graph(dotfilename=moco_wf.name, graph2use='flat', format='pdf') return moco_wf
#Wraps command **flirt** NodeHash_8f61130 = pe.MapNode(interface = fsl.FLIRT(), name = 'NodeName_8f61130', iterfield = ['in_file', 'in_matrix_file', 'reference', 'wm_seg']) NodeHash_8f61130.inputs.cost = 'bbr' NodeHash_8f61130.inputs.dof = 6 NodeHash_8f61130.inputs.no_resample = True #Wraps command **mcflirt** NodeHash_c3ef3c0 = pe.MapNode(interface = fsl.MCFLIRT(), name = 'NodeName_c3ef3c0', iterfield = ['in_file', 'ref_file']) NodeHash_c3ef3c0.inputs.interpolation = 'spline' NodeHash_c3ef3c0.inputs.save_mats = True NodeHash_c3ef3c0.inputs.save_plots = True NodeHash_c3ef3c0.inputs.save_rms = True #Wraps command **fsl_tsplot** NodeHash_c4a9c30 = pe.MapNode(interface = fsl.PlotMotionParams(), name = 'NodeName_c4a9c30', iterfield = ['in_file']) NodeHash_c4a9c30.inputs.args = '-a x,y,z -w 640 -h 144' NodeHash_c4a9c30.inputs.in_source = 'fsl' NodeHash_c4a9c30.iterables = [('plot_type', ['rotations', 'translations'])] #Wraps command **fslmaths** NodeHash_1040c610 = pe.MapNode(interface = fsl.MeanImage(), name = 'NodeName_1040c610', iterfield = ['in_file']) NodeHash_1040c610.inputs.dimension = 'T' #Wraps command **bet** NodeHash_1212d0d0 = pe.MapNode(interface = fsl.BET(), name = 'NodeName_1212d0d0', iterfield = ['in_file']) NodeHash_1212d0d0.inputs.frac = 0.3 NodeHash_1212d0d0.inputs.mask = True NodeHash_1212d0d0.inputs.no_output = True #Wraps command **fslmaths**
def create_motion_correction_workflow(name='moco', method='AFNI', extend_moco_params=False): """uses sub-workflows to perform different registration steps. Requires fsl and freesurfer tools Parameters ---------- name : string name of workflow Example ------- >>> motion_correction_workflow = create_motion_correction_workflow('motion_correction_workflow') >>> motion_correction_workflow.inputs.inputspec.output_directory = '/data/project/raw/BIDS/sj_1/' >>> motion_correction_workflow.inputs.inputspec.in_files = ['sub-001.nii.gz','sub-002.nii.gz'] >>> motion_correction_workflow.inputs.inputspec.which_file_is_EPI_space = 'middle' Inputs:: inputspec.output_directory : directory in which to sink the result files inputspec.in_files : list of functional files inputspec.which_file_is_EPI_space : determines which file is the 'standard EPI space' Outputs:: outputspec.EPI_space_file : standard EPI space file, one timepoint outputspec.motion_corrected_files : motion corrected files outputspec.motion_correction_plots : motion correction plots outputspec.motion_correction_parameters : motion correction parameters """ ### NODES input_node = pe.Node(IdentityInterface(fields=[ 'in_files', 'output_directory', 'which_file_is_EPI_space', 'sub_id', 'tr' ]), name='inputspec') output_node = pe.Node(IdentityInterface(fields=([ 'motion_corrected_files', 'EPI_space_file', 'mask_EPI_space_file', 'motion_correction_plots', 'motion_correction_parameters', 'extended_motion_correction_parameters', 'new_motion_correction_parameters' ])), name='outputspec') ######################################################################################## # Invariant nodes ######################################################################################## EPI_file_selector_node = pe.Node(interface=EPI_file_selector, name='EPI_file_selector_node') mean_bold = pe.Node(interface=fsl.maths.MeanImage(dimension='T'), name='mean_space') rename_mean_bold = pe.Node(niu.Rename(format_string='session_EPI_space', keep_ext=True), name='rename_mean_bold') ######################################################################################## # Workflow ######################################################################################## motion_correction_workflow = pe.Workflow(name=name) motion_correction_workflow.connect(input_node, 'which_file_is_EPI_space', EPI_file_selector_node, 'which_file') motion_correction_workflow.connect(input_node, 'in_files', EPI_file_selector_node, 'in_files') ######################################################################################## # outputs via datasink ######################################################################################## datasink = pe.Node(nio.DataSink(), name='sinker') datasink.inputs.parameterization = False # first link the workflow's output_directory into the datasink. motion_correction_workflow.connect(input_node, 'output_directory', datasink, 'base_directory') motion_correction_workflow.connect(input_node, 'sub_id', datasink, 'container') ######################################################################################## # FSL MCFlirt ######################################################################################## # new approach, which should aid in the joint motion correction of # multiple sessions together, by pre-registering each run. # the strategy would be to, for each run, take the first TR # and FLIRT-align (6dof) it to the EPI_space file. # then we can use this as an --infile argument to mcflirt. if method == 'FSL': rename_motion_files = pe.MapNode( niu.Rename(keep_ext=False), name='rename_motion_files', iterfield=['in_file', 'format_string']) remove_niigz_ext = pe.MapNode(interface=Remove_extension, name='remove_niigz_ext', iterfield=['in_file']) motion_correct_EPI_space = pe.Node(interface=fsl.MCFLIRT( cost='normcorr', interpolation='sinc', mean_vol=True), name='motion_correct_EPI_space') motion_correct_all = pe.MapNode(interface=fsl.MCFLIRT( save_mats=True, save_plots=True, cost='normcorr', interpolation='sinc', stats_imgs=True), name='motion_correct_all', iterfield=['in_file']) plot_motion = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) if extend_moco_params: # make extend_motion_pars node here # extend_motion_pars = pe.MapNode(Function(input_names=['moco_par_file', 'tr'], output_names=['new_out_file', 'ext_out_file'], # function=_extend_motion_parameters), name='extend_motion_pars', iterfield = ['moco_par_file']) pass # create reference: motion_correction_workflow.connect(EPI_file_selector_node, 'out_file', motion_correct_EPI_space, 'in_file') motion_correction_workflow.connect(motion_correct_EPI_space, 'out_file', mean_bold, 'in_file') motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'ref_file') # motion correction across runs motion_correction_workflow.connect(input_node, 'in_files', motion_correct_all, 'in_file') #motion_correction_workflow.connect(motion_correct_all, 'out_file', output_node, 'motion_corrected_files') # motion_correction_workflow.connect(motion_correct_all, 'par_file', extend_motion_pars, 'moco_par_file') # motion_correction_workflow.connect(input_node, 'tr', extend_motion_pars, 'tr') # motion_correction_workflow.connect(extend_motion_pars, 'ext_out_file', output_node, 'extended_motion_correction_parameters') # motion_correction_workflow.connect(extend_motion_pars, 'new_out_file', output_node, 'new_motion_correction_parameters') ######################################################################################## # Plot the estimated motion parameters ######################################################################################## # rename: motion_correction_workflow.connect(mean_bold, 'out_file', rename_mean_bold, 'in_file') motion_correction_workflow.connect(motion_correct_all, 'par_file', rename_motion_files, 'in_file') motion_correction_workflow.connect(motion_correct_all, 'par_file', remove_niigz_ext, 'in_file') motion_correction_workflow.connect(remove_niigz_ext, 'out_file', rename_motion_files, 'format_string') # plots: plot_motion.iterables = ('plot_type', ['rotations', 'translations']) motion_correction_workflow.connect(rename_motion_files, 'out_file', plot_motion, 'in_file') motion_correction_workflow.connect(plot_motion, 'out_file', output_node, 'motion_correction_plots') # output node: motion_correction_workflow.connect(mean_bold, 'out_file', output_node, 'EPI_space_file') motion_correction_workflow.connect(rename_motion_files, 'out_file', output_node, 'motion_correction_parameters') motion_correction_workflow.connect(motion_correct_all, 'out_file', output_node, 'motion_corrected_files') # datasink: motion_correction_workflow.connect(rename_mean_bold, 'out_file', datasink, 'reg') motion_correction_workflow.connect(motion_correct_all, 'out_file', datasink, 'mcf') motion_correction_workflow.connect(rename_motion_files, 'out_file', datasink, 'mcf.motion_pars') motion_correction_workflow.connect(plot_motion, 'out_file', datasink, 'mcf.motion_plots') # motion_correction_workflow.connect(extend_motion_pars, 'ext_out_file', datasink, 'mcf.ext_motion_pars') # motion_correction_workflow.connect(extend_motion_pars, 'new_out_file', datasink, 'mcf.new_motion_pars') ######################################################################################## # AFNI 3DVolReg ######################################################################################## # for speed, we use AFNI's 3DVolReg brute-force. # this loses plotting of motion parameters but increases speed # we hold on to the same setup, first moco the selected run # and then moco everything to that image, but without the # intermediate FLIRT step. if method == 'AFNI': motion_correct_EPI_space = pe.Node( interface=afni.Volreg( outputtype='NIFTI_GZ', zpad=5, args=' -cubic ' # -twopass -Fourier ), name='motion_correct_EPI_space') motion_correct_all = pe.MapNode( interface=afni.Volreg( outputtype='NIFTI_GZ', zpad=5, args=' -cubic ' # -twopass ), name='motion_correct_all', iterfield=['in_file']) # for renaming *_volreg.nii.gz to *_mcf.nii.gz set_postfix_mcf = pe.MapNode(interface=Set_postfix, name='set_postfix_mcf', iterfield=['in_file']) set_postfix_mcf.inputs.postfix = 'mcf' rename_volreg = pe.MapNode(interface=Rename(keep_ext=True), name='rename_volreg', iterfield=['in_file', 'format_string']) # curate for moco between sessions motion_correction_workflow.connect(EPI_file_selector_node, 'out_file', motion_correct_EPI_space, 'in_file') motion_correction_workflow.connect(motion_correct_EPI_space, 'out_file', mean_bold, 'in_file') # motion correction across runs motion_correction_workflow.connect(input_node, 'in_files', motion_correct_all, 'in_file') motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'basefile') # motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'rotparent') # motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'gridparent') # output node: motion_correction_workflow.connect(mean_bold, 'out_file', output_node, 'EPI_space_file') motion_correction_workflow.connect(motion_correct_all, 'md1d_file', output_node, 'max_displacement_info') motion_correction_workflow.connect(motion_correct_all, 'oned_file', output_node, 'motion_correction_parameter_info') motion_correction_workflow.connect( motion_correct_all, 'oned_matrix_save', output_node, 'motion_correction_parameter_matrix') motion_correction_workflow.connect(input_node, 'in_files', set_postfix_mcf, 'in_file') motion_correction_workflow.connect(set_postfix_mcf, 'out_file', rename_volreg, 'format_string') motion_correction_workflow.connect(motion_correct_all, 'out_file', rename_volreg, 'in_file') motion_correction_workflow.connect(rename_volreg, 'out_file', output_node, 'motion_corrected_files') # datasink: motion_correction_workflow.connect(mean_bold, 'out_file', rename_mean_bold, 'in_file') motion_correction_workflow.connect(rename_mean_bold, 'out_file', datasink, 'reg') motion_correction_workflow.connect(rename_volreg, 'out_file', datasink, 'mcf') motion_correction_workflow.connect(motion_correct_all, 'md1d_file', datasink, 'mcf.max_displacement_info') motion_correction_workflow.connect(motion_correct_all, 'oned_file', datasink, 'mcf.parameter_info') motion_correction_workflow.connect(motion_correct_all, 'oned_matrix_save', datasink, 'mcf.motion_pars') return motion_correction_workflow
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
def create_preprocess_mag_wf(): preprocmag = pe.Workflow(name="preprocmag") preprocmag.config['execution']['remove_unnecessary_outputs'] = False # define inputs inputspec = pe.Node(ul.IdentityInterface(fields=['input_mag', # raw phase data 'frac', # BET franction (-f parameter) 'rest', # volumes of rest in block design 'task', # volumes of task in block design ]), name='inputspec') # convert image to float img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') # motion correct each run volreg = pe.MapNode(interface=afni.Volreg(), name='volreg', iterfield='in_file') volreg.inputs.outputtype = 'NIFTI_GZ' # calculate relative motions calcrel = pe.MapNode(ul.Function(['in_file'], ['out_file'], calcrelmotion), name='calcrel', iterfield=['in_file']) #generate motion plots plotmc = pe.MapNode(interface=fsl.PlotMotionParams(), name='plotmc', iterfield='in_file') plotmc.inputs.in_source = 'fsl' plotmc.iterables = ("plot_type", ['rotations', 'translations', 'displacement']) # register each run to first volume of first run # A) extract the first volume of the first run extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1, t_min=0), name='extract_ref', iterfield=['in_file']) # B) registration align2first = pe.MapNode(interface=afni.Allineate(), name='align2first', iterfield=['in_file']) align2first.inputs.num_threads = 2 align2first.inputs.out_matrix = 'align2first' # merge xfm from moco and first run alignment merge_xfm = pe.MapNode(interface=ul.Merge(2), name='merge_xfm', iterfield=['in1', 'in2']) # concatenate moco and alignment to run 1 cat_xfm = pe.MapNode(interface=afni.CatMatvec(oneline=True), name='cat_xfm', iterfield=['in_file']) cat_xfm.inputs.out_file = 'concated_xfm.aff12.1D' # apply first volume registration and motion correction in a single step applyalign = pe.MapNode(interface=afni.Allineate(), name='applyalign', iterfield=['in_file', 'in_matrix']) applyalign.inputs.num_threads = 2 applyalign.inputs.final_interpolation = 'nearestneighbour' applyalign.inputs.outputtype = 'NIFTI_GZ' # afni messes with the header (unobliques the data) this puts it back cpgeommoco = pe.MapNode(interface=fsl.CopyGeom(), name='cpgeommoco', iterfield=['dest_file', 'in_file']) # linear detrending prior to SNR calculation detrend = pe.MapNode(interface=pp.DetrendMag(), name='detrend', iterfield=['mag']) # get the mean functional of run 1 for brain extraction meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc', iterfield=['in_file']) # calculate the phase noise (takes in volume of activation, if none provided them assumes resting state) calcSNR = pe.MapNode(interface=pp.RestAverage(), name='calcSNR', iterfield=['func', 'rest', 'task']) # extract brain with fsl and save the mask extractor = pe.Node(interface=fsl.BET(), name="extractor") extractor.inputs.mask = True # apply the mask to all runs maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name='maskfunc') # outputspec outputspec = pe.Node(ul.IdentityInterface(fields=['proc_mag','motion_par', 'motion_data', 'maxdisp_data' , 'motion_plot', 'run_txfm', 'mask_file','mean_file','snr']), name='outputspec') preprocmag = pe.Workflow(name='preprocmag') preprocmag.connect([(inputspec, img2float, [('input_mag', 'in_file')]), (img2float, volreg, [('out_file', 'in_file')]), (volreg, extract_ref, [('out_file', 'in_file')]), (extract_ref, align2first, [('roi_file', 'in_file')]), (extract_ref, align2first, [(('roi_file', pickfirst), 'reference')]), (extract_ref, applyalign, [(('roi_file', pickfirst), 'reference')]), (volreg, merge_xfm, [('oned_matrix_save', 'in2')]), (align2first, merge_xfm, [('out_matrix', 'in1')]), (merge_xfm, cat_xfm, [(('out', wraptuple), 'in_file')]), (volreg,applyalign, [('out_file', 'in_file')]), (volreg, calcrel, [('md1d_file', 'in_file')]), (volreg, plotmc, [('oned_file', 'in_file')]), (cat_xfm, applyalign, [('out_file', 'in_matrix')]), (img2float, cpgeommoco, [('out_file', 'in_file')]), (applyalign, cpgeommoco, [('out_file', 'dest_file')]), (cpgeommoco, detrend, [('out_file', 'mag')]), (detrend, meanfunc, [('detrended_mag', 'in_file')]), (inputspec, calcSNR, [('rest', 'rest'), ('task', 'task')]), (detrend, calcSNR, [('detrended_mag', 'func')]), (inputspec, extractor, [('frac', 'frac')]), (meanfunc, extractor, [(('out_file', pickfirst), 'in_file')]), (cpgeommoco, maskfunc, [('out_file', 'in_file')]), (extractor, maskfunc, [('mask_file', 'in_file2')]), (maskfunc, outputspec, [('out_file', 'proc_mag')]), (volreg, outputspec, [('oned_matrix_save', 'motion_par')]), (volreg, outputspec, [('oned_file', 'motion_data')]), (volreg, outputspec, [('md1d_file', 'maxdisp_data')]), (plotmc, outputspec, [('out_file', 'motion_plot')]), (cat_xfm, outputspec, [('out_file', 'run_txfm')]), (extractor, outputspec, [('mask_file', 'mask_file')]), (extractor, outputspec, [('out_file', 'mean_file')]), (calcSNR, outputspec, [('tsnr', 'snr')]), ]) return preprocmag
def __motion_correct_file__( self, input_file, output_file, subject, directory, use_example_pp=False): # Check whether motion correction has already been completed print ">>>> Working on {}".format(input_file) if use_example_pp: pp.preproc.inputs.inputspec.func = input_file pp.preproc.inputs.inputspec.struct = os.path.join( subject.anatomical_dir(), 'highres001.nii.gz') pp.preproc.base_dir = directory pp.preproc.run() # TODO: copy motion correction photos as well intnorm_file = output_file.replace('.nii.gz', '_intnorm.nii.gz') shutil.copy( os.path.join( directory, 'preproc', 'intnorm', 'mapflow', '_intnorm0', 'bold_dtype_mcf_mask_intnorm.nii.gz'), intnorm_file) shutil.copy( os.path.join( directory, 'preproc', 'maskfunc2', 'mapflow', '_maskfunc20', 'bold_dtype_mcf_mask.nii.gz'), output_file) cmd = "eog {}".format( os.path.join( directory, 'preproc', 'realign', 'mapflow', '_realign0', 'bold_dtype_mcf.nii.gz_rot.png')) subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) cmd = "eog {}".format( os.path.join( directory, 'preproc', 'realign', 'mapflow', '_realign0', 'bold_dtype_mcf.nii.gz_trans.png')) subprocess.Popen( cmd, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) else: mcflt = fsl.MCFLIRT( in_file=input_file, out_file=output_file, save_plots=True) result = mcflt.run() pmp = fsl.PlotMotionParams( in_file=result.outputs.par_file, in_source='fsl') pmp.inputs.plot_type = 'rotations' pmp.run() pmp.inputs.plot_type = 'translations' pmp.run()
iterfield=['in_file', 'in_matrix_file', 'reference', 'wm_seg']) NodeHash_a9682a0.inputs.cost = 'bbr' NodeHash_a9682a0.inputs.dof = 6 NodeHash_a9682a0.inputs.no_resample = True #Wraps command **mcflirt** NodeHash_de46680 = pe.MapNode(interface=fsl.MCFLIRT(), name='NodeName_de46680', iterfield=['in_file', 'ref_file']) NodeHash_de46680.inputs.interpolation = 'spline' NodeHash_de46680.inputs.save_mats = True NodeHash_de46680.inputs.save_plots = True NodeHash_de46680.inputs.save_rms = True #Wraps command **fsl_tsplot** NodeHash_e827000 = pe.MapNode(interface=fsl.PlotMotionParams(), name='NodeName_e827000', iterfield=['in_file']) NodeHash_e827000.inputs.args = '-a x,y,z -w 640 -h 144' NodeHash_e827000.inputs.in_source = 'fsl' NodeHash_e827000.iterables = [('plot_type', ['rotations', 'translations'])] #Wraps command **fslmaths** NodeHash_12ef2460 = pe.MapNode(interface=fsl.MeanImage(), name='NodeName_12ef2460', iterfield=['in_file']) NodeHash_12ef2460.inputs.dimension = 'T' #Wraps command **bet** NodeHash_131f40e0 = pe.MapNode(interface=fsl.BET(), name='NodeName_131f40e0',
def create_featreg_preproc(name='featpreproc', whichvol='middle', deletetimepoints=4): """Create a FEAT preprocessing workflow with registration to one volume of the first run Parameters ---------- name : name of workflow (default: featpreproc) whichvol : which volume of the first run to register to ('first', 'middle', 'mean') Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.meanscaled_files : meanscaled data Example ------- >>> from nipype.workflows.fsl import create_featreg_preproc >>> import os >>> preproc = create_featreg_preproc() >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP >>> preproc = create_featreg_preproc(highpass=False, whichvol='mean') >>> preproc.inputs.inputspec.func = 'f3.nii' >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) # Set up a node to define all inputs and outputs required for the # preprocessing workflow inputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'func', 'fwhm', ]), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'reference', 'motion_parameters', 'realigned_files', 'motion_rot_plots', 'motion_trans_plots', 'mask', 'maskfunc_files', 'smoothed_files', 'meanscaled_files', ]), name='outputspec') # Convert functional images to float representation. Since there can # be more than one functional run we use a MapNode to convert each # run. deletevol = pe.MapNode(interface=fsl.ExtractROI(t_min=deletetimepoints), iterfield=['in_file', 't_size'], name='deletevol') img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', deletevol, 'in_file') featpreproc.connect(inputnode, ('func', getTTP, deletetimepoints), deletevol, 't_size') featpreproc.connect(deletevol, 'roi_file', img2float, 'in_file') # Extract the first volume of the each run as the reference if whichvol != 'mean': extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file'], name='extractref') featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') # Realign the functional runs to the reference (1st volume of first run) motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats=True, save_plots=True, save_rms=True, interpolation='sinc'), name='realign', iterfield=['in_file', 'ref_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') else: motion_correct.inputs.mean_vol = True featpreproc.connect(motion_correct, 'mean_img', outputnode, 'reference') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') # Plot the estimated motion parameters plot_rotations = pe.MapNode(interface=fsl.PlotMotionParams( in_source='fsl', plot_type='rotations'), name='plot_rotations', iterfield=['in_file']) plot_translations = pe.MapNode(interface=fsl.PlotMotionParams( in_source='fsl', plot_type='translations'), name='plot_translations', iterfield=['in_file']) # plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_rotations, 'in_file') featpreproc.connect(motion_correct, 'par_file', plot_translations, 'in_file') featpreproc.connect(plot_rotations, 'out_file', outputnode, 'motion_rot_plots') featpreproc.connect(plot_translations, 'out_file', outputnode, 'motion_trans_plots') # Extract the mean volume of the first functional run meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file') # Strip the skull from the mean functional to generate a mask meanfuncmask = pe.MapNode(interface=fsl.BET(mask=True, no_output=True, frac=0.3), iterfield=['in_file'], name='meanfuncmask') featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file') # Mask the functional runs with the extracted mask maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2') # 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') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') # Threshold the first run of the 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') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') # Define a function to get 10% of the intensity 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') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') # Dilate the mask dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask') # Mask the motion corrected functional runs with the dilated mask maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2') featpreproc.connect(maskfunc2, 'out_file', outputnode, 'maskfunc_files') # Smooth each run using SUSAN with the brightness threshold set to 75% # of the median value for each run and a mask consituting the mean # functional smooth = create_susan_smooth() featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') # 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') featpreproc.connect(maskfunc2, ('out_file', tolist), concatnode, 'in1') 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', outputnode, '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') featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') # Mask the scaled data with the dilated mask maskfunc4 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc4') featpreproc.connect(meanscale, 'out_file', maskfunc4, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc4, 'in_file2') featpreproc.connect(maskfunc4, 'out_file', outputnode, 'meanscaled_files') return featpreproc
def create_featreg_preproc(name='featpreproc', highpass=True, whichvol='middle', whichrun=0): """Create a FEAT preprocessing workflow with registration to one volume of the first run. If whichvol = None and/or whichrun = None, no motion correction will be performed and the FEAT preprocessing workflow will not return outputspec.reference, outputspec.realigned_files, and outputspec.motion_plots. Parameters ---------- :: name : name of workflow (default: featpreproc) highpass : boolean (default: True) whichvol : which volume of the first run to register to ('first', 'middle', 'last', 'mean', None) whichrun : which run to draw reference volume from (integer index or 'first', 'middle', 'last' or None) Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.mean : mean file Example ------- >>> preproc = create_featreg_preproc() >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP >>> preproc = create_featreg_preproc(highpass=False, whichvol='mean') >>> preproc.inputs.inputspec.func = 'f3.nii' >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP """ version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 featpreproc = pe.Workflow(name=name) """ Derive boolean motion correction parameter from the given parameters """ if (whichrun != None) & (whichvol != None): motion_corr = True else: motion_corr = False """ Set up a node to define all inputs required for the preprocessing workflow """ output_fields = ['mask', 'smoothed_files', 'mean', 'functional_files'] input_fields = ['func', 'fwhm'] if highpass: output_fields += ['highpassed_files'] input_fields += ['highpass'] inputnode = pe.Node( interface=util.IdentityInterface(fields=input_fields), name='inputspec') if motion_corr: output_fields += [ 'reference', 'motion_parameters', 'realigned_files' ] outputnode = pe.Node( interface=util.IdentityInterface(fields=output_fields), name='outputspec') else: outputnode = pe.Node( interface=util.IdentityInterface(fields=output_fields), name='outputspec') else: inputnode = pe.Node( interface=util.IdentityInterface(fields=input_fields), name='inputspec') if motion_corr: output_fields += [ 'reference', 'motion_parameters', 'realigned_files' ] outputnode = pe.Node( interface=util.IdentityInterface(fields=output_fields), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string='', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') if motion_corr: """ Extract the middle (or what whichvol points to) volume of the first run as the reference """ if whichvol != 'mean': extract_ref = pe.Node(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file'], name='extractref') featpreproc.connect(img2float, ('out_file', pickrun, whichrun), extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickvol, 0, whichvol), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (`whichvol` volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT( save_mats=True, save_plots=True, interpolation='spline'), name='realign', iterfield=['in_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') if whichvol != 'mean': featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') else: motion_correct.inputs.mean_vol = True featpreproc.connect(motion_correct, ('mean_img', pickrun, whichrun), outputnode, 'reference') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode( interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """ Extract the mean volume of the first functional run """ else: featpreproc.connect(img2float, 'out_file', outputnode, 'functional_files') #TODO: check whether this is really necessary if motion_corr: meanfunc = pe.Node(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc') featpreproc.connect(motion_correct, ('out_file', pickrun, whichrun), meanfunc, 'in_file') else: meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(img2float, 'out_file', meanfunc, 'in_file') """ Strip the skull from the mean functional to generate a mask """ if motion_corr: meanfuncmask = pe.Node(interface=fsl.BET(mask=True, no_output=True, frac=0.3), name='meanfuncmask') else: meanfuncmask = pe.MapNode(interface=fsl.BET(mask=True, no_output=True, frac=0.3), iterfield=['in_file'], name='meanfuncmask') featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file') """ Mask the functional runs with the extracted mask """ if motion_corr: maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file'], name='maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') else: maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc') featpreproc.connect(img2float, 'out_file', maskfunc, 'in_file') featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2') """ 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') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') """ Threshold the first run of the 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') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ 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 motion_corr: featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') else: featpreproc.connect(img2float, 'out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') """ Dilate the mask """ dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask') """ Mask the motion corrected functional runs with the dilated mask """ maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') if motion_corr: featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') else: featpreproc.connect(img2float, 'out_file', maskfunc2, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc2, '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 = create_susan_smooth() featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') """ 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') featpreproc.connect(maskfunc2, ('out_file', tolist), concatnode, 'in1') 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', outputnode, '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') """ 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') if motion_corr: featpreproc.connect(meanscale, ('out_file', pickrun, whichrun), meanfunc3, 'in_file') else: featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') 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') return featpreproc
def create_parallelfeat_preproc(name='featpreproc', highpass=True): """Preprocess each run with FSL independently of the others Parameters ---------- :: name : name of workflow (default: featpreproc) highpass : boolean (default: True) Inputs:: inputspec.func : functional runs (filename or list of filenames) inputspec.fwhm : fwhm for smoothing with SUSAN inputspec.highpass : HWHM in TRs (if created with highpass=True) Outputs:: outputspec.reference : volume to which runs are realigned outputspec.motion_parameters : motion correction parameters outputspec.realigned_files : motion corrected files outputspec.motion_plots : plots of motion correction parameters outputspec.mask : mask file used to mask the brain outputspec.smoothed_files : smoothed functional data outputspec.highpassed_files : highpassed functional data (if highpass=True) outputspec.mean : mean file Example ------- >>> preproc = create_parallelfeat_preproc() >>> preproc.inputs.inputspec.func = ['f3.nii', 'f5.nii'] >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.inputs.inputspec.highpass = 128./(2*2.5) >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP >>> preproc = create_parallelfeat_preproc(highpass=False) >>> preproc.inputs.inputspec.func = 'f3.nii' >>> preproc.inputs.inputspec.fwhm = 5 >>> preproc.base_dir = '/tmp' >>> preproc.run() # doctest: +SKIP """ featpreproc = pe.Workflow(name=name) """ Set up a node to define all inputs required for the preprocessing workflow """ if highpass: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm', 'highpass']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask', 'smoothed_files', 'highpassed_files', 'mean']), name='outputspec') else: inputnode = pe.Node(interface=util.IdentityInterface(fields=['func', 'fwhm']), name='inputspec') outputnode = pe.Node(interface=util.IdentityInterface(fields=['reference', 'motion_parameters', 'realigned_files', 'motion_plots', 'mask', 'smoothed_files', 'mean']), name='outputspec') """ Set up a node to define outputs for the preprocessing workflow """ """ Convert functional images to float representation. Since there can be more than one functional run we use a MapNode to convert each run. """ img2float = pe.MapNode(interface=fsl.ImageMaths(out_data_type='float', op_string = '', suffix='_dtype'), iterfield=['in_file'], name='img2float') featpreproc.connect(inputnode, 'func', img2float, 'in_file') """ Extract the first volume of the first run as the reference """ extract_ref = pe.MapNode(interface=fsl.ExtractROI(t_size=1), iterfield=['in_file', 't_min'], name = 'extractref') featpreproc.connect(img2float, 'out_file', extract_ref, 'in_file') featpreproc.connect(img2float, ('out_file', pickmiddle), extract_ref, 't_min') featpreproc.connect(extract_ref, 'roi_file', outputnode, 'reference') """ Realign the functional runs to the reference (1st volume of first run) """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats = True, save_plots = True), name='realign', iterfield = ['in_file', 'ref_file']) featpreproc.connect(img2float, 'out_file', motion_correct, 'in_file') featpreproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') featpreproc.connect(motion_correct, 'par_file', outputnode, 'motion_parameters') featpreproc.connect(motion_correct, 'out_file', outputnode, 'realigned_files') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) featpreproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') featpreproc.connect(plot_motion, 'out_file', outputnode, 'motion_plots') """ Extract the mean volume of the first functional run """ meanfunc = pe.MapNode(interface=fsl.ImageMaths(op_string = '-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc') featpreproc.connect(motion_correct, 'out_file', meanfunc, 'in_file') """ Strip the skull from the mean functional to generate a mask """ meanfuncmask = pe.MapNode(interface=fsl.BET(mask = True, no_output=True, frac = 0.3), iterfield=['in_file'], name = 'meanfuncmask') featpreproc.connect(meanfunc, 'out_file', meanfuncmask, 'in_file') """ Mask the functional runs with the extracted mask """ maskfunc = pe.MapNode(interface=fsl.ImageMaths(suffix='_bet', op_string='-mas'), iterfield=['in_file', 'in_file2'], name = 'maskfunc') featpreproc.connect(motion_correct, 'out_file', maskfunc, 'in_file') featpreproc.connect(meanfuncmask, 'mask_file', maskfunc, 'in_file2') """ 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') featpreproc.connect(maskfunc, 'out_file', getthresh, 'in_file') """ Threshold the first run of the 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') featpreproc.connect(maskfunc, 'out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ 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') featpreproc.connect(motion_correct, 'out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') """ Dilate the mask """ dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(threshold, 'out_file', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputnode, 'mask') """ Mask the motion corrected functional runs with the dilated mask """ maskfunc2 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc2') featpreproc.connect(motion_correct, 'out_file', maskfunc2, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc2, 'in_file2') """ Smooth each run using SUSAN with the brightness threshold set to 75% of the median value for each run and a mask consituting the mean functional """ smooth = create_susan_smooth() featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(maskfunc2, 'out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') """ 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') featpreproc.connect(maskfunc2,('out_file', tolist), concatnode, 'in1') 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', outputnode, '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') """ Perform temporal highpass filtering on the data """ if highpass: highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') featpreproc.connect(highpass, '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') if highpass: featpreproc.connect(highpass, 'out_file', meanfunc3, 'in_file') else: featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean') return featpreproc
#Wraps command **flirt** NodeHash_22c888c0 = pe.MapNode(interface = fsl.FLIRT(), name = 'NodeName_22c888c0', iterfield = ['in_file', 'in_matrix_file', 'reference', 'wm_seg']) NodeHash_22c888c0.inputs.cost = 'bbr' NodeHash_22c888c0.inputs.dof = 6 NodeHash_22c888c0.inputs.no_resample = True #Wraps command **mcflirt** NodeHash_32896ec0 = pe.MapNode(interface = fsl.MCFLIRT(), name = 'NodeName_32896ec0', iterfield = ['in_file', 'ref_file']) NodeHash_32896ec0.inputs.interpolation = 'spline' NodeHash_32896ec0.inputs.save_mats = True NodeHash_32896ec0.inputs.save_plots = True NodeHash_32896ec0.inputs.save_rms = True #Wraps command **fsl_tsplot** NodeHash_2a03f5c0 = pe.MapNode(interface = fsl.PlotMotionParams(), name = 'NodeName_2a03f5c0', iterfield = ['in_file']) NodeHash_2a03f5c0.inputs.args = '-a x,y,z -w 640 -h 144' NodeHash_2a03f5c0.inputs.in_source = 'fsl' NodeHash_2a03f5c0.iterables = [('plot_type', ['rotations', 'translations'])] #Wraps command **fslmaths** NodeHash_22a97680 = pe.MapNode(interface = fsl.MeanImage(), name = 'NodeName_22a97680', iterfield = ['in_file']) NodeHash_22a97680.inputs.dimension = 'T' #Wraps command **bet** NodeHash_55ea000 = pe.MapNode(interface = fsl.BET(), name = 'NodeName_55ea000', iterfield = ['in_file']) NodeHash_55ea000.inputs.frac = 0.3 NodeHash_55ea000.inputs.mask = True NodeHash_55ea000.inputs.no_output = True #Wraps command **fslmaths**
preproc.connect(inputnode, ('func', getmiddlevolume), extract_ref, 't_min') """ Realign the functional runs to the middle volume of the first run """ motion_correct = pe.MapNode(interface=fsl.MCFLIRT(save_mats=True, save_plots=True), name='realign', iterfield=['in_file']) preproc.connect(img2float, 'out_file', motion_correct, 'in_file') preproc.connect(extract_ref, 'roi_file', motion_correct, 'ref_file') """ Plot the estimated motion parameters """ plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) plot_motion.iterables = ('plot_type', ['rotations', 'translations']) preproc.connect(motion_correct, 'par_file', plot_motion, 'in_file') """ Extract the mean volume of the first functional run """ meanfunc = pe.Node(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc') preproc.connect(motion_correct, ('out_file', pickfirst), meanfunc, 'in_file') """ Strip the skull from the mean functional to generate a mask """
def create_motion_correction_workflow(analysis_info, name='moco'): """uses sub-workflows to perform different registration steps. Requires fsl and freesurfer tools Parameters ---------- name : string name of workflow Example ------- >>> motion_correction_workflow = create_motion_correction_workflow('motion_correction_workflow') >>> motion_correction_workflow.inputs.inputspec.output_directory = '/data/project/raw/BIDS/sj_1/' >>> motion_correction_workflow.inputs.inputspec.in_files = ['sub-001.nii.gz','sub-002.nii.gz'] >>> motion_correction_workflow.inputs.inputspec.which_file_is_EPI_space = 'middle' Inputs:: inputspec.output_directory : directory in which to sink the result files inputspec.in_files : list of functional files inputspec.which_file_is_EPI_space : determines which file is the 'standard EPI space' Outputs:: outputspec.EPI_space_file : standard EPI space file, one timepoint outputspec.motion_corrected_files : motion corrected files outputspec.motion_correction_plots : motion correction plots outputspec.motion_correction_parameters : motion correction parameters """ import os import os.path as op import nipype.pipeline as pe import nipype.interfaces.fsl as fsl import nipype.interfaces.utility as util import nipype.interfaces.io as nio from nipype.interfaces.utility import Function, IdentityInterface import nipype.interfaces.utility as niu ######################################################################################## # nodes ######################################################################################## input_node = pe.Node(IdentityInterface(fields=[ 'in_files', 'inplane_T2_files', 'T2_files_reg_matrices', 'output_directory', 'which_file_is_EPI_space', 'sub_id', 'tr' ]), name='inputspec') output_node = pe.Node(IdentityInterface(fields=([ 'motion_corrected_files', 'EPI_space_file', 'T2_space_file', 'motion_correction_plots', 'motion_correction_parameters', 'extended_motion_correction_parameters', 'new_motion_correction_parameters' ])), name='outputspec') EPI_file_selector_node = pe.Node(Function( input_names=['which_file', 'in_files'], output_names='raw_EPI_space_file', function=EPI_file_selector), name='EPI_file_selector_node') # motion_correct_EPI_space = pe.Node(interface=fsl.MCFLIRT( # save_mats = True, # stats_imgs = True, # save_plots = True, # save_rms = True, # cost = 'normmi', # interpolation = 'sinc', # dof = 6, # # ref_vol = 0 # ), name='realign_space') # mean_bold = pe.Node(interface=fsl.maths.MeanImage(dimension='T'), name='mean_space') # new approach, which should aid in the joint motion correction of # multiple sessions together, by pre-registering each run. # the strategy would be to, for each run, take the first TR # and FLIRT-align (6dof) it to the EPI_space file. # then we can use this as an --infile argument to mcflirt. select_target_T2_node = pe.Node(Function( input_names=['T2_file_list', 'target_session'], output_names=['which_T2'], function=select_target_T2), name='select_target_T2_node') select_target_T2_node.inputs.target_session = analysis_info[ 'target_session'] # select_target_epi_node = pe.Node(Function(input_names=['epi_file_list', 'T2_file_list', 'target_session', 'which_file'], output_names=['target_epi'], # function=select_target_epi), name='select_target_epi_node') # select_target_epi_node.inputs.target_session = analysis_info['target_session'] select_T2_for_epi_node = pe.MapNode(Function( input_names=['epi_file', 'T2_file_list'], output_names=['which_T2_file'], function=select_T2_for_epi), name='select_T2_for_epi_node', iterfield=['epi_file']) select_T2_mat_for_epi_node = pe.MapNode(Function( input_names=['epi_file', 'T2_file_list'], output_names=['which_T2_file'], function=select_T2_for_epi), name='select_T2_mat_for_epi_node', iterfield=['epi_file']) bet_T2_node = pe.MapNode(interface=fsl.BET( frac=analysis_info['T2_bet_f_value'], vertical_gradient=analysis_info['T2_bet_g_value'], functional=False, mask=True), name='bet_T2', iterfield=['in_file']) bet_epi_node = pe.MapNode(interface=fsl.BET( frac=analysis_info['T2_bet_f_value'], vertical_gradient=analysis_info['T2_bet_g_value'], functional=True, mask=True), name='bet_epi', iterfield=['in_file']) motion_correct_all = pe.MapNode(interface=fsl.MCFLIRT(save_mats=True, save_plots=True, cost='normmi', interpolation='sinc', stats_imgs=True, dof=6), name='realign_all', iterfield=['in_file', 'ref_file']) plot_motion = pe.MapNode(interface=fsl.PlotMotionParams(in_source='fsl'), name='plot_motion', iterfield=['in_file']) extend_motion_pars = pe.MapNode(Function( input_names=['moco_par_file', 'tr'], output_names=['new_out_file', 'ext_out_file'], function=_extend_motion_parameters), name='extend_motion_pars', iterfield=['moco_par_file']) # registration node is set up for rigid-body within-modality reg # reg_flirt_N = pe.MapNode(fsl.FLIRT(cost_func='normcorr', output_type = 'NIFTI_GZ',# dof = 6, schedule = op.abspath(op.join(os.environ['FSLDIR'], 'etc', 'flirtsch', 'sch2D_6dof')), # interp = 'sinc', dof = 6), # name = 'reg_flirt_N', iterfield = ['in_file']) regapply_moco_node = pe.MapNode(interface=fsl.ApplyXfm(interp='spline'), name='regapply_moco_node', iterfield=['in_file', 'in_matrix_file']) resample_epis = pe.MapNode(fsl.maths.MathsCommand(args=' -subsamp2offc '), name='resample_epis', iterfield=['in_file']) resample_target_T2 = pe.Node( fsl.maths.MathsCommand(args=' -subsamp2offc '), name='resample_target_T2') rename = pe.Node(niu.Rename(format_string='session_EPI_space', keep_ext=True), name='namer') rename_T2 = pe.Node(niu.Rename(format_string='session_T2_space', keep_ext=True), name='namer_T2') ######################################################################################## # workflow ######################################################################################## motion_correction_workflow = pe.Workflow(name=name) motion_correction_workflow.connect(input_node, 'in_files', bet_epi_node, 'in_file') motion_correction_workflow.connect(input_node, 'inplane_T2_files', bet_T2_node, 'in_file') # select example func data, and example T2 space # motion_correction_workflow.connect(input_node, 'which_file_is_EPI_space', select_target_epi_node, 'which_file') # motion_correction_workflow.connect(bet_epi_node, 'out_file', select_target_epi_node, 'epi_file_list') # motion_correction_workflow.connect(bet_T2_node, 'out_file', select_target_epi_node, 'T2_file_list') motion_correction_workflow.connect(bet_T2_node, 'out_file', select_target_T2_node, 'T2_file_list') # motion correct and average the standard EPI file # motion_correction_workflow.connect(select_target_epi_node, 'target_epi', motion_correct_EPI_space, 'in_file') # motion_correction_workflow.connect(motion_correct_EPI_space, 'out_file', mean_bold, 'in_file') # output node, for later saving # motion_correction_workflow.connect(mean_bold, 'out_file', output_node, 'EPI_space_file') motion_correction_workflow.connect(select_target_T2_node, 'which_T2', output_node, 'T2_space_file') # find the relevant T2 files for each of the epi files motion_correction_workflow.connect(bet_epi_node, 'out_file', select_T2_for_epi_node, 'epi_file') motion_correction_workflow.connect(bet_T2_node, 'out_file', select_T2_for_epi_node, 'T2_file_list') # find the relevant T2 registration file for each of the epi files motion_correction_workflow.connect(bet_epi_node, 'out_file', select_T2_mat_for_epi_node, 'epi_file') motion_correction_workflow.connect(input_node, 'T2_files_reg_matrices', select_T2_mat_for_epi_node, 'T2_file_list') # motion correction across runs # motion_correction_workflow.connect(prereg_flirt_N, 'out_matrix_file', motion_correct_all, 'init') motion_correction_workflow.connect(bet_epi_node, 'out_file', motion_correct_all, 'in_file') motion_correction_workflow.connect(select_T2_for_epi_node, 'which_T2_file', motion_correct_all, 'ref_file') # motion_correction_workflow.connect(mean_bold, 'out_file', motion_correct_all, 'ref_file') # the registration # motion_correction_workflow.connect(select_T2_for_epi_node, 'which_T2_file', reg_flirt_N, 'in_file') # motion_correction_workflow.connect(select_target_T2_node, 'which_T2', reg_flirt_N, 'reference') # output of motion correction of all files motion_correction_workflow.connect(motion_correct_all, 'par_file', output_node, 'motion_correction_parameters') motion_correction_workflow.connect(motion_correct_all, 'out_file', regapply_moco_node, 'in_file') # registration has already been done by hand. This registration matrix is in the datasource, and applied here. motion_correction_workflow.connect(select_T2_mat_for_epi_node, 'which_T2_file', regapply_moco_node, 'in_matrix_file') motion_correction_workflow.connect(select_target_T2_node, 'which_T2', regapply_moco_node, 'reference') motion_correction_workflow.connect(regapply_moco_node, 'out_file', resample_epis, 'in_file') motion_correction_workflow.connect(resample_epis, 'out_file', output_node, 'motion_corrected_files') motion_correction_workflow.connect(motion_correct_all, 'par_file', extend_motion_pars, 'moco_par_file') motion_correction_workflow.connect(input_node, 'tr', extend_motion_pars, 'tr') motion_correction_workflow.connect( extend_motion_pars, 'ext_out_file', output_node, 'extended_motion_correction_parameters') motion_correction_workflow.connect(extend_motion_pars, 'new_out_file', output_node, 'new_motion_correction_parameters') motion_correction_workflow.connect(rename, 'out_file', output_node, 'EPI_space_file') ######################################################################################## # Plot the estimated motion parameters ######################################################################################## plot_motion.iterables = ('plot_type', ['rotations', 'translations']) motion_correction_workflow.connect(motion_correct_all, 'par_file', plot_motion, 'in_file') motion_correction_workflow.connect(plot_motion, 'out_file', output_node, 'motion_correction_plots') ######################################################################################## # outputs via datasink ######################################################################################## datasink = pe.Node(nio.DataSink(), name='sinker') datasink.inputs.parameterization = False # first link the workflow's output_directory into the datasink. motion_correction_workflow.connect(input_node, 'output_directory', datasink, 'base_directory') motion_correction_workflow.connect(input_node, 'sub_id', datasink, 'container') motion_correction_workflow.connect(select_target_T2_node, 'which_T2', resample_target_T2, 'in_file') motion_correction_workflow.connect(resample_target_T2, 'out_file', rename, 'in_file') motion_correction_workflow.connect(rename, 'out_file', datasink, 'reg') motion_correction_workflow.connect(select_target_T2_node, 'which_T2', rename_T2, 'in_file') motion_correction_workflow.connect(rename_T2, 'out_file', datasink, 'reg.@T2') # motion_correction_workflow.connect(regapply_moco_node, 'out_file', datasink, 'mcf.hr') motion_correction_workflow.connect(resample_epis, 'out_file', datasink, 'mcf') motion_correction_workflow.connect(motion_correct_all, 'par_file', datasink, 'mcf.motion_pars') motion_correction_workflow.connect(plot_motion, 'out_file', datasink, 'mcf.motion_plots') motion_correction_workflow.connect(extend_motion_pars, 'ext_out_file', datasink, 'mcf.ext_motion_pars') motion_correction_workflow.connect(extend_motion_pars, 'new_out_file', datasink, 'mcf.new_motion_pars') motion_correction_workflow.connect(bet_T2_node, 'out_file', datasink, 'mcf.T2s') # motion_correction_workflow.connect(motion_correct_all, 'out_file', datasink, 'mcf.hr_per_session') # motion_correction_workflow.connect(reg_flirt_N, 'out_file', datasink, 'mcf.T2_per_session') return motion_correction_workflow