def fix_channel_pos(inst): '''Scale channel positions to default mne head radius. FIXME - add docs''' import borsar from mne.bem import _fit_sphere # get channel positions matrix pos = borsar.channels.get_ch_pos(inst) # remove channels without positions no_pos = np.isnan(pos).any(axis=1) | (pos == 0).any(axis=1) pos = pos[~no_pos, :] # fit sphere to channel positions radius, origin = _fit_sphere(pos) default_sphere = 0.095 scale = radius / default_sphere info = get_info(inst) for idx, chs in enumerate(info['chs']): if chs['kind'] == 2: chs['loc'][:3] -= origin chs['loc'][:3] /= scale return inst
def _interpolate_bads_eeg(inst, picks=None, verbose=None): """ Interpolate bad EEG channels. Operates in place. Parameters ---------- inst : mne.io.Raw, mne.Epochs or mne.Evoked The data to interpolate. Must be preloaded. picks: np.ndarray, shape(n_channels, ) | list | None The channel indices to be used for interpolation. """ from mne.bem import _fit_sphere from mne.utils import logger, warn from mne.channels.interpolation import _do_interp_dots from mne.channels.interpolation import _make_interpolation_matrix import numpy as np if picks is None: picks = pick_types(inst.info, meg=False, eeg=True, exclude=[]) bads_idx = np.zeros(len(inst.ch_names), dtype=np.bool) goods_idx = np.zeros(len(inst.ch_names), dtype=np.bool) inst.info._check_consistency() bads_idx[picks] = [inst.ch_names[ch] in inst.info['bads'] for ch in picks] if len(picks) == 0 or bads_idx.sum() == 0: return goods_idx[picks] = True goods_idx[bads_idx] = False pos = inst._get_channel_positions(picks) # Make sure only good EEG are used bads_idx_pos = bads_idx[picks] goods_idx_pos = goods_idx[picks] pos_good = pos[goods_idx_pos] pos_bad = pos[bads_idx_pos] # test spherical fit radius, center = _fit_sphere(pos_good) distance = np.sqrt(np.sum((pos_good - center)**2, 1)) distance = np.mean(distance / radius) if np.abs(1. - distance) > 0.1: warn('Your spherical fit is poor, interpolation results are ' 'likely to be inaccurate.') logger.info('Computing interpolation matrix from {0} sensor ' 'positions'.format(len(pos_good))) interpolation = _make_interpolation_matrix(pos_good, pos_bad) logger.info('Interpolating {0} sensors'.format(len(pos_bad))) _do_interp_dots(inst, interpolation, goods_idx, bads_idx)
def _interpolate_bads_eeg(inst, picks=None, verbose=None): """ Interpolate bad EEG channels. Operates in place. Parameters ---------- inst : mne.io.Raw, mne.Epochs or mne.Evoked The data to interpolate. Must be preloaded. picks: np.ndarray, shape(n_channels, ) | list | None The channel indices to be used for interpolation. """ from mne.bem import _fit_sphere from mne.utils import logger, warn from mne.channels.interpolation import _do_interp_dots from mne.channels.interpolation import _make_interpolation_matrix import numpy as np if picks is None: picks = pick_types(inst.info, meg=False, eeg=True, exclude=[]) bads_idx = np.zeros(len(inst.ch_names), dtype=np.bool) goods_idx = np.zeros(len(inst.ch_names), dtype=np.bool) inst.info._check_consistency() bads_idx[picks] = [inst.ch_names[ch] in inst.info['bads'] for ch in picks] if len(picks) == 0 or bads_idx.sum() == 0: return goods_idx[picks] = True goods_idx[bads_idx] = False pos = inst._get_channel_positions(picks) # Make sure only good EEG are used bads_idx_pos = bads_idx[picks] goods_idx_pos = goods_idx[picks] pos_good = pos[goods_idx_pos] pos_bad = pos[bads_idx_pos] # test spherical fit radius, center = _fit_sphere(pos_good) distance = np.sqrt(np.sum((pos_good - center) ** 2, 1)) distance = np.mean(distance / radius) if np.abs(1. - distance) > 0.1: warn('Your spherical fit is poor, interpolation results are ' 'likely to be inaccurate.') logger.info('Computing interpolation matrix from {0} sensor ' 'positions'.format(len(pos_good))) interpolation = _make_interpolation_matrix(pos_good, pos_bad) logger.info('Interpolating {0} sensors'.format(len(pos_bad))) _do_interp_dots(inst, interpolation, goods_idx, bads_idx)
def test_set_montage(): """Test setting a montage.""" raw = read_raw_fif(fif_fname) orig_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) raw.set_montage('mgh60') # test loading with string argument new_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) assert_true((orig_pos != new_pos).all()) r0 = _fit_sphere(new_pos)[1] assert_allclose(r0, [0., -0.016, 0.], atol=1e-3)
def test_set_montage(): """Test setting a montage.""" raw = read_raw_fif(fif_fname) mon = read_montage('mgh60') orig_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) raw.set_montage(mon) new_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) assert_true((orig_pos != new_pos).all()) r0 = _fit_sphere(new_pos)[1] assert_allclose(r0, [0., -0.016, 0.], atol=1e-3)
def forward_to_tangential(fwd, center=None): """Convert a free orientation forward solution to a tangential one. Places two source dipoles at each vertex that are oriented tangentially to a sphere with its origin at the center of the brain. Recomputes the forward model according to the new dipoles. Parameters ---------- fwd : instance of Forward The forward solution to convert. center : tuple of float (x, y, z) | None The carthesian coordinates of the center of the brain. By default, a sphere is fitted through all the points in the source space. Returns ------- fwd_out : instance of Forward The tangential forward solution. """ if fwd['source_ori'] != FIFF.FIFFV_MNE_FREE_ORI: raise ValueError('Forward solution needs to have free orientation.') n_sources, n_channels = fwd['nsource'], fwd['nchan'] if fwd['sol']['ncol'] // n_sources == 2: raise ValueError('Forward solution already seems to be in tangential ' 'orientation.') # Compute two dipole directions tangential to a sphere that has its origin # in the center of the brain. if center is None: _, center = _fit_sphere(fwd['source_rr'], disp=False) _, tan1, tan2 = _make_radial_coord_system(fwd['source_rr'], center) # Make sure the forward solution is in head orientation for this fwd_out = convert_forward_solution(fwd, surf_ori=False, copy=True) G = fwd_out['sol']['data'].reshape(n_channels, n_sources, 3) # Compute the forward solution for the new dipoles Phi = np.einsum('ijk,ljk->ijl', G, [tan1, tan2]) fwd_out['sol']['data'] = Phi.reshape(n_channels, 2 * n_sources) fwd_out['sol']['ncol'] = 2 * n_sources # Store the source orientations fwd_out['source_nn'] = np.stack((tan1, tan2), axis=1).reshape(-1, 3) # Mark the orientation as free for now. In the future we should add a # new constant to indicate "tangential" orientations. fwd_out['source_ori'] = FIFF.FIFFV_MNE_FREE_ORI return fwd_out
def test_set_montage(): """Test setting 'mgh60' montage to old fif.""" raw = read_raw_fif(fif_fname) raw.rename_channels(lambda x: x.replace('EEG ', 'EEG')) orig_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) raw.set_montage('mgh60') # test loading with string argument new_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) assert ((orig_pos != new_pos).all()) r0 = _fit_sphere(new_pos)[1] assert_allclose(r0, [0.000775, 0.006881, 0.047398], atol=1e-3)
def test_set_montage(): """Test setting a montage.""" raw = read_raw_fif(fif_fname) orig_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) raw.set_montage('mgh60') # test loading with string argument new_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) assert_true((orig_pos != new_pos).all()) r0 = _fit_sphere(new_pos)[1] assert_allclose(r0, [0., -0.016, 0.], atol=1e-3) # mgh70 has no 61/62/63/64 (these are EOG/ECG) mon = read_montage('mgh70') assert 'EEG061' not in mon.ch_names assert 'EEG074' in mon.ch_names
def test_set_montage(): """Test setting a montage.""" raw = read_raw_fif(fif_fname) orig_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) raw.set_montage('mgh60') # test loading with string argument new_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) assert ((orig_pos != new_pos).all()) r0 = _fit_sphere(new_pos)[1] assert_allclose(r0, [0., -0.016, 0.], atol=1e-3) # mgh70 has no 61/62/63/64 (these are EOG/ECG) mon = read_montage('mgh70') assert 'EEG061' not in mon.ch_names assert 'EEG074' in mon.ch_names
def _make_interpolator(inst, bad_channels): """Find indexes and interpolation matrix to interpolate bad channels Parameters ---------- inst : mne.io.Raw, mne.Epochs or mne.Evoked The data to interpolate. Must be preloaded. """ logger = logging.getLogger(__name__) bads_idx = np.zeros(len(inst.ch_names), dtype=np.bool) goods_idx = np.zeros(len(inst.ch_names), dtype=np.bool) picks = pick_types(inst.info, meg=False, eeg=True, exclude=[]) bads_idx[picks] = [inst.ch_names[ch] in bad_channels for ch in picks] goods_idx[picks] = True goods_idx[bads_idx] = False pos = get_channel_positions(inst, picks) # Make sure only EEG are used bads_idx_pos = bads_idx[picks] goods_idx_pos = goods_idx[picks] pos_good = pos[goods_idx_pos] pos_bad = pos[bads_idx_pos] # test spherical fit radius, center = _fit_sphere(pos_good, False) distance = np.sqrt(np.sum((pos_good - center) ** 2, 1)) distance = np.mean(distance / radius) if np.abs(1. - distance) > 0.1: logger.warning('Your spherical fit is poor, interpolation results are ' 'likely to be inaccurate.') logger.info('Computing interpolation matrix from {0} sensor ' 'positions'.format(len(pos_good))) interpolation = _make_interpolation_matrix(pos_good, pos_bad) return goods_idx, bads_idx, interpolation
def test_set_montage_mgh(rename): """Test setting 'mgh60' montage to old fif.""" raw = read_raw_fif(fif_fname) eeg_picks = pick_types(raw.info, meg=False, eeg=True, exclude=()) assert list(eeg_picks) == [ii for ii, name in enumerate(raw.ch_names) if name.startswith('EEG')] orig_pos = np.array([raw.info['chs'][pick]['loc'][:3] for pick in eeg_picks]) atol = 1e-6 if rename == 'raw': raw.rename_channels(lambda x: x.replace('EEG ', 'EEG')) raw.set_montage('mgh60') # test loading with string argument elif rename == 'montage': mon = make_standard_montage('mgh60') mon.rename_channels(lambda x: x.replace('EEG', 'EEG ')) assert [raw.ch_names[pick] for pick in eeg_picks] == mon.ch_names raw.set_montage(mon) else: atol = 3e-3 # XXX old defs here apparently (maybe not realistic)? assert rename == 'custom' assert len(_MGH60) == 60 mon = make_standard_montage('standard_1020') def renamer(x): try: return 'EEG %03d' % (_MGH60.index(x) + 1,) except ValueError: return x mon.rename_channels(renamer) raw.set_montage(mon) new_pos = np.array([ch['loc'][:3] for ch in raw.info['chs'] if ch['ch_name'].startswith('EEG')]) assert ((orig_pos != new_pos).all()) r0 = _fit_sphere(new_pos)[1] assert_allclose(r0, [0.000775, 0.006881, 0.047398], atol=1e-3) # spot check assert_allclose(new_pos[:2], [[0.000273, 0.084920, 0.105838], [0.028822, 0.083529, 0.099164]], atol=atol)
def test_read_raw_curry_rfDC(fname, tol, mock_dev_head_t, tmpdir): """Test reading CURRY files.""" if mock_dev_head_t: if 'Curry 7' in fname: # not supported yet return # copy files to tmpdir base = op.splitext(fname)[0] for ext in ('.cdt', '.cdt.dpa'): src = base + ext dst = op.join(tmpdir, op.basename(base) + ext) copyfile(src, dst) if ext == '.cdt.dpa': with open(dst, 'a') as fid: fid.write(LM_CONTENT) fname = op.join(tmpdir, op.basename(fname)) with open(fname + '.hpi', 'w') as fid: fid.write(HPI_CONTENT) # check data bti_rfDC = read_raw_bti(pdf_fname=bti_rfDC_file, head_shape_fname=None) with catch_logging() as log: raw = read_raw_curry(fname, verbose=True) log = log.getvalue() if mock_dev_head_t: assert 'Composing device' in log else: assert 'Leaving device' in log assert 'no landmark' in log # test on the eeg chans, since these were not renamed by curry eeg_names = [ ch["ch_name"] for ch in raw.info["chs"] if ch["kind"] == FIFF.FIFFV_EEG_CH ] assert_allclose(raw.get_data(eeg_names), bti_rfDC.get_data(eeg_names), rtol=tol) assert bti_rfDC.info['dev_head_t'] is not None # XXX probably a BTI bug if mock_dev_head_t: assert raw.info['dev_head_t'] is not None assert_allclose(raw.info['dev_head_t']['trans'], WANT_TRANS, atol=1e-5) else: assert raw.info['dev_head_t'] is None # check that most MEG sensors are approximately oriented outward from # the device origin n_meg = n_eeg = n_other = 0 pos = list() nn = list() for ch in raw.info['chs']: if ch['kind'] == FIFF.FIFFV_MEG_CH: assert ch['coil_type'] == FIFF.FIFFV_COIL_CTF_GRAD t = _loc_to_coil_trans(ch['loc']) pos.append(t[:3, 3]) nn.append(t[:3, 2]) assert_allclose(np.linalg.norm(nn[-1]), 1.) n_meg += 1 elif ch['kind'] == FIFF.FIFFV_EEG_CH: assert ch['coil_type'] == FIFF.FIFFV_COIL_EEG n_eeg += 1 else: assert ch['coil_type'] == FIFF.FIFFV_COIL_NONE n_other += 1 assert n_meg == 148 assert n_eeg == 31 assert n_other == 15 pos = np.array(pos) nn = np.array(nn) rad, origin = _fit_sphere(pos, disp=False) assert 0.11 < rad < 0.13 pos -= origin pos /= np.linalg.norm(pos, axis=1, keepdims=True) angles = np.abs(np.rad2deg(np.arccos((pos * nn).sum(-1)))) assert (angles < 20).sum() > 100
def homologous_pairs(inst): ''' Construct homologous channel pairs based on channel names or positions. Parameters ---------- inst : mne object instance Mne object like mne.Raw or mne.Epochs. Returns ------- selection: dict of {str -> list of int} mappings Dictionary mapping hemisphere ('left' or 'right') to array of channel indices. ''' from borsar.channels import get_ch_pos, get_ch_names ch_names = get_ch_names(inst) ch_pos = get_ch_pos(inst) labels = ['right', 'left'] selection = {label: list() for label in labels} has_1020_names = 'Cz' in ch_names and 'F3' in ch_names if has_1020_names: # find homologues by channel names left_chans = ch_pos[:, 0] < 0 y_ord = np.argsort(ch_pos[left_chans, 1])[::-1] check_chans = [ ch for ch in list(np.array(ch_names)[left_chans][y_ord]) if 'z' not in ch ] for ch in check_chans: chan_base = ''.join([char for char in ch if not char.isdigit()]) chan_value = int(''.join([char for char in ch if char.isdigit()])) if (chan_value % 2) == 1: # sometimes homologous channels are missing in the cap homologous_ch = chan_base + str(chan_value + 1) if homologous_ch in ch_names: selection['left'].append(ch_names.index(ch)) selection['right'].append(ch_names.index(homologous_ch)) else: # channel names do not come from 10-20 system # constructing homologues from channel position # (this will not work well for digitized channel positions) from mne.bem import _fit_sphere # fit sphere to channel positions and calculate median distance # of the channels to the sphere origin radius, origin = _fit_sphere(ch_pos) origin_distance = ch_pos - origin[np.newaxis, :] dist = np.linalg.norm(origin_distance, axis=1) median_dist = np.median(dist) # find channels on the left from sphere origin left_x_val = origin[0] - median_dist * 0.05 sel = ch_pos[:, 0] < left_x_val left_chans = ch_pos[sel, :] sel_idx = np.where(sel)[0] for idx, pos in enumerate(left_chans): # represent channel position with respect to the origin this_distance = pos - origin # find similar origin-relative position on the right side this_distance[0] *= -1 this_simil = origin_distance - this_distance[np.newaxis, :] similar = np.linalg.norm(this_simil, axis=1).argmin() # fill selection dictionary selection['left'].append(sel_idx[idx]) selection['right'].append(similar) selection['left'] = np.array(selection['left']) selection['right'] = np.array(selection['right']) return selection
def _interpolate_bads_eeg(inst, picks=None, verbose=False): """ Interpolate bad EEG channels. Operates in place. Parameters ---------- inst : mne.io.Raw, mne.Epochs or mne.Evoked The data to interpolate. Must be preloaded. picks : str | list | slice | None Channels to include for interpolation. Slices and lists of integers will be interpreted as channel indices. In lists, channel *name* strings (e.g., ``['EEG 01', 'EEG 02']``) will pick the given channels. None (default) will pick all EEG channels. Note that channels in ``info['bads']`` *will be included* if their names or indices are explicitly provided. """ from mne.bem import _fit_sphere from mne.utils import logger, warn from mne.channels.interpolation import _do_interp_dots from mne.channels.interpolation import _make_interpolation_matrix import numpy as np inst.info._check_consistency() if picks is None: picks = pick_types(inst.info, meg=False, eeg=True, exclude=[]) else: picks = _handle_picks(inst.info, picks) bads_idx = np.zeros(len(inst.ch_names), dtype=np.bool) goods_idx = np.zeros(len(inst.ch_names), dtype=np.bool) bads_idx[picks] = [inst.ch_names[ch] in inst.info['bads'] for ch in picks] if len(picks) == 0 or bads_idx.sum() == 0: return goods_idx[picks] = True goods_idx[bads_idx] = False pos = inst._get_channel_positions(picks) # Make sure only good EEG are used bads_idx_pos = bads_idx[picks] goods_idx_pos = goods_idx[picks] pos_good = pos[goods_idx_pos] pos_bad = pos[bads_idx_pos] # test spherical fit radius, center = _fit_sphere(pos_good) distance = np.sqrt(np.sum((pos_good - center)**2, 1)) distance = np.mean(distance / radius) if np.abs(1. - distance) > 0.1: warn('Your spherical fit is poor, interpolation results are ' 'likely to be inaccurate.') logger.info('Computing interpolation matrix from {0} sensor ' 'positions'.format(len(pos_good))) interpolation = _make_interpolation_matrix(pos_good, pos_bad) logger.info('Interpolating {0} sensors'.format(len(pos_bad))) _do_interp_dots(inst, interpolation, goods_idx, bads_idx)
from config import fname, subjects mne.set_log_level('ERROR') fwd = mne.read_forward_solution(fname.fwd(subject=subjects[0])) fwd_r = mne.read_forward_solution(fname.fwd_r(subject=subjects[0])) trans = fname.trans(subject=subjects[0]) fig1 = mne.viz.plot_alignment(fwd['info'], trans=trans, src=fwd_r['src'], meg='sensors', surfaces='white') fig1.scene.background = (1, 1, 1) # white fig1.children[-1].children[0].children[0].glyph.glyph.scale_factor = 0.008 mlab.view(135, 120, 0.3, [0.01, 0.015, 0.058]) mlab.savefig('../paper/figures/forward1.png', magnification=4, figure=fig1) radius, center = _fit_sphere(fwd_r['source_rr']) rad, tan1, tan2 = conpy.forward._make_radial_coord_system( fwd_r['source_rr'], center) fig2 = conpy.forward._plot_coord_system(fwd_r['source_rr'], rad, tan1, tan2, scale=0.003, n_ori=2) fig2.scene.background = (1, 1, 1) # white mlab.view(75.115, 47.534, 0.311, [0.00068598, 0.01360262, 0.03581326]) mlab.savefig('../paper/figures/forward2.png', magnification=4, figure=fig2)