Last active
October 31, 2022 16:53
-
-
Save larsoner/2264fb5895070d29a8c9aa7c0dc0e8a6 to your computer and use it in GitHub Desktop.
Transcribe MGH montages to standard electrode names.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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