脑电麻醉深度监护模块matlab脚本【完整版】

-1

提示:仅作个人学习用

文章目录


前言

提示:仅作个人学习用:

麻醉深度BIS模块的matlab脚本代码复现,可能还有些bug和细节需要调整,各位如果拿去用的话参照自己的使用需求调整规划吧。


提示:以下是本篇文章正文内容,下面案例可供参考

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%               用公共数据集完成BIS模块的matlab脚本调试             %
%                           完整的模块代码                         %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

clear;
close all;
fclose('all');
clc;

load('...\0006.mat');

% 删掉nan的数值,防止出现滤波导致的全部nan值的问题
% EEG1 = double(EEG1);
nan_mask = isnan(EEG1);
EEG1(nan_mask) = [];


Fs = 128;               % 采样频率 128 Hz
total_points = length(EEG1);  % 总点数 120000
% 计算窗口参数
window_duration = 2;    % 窗长 2秒
window_length = Fs * window_duration;      % 窗长点数: 256点
stride = 0.5;    % 步长 0.5秒
nStride = Fs*stride;    % 窗口移动一个步长所移动的点数
step_length = Fs * nStride;          % 步长点数: 64点
N=floor((length(EEG1)-Fs)/nStride)-10;   % 当前数据长度,窗口总共可移动多少次
overlap_length = window_length - step_length; % 重叠点数: 192点

%% 计算BSR & BC
%%%%%%%%%%%%%%%%%%%%%%%%%% 计算抑制比——计算窗长1s,步长0.5s
BSRmap = zeros(N, 1);
for n = 1:N     % 循环取每个步长
    x = EEG1((n + 6.5)*nStride : (n + 6.5 + (1/stride))*nStride);     % 取每个步长取1s的数据
    v = (1:length(x))' .^(0:1);   
    baselineX = v * (v\x);      % a=v\x是方程va=x的解。即v的逆矩阵*矩阵x
    BSRmap(n) = all(abs(x-baselineX) <= 5);     % ≤5,是抑制段记录为1
end
BSR = 100 * movmean(BSRmap, [(63/stride) - 1, 0]);

%%%%%%%%%%%%%%%%%%%%% 整理参考另一篇论文中的方式进行了复现,都可以用,看怎么取舍吧
% 抑制阈值参数theta
theta = 50;  % 按照5uV设置即可,考虑ADC增益,视情况而定
min_suppr_len = samplingrate/2;     % 最小抑制段长度-0.5s

%% 使用递归方差估计对信号进行分段处理
[res, sig_segmented, sig_recvar] = recursive_variance_estimation(sigbuf(:,1), 0.9633, theta, 50);

sigbuf2 = (sigbuf - mean(sigbuf)) / (5 * rms(sigbuf));      % 信号归一化,画图方便

%%% Y1 Y2都是0是爆发段,1是抑制段,要处理一下
Y1AFT = ~Y1;
Y2AFT = ~Y2;

figure,
plot(sig_segmented,'r'); hold on
plot(sigbuf2(:,1),'b');hold on
plot(Y1AFT,'y');
plot(Y2AFT,'c');


%% 计算BIS结果

%%%%%%%%%%%%%%%%%%%%%%%%%% 过高通滤波
% [B, A] = butter(2, 0.65/(Fs/2), 'high');     % 创建0.65 Hz的二阶高通巴特沃斯滤波器
A = [1,-1.95488437262939,0.955879873967924];
B = [0.977691061649329, -1.95538212329866,0.977691061649329];
eegHiPassFiltered = filter(B, A, EEG1);       % 过高通滤波器
psd = nan(N, 4 * nStride/2);                 % 分配空间来存储功率谱密度,每移动一次窗口都有一组结果(7509个结果),每组结果是0到63.5 Hz, 0.5 Hz的频率分辨率(128长度)

%%%%%%%%%%%%%%%%%%%%%%%%%% 计算主要参数
xp = [0, 3, 6];
yp = [0, 0.25, 1];
x = 0:0.5:63.5;
xLimit = min(max(x, 0), 6);     % 将输入信号x限制在 0~6 范围内
suppressionFilter = interp1(xp, yp, xLimit).^2;      % 定义一个0 - 6hz范围内的抑制滤波器

components = nan(N, 3);     % 建个空矩阵
for n = 1:N    % 对可用于计算psd的epochs数量进行评估,每个epoch窗长2s,步长0.5s,所以每个窗口间有75%的数据是重叠的
    % 每0.5s输出一组计算结果

    if (n>=(window_duration/stride)) && (sum(BSRmap(n-((window_duration/stride)-1):n))==0)       % 如果这段2s的数据,不包含任何抑制段
        x = eegHiPassFiltered((n+(window_duration/stride))*nStride + (1:(window_duration/stride)*nStride));
        v = (1:length(x))' .^(0:1);
        baselineX = v * (v\x);
        FFTRet = fft(blackman(length(x)) .* (x - baselineX));        % 执行布莱克曼窗口FFT,消除基线信号漂移(减去baseline)
        psd(n,:) = 2 * abs(FFTRet(1:length(x)/2)').^2 / (length(x)*sum(blackman(length(x)).^2));  % 将频率幅值转换为频率功率
        RawEEG = EEG1((n+(window_duration/stride))*nStride + (1:(window_duration/stride)*nStride));        
        if sawtoothDetector(RawEEG, nStride)   % 如果检测到存在锯齿形k成分
            psd(n, :) = suppressionFilter .* psd(n, :);     % 相应地抑制低频成分的占比(防止k成分对低频结果造成影响,导致最终输出的BIS值偏低??)
        end
    end
    
    thirtySec   = max(1, (n -(30/stride)+1)):n;     % 最近30s的数据,窗口是0.5s长度,所以是找最近60个点的索引
    vhighPowerConc = sqrt(mean(psd(thirtySec, (((39.5/0.5):(46.5/0.5)) +1)) .* psd(thirtySec, (((40/0.5):(47/0.5)) +1)), 2));
    % 39.5-46.5Hz频率成分和40-47Hz频率成分,对应相乘后求平均,再做根号
    wholePowerConc = sqrt(mean(psd(thirtySec, (((0.5/0.5):(46.5/0.5)) +1)) .* psd(thirtySec, (((1/0.5):(47/0.5)) +1)), 2));        % 截取0.5-46.5Hz的频段成分
    % 0.5-46.5Hz频率成分和1-47Hz频率成分,对应相乘后求平均,再做根号
    % 上述都是一次计算0.5s的值

    MidPSD = psd(thirtySec, (((11/0.5):(20/0.5)) +1));      % 截取出来11-20Hz频率的成分
    midBandPower    = prctmean(nanmean(10*log10(MidPSD),1),50, 100);    % 计算11-20Hz频率的均值

    Psd30To47 = psd(thirtySec, (((30/0.5):(47/0.5)) +1));       % 取出30-47Hz部分的psd
    components(n ,1) = mean(10*log10(v(~isnan(v)))) - midBandPower;
    % 10*log10转化dB,之后计算均值(排除掉nan值)

    components(n, 2) = trimmean (10 * log10(vhighPowerConc./ wholePowerConc), 50);   % 全麻指数(40-47Hz/1-47Hz)
   
    
    Psd05To4 = psd(thirtySec, (((0.5/0.5):(4/0.5)) +1));       % 取出0.5-4Hz部分的psd
    components(n ,3) = mean(10*log10(v(~isnan(v)))) - midBandPower;
    % 10*log10转化dB,之后计算均值(排除掉nan值)
    
end

%%%%%%%%%%%%%%%%%%%%%%%%%% 加权计算得出最终的麻醉深度值结果
%%% 将成分1映射到logistic s曲线上,获得镇静评分
sedationScore = 104.4 - 49.4./(1+exp((components(:, 1)-(-13.9))/5.29));

%%% 将成分2映射到线性曲线上,并映射到logistic s曲线,获得全麻指数
componentsBoud = min(max(components(:,2), -60.89), -30);
% 在这里把成分2限制在[-60.89, -30]的区间内,因为后需要插值,x不能超出去
generalScore1 = interp1([-60.89, -30], [-40, 43-1], componentsBoud);
% interp1()函数————插值用(默认使用线性插值),按照x=[-60.89, -30],y=[-40,43-1],这个函数曲线,对刚刚限制后的成分2进行插值
generalScore2 = 61.3 - 72.6./(1+exp((components(:,2)-(-24.0))/3.55));   % % 在logistic s曲线上将成分2映射
generalScore2 = generalScore2.*(components(:,2)>= -30);
generalScore = generalScore1 + generalScore2;


bsrScore        = piecewise(BSR, [0, 100], [50, 0]);        % 使用分段线性函数将BSR转换为BSR分数
generalWeight   = piecewise( components(:,3), [0,5], [0.5, 1]) .* (generalScore < sedationScore);   % 将分量3转换为权重
bsrWeight       = piecewise(BSR, [10, 50], [0, 1]);     % 将BSR转换为权重
x               = (sedationScore .* (1-generalWeight)) + (generalScore .* generalWeight);   % 将镇静和全麻评分一起加权
MyOpeniBis      = piecewise(x, [-40, 10, 97, 110], [0, 10, 97, 100]).*(1-bsrWeight) + bsrScore.*bsrWeight;   % 用BSR压缩和加权
%%%%%% 最终的结果是0.5s一个值

MyBIS.BIS = MyOpeniBis(1:2:end);


figure,
plot(BIS,'b');hold on
plot(MyBIS.BIS,'r');
legend('BIS','MyBIS');


%% 计算SEF & MF & PPF & TP & EMG & 各频段占比 & Alpha/Delta(边界简单验证过)

%%%%%%%%%%%%%%%%%%%%%%%%%%% 生成调试用信号 %%%%%%%%%%%%%%%%%%%%%%%%%%%
% 参数设置
amplitude = 100;    % 幅值
Fs = 128;           % 采样率 128Hz
f = 30;              % 频率 5Hz
duration = 100;       % 持续时间 1秒

% 生成时间向量
t = 0:1/Fs:duration-1/Fs;

% 生成信号
eegHiPassFiltered = amplitude * sin(2*pi*f*t)';
N=floor((length(eegHiPassFiltered)-Fs)/nStride)-10;   % 当前数据长度,窗口总共可移动多少次
%%%%%%%%%%%%%%%%%%%%%%%%%%% 生成调试用信号 %%%%%%%%%%%%%%%%%%%%%%%%%%%


for n = 1:N    % 对可用于计算psd的epochs数量进行评估,每个epoch窗长2s,步长0.5s,所以每个窗口间有75%的数据是重叠的
    if (n>=(window_duration/stride)) && (sum(BSRmap(n-((window_duration/stride)-1):n))==0)       % 如果这段2s的数据,不包含任何抑制段
        x = eegHiPassFiltered((n+(window_duration/stride))*nStride + (1:(window_duration/stride)*nStride));
        v = (1:length(x))' .^(0:1);
        baselineX = v * (v\x);

        FFTRet = fft(x - baselineX);        % 执行布莱克曼窗口FFT,消除基线信号漂移(减去baseline)
        % 计算幅度谱
        magnitude = abs(FFTRet);
        % 计算功率谱 (|FFT|^2)
        power_spectrum = magnitude .^ 2;
        % 归一化:除以(采样点数 * 采样频率)
        PSD_full = power_spectrum / (N * Fs);
        % 取单边谱(由于实信号的对称性)
        num_unique_pts = length(x)/2 + 1;
        PSD = PSD_full(1:num_unique_pts);
        PSD(2:end-1) = 2 * PSD(2:end-1); % 乘以2补偿负频率部分

        %% 计算TP
        freq_range = 0.5/stride+1:30/stride+1;      % matlab索引从1开始,所以都+1 (2:61)对应频率(0.5:30)Hz
        TP(n) = sum(PSD(freq_range));
        
        %% 计算SEF 95%
        cumulative_power = cumsum(PSD(freq_range));      % 计算累积功率
        cumulative_percentage = cumulative_power / TP(n);     % 计算累积百分比
        idx = find(cumulative_percentage >= 0.95, 1, 'first');      % 找到第一个超过95%的位置
        % indices = find(condition, k, direction);
        % condition:逻辑条件表达式; k:要找到的元素数量; direction:搜索方向('first'或'last')
        SEF(n,1) = (idx)*0.5;     % matlab索引从1开始,C索引从0开始,应该要+1,C化的时候注意这个细节,调试确认是否需要+1

        %% 计算MF
        idx = find(cumulative_percentage >= 0.5, 1, 'first');      % 找到第一个超过50%的位置
        MF(n,1) = (idx)*0.5;     % matlab索引从1开始,C索引从0开始,应该要+1,C化的时候注意这个细节,调试确认是否需要+1

        %% 计算PPF
        [max_val, max_idx] = max(PSD(freq_range));      % 找到最大值的位置
        PPF(n,1) = (max_idx)*0.5;     % matlab索引从1开始,C索引从0开始,应该要+1,C化的时候注意这个细节,调试确认是否需要+1

        %% 计算EMG(70-100Hz)
        % freq_range = 70/stride:100/stride;
        % EMG(n,1) = sum(PSD(freq_range));

        %% 计算各频段占比 & Alpha/delta
        % Alpha waves: 8 to 13 Hz
        % Beta waves: 13 to 30 Hz
        % Theta waves: 4 to 8 Hz
        % Delta waves: 0.5 to 4 Hz
        Alpha(n,1) = 100*sum(PSD(8/stride+1:13/stride))/sum(PSD(freq_range));     % 这些索引都是左闭右开的区间,防止频点在边缘处重合
        Beta(n,1) = 100*sum(PSD(13/stride+1:30/stride+1))/sum(PSD(freq_range));     % 只有beta是左闭右闭
        Theta(n,1) = 100*sum(PSD(4/stride+1:8/stride))/sum(PSD(freq_range));
        Delta(n,1) = 100*sum(PSD(0.5/stride+1:4/stride))/sum(PSD(freq_range));
        BandSum(n,1) = Alpha(n)+Beta(n)+Theta(n)+Delta(n);          % 各频段占比总和应该是100%,这里做个验证
        AtoD(n,1) = 100*Alpha(n,1)/Delta(n,1);

    end
end

MyBIS.TP = TP(1:2:end);
MyBIS.SEF = SEF(1:2:end);
MyBIS.MF = MF(1:2:end);
MyBIS.PPF = PPF(1:2:end);
MyBIS.EMG = EMG(1:2:end);
MyBIS.Alpha = Alpha(1:2:end);
MyBIS.Beta = Beta(1:2:end);
MyBIS.Delta = Delta(1:2:end);
MyBIS.Theta = Theta(1:2:end);
MyBIS.AtoD = AtoD(1:2:end);



%% 计算SQI







%% 函数定义

% 递归方差估计函数,用于检测爆发-抑制模式
function [res, res_smoothed, res_var] = recursive_variance_estimation(sig, beta, theta, min_suppr_len)
    % 输入:
    %   sig - 输入信号
    %   beta - 遗忘因子(默认:0.9633)
    %   theta - 抑制阈值(默认:50)
    %   min_suppr_len - 最小抑制段长度(默认:50)
    % 输出:
    %   res - 原始分段结果(1=爆发,0=抑制)
    %   res_smoothed - 平滑后的分段结果(按照最小抑制段长度判断后,保留下来的抑制结果)
    %   res_var - 递归估计的方差值
    
    % 参数默认值设置
    if nargin < 2 || isempty(beta)
        beta = 0.9633;
    end
    if nargin < 3 || isempty(theta)
        theta = 50;
    end
    if nargin < 4 || isempty(min_suppr_len)
        min_suppr_len = 50;
    end
    
    % 确保sig是列向量
    sig = sig(:);
    n = length(sig);
    
    % 初始化结果数组
    res = zeros(n, 1);       % 原始分段结果(1=爆发,0=抑制)
    res_var = zeros(n, 1);   % 递归估计的方差值
    res_smoothed = zeros(n, 1); % 平滑后的分段结果
    
    % 初始均值和方差设为0
    mu = 0;          % 均值估计
    sigsquare = 0;   % 方差估计
    
    % 对每个信号点进行递归处理
    for i = 1:n
        mu = beta * mu + (1 - beta) * sig(i);
        sigsquare = beta * sigsquare + (1 - beta) * (sig(i) - mu)^2;
        res_var(i) = sigsquare;

        res(i) = ~(sigsquare < theta);
    end


    is_suppressed_keep = false;  % 标记当前抑制段是否需要保留
    nn = 0;     % 计数

    for i = 1:n
        if res(i) == 0  % 当前点被标记为抑制
            if is_suppressed_keep
                res_smoothed(i) = 0;
                nn = nn + 1;
                if nn>=min_suppr_len        % 当前段判断超过50个点之后,重置重新计数判断
                    is_suppressed_keep = false;
                    nn = 0;
                end
            else
                if i + min_suppr_len - 1 <= n && max(res(i:i+min_suppr_len-1)) == 0  % (i + min_suppr_len - 1)是防止超出数据索引界限
                    res_smoothed(i) = 0;
                    is_suppressed_keep = true;  % 标记为有效抑制段
                    nn = nn + 1;
                else
                    res_smoothed(i) = 1;
                end
            end
        else  % 当前点被标记为非抑制(爆发)
            % 重置标记
            is_suppressed_keep = false;
            res_smoothed(i) = 1;
        end
    end

    res = double(res);
    res_smoothed = double(res_smoothed);
end


%%%%%%%%%%%%%%%%% 生成模拟的EEG信号(包含爆发-抑制模式)
function sig = generate_simulation_eeg(samplingrate, duration)
    % 输入:
    %   samplingrate - 采样率(Hz)
    %   duration - 信号时长(秒)
    % 输出:
    %   sig - 生成的模拟EEG信号
    
    % 计算总样本数
    n_samples = round(samplingrate * duration);
    
    % 初始化信号为列向量
    sig = zeros(n_samples, 1);
    
    % 设置模拟参数
    burst_duration = 0.5;   % 爆发期持续时间(秒)
    suppression_duration = 1.0;  % 抑制期持续时间(秒)
    
    % 生成爆发-抑制模式
    i = 1;
    while i <= n_samples
        % 随机选择模式持续时间
        current_burst_duration = burst_duration + (rand() - 0.5) * 0.3;  % 添加一些随机性
        current_suppression_duration = suppression_duration + (rand() - 0.5) * 0.4;
        
        % 计算爆发期和抑制期的样本数
        n_burst = round(current_burst_duration * samplingrate);
        n_suppression = round(current_suppression_duration * samplingrate);
        
        % 生成爆发期信号(包含多种频率成分)
        burst_end = min(i + n_burst - 1, n_samples);
        if burst_end >= i
            % 混合多种频率的正弦波来模拟EEG爆发活动
            freq1 = 5 + rand() * 10;  % 5-15 Hz (alpha和beta活动)
            freq2 = 0.5 + rand() * 4.5;  % 0.5-5 Hz (delta和theta活动)
            
            % 生成噪声和信号
            noise_amp = 2;  % 噪声振幅
            signal_amp = 10 + rand() * 10;  % 信号振幅
            
            % 计算实际的信号索引
            actual_idx = i:burst_end;
            segment_length = length(actual_idx);
            
            % 为当前段创建局部时间向量 - 确保是列向量
            t_segment = (0:segment_length-1)';
            t_segment = t_segment / samplingrate;
            
            % 生成信号 - 确保是列向量
            burst_signal = signal_amp * (sin(2*pi*freq1*t_segment) + 0.5*sin(2*pi*freq2*t_segment)) + noise_amp * randn(segment_length, 1);
            
            % 验证维度是否匹配
            if length(burst_signal) ~= length(actual_idx)
                error('信号维度不匹配: burst_signal长度=%d, actual_idx长度=%d', length(burst_signal), length(actual_idx));
            end
            
            % 赋值到信号中
            sig(actual_idx) = burst_signal;
        end
        
        % 生成抑制期信号(主要是噪声)
        suppression_end = min(burst_end + 1 + n_suppression - 1, n_samples);
        if suppression_end >= burst_end + 1
            actual_idx = (burst_end + 1):suppression_end;
            segment_length = length(actual_idx);
            % 直接生成列向量噪声
            suppression_signal = 0.5 * randn(segment_length, 1);  % 非常低的振幅噪声
            sig(actual_idx) = suppression_signal;
        end
        
        % 更新索引
        i = suppression_end + 1;
    end
    
    % 添加一些人为的伪影(模拟运动伪影等)
    n_artifacts = randi(3);  % 随机添加1-3个人为伪影
    for k = 1:n_artifacts
        artifact_pos = randi(n_samples);
        artifact_len = round(0.1 * samplingrate);  % 100ms的伪影
        artifact_start = max(1, artifact_pos - round(artifact_len/2));
        artifact_end = min(n_samples, artifact_start + artifact_len - 1);
        
        if artifact_end >= artifact_start
            % 生成线性伪影 - 确保是列向量
            artifact_amp = 50 + rand() * 50;  % 50-100的振幅
            artifact = artifact_amp * linspace(0, 1, artifact_end - artifact_start + 1)';
            sig(artifact_start:artifact_end) = sig(artifact_start:artifact_end) + artifact;
        end
    end
    
    % 标准化最终信号
    max_abs_sig = max(abs(sig));
    if max_abs_sig > 0
        sig = sig / max_abs_sig * 100;  % 缩放到合适的振幅范围
    end
end


总结

上述文章中的计算方式均来自公开论文和公开数据集。
信号质量SQI的计算没写,暂时空余,后续会专门研究后发一篇博文,感兴趣的话可以关注后续更新情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值