Android IOS WebRTC 音视频开发总结(五一)-- 降噪基本原理
文章主要介绍噪声消除,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,欢迎关注微信公众号blacker,更多详见www.rtc.help
--------------------------------------------
RTC中声音处理是个很麻烦的事,难点很多,回声,噪声,啸声,增益,等等,其实从中国潜艇噪声那么大就可看出这东西确实不好处理。
这些年我也被这些问题搞得烦死了,特别是android上面的,当然折腾过程中也发现了很多被大家误解的现象:
1、只有qq和微信语音效果才处理得比较好?
不是,有些公司语音效果也很牛b,只不过他们的产品的终端用户不是个人,所以我们很少听说,但这不影响他们在业界的口碑,为了避嫌,名字我就不说了,大家以后有用到的时候自然会知道。
2、做图像识别和语音识别的公司在RTC方面就一定很牛?
不一定,昨天科技园一家做通讯的公司给我看了他们的一个合作客户——硅谷很牛的一家做机器人公司(语音识别和视频交互,不是那种简单的摄像头转一下,扫扫地的),人家对RTC基本上就是一片空白,这也正说明了术业有专攻。
3、视频通讯的都是用WebRTC搞的?
有些公司是完全用WebRTC搞的(用libjingle,ICE,webrtc),而更多公司只是用他里面的一些模块,当然也有公司基本上都是自己搞的(包括编解码,传输,抖动缓冲,回声消除,噪声消除等),WebRTC对他们来说主要是做参考。
4、找个大牛用webrtc就可以搞定RTC所有问题?
用之前在微信里面做底层编解码的朋友的话解答:“我们做底层算法的不熟悉上层应用,熟悉上层应用的不熟悉底层算法”,我当时补充了一句:“两者都能搞定的不熟悉市场,熟悉市场又懂技术的当老板去了”,世界就是这样,没有人能搞定所有问题,所以合作与互补很重要,这就是我为什么一直找志同道合的人的原因。
更多交流请关注我的微信公众号blacker,言归正传,下面这篇文章是刘老师(icoolmedia)写的,kelly进行编辑和整理
一、谱减法语音降噪基本原理
谱减算法为最早的语音降噪算法之一,它的提出,基于一个简单的原理:
假设语音中的噪声只有加性噪声,只要将带噪语音谱减去噪声谱,就可以得到纯净语音幅度。这么做的前提是噪声信号是平稳的或者缓慢变化的。
得到纯净信号的幅度谱后,可以结合带噪语音相位(近似带替纯净语音相位),从而得到近似的纯净语音,语音信号的相位对语音可懂度不敏感。
按上述所示,如果我们设y(n)为受噪声污染的信号,则y(n)由纯净语音信号x(n)和加性噪声d(n)组成,即:y(n)=X(n)+d(n)。
其傅里叶变换后表示为:Y(ω)=X(ω)+D(ω),或写为:
X(ω) = Y(ω) – D(ω),如果用功率谱表示可以写为:
这里被称为交叉项,我们假定d(n)具有0均值,并且与x(n)不相关,则交叉项为0,
上述公式简化为: 或写为:
二、音乐噪声和过减因子、谱下限的关系
如果带噪语音的幅度谱(功率谱也同此理)与估计出来的噪声谱相减出现负值时,说明对噪声出现了过估计问题,对这种现象最简单的处理就是将负值设为0,以保证非负的幅度谱。但是对负值的这种处理,会导致信号帧频谱的随机位置上出现小的,独立的峰值。
转换到频域后,这些峰值听起来就像帧与帧之间频率随机变化的多频音,这种情况在清音段尤其明显,这种由于半波整流引起的“噪声”被称为“音乐噪声”。从根本上,通常导致音乐噪声的原因主要有:
1,对谱减算法中的负数部分进行了非线性处理
2,对噪声谱的估计不准
3,抑制函数(增益函数)具有较大的可变性
减小音乐噪声的方法是对噪声谱使用过减技术,同时对谱减后的负值设置一个下限,而不是将它们设为0,其技术形式如下:
其中alpha(大于等于1)为过减因子,它主要影响语音谱的失真程度。Beta(大于0小于1)是谱下限参数,可以控制残留噪声的多少以及音乐噪声的大小。
使用过减因子与谱下限的动机在于:当从带噪语音谱中减去噪声谱估计的时候,频谱中会残留一些隆起的部分或谱峰,有些谱峰是宽带的,有些谱峰很窄,看起来像是频谱上的一个脉冲。通过对噪声谱的过减处理,我们可以减小宽带谱峰的幅度,有时还可以将其完全消除。但是仅仅这样还不够,因为谱峰周围可能还存在较深的谱谷。因此需要使用谱下限来“填充”这些谱谷。
在高信噪比中,alpha应取小值;对低信噪比中,alpha建议取大值。Berouti等人做了大量实验来确定alpha与beta的最优值,在这里我们直接使用就可以了。具体请参考论文:Enhancement of speech corrupted by a acoustic noise。
下面给出谱减算法的Matlab验证代码,调用方法为specsub(‘filename.wav’,’outfile.wav’);
function specsub(filename,outfile) if nargin < fprintf('Usage: specsub noisyfile.wav outFile.wav \n\n'); return; end [x,fs,nbits] = wavread(filename); len = floor(*fs/); % Frame size in samples if rem(len,) == , len=len+; end; PERC = ; % window overlap in percent of frame size len1 = floor(len*PERC/); len2 = len-len1; Thres = ; % VAD threshold in dB SNRseg Expnt = 2.0; % power exponent beta = 0.002; G = 0.9; win = hamming(len); winGain = len2/sum(win); % normalization gain for overlap+add with % overlap % Noise magnitude calculations - assuming that the first frames is noise/silence nFFT = *^nextpow2(len); noise_mean = zeros(nFFT,); j=; for k = : noise_mean = noise_mean+abs(fft(win.*x(j:j+len-),nFFT)); j = j+len; end noise_mu = noise_mean/; %--- allocate memory and initialize various variables k = ; img = sqrt(-); x_old = zeros(len1,); Nframes = floor(length(x)/len2)-; xfinal = zeros(Nframes*len2,); %========================= Start Processing =============================== for n = :Nframes insign = win.*x(k:k+len-); % Windowing spec = fft(insign,nFFT); % compute fourier transform of a frame sig = abs(spec); % compute the magnitude %save the noisy phase information theta = angle(spec); SNRseg = *log10(norm(sig,)^/norm(noise_mu,)^); if Expnt == 1.0 % 幅度谱 alpha = berouti1(SNRseg); else alpha = berouti(SNRseg); % 功率谱 end %&&&&&&&&& sub_speech = sig.^Expnt - alpha*noise_mu.^Expnt; diffw = sub_speech - beta*noise_mu.^Expnt; % 当纯净信号小于噪声信号的功率时 % beta negative components z = find(diffw <); if~isempty(z) sub_speech(z) = beta*noise_mu(z).^Expnt; % 用估计出来的噪声信号表示下限值 end % --- implement a simple VAD detector -------------- if (SNRseg < Thres) % Update noise spectrum noise_temp = G*noise_mu.^Expnt+(-G)*sig.^Expnt; % 平滑处理噪声功率谱 noise_mu = noise_temp.^(/Expnt); % 新的噪声幅度谱 end % flipud函数实现矩阵的上下翻转,是以矩阵的“水平中线”为对称轴 %交换上下对称元素 sub_speech(nFFT/+:nFFT) = flipud(sub_speech(:nFFT/)); x_phase = (sub_speech.^(/Expnt)).*(cos(theta)+img*(sin(theta))); % take the IFFT xi = real(ifft(x_phase)); % --- Overlap and add --------------- xfinal(k:k+len2-)=x_old+xi(:len1); x_old = xi(+len1:len); k = k+len2; end wavwrite(winGain*xfinal,fs,,outfile); function a = berouti1(SNR) if SNR >= -5.0 & SNR <= a = -SNR*/; else if SNR < -5.0 a = ; end if SNR > a = ; end end function a = berouti(SNR) if SNR >= -5.0 & SNR <= a = -SNR*/; else if SNR < -5.0 a = ; end if SNR > a = ; end end
三、几种改进的谱减算法
1,非线性谱减
Berouti等人提出的谱减算法,假设了噪声对所有的频谱分量都有同等的影响,继而只用了一个过减因子来减去对噪声的过估计。现实世界中的噪声并非如此,这意味着可以用一个频率相关的减法因子来处理不同类型的噪声。
2,多带谱减法
在多带算法中,将语音频谱划分为N个互不重叠的子带,谱减法在每个子带独立运行。将语音信号分为多个子带信号的过程可以通过在时域使用带通滤波器来进行,或者在频域使用适当的窗。通常会采用后一种办法,因为实现起来有更小的运算量。
多带谱减与非线性谱减的主要区别在于对过减因子的估计。多带算法针对频带估计减法因子,而非线性谱减算法针对每一个频点,导致频点上的信噪比可能有很大变化。这种剧烈变化是谱减法中所遇到的语音失真(音乐噪声)的原因之一。相反,子带信噪比变化则不会特别剧烈。
3,MMSE谱减算法
上面的方法中,谱减参数alpha和beta通过实验确定,无论如何都不会是最优的选择。MMSE谱减法能够在均方意义下最优地选择谱减参数。具体请参考论文:A parametic formulation of the generalized spectral subtractor method
4,扩展谱减法
基于自适应维纳滤波与谱减原理的结合。维纳滤波用于估计噪声谱,然后从带噪语音信号中减去该噪声谱。具体请参考以下两篇论文:
Extended Spectral Substraction:Description and Preliminary Results.
Extended Spectral Substraction
5,自适应增益平均的谱减
谱减法中导致音乐噪声的两个因素在于谱估计的大范围变化以及增益函数的不同。对于第一个问题,Gustafsson等人建议将分析帧划分为更换小的子帧以得到更低分辨率的频谱。子帧频谱通过连续平均以减小频谱的波动。对于第二个问题Gustafsson等人提出使用自适应指数平均,在时间上对增益函数做平滑。此外,为了避免因使用零相位增益函数导致的非因果滤波问题,Gustafsson等人建议在增益函数中引入线性相位。具体请参考论文:Spectral subtraction using reduced delay convolution and adaptive averaging
6,选择性谱减法
前面提到的方法对所有语音都做同样处理。并不区分是浊音段还是清音段。区分浊音与清音的谱减法有:
6.1, 双频带谱减法。通过将带噪语音能量与某一阈值进行比较,把语音帧分为浊音和清音。对于浊音帧,用算法确定一个截止频率,在该截止频率之上,语音被认为是随机信号。浊音段则通过滤波分为两个频带,一个频带位于截止频率之下(低通滤波后的语音),另外一个频带高于截止频率(高通滤波后的语音)。然后对低通和高通后的语音信号使用不同的算法进行处理。对低通语音部分在短时傅立叶变换的基础上使用过减算法,对于高通部分以及清音段,使用Thomson的多窗谱估计器取代FFT估计器。主要目的在于减小高频部分的频谱值的波动。具体请参考论文:Adaptive two-band spectral subtraction with multi-window spectral estimation
6.2,双激励语音模型法,该算法把语音分为两个独立的组成部分--浊音分量和清音分量。也就是说,语音由这两个分量的和来表示(注意不同于将语音分为浊音段和清音段)。浊音分量的分析是基于对基音频率和谐波幅度的提取。然后从带噪语音谱中减去浊音谱就得到了清音谱。然后使用一个双通道系统,基中一个包括改进的维纳滤波器,被用于增强清音谱。最终增强的语音由增强后的浊音分量和清音分量求和得到。具体请参考论文:Speech enhancement using the dual excitation speech model
6.3,还有一种基于浊音、清音的谱减算法,在该算法中语音帧首先根据能量和过零率被划分为浊音和清音。然后将带噪语音谱与锐化函数进行卷积,清音的频谱就会被锐化(用锐化函数进行镨锐化的目的在于增加谱对比度,即在抑制谱谷的同时使谱峰更加突出)。具体请参考论文:Spectral subtraction based on phonetic dependency and masking effects
7,基于感知特性的谱减
前面提到的方法,谱减参数要么是通过实验计算短时信噪比得到,要么是通过最优均方误差得到,均没有考虑听觉系统的特性,该算法的主要目的是使残余噪声在听觉上难以被察觉。利用了人类听觉系统改进系统的可懂度(即人耳的掩蔽效应)
喜欢系列文章请关注微信公众号blacker,或扫描下方二维码:
Android IOS WebRTC 音视频开发总结(五一)-- 降噪基本原理的更多相关文章
- 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)
随笔分类 - webrtc Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...
- Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)
本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...
- Android IOS WebRTC 音视频开发总结(八十三)-- 使用WebRTC广播网络摄像头视频(上)
本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...
- Android IOS WebRTC 音视频开发总结(四六)-- 从另一个角度看国内首届WebRTC大会
文章主要从开发者角度谈国内首届WebRTC大会,支持原创,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,更多详见www.rtc.help. -------------------- ...
- Android IOS WebRTC 音视频开发总结(六)-- iOS开发之含泪经验
前段时间在搞webrtc iOS开发,所以将标题改为了Android IOS WebRTC 音视频开发总结, 下面都是开发过程中的经验总结,转载请说明出处(博客园RTC.Blacker): 1. IO ...
- Android IOS WebRTC 音视频开发总结(二四)-- p2p调用堆栈
本文主要分析webrtc音视频点对点部分的代码结构,文章来自博客园RTC.Blacker,转载请说明出处. 前段时间在查一个偶尔断线的问题(这种问题最蛋疼,不好重现,只能凭经验去搞),所以理了下web ...
- Android IOS WebRTC 音视频开发总结(二三)-- hurtc使用说明
本文主要介绍如何测试基于浏览器和手机的视频通话程序,转载请说明出处,文章来自博客园RTC.Blacker,更多详见www.blackerteam.com 很多人想测试浏览器(包括浏览器版本和桌面e ...
- Android IOS WebRTC 音视频开发总结(六十)-- 您为什么招不到适合的音视频人才
本文主要介绍音视频行业招聘现状,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号blackerteam,更多详见www.blackerteam.com 有过音视频人才招聘经验的应该都深有 ...
- Android IOS WebRTC 音视频开发总结(五七)-- 网络传输上的一种QoS方案
本文主要介绍一种QoS的解决方案,文章来自博客园RTC.Blacker,欢迎关注微信公众号blacker,更多详见www.rtc.help QoS出现的背景: 而当网络发生拥塞的时候,所有的数据流都有 ...
随机推荐
- viewpage广告条的制作
package com.itheima27.viewpagerdemo; import java.util.ArrayList; import java.util.List; import andro ...
- qt上用opencv显示摄像头视频
参考:http://blog.csdn.net/augusdi/article/details/8865541 代码如下: 注意,要在ui界面上放置一个“Vertical Layout”控件,调整到合 ...
- 异步task处理
public async Task<Customers> GetCustomers() { return await Service.GetCustomersAsync(); } publ ...
- 二叉树的JAVA实现-二叉树的增删改查CRUD
package org.lyk.interfaces; import java.util.List; public interface IBiTree<T extends Comparable& ...
- CRM PrincipalObjectAccess(POA)
PrincipalObjectAccess (POA) table is an important table which holds all grants share on CRM objects. ...
- Spring配置项<context:annotation-config/>说明
配置applicationContext.xml时经常会看到: <context:annotation-config/> 它的作用是隐式地向Spring容器注册AutowiredAnnot ...
- C++ primer 练习9.49
如果一个字母延伸到中线之上,如d或f,则称其有上出头部分.如果一个字母延伸到中线之下,如p或g, 则称其有下出头部分.编写程序,读入一个单词,输出最长的即不包含上出头部分,也不包含下出头部分单 词. ...
- 学习Webservice测试
2014-04-01 可用Myeclipse10自带工具生成客户端, 也可用CXF生成,注意,不要用CXF3.0.0milestone,该版本不能生成,请用CXF2.2.8 2015-01-12 下载 ...
- OpenStack学习
========================================== openstack的场景是什么 openstack的目的是把空闲的机器组织起来,经过虚拟化,租给用户使用. 出现的 ...
- Oracle教程:如何诊断节点重启问题(转载)
本文对如何诊断RAC环境中节点重启问题进行了介绍.适用于10gR2和11gR1. 首先我们对能够导致节点重启的CRS进程进行介绍.1.ocssd : 它的主要功能是节点监控(Node Monitori ...