往期文章
希望了解更多的道友点这里
0. 分享【脑机接口 + 人工智能】的学习之路
1.1 . 脑电EEG代码开源分享 【1.前置准备-静息态篇】
1.2 . 脑电EEG代码开源分享 【1.前置准备-任务态篇】
2.1 . 脑电EEG代码开源分享 【2.预处理-静息态篇】
2.2 . 脑电EEG代码开源分享 【2.预处理-任务态篇】
3.1 . 脑电EEG代码开源分享 【3.可视化分析-静息态篇】
3.2 . 脑电EEG代码开源分享 【3.可视化分析-任务态篇】
4.1 . 脑电EEG代码开源分享 【4.特征提取-时域篇】
4.2 . 脑电EEG代码开源分享 【4.特征提取-频域篇】
4.3 . 脑电EEG代码开源分享 【4.特征提取-时频域篇】
4.4 . 脑电EEG代码开源分享 【4.特征提取-空域篇】
5 . 脑电EEG代码开源分享 【5.特征选择】
6.1 . 脑电EEG代码开源分享 【6.分类模型-机器学习篇】
6.2 . 脑电EEG代码开源分享 【6.分类模型-深度学习篇】
汇总. 专栏:脑电EEG代码开源分享【文档+代码+经验】
0 . 【深度学习】常用网络总结
脑电EEG代码开源分享 【2.预处理-静息态篇】
一、前言
本文档旨在归纳BCI-EEG-matlab的数据处理代码,作为EEG数据处理的总结,方便快速搭建处理框架的Baseline,实现自动化、模块插拔化、快速化。本文以任务态(锁时刺激,如快速序列视觉呈现)为例,分享脑电EEG的分析处理方法。
脑电数据分析系列。分为以下6个模块:
- 前置准备
- 数据预处理
- 数据可视化
- 特征提取(特征候选集)
- 特征选择(量化特征择优)
- 分类模型
本文内容:【2. 数据预处理】
提示:以下为各功能代码详细介绍,若节约阅读时间,请下滑至文末的整合代码
二、预处理 框架介绍
预处理
预处理的主要功能,分为以下5部分:
1. 基线校正
2. 滤波
3. 剔除坏导联
4. 填充坏导联
5. 剔除试次
预处理的代码框图、流程如下所示:
预处理与前置准备差异在于,预处理对脑电数据进行变换(线型or非线性),改变了原始数据形态
预处理的目的为:提升数据质量、降噪;前置准备的处理目的是:统一格式、保留关键数据;
对于预处理内容大致如下,顺序依据个人习惯有差异,本人处理顺序如下:
-1. 基线校正–2. 滤波–3. 去除坏导联–4. 填充坏导联–5. 剔除试次
-
基线校正:去除信号零飘,尤其是信号采集环境不好 or 被试状态不佳(犯困)。其操作内容为,静息态(非锁时任务)减去数据段内均值,根据傅里叶FFT变换推导,其功能相当于数据在0Hz降低了直流信号能量,等于变相的进行了一次直流滤波。由于静息态等长时间任务没有刺激诱发锚点,没有任务态刺激triger的标记,因此一般以本段数据为基线参考。
-
滤波:尽量保留脑电数据“有价值”内容,尽量去除噪声等伪迹干扰。由于脑电信号幅度为10^-6v,微伏级别的数据能被任何妖魔鬼怪干扰,啥噪声都能来折磨你(说到动情处,觉得有必要写一篇脑电EEG采集的苦痛经验)…滤波不仅需要滤除高频的干扰,一般过滤掉静息态信号中100Hz以上无关脑活动信息(还没验证大脑能产生这么高频率);还要过滤掉0.5Hz Or 1Hz 以下的低频伪迹,一般认为这是肌电、电极与头皮摩擦、心电(一秒钟跳一下多)产生的。其操作内容为,带通滤波0.05Hz-80Hz(也有0.01-100Hz等多种组合),可以使用高通0.05Hz + 低通80Hz,也可以使用0.05Hz-80Hz的带通。注:滤波器不是直上直下的,设计时应考虑 过渡带 + 带内抖动,,尤其是高通0.5Hz的过渡带区间窄,尤其难设计滤波器参数,详细知识可学习参考《数字信号处理》。我们这里经验设置的:低通Rp_low = 0.5;Rs_low =5 ; 高通Rp_high =1;Rs_high =10。
-
剔除坏导联(电极):由于实验过程中的环境噪声、电极不贴合、电极线故障等原因,导致单个或多个电极质量不佳,若不对个别坏道进行剔除、修复,下一步会影响整体数据质量。量化各电极通道的信号质量,对信号质量严重受损的通道进行标记、置零,为下一阶段导联修复、填充做准备。导联质量观测可分为两种,一是在数据采集过程中就发现,肉眼可见的信号质量不佳,此类坏导易标记;另一种是数据处理过程中发现的坏导,大家量化方法不一。我们提出的应用导联方差的量化方法,当某导联方差大于平均方差几倍时,则认为该导联抖动异常 应剔除,这里倍数的经验值为3,outlier_threshold=3,这个阈值是根据数据实际修改的,最终目的是保留80%以上的样本,如果信号整体质量偏高就降低阈值(例如到2),如果信号整体质量偏低就提升阈值(例如到5).
-
填充坏导联(电极):坏导在上一阶段进行了标记,但若坏导个数偏多 or 导联密度有要求,则需要对原坏导位置进行修复 or 填充。常用的方法是用相邻导联的数据进行均值替换,可用坏导附近的2导、4导、8导的均值代替坏导数据。
-
剔除试次:被试在进行某次任务 or 实验某阶段的样本质量不佳。目前研究普遍认为静息态脑电信号不高于100uv(有争论但在100uv上下浮动),因此一般认为幅值超过100uv的样本质量不佳,一般处理方法就是直接删除该样本。但是100uv也并非固定的,也会因为信号质量浮动,论文中的阈值80uv 、100uv、120uv、150uv我都见过,大家还是秉承工程思想,在满足最低样本量的基础上,最大程度保证样本质量。
-
重参考:重参考是寻找大脑的电位0点,相减电位零点以使全脑电极有统一基准。重参考电势零点的寻找有多个研究室团队探讨,例如平均参考、双侧乳突参考、REST参考等。由于这一步骤在硬件采集时设置了双耳乳突参考,因此本代码中未进行该操作。
(有需要的朋友可以留言,还有哪些参数设置比较困惑,可以再写一篇谈谈数据处理、实验设计中的参数设置)
三、代码格式说明
本文非锁时任务态(下文以静息态代替)范例为:ADHD患者、正常人群在静息状态下的脑模式分类
- **代码名称:**代码命名为Proprocess_xxx ( baseline \ filter \ channel_abandon\ trial_abandon)
- **输入格式:**输入格式承接规范输入的文件Standard_input_xx。
- **输出及保存格式:**分预处理阶段阶保存数据,数据仍为分类别保存,保存命名为Proprocess_xxx(预处理名称)_target/nontarget。除abandon阶段外其余预处理过程的cell格式同规范输入数据,abandon阶段数据仍为cell{1,通道数},只改变通道数内的剩余试次数[剩余试次数,512]。
三、脑电处理 代码
提示:代码环境为 matlab 2018
3.0 参数设置
预处理内容可以选择,把希望进行的步骤写在proprocess_content 中
- 预处理内容: proprocess_content = [‘baseline’,‘filter’,‘channel_abandon’,‘auto_abandon’,‘trial_abandon’]; 1. 基线校正 2. 滤波 3. 剔除坏导联 4. 填充坏导联 5. 剔除试次.
- 剔除坏导:试验中肉眼发现16导信号不佳,channel_abandon_num = [16];
- 剔除超过100uv的样本试次,trial_threshold = 100;
- 一次进行10人次的批处理,subject_num = [1;29]
- 取数据段内20%长度的数据均值进行基线矫正,Baseline_reference = [0;0.2];
- 带通滤波范围:0.01Hz-100Hz,低通起止频率为80Hz、100Hz,高通起止频率为0.01Hz、1Hz,filter_low_para = [80;100]; filter_high_para = [0.01;1];
- 坏导量化倍率3,当某导联方差为平均值的3倍以上,outlier_threshold = 3;,定义为坏导
%% 0.标准数据参数设置
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
data_path = 'C:\Users\Amax\Desktop\basetest_flod\save_fold\';
svae_path = 'C:\Users\Amax\Desktop\basetest_flod\save_fold\';
proprocess_content = ['baseline\','filter\','channel_abandon\','auto_abandon\','trial_abandon'];
channel_abandon_num = [16];
trial_threshold = 100;
subject_num = [1 ; 10];
Baseline_reference = [0;0.2];
filter_low_para = [80;100];
filter_high_para = [0.01;1];
outlier_threshold = 3;
disp(['||预处理参数设置||']);
disp(['预处理内容: ' , proprocess_content]);
disp(['基线长度: ' , num2str(Baseline_reference(1,1)),'-',num2str(Baseline_reference(2,1))]);
disp(['低通起止: ' , num2str(filter_low_para(1,1)),'-',num2str(filter_low_para(2,1))]);
disp(['高通起止: ' , num2str(filter_high_para(1,1)),'-',num2str(filter_high_para(2,1))]);
disp(['通道舍弃: ' , num2str(channel_abandon_num')]);
disp(['试次幅度阈值: ' , num2str(trial_threshold)]);
3.1 标准输入赋值
导入前置准备阶段处理后的数据:
%% 1.标准输入赋值
disp(['||1.静息态or任务态-标准输入数据加载中...||']);
Standard_nontarget_file = load([data_path , 'Standard_input_nontarget_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))]);
Standard_target_file = load([data_path , 'Standard_input_target_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))]);
stuct_target_name = 'Standard_input_target';
stuct_nontarget_name = 'Standard_input_nontarget';
Standard_nontarget_data = Standard_nontarget_file.(stuct_nontarget_name).data;
Standard_target_data = Standard_target_file.(stuct_target_name).data;
subject_num = Standard_target_file.(stuct_target_name).subject_num;
fs_down = Standard_target_file.(stuct_target_name).fs;
trial_every_sub = size(Standard_target_data,1);
disp(['被试量: ' , num2str(subject_num(1,1)),'-',num2str(subject_num(2,1))]);
if (filter_low_para(2,1)>fs_down/2)
disp(['低通滤波参数不符合奈奎斯特带宽,请调高fs_down或降低低通参数']);
end
3.2 预处理
3.2.1 预处理-基线矫正
主体调用函数Proprocess_baseline
%2.1 基线矫正
if contains(proprocess_content,'baseline')
Proprocess_baseline_target = Proprocess_baseline(Standard_target_data,fs_down,Baseline_reference);
Proprocess_baseline_nontarget = Proprocess_baseline(Standard_nontarget_data,fs_down,Baseline_reference);
end
主功能函数 Proprocess_baseline:
function baseline_out= Proprocess_baseline(Standard_input_data,fs_down,Baseline_reference)
% Standard_input_data 标准输入的cell脑电数据,cell(试次数*被试数)[通道数,时间点数]
% fs_down 标准输入时降采样率
% Baseline_reference 极限矫正的参考时间段,一般为0~0.2的试次前均值,或者0~1的全时段均值
baseline_start = floor(Baseline_reference(1,1)*fs_down)+1;
baseline_end = floor(Baseline_reference(2,1)*fs_down);
baseline_out = cell(size(Standard_input_data,1),size(Standard_input_data,2));
for trial_loop = 1:size(Standard_input_data,1)
for sub_loop = 1:size(Standard_input_data,2)
for channel_loop = 1:size(Standard_input_data{1,1},1)
if ~isempty(Standard_input_data{trial_loop,sub_loop})
baseline_out{trial_loop,sub_loop}(channel_loop,:) = Standard_input_data{trial_loop,sub_loop}(channel_loop,:) - mean(Standard_input_data{trial_loop,sub_loop}(channel_loop,baseline_start:baseline_end));
end
end
end
end
end
3.2.2 预处理-滤波
主体调用函数Proprocess_filter
%2.2 滤波
if contains(proprocess_content,'filter')
Proprocess_filter_target = Proprocess_filter(Proprocess_baseline_target,fs_down,filter_low_para,filter_high_para);
Proprocess_filter_nontarget = Proprocess_filter(Proprocess_baseline_nontarget,fs_down,filter_low_para,filter_high_para);
end
主功能函数 Proprocess_filter:
function [filter_out]= Proprocess_filter(Standard_input_data,fs_down,filter_low_para,filter_high_para)
%导入参数
low_start = filter_low_para(1,1);
low_end = filter_low_para(2,1);
high_start =filter_high_para(1,1);
high_end = filter_high_para(2,1);
filter_out = cell(size(Standard_input_data,1),size(Standard_input_data,2));
% 低通
Rp_low = 0.5;
Rs_low =5;
[N_low,Wpo_low]=cheb1ord(2*low_start/fs_down,2*low_end/fs_down,Rp_low,Rs_low);
[b_low,a_low]=cheby1(N_low,Rp_low,Wpo_low,'low');
% 高通
Rp_high =1;
Rs_high =10;
[N_high,Wpo_high]=cheb1ord(2*high_end/fs_down,2*high_start/fs_down,Rp_high,Rs_high);
[b_high,a_high]=cheby1(N_high,Rp_high,Wpo_high,'high');
% 滤波
for trial_loop = 1:size(Standard_input_data,1)
for sub_loop = 1:size(Standard_input_data,2)
for channel_loop = 1:size(Standard_input_data{1,1},1)
if ~isempty(Standard_input_data{trial_loop,sub_loop})
temp_filter = [];
temp_filter = filter(b_low,a_low,Standard_input_data{trial_loop,sub_loop}(channel_loop,:));
temp_filter = filter(b_high,a_high,temp_filter);
filter_out{trial_loop,sub_loop}(channel_loop,:) = temp_filter;
end
end
end
end
end
3.2.3 预处理-剔除坏导
主体调用函数Proprocess_channel_abandon
%2.3 多余通道剔除
if contains(proprocess_content,'channel_abandon')
Proprocess_channel_abandon_target = Proprocess_channel_abandon(Proprocess_filter_target,channel_abandon_num);
Proprocess_channel_abandon_nontarget = Proprocess_channel_abandon(Proprocess_filter_nontarget,channel_abandon_num);
end
主功能函数 Proprocess_channel_abandon:
function channel_abandon_out= Proprocess_channel_abandon(Standard_input_data,channel_abandon_num)
% Standard_input_data 标准输入的cell脑电数据,cell(试次数*被试数)[通道数,时间点数]
% channel_abandon_num 舍弃通道编号
abandon_size = size(channel_abandon_num,1);
channel_abandon_out = cell(size(Standard_input_data,1),size(Standard_input_data,2));
for sub_loop = 1:size(Standard_input_data,2)
for trial_loop = 1:size(Standard_input_data,1)
channel_count = 1;
for channel_loop = 1:size(Standard_input_data{1,1},1)
if ~isempty(Standard_input_data{trial_loop,sub_loop})
if isempty(find(channel_abandon_num==channel_loop))
channel_abandon_out{trial_loop,sub_loop}(channel_count,:) = Standard_input_data{trial_loop,sub_loop}(channel_loop,:);
channel_count = channel_count+1;
end
end
end
end
end
end
3.2.4 预处理-自动填补、修复坏导
主体调用函数Proprocess_auto_abandon
%2.4 异常通道自动替换
if contains(proprocess_content,'auto_abandon')
[Proprocess_auto_abandon_target,auto_channel_list_target] = Proprocess_auto_abandon(Proprocess_channel_abandon_target,outlier_threshold);
[Proprocess_auto_abandon_nontarget,auto_channel_list_nontarget] = Proprocess_auto_abandon(Proprocess_channel_abandon_nontarget,outlier_threshold);
end
主功能函数 Proprocess_auto_abandon:
function [auto_abandon_data,auto_channel_list]= Proprocess_auto_abandon(Standard_input_data,outlier_threshold)
%% 注:auto_abandon只是将抖动异常通道使用相邻通道替换,而不影响数据通道个数,处理后数据通道个数仍一致
% Standard_input_data 标准输入的cell脑电数据,cell(试次数*被试数)[通道数,时间点数]
% outlier_threshold 为异常点的阈值,即异常点抖动是均值的几倍则被判为异常点,一般设为5
% auto_abandon_data 为自动修正(用周围通道替换)通道后的数据
% auto_channel_list 为各被试替换的通道列表
channel_temp_std = [];
auto_channel_list = cell(1,size(Standard_input_data,2));
for sub_loop = 1:size(Standard_input_data,2)
abandon_count = 1;
channel_temp_std = [];
for trial_loop = 1:size(Standard_input_data,1)
channel_temp_std(:,trial_loop) = std(Standard_input_data{trial_loop,sub_loop}')';
end
std_mean = mean(channel_temp_std');
abandon_level = mean(outlier_threshold*std_mean);
for channel_loop = 1:size(Standard_input_data{1,1},1)
if std_mean(1,channel_loop) > abandon_level
auto_channel_list{1,sub_loop}(1,abandon_count) = channel_loop;
end
end
end
for sub_loop = 1:size(Standard_input_data,2)
if ~isempty(auto_channel_list{1,sub_loop})
for trial_loop = 1:size(Standard_input_data,1)
for channel_loop = 1:size(Standard_input_data{1,1},1)
if ismember( auto_channel_list{1,sub_loop}(1,abandon_count),channel_loop)
replace_data=[];
if channel_loop==1
replace_data = Standard_input_data{trial_loop,sub_loop}(2,:);
elseif channel_loop==size(Standard_input_data{1,1},1)
replace_data = Standard_input_data{trial_loop,sub_loop}(size(Standard_input_data{1,1},1)-1,:);
else
replace_data = (Standard_input_data{trial_loop,sub_loop}(channel_loop-1,:) + Standard_input_data{trial_loop,sub_loop}(channel_loop+1,:))/2;
end
Standard_input_data{trial_loop,sub_loop}(channel_loop,:) = replace_data;
end
end
end
end
end
auto_abandon_data = Standard_input_data;
end
3.2.5 预处理-剔除坏样本试次
主体调用函数Proprocess_trial_abandon
%2.5 试次剔除
if contains(proprocess_content,'trial_abandon')
Proprocess_trial_abandon_target = Proprocess_trial_abandon(Proprocess_auto_abandon_target,trial_threshold);
Proprocess_trial_abandon_nontarget = Proprocess_trial_abandon(Proprocess_auto_abandon_nontarget,trial_threshold);
end
主功能函数 Proprocess_trial_abandon:
function trial_abandon_out= Proprocess_trial_abandon(Standard_input_data,trial_threshold)
% Standard_input_data 标准输入的cell脑电数据,cell(试次数*被试数)[通道数,时间点数]
% trial_abandon_num 舍弃通道编号
trial_abandon_out = cell(size(Standard_input_data,1),size(Standard_input_data,2));
for sub_loop = 1:size(Standard_input_data,2)
trial_count = 1;
for trial_loop = 1:size(Standard_input_data,1)
if max(max(Standard_input_data{trial_loop,sub_loop}))<trial_threshold
trial_abandon_out{trial_count,sub_loop}= Standard_input_data{trial_loop,sub_loop};
trial_count = trial_count+1;
end
end
end
end
3.3 预处理-结果保存
最终,结果保存:
%% 3.预处理数据保存
Proprocess_target = [];
Proprocess_target.remain_trial = remain_target_trial;
Proprocess_target.fs_down = fs_down;
Proprocess_target.subject_num = subject_num;
Proprocess_target.data = Proprocess_trial_abandon_target;
Proprocess_target.Baseline_reference = Baseline_reference;
Proprocess_nontarget = [];
Proprocess_nontarget.remain_trial = remain_nontarget_trial;
Proprocess_nontarget.fs_down = fs_down;
Proprocess_nontarget.subject_num = subject_num;
Proprocess_nontarget.data = Proprocess_trial_abandon_nontarget;
Proprocess_nontarget.Baseline_reference = Baseline_reference;
disp(['标准分段保存中...']);
save([ svae_path , 'Proprocess_target_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))],'Proprocess_target');
save([ svae_path , 'Proprocess_nontarget_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))],'Proprocess_nontarget');
disp(['||已完成标准分段保存||']);
四、预处理 整体代码
静息态信号-预处理 整体代码:
%% 0.标准数据参数设置
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
data_path = 'C:\Users\Amax\Desktop\basetest_flod\save_fold\';
svae_path = 'C:\Users\Amax\Desktop\basetest_flod\save_fold\';
proprocess_content = ['baseline\','filter\','channel_abandon\','auto_abandon\','trial_abandon'];
channel_abandon_num = [16];
trial_threshold = 100;
subject_num = [1 ; 10];
Baseline_reference = [0;0.2];
filter_low_para = [80;100];
filter_high_para = [0.01;1];
outlier_threshold = 3;
disp(['||预处理参数设置||']);
disp(['预处理内容: ' , proprocess_content]);
disp(['基线长度: ' , num2str(Baseline_reference(1,1)),'-',num2str(Baseline_reference(2,1))]);
disp(['低通起止: ' , num2str(filter_low_para(1,1)),'-',num2str(filter_low_para(2,1))]);
disp(['高通起止: ' , num2str(filter_high_para(1,1)),'-',num2str(filter_high_para(2,1))]);
disp(['通道舍弃: ' , num2str(channel_abandon_num')]);
disp(['试次幅度阈值: ' , num2str(trial_threshold)]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1.标准输入赋值
disp(['||1.静息态or任务态-标准输入数据加载中...||']);
Standard_nontarget_file = load([data_path , 'Standard_input_nontarget_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))]);
Standard_target_file = load([data_path , 'Standard_input_target_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))]);
stuct_target_name = 'Standard_input_target';
stuct_nontarget_name = 'Standard_input_nontarget';
Standard_nontarget_data = Standard_nontarget_file.(stuct_nontarget_name).data;
Standard_target_data = Standard_target_file.(stuct_target_name).data;
subject_num = Standard_target_file.(stuct_target_name).subject_num;
fs_down = Standard_target_file.(stuct_target_name).fs;
trial_every_sub = size(Standard_target_data,1);
disp(['被试量: ' , num2str(subject_num(1,1)),'-',num2str(subject_num(2,1))]);
if (filter_low_para(2,1)>fs_down/2)
disp(['低通滤波参数不符合奈奎斯特带宽,请调高fs_down或降低低通参数']);
end
%% 2.预处理
%2.1 基线矫正
if contains(proprocess_content,'baseline')
Proprocess_baseline_target = Proprocess_baseline(Standard_target_data,fs_down,Baseline_reference);
Proprocess_baseline_nontarget = Proprocess_baseline(Standard_nontarget_data,fs_down,Baseline_reference);
end
%2.2 滤波
if contains(proprocess_content,'filter')
Proprocess_filter_target = Proprocess_filter(Proprocess_baseline_target,fs_down,filter_low_para,filter_high_para);
Proprocess_filter_nontarget = Proprocess_filter(Proprocess_baseline_nontarget,fs_down,filter_low_para,filter_high_para);
end
%2.3 多余通道剔除
if contains(proprocess_content,'channel_abandon')
Proprocess_channel_abandon_target = Proprocess_channel_abandon(Proprocess_filter_target,channel_abandon_num);
Proprocess_channel_abandon_nontarget = Proprocess_channel_abandon(Proprocess_filter_nontarget,channel_abandon_num);
end
%2.4 异常通道自动替换
if contains(proprocess_content,'auto_abandon')
[Proprocess_auto_abandon_target,auto_channel_list_target] = Proprocess_auto_abandon(Proprocess_channel_abandon_target,outlier_threshold);
[Proprocess_auto_abandon_nontarget,auto_channel_list_nontarget] = Proprocess_auto_abandon(Proprocess_channel_abandon_nontarget,outlier_threshold);
end
%2.5 试次剔除
if contains(proprocess_content,'trial_abandon')
Proprocess_trial_abandon_target = Proprocess_trial_abandon(Proprocess_auto_abandon_target,trial_threshold);
Proprocess_trial_abandon_nontarget = Proprocess_trial_abandon(Proprocess_auto_abandon_nontarget,trial_threshold);
end
[remain_target_trial,remain_nontarget_trial]= Proprocess_trial_remain(Proprocess_trial_abandon_target,Proprocess_trial_abandon_nontarget);
disp(['目标试次剩余比例: ' , num2str(remain_target_trial/size(Proprocess_trial_abandon_target,1)),'||平均: ', num2str(mean(remain_target_trial/size(Proprocess_trial_abandon_target,1)))]);
disp(['非目标试次剩余比例: ' , num2str(remain_nontarget_trial/size(Proprocess_trial_abandon_nontarget,1)),'||平均: ', num2str(mean(remain_nontarget_trial/size(Proprocess_trial_abandon_nontarget,1)))]);
%% 3.预处理数据保存
Proprocess_target = [];
Proprocess_target.remain_trial = remain_target_trial;
Proprocess_target.fs_down = fs_down;
Proprocess_target.subject_num = subject_num;
Proprocess_target.data = Proprocess_trial_abandon_target;
Proprocess_target.Baseline_reference = Baseline_reference;
Proprocess_nontarget = [];
Proprocess_nontarget.remain_trial = remain_nontarget_trial;
Proprocess_nontarget.fs_down = fs_down;
Proprocess_nontarget.subject_num = subject_num;
Proprocess_nontarget.data = Proprocess_trial_abandon_nontarget;
Proprocess_nontarget.Baseline_reference = Baseline_reference;
disp(['标准分段保存中...']);
save([ svae_path , 'Proprocess_target_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))],'Proprocess_target');
save([ svae_path , 'Proprocess_nontarget_',num2str(subject_num(1,1)),'_',num2str(subject_num(2,1))],'Proprocess_nontarget');
disp(['||已完成标准分段保存||']);
总结
预处理最终目的是:提升采集信号质量(前提是还有数据)
但是 数据质量 和 样本数量,大多数情况下是冲突的
有个前提是保证底线数量的样本量,如果因为严苛的预处理指标,而预处理后不剩几个样本,则本末倒置,后续处理无法进行
有时就因为楼下施工,被试困倦,电磁信号,电压不稳,导致数据质量基础不好,这都是很正常的
如果再一味地追求高质量的预处理结果,那真的强人所难了…
希望大家在这一阶段运用工程思想,以结果导向,在保证样本多多的情况下,尽量改善数据好好的
记住:一切预处理参数都是活的,必要时可以妥协的
囿于能力,挂一漏万,如有笔误请大家指正~
感谢您耐心的观看,本系列更新了约30000字,约3000行开源代码,体量相当于一篇硕士工作。
往期内容放在了文章开头,麻烦帮忙点点赞,分享给有需要的朋友~
坚定初心,本博客永远:
免费拿走,全部开源,全部无偿分享~
To:新想法、鬼点子的道友:
自己:脑机接口+人工智领域,主攻大脑模式解码、身份认证、仿脑模型…
在读博士第3年,在最后1年,希望将代码、文档、经验、掉坑的经历分享给大家~
做的不好请大佬们多批评、多指导~ 虚心向大伙请教!
想一起做些事情 or 奇奇怪怪点子 or 单纯批评我的,请至Rongkaizhang_bci@163.com