赞
踩
在人工智能中,算法固然很重要,但语音的预处理却直接地决定了算法的性能上限,因此有必要对语音进行去噪处理。
通过截取音频中的已知噪音部分,根据该噪音样本对整个音频进行降噪。截取噪音使用ffmpeg,降噪使用sox。
1. 将音频流和视频流拆分为2个不同的文件:
视频: ffmpeg -i input.mp4 -vcodec copy -an tmpvid.mp4
音频: ffmpeg -i input.mp4 -acodec pcm_s16le -ar 128k -vn tmpaud.wav
2. 从上一步的音频结果文件中剪切一个噪声样本:
ffmpeg -i itmpaud.wav -ss 00:00:00.0 -t 00:00:00.5 noiseaud.wav
-ss: 从开始的时间偏移. (h: m: s.ms).
-t duration: 表示要剪切的音频段的持续时间(h: m: s.ms),以便下一步用来作为噪声文件。
选择一段没有语音、只有噪音的音频(例如,讲话者静音时的那一秒钟)。
3. 使用sox生成噪音profile:
sox noiseaud.wav -n noiseprof noise.prof
4. 清除音频流中的噪声样本:
sox tmpaud.wav tmpaud-clean.wav noisered noise.prof 0.21
更改0.21以调整采样率的灵敏度级别(0.2-0.3通常提供最佳结果)。
5. 使用ffmpeg将新的音频和视频流合并到一起:
ffmpeg -i tmpvid.mp4 -i tmpaud-clean.wav -map 0:v -map 1:a -c:v copy -c:a aac -b:a 128k out.mp4
如果只是要简单的实现语音去噪,那么直接进行3、4步的操作,将整段噪声语音作为噪声文件也可。
sox tmpaud.wav -n noiseprof noise.prof
sox tmpaud.wav tmpaud-clean.wav noisered noise.prof 0.21
谱减法:谱减算法为最早的语音降噪算法之一,它的提出,基于一个简单的原理:假设语音中的噪声只有加性噪声,只要将带噪语音谱减去噪声谱,就可以得到纯净语音幅度。这么做的前提是噪声信号是平稳的或者缓慢变化的。
#!/usr/bin/env python import numpy as np import wave import math import ctypes as ct class FloatBits(ct.Structure): _fields_ = [ ('M', ct.c_uint, 23), ('E', ct.c_uint, 8), ('S', ct.c_uint, 1) ] class Float(ct.Union): _anonymous_ = ('bits',) _fields_ = [ ('value', ct.c_float), ('bits', FloatBits) ] def nextpow2(x): if x < 0: x = -x if x == 0: return 0 d = Float() d.value = x if d.M == 0: return d.E - 127 return d.E - 127 + 1 # 打开WAV文档 f = wave.open("input.wav") # 读取格式信息 # (nchannels, sampwidth, framerate, nframes, comptype, compname) params = f.getparams() nchannels, sampwidth, framerate, nframes = params[:4] fs = framerate # 读取波形数据 str_data = f.readframes(nframes) f.close() # 将波形数据转换为数组 x = np.fromstring(str_data, dtype=np.short) # 计算参数 len_ = 20 * fs // 1000 # 样本中帧的大小 PERC = 50 # 窗口重叠占帧的百分比 len1 = len_ * PERC // 100 # 重叠窗口 len2 = len_ - len1 # 非重叠窗口 # 设置默认参数 Thres = 3 Expnt = 2.0 beta = 0.002 G = 0.9 # 初始化汉明窗 win = np.hamming(len_) # normalization gain for overlap+add with 50% overlap winGain = len2 / sum(win) # Noise magnitude calculations - assuming that the first 5 frames is noise/silence nFFT = 2 * 2 ** (nextpow2(len_)) noise_mean = np.zeros(nFFT) j = 0 for k in range(1, 6): noise_mean = noise_mean + abs(np.fft.fft(win * x[j:j + len_], nFFT)) j = j + len_ noise_mu = noise_mean / 5 # --- allocate memory and initialize various variables k = 1 img = 1j x_old = np.zeros(len1) Nframes = len(x) // len2 - 1 xfinal = np.zeros(Nframes * len2) # ========================= Start Processing =============================== for n in range(0, Nframes): # Windowing insign = win * x[k-1:k + len_ - 1] # compute fourier transform of a frame spec = np.fft.fft(insign, nFFT) # compute the magnitude sig = abs(spec) # save the noisy phase information theta = np.angle(spec) SNRseg = 10 * np.log10(np.linalg.norm(sig, 2) ** 2 / np.linalg.norm(noise_mu, 2) ** 2) def berouti(SNR): if -5.0 <= SNR <= 20.0: a = 4 - SNR * 3 / 20 else: if SNR < -5.0: a = 5 if SNR > 20: a = 1 return a def berouti1(SNR): if -5.0 <= SNR <= 20.0: a = 3 - SNR * 2 / 20 else: if SNR < -5.0: a = 4 if SNR > 20: a = 1 return a if Expnt == 1.0: # 幅度谱 alpha = berouti1(SNRseg) else: # 功率谱 alpha = berouti(SNRseg) ############# sub_speech = sig ** Expnt - alpha * noise_mu ** Expnt; # 当纯净信号小于噪声信号的功率时 diffw = sub_speech - beta * noise_mu ** Expnt # beta negative components def find_index(x_list): index_list = [] for i in range(len(x_list)): if x_list[i] < 0: index_list.append(i) return index_list z = find_index(diffw) if len(z) > 0: # 用估计出来的噪声信号表示下限值 sub_speech[z] = beta * noise_mu[z] ** Expnt # --- implement a simple VAD detector -------------- if SNRseg < Thres: # Update noise spectrum noise_temp = G * noise_mu ** Expnt + (1 - G) * sig ** Expnt # 平滑处理噪声功率谱 noise_mu = noise_temp ** (1 / Expnt) # 新的噪声幅度谱 # flipud函数实现矩阵的上下翻转,是以矩阵的“水平中线”为对称轴 # 交换上下对称元素 sub_speech[nFFT // 2 + 1:nFFT] = np.flipud(sub_speech[1:nFFT // 2]) x_phase = (sub_speech ** (1 / Expnt)) * (np.array([math.cos(x) for x in theta]) + img * (np.array([math.sin(x) for x in theta]))) # take the IFFT xi = np.fft.ifft(x_phase).real # --- Overlap and add --------------- xfinal[k-1:k + len2 - 1] = x_old + xi[0:len1] x_old = xi[0 + len1:len_] k = k + len2 # 保存文件 wf = wave.open('output.wav', 'wb') # 设置参数 wf.setparams(params) # 设置波形文件 .tostring()将array转换为data wave_data = (winGain * xfinal).astype(np.short) wf.writeframes(wave_data.tostring()) wf.close()
个人感觉谱减法更加耗时,且适用场景相对有限。利用sox来去噪可能是一种相对更成熟的方法。
http://www.zoharbabin.com/how-to-do-noise-reduction-using-ffmpeg-and-sox/
https://github.com/itaa/soja-box/tree/master/enhance_speach
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。