def test_get_builtin_montages(): """Test help function to obtain builtin montages.""" EXPECTED_NUM = 24 assert len(get_builtin_montages()) == EXPECTED_NUM
def test_montage(): """Test making montages.""" tempdir = _TempDir() inputs = dict( sfp='FidNz 0 9.071585155 -2.359754454\n' 'FidT9 -6.711765 0.040402876 -3.251600355\n' 'very_very_very_long_name -5.831241498 -4.494821698 4.955347697\n' 'Cz 0 0 8.899186843', csd= '// MatLab Sphere coordinates [degrees] Cartesian coordinates\n' # noqa: E501 '// Label Theta Phi Radius X Y Z off sphere surface\n' # noqa: E501 'E1 37.700 -14.000 1.000 0.7677 0.5934 -0.2419 -0.00000000000000011\n' # noqa: E501 'E3 51.700 11.000 1.000 0.6084 0.7704 0.1908 0.00000000000000000\n' # noqa: E501 'E31 90.000 -11.000 1.000 0.0000 0.9816 -0.1908 0.00000000000000000\n' # noqa: E501 'E61 158.000 -17.200 1.000 -0.8857 0.3579 -0.2957 -0.00000000000000022', # noqa: E501 mm_elc= '# ASA electrode file\nReferenceLabel avg\nUnitPosition mm\n' # noqa:E501 'NumberPositions= 68\n' 'Positions\n' '-86.0761 -19.9897 -47.9860\n' '85.7939 -20.0093 -48.0310\n' '0.0083 86.8110 -39.9830\n' '-86.0761 -24.9897 -67.9860\n' 'Labels\nLPA\nRPA\nNz\nDummy\n', m_elc='# ASA electrode file\nReferenceLabel avg\nUnitPosition m\n' 'NumberPositions= 68\nPositions\n-.0860761 -.0199897 -.0479860\n' # noqa:E501 '.0857939 -.0200093 -.0480310\n.0000083 .00868110 -.0399830\n' '.08 -.02 -.04\n' 'Labels\nLPA\nRPA\nNz\nDummy\n', txt='Site Theta Phi\n' 'Fp1 -92 -72\n' 'Fp2 92 72\n' 'very_very_very_long_name -92 72\n' 'O2 92 -90\n', elp='346\n' 'EEG\t F3\t -62.027\t -50.053\t 85\n' 'EEG\t Fz\t 45.608\t 90\t 85\n' 'EEG\t F4\t 62.01\t 50.103\t 85\n' 'EEG\t FCz\t 68.01\t 58.103\t 85\n', hpts='eeg Fp1 -95.0 -3. -3.\n' 'eeg AF7 -1 -1 -3\n' 'eeg A3 -2 -2 2\n' 'eeg A 0 0 0', bvef='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' '<!-- Generated by EasyCap Configurator 19.05.2014 -->\n' '<Electrodes defaults="false">\n' ' <Electrode>\n' ' <Name>Fp1</Name>\n' ' <Theta>-90</Theta>\n' ' <Phi>-72</Phi>\n' ' <Radius>1</Radius>\n' ' <Number>1</Number>\n' ' </Electrode>\n' ' <Electrode>\n' ' <Name>Fz</Name>\n' ' <Theta>45</Theta>\n' ' <Phi>90</Phi>\n' ' <Radius>1</Radius>\n' ' <Number>2</Number>\n' ' </Electrode>\n' ' <Electrode>\n' ' <Name>F3</Name>\n' ' <Theta>-60</Theta>\n' ' <Phi>-51</Phi>\n' ' <Radius>1</Radius>\n' ' <Number>3</Number>\n' ' </Electrode>\n' ' <Electrode>\n' ' <Name>F7</Name>\n' ' <Theta>-90</Theta>\n' ' <Phi>-36</Phi>\n' ' <Radius>1</Radius>\n' ' <Number>4</Number>\n' ' </Electrode>\n' '</Electrodes>', ) # Get actual positions and save them for checking # csd comes from the string above, all others come from commit 2fa35d4 poss = dict( sfp=[[0.0, 9.07159, -2.35975], [-6.71176, 0.0404, -3.2516], [-5.83124, -4.49482, 4.95535], [0.0, 0.0, 8.89919]], mm_elc=[[-0.08608, -0.01999, -0.04799], [0.08579, -0.02001, -0.04803], [1e-05, 0.08681, -0.03998], [-0.08608, -0.02499, -0.06799]], m_elc=[[-0.08608, -0.01999, -0.04799], [0.08579, -0.02001, -0.04803], [1e-05, 0.00868, -0.03998], [0.08, -0.02, -0.04]], txt=[[-26.25044, 80.79056, -2.96646], [26.25044, 80.79056, -2.96646], [-26.25044, -80.79056, -2.96646], [0.0, -84.94822, -2.96646]], elp=[[-48.20043, 57.55106, 39.86971], [0.0, 60.73848, 59.4629], [48.1426, 57.58403, 39.89198], [41.64599, 66.91489, 31.8278]], hpts=[[-95, -3, -3], [-1, -1., -3.], [-2, -2, 2.], [0, 0, 0]], bvef=[[-26.266444, 80.839803, 5.204748e-15], [3.680313e-15, 60.104076, 60.104076], [-46.325632, 57.207392, 42.500000], [-68.766444, 49.961746, 5.204748e-15]], ) for key, text in inputs.items(): kind = key.split('_')[-1] fname = op.join(tempdir, 'test.' + kind) with open(fname, 'w') as fid: fid.write(text) montage = read_montage(fname) if kind in ('sfp', 'txt'): assert ('very_very_very_long_name' in montage.ch_names) assert_equal(len(montage.ch_names), 4) assert_equal(len(montage.ch_names), len(montage.pos)) assert_equal(montage.pos.shape, (4, 3)) assert_equal(montage.kind, 'test') if kind == 'csd': dtype = [('label', 'S4'), ('theta', 'f8'), ('phi', 'f8'), ('radius', 'f8'), ('x', 'f8'), ('y', 'f8'), ('z', 'f8'), ('off_sph', 'f8')] try: table = np.loadtxt(fname, skip_header=2, dtype=dtype) except TypeError: table = np.loadtxt(fname, skiprows=2, dtype=dtype) poss['csd'] = np.c_[table['x'], table['y'], table['z']] if kind == 'elc': # Make sure points are reasonable distance from geometric centroid centroid = np.sum(montage.pos, axis=0) / montage.pos.shape[0] distance_from_centroid = np.apply_along_axis( np.linalg.norm, 1, montage.pos - centroid) assert_array_less(distance_from_centroid, 0.2) assert_array_less(0.01, distance_from_centroid) assert_array_almost_equal(poss[key], montage.pos, 4, err_msg=key) # Test reading in different letter case. ch_names = [ "F3", "FZ", "F4", "FC3", "FCz", "FC4", "C3", "CZ", "C4", "CP3", "CPZ", "CP4", "P3", "PZ", "P4", "O1", "OZ", "O2" ] montage = read_montage('standard_1020', ch_names=ch_names) assert_array_equal(ch_names, montage.ch_names) # test transform input_strs = [ """ eeg Fp1 -95.0 -31.0 -3.0 eeg AF7 -81 -59 -3 eeg AF3 -87 -41 28 cardinal 2 -91 0 -42 cardinal 1 0 -91 -42 cardinal 3 0 91 -42 """, """ Fp1 -95.0 -31.0 -3.0 AF7 -81 -59 -3 AF3 -87 -41 28 FidNz -91 0 -42 FidT9 0 -91 -42 FidT10 0 91 -42 """ ] # sfp files seem to have Nz, T9, and T10 as fiducials: # https://github.com/mne-tools/mne-python/pull/4482#issuecomment-321980611 kinds = ['test_fid.hpts', 'test_fid.sfp'] for kind, input_str in zip(kinds, input_strs): fname = op.join(tempdir, kind) with open(fname, 'w') as fid: fid.write(input_str) montage = read_montage(op.join(tempdir, kind), transform=True) # check coordinate transformation pos = np.array([-95.0, -31.0, -3.0]) nasion = np.array([-91, 0, -42]) lpa = np.array([0, -91, -42]) rpa = np.array([0, 91, -42]) fids = np.vstack((nasion, lpa, rpa)) trans = get_ras_to_neuromag_trans(fids[0], fids[1], fids[2]) pos = apply_trans(trans, pos) assert_array_equal(montage.pos[0], pos) assert_array_equal(montage.nasion[[0, 2]], [0, 0]) assert_array_equal(montage.lpa[[1, 2]], [0, 0]) assert_array_equal(montage.rpa[[1, 2]], [0, 0]) pos = np.array([-95.0, -31.0, -3.0]) montage_fname = op.join(tempdir, kind) montage = read_montage(montage_fname, unit='mm') assert_array_equal(montage.pos[0], pos * 1e-3) # test with last info = create_info(montage.ch_names, 1e3, ['eeg'] * len(montage.ch_names)) _set_montage(info, montage) pos2 = np.array([c['loc'][:3] for c in info['chs']]) assert_array_equal(pos2, montage.pos) assert_equal(montage.ch_names, info['ch_names']) info = create_info(montage.ch_names, 1e3, ['eeg'] * len(montage.ch_names)) evoked = EvokedArray(data=np.zeros((len(montage.ch_names), 1)), info=info, tmin=0) # test return type as well as set montage assert (isinstance(evoked.set_montage(montage), type(evoked))) pos3 = np.array([c['loc'][:3] for c in evoked.info['chs']]) assert_array_equal(pos3, montage.pos) assert_equal(montage.ch_names, evoked.info['ch_names']) # Warning should be raised when some EEG are not specified in montage info = create_info(montage.ch_names + ['foo', 'bar'], 1e3, ['eeg'] * (len(montage.ch_names) + 2)) with pytest.warns(RuntimeWarning, match='position specified'): _set_montage(info, montage) # Channel names can be treated case insensitive info = create_info(['FP1', 'af7', 'AF3'], 1e3, ['eeg'] * 3) _set_montage(info, montage) # Unless there is a collision in names info = create_info(['FP1', 'Fp1', 'AF3'], 1e3, ['eeg'] * 3) assert (info['dig'] is None) with pytest.warns(RuntimeWarning, match='position specified'): _set_montage(info, montage) assert len(info['dig']) == 5 # 2 EEG w/pos, 3 fiducials montage.ch_names = ['FP1', 'Fp1', 'AF3'] info = create_info(['fp1', 'AF3'], 1e3, ['eeg', 'eeg']) assert (info['dig'] is None) with pytest.warns(RuntimeWarning, match='position specified'): _set_montage(info, montage, set_dig=False) assert (info['dig'] is None) # test get_pos2d method montage = read_montage("standard_1020") c3 = montage.get_pos2d()[montage.ch_names.index("C3")] c4 = montage.get_pos2d()[montage.ch_names.index("C4")] fz = montage.get_pos2d()[montage.ch_names.index("Fz")] oz = montage.get_pos2d()[montage.ch_names.index("Oz")] f1 = montage.get_pos2d()[montage.ch_names.index("F1")] assert (c3[0] < 0) # left hemisphere assert (c4[0] > 0) # right hemisphere assert (fz[1] > 0) # frontal assert (oz[1] < 0) # occipital assert_allclose(fz[0], 0, atol=1e-2) # midline assert_allclose(oz[0], 0, atol=1e-2) # midline assert (f1[0] < 0 and f1[1] > 0) # left frontal # test get_builtin_montages function montages = get_builtin_montages() assert (len(montages) > 0) # MNE should always ship with montages assert ("standard_1020" in montages) # 10/20 montage assert ("standard_1005" in montages) # 10/05 montage