2023/1/2 -1/3脑机接口学习内容一览:
这一篇博客主要是对自己这几个星期学习脑机接口基本操作情况的总结,对刚进入这个领域、感觉有些迷茫的同学来说可能会有一定的参考价值。
这项工作主要考验自己对脑电信号基本处理流程的熟悉程度,在写代码的同时也发现了自己较多的不足,有些bug也未能完美处理。但是相对于一个星期前一知半解的状态明显有着很大的进步。
典型的 M/EEG 工作流程
import mne
import matplotlib.pyplot as plt
import numpy as np"""
第一步:导入数据集,定位通道数据
通过mne.io.read_raw_eeglab来读取.set文件
得到原始数据对象
p.s.此数据集为eeglab的自带数据集
在此文件中已经包含各个脑电通道的位置,故在此不读入.locs文件
"""
raw = mne.io.read_raw_eeglab("secdata.set", preload=True, uint16_codec=None)
# 提取0~20s的数据
tmin, tmax = 0, 20
raw.crop(tmin, tmax).load_data()
print(raw.info)'''
第二步:删除无用通道的数据
在采集数据的过程中,有可能会记录一些后期并不需要用到的通道信息,这时候我们就可以将它们剔除掉,不必纳入后续的分析中。
比如双侧乳突点。又比如眼电通道的数据。
在使用eeglab来对数据进行预处理的时候,可以通过ICA的方式来去除眼电成分
而这种方式可以不需要眼电通道数据的参与就可以进行。
在此我们将不需要用到的通道列入bads中
这里将两个眼电信号通道加入bads
'''
raw.info['bads'].extend(['EOG1', 'EOG2'])"""
绘制从第5s开始,5s时间窗口长的原始数据start:指定开始绘制的时间
duration:要绘制的时间窗口从该图中可以看出每个通道的数据波动情况
可以通过设置duration的大小来放大或缩小这个有点类似于eeglab中,放大缩小功能。
这里相同大小的窗口显示更短时间的数据,相当于eeglab中的选中数据进行放大,
反之,即缩小。
"""
raw.plot(start=5, duration=5)
plt.show()"""
打印通道名
p.s.标记为bads后的通道仍可见,但在各种操作中bads通道不对整体产生影响
"""
print('all the channels are\n', raw.info['ch_names'])# 绘制电极位置
raw.plot_sensors("3d")
plt.show()"""
绘制通道位置图,并对应位置上显示通道名称
在此文件中已经包含各个脑电通道的位置,故在此不读入.locs文件
"""
layout_from_raw = mne.channels.make_eeg_layout(raw.info)
layout_from_raw.plot()
plt.show()'''
第三步:重参考
与中文教程不同。这里我们参照文档中事件相关电位的处理过程,先进行重参考
这里我们选择平均参考
'''
raw.set_eeg_reference('average', projection=True)'''
第四步:滤波
因为这个数据集似乎是记录观察者看到不同形状的图形的反应
为了去除电源线噪声,我们做一个凹陷滤波
我们暂且假设他的alpha波比较活跃,故做一个9-13hz的带通滤波
为了去除漂移现象,再做一个1hz的高通滤波
为了使操作更加直观,我们可以使用subplots函数将几张图对比来看
'''
fmin, fmax = 9, 13
fig, ax = plt.subplots(1, 4, figsize=(16, 4))
raw.compute_psd(method='welch', average=False).plot(axes=ax[0])
raw.notch_filter(60)
raw.compute_psd(method='welch', average=False).plot(axes=ax[1])
raw.filter(fmin, fmax)
raw.compute_psd(method='welch', average=False).plot(axes=ax[2])
raw.filter(1., None)
raw.compute_psd(method='welch', average=False).plot(axes=ax[3])for ax, title in zip(ax[:4], ['native', '60hz filter', '9hz-13hz pass', '1hz high pass']):ax.set_title(title)
plt.show()'''
第四步:根据事件分段,提取epochs
在这个set数据集中暂且只有annotation而没有events事件
故我们需要考虑将注释转化为我们需要的事件
即Consider using mne.events_from_annotations to convert these to events.
'''
# 打印注释信息,发现存在5个rt注释,8个square注释
print(raw.annotations)
# 将rt改名为事件1,square改名为事件2
dictionary = {'rt': 1, 'square': 2}
events, event_id = mne.events_from_annotations(raw, event_id=dictionary)
print(events, '\n', event_id)'''
创建epochs对象
这里我们用事件来创造epoch对象
取事件前0.2s至事件后1s创建,并且将事件前0.2秒至事件发生的0s作为基线校正
print可以看见epoch共包含5个rt事件,8个square事件
'''
epochs = mne.Epochs(raw, events, event_id, tmin=-0.2, tmax=1,baseline=(None, 0), preload=True)
print(epochs.info)'''
通过epochs平均叠加得到evoked对象,然后进行绘图
(不知道为什么线条那么乱,可能是前面的操作有问题?)
'''
see_rt = epochs['rt'].average()
see_square = epochs['square'].average()
f, axs = plt.subplots(1, 2, figsize=(10, 4))
_ = f.suptitle('rt & square events in eeg', fontsize=20)
_ = see_rt.plot(axes=axs[0], show=False, time_unit='s')
_ = see_square.plot(axes=axs[1], show=False, time_unit='s')
plt.show()'''
第五步:时频域分析
这里使用小波变换做时频域分析
'''
# 时频分析
freqs = np.logspace(*np.log10([9, 13]), num=10)
n_cycles = freqs/2.
power, itc = mne.time_frequency.tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles, use_fft=True)# 时频结果绘制
power.plot(picks=['O1'], baseline=(None, 0), mode='logratio', title='auto') # 枕叶导联的power结果
power.plot_topo(baseline=(-0.5, 0), mode='logratio', title='Average power') # 绘制power拓扑图
参考文献:
脑电时频分析
用MNE包进行Python脑电数据处理
脑电数据处理分析教程汇总(eeglab, mne-python)