2020-1|练习:DTMF按键声识别

练习:DTMF按键声识别

  • 前言
    • 题目及思路
    • 实现
      • 0.读取
      • 技能1 语音预处理的分帧
      • 技能2 基于短时能量与过零率的峰值检测(分音)
      • 技能3 特征提取
      • 技能4 创建特征库-结构体使用
      • 技能5 特征匹配——余弦相似度
        • 余弦相似度推导
    • 结果及分析

前言

最近开始学习语音信号处理,在CSDN上看到一个挺有意思的题目,因此依葫芦画瓢,花了两天时间,按照博客里的思路实现了这个练习,算作入门收获不小。
时间:2020.1.4-1.6

题目及思路

2020-1|练习:DTMF按键声识别_第1张图片
2020-1|练习:DTMF按键声识别_第2张图片

实现

0.读取

2020-1|练习:DTMF按键声识别_第3张图片

%% 原始波形
% m=[1 2 3 4 5 6 7 8 9 9 2];%电话号码
% creatdtmf(m);

filename = '1  2  3  4  5  6  7  8  9  9  2.wav';

[x,fs] = audioread(filename);
len = length(x);
T = (len-1)/fs;
t = linspace(0,T,len)';
f = -fs/2 : 1/T : fs/2;

X = fft(x);
X_shift = fftshift(X);

figure(1);
subplot(311);
plot(t,x./max(abs(x)));
grid on;
set(gca,'XMinorGrid','on');set(gca,'YMinorGrid','on');
xlabel('时间(s)');ylabel('归一化幅度(a.u.)');title('手机号录音');

subplot(312);
plot(f,abs(X_shift)./max(abs(X_shift)));
grid on;
set(gca,'XMinorGrid','on');set(gca,'YMinorGrid','on');
title('频谱密度');xlabel('频率(Hz)');ylabel('归一化幅度(a.u.)');

技能1 语音预处理的分帧

问:从原始音频中得到的数据效果很差,该怎么做?
答:进行预处理。分帧、降噪、滤波…

what: 把语音分割成一帧一帧的加过窗函数的短时信号,便于语音分析。
2020-1|练习:DTMF按键声识别_第4张图片
效果:原始-分帧-量化
2020-1|练习:DTMF按键声识别_第5张图片
how:

%% 方法1:
fram_time=0.1;%帧时长
fram_step_time=0.08;%帧步进
win='hamming';
[row,col]=size(x);
if row>col 
    x=x';
else
end

ts=1/fs;

l=length(x);%序列的总长度
% xn_time=l*ts;%序列的总时长
fram_length=ceil(fram_time/ts);%每一帧的长度
fram_step_length=ceil(fram_step_time/ts);%帧移的长度
if win=='hanning' win=hanning(fram_length);
elseif win=='hamming' win=hamming(fram_length);
end
%帧数的计算公式为:序列点数-帧长度+帧移)/帧移
numOfframs=(l-fram_length+fram_step_length)/fram_step_length;%计算得到帧数
%考虑到序列总长度和分帧结果不匹配,补0以满足分帧要求
numOfframs=ceil(numOfframs);
%反算为了满足分帧序列应该增加的长度
l_added=(numOfframs*fram_step_length-fram_step_length+fram_length);%得到序列应该有的长度
l=l_added-l;
x=[x,zeros(1,l)];%0
xn_time=ceil(l*ts);
xn_frams=zeros(fram_length,numOfframs);%建立存放分帧结果的矩阵

%开始分帧
for k=1:numOfframs
    dn=(k-1)*fram_step_length+(1:fram_length);
    xn_frams(:,k)=x(dn).*win';
end

%% 频率量化
f_new=[0 700 800 900 1100 1250 2000];
for i=2:7
    pos1=find(f_new(i-1)==f);%找到非零点
    pos2=find(f==f_new(i));
    fftframs_new(i-1,:)=sum(fftframs_end(pos1:pos2,:));
end   
f_new=[1 2 3 4 5 6];

方法2:voicebox中:enframe

f=enframe(x,win,inc)

技能2 基于短时能量与过零率的峰值检测(分音)

问:如何找到这11个短时信号呢?
答:通过短时功率(频域)或者短时能量(时域),找到最大值所在的帧数即可。
注意:阈值的设置。根据波形,手动设置。在第9个峰值附近有一个小峰值,起初的阈值为0.5时,也识别到了这个伪峰值,导致之后的识别不正确。(感觉手动更改阈值的方法,很麻烦。改进方法:如何自动设置阈值)
2020-1|练习:DTMF按键声识别_第6张图片
what:短时能量和过零率

how:

%计算短时功率
power_E = sum(fftframs_new);
power_E = power_E/max(power_E);%归一化
threshold=0.55;
计算过零率
function [f] = zcr(x)    
f=zeros(size(x,1),1);
for i=1:size(x,1)
    z=x(i,:);
    for j=1:(length(z)-1)
        if z(j)*z(j+1)<0
            f(i)=f(i)+1;
        end
    end
end
end

显示阈值线
plot(1:numOfframs,power_E);
hold on;
plot(1:numOfframs,threshold*ones(numOfframs,1));
%% 基于短时功率找到最大11个峰值
power_prev = 0;
phone_number_frame_index = zeros(1,11); % 建立一个空矩阵,用来存放果电话号码按键音出现的帧数
N=1;
%找到最大值11for k1 = 1:numOfframs
    idx_temp = 0;
    power_temp = power_E(k1);
    if power_prev <= power_temp
        power_prev = power_temp;
    elseif power_prev > power_temp && power_prev>threshold
            power_prev = 0;
            phone_number_frame_index(N) = k1-1;
            N = N+1; 
    else
    end
end

技能3 特征提取

根据提取到的峰值帧数 phone_number_frame_index,把对应的帧信息提取出来。(6*11)
2020-1|练习:DTMF按键声识别_第7张图片
2020-1|练习:DTMF按键声识别_第8张图片

%% 基于短时功率的分割
phone_number_frame = fftframs_new(:,phone_number_frame_index);
% 分别对每一帧进行归一化,实现增强
feature = phone_number_frame./repmat(max(phone_number_frame),size(phone_number_frame,1),1);

技能4 创建特征库-结构体使用

2020-1|练习:DTMF按键声识别_第9张图片

char(1).feature=[1 0 0 1 0 0];
char(1).name='1';

技能5 特征匹配——余弦相似度

问:如何识别出特征向量对应的数字呢?
答:用余弦相似度。计算待识别向量和已知向量的夹角余弦,值越大即越匹配。

%% 特征匹配 (计算余弦相似度)

for k=1:11
    best=0;
    for i=1:9
        char_num=(feature(:,k)')*(char(i).feature)/norm(feature(:,k))/norm(char(i).feature);  
        if (char_num+0.1)>=best%说明有相同的特征而已,还需要比较是否有更多的相同特征
            best=str2double(char(i).name);
        else
        end
    end
    phone_number(k)=best;
end

str=num2str(phone_number);
disp(str)

余弦相似度推导

2020-1|练习:DTMF按键声识别_第10张图片
理解:
使用向量、使用只有1和0的向量进行模式识别的根本原因
计算余弦值一定会遇到两个向量做点乘的情况。点乘就意味着相应的位置相乘,自然,只有都是1的位置才不为0,都是1意味着什么?——意味着这两个向量具有相同的特征。

结果及分析

输入声音:12345678992
识别结果:13356619913
在这里插入图片描述
可能原因:预处理的分帧阶段存在问题
理由:观察到分帧后的2、3有肉眼可见的明显区别,但是经过频率量化后2、3基本相同,因此可能是频率量化的区间值设置存在问题。
2020-1|练习:DTMF按键声识别_第11张图片
结果:在修改量化区间之后,效果貌似更差了…(我也不知道该怎样去修正)
2020-1|练习:DTMF按键声识别_第12张图片

结论:量化区间值,与识别有很大很大很大关系,由此可见预处理的重要性。

感谢:
https://blog.csdn.net/shenziheng1/article/details/52891807
https://blog.csdn.net/hitzyh1505/article/details/79617498
https://blog.csdn.net/qcyfred/article/details/52998644

你可能感兴趣的:(学习博客)