语音信号处理 | 使用短时能量和谱质心特征进行端点检测

2023-05-16

文章目录

    • 概述
    • 原理及MATLAB实现
      • 基本流程
      • 特征提取
        • 短时能量
        • 谱质心
      • 阈值估计和阈值化处理
      • 提取语音片段
    • MATLAB2020a中的VAD函数
    • 参考

概述

在复杂的应用环境下,从音频中分割出语音信号和和非语音信号,是一个很重要的环节,因为它不仅可以减少数据以及系统的运行时间,还能够抑制噪声对系统的干扰。端点检测就是判断语音的起点和终点。常用的方法有基于短时能量和过零率的双门限法。

本文将介绍一种基于信号短时能量和谱质心的端点检测方法。

原理及MATLAB实现

基本流程

  1. 从语音信号中提取短时能量特征和谱质心特征序列(即对语音信号分帧加窗后,从每一帧中提取上述两个特征,最后将提取的特征组成两个特征序列)
  2. 分别为两个特征序列动态地估计一个阈值
  3. 利用估计的阈值对这两个特征序列进行阈值判断
  4. 根据阈值判断结果提取出语音片段

特征提取

使用短时能量特征和谱质心特征进行端点检测的原因为:

  1. 在一些简单的场景中(信噪比较高),语音段的能量比静音段的能量高很多。
  2. 如果非语音段包含一些环境的声音,那么语音段的谱质心将会比较大,因为噪声信号往往具有较低的频谱,因此它的谱质心也较小

因此可以用短时能量和谱质心分割出语音段、静音段和噪声段。在进行特征提取之前,需要将语音信号分割成无重叠的短时信号,本文采用的帧长为50ms。

接下来对每一帧信号进行如下处理,从而提取该帧信号的短时能量和谱质心特征

  • 短时能量

x i ( n ) , n = 1 , ⋯   , N x_i(n),n=1,\cdots,N xi(n),n=1,,N是第 i i i帧信号,长度为 N N N,通过以下公式计算该帧的能量:
E ( i ) = 1 N ∑ n = 1 N ∣ x i ( n ) ∣ 2 E(i)=\frac{1}{N}\sum_{n=1}^N|x_i(n)|^2 E(i)=N1n=1Nxi(n)2
这个特征可以用于检测静音帧,也可用于区分音频的种类

提取短时能量序列的MATLAB程序如下:

function E = ShortTimeEnergy(signal, windowLength, step)
%% 计算短时能量
% 输入:
% 	signal		:原始信号
% 	windowLength:帧长
% 	step		:帧移
% 输出:
% 	E			:每一帧的能量
    signal = signal / max(max(signal));
    curPos = 1;
    L = length(signal);
    numOfFrames = floor((L-windowLength)/step) + 1;
    E = zeros(numOfFrames,1);
    for i=1:numOfFrames
        window = (signal(curPos:curPos+windowLength-1));
        E(i) = (1/(windowLength)) * sum(abs(window.^2));
        curPos = curPos + step;
    end
end
  • 谱质心

谱质心被定义为频谱的重心,设第 i i i帧的谱质心为 C i C_i Ci,则:
C i = ∑ k = 1 N ( k + 1 ) X i ( k ) ∑ k = 1 N X i ( k ) C_i=\frac{\sum_{k=1}^N(k+1)X_i(k)}{\sum_{k=1}^NX_i(k)} Ci=k=1NXi(k)k=1N(k+1)Xi(k)
其中 X i ( k ) , k = 1 , ⋯   , N X_i(k),k=1,\cdots,N Xi(k),k=1,,N是第 i i i帧的离散傅里叶变换, N N N为帧长度。这个特征用来测量频谱的位置,它的值越大就表示声音越洪亮

提取谱质心序列的MATLAB程序如下:

function C = SpectralCentroid(signal,windowLength, step, fs)
%% 计算谱质心
% 输入:
% 	signal		:原始信号
% 	windowLength:帧长
% 	step		:帧移
% 输出:
% 	C			:每一帧的谱质心
    signal = signal / max(abs(signal));
    curPos = 1;
    L = length(signal);
    numOfFrames = floor((L-windowLength)/step) + 1;
    H = hamming(windowLength);
    m = ((fs/(2*windowLength))*[1:windowLength])';
    C = zeros(numOfFrames,1);
    for i=1:numOfFrames
        window = H.*(signal(curPos:curPos+windowLength-1));    
        FFT = (abs(fft(window,2*windowLength)));
        FFT = FFT(1:windowLength);  
        FFT = FFT / max(FFT);
        C(i) = sum(m.*FFT)/sum(FFT);
        if (sum(window.^2)<0.010)
            C(i) = 0.0;
        end
        curPos = curPos + step;
    end
    C = C / (fs/2);
end

调用上述函数即可得到短时能量和谱质心特征序列

[x, fs] = audioread("xxxx.wav");

% 窗长和帧移 单位:秒
win = 0.05;
step = 0.05;

% 计算短时能量
Eor = ShortTimeEnergy(x, win * fs, step * fs);
% 计算谱质心
Cor = SpectralCentroid(x, win * fs, step * fs, fs);

% 对特征序列进行中值滤波
E = medfilt1(Eor, 5);
E = medfilt1(E, 5);
C = medfilt1(Cor, 5); 
C = medfilt1(C, 5);

阈值估计和阈值化处理

接着估计两个特征序列的阈值:

  1. 计算每一条特征序列的直方图

  2. 对直方图进行平滑处理

  3. 检测直方图的局部最大值

  4. M 1 M_1 M1 M 2 M_2 M2分别为第一大和第二大的局部最大值的位置,则阈值 T T T可通过以下公式算出:
    T = W ⋅ M 1 + M 2 W + 1 T=\frac{W\cdot M_1 + M_2}{W+1} T=W+1WM1+M2 W W W是我们自己设置的参数,显然 W W W越大,阈值 T T T将越靠近 M 1 M_1 M1

使用上述方法估计出短时能量特征和谱质心特征的阈值后,就可以进行阈值化处理

阈值估计和阈值化处理的程序如下:

% 计算特征序列的平均值
E_mean = mean(E);
Z_mean = mean(C);

Weight = 10; % 阈值估计的参数
% 寻找短时能量的阈值
Hist = histogram(E, round(length(E) / 10));  % 计算直方图
HistE = Hist.Values;
X_E = Hist.BinEdges;
[MaximaE, countMaximaE] = findMaxima(HistE, 3); % 寻找直方图的局部最大值
if (size(MaximaE, 2) >= 2) % 如果找到了两个以上局部最大值
    T_E = (Weight*X_E(MaximaE(1, 1)) + X_E(MaximaE(1, 2))) / (Weight + 1);
else
    T_E = E_mean / 2;
end

% 寻找谱质心的阈值
Hist = histogram(C, round(length(C) / 10));
HistC = Hist.Values;
X_C = Hist.BinEdges;
[MaximaC, countMaximaC] = findMaxima(HistC, 3);
if (size(MaximaC,2)>=2)
    T_C = (Weight*X_C(MaximaC(1,1))+X_C(MaximaC(1,2))) / (Weight+1);
else
    T_C = Z_mean / 2;
end

% 阈值判断
Flags1 = (E>=T_E);
Flags2 = (C>=T_C);
flags = Flags1 & Flags2;

% 画出以上过程的结果
% 短时能量
subplot(3,1,1); plot(Eor, 'g'); hold on; plot(E, 'c'); 
L = line([0 length(E)],[T_E T_E]); set(L,'Color',[0 0 0]); set(L, 'LineWidth', 2);
axis([0 length(Eor) min(Eor) max(Eor)]);
legend({'Short time energy (original)', 'Short time energy (filtered)', 'Threshold'});
% 谱质心
subplot(3,1,2); plot(Cor, 'g'); hold on; plot(C, 'c'); 
L = line([0 length(C)],[T_C T_C]); set(L,'Color',[0 0 0]); set(L, 'LineWidth', 2);   
axis([0 length(Cor) min(Cor) max(Cor)]);
legend({'Spectral Centroid (original)', 'Spectral Centroid (filtered)', 'Threshold'});    

用到的寻找局部最大值函数findMaxima为:

function [Maxima, countMaxima] = findMaxima(f, step)
%% 寻找局部最大值
%
% 输入:
% f:    输入序列
% step: 搜寻窗长
%
% 输出:
% Maxima: [2xcountMaxima] matrix containing: 
%         1. 最大值的索引
%         2. 最大值
% countMaxima: 最大值的数量

%% STEP 1: 寻找最大值
    countMaxima = 0;
    for i = 1 : length(f) - step - 1 % for each element of the sequence:
        if i > step
            if (mean(f(i - step : i - 1)) <  f(i)) && (mean(f(i + 1 : i + step))< f(i))  
                % IF the current element is larger than its neighbors (2*step window)
                % --> keep maximum:
                countMaxima = countMaxima + 1;
                Maxima(1, countMaxima) = i;
                Maxima(2, countMaxima) = f(i);
            end
        else
            if (mean(f(1 : i)) <= f(i)) && (mean(f(i + 1 : i + step)) < f(i)) 
                % IF the current element is larger than its neighbors (2*step window)
                % --> keep maximum:
                countMaxima = countMaxima + 1;
                Maxima(1, countMaxima) = i;
                Maxima(2, countMaxima) = f(i);
            end

        end
    end

    %% STEP 2: 对最大值进行进一步处理
    
    MaximaNew = [];
    countNewMaxima = 0;
    i = 0;
    while i < countMaxima
        % get current maximum:
        i = i + 1;
        curMaxima = Maxima(1,i);
        curMavVal = Maxima(2,i);

        tempMax = Maxima(1,i);
        tempVals = Maxima(2,i);

        % search for "neighbourh maxima":
        while (i < countMaxima) && ( Maxima(1,i+1) - tempMax(end) < step / 2)
            i = i + 1;
            tempMax(end + 1) = Maxima(1,i);
            tempVals(end + 1) = Maxima(2,i);
        end

        [MM, MI] = max(tempVals);

        if MM > 0.02 * mean(f) % if the current maximum is "large" enough:
            countNewMaxima = countNewMaxima + 1;   % add maxima
            % keep the maximum of all maxima in the region:
            MaximaNew(1, countNewMaxima) = tempMax(MI); 
            MaximaNew(2, countNewMaxima) = f(MaximaNew(1, countNewMaxima));
        end        
    end

    Maxima = MaximaNew;
    countMaxima = countNewMaxima;
end

运行结果如下:

01
02

提取语音片段

经过上一步的阈值处理后,就得到了一段标记语音段的阈值化序列,接下来将该序列映射到原始信号中,从而获得语音段在原始信号中的起始和结束位置

%% 提取语音片段
count = 1;
segments = [];
while (count < length(flags)) % 当还有未处理的帧时
	% 初始化
	curX = [];	
	countTemp = 1;
	while ((flags(count) == 1) && (count < length(flags)))
		if (countTemp == 1) % 如果是该语音段的第一帧
			Limit1 = round((count-1)*step*fs)+1; % 设置该语音段的开始边界
            if Limit1 < 1
                Limit1 = 1; 
            end       
		end	
		count = count + 1; 		% 计数器加一
		countTemp = countTemp + 1;	% 当前语音段的计数器加一
	end

	if countTemp > 1 % 如果当前循环中有语音段
		Limit2 = round((count - 1) * step * fs); % 设置该语音段的结束边界
		if Limit2 > length(x)
            Limit2 = length(x);
        end
        % 将该语音段的首尾位置加入到segments的最后一行
        segments(end + 1, 1) = Limit1;
        segments(end,     2) = Limit2;
    end
	count = count + 1;
end

% 合并重叠的语音段
for i = 1 : size(segments, 1) - 1 % 对每一个语音段进行处理
    if segments(i, 2) >= segments(i + 1, 1)
        segments(i, 2) = segments(i + 1, 2);
        segments(i + 1, :) = [];
        i = 1;
    end
end

运行结果如下:

03

MATLAB2020a中的VAD函数

在最新的2020a中,MATLAB引入了一个端点检测函数detectSpeech,该函数也是基于上述算法的,只是将提取的两个特征由短时能量和谱质心换成了短时能量和频谱扩展度(Spectral Spread,即频谱的二阶中心距,描述信号在频谱中心周围的分布状况)

算法流程如下图所示:
在这里插入图片描述
步骤1~8分别为:

  1. 使用短时傅里叶变换,将信号转换为时频辨识
  2. 为每一帧计算短时能量和频谱扩展度
  3. 分别计算上述两个特征序列的直方图
  4. 阈值估计
  5. 对特征序列进行平滑处理
  6. 分别对平滑后的特征序列进行阈值判断
  7. 将两个阈值判断结果相与,得到提取的语音片段
  8. 合并重叠或者两者距离小于设定值MergeDistance的语音片段

使用该函数对一段语音进行端点检测的效果如下图所示:

04

具体使用方法参见:https://www.mathworks.com/help/audio/ref/detectspeech.html

参考

Giannakopoulos, Theodoros. “A Method for Silence Removal and Segmentation of Speech Signals, Implemented in MATLAB”, (University of Athens, Athens, 2009).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

语音信号处理 | 使用短时能量和谱质心特征进行端点检测 的相关文章

  • SLAM会议笔记(三)V-LOAM

    Visual lidar Odometry and Mapping Low drift Robust and Fast Abstract 提出了一种新的使用激光雷达里程计和视觉里程计的框架 xff0c 提升了表现 xff0c 特别是在剧烈运
  • SLAM会议笔记(四)Lego-LOAM

    LeGO LOAM Lightweight and Ground Optimized Lidar Odometry and Mapping on Variable Terrain Abstract 提出一种轻量级的ground optimi
  • SLAM会议笔记(五)LLOAM

    LLOAM LiDAR Odometry and Mapping with Loop closure Detection Based Correction Abstract 在LOAM的基础上加入了回环检测 xff0c 实现了雷达SLAM的
  • C++基础:第三章 对象与基本类型

    文章目录 第3章 对象与基本类型第1节 初始化 赋值语句第2节 类型详述类型描述类型种类划分与类型相关的标准未定义部分变量 第3节 复合类型 xff1a 从指针到引用指针指针特点相关操作符指针的定义 96 void 96 指针指针的指针指针
  • C++基础:第四章 数组、vector和字符串

    文章目录 第4章 数组 vector和字符串第1节 数组注意事项数组的复杂声明数组中元素的访问数组到指针的隐式转换获得s指向数组开头和结尾的指针数组的其他操作C字符串多维数组 第2节 vector构造和初始化元素的索引和遍历迭代器itera
  • C++基础:第五章 表达式基础与详述

    第五章 表达式基础与详述 第1节 基础 xff1a 引入 表达式由一到多个操作数组成 xff0c 可以求值并 xff08 通常会 xff09 返回求值结果 xff08 函数的调用是一种表达式 xff0c 有时函数不会返回求值结果 xff09
  • C++基础:第六章 语句

    第六章 语句 第1节 语句基础 常见类别 xff1a 表达式语句 xff0c 求值后丢弃 2 43 3 空语句复合语句 xff0c 用大括号 xff0c 形成独立的域 顺序语句 按先后顺序执行实际执行顺序可能产生变换 xff08 编译器优化
  • C++基础:第七章 函数

    第七章 函数 第1节 函数基础 栈帧结构 函数的外部链接 第2节 函数详解 传值 传址 传引用 传参数时的类型退化 xff0c 传数组时函数形参退化成指针 xff0c 所以形参不要写数组个数 多维数组作为函数参数时 void fun int
  • C++基础:第八章 深入IO

    第八章 深入IO 第1节 序 第2节 IOStream概述 流式IO而非记录IO 处理的主要问题 表示形式的变化 xff1a 使用格式化 解析在数据的内部表示与字符序列之间切换与外部设备的通信 xff1a 针对不同的外部设备引入不同的处理逻
  • 在vscode终端安装vue构建工具vite

    首先确保已安装npm 第一步 xff1a 全局安装yarn 0 打开cmd xff08 windows 43 R xff09 1 输入安装命令 npm install g yarn 2 如果能看到版本号 xff0c 则安装成功 yarn v

随机推荐

  • cmake相关:sudo make install后的卸载

    sudo make install后的卸载 我们知道linux中一般的编译一个package的顺序是 span class token function git span clone package git span class token
  • 提取rosbag中的图像话题存为本地图像

    新建存放图片文件夹 首先运行ros master roscore 在目标文件夹目录下运行 rosrun image view extract images sec per frame 61 0 05 image 61 lt ROSIMAGE
  • matlab循环读取文件

    一般情况下 xff0c 假如我要读取一个名为a txt的文件 xff0c 只需要利用下面的语句 xff1a a span class token operator 61 span span class token function load
  • 使用OpenMVG获取相机位姿的方法

    在生成sfm data bin文件后 xff0c 在文件目录下执行 openMVG main ConvertSfM DataFormat binary span class token operator span i yoursfm dat
  • Ubuntu修改文件夹下面所有文件权限的方法

    ubuntu修改文件夹下所有文件的权限 命令为 xff1a sudo chmod span class token operator span R 777 filename filename为要修改的文件夹名字 R应该是表示递归修改file
  • 写出对js事件,事件流,事件对象的理解

    事件 JavaScript 使我们有能力创建动态页面 事件是可以被 JavaScript 侦测到的行为 网页中的每个元素都可以产生某些可以触发 JavaScript 函数的事件 比方说 xff0c 我们可以在用户点击某按钮时产生一个 onC
  • UDP实时图像传输

    写在前面 首先问个问题 xff0c 为什么要用UDP传输图像 xff0c 而不是TCP xff1f TCP是我们经常使用的通信协议 xff0c 从认识它的第一天起 xff0c 就应该知道 xff0c 它非常稳 xff0c 丢包率超低 但是一
  • 机器学习 | 使用k-近邻算法实现手写识别系统

    KNN概述 k 近邻算法就是通过计算不同特征值之间的距离来进行分类的算法 假设我们现在有一个样本集 xff0c 每个样本都有几个特征用来描述这个样本 xff0c 以及一个它所属分类的标签 当我们拿到一个没有标签的样本时 xff0c 该如何判
  • Windows下如何查看一个process内有哪些thread

    从https docs microsoft com en us sysinternals downloads pslist下载PsTools xff0c 解压后找到pslist exe并移动到C盘任一目录下 xff0c 使用说明都在Psto
  • 机器人路径规划之动态窗口法

    动态窗口法 Dynamic Window Approach 概述 DWA是一种基于速度的局部规划器 xff0c 可计算达到目标所需的机器人的最佳无碰撞速度 程序实现 DWA算法主要分三步 xff1a 计算动态窗口计算最优 v
  • cso(布谷鸟)算法优化神经网络参数

    之前写了一篇pso工程上使用方法 xff0c 这一篇使用布谷鸟算法 xff0c 相关的原理也比较多的介绍了 目前实验结果还是pso快一点 一 布谷鸟算法介绍 布谷鸟搜索算法 xff0c 是 由剑 桥 大 学YANG等在文献 中提出的一种群智
  • 多线程之线程安全(Thread Safety)

    什么是线程安全 Thread Safety xff1f 怎样才能做到线程安全 xff1f 线程安全 线程安全指某个函数 函数库在多线程环境中被调用时 xff0c 能够正确地处理多个线程之间的共享变量 xff0c 使程序功能正确完成 数据类型
  • 多线程之简易注册验证程序

    问题描述 使用VC2010或以上版本编写一个多线程注册验证程序 xff0c 要求 xff1a 通过对话框输入若干人的学号和姓名 xff0c 并存入列表中作为注册记录 用户输入一个学号 xff0c 程序能通过多线程的方式与注册记录比对来验证其
  • 多线程之基于积分法与欧拉恒等式法的圆周率计算及OMP优化

    文章目录 一 问题描述二 积分法算法推导编程实现OMP优化 三 欧拉恒等式算法推导编程实现前期准备加法减法乘法除法 算法实现 OMP优化 四 总结积分法与欧拉恒等式法的对比OMP实现方式的对比 一 问题描述 分别采用积分法和欧拉恒等式计算
  • 语音信号处理 | 基于Hilbert-Huang变换的基音检测方法

    HHT原理 Hiibert Huang变换是由Huang等人于1998年提出来的一种信号分析方法 xff0c 它主要由两个部分组成 经验模型分解 Empirical Mode Decomposition EMD 和希尔伯特变换 xff08
  • 机器学习 | 使用TensorFlow搭建神经网络实现鸢尾花分类

    鸢尾花分类问题是机器学习领域一个非常经典的问题 xff0c 本文将利用神经网络来实现鸢尾花分类 实验环境 xff1a Windows10 TensorFlow2 0 Spyder 参考资料 xff1a 人工智能实践 xff1a Tensor
  • 语音信号处理 | 基于卡尔曼滤波的语音增强算法

    文章目录 1 概述2 卡尔曼滤波原理被估计的信号离散卡尔曼滤波算法参数选择 3 基于卡尔曼滤波的语音增强算法语音模型分析参数确定 4 程序实现语音数据的导入 加噪与分帧卡尔曼滤波器参数初始化卡尔曼滤波过程结果可视化 5 运行结果与结果分析运
  • UDP实时图像传输进阶篇——1080P视频传输

    在UDP实时图像传输一文中 xff0c 介绍了如何使用UDP来实现图像的实时传输 xff0c 并使用C 进行了发送端和接收端的搭建 但是文中的方法是对整张图片进行JPEG压缩 xff0c 并通过UDP一次性地发送到接收端 xff0c 由于一
  • 机器人路径规划之Dijkstra算法

    在机器人路径规划之动态窗口法文中 xff0c 介绍了一种局部路径规划方法 动态窗口法 xff0c 本文将介绍一种全局路径规划方法 Dijkstra算法 狄克斯特拉算法 Dijkstra算法是从一个顶点到其余各顶点的最短路径算法 xff0c
  • 语音信号处理 | 使用短时能量和谱质心特征进行端点检测

    文章目录 概述原理及MATLAB实现基本流程特征提取短时能量谱质心 阈值估计和阈值化处理提取语音片段 MATLAB2020a中的VAD函数参考 概述 在复杂的应用环境下 xff0c 从音频中分割出语音信号和和非语音信号 xff0c 是一个很