采用双门限法的思路:
首先通过噪声段(随机性大,混乱度高)的谱熵大于语音段,区分出语音的浊音与噪声,保留浊音,另一部分主要是发音起始和终止时的清音阶段与噪声,再由清音的短时过零率低于噪声段,将清音与噪声分离,保留清音,由此就得到了完整的语音段,实现了语音段的端点检测,可以自动去除噪声段。
按照矩阵运算的思路编辑程序:先计算分帧矩阵的fft结果,转为dB单位(实验测试了使用dB作为单位效果明显)平方后得到矩阵Y对Y每列的前N/2行相加得概率计算的分母cigmaY再把cigY拓展成N行,便于与Y做./的矩阵运算得到概率矩阵P代入最终的谱熵计算公式即可。代码如下:freq=fft(frame_w,N); %将数据由时域转换到频域spect=real(10log10(freq));%将频域结果单位换dB
Y=spectconj(spect);%计算能量sumY=sum(Y(1:end/2,:));sumY=sumY(ones(1,N),:);
P=Y./sumY;%计算样本点概率
H=-sum(P(1:end/2,:).*(log2(P(1:end/2,:))));%代入谱熵定义的公式
检测过程根据双门限法的思想,对分帧矩阵做一次帧数的遍历,假如某帧的谱熵小于了高谱熵门限,则可能进入了语音段,继续遍历,假如某帧的谱熵小于了低谱熵门限,确定已经进入了语音段,记录下该帧的位置,从该位置向前遍历,假如某帧的短时过零率大于了过零率门限,确定该帧为噪声首部结束的位置,记录改帧位置为noiseEnd,跳出向前遍历。 接着对噪声尾部处理的思路类似,假如某帧的谱熵小于低谱熵门限,而它的下一帧谱熵高于门限,确定该帧在语音片段,开始向后遍历,加入过零率大于了过零率门限,记录该帧位置为noiseBegin,跳出所有遍历。
思路还是非常清晰的,代码写起来不难。
先给一遍实验流程中得出的图:
下边上完整代码(仅供参考,实验中用到的语音数据为:oh.mat,你自己做可能需要修改相应的门限参数)
此外实验中需要的语音信号分帧函数,我有篇索引分帧法的博文里边有讲解和代码
传送门
%Ocross:短时过零率
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%输入参数:
% frame_w 分帧矩阵
%输出参数:
% zerocross 过零率序列
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function [zerocross]=Ocross(frame_w)
[frame_length,frame_number]=size(frame_w);
zerocross=zeros(1,frame_number);
for i=1 : frame_number
u=frame_w(:,i); % 取出一帧
for j=1 : frame_length-1
if u(j)* u(j+1)< 0 % 判断是否为过零点
zerocross(i)=zerocross(i)+1; % 是过零点,记录1次
end % end过零判断
end % end单帧循环
end % end帧数循环
% Experiment 4 endpointDetection
% Apr.18 2020
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
clear
close all
clc
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%
load(‘oh.mat’,‘data’);
fs=10000;
N=1024;
width = 3; % Width in inches
height = 3; % Height in inches
alw = 0.75; % AxesLineWidth
fsz = 13; % Fontsize
lw = 1.2; % LineWidth 1.5
msz = 7; % MarkerSize
frame_time=20e-3;
[frame_m,frame_w,frame_length,frame_shift,frame_number]=enframe(data,fs,20e-3,10e-3,‘hamming’);
% 调用分帧加窗函数,获取分帧矩阵、分帧加窗矩阵、帧长、帧数
timeAxis=(1:frame_number)*20e-3;
zerocross=Ocross(frame_w);
plot(data)
legend(‘原始语音时域图’,‘Location’,‘best’)
xlabel(‘时间(采用点数)’);
ylabel(‘幅度’);
mag=sum(abs(frame_w));%幅度
f = (0:N/2-1)/Nfs; %计算频率序列
freq=fft(frame_w,N); %将数据由时域转换到频域
spect=real(10log10(freq));%将频域结果单位换算为dB
Y=spect.conj(spect);%计算能量
sumY=sum(Y(1:end/2,:));
sumY=sumY(ones(1,N),:);
P=Y./sumY;%计算样本点概率
H=-sum(P(1:end/2,:).(log2(P(1:end/2,:))));%代入谱熵定义的公式
figure();
plot(timeAxis,H)
legend(‘谱熵时域图’,‘Location’,‘best’)
xlabel(‘时间/s’);
ylabel(‘幅度’);
figure()
subplot(211)
plot(timeAxis,mag)
subplot(212)
plot(timeAxis,H)
EntropyHigh=max(H)*0.995;%谱熵高门限
EntropyLow=min(H)*1.06;%谱熵低门限
hold on
plot([timeAxis(1), timeAxis(end)], [EntropyHigh, EntropyHigh], ‘r’, ‘LineWidth’,lw, ‘MarkerSize’, msz);
plot([timeAxis(1), timeAxis(end)], [EntropyLow, EntropyLow], ‘g’, ‘LineWidth’,lw, ‘MarkerSize’, msz);
legend(‘谱熵与赋值对比时域图’,‘高谱熵门限’,‘低谱熵门限’,‘Location’,‘best’)
xlabel(‘时间/s’);
ylabel(‘幅度’);
figure()
T=24;%设置过零率门限
plot(timeAxis,zerocross)
hold on
plot([timeAxis(1), timeAxis(end)], [T, T], ‘r’, ‘LineWidth’,lw, ‘MarkerSize’, msz);
legend(‘过零率’,‘门限’,‘Location’,‘best’)
xlabel(‘时间/s’);
ylabel(‘幅度’);
figure()
plot(data)
hold on
for i=1:frame_number
if(H(i)
noiseEnd=i-1;
for j=noiseEnd:-1:1
if(zerocross(j)>T)
noiseEnd=j;
break
end
end
end
if(H(i)
noiseBegin=i+1;
for j=noiseBegin:frame_number
if(zerocross(j)>T)
noiseBegin=j;
break
end
end
break
end
end
endIndex=noiseEnd*(frame_length+1)/2;
beginIndex=noiseBegin*(frame_length+1)/2;
noise1=data(1:endIndex);
noise2=data(beginIndex:length(data));
plot(noise1,‘r’)
hold on
plot(beginIndex:length(data),noise2,‘g’)
legend(‘原始语音’,‘首噪声段’,‘尾噪声段’,‘Location’,‘best’)
xlabel(‘时间(采样点数)’);
ylabel(‘幅度’);
figure()
oh_clean=data(endIndex:beginIndex);
plot(oh_clean)
legend(‘cleanData’,‘Location’,‘best’)
xlabel(‘时间(采样点数)’);
ylabel(‘幅度’);
save(‘oh_clean’)