def test_morphing(): mne.set_log_level('warning') data_dir = mne.datasets.sample.data_path() subjects_dir = os.path.join(data_dir, 'subjects') sss = datasets._mne_source_space('fsaverage', 'ico-4', subjects_dir) vertices_to = [sss[0]['vertno'], sss[1]['vertno']] ds = datasets.get_mne_sample(-0.1, 0.1, src='ico', sub='index==0', stc=True) stc = ds['stc', 0] morph_mat = mne.compute_morph_matrix('sample', 'fsaverage', stc.vertices, vertices_to, None, subjects_dir) ndvar = ds['src'] morphed_ndvar = morph_source_space(ndvar, 'fsaverage') morphed_stc = mne.morph_data_precomputed('sample', 'fsaverage', stc, vertices_to, morph_mat) assert_array_equal(morphed_ndvar.x[0], morphed_stc.data) morphed_stc_ndvar = load.fiff.stc_ndvar([morphed_stc], 'fsaverage', 'ico-4', subjects_dir, 'dSPM', False, 'src', parc=None) assert_dataobj_equal(morphed_ndvar, morphed_stc_ndvar)
def morph_stc(subject_id): subject = "sub%03d" % subject_id print("processing subject: %s" % subject) data_path = op.join(meg_dir, subject) # Morph STCs morph_mat = None for condition in ('contrast', 'faces_eq', 'scrambled_eq'): stc = mne.read_source_estimate( op.join(data_path, 'mne_dSPM_inverse_highpass-%sHz-%s' % (l_freq, condition)), subject) if morph_mat is None: morph_mat = mne.compute_morph_matrix(subject, 'fsaverage', stc.vertices, fsaverage_vertices, smooth, subjects_dir=subjects_dir, warn=False) morphed = stc.morph_precomputed('fsaverage', fsaverage_vertices, morph_mat) morphed.save( op.join( data_path, 'mne_dSPM_inverse_morph_highpass-%sHz-%s' % (l_freq, condition))) if condition == 'contrast': out = morphed return out
def morph_data(data_array, parameters_cache, vector, subjects_dir): """Morphs the subject brains to fs_average brain. :param data_array: Statistical data obtained from obtain_statistical_data function :param parameters_cache: Statistical parameters cache obtained from obtain_statistical_data function :param vector: Method to perform modelling ('sLORETA' etc.) :param subjects_dir: Directory to the brain model :return: (morphed brain data array) """ # Unpack parameter cache dictionary n_vertices = parameters_cache['n_vertices'] n_times = parameters_cache['n_times'] n_subjects = parameters_cache['n_subjects'] subject_vertices = parameters_cache['subject_vertices'] if vector is True: n_vec = parameters_cache['n_vec'] # Create the fs average vertices fs_ave_vertices = [np.arange(10242), np.arange(10242)] # Add in fs_ave_vertices to parameter_cache parameters_cache['fs_ave_vertices'] = fs_ave_vertices # Compute the morph matrix smooth_int = 20 morph_mat = compute_morph_matrix('subjects', 'fsaverage', subject_vertices, fs_ave_vertices, smooth_int, subjects_dir) n_vertices_fs_ave = morph_mat.shape[0] # morph_mat shape is (20484, 20484) # Reshape in order for dot() to work properly if vector is False: X = data_array.reshape(n_vertices, n_times * n_subjects * 2) # Shape is (20484, 257*12*2) else: X = data_array.reshape( n_vertices * n_vec, n_times * n_subjects * 2) # Shape is (20484*3, 257*12*2) ##### TO DOUBLE CHECK ##### print('Morphing data...') X = morph_mat.dot(X) # morph_mat is a sparse matrix # Reshape into (vertices, times, subjects and conditions) if vector is False: X = X.reshape(n_vertices_fs_ave, n_times, n_subjects, 2) # Shape is (20484, 257, 12, 2) # Reshape into (vertices, vecs, times, subjects and conditions) else: X = X.reshape(n_vertices, n_vec, n_times, n_subjects, 2) # Shape is (20484, 3, 257, 12, 2) return X, parameters_cache
def test_morphing(): mne.set_log_level('warning') sss = datasets._mne_source_space('fsaverage', 'ico-4', subjects_dir) vertices_to = [sss[0]['vertno'], sss[1]['vertno']] ds = datasets.get_mne_sample(-0.1, 0.1, src='ico', sub='index==0', stc=True) stc = ds['stc', 0] morph_mat = mne.compute_morph_matrix('sample', 'fsaverage', stc.vertno, vertices_to, None, subjects_dir) ndvar = ds['src'] morphed_ndvar = morph_source_space(ndvar, 'fsaverage') morphed_stc = mne.morph_data_precomputed('sample', 'fsaverage', stc, vertices_to, morph_mat) assert_array_equal(morphed_ndvar.x[0], morphed_stc.data) morphed_stc_ndvar = load.fiff.stc_ndvar([morphed_stc], 'fsaverage', 'ico-4', subjects_dir, 'src', parc=None) assert_dataobj_equal(morphed_ndvar, morphed_stc_ndvar)
def make_stcs(self, snr = 3.0, method='dSPM', save_to_disk = False, morph=True): """docstring for make_stcs""" morph_status = 'no_morph' lambda2 = 1.0 / snr ** 2.0 pick_ori = None stcs = [] for evoked_object, cond in self.evoked_list: print "Making source estimates for %s." %cond stc = mne.minimum_norm.apply_inverse(evoked_object, self.inverse_solution, method = method, lambda2 = lambda2, pick_ori = pick_ori) if morph == True: morph_status = 'morphed' # create morph map # get vertices to morph to (we'll take the fsaverage vertices) subject_to = 'fsaverage' fs = mne.read_source_spaces(self.subjects_dir + '%s/bem/%s-ico-4-src.fif' % (subject_to, subject_to)) vertices_to = [fs[0]['vertno'], fs[1]['vertno']] # info of the stc we're going to morph is just the present stc subject_from = self.subject stc_from = stc # use the morph function morph_mat = mne.compute_morph_matrix(subject_from, subject_to, vertices_from=stc_from.vertices, vertices_to=vertices_to, subjects_dir=self.subjects_dir) stc = mne.morph_data_precomputed(subject_from, subject_to, stc_from, vertices_to, morph_mat) # stc_to.save('%s_audvis-meg' % subject_from) # mne.compute_morph_matrix('2-COMB','3-COMB',stcs[0].vertices, stcs[5].vertices, subjects_dir=self.subjects_dir) if save_to_disk: fact_morph_cond_path = op.join(self.stc_fact, morph_status, cond) if not os.path.isdir(fact_morph_cond_path): os.makedirs(fact_morph_cond_path) stc.save(op.join(fact_morph_cond_path, '%s_%s_%s' %(self.subject, cond, morph_status))) self.add_preprocessing_notes("Saved source estimates (%s) for %s." %(morph_status, cond))
def make_epoch_stcs(self, epochs, snr = 2.0, method='dSPM', morph=True, save_to_disk = False): """Apply inverse operator to epochs to get source estimates of each item""" lambda2 = 1.0 / snr ** 2.0 inverse = mne.minimum_norm.read_inverse_operator( self.processed_files + self.subject + '_inv.fif' ) eps = mne.minimum_norm.apply_inverse_epochs(epochs=epochs,inverse_operator=inverse,lambda2=lambda2,method = method) if morph == True: eps_morphed = [] counter = 1 morph_status = 'morphed' # create morph map # get vertices to morph to (we'll take the fsaverage vertices) subject_to = 'fsaverage' fs = mne.read_source_spaces(self.subjects_dir + '%s/bem/%s-ico-4-src.fif' % (subject_to, subject_to)) vertices_to = [fs[0]['vertno'], fs[1]['vertno']] subject_from = self.subject for stc_from in eps: print "Morphing source estimate for epoch %d" %counter # use the morph function morph_mat = mne.compute_morph_matrix(subject_from, subject_to, vertices_from=stc_from.vertices, vertices_to=vertices_to, subjects_dir=self.subjects_dir) stc = mne.morph_data_precomputed(subject_from, subject_to, stc_from, vertices_to, morph_mat) # stc = mne.morph_data(subject_from, subject_to, stc_from, n_jobs=1, # grade=vertices_to, subjects_dir=self.subjects_dir) eps_morphed.append(stc) counter += 1 eps = eps_morphed if save_to_disk: with open(op.join(self.stc_cont, '%s_stc_epochs.pickled' %self.subject), 'w') as fileout: pickle.dump(eps, fileout) #save.pickle(obj=eps,dest=self.stc_cont + self.subject + '_stc_epochs') return eps
def test_source_estimate(): "Test SourceSpace dimension" ds = datasets.get_mne_sample(src='ico') dsa = ds.aggregate('side') # test auto-conversion asndvar('epochs', ds=ds) asndvar('epochs', ds=dsa) asndvar(dsa['epochs'][0]) # source space clustering res = testnd.ttest_ind('src', 'side', ds=ds, samples=0, pmin=0.05, tstart=0.05, mintime=0.02, minsource=10) assert_equal(res.clusters.n_cases, 52) # test morphing dsa = ds.aggregate('side') ndvar = dsa['src'] stc = mne.SourceEstimate(ndvar.x[0], ndvar.source.vertno, ndvar.time.tmin, ndvar.time.tstep, ndvar.source.subject) subjects_dir = ndvar.source.subjects_dir path = ndvar.source._src_pattern.format(subject='fsaverage', src=ndvar.source.src, subjects_dir=subjects_dir) if os.path.exists(path): src_to = mne.read_source_spaces(path) else: src_to = mne.setup_source_space('fsaverage', path, 'ico4', subjects_dir=subjects_dir) vertices_to = [src_to[0]['vertno'], src_to[1]['vertno']] mm = mne.compute_morph_matrix('sample', 'fsaverage', ndvar.source.vertno, vertices_to, None, subjects_dir) stc_to = mne.morph_data_precomputed('sample', 'fsaverage', stc, vertices_to, mm) ndvar_m = morph_source_space(ndvar, 'fsaverage') assert_array_equal(ndvar_m.x[0], stc_to.data)
epochs_ds.resample(20) evoked_ds = [epochs_ds[name].average() for name in ['STI-correct', 'STI-incorrect']] # contruct two types of inverse solution: one based on baseline data (before the red square appears), and one based on blank data cov_blank = mne.compute_covariance(epochs_ds['STB'], tmin=0, tmax=None, method='auto') inv_blank = make_inverse_operator(epochs_ds.info, forward, cov_blank, loose=0.2, depth=0.8) blank_idx = np.nonzero(epochs_ds.events[:, 2] == 15)[0] epochs_ds.drop_epochs(blank_idx) cov_base = mne.compute_covariance(epochs_ds, tmin=None, tmax=0, method='auto') inv_base = make_inverse_operator(epochs_ds.info, forward, cov_base, loose=0.2, depth=0.8) vertices_to = [np.arange(10242), np.arange(10242)] vertices_from = [forward['src'][0]['vertno'], forward['src'][1]['vertno']] morph_mat = mne.compute_morph_matrix(subj, 'fsaverage', vertices_from, vertices_to) for c in range(len(conds)): # start with the simplest method, MNE + dSPM stc = apply_inverse(evoked_ds[c], inv_base, lambda2, method) stc = mne.morph_data_precomputed(subj, 'fsaverage', stc, vertices_to, morph_mat) fname = out_dir + '%s_%s_dSPM_base_clean' % (subj, conds[c]) stc.save(fname) stc = apply_inverse(evoked_ds[c], inv_blank, lambda2, method) stc = mne.morph_data_precomputed(subj, 'fsaverage', stc, vertices_to, morph_mat) fname = out_dir + '%s_%s_dSPM_blank_clean' % (subj, conds[c]) stc.save(fname) # the next estimate is LCMV beamformer in time data_cov = mne.compute_covariance(epochs_ds[conds[c]], tmin=0, tmax=None,
print("Simulating data for %d subjects." % n_subjects) # Let's make sure our results replicate, so set the seed. np.random.seed(0) X = randn(n_vertices_sample, n_times, n_subjects, 4) * 10 for ii, condition in enumerate(conditions): X[:, :, :, ii] += condition.lh_data[:, :, np.newaxis] # It's a good idea to spatially smooth the data, and for visualization # purposes, let's morph these to fsaverage, which is a grade 5 source space # with vertices 0:10242 for each hemisphere. Usually you'd have to morph # each subject's data separately (and you might want to use morph_data # instead), but here since all estimates are on 'sample' we can use one # morph matrix for all the heavy lifting. fsave_vertices = [np.arange(10242), np.array([])] # right hemisphere is empty morph_mat = compute_morph_matrix("sample", "fsaverage", sample_vertices, fsave_vertices, 20, subjects_dir) n_vertices_fsave = morph_mat.shape[0] # We have to change the shape for the dot() to work properly X = X.reshape(n_vertices_sample, n_times * n_subjects * 4) print("Morphing data.") X = morph_mat.dot(X) # morph_mat is a sparse matrix X = X.reshape(n_vertices_fsave, n_times, n_subjects, 4) # Now we need to prepare the group matrix for the ANOVA statistic. # To make the clustering function work correctly with the # ANOVA function X needs to be a list of multi-dimensional arrays # (one per condition) of shape: samples (subjects) x time x space X = np.transpose(X, [2, 1, 0, 3]) # First we permute dimensions # finally we split the array into a list a list of conditions
import mne from library.config import (subjects_dir, meg_dir, exclude_subjects, smooth, fsaverage_vertices, l_freq) stcs = list() for subject_id in range(1, 20): if subject_id in exclude_subjects: continue subject = "sub%03d" % subject_id print("processing subject: %s" % subject) data_path = op.join(meg_dir, subject) stc = mne.read_source_estimate( op.join(data_path, 'mne_LCMV_inverse_highpass-%sHz-contrast' % l_freq), subject) morph_mat = mne.compute_morph_matrix(subject, 'fsaverage', stc.vertices, fsaverage_vertices, smooth, subjects_dir=subjects_dir, warn=False) morphed = stc.morph_precomputed('fsaverage', fsaverage_vertices, morph_mat) stcs.append(morphed) data = np.average([s.data for s in stcs], axis=0) stc = mne.SourceEstimate(data, stcs[0].vertices, stcs[0].tmin, stcs[0].tstep) stc.save(op.join(meg_dir, 'contrast-average-lcmv_highpass-%sHz' % l_freq))
def morph_source_space(ndvar, subject_to, vertices_to=None, morph_mat=None, copy=False): """Morph source estimate to a different MRI subject Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. subject_to : string Name of the subject on which to morph. vertices_to : None | list of array of int The vertices on the destination subject's brain. If ndvar contains a whole source space, vertices_to can be automatically loaded, although providing them as argument can speed up processing by a second or two. morph_mat : None | sparse matrix The morphing matrix. If ndvar contains a whole source space, the morph matrix can be automatically loaded, although providing a cached matrix can speed up processing by a second or two. copy : bool Make sure that the data of ``morphed_ndvar`` is separate from ``ndvar`` (default False). Returns ------- morphed_ndvar : NDVar NDVar morphed to the destination subject. Notes ----- This function is used to make sure a number of different NDVars are defined on the same MRI subject and handles scaled MRIs efficiently. If the MRI subject on which ``ndvar`` is defined is a scaled copy of ``subject_to``, by default a shallow copy of ``ndvar`` is returned. That means that it is not safe to assume that ``morphed_ndvar`` can be modified in place without altering ``ndvar``. To make sure the date of the output is independent from the data of the input, set the argument ``copy=True``. """ subjects_dir = ndvar.source.subjects_dir subject_from = ndvar.source.subject src = ndvar.source.src if vertices_to is None: path = SourceSpace._src_pattern.format(subjects_dir=subjects_dir, subject=subject_to, src=src) src_to = mne.read_source_spaces(path) vertices_to = [src_to[0]['vertno'], src_to[1]['vertno']] elif not isinstance(vertices_to, list) or not len(vertices_to) == 2: raise ValueError('vertices_to must be a list of length 2') if subject_from == subject_to and _vertices_equal(ndvar.source.vertno, vertices_to): if copy: return ndvar.copy() else: return ndvar axis = ndvar.get_axis('source') x = ndvar.x # check whether it is a scaled brain do_morph = True cfg_path = os.path.join(subjects_dir, subject_from, 'MRI scaling parameters.cfg') if os.path.exists(cfg_path): cfg = mne.coreg.read_mri_cfg(subject_from, subjects_dir) subject_from = cfg['subject_from'] if subject_to == subject_from and _vertices_equal(ndvar.source.vertno, vertices_to): if copy: x_ = x.copy() else: x_ = x vertices_to = ndvar.source.vertno do_morph = False if do_morph: vertices_from = ndvar.source.vertno if morph_mat is None: morph_mat = mne.compute_morph_matrix(subject_from, subject_to, vertices_from, vertices_to, None, subjects_dir) elif not sp.sparse.issparse(morph_mat): raise ValueError('morph_mat must be a sparse matrix') elif not sum(len(v) for v in vertices_to) == morph_mat.shape[0]: raise ValueError('morph_mat.shape[0] must match number of ' 'vertices in vertices_to') # flatten data if axis != 0: x = x.swapaxes(0, axis) n_sources = len(x) if not n_sources == morph_mat.shape[1]: raise ValueError('ndvar source dimension length must be the same ' 'as morph_mat.shape[0]') if ndvar.ndim > 2: shape = x.shape x = x.reshape((n_sources, -1)) # apply morph matrix x_ = morph_mat * x # restore data shape if ndvar.ndim > 2: shape_ = (len(x_),) + shape[1:] x_ = x_.reshape(shape_) if axis != 0: x_ = x_.swapaxes(axis, 0) # package output NDVar if ndvar.source.parc is None: parc = None else: parc = ndvar.source.parc.name source = SourceSpace(vertices_to, subject_to, src, subjects_dir, parc) dims = ndvar.dims[:axis] + (source,) + ndvar.dims[axis + 1:] info = ndvar.info.copy() out = NDVar(x_, dims, info, ndvar.name) return out
# vertices_to = mne.grade_to_vertices(subject_to, grade=5) # But fsaverage's source space was set up so we can just do this: vertices_to = [np.arange(10242), np.arange(10242)] stc_to = mne.morph_data(subject_from, subject_to, stc_from, n_jobs=1, grade=vertices_to, subjects_dir=subjects_dir) stc_to.save('%s_audvis-meg' % subject_to) # Morph using another method -- useful if you're going to do a lot of the # same inter-subject morphing operations; you could save and load morph_mat morph_mat = mne.compute_morph_matrix(subject_from, subject_to, stc_from.vertices, vertices_to, subjects_dir=subjects_dir) stc_to_2 = mne.morph_data_precomputed(subject_from, subject_to, stc_from, vertices_to, morph_mat) stc_to_2.save('%s_audvis-meg_2' % subject_to) # View source activations plt.plot(stc_from.times, stc_from.data.mean(axis=0), 'r', label='from') plt.plot(stc_to.times, stc_to.data.mean(axis=0), 'b', label='to') plt.plot(stc_to_2.times, stc_to.data.mean(axis=0), 'g', label='to_2') plt.xlabel('time (ms)') plt.ylabel('Mean Source amplitude') plt.legend() plt.show()
def stft_source_localization(data, fn_inv, method="dSPM", morph2fsaverage=False, nave=1, snr=3., pick_ori="normal", verbose=True): """ Apply inverse operator to data. In general, the L2-norm inverse solution is computed. Parameters ---------- data: array of MEG data (only the data, no MNE-data object!) kernel: kernel of the inverse operator. (could be estimated using mne.minimum_norm._assemble_kernel()) noise_norm: noise normalization array. (could be estimated using mne.minimum_norm._assemble_kernel()) Returns ------- src_loc: SourceEstimate | VolSourceEstimate The source estimates estimation_time: time needed to perform source localization (for one time slice) """ # ------------------------------------------- # import necessary modules # ------------------------------------------- import numpy as np import types # check if data should be morphed if morph2fsaverage: from mne import compute_morph_matrix, grade_to_vertices, morph_data_precomputed from mne.source_estimate import SourceEstimate from os.path import basename, dirname # ------------------------------------------- # estimate inverse kernel # ------------------------------------------- kernel, noise_norm, vertno = calc_inv_kernel(fn_inv, method=method, nave=nave, snr=snr, pick_ori=pick_ori) # ------------------------------------------- # get some information from the # input data # ------------------------------------------- nfreq, nepochs, nchan = data.shape nvoxel = noise_norm.shape[0] if isinstance(data[0, 0, 0], types.ComplexType): src_loc_data = np.zeros((nfreq, nepochs, nvoxel), dtype=np.complex) else: src_loc_data = np.zeros((nfreq, nepochs, nvoxel)) # ------------------------------------------- # read in morphing matrix # ------------------------------------------- if morph2fsaverage: subject_id = basename(fn_inv)[:6] subjects_dir = dirname(dirname(fn_inv)) vertices_to = grade_to_vertices('fsaverage', grade=4, subjects_dir=subjects_dir) morph_mat = compute_morph_matrix(subject_id, 'fsaverage', vertno, vertices_to, subjects_dir=subjects_dir) nvoxel_morph = 2 * len(vertices_to[0]) if isinstance(data[0, 0, 0], types.ComplexType): morphed_src_loc_data = np.zeros((nfreq, nepochs, nvoxel_morph), dtype=np.complex) else: morphed_src_loc_data = np.zeros((nfreq, nepochs, nvoxel_morph), dtype=np.complex) # ------------------------------------------- # apply inverse operator for each time slice # ------------------------------------------- for iepoch in range(nepochs): if verbose: from sys import stdout info = "\r" if iepoch > 0 else "" info += "... --> Epoch %d of %d done" % (iepoch+1, nepochs) stdout.write(info) stdout.flush() for ifreq in range(0, nfreq): # multiply measured data with inverse kernel loc_tmp = np.dot(kernel, data[ifreq, iepoch, :]) if pick_ori != "normal": # estimate L2-norm and apply noise normalization src_loc_data[ifreq, iepoch, :] = loc_tmp[0::3].real ** 2 + 1j * loc_tmp[0::3].imag ** 2 src_loc_data[ifreq, iepoch, :] += loc_tmp[1::3].real ** 2 + 1j * loc_tmp[1::3].imag ** 2 src_loc_data[ifreq, iepoch, :] += loc_tmp[2::3].real ** 2 + 1j * loc_tmp[2::3].imag ** 2 src_loc_data[ifreq, iepoch, :] = (np.sqrt(src_loc_data[ifreq, iepoch, :].real) + 1j * np.sqrt(src_loc_data[ifreq, iepoch, :].imag)) else: src_loc_data[ifreq, iepoch, :] = loc_tmp src_loc_data[ifreq, iepoch, :] *= noise_norm[:, 0] if morph2fsaverage: SrcEst = SourceEstimate(src_loc_data[:, iepoch, :].T, vertno, 0, 1, verbose=verbose) SrcEst_morphed = morph_data_precomputed(subject_id, 'fsaverage', SrcEst, vertices_to, morph_mat) morphed_src_loc_data[:, iepoch, :] = SrcEst_morphed.data.T if verbose: print "" if morph2fsaverage: src_loc_data = morphed_src_loc_data vertno = vertices_to return src_loc_data, vertno
def morph_source_space(ndvar, subject_to=None, vertices_to=None, morph_mat=None, copy=False, parc=True, xhemi=False, mask=None): """Morph source estimate to a different MRI subject Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. subject_to : str Name of the subject on which to morph (by default this is the same as the current subject for ``xhemi`` morphing). vertices_to : None | list of array of int | 'lh' | 'rh' The vertices on the destination subject's brain. If ndvar contains a whole source space, vertices_to can be automatically loaded, although providing them as argument can speed up processing by a second or two. Use 'lh' or 'rh' to target vertices from only one hemisphere. morph_mat : None | sparse matrix The morphing matrix. If ndvar contains a whole source space, the morph matrix can be automatically loaded, although providing a cached matrix can speed up processing by a second or two. copy : bool Make sure that the data of ``morphed_ndvar`` is separate from ``ndvar`` (default False). parc : bool | str Parcellation for target source space. The default is to keep the parcellation from ``ndvar``. Set to ``False`` to load no parcellation. If the annotation files are missing for the target subject an IOError is raised. xhemi : bool Mirror hemispheres (i.e., project data from the left hemisphere to the right hemisphere and vice versa). mask : bool Restrict output to known sources. If the parcellation of ``ndvar`` is retained keep only sources with labels contained in ``ndvar``, otherwise remove only sourves with ``”unknown-*”`` label (default is True unless ``vertices_to`` is specified). Returns ------- morphed_ndvar : NDVar NDVar morphed to the destination subject. Notes ----- This function is used to make sure a number of different NDVars are defined on the same MRI subject and handles scaled MRIs efficiently. If the MRI subject on which ``ndvar`` is defined is a scaled copy of ``subject_to``, by default a shallow copy of ``ndvar`` is returned. That means that it is not safe to assume that ``morphed_ndvar`` can be modified in place without altering ``ndvar``. To make sure the date of the output is independent from the data of the input, set the argument ``copy=True``. """ axis = ndvar.get_axis('source') source = ndvar.get_dim('source') subjects_dir = source.subjects_dir subject_from = source.subject if subject_to is None: subject_to = subject_from else: assert_subject_exists(subject_to, subjects_dir) # catch cases that don't require morphing if not xhemi: subject_is_same = subject_from == subject_to subject_is_scaled = find_source_subject(subject_to, subjects_dir) == subject_from or find_source_subject(subject_from, subjects_dir) == subject_to if subject_is_same or subject_is_scaled: if vertices_to is None: pass elif vertices_to in ('lh', 'rh'): ndvar = ndvar.sub(source=vertices_to) elif isinstance(vertices_to, str): raise ValueError(f"vertices_to={vertices_to!r}") else: raise TypeError(f"vertices_to={vertices_to!r}") x = ndvar.x.copy() if copy else ndvar.x parc_arg = None if parc is True else parc if subject_is_scaled or parc_arg is not None: source_to = source._copy(subject_to, parc=parc_arg) else: source_to = source dims = (*ndvar.dims[:axis], source_to, *ndvar.dims[axis + 1:]) return NDVar(x, dims, ndvar.info, ndvar.name) has_lh_out = bool(source.rh_n if xhemi else source.lh_n) has_rh_out = bool(source.lh_n if xhemi else source.rh_n) if vertices_to in (None, 'lh', 'rh'): default_vertices = source_space_vertices(source.kind, source.grade, subject_to, subjects_dir) lh_out = vertices_to == 'lh' or (vertices_to is None and has_lh_out) rh_out = vertices_to == 'rh' or (vertices_to is None and has_rh_out) vertices_to = [default_vertices[0] if lh_out else np.empty(0, int), default_vertices[1] if rh_out else np.empty(0, int)] if mask is None: if source.parc is None: mask = False else: mask = not np.any(source.parc.startswith('unknown-')) elif not isinstance(vertices_to, list) or not len(vertices_to) == 2: raise ValueError(f"vertices_to={vertices_to!r}: must be a list of length 2") # check that requested data is available n_to_lh = len(vertices_to[0]) n_to_rh = len(vertices_to[1]) if n_to_lh and not has_lh_out: raise ValueError("Data on the left hemisphere was requested in vertices_to but is not available in ndvar") elif n_to_rh and not has_rh_out: raise ValueError("Data on the right hemisphere was requested in vertices_to but is not available in ndvar") elif n_to_lh == 0 and n_to_rh == 0: raise ValueError("No target vertices") # parc for new source space if parc is True: parc_to = None if source.parc is None else source.parc.name else: parc_to = parc if mask and parc_to is None: raise ValueError("Can't mask source space without parcellation...") # check that annot files are available if parc_to: fname = SourceSpace._ANNOT_PATH.format( subjects_dir=subjects_dir, subject=subject_to, hemi='%s', parc=parc_to) fnames = tuple(fname % hemi for hemi in ('lh', 'rh')) missing = tuple(fname for fname in fnames if not os.path.exists(fname)) if missing: missing = '\n'.join(missing) raise IOError(f"Annotation files are missing for parc={parc_to!r}, subject={subject_to!r}. Use the parc parameter when morphing to set a different parcellation. The following files are missing:\n{missing}") # find target source space source_to = SourceSpace(vertices_to, subject_to, source.src, subjects_dir, parc_to) if mask is True: if parc is True: keep_labels = source.parc.cells if xhemi: keep_labels = [switch_hemi_tag(label) for label in keep_labels] index = source_to.parc.isin(keep_labels) else: index = source_to.parc.isnotin(('unknown-lh', 'unknown-rh')) source_to = source_to[index] elif mask not in (None, False): raise TypeError(f"mask={mask!r}") if morph_mat is None: with warnings.catch_warnings(): warnings.filterwarnings('ignore', r'\d+/\d+ vertices not included in smoothing', module='mne') morph_mat = mne.compute_morph_matrix(subject_from, subject_to, source.vertices, source_to.vertices, None, subjects_dir, xhemi=xhemi) elif not sp.sparse.issparse(morph_mat): raise ValueError('morph_mat must be a sparse matrix') elif not sum(len(v) for v in source_to.vertices) == morph_mat.shape[0]: raise ValueError('morph_mat.shape[0] must match number of vertices in vertices_to') # flatten data x = ndvar.x if axis != 0: x = x.swapaxes(0, axis) n_sources = len(x) if not n_sources == morph_mat.shape[1]: raise ValueError('ndvar source dimension length must be the same as morph_mat.shape[0]') if ndvar.ndim > 2: shape = x.shape x = x.reshape((n_sources, -1)) # apply morph matrix x_m = morph_mat * x # restore data shape if ndvar.ndim > 2: shape_ = (len(x_m),) + shape[1:] x_m = x_m.reshape(shape_) if axis != 0: x_m = x_m.swapaxes(axis, 0) # package output NDVar dims = (*ndvar.dims[:axis], source_to, *ndvar.dims[axis + 1:]) return NDVar(x_m, dims, ndvar.info, ndvar.name)
cov = mne.compute_covariance(epochs_rej,tmin=None,tmax=0, method=['shrunk', 'diagonal_fixed', 'empirical']) cov.save(cov_fname) print 'Done. File saved.' #---------------------Inverse operator-------------------------# print 'Getting inverse operator' if fixed == True: fwd = mne.convert_forward_solution(fwd, surf_ori=True) inv = mne.minimum_norm.make_inverse_operator(info, fwd, cov, depth=None, loose=None, fixed=fixed) #fixed=False: Ignoring dipole direction. lambda2 = 1.0 / SNR ** 2.0 #--------------------------STCs--------------------------------# print '%s: Creating STCs...'%subj os.makedirs('STC/%s' %subj) for ev in evoked: stc = mne.minimum_norm.apply_inverse(ev, inv, lambda2=lambda2, method='dSPM') # mophing stcs to the fsaverage using precomputed matrix method: vertices_to = mne.grade_to_vertices('fsaverage', grade=4, subjects_dir=subjects_dir) #fsaverage's source space morph_mat = mne.compute_morph_matrix(subject_from=subj, subject_to='fsaverage', vertices_from=stc.vertices, vertices_to=vertices_to, subjects_dir=subjects_dir) stc_morph = mne.morph_data_precomputed(subject_from=subj, subject_to='fsaverage', stc_from=stc, vertices_to=vertices_to, morph_mat=morph_mat) stc_morph.save('STC/%s/%s_%s_dSPM' %(subj,subj,ev.comment)) del stc, stc_morph print '>> DONE CREATING STCS FOR SUBJ=%s'%subj print '-----------------------------------------\n' #deleting variables del epochs_rej, evoked, info, trans, src, fwd, cov, inv
continue raw_fname = paths('sss', subject=meg_subject, block=1) inv_fname = paths('inv', subject=meg_subject) cov = load('cov', subject=meg_subject) fwd = load('fwd', subject=meg_subject) info = read_info(raw_fname) inv = make_inverse_operator(info, fwd, cov, loose=0.2, depth=0.8) save(inv, 'inv', subject=meg_subject, overwrite=True) # Morph anatomy into common model --------------------------------------------- from mne import EvokedArray from mne import compute_morph_matrix from mne.minimum_norm import apply_inverse if True: for meg_subject, subject in zip(range(1, 21), subjects_id): if subject in bad_mri: continue raw_fname = paths('sss', subject=meg_subject, block=1) info = read_info(raw_fname) # precompute morphing matrix for faster processing inv = load('inv', subject=meg_subject) evoked = EvokedArray(np.zeros((len(info['chs']), 2)), info, 0) evoked.pick_types(eeg=False, meg=True) stc = apply_inverse(evoked, inv, lambda2=1.0 / (2 ** 3.0), method='dSPM', pick_ori=None) morph = compute_morph_matrix(subject, 'fsaverage', stc.vertices, vertices_to=[np.arange(10242)] * 2, subjects_dir=subjects_dir) save(morph, 'morph', subject=meg_subject)
time_unit='s') brain.add_foci(vertno_max, coords_as_verts=True, hemi='rh', color='blue', scale_factor=0.6) brain.show_view('lateral') ############################################################################### # Morph data to average brain # --------------------------- fs_vertices = [np.arange(10242)] * 2 morph_mat = mne.compute_morph_matrix('sample', 'fsaverage', stc.vertices, fs_vertices, smooth=None, subjects_dir=subjects_dir) stc_fsaverage = stc.morph_precomputed('fsaverage', fs_vertices, morph_mat) brain_fsaverage = stc_fsaverage.plot(surface='inflated', hemi='rh', subjects_dir=subjects_dir, clim=dict(kind='value', lims=[8, 12, 15]), initial_time=time_max, time_unit='s') brain_fsaverage.show_view('lateral') ############################################################################### # Exercise # -------- # - By changing the method parameter to 'sloreta' recompute the source
src_fname = data_path + '/MEG/sample/sample_audvis-meg-oct-6-fwd.fif' # Read input stc file stc_from = mne.read_source_estimate(fname) # Morph using one method (supplying the vertices in fsaverage's source # space makes it faster). Note that for any generic subject, you could do: # vertices_to = mne.grade_to_vertices(subject_to, grade=5) # But fsaverage's source space was set up so we can just do this: vertices_to = [np.arange(10242), np.arange(10242)] stc_to = mne.morph_data(subject_from, subject_to, stc_from, n_jobs=1, grade=vertices_to, subjects_dir=subjects_dir) stc_to.save('%s_audvis-meg' % subject_to) # Morph using another method -- useful if you're going to do a lot of the # same inter-subject morphing operations; you could save and load morph_mat morph_mat = mne.compute_morph_matrix(subject_from, subject_to, stc_from.vertices, vertices_to, subjects_dir=subjects_dir) stc_to_2 = mne.morph_data_precomputed(subject_from, subject_to, stc_from, vertices_to, morph_mat) stc_to_2.save('%s_audvis-meg_2' % subject_to) # View source activations plt.plot(stc_from.times, stc_from.data.mean(axis=0), 'r', label='from') plt.plot(stc_to.times, stc_to.data.mean(axis=0), 'b', label='to') plt.plot(stc_to_2.times, stc_to.data.mean(axis=0), 'g', label='to_2') plt.xlabel('time (ms)') plt.ylabel('Mean Source amplitude') plt.legend() plt.show()
info = read_info(raw_fname) inv = make_inverse_operator(info, fwd, cov, loose=0.2, depth=0.8) save(inv, 'inv', subject=meg_subject, overwrite=True) # Morph anatomy into common model --------------------------------------------- from mne import EvokedArray from mne import compute_morph_matrix from mne.minimum_norm import apply_inverse if True: for meg_subject, subject in zip(range(1, 21), subjects_id): if subject in bad_mri: continue raw_fname = paths('sss', subject=meg_subject, block=1) info = read_info(raw_fname) # precompute morphing matrix for faster processing inv = load('inv', subject=meg_subject) evoked = EvokedArray(np.zeros((len(info['chs']), 2)), info, 0) evoked.pick_types(eeg=False, meg=True) stc = apply_inverse(evoked, inv, lambda2=1.0 / (2**3.0), method='dSPM', pick_ori=None) morph = compute_morph_matrix(subject, 'fsaverage', stc.vertices, vertices_to=[np.arange(10242)] * 2, subjects_dir=subjects_dir) save(morph, 'morph', subject=meg_subject)
data_dir = mne.datasets.sample.data_path() subjects_dir = data_dir + '/subjects' stc_path = data_dir + '/MEG/sample/sample_audvis-meg-eeg' stc = mne.read_source_estimate(stc_path, 'sample') # First, morph the data to fsaverage_sym, for which we have left_right # registrations: stc = stc.morph('fsaverage_sym', subjects_dir=subjects_dir, smooth=5) # Compute a morph-matrix mapping the right to the left hemisphere. Use the # vertices parameters to determine source and target hemisphere: mm = mne.compute_morph_matrix( 'fsaverage_sym', 'fsaverage_sym', xhemi=True, # cross-hemisphere morphing vertices_from=[[], stc.vertices[1]], # from the right hemisphere vertices_to=[stc.vertices[0], []], # to the left hemisphere subjects_dir=subjects_dir) # SourceEstimate on the left hemisphere: stc_lh = mne.SourceEstimate(stc.lh_data, [stc.vertices[0], []], stc.tmin, stc.tstep, stc.subject) # SourceEstimate of the right hemisphere, morphed to the left: stc_rh_on_lh = mne.SourceEstimate(mm * stc.rh_data, [stc.vertices[0], []], stc.tmin, stc.tstep, stc.subject) # Since both STCs are now on the same hemisphere we can subtract them: diff = stc_lh - stc_rh_on_lh diff.plot(hemi='lh', subjects_dir=subjects_dir,
def morph_source_space(ndvar, subject_to, vertices_to=None, morph_mat=None, copy=False, parc=True, xhemi=False, mask=None): """Morph source estimate to a different MRI subject Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. subject_to : string Name of the subject on which to morph. vertices_to : None | list of array of int | 'lh' | 'rh' The vertices on the destination subject's brain. If ndvar contains a whole source space, vertices_to can be automatically loaded, although providing them as argument can speed up processing by a second or two. Use 'lh' or 'rh' to target vertices from only one hemisphere. morph_mat : None | sparse matrix The morphing matrix. If ndvar contains a whole source space, the morph matrix can be automatically loaded, although providing a cached matrix can speed up processing by a second or two. copy : bool Make sure that the data of ``morphed_ndvar`` is separate from ``ndvar`` (default False). parc : bool | str Parcellation for target source space. The default is to keep the parcellation from ``ndvar``. Set to ``False`` to load no parcellation. If the annotation files are missing for the target subject an IOError is raised. xhemi : bool Mirror hemispheres (i.e., project data from the left hemisphere to the right hemisphere and vice versa). mask : bool Remove sources in "unknown-" labels (default is True unless ``ndvar`` contains sources with "unknown-" label or ``vertices_to`` is specified). Returns ------- morphed_ndvar : NDVar NDVar morphed to the destination subject. Notes ----- This function is used to make sure a number of different NDVars are defined on the same MRI subject and handles scaled MRIs efficiently. If the MRI subject on which ``ndvar`` is defined is a scaled copy of ``subject_to``, by default a shallow copy of ``ndvar`` is returned. That means that it is not safe to assume that ``morphed_ndvar`` can be modified in place without altering ``ndvar``. To make sure the date of the output is independent from the data of the input, set the argument ``copy=True``. """ source = ndvar.get_dim('source') subjects_dir = source.subjects_dir subject_from = source.subject src = source.src has_lh_out = bool(source.rh_n if xhemi else source.lh_n) has_rh_out = bool(source.lh_n if xhemi else source.rh_n) assert_subject_exists(subject_to, subjects_dir) if vertices_to in (None, 'lh', 'rh'): default_vertices = source_space_vertices(source.kind, source.grade, subject_to, subjects_dir) lh_out = vertices_to == 'lh' or (vertices_to is None and has_lh_out) rh_out = vertices_to == 'rh' or (vertices_to is None and has_rh_out) vertices_to = [ default_vertices[0] if lh_out else np.empty(0, int), default_vertices[1] if rh_out else np.empty(0, int) ] if mask is None: if source.parc is None: mask = False else: mask = not np.any(source.parc.startswith('unknown-')) elif not isinstance(vertices_to, list) or not len(vertices_to) == 2: raise ValueError('vertices_to must be a list of length 2, got %r' % (vertices_to, )) # check that requested data is available n_to_lh = len(vertices_to[0]) n_to_rh = len(vertices_to[1]) if n_to_lh and not has_lh_out: raise ValueError("Data on the left hemisphere was requested in " "vertices_to but is not available in ndvar") elif n_to_rh and not has_rh_out: raise ValueError("Data on the right hemisphere was requested in " "vertices_to but is not available in ndvar") elif n_to_lh == 0 and n_to_rh == 0: raise ValueError("No target vertices") # parc for new source space if parc is True: parc_to = None if source.parc is None else source.parc.name else: parc_to = parc if mask and parc_to is None: raise ValueError("Can't mask source space without parcellation...") # check that annot files are available if parc_to: fname = SourceSpace._ANNOT_PATH.format(subjects_dir=subjects_dir, subject=subject_to, hemi='%s', parc=parc_to) fnames = tuple(fname % hemi for hemi in ('lh', 'rh')) missing = tuple(fname for fname in fnames if not os.path.exists(fname)) if missing: raise IOError( "Annotation files are missing for parc=%r for target subject " "%s. Use the parc parameter to change the parcellation. The " "following files are missing:\n%s" % (parc_to, subject_to, '\n'.join(missing))) # catch in == out if subject_from == subject_to and _vertices_equal(source.vertices, vertices_to): if copy: ndvar = ndvar.copy() if parc is not True: set_parc(ndvar, parc) return ndvar # find target source space source_to = SourceSpace(vertices_to, subject_to, src, subjects_dir, parc_to) if mask is True: index = np.invert(source_to.parc.startswith('unknown-')) source_to = source_to[index] elif mask not in (None, False): raise TypeError("mask=%r" % (mask, )) axis = ndvar.get_axis('source') x = ndvar.x # check whether it is a scaled brain do_morph = True if not xhemi: cfg_path = os.path.join(subjects_dir, subject_from, 'MRI scaling parameters.cfg') if os.path.exists(cfg_path): cfg = mne.coreg.read_mri_cfg(subject_from, subjects_dir) subject_from = cfg['subject_from'] if (subject_to == subject_from and _vertices_equal(source_to.vertices, source.vertices)): if copy: x_ = x.copy() else: x_ = x do_morph = False if do_morph: if morph_mat is None: with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', '\d+/\d+ vertices not included in smoothing', module='mne') morph_mat = mne.compute_morph_matrix(subject_from, subject_to, source.vertices, source_to.vertices, None, subjects_dir, xhemi=xhemi) elif not sp.sparse.issparse(morph_mat): raise ValueError('morph_mat must be a sparse matrix') elif not sum(len(v) for v in source_to.vertices) == morph_mat.shape[0]: raise ValueError('morph_mat.shape[0] must match number of ' 'vertices in vertices_to') # flatten data if axis != 0: x = x.swapaxes(0, axis) n_sources = len(x) if not n_sources == morph_mat.shape[1]: raise ValueError('ndvar source dimension length must be the same ' 'as morph_mat.shape[0]') if ndvar.ndim > 2: shape = x.shape x = x.reshape((n_sources, -1)) # apply morph matrix x_ = morph_mat * x # restore data shape if ndvar.ndim > 2: shape_ = (len(x_), ) + shape[1:] x_ = x_.reshape(shape_) if axis != 0: x_ = x_.swapaxes(axis, 0) # package output NDVar dims = ndvar.dims[:axis] + (source_to, ) + ndvar.dims[axis + 1:] info = ndvar.info.copy() return NDVar(x_, dims, info, ndvar.name)
def xhemi(ndvar, mask=None, hemi='lh', parc=True): """Project data from both hemispheres to ``hemi`` of fsaverage_sym Project data from both hemispheres to the same hemisphere for interhemisphere comparisons. The fsaverage_sym brain is a symmetric version of fsaverage to facilitate interhemisphere comparisons. It is included with FreeSurfer > 5.1 and can be obtained as described `here <http://surfer.nmr.mgh.harvard.edu/fswiki/Xhemi>`_. For statistical comparisons between hemispheres, use of the symmetric ``fsaverage_sym`` model is recommended to minimize bias [1]_. Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. mask : bool Remove sources in "unknown-" labels (default is True unless ``ndvar`` contains sources with "unknown-" label). hemi : 'lh' | 'rh' Hemisphere onto which to morph the data. parc : bool | str Parcellation for target source space; True to use same as in ``ndvar`` (default). Returns ------- lh : NDVAr Data from the left hemisphere on ``hemi`` of ``fsaverage_sym``. rh : NDVar Data from the right hemisphere on ``hemi`` of ``fsaverage_sym``. References ---------- .. [1] Greve D. N., Van der Haegen L., Cai Q., Stufflebeam S., Sabuncu M. R., Fischl B., Brysbaert M. A Surface-based Analysis of Language Lateralization and Cortical Asymmetry. Journal of Cognitive Neuroscience 25(9), 1477-1492, 2013. """ other_hemi = 'rh' if hemi == 'lh' else 'lh' if ndvar.source.subject == 'fsaverage_sym': ndvar_sym = ndvar else: ndvar_sym = morph_source_space(ndvar, 'fsaverage_sym', parc=parc, mask=mask) vert_lh, vert_rh = ndvar_sym.source.vertices vert_from = [[], vert_rh] if hemi == 'lh' else [vert_lh, []] vert_to = [vert_lh, []] if hemi == 'lh' else [[], vert_rh] with warnings.catch_warnings(): warnings.filterwarnings('ignore', '\d+/\d+ vertices not included in smoothing', module='mne') morph_mat = mne.compute_morph_matrix( 'fsaverage_sym', 'fsaverage_sym', vert_from, vert_to, subjects_dir=ndvar.source.subjects_dir, xhemi=True) out_same = ndvar_sym.sub(source=hemi) out_other = morph_source_space(ndvar_sym.sub(source=other_hemi), 'fsaverage_sym', out_same.source.vertices, morph_mat, parc=parc, xhemi=True, mask=mask) if hemi == 'lh': return out_same, out_other else: return out_other, out_same
evokeds = [ mne.read_evokeds(op.join(data_path, '%s' % s, 'inverse', 'Conditions_80-sss_eq_%s-ave.fif' % s), condition=c) for c in conditions ] stcs = [ apply_inverse(e, inv, lambda2, method=method, pick_ori=None) for e in evokeds ] if not op.isdir(op.join(data_path, '%s' % s, 'stc')): os.mkdir(op.join(data_path, '%s' % s, 'stc')) for j, stc in enumerate(stcs): stc.save( op.join(data_path, '%s' % s, 'stc', '%s_' % s + evokeds[j].comment)) m = mne.compute_morph_matrix(s, 'BBC_249', stcs[0].vertices, source_verts) morphed_stcs = [ stcz.morph_precomputed('BBC_249-5-src.fif', source_verts, m) for stcz in stcs ] for j, stc in enumerate(morphed_stcs): stc.save( op.join(data_path, '%s' % s, 'stc', '%s_BBC_249_morph_' % s + evokeds[j].comment)) if si == 0: data = np.empty( (len(morphed_stcs), len(subj), morphed_stcs[0].shape[0], morphed_stcs[0].shape[1])) # conditions X subjs X space X time for k, stc in enumerate(morphed_stcs): data[k, si] = stc.data # test = mne.SourceEstimate(data[0].squeeze(), vertices=source_verts,
vertno_max, time_max = stc.get_peak(hemi='rh') subjects_dir = data_path + '/subjects' brain = stc.plot(surface='inflated', hemi='rh', subjects_dir=subjects_dir, clim=dict(kind='value', lims=[8, 12, 15]), initial_time=time_max, time_unit='s') brain.add_foci(vertno_max, coords_as_verts=True, hemi='rh', color='blue', scale_factor=0.6) brain.show_view('lateral') ############################################################################### # Morph data to average brain # --------------------------- fs_vertices = [np.arange(10242)] * 2 morph_mat = mne.compute_morph_matrix('sample', 'fsaverage', stc.vertices, fs_vertices, smooth=None, subjects_dir=subjects_dir) stc_fsaverage = stc.morph_precomputed('fsaverage', fs_vertices, morph_mat) brain_fsaverage = stc_fsaverage.plot(surface='inflated', hemi='rh', subjects_dir=subjects_dir, clim=dict(kind='value', lims=[8, 12, 15]), initial_time=time_max, time_unit='s') brain_fsaverage.show_view('lateral') ############################################################################### # Exercise # -------- # - By changing the method parameter to 'sloreta' recompute the source # estimates using the sLORETA method.
src_fname = data_path + '/MEG/sample/sample_audvis-meg-oct-6-fwd.fif' # Read input stc file stc_from = mne.read_source_estimate(fname) # Morph using one method (supplying the vertices in fsaverage's source # space makes it faster). Note that for any generic subject, you could do: # vertices_to = mne.grade_to_vertices(subject_to, grade=5) # But fsaverage's source space was set up so we can just do this: vertices_to = [np.arange(10242), np.arange(10242)] stc_to = mne.morph_data(subject_from, subject_to, stc_from, n_jobs=1, grade=vertices_to) stc_to.save('%s_audvis-meg' % subject_to) # Morph using another method -- useful if you're going to do a lot of the # same inter-subject morphing operations; you could save and load morph_mat morph_mat = mne.compute_morph_matrix(subject_from, subject_to, stc_from.vertno, vertices_to) stc_to_2 = mne.morph_data_precomputed(subject_from, subject_to, stc_from, vertices_to, morph_mat) stc_to_2.save('%s_audvis-meg_2' % subject_to) # View source activations import pylab as pl pl.plot(stc_from.times, stc_from.data.mean(axis=0), 'r', label='from') pl.plot(stc_to.times, stc_to.data.mean(axis=0), 'b', label='to') pl.plot(stc_to_2.times, stc_to.data.mean(axis=0), 'g', label='to_2') pl.xlabel('time (ms)') pl.ylabel('Mean Source amplitude') pl.legend() pl.show()
fname_morph = C.fname_STC( C, 'SensitivityMaps', subject, 'SensMap_' + modality + '_' + metric + '_mph') # read existing source estimate print('Reading: %s.' % fname_stc) stc = mne.read_source_estimate(fname_stc, subject) # compute morph_mat only once per subject if morph_mat == []: vertices_to = mne.grade_to_vertices(subject=C.stc_morph, grade=5, subjects_dir=C.subjects_dir) morph_mat = mne.compute_morph_matrix(subject_from=subject, subject_to=C.stc_morph, vertices_from=stc.vertices, vertices_to=vertices_to, subjects_dir=C.subjects_dir) # Morphing to standard brain morphed = mne.morph_data_precomputed(subject_from=subject, subject_to=C.stc_morph, stc_from=stc, vertices_to=vertices_to, morph_mat=morph_mat) print('Writing morphed to: %s.' % fname_morph) morphed.save(fname_morph) # Done
def xhemi(ndvar, mask=None, hemi='lh', parc=True): """Project data from both hemispheres to ``hemi`` of fsaverage_sym Project data from both hemispheres to the same hemisphere for interhemisphere comparisons. The fsaverage_sym brain is a symmetric version of fsaverage to facilitate interhemisphere comparisons. It is included with FreeSurfer > 5.1 and can be obtained as described `here <http://surfer.nmr.mgh.harvard.edu/fswiki/Xhemi>`_. For statistical comparisons between hemispheres, use of the symmetric ``fsaverage_sym`` model is recommended to minimize bias [1]_. Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. mask : bool Remove sources in "unknown-" labels (default is True unless ``ndvar`` contains sources with "unknown-" label). hemi : 'lh' | 'rh' Hemisphere onto which to morph the data. parc : bool | str Parcellation for target source space; True to use same as in ``ndvar`` (default). Returns ------- lh : NDVAr Data from the left hemisphere on ``hemi`` of ``fsaverage_sym``. rh : NDVar Data from the right hemisphere on ``hemi`` of ``fsaverage_sym``. See Also -------- morph_source_space: lower level function for morphing Notes ----- Only symmetric volume source spaces are currently supported. References ---------- .. [1] Greve D. N., Van der Haegen L., Cai Q., Stufflebeam S., Sabuncu M. R., Fischl B., Brysbaert M. A Surface-based Analysis of Language Lateralization and Cortical Asymmetry. Journal of Cognitive Neuroscience 25(9), 1477-1492, 2013. """ source = ndvar.get_dim('source') other_hemi = 'rh' if hemi == 'lh' else 'lh' if isinstance(source, VolumeSourceSpace): ax = ndvar.get_axis('source') # extract hemi is_in_hemi = source.hemi == hemi source_out = source[is_in_hemi] # map other_hemi into hemi is_in_other = source.hemi == other_hemi coord_map = { tuple(source.coordinates[i]): i for i in np.flatnonzero(is_in_other) } try: other_source = [ coord_map[-x, y, z] for x, y, z in source_out.coordinates ] except KeyError: raise NotImplementedError( "Only implemented for symmetric volume source spaces") # extract hemi-data x_hemi = ndvar.x[index(is_in_hemi, at=ax)] x_other = ndvar.x[index(other_source, at=ax)] # for vector data, the L/R component has to be mirrored for space_ax, dim in enumerate(ndvar.dims): if isinstance(dim, Space): for direction in 'LR': if direction in dim: i = dim._array_index(direction) x_other[index(i, at=space_ax)] *= -1 # combine data dims = list(ndvar.dims) dims[ax] = source_out out_same = NDVar(x_hemi, dims, ndvar.name, ndvar.info) out_other = NDVar(x_other, dims, ndvar.name, ndvar.info) else: if source.subject == 'fsaverage_sym': ndvar_sym = ndvar else: ndvar_sym = morph_source_space(ndvar, 'fsaverage_sym', parc=parc, mask=mask) vert_lh, vert_rh = ndvar_sym.source.vertices vert_from = [[], vert_rh] if hemi == 'lh' else [vert_lh, []] vert_to = [vert_lh, []] if hemi == 'lh' else [[], vert_rh] with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', r'\d+/\d+ vertices not included in smoothing', module='mne') morph_mat = mne.compute_morph_matrix( 'fsaverage_sym', 'fsaverage_sym', vert_from, vert_to, subjects_dir=ndvar.source.subjects_dir, xhemi=True) out_same = ndvar_sym.sub(source=hemi) out_other = morph_source_space(ndvar_sym.sub(source=other_hemi), 'fsaverage_sym', out_same.source.vertices, morph_mat, parc=parc, xhemi=True, mask=mask) if hemi == 'lh': return out_same, out_other else: return out_other, out_same
def morph_source_space(ndvar, subject_to, morph_mat=None, vertices_to=None): """Morph source estimate between subjects using a precomputed matrix Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. subject_to : string Name of the subject on which to morph. morph_mat : None | sparse matrix The morphing matrix. If ndvar contains a whole source space, the morph matrix can be automatically loaded, although providing a cached matrix can speed up processing by a second or two. vertices_to : None | list of array of int The vertices on the destination subject's brain. If ndvar contains a whole source space, vertices_to can be automatically loaded, although providing them as argument can speed up processing by a second or two. Returns ------- morphed_ndvar : NDVar NDVar morphed to the destination subject. """ src = ndvar.source.src subject_from = ndvar.source.subject subjects_dir = ndvar.source.subjects_dir vertices_from = ndvar.source.vertno if vertices_to is None: path = SourceSpace._src_pattern.format(subjects_dir=subjects_dir, subject=subject_to, src=src) src_to = mne.read_source_spaces(path) vertices_to = [src_to[0]['vertno'], src_to[1]['vertno']] elif not isinstance(vertices_to, list) or not len(vertices_to) == 2: raise ValueError('vertices_to must be a list of length 2') if morph_mat is None: morph_mat = mne.compute_morph_matrix(subject_from, subject_to, vertices_from, vertices_to, None, subjects_dir) elif not sp.sparse.issparse(morph_mat): raise ValueError('morph_mat must be a sparse matrix') elif not sum(len(v) for v in vertices_to) == morph_mat.shape[0]: raise ValueError('morph_mat.shape[0] must match number of vertices in ' 'vertices_to') # flatten data axis = ndvar.get_axis('source') x = ndvar.x if axis != 0: x = x.swapaxes(0, axis) n_sources = len(x) if not n_sources == morph_mat.shape[1]: raise ValueError('ndvar source dimension length must be the same as ' 'morph_mat.shape[0]') if ndvar.ndim > 2: shape = x.shape x = x.reshape((n_sources, -1)) # apply morph matrix x_ = morph_mat * x # restore data shape if ndvar.ndim > 2: shape_ = (len(x_),) + shape[1:] x_ = x_.reshape(shape_) if axis != 0: x_ = x_.swapaxes(axis, 0) # package output NDVar parc = ndvar.source.parc source = SourceSpace(vertices_to, subject_to, src, subjects_dir, parc) dims = ndvar.dims[:axis] + (source,) + ndvar.dims[axis + 1:] info = ndvar.info.copy() out = NDVar(x_, dims, info, ndvar.name) return out
def xhemi(ndvar, mask=None, hemi='lh', parc=True): """Project data from both hemispheres to ``hemi`` of fsaverage_sym Project data from both hemispheres to the same hemisphere for interhemisphere comparisons. The fsaverage_sym brain is a symmetric version of fsaverage to facilitate interhemisphere comparisons. It is included with FreeSurfer > 5.1 and can be obtained as described `here <http://surfer.nmr.mgh.harvard.edu/fswiki/Xhemi>`_. For statistical comparisons between hemispheres, use of the symmetric ``fsaverage_sym`` model is recommended to minimize bias [1]_. Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. mask : bool Remove sources in "unknown-" labels (default is True unless ``ndvar`` contains sources with "unknown-" label). hemi : 'lh' | 'rh' Hemisphere onto which to morph the data. parc : bool | str Parcellation for target source space; True to use same as in ``ndvar`` (default). Returns ------- lh : NDVAr Data from the left hemisphere on ``hemi`` of ``fsaverage_sym``. rh : NDVar Data from the right hemisphere on ``hemi`` of ``fsaverage_sym``. Notes ----- Only symmetric volume source spaces are currently supported. References ---------- .. [1] Greve D. N., Van der Haegen L., Cai Q., Stufflebeam S., Sabuncu M. R., Fischl B., Brysbaert M. A Surface-based Analysis of Language Lateralization and Cortical Asymmetry. Journal of Cognitive Neuroscience 25(9), 1477-1492, 2013. """ source = ndvar.get_dim('source') other_hemi = 'rh' if hemi == 'lh' else 'lh' if isinstance(source, VolumeSourceSpace): ax = ndvar.get_axis('source') # extract hemi is_in_hemi = source.hemi == hemi source_out = source[is_in_hemi] # map other_hemi into hemi is_in_other = source.hemi == other_hemi coord_map = {tuple(source.coordinates[i]): i for i in np.flatnonzero(is_in_other)} other_source = [coord_map[-x, y, z] for x, y, z in source_out.coordinates] # combine data x_hemi = ndvar.x[index(is_in_hemi, at=ax)] x_other = ndvar.x[index(other_source, at=ax)] dims = list(ndvar.dims) dims[ax] = source_out out_same = NDVar(x_hemi, dims, ndvar.info, ndvar.name) out_other = NDVar(x_other, dims, ndvar.info, ndvar.name) else: if source.subject == 'fsaverage_sym': ndvar_sym = ndvar else: ndvar_sym = morph_source_space(ndvar, 'fsaverage_sym', parc=parc, mask=mask) vert_lh, vert_rh = ndvar_sym.source.vertices vert_from = [[], vert_rh] if hemi == 'lh' else [vert_lh, []] vert_to = [vert_lh, []] if hemi == 'lh' else [[], vert_rh] with warnings.catch_warnings(): warnings.filterwarnings('ignore', r'\d+/\d+ vertices not included in smoothing', module='mne') morph_mat = mne.compute_morph_matrix( 'fsaverage_sym', 'fsaverage_sym', vert_from, vert_to, subjects_dir=ndvar.source.subjects_dir, xhemi=True) out_same = ndvar_sym.sub(source=hemi) out_other = morph_source_space( ndvar_sym.sub(source=other_hemi), 'fsaverage_sym', out_same.source.vertices, morph_mat, parc=parc, xhemi=True, mask=mask) if hemi == 'lh': return out_same, out_other else: return out_other, out_same
def morph_source_space(ndvar, subject_to, vertices_to=None, morph_mat=None, copy=False, parc=True): """Morph source estimate to a different MRI subject Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. subject_to : string Name of the subject on which to morph. vertices_to : None | list of array of int The vertices on the destination subject's brain. If ndvar contains a whole source space, vertices_to can be automatically loaded, although providing them as argument can speed up processing by a second or two. morph_mat : None | sparse matrix The morphing matrix. If ndvar contains a whole source space, the morph matrix can be automatically loaded, although providing a cached matrix can speed up processing by a second or two. copy : bool Make sure that the data of ``morphed_ndvar`` is separate from ``ndvar`` (default False). parc : bool | str Parcellation for target source space. The default is to keep the parcellation from ``ndvar``. Set to ``False`` to load no parcellation. If the annotation files are missing for the target subject an IOError is raised. Returns ------- morphed_ndvar : NDVar NDVar morphed to the destination subject. Notes ----- This function is used to make sure a number of different NDVars are defined on the same MRI subject and handles scaled MRIs efficiently. If the MRI subject on which ``ndvar`` is defined is a scaled copy of ``subject_to``, by default a shallow copy of ``ndvar`` is returned. That means that it is not safe to assume that ``morphed_ndvar`` can be modified in place without altering ``ndvar``. To make sure the date of the output is independent from the data of the input, set the argument ``copy=True``. """ subjects_dir = ndvar.source.subjects_dir subject_from = ndvar.source.subject src = ndvar.source.src if vertices_to is None: path = SourceSpace._SRC_PATH.format(subjects_dir=subjects_dir, subject=subject_to, src=src) src_to = mne.read_source_spaces(path) vertices_to = [ src_to[0]['vertno'] if ndvar.source.lh_n else np.empty(0, int), src_to[1]['vertno'] if ndvar.source.rh_n else np.empty(0, int) ] elif not isinstance(vertices_to, list) or not len(vertices_to) == 2: raise ValueError('vertices_to must be a list of length 2') # parc for new source space if parc is True: parc_to = ndvar.source.parc.name if ndvar.source.parc else None else: parc_to = parc # check that annot files are available if parc_to: fname = SourceSpace._ANNOT_PATH.format(subjects_dir=subjects_dir, subject=subject_to, hemi='%s', parc=parc_to) fnames = tuple(fname % hemi for hemi in ('lh', 'rh')) missing = tuple(fname for fname in fnames if not os.path.exists(fname)) if missing: raise IOError( "Annotation files are missing for parc=%r for target subject " "%s. Use the parc parameter to change the parcellation. The " "following files are missing:\n%s" % (parc_to, subject_to, '\n'.join(missing))) if subject_from == subject_to and _vertices_equal(ndvar.source.vertno, vertices_to): if copy: ndvar = ndvar.copy() if parc is not True: ndvar.source.set_parc(parc) return ndvar axis = ndvar.get_axis('source') x = ndvar.x # check whether it is a scaled brain do_morph = True cfg_path = os.path.join(subjects_dir, subject_from, 'MRI scaling parameters.cfg') if os.path.exists(cfg_path): cfg = mne.coreg.read_mri_cfg(subject_from, subjects_dir) subject_from = cfg['subject_from'] if subject_to == subject_from and _vertices_equal( ndvar.source.vertno, vertices_to): if copy: x_ = x.copy() else: x_ = x vertices_to = ndvar.source.vertno do_morph = False if do_morph: vertices_from = ndvar.source.vertno if morph_mat is None: morph_mat = mne.compute_morph_matrix(subject_from, subject_to, vertices_from, vertices_to, None, subjects_dir) elif not sp.sparse.issparse(morph_mat): raise ValueError('morph_mat must be a sparse matrix') elif not sum(len(v) for v in vertices_to) == morph_mat.shape[0]: raise ValueError('morph_mat.shape[0] must match number of ' 'vertices in vertices_to') # flatten data if axis != 0: x = x.swapaxes(0, axis) n_sources = len(x) if not n_sources == morph_mat.shape[1]: raise ValueError('ndvar source dimension length must be the same ' 'as morph_mat.shape[0]') if ndvar.ndim > 2: shape = x.shape x = x.reshape((n_sources, -1)) # apply morph matrix x_ = morph_mat * x # restore data shape if ndvar.ndim > 2: shape_ = (len(x_), ) + shape[1:] x_ = x_.reshape(shape_) if axis != 0: x_ = x_.swapaxes(axis, 0) # package output NDVar source = SourceSpace(vertices_to, subject_to, src, subjects_dir, parc_to) dims = ndvar.dims[:axis] + (source, ) + ndvar.dims[axis + 1:] info = ndvar.info.copy() out = NDVar(x_, dims, info, ndvar.name) return out
inv = op.join(inv_dir, '%s-%d-sss-%s-inv.fif' % (subj, p.lp_cut, inv_type)) inv = read_inverse_operator(inv) fname = op.join(inv_dir, '%s_%d-sss_eq_%s-ave.fif' % (p.analyses[0], p.lp_cut, subj)) aves = [mne.Evoked(fname, cond, baseline=(None, 0), proj=True, kind='average') for cond in conditions] nave = np.unique([a.nave for a in aves]) assert len(nave) == 1 for ave, cond in zip(aves, conditions): assert ave.comment == cond naves[si] = nave[0] # apply inverse, bin, morph stcs = [apply_inverse(ave, inv, lambda2, 'dSPM') for ave in aves] stcs = [stc.bin(0.005) for stc in stcs] m = mne.compute_morph_matrix(struc, 'fsaverage', stcs[0].vertices, fs_verts, n_smooth) stcs = [stc.morph_precomputed('fsaverage', fs_verts, m) for stc in stcs] # put in big matrix if subj == p.subjects[0]: data = np.empty((len(stcs), len(p.subjects), stcs[0].shape[0], stcs[0].shape[1])) for di, stc in enumerate(stcs): data[di, si, :, :] = stc.data times = stc.times print('Writing data...') np.savez_compressed(fname_data, data=data, times=times, naves=naves) else: print('Loading saved data...') data = np.load(fname_data)
def morph_source_space(ndvar, subject_to=None, vertices_to=None, morph_mat=None, copy=False, parc=True, xhemi=False, mask=None): """Morph source estimate to a different MRI subject Parameters ---------- ndvar : NDVar NDVar with SourceSpace dimension. subject_to : str Name of the subject on which to morph (by default this is the same as the current subject for ``xhemi`` morphing). vertices_to : None | list of array of int | 'lh' | 'rh' The vertices on the destination subject's brain. If ndvar contains a whole source space, vertices_to can be automatically loaded, although providing them as argument can speed up processing by a second or two. Use 'lh' or 'rh' to target vertices from only one hemisphere. morph_mat : None | sparse matrix The morphing matrix. If ndvar contains a whole source space, the morph matrix can be automatically loaded, although providing a cached matrix can speed up processing by a second or two. copy : bool Make sure that the data of ``morphed_ndvar`` is separate from ``ndvar`` (default False). parc : bool | str Parcellation for target source space. The default is to keep the parcellation from ``ndvar``. Set to ``False`` to load no parcellation. If the annotation files are missing for the target subject an IOError is raised. xhemi : bool Mirror hemispheres (i.e., project data from the left hemisphere to the right hemisphere and vice versa). mask : bool Restrict output to known sources. If the parcellation of ``ndvar`` is retained keep only sources with labels contained in ``ndvar``, otherwise remove only sourves with ``”unknown-*”`` label (default is True unless ``vertices_to`` is specified). Returns ------- morphed_ndvar : NDVar NDVar morphed to the destination subject. See Also -------- xhemi: morph data from both hemisphere to one for comparing hemispheres Notes ----- This function is used to make sure a number of different NDVars are defined on the same MRI subject and handles scaled MRIs efficiently. If the MRI subject on which ``ndvar`` is defined is a scaled copy of ``subject_to``, by default a shallow copy of ``ndvar`` is returned. That means that it is not safe to assume that ``morphed_ndvar`` can be modified in place without altering ``ndvar``. To make sure the date of the output is independent from the data of the input, set the argument ``copy=True``. Examples -------- Generate a symmetric ROI based on a test result (``res``):: # Generate a mask based on significance mask = res.p.min('time') <= 0.05 # store the vertices for which we want the end result fsa_vertices = mask.source.vertices # morphing is easier with a complete source space mask = complete_source_space(mask) # Use a parcellation that is available for the ``fsaverage_sym`` brain mask = set_parc(mask, 'aparc') # morph both hemispheres to the left hemisphere mask_from_lh, mask_from_rh = xhemi(mask) # take the union; morphing interpolates, so re-cast values to booleans mask_lh = (mask_from_lh > 0) | (mask_from_rh > 0) # morph the new ROI to the right hemisphere mask_rh = morph_source_space(mask_lh, vertices_to=[[], mask_lh.source.vertices[0]], xhemi=True) # cast back to boolean mask_rh = mask_rh > 0 # combine the two hemispheres mask_sym = concatenate([mask_lh, mask_rh], 'source') # morph the result back to the source brain (fsaverage) mask = morph_source_space(mask_sym, 'fsaverage', fsa_vertices) """ axis = ndvar.get_axis('source') source = ndvar.get_dim('source') subjects_dir = source.subjects_dir subject_from = source.subject if subject_to is None: subject_to = subject_from else: assert_subject_exists(subject_to, subjects_dir) # catch cases that don't require morphing if not xhemi: subject_is_same = subject_from == subject_to subject_is_scaled = find_source_subject( subject_to, subjects_dir) == subject_from or find_source_subject( subject_from, subjects_dir) == subject_to if subject_is_same or subject_is_scaled: if vertices_to is None: pass elif vertices_to in ('lh', 'rh'): ndvar = ndvar.sub(source=vertices_to) elif isinstance(vertices_to, str): raise ValueError(f"vertices_to={vertices_to!r}") else: raise TypeError(f"vertices_to={vertices_to!r}") x = ndvar.x.copy() if copy else ndvar.x parc_arg = None if parc is True else parc if subject_is_scaled or parc_arg is not None: source_to = source._copy(subject_to, parc=parc_arg) else: source_to = source dims = (*ndvar.dims[:axis], source_to, *ndvar.dims[axis + 1:]) return NDVar(x, dims, ndvar.name, ndvar.info) has_lh_out = bool(source.rh_n if xhemi else source.lh_n) has_rh_out = bool(source.lh_n if xhemi else source.rh_n) if vertices_to in (None, 'lh', 'rh'): default_vertices = source_space_vertices(source.kind, source.grade, subject_to, subjects_dir) lh_out = vertices_to == 'lh' or (vertices_to is None and has_lh_out) rh_out = vertices_to == 'rh' or (vertices_to is None and has_rh_out) vertices_to = [ default_vertices[0] if lh_out else np.empty(0, int), default_vertices[1] if rh_out else np.empty(0, int) ] if mask is None: if source.parc is None: mask = False else: mask = not np.any(source.parc.startswith('unknown-')) elif not isinstance(vertices_to, list) or not len(vertices_to) == 2: raise ValueError( f"vertices_to={vertices_to!r}: must be a list of length 2") # check that requested data is available n_to_lh = len(vertices_to[0]) n_to_rh = len(vertices_to[1]) if n_to_lh and not has_lh_out: raise ValueError( "Data on the left hemisphere was requested in vertices_to but is not available in ndvar" ) elif n_to_rh and not has_rh_out: raise ValueError( "Data on the right hemisphere was requested in vertices_to but is not available in ndvar" ) elif n_to_lh == 0 and n_to_rh == 0: raise ValueError("No target vertices") # parc for new source space if parc is True: parc_to = None if source.parc is None else source.parc.name else: parc_to = parc if mask and parc_to is None: raise ValueError("Can't mask source space without parcellation...") # check that annot files are available if parc_to: fname = SourceSpace._ANNOT_PATH.format(subjects_dir=subjects_dir, subject=subject_to, hemi='%s', parc=parc_to) fnames = tuple(fname % hemi for hemi in ('lh', 'rh')) missing = tuple(fname for fname in fnames if not os.path.exists(fname)) if missing: missing = '\n'.join(missing) raise IOError( f"Annotation files are missing for parc={parc_to!r}, subject={subject_to!r}. Use the parc parameter when morphing to set a different parcellation. The following files are missing:\n{missing}" ) # find target source space source_to = SourceSpace(vertices_to, subject_to, source.src, subjects_dir, parc_to) if mask is True: if parc is True: keep_labels = source.parc.cells if xhemi: keep_labels = [switch_hemi_tag(label) for label in keep_labels] index = source_to.parc.isin(keep_labels) else: index = source_to.parc.isnotin(('unknown-lh', 'unknown-rh')) source_to = source_to[index] elif mask not in (None, False): raise TypeError(f"mask={mask!r}") if morph_mat is None: with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', r'\d+/\d+ vertices not included in smoothing', module='mne') morph_mat = mne.compute_morph_matrix(subject_from, subject_to, source.vertices, source_to.vertices, None, subjects_dir, xhemi=xhemi) elif not sp.sparse.issparse(morph_mat): raise ValueError('morph_mat must be a sparse matrix') elif not sum(len(v) for v in source_to.vertices) == morph_mat.shape[0]: raise ValueError( 'morph_mat.shape[0] must match number of vertices in vertices_to') # flatten data x = ndvar.x if axis != 0: x = x.swapaxes(0, axis) n_sources = len(x) if not n_sources == morph_mat.shape[1]: raise ValueError( 'ndvar source dimension length must be the same as morph_mat.shape[0]' ) if ndvar.ndim > 2: shape = x.shape x = x.reshape((n_sources, -1)) # apply morph matrix x_m = morph_mat * x # restore data shape if ndvar.ndim > 2: shape_ = (len(x_m), ) + shape[1:] x_m = x_m.reshape(shape_) if axis != 0: x_m = x_m.swapaxes(axis, 0) # package output NDVar dims = (*ndvar.dims[:axis], source_to, *ndvar.dims[axis + 1:]) return NDVar(x_m, dims, ndvar.name, ndvar.info)
data_dir = mne.datasets.sample.data_path() subjects_dir = data_dir + '/subjects' stc_path = data_dir + '/MEG/sample/sample_audvis-meg-eeg' stc = mne.read_source_estimate(stc_path, 'sample') # First, morph the data to fsaverage_sym, for which we have left_right # registrations: stc = stc.morph('fsaverage_sym', subjects_dir=subjects_dir, smooth=5) # Compute a morph-matrix mapping the right to the left hemisphere. Use the # vertices parameters to determine source and target hemisphere: mm = mne.compute_morph_matrix( 'fsaverage_sym', 'fsaverage_sym', xhemi=True, # cross-hemisphere morphing vertices_from=[[], stc.vertices[1]], # from the right hemisphere vertices_to=[stc.vertices[0], []], # to the left hemisphere subjects_dir=subjects_dir) # SourceEstimate on the left hemisphere: stc_lh = mne.SourceEstimate(stc.lh_data, [stc.vertices[0], []], stc.tmin, stc.tstep, stc.subject) # SourceEstimate of the right hemisphere, morphed to the left: stc_rh_on_lh = mne.SourceEstimate(mm * stc.rh_data, [stc.vertices[0], []], stc.tmin, stc.tstep, stc.subject) # Since both STCs are now on the same hemisphere we can subtract them: diff = stc_lh - stc_rh_on_lh diff.plot(hemi='lh', subjects_dir=subjects_dir, initial_time=0.07, size=(800, 600))
print('Simulating data for %d subjects.' % n_subjects) # Let's make sure our results replicate, so set the seed. np.random.seed(0) X = randn(n_vertices_sample, n_times, n_subjects, 4) * 10 for ii, condition in enumerate(conditions): X[:, :, :, ii] += condition.lh_data[:, :, np.newaxis] # It's a good idea to spatially smooth the data, and for visualization # purposes, let's morph these to fsaverage, which is a grade 5 source space # with vertices 0:10242 for each hemisphere. Usually you'd have to morph # each subject's data separately (and you might want to use morph_data # instead), but here since all estimates are on 'sample' we can use one # morph matrix for all the heavy lifting. fsave_vertices = [np.arange(10242), np.array([])] # right hemisphere is empty morph_mat = compute_morph_matrix('sample', 'fsaverage', sample_vertices, fsave_vertices, 20, subjects_dir) n_vertices_fsave = morph_mat.shape[0] # We have to change the shape for the dot() to work properly X = X.reshape(n_vertices_sample, n_times * n_subjects * 4) print('Morphing data.') X = morph_mat.dot(X) # morph_mat is a sparse matrix X = X.reshape(n_vertices_fsave, n_times, n_subjects, 4) # Now we need to prepare the group matrix for the ANOVA statistic. # To make the clustering function work correctly with the # ANOVA function X needs to be a list of multi-dimensional arrays # (one per condition) of shape: samples (subjects) x time x space X = np.transpose(X, [2, 1, 0, 3]) # First we permute dimensions # finally we split the array into a list a list of conditions
def sublevel_spatial_stats(X, subject, index, stc, clust_p, modality, method, condition): con = mne.spatial_tris_connectivity(grade_to_tris(5)) # morphing data subjects_dir = '/neurospin/meg/meg_tmp/MTT_MEG_Baptiste/mri/' fsave_vertices = [np.arange(10242), np.arange(10242)] subject_vertices = [stc.lh_vertno, stc.rh_vertno] morph_mat = compute_morph_matrix(subject, 'fsaverage', subject_vertices, fsave_vertices, 20, subjects_dir) n_vertices_fsave = morph_mat.shape[0] nobs = index[0] ncond = len(index) ntimes = X.shape[2] nvertices = stc.lh_vertno.shape[0] + stc.rh_vertno.shape[0] # We have to change the shape for the dot() to work properly X = np.transpose(X, [1, 2, 0]) # vertices * times * obs X = X.reshape(nvertices, ntimes * nobs * ncond) print('Morphing data.') X = morph_mat.dot(X) # morph_mat is a sparse matrix X = X.reshape(n_vertices_fsave, ntimes, nobs, ncond) # list of array of shapes ncond*(obs, time, vertices) X = np.transpose(X, [2, 1, 0, 3]) X = [np.squeeze(x) for x in np.split(X, 3, axis=-1)] # average over time to clusterize only on spatial dimension X = [np.mean(x, 1) for x in X] p_threshold = clust_p f_threshold = stats.distributions.f.ppf(1 - p_threshold, ncond - 1, ncond * (nobs - 1)) # use default function one-way anova (not repeated measures!) F_obs, clu, clu_p_val, H0 = mne.stats.permutation_cluster_test( X, threshold=f_threshold, connectivity=con, n_jobs=4, verbose=True, seed=666) wdir = "/neurospin/meg/meg_tmp/MTT_MEG_Baptiste/MEG/" save_path = (wdir + subject + '/mne_python/STATS') if not os.path.exists(save_path): os.makedirs(save_path) # save cluster stats spatial_clust_F = np.array((F_obs, clu, clu_p_val, H0)) np.save((save_path + '/' + modality + '_' + 'cluster_stats_' + "_vs_".join(condition)), spatial_clust_F) # save F-Map tmp = F_obs tmp = tmp[:, np.newaxis] fsave_vertices = [np.arange(10242), np.arange(10242)] stc_Ftest = mne.SourceEstimate(tmp, fsave_vertices, 0, stc.tstep) stc_Ftest.save( (save_path + '/' + modality + '_' + "_vs_".join(condition))) # to create probability maps: threshold the map at p < 0.05 and binarize ind = np.where(F_obs >= f_threshold)[0] VertKept = np.empty((len(F_obs))) for v, vertices in enumerate(VertKept): if v in ind: VertKept[v] = 1 else: VertKept[v] = 0 VertKept = VertKept[:, np.newaxis] fsave_vertices = [np.arange(10242), np.arange(10242)] stc_Ftest = mne.SourceEstimate(VertKept, fsave_vertices, 0, stc.tstep) stc_Ftest.save((save_path + '/' + modality + '_' + 'BinForProb_' + "_vs_".join(condition))) return F_obs, clu, clu_p_val, H0
# plt.xlabel('time (ms)') # plt.ylabel('%s value' % method) # plt.show() stc.crop(-0.1, 0.9) tstep = stc.tstep times = stc.times # Average brain """ One should check if morph map is current and correct. Otherwise, it will spit out and error. Check SUBJECTS_DIR/morph-maps """ morph_mat = mne.compute_morph_matrix(subs[n], 'fsaverage', stc.vertices, fs_vertices, smooth=20, subjects_dir=fs_dir) stc_fsaverage = stc.morph_precomputed('fsaverage', fs_vertices, morph_mat, subs[n]) # tmin, tmax = 0.080, 0.120 # stc_mean = stc_fsaverage.copy().crop(tmin, tmax).mean() # # labels = mne.read_labels_from_annot('fsaverage', parc='HCPMMP1', surf_name='white', subjects_dir=fs_dir) # V1_label_lh = [label for label in labels if label.name == 'L_V1_ROI-lh'][0] # V1_label_rh = [label for label in labels if label.name == 'R_V1_ROI-rh'][0] # # stc_mean_label = stc_mean.in_label(V1_label_lh) # data = np.abs(stc_mean_label.data) # stc_mean_label.data[data < 0.6 * np.max(data)] = 0.
#early exclude = [1,2,3,4,5,6,7,8,9,10,11,12,14,15,16,18,20,22,24,25,26,27,29,31,33,37,38,40,41,42,43,44,45,46,47,48,49,50] for run in range(1, 51): if run in exclude: continue subject = "s%02d" % run stc_fname = op.join('/raid5/rcho/MEG_NM_NR_testing/FINALMNE/DATA/stc/', '%s_40hz' % (subject)) fssub = "S%02d" % run subjects_dir=op.join('/raid5/rcho/PSYCH_CFC/fMRI/', '%s/RAW/fs_test/' % (fssub)) print("processing subject: %s" % subject) stc=mne.read_source_estimate(stc_fname, subject=subject) # morphed = stc.morph(subject_from=fssub, subject_to='fsaverage', # subjects_dir=subjects_dir, grade=4) fs_vertices = [np.arange(10242)] * 2 # fsaverage is special this way morph_mat = mne.compute_morph_matrix(fssub, 'fsaverage', stc.vertices, fs_vertices, smooth=None, subjects_dir=subjects_dir) stc_fsaverage = stc.morph_precomputed('fsaverage', fs_vertices, morph_mat) #morphed.save(op.join(datapath, '%s-morphed' % (subject))) stcs.append(stc_fsaverage) data = np.average([s.data for s in stcs], axis=0) stc_e = mne.SourceEstimate(data, stcs[0].vertices, stcs[0].tmin, stcs[0].tstep) stc_e.save(op.join('/raid5/rcho/MEG_NM_NR_testing/FINALMNE/DATA/group/patient/', 'STCearly_average')) ########################################### #plotting stc_e_fname=op.join('/raid5/rcho/MEG_NM_NR_testing/FINALMNE/DATA/group/patient/', 'STCearly_average') stc_e=mne.read_source_estimate(stc_e_fname) tmin=0.150 tmax=0.900 stc_e_mean = stc_e.copy().crop(tmin, tmax).mean()
stc_from = mne.read_source_estimate(fname) # Morph using one method (supplying the vertices in fsaverage's source # space makes it faster). Note that for any generic subject, you could do: # vertices_to = mne.grade_to_vertices(subject_to, grade=5) # But fsaverage's source space was set up so we can just do this: vertices_to = [np.arange(10242), np.arange(10242)] stc_to = mne.morph_data(subject_from, subject_to, stc_from, n_jobs=1, grade=vertices_to) stc_to.save('%s_audvis-meg' % subject_to) # Morph using another method -- useful if you're going to do a lot of the # same inter-subject morphing operations; you could save and load morph_mat morph_mat = mne.compute_morph_matrix(subject_from, subject_to, stc_from.vertno, vertices_to) stc_to_2 = mne.morph_data_precomputed(subject_from, subject_to, stc_from, vertices_to, morph_mat) stc_to_2.save('%s_audvis-meg_2' % subject_to) # View source activations import matplotlib.pyplot as plt plt.plot(stc_from.times, stc_from.data.mean(axis=0), 'r', label='from') plt.plot(stc_to.times, stc_to.data.mean(axis=0), 'b', label='to') plt.plot(stc_to_2.times, stc_to.data.mean(axis=0), 'g', label='to_2') plt.xlabel('time (ms)') plt.ylabel('Mean Source amplitude') plt.legend() plt.show()
forward, cov_blank, loose=0.2, depth=0.8) blank_idx = np.nonzero(epochs_ds.events[:, 2] == 15)[0] epochs_ds.drop_epochs(blank_idx) cov_base = mne.compute_covariance(epochs_ds, tmin=None, tmax=0, method='auto') inv_base = make_inverse_operator(epochs_ds.info, forward, cov_base, loose=0.2, depth=0.8) vertices_to = [np.arange(10242), np.arange(10242)] vertices_from = [forward['src'][0]['vertno'], forward['src'][1]['vertno']] morph_mat = mne.compute_morph_matrix(subj, 'fsaverage', vertices_from, vertices_to) for c in range(len(conds)): # start with the simplest method, MNE + dSPM stc = apply_inverse(evoked_ds[c], inv_base, lambda2, method) stc = mne.morph_data_precomputed(subj, 'fsaverage', stc, vertices_to, morph_mat) fname = out_dir + '%s_%s_dSPM_base_clean' % (subj, conds[c]) stc.save(fname) stc = apply_inverse(evoked_ds[c], inv_blank, lambda2, method) stc = mne.morph_data_precomputed(subj, 'fsaverage', stc, vertices_to, morph_mat) fname = out_dir + '%s_%s_dSPM_blank_clean' % (subj, conds[c]) stc.save(fname) # the next estimate is LCMV beamformer in time data_cov = mne.compute_covariance(epochs_ds[conds[c]],
print('Simulating data for %d subjects.' % n_subjects) # Let's make sure our results replicate, so set the seed. np.random.seed(0) X = randn(n_vertices_sample, n_times, n_subjects, 4) * 10 for ii, condition in enumerate(conditions): X[:, :, :, ii] += condition.lh_data[:, :, np.newaxis] # It's a good idea to spatially smooth the data, and for visualization # purposes, let's morph these to fsaverage, which is a grade 5 source space # with vertices 0:10242 for each hemisphere. Usually you'd have to morph # each subject's data separately (and you might want to use morph_data # instead), but here since all estimates are on 'sample' we can use one # morph matrix for all the heavy lifting. fsave_vertices = [np.arange(10242), np.array([], int)] # right hemi is empty morph_mat = compute_morph_matrix('sample', 'fsaverage', sample_vertices, fsave_vertices, 20, subjects_dir) n_vertices_fsave = morph_mat.shape[0] # We have to change the shape for the dot() to work properly X = X.reshape(n_vertices_sample, n_times * n_subjects * 4) print('Morphing data.') X = morph_mat.dot(X) # morph_mat is a sparse matrix X = X.reshape(n_vertices_fsave, n_times, n_subjects, 4) # Now we need to prepare the group matrix for the ANOVA statistic. # To make the clustering function work correctly with the # ANOVA function X needs to be a list of multi-dimensional arrays # (one per condition) of shape: samples (subjects) x time x space X = np.transpose(X, [2, 1, 0, 3]) # First we permute dimensions # finally we split the array into a list a list of conditions
n_epochs2[n][iCond] = evoked.nave stc = mne.minimum_norm.apply_inverse(evoked,inv,lambda2, method=method, pick_ori="normal") stc.crop(-0.1, 0.9) tmin = stc.tmin tstep = stc.tstep times = stc.times # Average brain """ One should check if morph map is current and correct. Otherwise, it will spit out and error. Check SUBJECTS_DIR/morph-maps """ morph_mat = mne.compute_morph_matrix(subs2[n], 'fsaverage', stc.vertices, fs_vertices, smooth=None, subjects_dir=fs_dir) stc_fsaverage = stc.morph_precomputed('fsaverage', fs_vertices, morph_mat) # morph_dat = mne.morph_data(subs[n], 'fsaverage', stc, n_jobs=16, # grade=fs_vertices, subjects_dir=fs_dir) X2[:,:,n,iCond] = stc_fsaverage.data os.chdir(raw_dir) np.save(fname_data, X2) np.save('session2_times.npy',times) np.save('session2_tstep.npy',tstep) np.save('session2_n_averages.npy',n_epochs2) else: os.chdir(raw_dir) X2 = np.load(fname_data) times = np.load('session2_times.npy')