⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计7267字,阅读大概需要3分钟
🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号:不懂开发的程序猿
个人网站:https://jerry-jy.co/❗❗❗知识付费,🈲止白嫖,有需要请后台私信或【文末】个人微信公众号联系我
语音识别--光谱门控降噪
- 光谱门控降噪
- 一、任务需求
- 二、任务目标
- 1、在噪声音频剪辑上计算 FFT
- 2、通过噪声的 FFT 计算频率数据
- 3、根据噪声的统计数据(以及算法的所需灵敏度)计算阈值
- 4、对信号计算 FFT
- 5、通过将信号 FFT 与阈值进行比较来确定掩膜
- 6、使用滤波器在频率和时间上平滑掩膜
- 7、掩码应用于信号的 FFT,并反转
- 三、任务环境
- 1、jupyter开发环境
- 2、python3.6
- 3、tensorflow2.4
- 四、任务实施过程
- 加载工具
- 1、加载数据
- 2、添加噪音
- 3、去噪
- 五、任务小结
- 说明
光谱门控降噪
一、任务需求
本降噪方法使用了audacity的降噪方法
该降噪方法使用 Python 实现,可以降低语音、生物声学和生理信号等时域信号中的噪声。它依赖于一种称为“频谱门控”的方法,这是一种噪声门控方法。它的工作原理是计算信号(以及可选的噪声信号)的频谱图并估计该信号/噪声的每个频带的噪声阈值(或门)。该阈值用于计算掩码,该掩码将噪声选入频率变化阈值以下。
本方法要求两个输入:
原型噪声音频
要去除的信号
要求:使用audacity的降噪方法对音频进行降噪
二、任务目标
1、在噪声音频剪辑上计算 FFT
2、通过噪声的 FFT 计算频率数据
3、根据噪声的统计数据(以及算法的所需灵敏度)计算阈值
4、对信号计算 FFT
5、通过将信号 FFT 与阈值进行比较来确定掩膜
6、使用滤波器在频率和时间上平滑掩膜
7、掩码应用于信号的 FFT,并反转
三、任务环境
1、jupyter开发环境
2、python3.6
3、tensorflow2.4
四、任务实施过程
实验步骤
- 在噪声音频剪辑上计算 FFT
- 通过噪声的 FFT 计算频率数据
- 根据噪声的统计数据(以及算法的所需灵敏度)计算阈值
- 对信号计算 FFT
- 通过将信号 FFT 与阈值进行比较来确定掩码
- 使用过滤器在频率和时间上平滑蒙版
- 掩码应用于信号的 FFT,并反转
加载工具
import IPython
from scipy.io import wavfile
import scipy.signal
import numpy as np
import matplotlib.pyplot as plt
import librosa
%matplotlib inline
1、加载数据
原始语音内容如下:
"I know the human being and fish can coexist peacefully."– Saginaw, Michigan, September 29, 2000
wav_loc = "/home/jovyan/datas/fish.wav"
rate, data = wavfile.read(wav_loc)
data = data / 32768
IPython.display.Audio(data=data, rate=rate)
fig, ax = plt.subplots(figsize=(20,4))
ax.plot(data)
[<matplotlib.lines.Line2D at 0x7f564b8feb00>]
上图显示的是该语音对应的波形,横坐标是采样点
2、添加噪音
定义噪声生成器
- 使用快速傅里叶变换生成噪声
- 生成带限噪声
def fftnoise(f):f = np.array(f, dtype="complex")Np = (len(f) - 1) // 2phases = np.random.rand(Np) * 2 * np.piphases = np.cos(phases) + 1j * np.sin(phases)f[1 : Np + 1] *= phasesf[-1 : -1 - Np : -1] = np.conj(f[1 : Np + 1])return np.fft.ifft(f).realdef band_limited_noise(min_freq, max_freq, samples=1024, samplerate=1):freqs = np.abs(np.fft.fftfreq(samples, 1 / samplerate))f = np.zeros(samples)f[np.logical_and(freqs >= min_freq, freqs <= max_freq)] = 1return fftnoise(f)
生成噪声,并在原始音频上添加噪声
noise_len = 2 # 单位:秒
# 使用自定义函数生成噪声
noise = band_limited_noise(min_freq=4000, max_freq = 12000, samples=len(data), samplerate=rate)*30
# 将噪声裁剪成和原始音频同样长
noise_clip = noise[:rate*noise_len]
# 将噪声与原始音频叠加
audio_clip_band_limited = data+noise
演示添加噪声后的音频数据
fig, ax = plt.subplots(figsize=(20,4))
ax.plot(audio_clip_band_limited)
IPython.display.Audio(data=audio_clip_band_limited, rate=rate)
可以看到,添加噪声以后,声音的波形明显受到干扰,听起来也不够清楚。
3、去噪
自定义相关函数,用于降噪及演示降噪的过程
import time
from datetime import timedelta as td# 短时傅立叶变换
def _stft(y, n_fft, hop_length, win_length):return librosa.stft(y=y, n_fft=n_fft, hop_length=hop_length, win_length=win_length)# 逆短时傅立叶变换
def _istft(y, hop_length, win_length):return librosa.istft(y, hop_length, win_length)# 将幅度谱图转换为 dB 标度的谱图。
def _amp_to_db(x):return librosa.core.amplitude_to_db(x, ref=1.0, amin=1e-20, top_db=80.0)# 将 dB 标度的谱图转换为幅度谱图。
def _db_to_amp(x,):return librosa.core.db_to_amplitude(x, ref=1.0)# 绘制频谱图
def plot_spectrogram(signal, title):fig, ax = plt.subplots(figsize=(20, 4))cax = ax.matshow(signal,origin="lower",aspect="auto",cmap=plt.cm.seismic,vmin=-1 * np.max(np.abs(signal)),vmax=np.max(np.abs(signal)),)fig.colorbar(cax)ax.set_title(title)plt.tight_layout()plt.show()# 绘制统计量及滤波器
def plot_statistics_and_filter(mean_freq_noise, std_freq_noise, noise_thresh, smoothing_filter
):fig, ax = plt.subplots(ncols=2, figsize=(20, 4))plt_mean, = ax[0].plot(mean_freq_noise, label="Mean power of noise")plt_std, = ax[0].plot(std_freq_noise, label="Std. power of noise")plt_std, = ax[0].plot(noise_thresh, label="Noise threshold (by frequency)")ax[0].set_title("Threshold for mask")ax[0].legend()cax = ax[1].matshow(smoothing_filter, origin="lower")fig.colorbar(cax)ax[1].set_title("Filter for smoothing Mask")plt.show()# 定义降噪函数
def removeNoise(audio_clip,noise_clip,n_grad_freq=2,n_grad_time=4,n_fft=2048,win_length=2048,hop_length=512,n_std_thresh=1.5,prop_decrease=1.0,verbose=False,visual=False,
):"""根据仅包含噪声的剪辑从音频中去除噪声参数:audio_clip(数组):第一个参数。noise_clip(数组):第二个参数。n_grad_freq (int):使用掩码平滑处理多少个频率通道。n_grad_time (int):用掩码平滑的时间通道数。n_fft (int):STFT 列之间的帧数。win_length (int): 每帧音频都被 window() 窗口化。窗口的长度为“win_length”,然后用零填充以匹配“n_fft”。hop_length (int):STFT 列之间的帧数。n_std_thresh (int):比噪声的平均 dB(在每个频率级别)大多少个标准偏差被视为信号prop_decrease (float): 你应该在多大程度上减少噪音(1 = 全部,0 = 无)visual (bool): 是否绘制算法的步骤返回:数组:减去噪声的恢复信号"""if verbose:start = time.time()# 对噪声应用短时傅里叶变换noise_stft = _stft(noise_clip, n_fft, hop_length, win_length)noise_stft_db = _amp_to_db(np.abs(noise_stft)) # 转换为dB频谱# 对噪声计算均值、标准差统计量mean_freq_noise = np.mean(noise_stft_db, axis=1)std_freq_noise = np.std(noise_stft_db, axis=1)noise_thresh = mean_freq_noise + std_freq_noise * n_std_threshif verbose:print("STFT on noise:", td(seconds=time.time() - start))start = time.time()# 对信号应用短时傅里叶变换if verbose:start = time.time()sig_stft = _stft(audio_clip, n_fft, hop_length, win_length)sig_stft_db = _amp_to_db(np.abs(sig_stft))if verbose:print("STFT on signal:", td(seconds=time.time() - start))start = time.time()# 计算dB频谱掩码mask_gain_dB = np.min(_amp_to_db(np.abs(sig_stft)))print(noise_thresh, mask_gain_dB)# 在时间和频率上为掩码创建平滑过滤器smoothing_filter = np.outer(np.concatenate([np.linspace(0, 1, n_grad_freq + 1, endpoint=False),np.linspace(1, 0, n_grad_freq + 2),])[1:-1],np.concatenate([np.linspace(0, 1, n_grad_time + 1, endpoint=False),np.linspace(1, 0, n_grad_time + 2),])[1:-1],)smoothing_filter = smoothing_filter / np.sum(smoothing_filter)# 计算每个频率/时间窗的阈值db_thresh = np.repeat(np.reshape(noise_thresh, [1, len(mean_freq_noise)]),np.shape(sig_stft_db)[1],axis=0,).T# 如果信号高于阈值则屏蔽sig_mask = sig_stft_db < db_threshif verbose:print("Masking:", td(seconds=time.time() - start))start = time.time()# 使用平滑滤波器对掩码进行卷积sig_mask = scipy.signal.fftconvolve(sig_mask, smoothing_filter, mode="same")sig_mask = sig_mask * prop_decreaseif verbose:print("Mask convolution:", td(seconds=time.time() - start))start = time.time()# 对信号应用掩码sig_stft_db_masked = (sig_stft_db * (1 - sig_mask)+ np.ones(np.shape(mask_gain_dB)) * mask_gain_dB * sig_mask) # 对真实值进行掩码sig_imag_masked = np.imag(sig_stft) * (1 - sig_mask)sig_stft_amp = (_db_to_amp(sig_stft_db_masked) * np.sign(sig_stft)) + (1j * sig_imag_masked)if verbose:print("Mask application:", td(seconds=time.time() - start))start = time.time()# 修复信号recovered_signal = _istft(sig_stft_amp, hop_length, win_length)recovered_spec = _amp_to_db(np.abs(_stft(recovered_signal, n_fft, hop_length, win_length)))if verbose:print("Signal recovery:", td(seconds=time.time() - start))if visual:plot_spectrogram(noise_stft_db, title="Noise")if visual:plot_statistics_and_filter(mean_freq_noise, std_freq_noise, noise_thresh, smoothing_filter)if visual:plot_spectrogram(sig_stft_db, title="Signal")if visual:plot_spectrogram(sig_mask, title="Mask applied")if visual:plot_spectrogram(sig_stft_db_masked, title="Masked signal")if visual:plot_spectrogram(recovered_spec, title="Recovered spectrogram")return recovered_signal
output = removeNoise(audio_clip=audio_clip_band_limited, noise_clip=noise_clip,verbose=True,visual=True)
上图依次为我们演示了:
- 噪声的频谱图
- 计算的掩膜的阈值
- 用于平滑掩膜的滤波器
- 原始信号对应的频谱图
- 计算得到的掩膜频谱图
- 经过掩膜处理的信号频谱图
- 经过恢复后的声音信号频谱图
最后我们观察一下降噪以后得到的声音结果
fig, ax = plt.subplots(nrows=1,ncols=1, figsize=(20,4))
plt.plot(output, color='black')
ax.set_xlim((0, len(output)))
plt.show()
# 播放声音样本
IPython.display.Audio(data=output, rate=44100)
五、任务小结
本实验完成光谱门控降噪方法对声音进行降噪处理。通过本实验我们学习到了光谱门控降噪的相关知识,需要掌握以下知识点:
- 通过噪声的傅里叶变换计算得到噪声相关特征
- 通过对信号计算傅里叶变换,确定阈值,从而确定掩膜/掩码
- 对声音掩膜应用滤波器,实现降噪
–end–
说明
本实验(项目)/论文若有需要,请后台私信或【文末】个人微信公众号联系我