def _list_outputs(self): outputs = self._outputs().get() outputs['out_file'] = self._gen_outfilename() output_dir = os.path.dirname(outputs['out_file']) if isdefined(self.inputs.stats_imgs) and self.inputs.stats_imgs: if LooseVersion(Info.version()) < LooseVersion('6.0.0'): # FSL <6.0 outputs have .nii.gz_variance.nii.gz as extension outputs['variance_img'] = self._gen_fname( outputs['out_file'] + '_variance.ext', cwd=output_dir) outputs['std_img'] = self._gen_fname( outputs['out_file'] + '_sigma.ext', cwd=output_dir) else: outputs['variance_img'] = self._gen_fname( outputs['out_file'], suffix='_variance', cwd=output_dir) outputs['std_img'] = self._gen_fname( outputs['out_file'], suffix='_sigma', cwd=output_dir) # The mean image created if -stats option is specified ('meanvol') # is missing the top and bottom slices. Therefore we only expose the # mean image created by -meanvol option ('mean_reg') which isn't # corrupted. # Note that the same problem holds for the std and variance image. if isdefined(self.inputs.mean_vol) and self.inputs.mean_vol: if LooseVersion(Info.version()) < LooseVersion('6.0.0'): # FSL <6.0 outputs have .nii.gz_mean_img.nii.gz as extension outputs['mean_img'] = self._gen_fname( outputs['out_file'] + '_mean_reg.ext', cwd=output_dir) else: outputs['mean_img'] = self._gen_fname( outputs['out_file'], suffix='_mean_reg', cwd=output_dir) if isdefined(self.inputs.save_mats) and self.inputs.save_mats: _, filename = os.path.split(outputs['out_file']) mat_dir = os.path.join(output_dir, filename + '.mat') outputs['mat_dir'] = mat_dir _, _, _, timepoints = load(self.inputs.in_file).shape outputs['mat_file'] = [] for t in range(timepoints): outputs['mat_file'].append( os.path.join(mat_dir, 'MAT_%04d' % t)) if isdefined(self.inputs.save_plots) and self.inputs.save_plots: # Note - if e.g. out_file has .nii.gz, you get .nii.gz.par, # which is what mcflirt does! outputs['par_file'] = outputs['out_file'] + '.par' if isdefined(self.inputs.save_rms) and self.inputs.save_rms: outfile = outputs['out_file'] outputs['rms_files'] = [outfile + '_abs.rms', outfile + '_rel.rms'] return outputs
def test_bbregister(create_files_in_directory): filelist, outdir = create_files_in_directory bbr = freesurfer.BBRegister() # make sure command gets called assert bbr.cmd == "bbregister" # test raising error with mandatory args absent with pytest.raises(ValueError): bbr.cmdline bbr.inputs.subject_id = "fsaverage" bbr.inputs.source_file = filelist[0] bbr.inputs.contrast_type = "t2" # Check that 'init' is mandatory in FS < 6, but not in 6+ if Info.looseversion() < LooseVersion("6.0.0"): with pytest.raises(ValueError): bbr.cmdline else: bbr.cmdline bbr.inputs.init = "fsl" base, ext = os.path.splitext(os.path.basename(filelist[0])) if ext == ".gz": base, _ = os.path.splitext(base) assert bbr.cmdline == ("bbregister --t2 --init-fsl " "--reg {base}_bbreg_fsaverage.dat " "--mov {full} --s fsaverage".format( full=filelist[0], base=base))
from nipype.workflows.fmri.fsl import (create_featreg_preproc, create_modelfit_workflow, create_fixed_effects_flow) from nipype import LooseVersion from nipype import Workflow, Node, MapNode from nipype.interfaces import (fsl, Function, ants, freesurfer) from nipype.interfaces.utility import Rename, Merge, IdentityInterface from nipype.utils.filemanip import filename_to_list from nipype.interfaces.io import DataSink, FreeSurferSource import nipype.interfaces.freesurfer as fs version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 fsl.FSLCommand.set_default_output_type('NIFTI_GZ') imports = ['import os', 'import nibabel as nb', 'import numpy as np', 'import scipy as sp', 'from nipype.utils.filemanip import filename_to_list, list_to_filename, split_filename', 'from scipy.special import legendre' ] def median(in_files): """Computes an average of the median of each realigned timeseries
def test_FSVersion(): """Check that FSVersion is a string that can be compared with LooseVersion""" assert isinstance(freesurfer.preprocess.FSVersion, str) assert LooseVersion(freesurfer.preprocess.FSVersion) >= LooseVersion("0")
def create_nuisance_modelfit_workflow(name='modelfit', f_contrasts=False): """ Create an FSL modelfitting workflow that returns also residual4d and sigmasquareds. Example ------- # >>> modelfit = create_modelfit_workflow() # >>> modelfit.base_dir = '.' # >>> info = dict() # >>> modelfit.inputs.inputspec.session_info = info # >>> modelfit.inputs.inputspec.interscan_interval = 3. # >>> modelfit.inputs.inputspec.film_threshold = 1000 # >>> modelfit.run() #doctest: +SKIP Inputs:: inputspec.session_info : info generated by modelgen.SpecifyModel inputspec.interscan_interval : interscan interval inputspec.contrasts : list of contrasts inputspec.film_threshold : image threshold for FILM estimation inputspec.model_serial_correlations inputspec.bases Outputs:: outputspec.copes outputspec.varcopes outputspec.dof_file outputspec.pfiles outputspec.zfiles outputspec.parameter_estimates outputspec.residual4d outputspec.sigmasquareds """ version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 modelfit = pe.Workflow(name=name) """ Create the nodes """ inputspec = pe.Node(util.IdentityInterface(fields=[ 'session_info', 'interscan_interval', 'contrasts', 'film_threshold', 'functional_data', 'bases', 'model_serial_correlations' ]), name='inputspec') level1design = pe.Node(interface=fsl.Level1Design(), name="level1design") modelgen = pe.MapNode(interface=fsl.FEATModel(), name='modelgen', iterfield=['fsf_file', 'ev_files']) if version < 507: modelestimate = pe.MapNode(interface=fsl.FILMGLS(smooth_autocorr=True, mask_size=5), name='modelestimate', iterfield=['design_file', 'in_file']) else: if f_contrasts: iterfield = ['design_file', 'in_file', 'tcon_file', 'fcon_file'] else: iterfield = ['design_file', 'in_file', 'tcon_file'] modelestimate = pe.MapNode(interface=fsl.FILMGLS(smooth_autocorr=True, mask_size=5), name='modelestimate', iterfield=iterfield) if version < 507: if f_contrasts: iterfield = [ 'tcon_file', 'fcon_file', 'param_estimates', 'sigmasquareds', 'corrections', 'dof_file' ] else: iterfield = [ 'tcon_file', 'param_estimates', 'sigmasquareds', 'corrections', 'dof_file' ] conestimate = pe.MapNode(interface=fsl.ContrastMgr(), name='conestimate', iterfield=[ 'tcon_file', 'fcon_file', 'param_estimates', 'sigmasquareds', 'corrections', 'dof_file' ]) if f_contrasts: iterfield = ['in1', 'in2'] else: iterfield = ['in1'] merge_contrasts = pe.MapNode(interface=util.Merge(2), name='merge_contrasts', iterfield=iterfield) ztopval = pe.MapNode(interface=fsl.ImageMaths(op_string='-ztop', suffix='_pval'), nested=True, name='ztop', iterfield=['in_file']) outputspec = pe.Node(util.IdentityInterface(fields=[ 'copes', 'varcopes', 'dof_file', 'pfiles', 'zfiles', 'parameter_estimates', 'residual4d', 'sigmasquareds' ]), name='outputspec') """ Setup the connections """ modelfit.connect([ (inputspec, level1design, [('interscan_interval', 'interscan_interval'), ('session_info', 'session_info'), ('contrasts', 'contrasts'), ('bases', 'bases'), ('model_serial_correlations', 'model_serial_correlations')]), (inputspec, modelestimate, [('film_threshold', 'threshold'), ('functional_data', 'in_file')]), (level1design, modelgen, [('fsf_files', 'fsf_file'), ('ev_files', 'ev_files')]), (modelgen, modelestimate, [('design_file', 'design_file')]), # connect also residual4d and sigmasquared (modelestimate, outputspec, [('param_estimates', 'parameter_estimates'), ('dof_file', 'dof_file'), ('residual4d', 'residual4d'), ('sigmasquareds', 'sigmasquareds')]), ]) if version < 507: modelfit.connect([ (modelgen, conestimate, [('con_file', 'tcon_file'), ('fcon_file', 'fcon_file')]), (modelestimate, conestimate, [('param_estimates', 'param_estimates'), ('sigmasquareds', 'sigmasquareds'), ('corrections', 'corrections'), ('dof_file', 'dof_file')]), (conestimate, outputspec, [('copes', 'copes'), ('varcopes', 'varcopes')]), ]) else: modelfit.connect([ (modelgen, modelestimate, [('con_file', 'tcon_file'), ('fcon_file', 'fcon_file')]), (modelestimate, outputspec, [('copes', 'copes'), ('varcopes', 'varcopes')]), ]) return modelfit
def create_workflow(): featpreproc = pe.Workflow(name="featpreproc") featpreproc.base_dir = os.path.join(ds_root, 'workingdirs') # =================================================================== # _____ _ # |_ _| | | # | | _ __ _ __ _ _| |_ # | | | '_ \| '_ \| | | | __| # _| |_| | | | |_) | |_| | |_ # |_____|_| |_| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Specify variables inputnode = pe.Node( niu.IdentityInterface(fields=[ 'funcs', 'subject_id', 'session_id', 'fwhm', # smoothing 'highpass' ]), name="inputspec") # SelectFiles templates = { 'ref_manual_fmapmask': # was: manual_fmapmask 'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_magnitude1_res-1x1x1_manualmask.nii.gz', 'ref_fmap_magnitude': 'derivatives/manual-masks/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_magnitude1_res-1x1x1_reference.nii.gz', 'ref_fmap_phasediff': 'derivatives/resampled-isotropic-1mm/sub-eddy/ses-20170511/fmap/' 'sub-eddy_ses-20170511_phasediff_res-1x1x1_preproc' '.nii.gz', # 'manualweights': # 'manual-masks/sub-eddy/ses-20170511/func/' # 'sub-eddy_ses-20170511_task-curvetracing_run-01_frame-50_bold' # '_res-1x1x1_manualweights.nii.gz', 'ref_func': # was: manualmask_func_ref 'derivatives/manual-masks/sub-eddy/ses-20170607/func/' 'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_' 'res-1x1x1_fnirt_reference.nii.gz', 'ref_funcmask': # was: manualmask 'derivatives/manual-masks/sub-eddy/ses-20170607/func/' 'sub-eddy_ses-20170607_task-RestingPRF_run-02_bold_' 'res-1x1x1_fnirt_mask.nii.gz', 'ref_t1': 'derivatives/manual-masks/sub-eddy/ses-20170511/anat/' 'sub-eddy_ses-20170511_T1w_res-1x1x1_reference.nii.gz', 'ref_t1mask': 'derivatives/manual-masks/sub-eddy/ses-20170511/anat/' 'sub-eddy_ses-20170511_T1w_res-1x1x1_manualmask.nii.gz', # 'funcs': # 'resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/func/' # # 'sub-{subject_id}_ses-{session_id}*_bold_res-1x1x1_preproc' # 'sub-{subject_id}_ses-{session_id}*run-01_bold_res-1x1x1_preproc' # # '.nii.gz', # '_nvol10.nii.gz', 'fmap_phasediff': 'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}_phasediff_res-1x1x1_preproc' '.nii.gz', 'fmap_magnitude': 'derivatives/resampled-isotropic-1mm/sub-{subject_id}/ses-{session_id}/fmap/' 'sub-{subject_id}_ses-{session_id}_magnitude1_res-1x1x1_preproc' '.nii.gz', # 'fmap_mask': # 'transformed-manual-fmap-mask/sub-{subject_id}/ses-{session_id}/fmap/' # 'sub-{subject_id}_ses-{session_id}_' # 'magnitude1_res-1x1x1_preproc.nii.gz', } inputfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir), name="input_files") featpreproc.connect([(inputnode, inputfiles, [ ('subject_id', 'subject_id'), ('session_id', 'session_id'), ])]) # =================================================================== # ____ _ _ # / __ \ | | | | # | | | |_ _| |_ _ __ _ _| |_ # | | | | | | | __| '_ \| | | | __| # | |__| | |_| | |_| |_) | |_| | |_ # \____/ \__,_|\__| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Output Files # Datasink outputfiles = pe.Node(nio.DataSink(base_directory=ds_root, container='derivatives/featpreproc', parameterization=True), name="output_files") # Use the following DataSink output substitutions # each tuple is only matched once per file outputfiles.inputs.substitutions = [ ('/_mc_method_afni3dAllinSlices/', '/'), ('/_mc_method_afni3dAllinSlices/', '/'), # needs to appear twice ('/oned_file/', '/'), ('/out_file/', '/'), ('/oned_matrix_save/', '/'), ('subject_id_', 'sub-'), ('session_id_', 'ses-'), ] # Put result into a BIDS-like format outputfiles.inputs.regexp_substitutions = [ (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1'), (r'/_addmean[0-9]+/', r'/func/'), (r'/_funcbrains[0-9]+/', r'/func/'), (r'/_maskfunc[0-9]+/', r'/func/'), (r'/_mc[0-9]+/', r'/func/'), (r'/_meanfunc[0-9]+/', r'/func/'), (r'/_outliers[0-9]+/', r'/func/'), (r'_run_id_[0-9][0-9]', r''), ] outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'motion_parameters', 'motion_corrected', 'motion_plots', 'motion_outlier_files', 'mask', 'smoothed_files', 'highpassed_files', 'mean', 'func_unwarp', 'ref_func', 'ref_funcmask', 'ref_t1', 'ref_t1mask', ]), name='outputspec') # =================================================================== # _____ _ _ _ # | __ (_) | (_) # | |__) | _ __ ___| |_ _ __ ___ # | ___/ | '_ \ / _ \ | | '_ \ / _ \ # | | | | |_) | __/ | | | | | __/ # |_| |_| .__/ \___|_|_|_| |_|\___| # | | # |_| # =================================================================== # ~|~ _ _ _ _ |` _ _ _ _ _ _ _ _| _ # | | (_|| |_\~|~(_)| | | | | | |(_|_\|<_\ # # Transform manual skull-stripped masks to multiple images # -------------------------------------------------------- # should just be used as input to motion correction, # after mc, all functionals should be aligned to reference transmanmask_mc = transform_manualmask.create_workflow() # - - - - - - Connections - - - - - - - featpreproc.connect([(inputfiles, transmanmask_mc, [ ('subject_id', 'in.subject_id'), ('session_id', 'in.session_id'), ])]) featpreproc.connect(inputfiles, 'ref_funcmask', transmanmask_mc, 'in.manualmask') featpreproc.connect(inputnode, 'funcs', transmanmask_mc, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', transmanmask_mc, 'in.manualmask_func_ref') # fieldmaps not being used if False: trans_fmapmask = transmanmask_mc.clone('trans_fmapmask') featpreproc.connect(inputfiles, 'ref_manual_fmapmask', trans_fmapmask, 'in.manualmask') featpreproc.connect(inputfiles, 'fmap_magnitude', trans_fmapmask, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', trans_fmapmask, 'in.manualmask_func_ref') # |\/| _ _|_. _ _ _ _ _ _ _ __|_. _ _ # | |(_) | |(_)| | (_(_)| | (/_(_ | |(_)| | # # Perform motion correction, using some pipeline # -------------------------------------------------------- # mc = motioncorrection_workflow.create_workflow_afni() # Register an image from the functionals to the reference image median_func = pe.MapNode( interface=fsl.maths.MedianImage(dimension="T"), name='median_func', iterfield=('in_file'), ) pre_mc = motioncorrection_workflow.create_workflow_allin_slices( name='premotioncorrection') featpreproc.connect([ (inputnode, median_func, [ ('funcs', 'in_file'), ]), (median_func, pre_mc, [ ('out_file', 'in.funcs'), ]), ( inputfiles, pre_mc, [ # median func image will be used a reference / base ('ref_func', 'in.ref_func'), ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, pre_mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (pre_mc, outputnode, [ ('mc.out_file', 'pre_motion_corrected'), ('mc.oned_file', 'pre_motion_parameters.oned_file'), ('mc.oned_matrix_save', 'pre_motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('pre_motion_corrected', 'pre_motion_corrected.out_file'), ('pre_motion_parameters.oned_file', 'pre_motion_corrected.oned_file' ), # warp parameters in ASCII (.1D) ('pre_motion_parameters.oned_matrix_save', 'pre_motion_corrected.oned_matrix_save' ), # transformation matrices for each sub-brick ]), ]) mc = motioncorrection_workflow.create_workflow_allin_slices( name='motioncorrection', iterfield=('in_file', 'ref_file', 'in_weight_file')) # - - - - - - Connections - - - - - - - featpreproc.connect([ (inputnode, mc, [ ('funcs', 'in.funcs'), ]), ( pre_mc, mc, [ # the median image realigned to the reference functional will serve as reference # this way motion correction is done to an image more similar to the functionals ('mc.out_file', 'in.ref_func'), ]), ( inputfiles, mc, [ # Check and make sure the ref func mask is close enough to the registered median # image. ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (mc, outputnode, [ ('mc.out_file', 'motion_corrected'), ('mc.oned_file', 'motion_parameters.oned_file'), ('mc.oned_matrix_save', 'motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('motion_corrected', 'motion_corrected.out_file'), ('motion_parameters.oned_file', 'motion_corrected.oned_file' ), # warp parameters in ASCII (.1D) ('motion_parameters.oned_matrix_save', 'motion_corrected.oned_matrix_save' ), # transformation matrices for each sub-brick ]), ]) # |~. _ | _| _ _ _ _ _ _ _ _ _ __|_. _ _ # |~|(/_|(_|| | |(_||_) (_(_)| | (/_(_ | |(_)| | # | # Unwarp EPI distortions # -------------------------------------------------------- # Performing motion correction to a reference that is undistorted, # so b0_unwarp is currently not needed if False: b0_unwarp = undistort_workflow.create_workflow() featpreproc.connect([ ( inputfiles, b0_unwarp, [ # ('subject_id', 'in.subject_id'), # ('session_id', 'in.session_id'), ('fmap_phasediff', 'in.fmap_phasediff'), ('fmap_magnitude', 'in.fmap_magnitude'), ]), (mc, b0_unwarp, [ ('mc.out_file', 'in.funcs'), ]), (transmanmask_mc, b0_unwarp, [ ('funcreg.out_file', 'in.funcmasks'), ]), (trans_fmapmask, b0_unwarp, [('funcreg.out_file', 'in.fmap_mask') ]), (b0_unwarp, outputfiles, [ ('out.funcs', 'func_unwarp.funcs'), ('out.funcmasks', 'func_unwarp.funcmasks'), ]), (b0_unwarp, outputnode, [ ('out.funcs', 'func_unwarp.funcs'), ('out.funcmasks', 'mask'), ]), ]) # undistort the reference images if False: b0_unwarp_ref = b0_unwarp.clone('b0_unwarp_ref') featpreproc.connect([ ( inputfiles, b0_unwarp_ref, [ # ('subject_id', 'in.subject_id'), # ('session_id', 'in.session_id'), ('ref_fmap_phasediff', 'in.fmap_phasediff'), ('ref_fmap_magnitude', 'in.fmap_magnitude'), ('ref_manual_fmapmask', 'in.fmap_mask'), ('ref_func', 'in.funcs'), ('ref_funcmask', 'in.funcmasks'), ]), (b0_unwarp_ref, outputfiles, [ ('out.funcs', 'func_unwarp_ref.func'), ('out.funcmasks', 'func_unwarp_ref.funcmask'), ]), (b0_unwarp_ref, outputnode, [ ('out.funcs', 'ref_func'), ('out.funcmasks', 'ref_mask'), ]), ]) else: featpreproc.connect([ (inputfiles, outputfiles, [ ('ref_func', 'reference/func'), ('ref_funcmask', 'reference/func_mask'), ]), (inputfiles, outputnode, [ ('ref_func', 'ref_func'), ('ref_funcmask', 'ref_funcmask'), ]), ]) # |~) _ _ . __|_ _ _ _|_ _ |~) _ |` _ _ _ _ _ _ # |~\(/_(_||_\ | (/_| | (_) |~\(/_~|~(/_| (/_| |(_(/_ # _| # Register all functionals to common reference # -------------------------------------------------------- if False: # this is now done during motion correction # FLIRT cost: intermodal: corratio, intramodal: least squares and normcorr reg_to_ref = pe.MapNode( # intra-modal # some runs need to be scaled along the anterior-posterior direction interface=fsl.FLIRT(dof=12, cost='normcorr'), name='reg_to_ref', iterfield=('in_file', 'in_weight'), ) refEPI_to_refT1 = pe.Node( # some runs need to be scaled along the anterior-posterior direction interface=fsl.FLIRT(dof=12, cost='corratio'), name='refEPI_to_refT1', ) # combine func -> ref_func and ref_func -> ref_T1 reg_to_refT1 = pe.MapNode( interface=fsl.ConvertXFM(concat_xfm=True), name='reg_to_refT1', iterfield=('in_file'), ) reg_funcs = pe.MapNode( interface=fsl.preprocess.ApplyXFM(), name='reg_funcs', iterfield=('in_file', 'in_matrix_file'), ) reg_funcmasks = pe.MapNode(interface=fsl.preprocess.ApplyXFM(), name='reg_funcmasks', iterfield=('in_file', 'in_matrix_file')) def deref_list(x): assert len(x) == 1 return x[0] featpreproc.connect([ ( b0_unwarp, reg_to_ref, # --> reg_to_ref, (A) [ ('out.funcs', 'in_file'), ('out.funcmasks', 'in_weight'), ]), (b0_unwarp_ref, reg_to_ref, [ (('out.funcs', deref_list), 'reference'), (('out.funcmasks', deref_list), 'ref_weight'), ]), ( b0_unwarp_ref, refEPI_to_refT1, # --> refEPI_to_refT1 (B) [ (('out.funcs', deref_list), 'in_file'), (('out.funcmasks', deref_list), 'in_weight'), ]), (inputfiles, refEPI_to_refT1, [ ('ref_t1', 'reference'), ('ref_t1mask', 'ref_weight'), ]), ( reg_to_ref, reg_to_refT1, # --> reg_to_refT1 (A*B) [ ('out_matrix_file', 'in_file'), ]), (refEPI_to_refT1, reg_to_refT1, [ ('out_matrix_file', 'in_file2'), ]), ( reg_to_refT1, reg_funcs, # --> reg_funcs [ # ('out_matrix_file', 'in_matrix_file'), ('out_file', 'in_matrix_file'), ]), (b0_unwarp, reg_funcs, [ ('out.funcs', 'in_file'), ]), (b0_unwarp_ref, reg_funcs, [ (('out.funcs', deref_list), 'reference'), ]), ( reg_to_refT1, reg_funcmasks, # --> reg_funcmasks [ # ('out_matrix_file', 'in_matrix_file'), ('out_file', 'in_matrix_file'), ]), (b0_unwarp, reg_funcmasks, [ ('out.funcmasks', 'in_file'), ]), (b0_unwarp_ref, reg_funcmasks, [ (('out.funcs', deref_list), 'reference'), ]), (reg_funcs, outputfiles, [ ('out_file', 'common_ref.func'), ]), (reg_funcmasks, outputfiles, [ ('out_file', 'common_ref.funcmask'), ]), ]) # |\/| _ _|_. _ _ _ _|_|. _ _ _ # | |(_) | |(_)| | (_)|_|| ||(/_| _\ # # -------------------------------------------------------- # Apply brain masks to functionals # -------------------------------------------------------- # Dilate mask """ Dilate the mask """ if False: dilatemask = pe.MapNode(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), iterfield=['in_file'], name='dilatemask') featpreproc.connect(reg_funcmasks, 'out_file', dilatemask, 'in_file') else: dilatemask = pe.Node(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), name='dilatemask') featpreproc.connect(inputfiles, 'ref_funcmask', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputfiles, 'dilate_mask') funcbrains = pe.MapNode(fsl.BinaryMaths(operation='mul'), iterfield=('in_file', 'operand_file'), name='funcbrains') featpreproc.connect([ (mc, funcbrains, [ ('mc.out_file', 'in_file'), ]), (dilatemask, funcbrains, [ ('out_file', 'operand_file'), ]), (funcbrains, outputfiles, [ ('out_file', 'funcbrains'), ]), ]) # Detect motion outliers # -------------------------------------------------------- import nipype.algorithms.rapidart as ra outliers = pe.MapNode( ra.ArtifactDetect( mask_type='file', # trying to "disable" `norm_threshold`: use_norm=True, norm_threshold=10.0, # combines translations in mm and rotations # use_norm=Undefined, # translation_threshold=1.0, # translation in mm # rotation_threshold=0.02, # rotation in radians zintensity_threshold=3.0, # z-score parameter_source='AFNI', save_plot=True), iterfield=('realigned_files', 'realignment_parameters', 'mask_file'), name='outliers') featpreproc.connect([ ( mc, outliers, [ # ('mc.par_file', 'realignment_parameters'), ('mc.oned_file', 'realignment_parameters'), ]), (funcbrains, outliers, [ ('out_file', 'realigned_files'), ]), (dilatemask, outliers, [ ('out_file', 'mask_file'), ]), ( outliers, outputfiles, [ ('outlier_files', 'motion_outliers.@outlier_files'), ('plot_files', 'motion_outliers.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]), (mc, outputnode, [ ('mc.oned_file', 'motion_parameters'), ]), ( outliers, outputnode, [ ('outlier_files', 'motion_outlier_files'), ('plot_files', 'motion_plots.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]) ]) """ Determine the 2nd and 98th percentile intensities of each functional run """ getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield=['in_file'], name='getthreshold') if False: featpreproc.connect(b0_unwarp, 'out.funcs', getthresh, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', getthresh, 'in_file') """ Threshold the first run of functional data at 10% of the 98th percentile """ threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') if False: featpreproc.connect(b0_unwarp, 'out.funcs', threshold, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ def getthreshop(thresh): return ['-thr %.10f -Tmin -bin' % (0.1 * val[1]) for val in thresh] featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield=['in_file', 'mask_file'], name='medianval') if False: featpreproc.connect(b0_unwarp, 'out.funcs', medianval, 'in_file') else: featpreproc.connect(mc, 'mc.out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') # (~ _ _ _|_. _ | (~ _ _ _ _ _|_|_ . _ _ # _)|_)(_| | |(_|| _)| | |(_)(_) | | ||| |(_| # | _| # Spatial smoothing (SUSAN) # -------------------------------------------------------- # create_susan_smooth takes care of calculating the mean and median # functional, applying mask to functional, and running the smoothing smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') # featpreproc.connect(b0_unwarp, 'out.funcs', smooth, 'inputnode.in_files') if False: featpreproc.connect(reg_funcs, 'out_file', smooth, 'inputnode.in_files') else: featpreproc.connect(mc, 'mc.out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') # ------------------------------------------------------- # The below is from workflows/fmri/fsl/preprocess.py """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') tolist = lambda x: [x] def chooseindex(fwhm): if fwhm < 1: return [0] else: return [1] # maskfunc2 is the functional data before SUSAN if False: featpreproc.connect(b0_unwarp, ('out.funcs', tolist), concatnode, 'in1') else: featpreproc.connect(mc, ('mc.out_file', tolist), concatnode, 'in1') # maskfunc3 is the functional data after SUSAN featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(), name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputfiles, 'smoothed_files') """ Scale the median value of the run is set to 10000. """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file', 'op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') # |_|. _ |_ _ _ _ _ # | ||(_|| ||_)(_|_\_\ # _| | # Temporal filtering # -------------------------------------------------------- highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') highpass_operand = lambda x: '-bptf %.10f -1' % x featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 if version < 507: featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') else: """ Add back the mean removed by the highpass filter operation as of FSL 5.0.7 """ meanfunc4 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc4') featpreproc.connect(meanscale, 'out_file', meanfunc4, 'in_file') addmean = pe.MapNode(interface=fsl.BinaryMaths(operation='add'), iterfield=['in_file', 'operand_file'], name='addmean') featpreproc.connect(highpass, 'out_file', addmean, 'in_file') featpreproc.connect(meanfunc4, 'out_file', addmean, 'operand_file') featpreproc.connect(addmean, 'out_file', outputnode, 'highpassed_files') """ Generate a mean functional image from the first run """ meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputfiles, 'mean') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean_highpassed') featpreproc.connect(outputnode, 'highpassed_files', outputfiles, 'highpassed_files') return (featpreproc)
from nipype.algorithms.misc import TSNR, CalculateMedian from nipype.interfaces.c3 import C3dAffineTool from nipype.interfaces import fsl, Function, ants, freesurfer as fs import nipype.interfaces.io as nio from nipype.interfaces.io import FreeSurferSource import nipype.interfaces.utility as niu from nipype.interfaces.utility import Merge, IdentityInterface from nipype.workflows.fmri.fsl import (create_featreg_preproc, create_modelfit_workflow, create_fixed_effects_flow) from nipype.utils import NUMPY_MMAP config.enable_provenance() version = 0 if (fsl.Info.version() and LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6')): version = 507 fsl.FSLCommand.set_default_output_type('NIFTI_GZ') imports = [ 'import os', 'import nibabel as nb', 'import numpy as np', 'import scipy as sp', 'from nipype.utils.filemanip import filename_to_list, list_to_filename, split_filename', 'from scipy.special import legendre' ] def create_reg_workflow(name='registration'): """Create a FEAT preprocessing workflow together with freesurfer
def looseversion(cls): """ Return a comparable version object If no version found, use LooseVersion('0.0.0') """ return LooseVersion(cls.version() or '0.0.0')
def create_featreg_preproc(name='featpreproc', highpass=True, whichvol='middle'): """Create a FEAT preprocessing workflow with registration to one volume of the first run Parameters ---------- :: name : name of workflow (default: featpreproc) 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) 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) """ 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 """ 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='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', pickfirst), 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 """ meanfunc = pe.Node(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), name='meanfunc') featpreproc.connect(motion_correct, ('out_file', pickfirst), meanfunc, 'in_file') """ Strip the skull from the mean functional to generate a mask """ meanfuncmask = pe.Node(interface=fsl.BET(mask=True, no_output=True, frac=0.3), 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'], 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 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.Node(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') featpreproc.connect(meanscale, ('out_file', pickfirst), 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
from os import path as osp from time import time import nipype.interfaces.fsl as fsl from nipype import LooseVersion from nipype.algorithms.misc import Gunzip from nipype.interfaces.io import SelectFiles, DataSink from nipype.interfaces.matlab import MatlabCommand from nipype.interfaces.utility import IdentityInterface, Merge from nipype.pipeline.engine import Workflow, Node, MapNode t_start = time() # Check FSL version version = 0 if fsl.Info.version() and LooseVersion( fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 # Set up interface defaults MatlabCommand.set_default_paths('/usr/local/matlabtools/2016a/spm12') MatlabCommand.set_default_matlab_cmd("matlab -nodesktop -nosplash") # Disabled default output type as this will force all other file extensions to NIFTI as well # fsl.FSLCommand.set_default_output_type('NIFTI') BASE_DIR = '/data/wbbruin/Desktop/resting-state_nipype/testset' DATA_DIR = osp.join(BASE_DIR, 'data') WORKING_DIR = osp.join(BASE_DIR, 'workdir') # Configure pre processing parameters template = fsl.Info.standard_image('MNI152_T1_2mm.nii.gz')
def create_workflow(undist): featpreproc = pe.Workflow(name="featpreproc") featpreproc.base_dir = os.path.join(ds_root, 'workingdirs') # =================================================================== # _____ _ # |_ _| | | # | | _ __ _ __ _ _| |_ # | | | '_ \| '_ \| | | | __| # _| |_| | | | |_) | |_| | |_ # |_____|_| |_| .__/ \__,_|\__| # | | # |_| # =================================================================== # ------------------ Specify variables inputnode = pe.Node( niu.IdentityInterface(fields=[ 'funcs', 'subject_id', 'session_id', 'refsubject_id', 'fwhm', # smoothing 'highpass' ]), name="inputspec") if undist: ud_flag = '_undist_PLUS' else: ud_flag = '' # SelectFiles templates = { # EPI ======== 'ref_func': 'reference-vols/sub-{refsubject_id}/func/' 'sub-{subject_id}_ref_func_res-1x1x1' + ud_flag + '.nii.gz', 'ref_funcmask': 'reference-vols/sub-{refsubject_id}/func/' 'sub-{subject_id}_ref_func_mask_res-1x1x1.nii.gz', # T1 ======== # 0.5 mm iso --- 'ref_t1': 'reference-vols/sub-{refsubject_id}/anat/' 'sub-{subject_id}_ref_anat_res-0.5x0.5x0.5.nii.gz', 'ref_t1mask': 'reference-vols/sub-{refsubject_id}/anat/' 'sub-{subject_id}_ref_anat_mask_res-0.5x0.5x0.5.nii.gz', } inputfiles = pe.Node(nio.SelectFiles(templates, base_directory=data_dir), name="input_files") featpreproc.connect([(inputnode, inputfiles, [ ('subject_id', 'subject_id'), ('session_id', 'session_id'), ('refsubject_id', 'refsubject_id'), ])]) # =================================================================== # ____ _ _ # / __ \ | | | | # | | | |_ _| |_ _ __ _ _| |_ # | | | | | | | __| '_ \| | | | __| # | |__| | |_| | |_| |_) | |_| | |_ # \____/ \__,_|\__| .__/ \__,_|\__| # | | # |_| # =================================================================== # Datasink outputfiles = pe.Node(nio.DataSink(base_directory=ds_root, container='derivatives/featpreproc', parameterization=True), name="output_files") # Use the following DataSink output substitutions # each tuple is only matched once per file outputfiles.inputs.substitutions = [ ('/_mc_method_afni3dAllinSlices/', '/'), ('/_mc_method_afni3dAllinSlices/', '/'), # needs to appear twice ('/oned_file/', '/'), ('/out_file/', '/'), ('/oned_matrix_save/', '/'), ('refsubject_id_', 'ref-'), ('subject_id_', 'sub-'), ('session_id_', 'ses-'), ] # Put result into a BIDS-like format outputfiles.inputs.regexp_substitutions = [ (r'_ses-([a-zA-Z0-9]+)_sub-([a-zA-Z0-9]+)', r'sub-\2/ses-\1'), (r'/_addmean[0-9]+/', r'/func/'), (r'/_funcbrains[0-9]+/', r'/func/'), (r'/_maskfunc[0-9]+/', r'/func/'), (r'/_mc[0-9]+/', r'/func/'), (r'/_meanfunc[0-9]+/', r'/func/'), (r'/_outliers[0-9]+/', r'/func/'), (r'_ref-([a-zA-Z0-9]+)_run_id_[0-9][0-9]', r''), ] outputnode = pe.Node(interface=util.IdentityInterface(fields=[ 'motion_parameters', 'motion_corrected', 'motion_plots', 'motion_outlier_files', 'mask', 'smoothed_files', 'highpassed_files', 'mean', 'func_unwarp', 'ref_func', 'ref_funcmask', 'ref_t1', 'ref_t1mask', ]), name='outputspec') # =================================================================== # _____ _ _ _ # | __ (_) | (_) # | |__) | _ __ ___| |_ _ __ ___ # | ___/ | '_ \ / _ \ | | '_ \ / _ \ # | | | | |_) | __/ | | | | | __/ # |_| |_| .__/ \___|_|_|_| |_|\___| # | | # |_| # =================================================================== # ~|~ _ _ _ _ |` _ _ _ _ _ _ _ _| _ # | | (_|| |_\~|~(_)| | | | | | |(_|_\|<_\ # # Transform manual skull-stripped masks to multiple images # -------------------------------------------------------- # should just be used as input to motion correction, # after mc, all functionals should be aligned to reference transmanmask_mc = transform_manualmask.create_workflow() # - - - - - - Connections - - - - - - - featpreproc.connect([(inputfiles, transmanmask_mc, [ ('subject_id', 'in.subject_id'), ('session_id', 'in.session_id'), ('refsubject_id', 'in.refsubject_id'), ])]) #featpreproc.connect(inputfiles, 'ref_funcmask', # transmanmask_mc, 'in.manualmask') featpreproc.connect(inputfiles, 'ref_funcmask', transmanmask_mc, 'in.ref_funcmask') featpreproc.connect(inputnode, 'funcs', transmanmask_mc, 'in.funcs') featpreproc.connect(inputfiles, 'ref_func', transmanmask_mc, 'in.ref_func') # |\/| _ _|_. _ _ _ _ _ _ _ __|_. _ _ # | |(_) | |(_)| | (_(_)| | (/_(_ | |(_)| | # # Perform motion correction, using some pipeline # -------------------------------------------------------- # Register an image from the functionals to the reference image median_func = pe.MapNode( interface=fsl.maths.MedianImage(dimension="T"), name='median_func', iterfield=('in_file'), ) # pre_mc = motioncorrection_workflow.create_workflow_allin_slices( # name='premotioncorrection') pre_mc = create_workflow_allin_slices(name='premotioncorrection') featpreproc.connect([ (inputnode, median_func, [ ('funcs', 'in_file'), ]), (median_func, pre_mc, [ ('out_file', 'in.funcs'), ]), ( inputfiles, pre_mc, [ # median func image will be used a reference / base ('ref_func', 'in.ref_func'), ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, pre_mc, [ ('funcreg.out_file', 'in.funcs_masks' ), # use mask as weights >>>> are we sure this is correct? ]), (pre_mc, outputnode, [ ('mc.out_file', 'pre_motion_corrected'), ('mc.oned_file', 'pre_motion_parameters.oned_file'), ('mc.oned_matrix_save', 'pre_motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('pre_motion_corrected', 'pre_motion_corrected.out_file'), ('pre_motion_parameters.oned_file', 'pre_motion_corrected.oned_file'), # warp parameters in ASCII (.1D) ('pre_motion_parameters.oned_matrix_save', 'pre_motion_corrected.oned_matrix_save'), # transformation matrices for each sub-brick ]), ]) # mc = motioncorrection_workflow.create_workflow_allin_slices( # name='motioncorrection', # iterfield=('in_file', 'ref_file', 'in_weight_file')) mc = create_workflow_allin_slices(name='motioncorrection', iterfield=('in_file', 'ref_file', 'in_weight_file')) # - - - - - - Connections - - - - - - - featpreproc.connect([ (inputnode, mc, [ ('funcs', 'in.funcs'), ]), ( pre_mc, mc, [ # the median image realigned to the reference functional # will serve as reference. This way motion correction is # done to an image more similar to the functionals ('mc.out_file', 'in.ref_func'), ]), ( inputfiles, mc, [ # Check and make sure the ref func mask is close enough # to the registered median image. ('ref_funcmask', 'in.ref_func_weights'), ]), ( transmanmask_mc, mc, [ ('funcreg.out_file', 'in.funcs_masks'), # use mask as weights ]), (mc, outputnode, [ ('mc.out_file', 'motion_corrected'), ('mc.oned_file', 'motion_parameters.oned_file'), ('mc.oned_matrix_save', 'motion_parameters.oned_matrix_save'), ]), ( outputnode, outputfiles, [ ('motion_corrected', 'motion_corrected.out_file'), ('motion_parameters.oned_file', 'motion_corrected.oned_file'), # warp parameters in ASCII (.1D) ('motion_parameters.oned_matrix_save', 'motion_corrected.oned_matrix_save'), # transformation matrices for each sub-brick ]), ]) # |~. _ | _| _ _ _ _ _ _ _ _ _ __|_. _ _ # |~|(/_|(_|| | |(_||_) (_(_)| | (/_(_ | |(_)| | # | # Unwarp EPI distortions # -------------------------------------------------------- # Performing motion correction to a reference that is undistorted, # So b0_unwarp is currently not needed or used. # we have moved this to a separate workflow and use the blip-up/down # method now (reverse phase-encoding directions). This has also been # done for the new reference images. featpreproc.connect([ (inputfiles, outputfiles, [ ('ref_func', 'reference/func'), ('ref_funcmask', 'reference/func_mask'), ]), (inputfiles, outputnode, [ ('ref_func', 'ref_func'), ('ref_funcmask', 'ref_funcmask'), ]), ]) # |\/| _ _|_. _ _ _ _|_|. _ _ _ # | |(_) | |(_)| | (_)|_|| ||(/_| _\ # # -------------------------------------------------------- # Apply brain masks to functionals # -------------------------------------------------------- # Dilate mask dilatemask = pe.Node(interface=fsl.ImageMaths(suffix='_dil', op_string='-dilF'), name='dilatemask') featpreproc.connect(inputfiles, 'ref_funcmask', dilatemask, 'in_file') featpreproc.connect(dilatemask, 'out_file', outputfiles, 'dilate_mask') funcbrains = pe.MapNode(fsl.BinaryMaths(operation='mul'), iterfield=('in_file', 'operand_file'), name='funcbrains') featpreproc.connect([ (mc, funcbrains, [ ('mc.out_file', 'in_file'), ]), (dilatemask, funcbrains, [ ('out_file', 'operand_file'), ]), (funcbrains, outputfiles, [ ('out_file', 'funcbrains'), ]), ]) # Detect motion outliers # -------------------------------------------------------- import nipype.algorithms.rapidart as ra outliers = pe.MapNode( ra.ArtifactDetect( mask_type='file', use_norm=True, norm_threshold=10.0, # combines translations in mm and rotations zintensity_threshold=3.0, # z-score parameter_source='AFNI', save_plot=True), iterfield=('realigned_files', 'realignment_parameters', 'mask_file'), name='outliers') featpreproc.connect([ ( mc, outliers, [ # ('mc.par_file', 'realignment_parameters'), ('mc.oned_file', 'realignment_parameters'), ]), (funcbrains, outliers, [ ('out_file', 'realigned_files'), ]), (dilatemask, outliers, [ ('out_file', 'mask_file'), ]), ( outliers, outputfiles, [ ('outlier_files', 'motion_outliers.@outlier_files'), ('plot_files', 'motion_outliers.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]), (mc, outputnode, [ ('mc.oned_file', 'motion_parameters'), ]), ( outliers, outputnode, [ ('outlier_files', 'motion_outlier_files'), ('plot_files', 'motion_plots.@plot_files'), ('displacement_files', 'motion_outliers.@displacement_files'), ('intensity_files', 'motion_outliers.@intensity_files'), ('mask_files', 'motion_outliers.@mask_files'), ('statistic_files', 'motion_outliers.@statistic_files'), # ('norm_files', 'outliers.@norm_files'), ]) ]) """ Determine the 2nd and 98th percentile intensities of each functional run """ getthresh = pe.MapNode(interface=fsl.ImageStats(op_string='-p 2 -p 98'), iterfield=['in_file'], name='getthreshold') featpreproc.connect(mc, 'mc.out_file', getthresh, 'in_file') """ Threshold the first run of functional data at 10% of the 98th percentile """ threshold = pe.MapNode(interface=fsl.ImageMaths(out_data_type='char', suffix='_thresh'), iterfield=['in_file', 'op_string'], name='threshold') featpreproc.connect(mc, 'mc.out_file', threshold, 'in_file') """ Define a function to get 10% of the intensity """ def getthreshop(thresh): return ['-thr %.10f -Tmin -bin' % (0.1 * val[1]) for val in thresh] featpreproc.connect(getthresh, ('out_stat', getthreshop), threshold, 'op_string') """ Determine the median value of the functional runs using the mask """ medianval = pe.MapNode(interface=fsl.ImageStats(op_string='-k %s -p 50'), iterfield=['in_file', 'mask_file'], name='medianval') featpreproc.connect(mc, 'mc.out_file', medianval, 'in_file') featpreproc.connect(threshold, 'out_file', medianval, 'mask_file') # (~ _ _ _|_. _ | (~ _ _ _ _ _|_|_ . _ _ # _)|_)(_| | |(_|| _)| | |(_)(_) | | ||| |(_| # | _| # Spatial smoothing (SUSAN) # -------------------------------------------------------- # create_susan_smooth takes care of calculating the mean and median # functional, applying mask to functional, and running the smoothing smooth = create_susan_smooth(separate_masks=False) featpreproc.connect(inputnode, 'fwhm', smooth, 'inputnode.fwhm') featpreproc.connect(mc, 'mc.out_file', smooth, 'inputnode.in_files') featpreproc.connect(dilatemask, 'out_file', smooth, 'inputnode.mask_file') # ------------------------------------------------------- # The below is from workflows/fmri/fsl/preprocess.py """ Mask the smoothed data with the dilated mask """ maskfunc3 = pe.MapNode(interface=fsl.ImageMaths(suffix='_mask', op_string='-mas'), iterfield=['in_file', 'in_file2'], name='maskfunc3') featpreproc.connect(smooth, 'outputnode.smoothed_files', maskfunc3, 'in_file') featpreproc.connect(dilatemask, 'out_file', maskfunc3, 'in_file2') concatnode = pe.Node(interface=util.Merge(2), name='concat') tolist = lambda x: [x] def chooseindex(fwhm): if fwhm < 1: return [0] else: return [1] # maskfunc2 is the functional data before SUSAN featpreproc.connect(mc, ('mc.out_file', tolist), concatnode, 'in1') # maskfunc3 is the functional data after SUSAN featpreproc.connect(maskfunc3, ('out_file', tolist), concatnode, 'in2') """ The following nodes select smooth or unsmoothed data depending on the fwhm. This is because SUSAN defaults to smoothing the data with about the voxel size of the input data if the fwhm parameter is less than 1/3 of the voxel size. """ selectnode = pe.Node(interface=util.Select(), name='select') featpreproc.connect(concatnode, 'out', selectnode, 'inlist') featpreproc.connect(inputnode, ('fwhm', chooseindex), selectnode, 'index') featpreproc.connect(selectnode, 'out', outputfiles, 'smoothed_files') """ Scale the median value of the run is set to 10000. """ meanscale = pe.MapNode(interface=fsl.ImageMaths(suffix='_gms'), iterfield=['in_file', 'op_string'], name='meanscale') featpreproc.connect(selectnode, 'out', meanscale, 'in_file') """ Define a function to get the scaling factor for intensity normalization """ featpreproc.connect(medianval, ('out_stat', getmeanscale), meanscale, 'op_string') # |_|. _ |_ _ _ _ _ # | ||(_|| ||_)(_|_\_\ # _| | # Temporal filtering # -------------------------------------------------------- highpass = pe.MapNode(interface=fsl.ImageMaths(suffix='_tempfilt'), iterfield=['in_file'], name='highpass') highpass_operand = lambda x: '-bptf %.10f -1' % x featpreproc.connect(inputnode, ('highpass', highpass_operand), highpass, 'op_string') featpreproc.connect(meanscale, 'out_file', highpass, 'in_file') version = 0 if fsl.Info.version() and \ LooseVersion(fsl.Info.version()) > LooseVersion('5.0.6'): version = 507 if version < 507: featpreproc.connect(highpass, 'out_file', outputnode, 'highpassed_files') else: """ Add back the mean removed by the highpass filter operation as of FSL 5.0.7 """ meanfunc4 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc4') featpreproc.connect(meanscale, 'out_file', meanfunc4, 'in_file') addmean = pe.MapNode(interface=fsl.BinaryMaths(operation='add'), iterfield=['in_file', 'operand_file'], name='addmean') featpreproc.connect(highpass, 'out_file', addmean, 'in_file') featpreproc.connect(meanfunc4, 'out_file', addmean, 'operand_file') featpreproc.connect(addmean, 'out_file', outputnode, 'highpassed_files') """ Generate a mean functional image from the first run """ meanfunc3 = pe.MapNode(interface=fsl.ImageMaths(op_string='-Tmean', suffix='_mean'), iterfield=['in_file'], name='meanfunc3') featpreproc.connect(meanscale, 'out_file', meanfunc3, 'in_file') featpreproc.connect(meanfunc3, 'out_file', outputfiles, 'mean') featpreproc.connect(meanfunc3, 'out_file', outputnode, 'mean_highpassed') featpreproc.connect(outputnode, 'highpassed_files', outputfiles, 'highpassed_files') return (featpreproc)