Skip to content

Instantly share code, notes, and snippets.

@larsoner
Last active October 31, 2022 16:53
Show Gist options
  • Select an option

  • Save larsoner/2264fb5895070d29a8c9aa7c0dc0e8a6 to your computer and use it in GitHub Desktop.

Select an option

Save larsoner/2264fb5895070d29a8c9aa7c0dc0e8a6 to your computer and use it in GitHub Desktop.
Transcribe MGH montages to standard electrode names.
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Manually enter MGH 70- and 60-channel cap names and convert to montages.
"""
from pathlib import Path
import numpy as np
import mne
mgh = dict()
mgh[70] = [
'Fp1', 'Fpz', 'Fp2',
'AF7', 'AF3', 'AFz', 'AF4', 'AF8',
'F7', 'F5', 'F3', 'F1', 'Fz', 'F2', 'F4', 'F6', 'F8',
'FT9', 'FT7', 'FC5', 'FC3', 'FC1', 'FCz', 'FC2', 'FC4', 'FC6', 'FT8', 'FT10', # noqa
'T9', 'T7', 'C5', 'C3', 'C1', 'Cz', 'C2', 'C4', 'C6', 'T8', 'T10',
'TP9', 'TP7', 'CP5', 'CP3', 'CP1', 'CPz', 'CP2', 'CP4', 'CP6', 'TP8', 'TP10', # noqa
'P9', 'P7', 'P5', 'P3', 'P1', 'Pz', 'P2', 'P4', 'P6', 'P8', 'P10',
'PO7', 'PO3', 'POz', 'PO4', 'PO8',
'O1', 'Oz', 'O2',
'Iz',
]
# mgh[60] = [
# 'Fz', 'F2', 'AF4', 'Fpz', 'Fp1', 'AF8', 'FT9', 'F7', 'FC5', 'FC6', 'FT7',
# 'F1', 'AF7', 'FT8', 'F6', 'F5', 'FC1', 'FC2', 'FT10', 'T9', 'Cz', 'F4',
# 'T7', 'C2', 'C4', 'C1', 'C3', 'F8', 'F3', 'C5', 'Fp2', 'AF3',
# 'CP2', 'P2', 'O2', 'Iz', 'Oz', 'PO4', 'O1', 'P8', 'PO8', 'P6', 'PO7', 'PO3', 'C6', 'TP9', 'TP8', 'CP4', 'P4', # noqa
# 'CP3', 'CP1', 'TP7', 'P3', 'Pz', 'P1', 'P7', 'P5', 'TP10', 'T8', 'T10',
# ]
# # Visual inspection of sample dataset gave this remapping:
# mgh[60] = [mgh[60][idx - 1] for idx in map(int, (
# '5 4 31 '
# '13 32 3 6 '
# '8 16 29 12 1 2 22 15 28 '
# '7 11 9 17 18 10 14 19 '
# '20 23 30 27 26 21 24 25 45 59 60 '
# '46 52 50 51 33 48 47 58 '
# '56 57 53 55 54 34 49 42 40 '
# '43 44 38 41 '
# '39 37 35 '
# '36'
# ).split())]
# Which was then translated to this directly:
mgh[60] = (
'Fp1 Fpz Fp2 '
'AF7 AF3 AF4 AF8 '
'F7 F5 F3 F1 Fz F2 F4 F6 F8 '
'FT9 FT7 FC5 FC1 FC2 FC6 FT8 FT10 '
'T9 T7 C5 C3 C1 Cz C2 C4 C6 T8 T10 '
'TP9 TP7 CP3 CP1 CP2 CP4 TP8 TP10 '
'P7 P5 P3 P1 Pz P2 P4 P6 P8 '
'PO7 PO3 PO4 PO8 '
'O1 Oz O2 '
'Iz'
).split()
fiducials = ['LPA', 'RPA', 'Nz']
mon_path = Path(mne.__file__).parent / 'channels' / 'data' / 'montages'
standard_montage = mne.channels.read_custom_montage(
mon_path / 'standard_1020.elc', head_size=None)
for count in (60, 70):
mgh_names = mgh[count]
assert len(mgh_names) == count == len(set(mgh_names))
assert set(mgh_names) - set(standard_montage.ch_names) == set()
mgh_pos = standard_montage.get_positions()
fid_map = dict(Nz='nasion', LPA='lpa', RPA='rpa')
mgh_pos = np.array([mgh_pos[fid_map[key]] for key in fiducials] + [
mgh_pos['ch_pos'][name] for name in mgh_names], float)
assert mgh_pos.shape == (len(mgh_names) + 3, 3)
mgh_eeg_no = list(range(1, 61))
if count > 60:
mgh_eeg_no.extend(range(65, 75)) # 61, 62, 63, 64 are reserved EOG/ECG
mgh_chs = fiducials + [f'EEG{ii:03d}' for ii in mgh_eeg_no]
assert len(mgh_chs) == len(mgh_pos)
with open(mon_path / f'mgh{count}.elc', 'w') as fid:
fid.write('# ASA electrode file\nReferenceLabel avg\nUnitPosition mm\n'
'NumberPositions= %d\nPositions\n' % (len(mgh_pos),))
for p in mgh_pos:
fid.write('%0.4f %0.4f %0.4f\n' % tuple(1000 * p))
fid.write('Labels\n')
fid.write('\n'.join(mgh_chs))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment