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'

»>关于computer()函数的更多信息请参考 Matlab 官方论坛«<

随后我将 edfmex.mexmaci64 拷贝至我自定义的 Matlab 路径下。

当然您也可以简单粗暴地将整个 edfmex 文件夹添加至 Matlab 的路径下。虽然有不需要的文件,但并不会影响使用正确的 edfmex 进行数据读取。

1.2 读取 edf 文件

cd 到 edf 文件的文件夹后使用 edf = defmex(filename) 函数读取数据。其中 filename 既可以使用绝对路径也可以使用相对路径。

注意:edf = defmex(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'

matlab_defmex_edfstructure


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’ 结构体中,存在如下字段。

matlab_edfmex_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 共有如下几种:

matlab_defmex_FEVENT_codestring

实际使用过程中我们只需要以下几种:

ENDFIX          - 此条 `Event` 中包括了对应 注视 的全部信息
ENDSACC         - 此条 `Event` 中包括了对应 眼跳 的全部信息
ENDBLINK        - 此条 `Event` 中包括了对应 眨眼 的全部信息
MESSAGEEVENT    - 这是一条 Message

我们可以看到在 codestring 中也包含很多 “START” 和 “UPDATE” 相关的内容,标记事件检测的进程。但是其实这些数据已经全部包含在了 “END” 相关的 Event 中。因此我们只需要检测注视、眼跳和眨眼的 “END” 信息即可获取该眼动事件的全部信息。

程序中的 Message 全部用 “MESSAGEEVENT” 标记出来。

3.3 其他

略。


以上。