1. 使用 Matlab 中的 edfmex 读取 .edf 数据
使用 Matlab 读取 edf 文件,需要使用 edfmex。如果尝试过百度搜索“Matlab读取edf文件”,您可能会找到 edfread() 这个函数。edfread() 是用来读取脑电 edf 数据的,而非眼动的 edf 数据。
1.1 下载 edfmex
下载链接: https://pan.baidu.com/s/1I1XeEht2tWQAs0aMaZ0-VA 密码: oe6w
首先,下载上面链接中的压缩文件,解压后您会发现有很多个版本的 edfmex。
edfmex
├── edfmex.mexa64
├── edfmex.mexglx
├── edfmex.mexmaci
├── edfmex.mexmaci64
├── edfmex.mexw32
└── edfmex.mexw64
根据电脑和系统的不同,您需要的文件版本也不尽相同。您可以在 Matlab 中使用 computer('arch')
函数来检测 Matlab 的版本。
例如 Charlie 使用的是 64位 MacOS,在 Command Window 中输入 computer('arch')
直接返回 ‘maci64’。因此,Charlie 应该使用 edfmex.mexmaci64 读取 edf 文件。其他5个类型不匹配的文件对我的电脑来说都是不需要的。
>> computer('arch')
ans =
'maci64'
随后我将 edfmex.mexmaci64 拷贝至我自定义的 Matlab 路径下。
当然您也可以简单粗暴地将整个 edfmex 文件夹添加至 Matlab 的路径下。虽然有不需要的文件,但并不会影响使用正确的 edfmex 进行数据读取。
1.2 读取 edf 文件
cd 到 edf 文件的文件夹后使用 edf = edfmex(filename)
函数读取数据。其中 filename 既可以使用绝对路径也可以使用相对路径。
注意:edf = edfmex(filename)
等号左边的结构体名不可省略。
详情参照如下的例子:
>>>edf=edfmex('br.edf')
Loading:100%
edf =
包含以下字段的 struct:
FSAMPLE: [1×1 struct]
FEVENT: [1×2287 struct]
IOEVENT: [1×56 struct]
RECORDINGS: [1×16 struct]
HEADER: '** DATE: Fri Jul 6 12:35:28 2018↵** TYPE: EDF_FILE BINARY EVENT SAMPLE TAGGED↵** VERSION: EYELINK II 1↵** SOURCE: EYELINK CL↵** EYELINK II CL v5.12 May 12 2017↵** CAMERA: Eyelink GL Version 1.2 Sensor=AJ7↵** SERIAL NUMBER: CLG-BBC50↵** CAMERA_CONFIG: BBC50200.SCD↵** RECORDED BY Picture_DV↵** SREB2.2.0.321 WIN32 LID:2A80E100 Mod:2018.07.05 06:21 PDT↵'
FILENAME: 'br.edf'
2. 使用 pyedfread 在 Python 中读取 .edf 数据
截止目前,SR Research官方还未发布官方的读取方法。
但是有一位用户在 Github 上上传了一个名为 pyedfread 的开源项目。在自述文件中作者对此项目做了如下描述:将 .edf Eyelink 眼动数据数据直接读取为 pandas 的 DataFrames。
这个项目最后一次更新是 2019 年 7 月。Charlie 自测可用,详情请点击如下传送门:
pyedfread - A utility that parses SR research EDF data files into pandas DataFrames.
3. 数据结构
FSAMPLE: [1×1 struct]
FEVENT: [1×2287 struct]
IOEVENT: [1×56 struct]
RECORDINGS: [1×16 struct]
HEADER: '** DATE: Fri Jul 6 12:35:28 2018↵** TYPE: EDF_FILE BINARY EVENT SAMPLE TAGGED↵** VERSION: EYELINK II 1↵** SOURCE: EYELINK CL↵** EYELINK II CL v5.12 May 12 2017↵** CAMERA: Eyelink GL Version 1.2 Sensor=AJ7↵** SERIAL NUMBER: CLG-BBC50↵** CAMERA_CONFIG: BBC50200.SCD↵** RECORDED BY Picture_DV↵** SREB2.2.0.321 WIN32 LID:2A80E100 Mod:2018.07.05 06:21 PDT↵'
FILENAME: 'br.edf'
在’edf’结构体的 6 个字段之中,核心数据在 ‘FSAMPLE’ 和 ‘FEVENT’ 中。其他部分暂且省略搁置。
3.1 FSAMPLE
如下图所示,’FSAMPLE’ 结构体中,存在如下字段。
每个字段的长度都是相同的(64681),根据情况存在 1~2 行。
...
├── FSAMPLE
├── time # 时间戳,单位毫秒
├── px # 在相机视野中 Pupil 中心的 x 坐标
├── py # 在相机视野中 Pupil 中心的 y 坐标
├── hx # HREF(头部参考)的 x 方向角度坐标
├── hy # HREF(头部参考)的 y 方向角度坐标
├── pa # 瞳孔尺寸(根据程序设置为直径或面积)
├── gx # 注视位置在屏幕上的 x 方向像素坐标(常用)
├── gy # 注视位置在屏幕上的 y 方向像素坐标(常用)
├── rx # 当前注视位置在 x 方向上每度视角对应的像素数
├── ry # 当前注视位置在 y 方向上每度视角对应的像素数
├── gxvel # 当前眼睛在 x 方向的运动速度,基于屏幕的像素为单位计算
├── gyvel # 当前眼睛在 y 方向的运动速度,基于屏幕的像素为单位计算
├── hxvel # 当前眼睛在 x 方向的运动速度,基于 HREF 计算
├── hyvel # 当前眼睛在 y 方向的运动速度,基于 HREF 计算
├── rxvel # 当前眼睛在 x 方向的运动速度,基于原始数据(瞳孔在相机中的位置)计算
├── ryvel # 当前眼睛在 y 方向的运动速度,基于原始数据(瞳孔在相机中的位置)计算
├── fgxvel # 快速计算的 gxvel
├── fgyvel # 快速计算的 gyvel
├── ghxvel # 快速计算的 hxvel
├── ghyvel # 快速计算的 hyvel
├── frxvel # 快速计算的 rxvel
├── fryvel # 快速计算的 ryvel
├── hdata # 头部追踪数据(未缩放)
├── flags # 眼动仪事件标记
├── input # 其他设备输入数据(主试机TTL)
├── buttons # 按键
├── htype # 头部追踪数据类型(0为不存在)
└── errors # 错误标记
└── ...
为方面理解记忆,此处我们其实可以进行如下的关键词解构:
> x - x方向
> y - y方向
> p - 瞳孔
> h - 头部参考/Head-Reference
> - 头部追踪/Head-Tracker
> g - 基于屏幕的数据/Gaze
> r - 瞳孔在相机中的原始位置数据/Raw
> - 数值变换/Reference
> vel - 速度
> f - 快速计算
3.2 FEVENT
在 FEVENT
结构体中,每行都一个 Event
。
...
├── ...
├── FEVENT
├── time # 记录本事件的耗时(记录延迟)
├── type # 时间类型(数字编号)
├── read # 标记包括哪些项目
├── sttime # 事件开始的时间
├── entime # 事件结束的时间
├── hstx # 事件起始位置的 HREF x 坐标
├── hsty # 事件起始位置的 HREF y 坐标
├── gstx # 事件起始位置的屏幕像素 x 坐标
├── gsty # 事件起始位置的屏幕像素 y 坐标
├── sta # 事件开始时的瞳孔尺寸
├── henx # 事件结束位置的 HREF x 坐标
├── heny # 事件结束位置的 HREF y 坐标
├── genx # 事件结束位置的屏幕像素 x 坐标
├── geny # 事件结束位置的屏幕像素 y 坐标
├── ena # 事件结束时的瞳孔尺寸
├── havx # 平均 HREF x 坐标
├── havy # 平均 HREF y 坐标
├── gavx # 平均屏幕像素 x 坐标
├── gavy # 平均屏幕像素 y 坐标
├── ava # 事件过程中平均的瞳孔尺寸
├── avel # 累计平均速度
├── pvel # 累计峰值速度
├── svel # 事件开始的运动速度
├── evel # 事件结束的运动速度
├── supd_x # 事件起始位置 x 方向每度视角对应像素数
├── eupd_x # 事件结束位置 x 方向每度视角对应像素数
├── supd_y # 事件起始位置 y 方向每度视角对应像素数
├── eupd_y # 事件结束位置 y 方向每度视角对应像素数
├── eye # 追踪的眼睛 0=left 1=right 2=both
├── status # 错误或警告标记
├── flags # 错误或警告标记
├── input # 主试机 TTL 信号输入
├── buttons # 按键反映
├── parsedby # 7位标志:PARSEDBY码
├── message # message文本
└── codestring # 事件类型描述(关键信息)
└── ...
在这么多行 Event
中,每行的 codestring
标记了本 Event
的类型。codestring
共有如下几种:
实际使用过程中我们只需要以下几种:
ENDFIX - 此条 `Event` 中包括了对应 注视 的全部信息
ENDSACC - 此条 `Event` 中包括了对应 眼跳 的全部信息
ENDBLINK - 此条 `Event` 中包括了对应 眨眼 的全部信息
MESSAGEEVENT - 这是一条 Message
我们可以看到在 codestring
中也包含很多 “START” 和 “UPDATE” 相关的内容,标记事件检测的进程。但是其实这些数据已经全部包含在了 “END” 相关的 Event
中。因此我们只需要检测注视、眼跳和眨眼的 “END” 信息即可获取该眼动事件的全部信息。
程序中的 Message 全部用 “MESSAGEEVENT” 标记出来。
3.3 其他
略。
以上。