一、EEG数据格式与读取:国际10-20系统、EDF/BDF文件格式、使用MNE读取原始数据
大家好,我是你们这门课的主讲。今天咱们来聊聊EEG数据的“门道”。
做脑电信号处理,第一步就是得搞清楚数据长什么样、怎么存、怎么读。这就像你要做一道菜,总得先认识食材、知道怎么切吧?
我个人习惯,在开始任何深度学习项目前,都会花至少半天时间把数据格式摸透。为什么?因为格式不对,后面全是白费功夫。你想想看,模型训练到一半发现数据读错了,那得多崩溃。
1.1 国际10-20系统:电极的“GPS坐标”
先说说电极是怎么放的。国际10-20系统,说白了就是一套给大脑头皮画“地图”的标准。
为什么叫10-20?因为相邻电极之间的距离,是颅骨前后或左右总长的10%或20%。嗯,就是这么直白。
这套系统有几个关键点:
- 命名规则:F代表额叶(Frontal),C代表中央区(Central),P代表顶叶(Parietal),O代表枕叶(Occipital),T代表颞叶(Temporal)。
- 左右区分:奇数(1,3,5...)在左侧,偶数(2,4,6...)在右侧。z代表中线。
- 参考电极:通常用A1/A2(耳垂)或Cz作为参考。
重要提示:我在项目中遇到过,有些公开数据集用的不是标准10-20命名,比如把Fz写成了FZ。这种大小写不一致,在MNE里会报错。所以拿到数据第一件事,先检查电极名称。
下面我用一张SVG图,帮你直观理解10-20系统的电极布局:
1.2 EDF/BDF文件格式:脑电数据的“容器”
搞清楚了电极位置,接下来看看数据怎么存的。EEG领域最常见的格式就是EDF和BDF。
EDF(European Data Format):欧洲数据格式,诞生于1992年。它把多通道的生理信号(EEG、ECG、EOG等)打包在一个文件里。每个通道可以有不同的采样率。
BDF(Biosemi Data Format):可以理解为EDF的升级版。BDF用24位来存数据,而EDF只用16位。这意味着BDF的动态范围更大,能记录更微弱的信号。
| 特性 | EDF | BDF |
|---|---|---|
| 位深 | 16位 | 24位 |
| 最大通道数 | 64(通常) | 256+ |
| 文件大小 | 较小 | 较大(约1.5倍) |
| 兼容性 | 最广泛 | Biosemi设备专用 |
我的经验:如果你用的是Biosemi设备,数据默认就是BDF格式。但很多公开数据集(比如PhysioNet上的)都是EDF格式。我个人建议,如果条件允许,优先用BDF。24位精度在做微状态分析时优势很明显。
1.3 使用MNE读取原始数据
好了,理论说完了,咱们来点实际的。MNE-Python是Python生态里处理EEG/MEG数据的神器。我用了快五年,几乎没遇到过它搞不定的格式。
先安装:
pip install mne
然后读取EDF文件:
import mne
# 读取EDF文件
raw = mne.io.read_raw_edf('subject01.edf', preload=True)
# 看看基本信息
print(raw.info)
# 查看通道名称
print(raw.ch_names)
# 查看数据形状(通道数 × 时间点)
data = raw.get_data()
print(data.shape)
就这么简单?嗯,基本就是这样。但实际项目中,你可能会遇到几个坑:
避坑指南:我曾经遇到过一个问题——读取一个BDF文件时,MNE报错说“文件头损坏”。后来发现是文件传输过程中丢包了。所以拿到数据后,第一件事就是用mne.io.read_raw_edf()试试能不能正常读取。如果报错,先检查文件完整性。
读取BDF文件也类似:
# 读取BDF文件
raw_bdf = mne.io.read_raw_bdf('subject01.bdf', preload=True)
# 查看采样频率
sfreq = raw_bdf.info['sfreq']
print(f'采样率: {sfreq} Hz')
# 查看时间长度
print(f'时长: {raw_bdf.times[-1]:.2f} 秒')
读取之后,我通常会做几件事:
- 检查坏通道:用
raw.plot()可视化一下,看看有没有明显噪声大的通道。 - 设置电极位置:用
raw.set_montage('standard_1020')把10-20系统的坐标映射上去。 - 查看事件:如果数据里有刺激标记,用
mne.find_events()提取出来。
举个例子,设置电极位置:
# 设置标准10-20电极位置
montage = mne.channels.make_standard_montage('standard_1020')
raw.set_montage(montage)
# 现在你可以做拓扑图了
raw.plot_sensors(show_names=True)
核心要点:MNE读取数据后,raw对象就是一个“容器”,里面包含了:原始数据、通道信息、采样率、事件标记等。你后续的所有预处理(滤波、去伪迹、分段)都是基于这个raw对象。
最后,我想强调一点:数据读取只是第一步,但这一步走稳了,后面就顺了。我见过太多同学,数据都没读对就开始跑模型,结果折腾半天发现是数据格式问题。
所以,拿到数据后,花10分钟用MNE把基本信息打印出来,看看通道数对不对、采样率对不对、时长对不对。这10分钟,能帮你省下后面10小时。