示例#1
0
def test_plot_topomap_bads():
    """Test plotting topomap with bad channels (gh-7213)."""
    import matplotlib.pyplot as plt
    data = np.random.RandomState(0).randn(3, 1000)
    raw = RawArray(data, create_info(3, 1000., 'eeg'))
    ch_pos_dict = {name: pos for name, pos in zip(raw.ch_names, np.eye(3))}
    raw.info.set_montage(make_dig_montage(ch_pos_dict, coord_frame='head'))
    for count in range(3):
        raw.info['bads'] = raw.ch_names[:count]
        raw.info._check_consistency()
        plot_topomap(data[:, 0], raw.info)
    plt.close('all')
示例#2
0
def make_montage(info, kind, check=False):
    from mne.utils import _clean_names
    import mnefun
    assert kind in ('mgh60', 'mgh70', 'uw_70', 'uw_60')
    picks = pick_types(info, meg=False, eeg=True, exclude=())
    sphere = make_sphere_model('auto', 'auto', info)
    info = pick_info(info, picks)
    to_names = info['ch_names']
    if kind in ('mgh60', 'mgh70'):
        if kind == 'mgh60':
            assert len(to_names) in (59, 60)
        else:
            assert len(to_names) in (70, )
        montage = make_standard_montage(kind, head_size=sphere.radius)
        from_names = _clean_names(to_names, remove_whitespace=True)
    else:
        assert len(to_names) == 60
        from_names = getattr(mnefun, 'ch_names_' + kind)
        montage = make_standard_montage('standard_1020',
                                        head_size=sphere.radius)
    assert len(from_names) == len(to_names)
    montage_pos = montage._get_ch_pos()
    montage = make_dig_montage(
        {to: montage_pos[fro]
         for fro, to in zip(from_names, to_names)},
        coord_frame='head')
    eeg_pos = np.array([ch['loc'][:3] for ch in info['chs']])
    montage_pos = montage._get_ch_pos()
    montage_pos = np.array([montage_pos[name] for name in to_names])
    assert len(eeg_pos) == len(montage_pos)
    if check:
        from mayavi import mlab
        mlab.figure(size=(800, 800))
        mlab.points3d(*sphere['r0'],
                      scale_factor=2 * sphere.radius,
                      color=(0., 0., 1.),
                      opacity=0.1,
                      mode='sphere')
        mlab.points3d(*montage_pos.T,
                      scale_factor=0.01,
                      color=(1, 0, 0),
                      mode='sphere',
                      opacity=0.5)
        mlab.points3d(*eeg_pos.T,
                      scale_factor=0.005,
                      color=(1, 1, 1),
                      mode='sphere',
                      opacity=1)
    return montage, sphere
示例#3
0
def test_get_montage_volume_labels():
    """Test finding ROI labels near montage channel locations."""
    ch_coords = np.array([[-8.7040273, 17.99938754, 10.29604017],
                          [-14.03007764, 19.69978401, 12.07236939],
                          [-21.1130506, 21.98310911, 13.25658887]])
    ch_pos = dict(zip(['1', '2', '3'], ch_coords / 1000))  # mm -> m
    montage = make_dig_montage(ch_pos, coord_frame='mri')
    labels, colors = get_montage_volume_labels(montage,
                                               'sample',
                                               subjects_dir,
                                               aseg='aseg',
                                               dist=1)
    assert labels == {
        '1': ['Unknown'],
        '2': ['Left-Cerebral-Cortex'],
        '3': ['Left-Cerebral-Cortex']
    }
    assert 'Unknown' in colors
    assert 'Left-Cerebral-Cortex' in colors
    np.testing.assert_almost_equal(
        colors['Left-Cerebral-Cortex'],
        (0.803921568627451, 0.24313725490196078, 0.3058823529411765, 1.0))
    np.testing.assert_almost_equal(colors['Unknown'], (0.0, 0.0, 0.0, 1.0))

    # test inputs
    with pytest.raises(RuntimeError,
                       match='`aseg` file path must end with "aseg"'):
        get_montage_volume_labels(montage, 'sample', subjects_dir, aseg='foo')
    fail_montage = make_dig_montage(ch_pos, coord_frame='head')
    with pytest.raises(RuntimeError, match='Coordinate frame not supported'):
        get_montage_volume_labels(fail_montage,
                                  'sample',
                                  subjects_dir,
                                  aseg='aseg')
    with pytest.raises(ValueError, match='between 0 and 10'):
        get_montage_volume_labels(montage, 'sample', subjects_dir, dist=11)
示例#4
0
def test_set_montage_with_mismatching_ch_names():
    """Test setting a DigMontage with mismatching ch_names."""
    raw = read_raw_fif(fif_fname)
    montage = make_standard_montage('mgh60')

    # 'EEG 001' and 'EEG001' won't match
    missing_err = '60 channel positions not present'
    with pytest.raises(ValueError, match=missing_err):
        raw.set_montage(montage)

    montage.ch_names = [  # modify the names in place
        name.replace('EEG', 'EEG ') for name in montage.ch_names
    ]
    raw.set_montage(montage)  # does not raise

    # Case sensitivity
    raw.rename_channels(lambda x: x.lower())
    with pytest.raises(ValueError, match=missing_err):
        raw.set_montage(montage)
    # should work
    raw.set_montage(montage, match_case=False)
    raw.rename_channels(lambda x: x.upper())  # restore
    assert 'EEG 001' in raw.ch_names and 'eeg 001' not in raw.ch_names
    raw.rename_channels({'EEG 002': 'eeg 001'})
    assert 'EEG 001' in raw.ch_names and 'eeg 001' in raw.ch_names
    raw.set_channel_types({'eeg 001': 'misc'})
    raw.set_montage(montage)
    raw.set_channel_types({'eeg 001': 'eeg'})
    with pytest.raises(ValueError, match='1 channel position not present'):
        raw.set_montage(montage)
    with pytest.raises(ValueError, match='match_case=False as 1 channel name'):
        raw.set_montage(montage, match_case=False)
    info = create_info(['EEG 001'], 1000., 'eeg')
    mon = make_dig_montage({
        'EEG 001': np.zeros(3),
        'eeg 001': np.zeros(3)
    },
                           nasion=[0, 1., 0],
                           rpa=[1., 0, 0],
                           lpa=[-1., 0, 0])
    info.set_montage(mon)
    with pytest.raises(ValueError, match='match_case=False as 1 montage name'):
        info.set_montage(mon, match_case=False)
示例#5
0
def test_set_montage_with_missing_coordinates():
    """Test set montage with missing coordinates."""
    N_CHANNELS, NaN = 3, np.nan

    raw = _make_toy_raw(N_CHANNELS)
    raw.set_channel_types({ch: 'ecog' for ch in raw.ch_names})
    # don't include all the channels
    ch_names = raw.ch_names[1:]
    n_channels = len(ch_names)
    ch_coords = np.arange(n_channels * 3).reshape(n_channels, 3)
    montage_in_mri = make_dig_montage(
        ch_pos=dict(zip(
            ch_names,
            ch_coords,
        )),
        coord_frame='unknown',
        nasion=[0, 1, 0],
        lpa=[1, 0, 0],
        rpa=[-1, 0, 0],
    )

    with pytest.raises(ValueError,
                       match='DigMontage is '
                       'only a subset of info'):
        raw.set_montage(montage_in_mri)

    with pytest.raises(ValueError, match='Invalid value'):
        raw.set_montage(montage_in_mri, on_missing=True)

    with pytest.warns(RuntimeWarning,
                      match='DigMontage is '
                      'only a subset of info'):
        raw.set_montage(montage_in_mri, on_missing='warn')

    raw.set_montage(montage_in_mri, on_missing='ignore')
    assert_allclose(
        actual=np.array([ch['loc'] for ch in raw.info['chs']]),
        desired=[
            [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN],
            [0., 1., -2., 0., 0., 0., NaN, NaN, NaN, NaN, NaN, NaN],
            [-3., 4., -5., 0., 0., 0., NaN, NaN, NaN, NaN, NaN, NaN],
        ])
def test_read_dig_montage_using_polhemus_fastscan():
    """Test FastScan."""
    N_EEG_CH = 10

    my_electrode_positions = read_polhemus_fastscan(
        op.join(kit_dir, 'test_elp.txt')
    )

    montage = make_dig_montage(
        # EEG_CH
        ch_pos=dict(zip(ascii_lowercase[:N_EEG_CH],
                        np.random.RandomState(0).rand(N_EEG_CH, 3))),
        # NO NAMED points
        nasion=my_electrode_positions[0],
        lpa=my_electrode_positions[1],
        rpa=my_electrode_positions[2],
        hpi=my_electrode_positions[3:],
        hsp=read_polhemus_fastscan(op.join(kit_dir, 'test_hsp.txt')),

        # Other defaults
        coord_frame='unknown'
    )

    assert repr(montage) == (
        '<DigMontage | '
        '500 extras (headshape), 5 HPIs, 3 fiducials, 10 channels>'
    )  # XXX: is this wrong? extra is not in headspace, is it?

    assert set([d['coord_frame'] for d in montage.dig]) == {
        FIFF.FIFFV_COORD_UNKNOWN
    }  # XXX: so far we build everything in 'unknown'

    EXPECTED_FID_IN_POLHEMUS = {
        'nasion': [0.001393, 0.0131613, -0.0046967],
        'lpa': [-0.0624997, -0.0737271, 0.07996],
        'rpa': [-0.0748957, 0.0873785, 0.0811943],
    }
    fiducials, fid_coordframe = _get_fid_coords(montage.dig)
    assert fid_coordframe == FIFF.FIFFV_COORD_UNKNOWN
    for kk, val in fiducials.items():
        assert_allclose(val, EXPECTED_FID_IN_POLHEMUS[kk])
    assert_equal(len(set(kinds)), len(kinds), err_msg=str(sorted(kinds)))
    assert_equal(set(montages), set(kinds))


@pytest.mark.parametrize('reader, file_content, expected_dig, ext', [
    pytest.param(
        partial(read_custom_montage, head_size=None, unit='m'),
        ('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'),
        make_dig_montage(
            ch_pos={
                'very_very_very_long_name': [-5.8312416, -4.4948215, 4.9553475],  # noqa
                'Cz': [0., 0., 8.899187],
            },
            nasion=[0., 9.071585, -2.3597546],
            lpa=[-6.711765, 0.04040287, -3.2516003],
            rpa=None,
        ),
        'sfp', id='sfp'),

    pytest.param(
        partial(read_custom_montage, head_size=1, unit='n/a'),
        ('1	       0	 0.50669	     FPz\n'
         '2	      23	 0.71	    	EOG1\n'
         '3	 -39.947	 0.34459	      F3\n'
         '4	       0	 0.25338	      Fz\n'),
        make_dig_montage(
            ch_pos={
                'EOG1': [0.30873816, 0.72734152, -0.61290705],
示例#8
0
def test_plot_topomap_basic(monkeypatch):
    """Test basics of topomap plotting."""
    evoked = read_evokeds(evoked_fname, 'Left Auditory', baseline=(None, 0))
    res = 8
    fast_test = dict(res=res, contours=0, sensors=False, time_unit='s')
    fast_test_noscale = dict(res=res, contours=0, sensors=False)
    ev_bad = evoked.copy().pick_types(meg=False, eeg=True)
    ev_bad.pick_channels(ev_bad.ch_names[:2])
    plt_topomap = partial(ev_bad.plot_topomap, **fast_test)
    plt_topomap(times=ev_bad.times[:2] - 1e-6)  # auto, plots EEG
    pytest.raises(ValueError, plt_topomap, ch_type='mag')
    pytest.raises(ValueError, plt_topomap, times=[-100])  # bad time
    pytest.raises(ValueError, plt_topomap, times=[[0]])  # bad time

    evoked.plot_topomap([0.1],
                        ch_type='eeg',
                        scalings=1,
                        res=res,
                        contours=[-100, 0, 100],
                        time_unit='ms')

    # extrapolation to the edges of the convex hull or the head circle
    evoked.plot_topomap([0.1],
                        ch_type='eeg',
                        scalings=1,
                        res=res,
                        contours=[-100, 0, 100],
                        time_unit='ms',
                        extrapolate='local')
    evoked.plot_topomap([0.1],
                        ch_type='eeg',
                        scalings=1,
                        res=res,
                        contours=[-100, 0, 100],
                        time_unit='ms',
                        extrapolate='head')
    evoked.plot_topomap([0.1],
                        ch_type='eeg',
                        scalings=1,
                        res=res,
                        contours=[-100, 0, 100],
                        time_unit='ms',
                        extrapolate='head',
                        outlines='skirt')

    # extrapolation options when < 4 channels:
    temp_data = np.random.random(3)
    picks = channel_indices_by_type(evoked.info)['mag'][:3]
    info_sel = pick_info(evoked.info, picks)
    plot_topomap(temp_data, info_sel, extrapolate='local', res=res)
    plot_topomap(temp_data, info_sel, extrapolate='head', res=res)

    # make sure extrapolation works for 3 channels with border='mean'
    # (if extra points are placed incorrectly some of them have only
    #  other extra points as neighbours and border='mean' fails)
    plot_topomap(temp_data,
                 info_sel,
                 extrapolate='local',
                 border='mean',
                 res=res)

    # border=0 and border='mean':
    # ---------------------------
    ch_pos = np.array(
        sum(([[0, 0, r], [r, 0, 0], [-r, 0, 0], [0, -r, 0], [0, r, 0]]
             for r in np.linspace(0.2, 1.0, 5)), []))
    rng = np.random.RandomState(23)
    data = np.full(len(ch_pos), 5) + rng.randn(len(ch_pos))
    info = create_info(len(ch_pos), 250, 'eeg')
    ch_pos_dict = {name: pos for name, pos in zip(info['ch_names'], ch_pos)}
    dig = make_dig_montage(ch_pos_dict, coord_frame='head')
    info.set_montage(dig)

    # border=0
    ax, _ = plot_topomap(data, info, extrapolate='head', border=0, sphere=1)
    img_data = ax.get_array().data

    assert np.abs(img_data[31, 31] - data[0]) < 0.12
    assert np.abs(img_data[0, 0]) < 1.5

    # border='mean'
    ax, _ = plot_topomap(data,
                         info,
                         extrapolate='head',
                         border='mean',
                         sphere=1)
    img_data = ax.get_array().data

    assert np.abs(img_data[31, 31] - data[0]) < 0.12
    assert img_data[0, 0] > 5

    # error when not numeric or str:
    error_msg = 'border must be an instance of numeric or str'
    with pytest.raises(TypeError, match=error_msg):
        plot_topomap(data, info, extrapolate='head', border=[1, 2, 3])

    # error when str is not 'mean':
    error_msg = "The only allowed value is 'mean', but got 'fancy' instead."
    with pytest.raises(ValueError, match=error_msg):
        plot_topomap(data, info, extrapolate='head', border='fancy')

    # test channel placement when only 'grad' are picked:
    # ---------------------------------------------------
    info_grad = evoked.copy().pick('grad').info
    n_grads = len(info_grad['ch_names'])
    data = np.random.randn(n_grads)
    img, _ = plot_topomap(data, info_grad)

    # check that channels are scattered around x == 0
    pos = img.axes.collections[-1].get_offsets()
    prop_channels_on_the_right = (pos[:, 0] > 0).mean()
    assert prop_channels_on_the_right < 0.6

    # other:
    # ------
    plt_topomap = partial(evoked.plot_topomap, **fast_test)
    plt.close('all')
    axes = [plt.subplot(221), plt.subplot(222)]
    plt_topomap(axes=axes, colorbar=False)
    plt.close('all')
    plt_topomap(times=[-0.1, 0.2])
    plt.close('all')
    evoked_grad = evoked.copy().crop(0, 0).pick_types(meg='grad')
    mask = np.zeros((204, 1), bool)
    mask[[0, 3, 5, 6]] = True
    names = []

    def proc_names(x):
        names.append(x)
        return x[4:]

    evoked_grad.plot_topomap(ch_type='grad',
                             times=[0],
                             mask=mask,
                             show_names=proc_names,
                             **fast_test)
    assert_equal(sorted(names),
                 ['MEG 011x', 'MEG 012x', 'MEG 013x', 'MEG 014x'])
    mask = np.zeros_like(evoked.data, dtype=bool)
    mask[[1, 5], :] = True
    plt_topomap(ch_type='mag', outlines=None)
    times = [0.1]
    plt_topomap(times, ch_type='grad', mask=mask)
    plt_topomap(times, ch_type='planar1')
    plt_topomap(times, ch_type='planar2')
    plt_topomap(times,
                ch_type='grad',
                mask=mask,
                show_names=True,
                mask_params={'marker': 'x'})
    plt.close('all')
    with pytest.raises(ValueError, match='number of seconds; got -'):
        plt_topomap(times, ch_type='eeg', average=-1e3)
    with pytest.raises(TypeError, match='number of seconds; got type'):
        plt_topomap(times, ch_type='eeg', average='x')

    p = plt_topomap(times,
                    ch_type='grad',
                    image_interp='bilinear',
                    show_names=lambda x: x.replace('MEG', ''))
    subplot = [x for x in p.get_children() if 'Subplot' in str(type(x))]
    assert len(subplot) >= 1, [type(x) for x in p.get_children()]
    subplot = subplot[0]

    have_all = all('MEG' not in x.get_text() for x in subplot.get_children()
                   if isinstance(x, matplotlib.text.Text))
    assert have_all

    # Plot array
    for ch_type in ('mag', 'grad'):
        evoked_ = evoked.copy().pick_types(eeg=False, meg=ch_type)
        plot_topomap(evoked_.data[:, 0], evoked_.info, **fast_test_noscale)
    # fail with multiple channel types
    pytest.raises(ValueError, plot_topomap, evoked.data[0, :], evoked.info)

    # Test title
    def get_texts(p):
        return [
            x.get_text() for x in p.get_children()
            if isinstance(x, matplotlib.text.Text)
        ]

    p = plt_topomap(times, ch_type='eeg', average=0.01)
    assert_equal(len(get_texts(p)), 0)
    p = plt_topomap(times, ch_type='eeg', title='Custom')
    texts = get_texts(p)
    assert_equal(len(texts), 1)
    assert_equal(texts[0], 'Custom')
    plt.close('all')

    # delaunay triangulation warning
    plt_topomap(times, ch_type='mag')
    # projs have already been applied
    pytest.raises(RuntimeError,
                  plot_evoked_topomap,
                  evoked,
                  0.1,
                  'mag',
                  proj='interactive',
                  time_unit='s')

    # change to no-proj mode
    evoked = read_evokeds(evoked_fname,
                          'Left Auditory',
                          baseline=(None, 0),
                          proj=False)
    fig1 = evoked.plot_topomap('interactive',
                               'mag',
                               proj='interactive',
                               **fast_test)
    _fake_click(fig1, fig1.axes[1], (0.5, 0.5))  # click slider
    data_max = np.max(fig1.axes[0].images[0]._A)
    fig2 = plt.gcf()
    _fake_click(fig2, fig2.axes[0], (0.075, 0.775))  # toggle projector
    # make sure projector gets toggled
    assert (np.max(fig1.axes[0].images[0]._A) != data_max)

    with monkeypatch.context() as m:  # speed it up by not actually plotting
        m.setattr(topomap, '_plot_topomap', lambda *args, **kwargs:
                  (None, None, None))
        with pytest.warns(RuntimeWarning, match='More than 25 topomaps plots'):
            plot_evoked_topomap(evoked, [0.1] * 26, colorbar=False)

    pytest.raises(ValueError,
                  plot_evoked_topomap,
                  evoked, [-3e12, 15e6],
                  time_unit='s')

    for ch in evoked.info['chs']:
        if ch['coil_type'] == FIFF.FIFFV_COIL_EEG:
            ch['loc'].fill(0)

    # Remove extra digitization point, so EEG digitization points
    # correspond with the EEG electrodes
    del evoked.info['dig'][85]

    # Plot skirt
    evoked.plot_topomap(times, ch_type='eeg', outlines='skirt', **fast_test)

    # Pass custom outlines without patch
    eeg_picks = pick_types(evoked.info, meg=False, eeg=True)
    pos, outlines = _get_pos_outlines(evoked.info, eeg_picks, 0.1)
    evoked.plot_topomap(times, ch_type='eeg', outlines=outlines, **fast_test)
    plt.close('all')

    # Test interactive cmap
    fig = plot_evoked_topomap(evoked,
                              times=[0., 0.1],
                              ch_type='eeg',
                              cmap=('Reds', True),
                              title='title',
                              **fast_test)
    fig.canvas.key_press_event('up')
    fig.canvas.key_press_event(' ')
    fig.canvas.key_press_event('down')
    cbar = fig.get_axes()[0].CB  # Fake dragging with mouse.
    ax = cbar.cbar.ax
    _fake_click(fig, ax, (0.1, 0.1))
    _fake_click(fig, ax, (0.1, 0.2), kind='motion')
    _fake_click(fig, ax, (0.1, 0.3), kind='release')

    _fake_click(fig, ax, (0.1, 0.1), button=3)
    _fake_click(fig, ax, (0.1, 0.2), button=3, kind='motion')
    _fake_click(fig, ax, (0.1, 0.3), kind='release')

    fig.canvas.scroll_event(0.5, 0.5, -0.5)  # scroll down
    fig.canvas.scroll_event(0.5, 0.5, 0.5)  # scroll up

    plt.close('all')

    # Pass custom outlines with patch callable
    def patch():
        return Circle((0.5, 0.4687),
                      radius=.46,
                      clip_on=True,
                      transform=plt.gca().transAxes)

    outlines['patch'] = patch
    plot_evoked_topomap(evoked,
                        times,
                        ch_type='eeg',
                        outlines=outlines,
                        **fast_test)

    # Remove digitization points. Now topomap should fail
    evoked.info['dig'] = None
    pytest.raises(RuntimeError,
                  plot_evoked_topomap,
                  evoked,
                  times,
                  ch_type='eeg',
                  time_unit='s')
    plt.close('all')

    # Error for missing names
    n_channels = len(pos)
    data = np.ones(n_channels)
    pytest.raises(ValueError, plot_topomap, data, pos, show_names=True)

    # Test error messages for invalid pos parameter
    pos_1d = np.zeros(n_channels)
    pos_3d = np.zeros((n_channels, 2, 2))
    pytest.raises(ValueError, plot_topomap, data, pos_1d)
    pytest.raises(ValueError, plot_topomap, data, pos_3d)
    pytest.raises(ValueError, plot_topomap, data, pos[:3, :])

    pos_x = pos[:, :1]
    pos_xyz = np.c_[pos, np.zeros(n_channels)[:, np.newaxis]]
    pytest.raises(ValueError, plot_topomap, data, pos_x)
    pytest.raises(ValueError, plot_topomap, data, pos_xyz)

    # An #channels x 4 matrix should work though. In this case (x, y, width,
    # height) is assumed.
    pos_xywh = np.c_[pos, np.zeros((n_channels, 2))]
    plot_topomap(data, pos_xywh)
    plt.close('all')

    # Test peak finder
    axes = [plt.subplot(131), plt.subplot(132)]
    evoked.plot_topomap(times='peaks', axes=axes, **fast_test)
    plt.close('all')
    evoked.data = np.zeros(evoked.data.shape)
    evoked.data[50][1] = 1
    assert_array_equal(_find_peaks(evoked, 10), evoked.times[1])
    evoked.data[80][100] = 1
    assert_array_equal(_find_peaks(evoked, 10), evoked.times[[1, 100]])
    evoked.data[2][95] = 2
    assert_array_equal(_find_peaks(evoked, 10), evoked.times[[1, 95]])
    assert_array_equal(_find_peaks(evoked, 1), evoked.times[95])

    # Test excluding bads channels
    evoked_grad.info['bads'] += [evoked_grad.info['ch_names'][0]]
    orig_bads = evoked_grad.info['bads']
    evoked_grad.plot_topomap(ch_type='grad', times=[0], time_unit='ms')
    assert_array_equal(evoked_grad.info['bads'], orig_bads)
    plt.close('all')
示例#9
0
def test_plot_digmontage():
    """Test plot DigMontage."""
    montage = make_dig_montage(ch_pos=dict(zip(list('abc'), np.eye(3))))
    montage.plot()
    plt.close('all')
示例#10
0
def test_array_raw():
    """Test creating raw from array."""
    # creating
    raw = read_raw_fif(fif_fname).crop(2, 5)
    data, times = raw[:, :]
    sfreq = raw.info['sfreq']
    ch_names = [(ch[4:] if 'STI' not in ch else ch)
                for ch in raw.info['ch_names']]  # change them, why not
    types = list()
    for ci in range(101):
        types.extend(('grad', 'grad', 'mag'))
    types.extend(['ecog', 'seeg', 'hbo'])  # really 3 meg channels
    types.extend(['stim'] * 9)
    types.extend(['eeg'] * 60)
    picks = np.concatenate([
        pick_types(raw.info)[::20],
        pick_types(raw.info, meg=False, stim=True),
        pick_types(raw.info, meg=False, eeg=True)[::20]
    ])
    del raw
    data = data[picks]
    ch_names = np.array(ch_names)[picks].tolist()
    types = np.array(types)[picks].tolist()
    types.pop(-1)
    # wrong length
    pytest.raises(ValueError, create_info, ch_names, sfreq, types)
    # bad entry
    types.append('foo')
    pytest.raises(KeyError, create_info, ch_names, sfreq, types)
    types[-1] = 'eog'
    # default type
    info = create_info(ch_names, sfreq)
    assert_equal(info['chs'][0]['kind'], _kind_dict['misc'][0])
    # use real types
    info = create_info(ch_names, sfreq, types)
    raw2 = _test_raw_reader(RawArray,
                            test_preloading=False,
                            data=data,
                            info=info,
                            first_samp=2 * data.shape[1])
    data2, times2 = raw2[:, :]
    assert_allclose(data, data2)
    assert_allclose(times, times2)
    assert ('RawArray' in repr(raw2))
    pytest.raises(TypeError, RawArray, info, data)

    # filtering
    picks = pick_types(raw2.info, misc=True, exclude='bads')[:4]
    assert_equal(len(picks), 4)
    raw_lp = raw2.copy()
    kwargs = dict(fir_design='firwin', picks=picks)
    raw_lp.filter(None, 4.0, h_trans_bandwidth=4., **kwargs)
    raw_hp = raw2.copy()
    raw_hp.filter(16.0, None, l_trans_bandwidth=4., **kwargs)
    raw_bp = raw2.copy()
    raw_bp.filter(8.0,
                  12.0,
                  l_trans_bandwidth=4.,
                  h_trans_bandwidth=4.,
                  **kwargs)
    raw_bs = raw2.copy()
    raw_bs.filter(16.0,
                  4.0,
                  l_trans_bandwidth=4.,
                  h_trans_bandwidth=4.,
                  **kwargs)
    data, _ = raw2[picks, :]
    lp_data, _ = raw_lp[picks, :]
    hp_data, _ = raw_hp[picks, :]
    bp_data, _ = raw_bp[picks, :]
    bs_data, _ = raw_bs[picks, :]
    sig_dec = 15
    assert_array_almost_equal(data, lp_data + bp_data + hp_data, sig_dec)
    assert_array_almost_equal(data, bp_data + bs_data, sig_dec)

    # plotting
    raw2.plot()
    raw2.plot_psd(tmax=2., average=True, n_fft=1024, spatial_colors=False)
    plt.close('all')

    # epoching
    events = find_events(raw2, stim_channel='STI 014')
    events[:, 2] = 1
    assert len(events) > 2
    epochs = Epochs(raw2, events, 1, -0.2, 0.4, preload=True)
    evoked = epochs.average()
    assert_equal(evoked.nave, len(events) - 1)

    # complex data
    rng = np.random.RandomState(0)
    data = rng.randn(1, 100) + 1j * rng.randn(1, 100)
    raw = RawArray(data, create_info(1, 1000., 'eeg'))
    assert_allclose(raw._data, data)

    # Using digital montage to give MNI electrode coordinates
    n_elec = 10
    ts_size = 10000
    Fs = 512.
    ch_names = [str(i) for i in range(n_elec)]
    ch_pos_loc = np.random.randint(60, size=(n_elec, 3)).tolist()

    data = np.random.rand(n_elec, ts_size)
    montage = make_dig_montage(ch_pos=dict(zip(ch_names, ch_pos_loc)),
                               coord_frame='head')
    info = create_info(ch_names, Fs, 'ecog')

    raw = RawArray(data, info)
    raw.set_montage(montage)
    raw.plot_psd(average=False)  # looking for nonexistent layout
    raw.plot_psd_topo()
示例#11
0
def to_mne_eeg(eegstream=None,
               line_freq=None,
               filenames=None,
               nasion=None,
               lpa=None,
               rpa=None):
    '''Convert recordings to MNE format.

    Args:
        eegstream : array
            EEG streams previously imported.
        line_freq : int
            Powerline frequency (50 or 60).
        filenames : array
            Full path of XDF files. Used for recording identification.
        nasion : array, shape(3,)
            Position of the nasion fiducial point.
            If specified, the array must have the same lenght of eegstream.
            Format for every recording (X, Y, Z) in meters: [0,0,0]
        lpa : array, shape(3,)
            Position of the left periauricular fiducial point.
            If specified, the array must have the same lenght of eegstream.
            Format for every recording (X, Y, Z) in meters: [0,0,0]
        rpa : array, shape(3,)
            Position of the right periauricular fiducial point.
            If specified, the array must have the same lenght of eegstream.
            Format for every recording (X, Y, Z) in meters: [0,0,0]
    Returns:
        Array of MNE RawArray instances with the recordings specified in eegstream.
    Raises:
        ValueError: if no stream is specified in eegstream or powerline frequency is not 50 or 60.
    See also:
        read_raw_xdf
        read_raw_xdf_dir
    '''
    if eegstream is None:
        raise (ValueError('Enter parameter array of EEG recordings.'))

    if line_freq is None or line_freq not in [50, 60]:
        raise (ValueError(
            'Enter the powerline frequency of your region (50 Hz or 60 Hz).'))

    eegstream = [eegstream] if not isinstance(eegstream, list) else eegstream

    raweeg = []

    # Get the names of the channels
    ch_names = [
        eegstream[0]['info']['desc'][0]['channels'][0]['channel'][i]['label']
        [0] for i in range(len(eegstream[0]['time_series'][0]))
    ]

    # Define sensor coordinates
    sensor_coord = [[-0.0856192, -0.0465147, -0.0457070],
                    [-0.0548397, 0.0685722, -0.0105900],
                    [0.0557433, 0.0696568, -0.0107550],
                    [0.0861618, -0.0470353, -0.0458690]]

    for index, stream in enumerate(eegstream):
        # Get channels position
        dig_montage = channels.make_dig_montage(
            ch_pos=dict(zip(ch_names, sensor_coord)),
            nasion=nasion[index] if nasion is not None else None,
            lpa=lpa[index] if lpa is not None else None,
            rpa=rpa[index] if rpa is not None else None,
            coord_frame='head')
        # Create raw info for processing
        info = create_info(ch_names=dig_montage.ch_names,
                           sfreq=float(stream['info']['nominal_srate'][0]),
                           ch_types='eeg')
        # Add channels position to info
        info.set_montage(dig_montage)
        # Convert data from microvolts to volts
        conv_data = stream["time_series"] * 1e-6
        # Reorder channels
        ord_data = [[sublist[1][item] for item in [1, 2, 3, 0]]
                    for sublist in enumerate(conv_data)]
        # Create raw data for mne
        raw = io.RawArray(np.array(ord_data).T, info)
        # Get the information of each stream
        stream_info = stream['info']['name'][0][:9] + ' ' + (
            filenames[index] if filenames is not None else '')
        # Print the information of each stream
        print('\nInfo: ' + str(index) + ' ' + stream_info + '\n')
        # Add the powerline frequency of each stream
        raw.info['line_freq'] = line_freq
        # Create annotation to store the name of the device and the filenames
        annotations = Annotations(0, 0, stream_info)
        # Add the annotations to raw data
        raw.set_annotations(annotations)
        # Add the RawArray object to list of eeg recordings
        raweeg.append(raw)

    return raweeg
示例#12
0
def test_combining_digmontage_objects():
    """Test combining different DigMontage objects."""
    rng = np.random.RandomState(0)
    fiducials = dict(zip(('nasion', 'lpa', 'rpa'), rng.rand(3, 3)))

    # hsp positions are [1X, 1X, 1X]
    hsp1 = make_dig_montage(**fiducials, hsp=np.full((2, 3), 11.))
    hsp2 = make_dig_montage(**fiducials, hsp=np.full((2, 3), 12.))
    hsp3 = make_dig_montage(**fiducials, hsp=np.full((2, 3), 13.))

    # hpi positions are [2X, 2X, 2X]
    hpi1 = make_dig_montage(**fiducials, hpi=np.full((2, 3), 21.))
    hpi2 = make_dig_montage(**fiducials, hpi=np.full((2, 3), 22.))
    hpi3 = make_dig_montage(**fiducials, hpi=np.full((2, 3), 23.))

    # channels have positions at 40s, 50s, and 60s.
    ch_pos1 = make_dig_montage(**fiducials,
                               ch_pos={
                                   'h': [41, 41, 41],
                                   'b': [42, 42, 42],
                                   'g': [43, 43, 43]
                               })
    ch_pos2 = make_dig_montage(**fiducials,
                               ch_pos={
                                   'n': [51, 51, 51],
                                   'y': [52, 52, 52],
                                   'p': [53, 53, 53]
                               })
    ch_pos3 = make_dig_montage(**fiducials,
                               ch_pos={
                                   'v': [61, 61, 61],
                                   'a': [62, 62, 62],
                                   'l': [63, 63, 63]
                               })

    montage = (DigMontage() + hsp1 + hsp2 + hsp3 + hpi1 + hpi2 + hpi3 +
               ch_pos1 + ch_pos2 + ch_pos3)
    assert repr(montage) == (
        '<DigMontage | '
        '6 extras (headshape), 6 HPIs, 3 fiducials, 9 channels>')

    EXPECTED_MONTAGE = make_dig_montage(**fiducials,
                                        hsp=np.concatenate([
                                            np.full((2, 3), 11.),
                                            np.full((2, 3), 12.),
                                            np.full((2, 3), 13.)
                                        ]),
                                        hpi=np.concatenate([
                                            np.full((2, 3), 21.),
                                            np.full((2, 3), 22.),
                                            np.full((2, 3), 23.)
                                        ]),
                                        ch_pos={
                                            'h': [41, 41, 41],
                                            'b': [42, 42, 42],
                                            'g': [43, 43, 43],
                                            'n': [51, 51, 51],
                                            'y': [52, 52, 52],
                                            'p': [53, 53, 53],
                                            'v': [61, 61, 61],
                                            'a': [62, 62, 62],
                                            'l': [63, 63, 63],
                                        })

    # Do some checks to ensure they are the same DigMontage
    assert len(montage.ch_names) == len(EXPECTED_MONTAGE.ch_names)
    assert all([c in montage.ch_names for c in EXPECTED_MONTAGE.ch_names])
    actual_occurrences = _count_points_by_type(montage.dig)
    expected_occurrences = _count_points_by_type(EXPECTED_MONTAGE.dig)
    assert actual_occurrences == expected_occurrences
示例#13
0
def test_warp_montage_volume():
    """Test warping an montage based on intracranial electrode positions."""
    import nibabel as nib
    subject_brain = nib.load(
        op.join(subjects_dir, 'sample', 'mri', 'brain.mgz'))
    template_brain = nib.load(
        op.join(subjects_dir, 'fsaverage', 'mri', 'brain.mgz'))
    zooms = dict(translation=10, rigid=10, sdr=10)
    reg_affine, sdr_morph = compute_volume_registration(
        subject_brain, template_brain, zooms=zooms,
        niter=[3, 3, 3],
        pipeline=('translation', 'rigid', 'sdr'))
    # make an info object with three channels with positions
    ch_coords = np.array([[-8.7040273, 17.99938754, 10.29604017],
                          [-14.03007764, 19.69978401, 12.07236939],
                          [-21.1130506, 21.98310911, 13.25658887]])
    ch_pos = dict(zip(['1', '2', '3'], ch_coords / 1000))  # mm -> m
    lpa, nasion, rpa = get_mni_fiducials('sample', subjects_dir)
    montage = make_dig_montage(ch_pos, lpa=lpa['r'], nasion=nasion['r'],
                               rpa=rpa['r'], coord_frame='mri')
    # make fake image based on the info
    CT_data = np.zeros(subject_brain.shape)
    # convert to voxels
    ch_coords_vox = apply_trans(
        np.linalg.inv(subject_brain.header.get_vox2ras_tkr()), ch_coords)
    for (x, y, z) in ch_coords_vox.round().astype(int):
        # make electrode contact hyperintensities
        # first, make the surrounding voxels high intensity
        CT_data[x - 1:x + 2, y - 1:y + 2, z - 1:z + 2] = 500
        # then, make the center even higher intensity
        CT_data[x, y, z] = 1000
    CT = nib.Nifti1Image(CT_data, subject_brain.affine)
    ch_coords = np.array([[-8.7040273, 17.99938754, 10.29604017],
                          [-14.03007764, 19.69978401, 12.07236939],
                          [-21.1130506, 21.98310911, 13.25658887]])
    ch_pos = dict(zip(['1', '2', '3'], ch_coords / 1000))  # mm -> m
    lpa, nasion, rpa = get_mni_fiducials('sample', subjects_dir)
    montage = make_dig_montage(ch_pos, lpa=lpa['r'], nasion=nasion['r'],
                               rpa=rpa['r'], coord_frame='mri')
    montage_warped, image_from, image_to = warp_montage_volume(
        montage, CT, reg_affine, sdr_morph, 'sample',
        subjects_dir_from=subjects_dir, thresh=0.99)
    # checked with nilearn plot from `tut-ieeg-localize`
    # check montage in surface RAS
    ground_truth_warped = np.array([[-0.009, -0.00133333, -0.033],
                                    [-0.01445455, 0.00127273, -0.03163636],
                                    [-0.022, 0.00285714, -0.031]])
    for i, d in enumerate(montage_warped.dig):
        assert np.linalg.norm(  # off by less than 1.5 cm
            d['r'] - ground_truth_warped[i]) < 0.015
    # check image_from
    for idx, contact in enumerate(range(1, len(ch_pos) + 1)):
        voxels = np.array(np.where(np.array(image_from.dataobj) == contact)).T
        assert ch_coords_vox.round()[idx] in voxels
        assert ch_coords_vox.round()[idx] + 5 not in voxels
    # check image_to, too many, just check center
    ground_truth_warped_voxels = np.array(
        [[135.5959596, 161.97979798, 123.83838384],
         [143.11111111, 159.71428571, 125.61904762],
         [150.53982301, 158.38053097, 127.31858407]])
    for i in range(len(montage.ch_names)):
        assert np.linalg.norm(
            np.array(np.where(np.array(image_to.dataobj) == i + 1)
                     ).mean(axis=1) - ground_truth_warped_voxels[i]) < 8
    # test inputs
    with pytest.raises(ValueError, match='`thresh` must be between 0 and 1'):
        warp_montage_volume(
            montage, CT, reg_affine, sdr_morph, 'sample', thresh=11.)
    with pytest.raises(ValueError, match='subject folder is incorrect'):
        warp_montage_volume(
            montage, CT, reg_affine, sdr_morph, subject_from='foo')
    CT_unaligned = nib.Nifti1Image(CT_data, template_brain.affine)
    with pytest.raises(RuntimeError, match='not aligned to Freesurfer'):
        warp_montage_volume(montage, CT_unaligned, reg_affine,
                            sdr_morph, 'sample',
                            subjects_dir_from=subjects_dir)
    bad_montage = montage.copy()
    for d in bad_montage.dig:
        d['coord_frame'] = 99
    with pytest.raises(RuntimeError, match='Coordinate frame not supported'):
        warp_montage_volume(bad_montage, CT, reg_affine,
                            sdr_morph, 'sample',
                            subjects_dir_from=subjects_dir)

    # check channel not warped
    ch_pos_doubled = ch_pos.copy()
    ch_pos_doubled.update(zip(['4', '5', '6'], ch_coords / 1000))
    doubled_montage = make_dig_montage(
        ch_pos_doubled, lpa=lpa['r'], nasion=nasion['r'],
        rpa=rpa['r'], coord_frame='mri')
    with pytest.warns(RuntimeWarning, match='not assigned'):
        warp_montage_volume(doubled_montage, CT, reg_affine,
                            None, 'sample', subjects_dir_from=subjects_dir)
def _fake_montage(ch_names):
    pos = np.random.RandomState(42).randn(len(ch_names), 3)
    return make_dig_montage(ch_pos=dict(zip(ch_names, pos)),
                            coord_frame='head')
示例#15
0
def test_warp_montage_volume():
    """Test warping an montage based on intracranial electrode positions."""
    import nibabel as nib
    subject_T1 = nib.load(op.join(subjects_dir, 'sample', 'mri', 'T1.mgz'))
    subject_brain = nib.load(
        op.join(subjects_dir, 'sample', 'mri', 'brain.mgz'))
    template_brain = nib.load(
        op.join(subjects_dir, 'fsaverage', 'mri', 'brain.mgz'))
    reg_affine, sdr_morph = compute_volume_registration(
        subject_brain,
        template_brain,
        zooms=5,
        niter=dict(translation=[5, 5, 5], rigid=[5, 5, 5], sdr=[3, 3, 3]),
        pipeline=('translation', 'rigid', 'sdr'))
    # make fake image with three coordinates
    CT_data = np.zeros(subject_brain.shape)
    # make electrode contact hyperintensities
    CT_data[45:47, 39:41, 49:50] = 500  # surround high intensity
    CT_data[46, 40, 49] = 1000  # center even higher intensity
    CT_data[47:49, 39:40, 49:50] = 500
    CT_data[48, 39, 50] = 1000
    CT_data[50:52, 38:40, 50:51] = 500
    CT_data[50, 39, 50] = 1000
    CT = nib.Nifti1Image(CT_data, subject_T1.affine)
    ch_coords = np.array([[-8.7040273, 17.99938754, 10.29604017],
                          [-14.03007764, 19.69978401, 12.07236939],
                          [-21.1130506, 21.98310911, 13.25658887]])
    ch_pos = dict(zip(['1', '2', '3'], ch_coords / 1000))  # mm -> m
    montage = make_dig_montage(ch_pos, coord_frame='mri')
    montage_warped, image_from, image_to = warp_montage_volume(
        montage,
        CT,
        reg_affine,
        sdr_morph,
        'sample',
        subjects_dir=subjects_dir,
        thresh=0.99)
    # checked with nilearn plot from `tut-ieeg-localize`
    # check montage in surface RAS
    ground_truth_warped = np.array([[-0.27778788, 0.24251515, -0.35693939],
                                    [-0.30033333, 0.24785714, -0.35014286],
                                    [-0.32261947, 0.25295575, -0.34614159]])
    for i in range(len(montage.ch_names)):
        assert np.linalg.norm(  # off by less than 1.5 cm
            montage_warped.dig[i]['r'] - ground_truth_warped[i]) < 0.015
    # check image_from
    assert_array_equal(np.array(np.where(_get_img_fdata(image_from) == 1)),
                       np.array([[45, 46, 46], [40, 39, 40], [49, 49, 49]]))
    assert_array_equal(np.array(np.where(_get_img_fdata(image_from) == 2)),
                       np.array([[48, 48], [39, 39], [49, 50]]))
    assert_array_equal(np.array(np.where(_get_img_fdata(image_from) == 3)),
                       np.array([[50, 50, 51], [38, 39, 39], [50, 50, 50]]))
    # check image_to, too many, just check center
    ground_truth_warped_voxels = np.array(
        [[135.5959596, 161.97979798, 123.83838384],
         [143.11111111, 159.71428571, 125.61904762],
         [150.53982301, 158.38053097, 127.31858407]])
    for i in range(len(montage.ch_names)):
        assert np.linalg.norm(
            np.array(np.where(_get_img_fdata(image_to) == i + 1)).mean(
                axis=1) - ground_truth_warped_voxels[i]) < 5

    # test inputs
    with pytest.raises(ValueError, match='`thresh` must be between 0 and 1'):
        warp_montage_volume(montage,
                            CT,
                            reg_affine,
                            sdr_morph,
                            'sample',
                            thresh=11.)
    with pytest.raises(ValueError, match='subject folder is incorrect'):
        warp_montage_volume(montage,
                            CT,
                            reg_affine,
                            sdr_morph,
                            subject_from='foo')
    CT_unaligned = nib.Nifti1Image(CT_data, subject_brain.affine)
    with pytest.raises(RuntimeError, match='not aligned to Freesurfer'):
        warp_montage_volume(montage,
                            CT_unaligned,
                            reg_affine,
                            sdr_morph,
                            'sample',
                            subjects_dir=subjects_dir)
    bad_montage = make_dig_montage(ch_pos, coord_frame='mri')
    bad_montage.dig[0]['coord_frame'] = 99
    with pytest.raises(RuntimeError,
                       match='Only single coordinate frame in '
                       'dig is supported'):
        warp_montage_volume(bad_montage,
                            CT,
                            reg_affine,
                            sdr_morph,
                            'sample',
                            subjects_dir=subjects_dir)
    wrong_montage = make_dig_montage(ch_pos, coord_frame='head')
    with pytest.raises(RuntimeError, match='Coordinate frame not supported'):
        warp_montage_volume(wrong_montage,
                            CT,
                            reg_affine,
                            sdr_morph,
                            'sample',
                            subjects_dir=subjects_dir)

    # check channel not warped
    ch_pos_doubled = ch_pos.copy()
    ch_pos_doubled.update(zip(['4', '5', '6'], ch_coords / 1000))
    doubled_montage = make_dig_montage(ch_pos_doubled, coord_frame='mri')
    with pytest.warns(RuntimeWarning, match='not assigned'):
        warp_montage_volume(doubled_montage,
                            CT,
                            reg_affine,
                            sdr_morph,
                            'sample',
                            subjects_dir=subjects_dir)
def test_transform_to_head_and_compute_dev_head_t():
    """Test transform_to_head and compute_dev_head_t."""
    EXPECTED_DEV_HEAD_T = \
        [[-3.72201691e-02, -9.98212167e-01, -4.67667497e-02, -7.31583414e-04],
         [8.98064989e-01, -5.39382685e-02, 4.36543170e-01, 1.60134431e-02],
         [-4.38285221e-01, -2.57513699e-02, 8.98466990e-01, 6.13035748e-02],
         [0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]

    EXPECTED_FID_IN_POLHEMUS = {
        'nasion': np.array([0.001393, 0.0131613, -0.0046967]),
        'lpa': np.array([-0.0624997, -0.0737271, 0.07996]),
        'rpa': np.array([-0.0748957, 0.0873785, 0.0811943]),
    }

    EXPECTED_FID_IN_HEAD = {
        'nasion': np.array([-8.94466792e-18, 1.10559624e-01, -3.85185989e-34]),
        'lpa': np.array([-8.10816716e-02, 6.56321671e-18, 0]),
        'rpa': np.array([8.05048781e-02, -6.47441364e-18, 0]),
    }

    hpi_dev = np.array(
        [[ 2.13951493e-02,  8.47444056e-02, -5.65431188e-02],  # noqa
         [ 2.10299433e-02, -8.03141101e-02, -6.34420259e-02],  # noqa
         [ 1.05916829e-01,  8.18485672e-05,  1.19928083e-02],  # noqa
         [ 9.26595105e-02,  4.64804385e-02,  8.45141253e-03],  # noqa
         [ 9.42554419e-02, -4.35206589e-02,  8.78999363e-03]]  # noqa
    )

    hpi_polhemus = np.array(
        [[-0.0595004, -0.0704836,  0.075893 ],  # noqa
         [-0.0646373,  0.0838228,  0.0762123],  # noqa
         [-0.0135035,  0.0072522, -0.0268405],  # noqa
         [-0.0202967, -0.0351498, -0.0129305],  # noqa
         [-0.0277519,  0.0452628, -0.0222407]]  # noqa
    )

    montage_polhemus = make_dig_montage(
        **EXPECTED_FID_IN_POLHEMUS, hpi=hpi_polhemus, coord_frame='unknown'
    )

    montage_meg = make_dig_montage(hpi=hpi_dev, coord_frame='meg')

    # Test regular worflow to get dev_head_t
    montage = montage_polhemus + montage_meg
    fids, _ = _get_fid_coords(montage.dig)
    for kk in fids:
        assert_allclose(fids[kk], EXPECTED_FID_IN_POLHEMUS[kk], atol=1e-5)

    with pytest.raises(ValueError, match='set to head coordinate system'):
        _ = compute_dev_head_t(montage)

    montage = transform_to_head(montage)

    fids, _ = _get_fid_coords(montage.dig)
    for kk in fids:
        assert_allclose(fids[kk], EXPECTED_FID_IN_HEAD[kk], atol=1e-5)

    dev_head_t = compute_dev_head_t(montage)
    assert_allclose(dev_head_t['trans'], EXPECTED_DEV_HEAD_T, atol=1e-7)

    # Test errors when number of HPI points do not match
    EXPECTED_ERR_MSG = 'Device-to-Head .*Got 0 .*device and 5 points in head'
    with pytest.raises(ValueError, match=EXPECTED_ERR_MSG):
        _ = compute_dev_head_t(transform_to_head(montage_polhemus))

    EXPECTED_ERR_MSG = 'Device-to-Head .*Got 5 .*device and 0 points in head'
    with pytest.raises(ValueError, match=EXPECTED_ERR_MSG):
        _ = compute_dev_head_t(transform_to_head(
            montage_meg + make_dig_montage(**EXPECTED_FID_IN_POLHEMUS)
        ))

    EXPECTED_ERR_MSG = 'Device-to-Head .*Got 3 .*device and 5 points in head'
    with pytest.raises(ValueError, match=EXPECTED_ERR_MSG):
        _ = compute_dev_head_t(transform_to_head(
            DigMontage(dig=_format_dig_points(montage_meg.dig[:3])) +
            montage_polhemus
        ))
示例#17
0
def test_plot_ch_adjacency():
    """Test plotting of adjacency matrix."""
    xyz_pos = np.array([[-0.1, 0.1, 0.1], [0.1, 0.1, 0.1], [0., 0., 0.12],
                        [-0.1, -0.1, 0.1], [0.1, -0.1, 0.1]])

    info = create_info(list('abcde'), 23, ch_types='eeg')
    montage = make_dig_montage(
        ch_pos={ch: pos for ch, pos in zip(info.ch_names, xyz_pos)},
        coord_frame='head')
    info.set_montage(montage)

    # construct adjacency
    adj_sparse, ch_names = find_ch_adjacency(info, 'eeg')

    # plot adjacency
    fig = plot_ch_adjacency(info, adj_sparse, ch_names, kind='2d', edit=True)

    # find channel positions
    collection = fig.axes[0].collections[0]
    pos = collection.get_offsets().data

    # get adjacency lines
    lines = fig.axes[0].lines[4:]  # (first four lines are head outlines)

    # make sure lines match adjacency relations in the matrix
    for line in lines:
        x, y = line.get_data()
        ch_idx = [np.where((pos == [[x[ix], y[ix]]]).all(axis=1))[0][0]
                  for ix in range(2)]
        assert adj_sparse[ch_idx[0], ch_idx[1]]

    # make sure additional point is generated after clicking a channel
    _fake_click(fig, fig.axes[0], pos[0], xform='data')
    collections = fig.axes[0].collections
    assert len(collections) == 2

    # make sure the point is green
    green = matplotlib.colors.to_rgba('tab:green')
    assert (collections[1].get_facecolor() == green).all()

    # make sure adjacency entry is modified after second click on another node
    assert adj_sparse[0, 1]
    assert adj_sparse[1, 0]
    n_lines_before = len(lines)
    _fake_click(fig, fig.axes[0], pos[1], xform='data')

    assert not adj_sparse[0, 1]
    assert not adj_sparse[1, 0]

    # and there is one line less
    lines = fig.axes[0].lines[4:]
    n_lines_after = len(lines)
    assert n_lines_after == n_lines_before - 1

    # make sure there is still one green point ...
    collections = fig.axes[0].collections
    assert len(collections) == 2
    assert (collections[1].get_facecolor() == green).all()

    # ... but its at a different location
    point_pos = collections[1].get_offsets().data
    assert (point_pos == pos[1]).all()

    # check that clicking again removes the green selection point
    _fake_click(fig, fig.axes[0], pos[1], xform='data')
    collections = fig.axes[0].collections
    assert len(collections) == 1

    # clicking the points again adds a green line
    _fake_click(fig, fig.axes[0], pos[1], xform='data')
    _fake_click(fig, fig.axes[0], pos[0], xform='data')

    lines = fig.axes[0].lines[4:]
    assert len(lines) == n_lines_after + 1
    assert lines[-1].get_color() == 'tab:green'

    # smoke test for 3d option
    adj = adj_sparse.toarray()
    fig = plot_ch_adjacency(info, adj, ch_names, kind='3d')

    # test errors
    # -----------
    # number of channels in the adjacency matrix and info must match
    msg = ("``adjacency`` must have the same number of rows as the number of "
           "channels in ``info``")
    with pytest.raises(ValueError, match=msg):
        plot_ch_adjacency(info, adj_sparse, ch_names[:3], kind='2d')

    # edition mode only available for 2d plot
    msg = "Editing a 3d adjacency plot is not supported."
    with pytest.raises(ValueError, match=msg):
        plot_ch_adjacency(info, adj, ch_names, kind='3d', edit=True)