1.简介及准备工作

当需要批量处理数据集文件时,手动使用EEGlab就变得繁琐复杂,如何使用EEGlab的函数,手动对脑电数据进行预处理呢?本文以Open BMI运动想象数据为例,手动调用EEGlab的函数命令,完成脑电数据的重参考、滤波、降采样等操作,并对处理完成的数据集进行格式加工与保存。

1.1 Open BMI 数据集

Open BMI数据集是一个多范式大数据集,包含54个用户三种范式(MI、ERP、SSVEP任务)的数据,本文选用Open BMI MI 数据进行分析,从下方链接进入进行数据集的下载,其中只下载MI数据就可以。

数据集下载示意

Open BMI 官方网址:openbmi.org

数据集下载链接:GigaDB Dataset - DOI 10.5524/100542 - Supporting data for "EEG Dataset and OpenBMI Toolbox for Three BCI Paradigms: An Investigation into …

关于该数据集的更多资料可以参考官方论文:EEG Dataset and OpenBMI Toolbox for Three BCI Paradigms: An Investigation into BCI Illiteracy.

1.2 EEGlab函数源文件下载

要使用EEGlab的函数文件,需要下载相应的".m"文件,下载“functions”下的所有文件,解压。

EEGlab函数下载示意

下载链接:sccn/eeglab: EEGLAB is an open source signal processing environment for electrophysiological signals running on Matlab and developed at the SCCN/UCSD (github.com)

另外,下载完成后需要将文件夹下所有文件添加到Matlab的路径,否则无法调用。

将文件夹添加到matlab路径

添加的方法建议参考该教程:MATLAB 路径设置_matlab添加到路径和更改文件夹_如若明镜的博客-CSDN博客

2. 数据读取

首先进行数据的读取,设定数据集的session及用户数。

session_num = 2; % 数据集session数目
subject_num = 54; % 数据集用户数

指定session和subject为固定值,编写程序的基本框架:

session = 1;
subject = 1;

依据设定,读取对应的数据集:

% 构建时变读取路径
current_load_path = [data_path, 'sess0', int2str(session), '_subj0', int2str(subject), '_EEG_MI.mat'];
datasets = load(current_load_path);

% 读取训练、测试的CNT数据
cnt_data_train = datasets.EEG_MI_train.x';
cnt_data_test = datasets.EEG_MI_test.x';

注意:此处数据需要进行转置,因为EEGlab中的默认格式为[channel, timepoints],即第一维是电极通道数目,第二维是样本点数,而原始数据的格式维[timepoints, channel]。

3. 将数据加载成EEGlab的格式

使用之前下载的EEGlab函数-"pop_importdata"对原始数据进行装载。函数"pop_importdata"中规定了数据输入的格式为:[channel, timepoints],上节中我们已经按照规定进行了数据的转置,接下来将数据加载(封装)成EEGlab所用的格式(为一个大struct,其中包含了通道数、采样率等许多信息):

% 将数据加载成EEGLab格式 

%'setname':数据集在EEGlab中的名称
%'data':待加载数据名称,可为mat矩阵文件或其余格式
%'dataformat': 数据的格式
%'nbchan':数据的电极通道数目

EEG = pop_importdata('setname','A', ...
    'data', cnt_data_test, ...
    'dataformat', 'array', ...
    'nbchan', 62);

函数"pop_importdata"中有许多可设置的输入参数,在这里我们仅需要输入上述几个基本的参数就可以完成数据的导入,关于其余参数,大家可以自行打开函数文件查看或查阅EEGlab官方维基百科:b. Re-referencing - EEGLAB Wiki

pop_importdata参数说明

4. 数据预处理( 重参考、滤波及降采样 )及数据裁切

4.1 数据预处理

EEGlab的函数能够实现多种脑电数据的预处理,本文以重参考、滤波及降采样为例,其余的操作大家可以访问EEGlab官方百科进行学习和使用:b. Re-referencing - EEGLAB Wiki

进行平均参考:

% 进行平均参考
% Inputs:
%  EEG   - input dataset
%  reference: []  = convert to average reference
%  [int vector]  = new reference electrode number(s)
%  'Cz'  = string
%  { 'P09' 'P10 } = cell array of strings

EEG = pop_reref(EEG, []);

接下来进行EEG数据滤波,采用FIR滤波,低频5Hz,高频40Hz:

% 进行FIR滤波
EEG = pop_eegfilt(EEG, 5, 40);

原数据采样率为1000Hz,为降低数据量,将采样率降低至100Hz,大家可自行设定:

% 进行降采样
EEG = pop_resample(train_EEG, 100);

4.2 数据裁切、标签读取

读取数据各trial的标签,储存成向量形式:

% 读取数据标签
train_class = datasets.EEG_MI_train.y_dec;
test_class = datasets.EEG_MI_test.y_dec;
class = [train_class, test_class];

% 读取裁切信号 cue 因为上方降采样了10倍,所以cue也需要缩小10倍
train_cue = round(datasets.EEG_MI_train.t/10);
test_cue = round(datasets.EEG_MI_test.t/10);
cue = [train_cue, test_cue];

% 数据裁切
cnt = [train_EEG.data, test_EEG.data]; % cnt数据合并
trial = size(cue, 2); % 数据集中的trial数目
task_time = 400; % 执行MI任务的时间
left_cut = 0; % 在cue的左侧裁剪的points数目
right_cut = task_time - 1; % 在cue的右侧裁剪的points数目
for tr = 1:trial
    I = cnt(:, (cue(trial) - left_cut) : (cue(trial) + right_cut));
    cnt_subject(trial, :, :) = I; % 用户的cnt数据 格式为:[trial, channel, samplepoints]
end

接下来我们将4.1与4.2中的函数封装成函数的形式,以方便调用:

%% 加载指定用户的所有session数据
function [cnt_subject, class] = load_BMI_data(subject, session, left_cut, right_cut, data_path)
    %% 数据读取
    % 构建时变读取路径
    if subject < 10
        current_load_path = [data_path, 'sess0', int2str(session), '_subj0', int2str(subject), '_EEG_MI.mat'];
    else
        current_load_path = [data_path, 'sess0', int2str(session), '_subj', int2str(subject), '_EEG_MI.mat'];
    end

    datasets = load(current_load_path);
    
    % 读取训练、测试的CNT数据
    cnt_data_train = datasets.EEG_MI_train.x';
    cnt_data_test = datasets.EEG_MI_test.x';
    
    % 将数据加载成EEGLab格式
    train_EEG = pop_importdata('setname','train', ...
        'data', cnt_data_train, ...
        'dataformat', 'array', ...
        'srate', 1000, ...
        'nbchan', 62);
    
    % 进行平均参考
    train_EEG = pop_reref(train_EEG, []);
    % 进行FIR滤波
    train_EEG = pop_eegfilt(train_EEG, 5, 40);
    % 进行降采样
    train_EEG = pop_resample(train_EEG, 100);
    
    % 将数据加载成EEGLab格式
    test_EEG = pop_importdata('setname','test', ...
        'data', cnt_data_test, ...
        'dataformat', 'array', ...
        'srate', 1000, ...
        'nbchan', 62);
    test_EEG = pop_reref(test_EEG, []);
    test_EEG = pop_eegfilt(test_EEG, 5, 40);
    test_EEG = pop_resample(test_EEG, 100);
    
    %% 裁切数据,创建标签
    
    % 读取数据标签
    train_class = datasets.EEG_MI_train.y_dec;
    test_class = datasets.EEG_MI_test.y_dec;
    class = [train_class, test_class];
    
    % 读取裁切信号 cue 因为上方降采样了10倍,所以cue也需要缩小10倍
    train_cue = round(datasets.EEG_MI_train.t/10);
    test_cue = round(datasets.EEG_MI_test.t/10);
    cue = [train_cue, test_cue];
    
    % 数据裁切
    cnt = [train_EEG.data, test_EEG.data]; % cnt数据合并
    trial = size(cue, 2); % 数据集中的trial数目
    for tr = 1:trial
        I = cnt(:, (cue(trial) - left_cut) : (cue(trial) + right_cut));
        cnt_subject(tr, :, :) = I; % 用户的cnt数据 格式为:[trial, channel, samplepoints]
    end
end

由此, 我们可以方便地利用定义的函数 “load_BMI_data” 完成某个用户所有session数据的读取。以subject 01 为例,读取其所有session(共两个)的所有数据,结果保存在矩阵cnt_subject中:

BMI_data_path = 'D:\数据集\脑电EEG\RawDataset\OpenBMI\'; % 原始数据集读取路径
BMI_processed_save_path = 'D:\数据集\脑电EEG\ProcessedData\OpenBMI\'; %加工数据集储存路径
session_num = 2; % 数据集session数目
subject_num = 54; % 数据集用户数

subject = 1; % 指定当前用户
cnt_subject = []; % 创建空矩阵,用来储存某个用户所有session的数据
for session = 1:session_num
    [cnt_subject_session, label_subject] = load_BMI_data(subject, session, 0, 400, BMI_data_path); % 提取用户数据
    cnt_subject = cat(1, cnt_subject, cnt_subject_session); % 拼接各个session的数据
end

5. 数据储存

接下来,需要储存每个用户的数据及标签,定义储存的路径"BMI_processed_save_path":

BMI_processed_save_path = 'D:\数据集\脑电EEG\ProcessedData\Open BMI\'; %加工数据集储存路径

我们希望将每个用户的脑电数据保存成"data_(subject_ID)“,将标签保存成"label_(subject_ID)”,(subject_ID)表示用户的编号,比如1号用户的数据、标签将被保存成:“data_1.mat”, “label_1.mat” 两个矩阵。

首先设定保存矩阵的名称:

% 设定数据与标签保存名称
cnt_save_name = ['data_', int2str(subject)]; % cnt数据保存名称
label_save_name = ['label_', int2str(subject)]; % label数据保存名称
eval([cnt_save_name,'=cnt_subject',';']); % 将字符串转换为matlab可执行语句
eval([label_save_name,'=class',';']); % 将字符串转换为matlab可执行语句

随后对数据(cnt_subject)与标签(label_subject)进行储存:

% 储存数据与标签
save([BMI_processed_save_path,'\data_', int2str(subject),'.mat'],['data_', int2str(subject)]);
save([BMI_processed_save_path,'\label_', int2str(subject),'.mat'],['label_', int2str(subject)]);

对数据存储函数进行封装,封装成函数"save_BMI_data",其可以保存指定用户的数据:

function save_BMI_data(subject, session_num, left_cut, right_cut, BMI_data_path, BMI_processed_save_path)
    cnt_subject = []; % 创建空矩阵,用来储存某个用户所有session的数据

    for session = 1:session_num
        [cnt_subject_session, label_subject] = load_BMI_data(subject, session, left_cut, right_cut, BMI_data_path); % 提取用户数据
        cnt_subject = cat(1, cnt_subject, cnt_subject_session); % 拼接各个session的数据
    end

    % 设定数据与标签保存名称
    cnt_save_name = ['data_', int2str(subject)]; % cnt数据保存名称
    label_save_name = ['label_', int2str(subject)]; % label数据保存名称
    eval([cnt_save_name,'=cnt_subject',';']); % 将字符串转换为matlab可执行语句
    eval([label_save_name,'=label_subject',';']); % 将字符串转换为matlab可执行语句

    % 储存数据与标签
    save([BMI_processed_save_path,'\data_', int2str(subject),'.mat'],['data_', int2str(subject)]);
    save([BMI_processed_save_path,'\label_', int2str(subject),'.mat'],['label_', int2str(subject)]);
end

只需要指定subject的编号,总session数目,及左裁切,右裁切窗口大小,加载数据集的目录,保存数据集的目录,即可成功保存该用户的数据与标签:

%% 输入"save_BMI_data"参数设定
subject = 1; % 待保存用户编号
session_num = 2; % 数据集session数目
left_cut = 0; % 左裁切大小
right_cut = 400; % 右裁切大小
BMI_data_path = 'D:\数据集\脑电EEG\RawDataset\OpenBMI\'; % 原始数据集读取路径
BMI_processed_save_path = 'D:\数据集\脑电EEG\ProcessedData\OpenBMI\'; %加工数据集储存路径
% 加载,保存某个用户的数据
save_BMI_data(subject, session_num, left_cut, right_cut, BMI_data_path, BMI_processed_save_path);

% 加载,保存某个用户的数据
save_BMI_data(subject, session_num, left_cut, right_cut, BMI_data_path, BMI_processed_save_path);

6. 处理所有54个用户的数据

最后,通过调用函数"save_BMI_data",即可轻松完成对所有54个用户进行上述所有步骤,成功对所有54个用户的数据完成预处理与保存工作:

%% 参数设定
subject_num = 54; % 数据集用户数
session_num = 2; % 数据集session数目
left_cut = 0; % 左裁切大小
right_cut = 350 - 1; % 右裁切大小
BMI_data_path = 'D:\数据集\脑电EEG\RawDataset\OpenBMI\'; % 原始数据集读取路径
BMI_processed_save_path = 'D:\数据集\脑电EEG\ProcessedData\OpenBMI\'; %加工数据集储存路径

for subject = 1:10
    % 加载,保存某个用户的数据
    save_BMI_data(subject, session_num, left_cut, right_cut, BMI_data_path, BMI_processed_save_path);
end
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐