3. 维纳滤波降噪
任务目标
实现维纳滤波降噪,针对平稳加性噪声场景,通过估计噪声功率谱与带噪信号功率谱,计算维纳增益函数并对带噪语音进行频域滤波,最终输出降噪后的语音信号。
核心原理
维纳滤波在频域的最优解为:
H(k) = Ps(k) / (Ps(k) + Pn(k))
其中 Ps(k) 为纯净信号功率谱,Pn(k) 为噪声功率谱。实际中 Ps(k) 不可直接获得,用 Px(k) - Pn(k) 近似(Px(k) 为带噪信号功率谱),并做半波整流防止负值。
实现方案
采用分帧加窗、逐帧FFT处理的方式。噪声功率谱通过前导无语音段(前10帧)估计并保持固定,适用于平稳噪声场景。增益下限设为0.01以避免完全消除语音。
完整代码
import numpy as np
import soundfile as sf
def wiener_filter(input_file, output_file, noise_frames=10, alpha=0.98):
"""
维纳滤波降噪(平稳噪声场景)
参数:
input_file: 输入带噪语音路径
output_file: 输出降噪语音路径
noise_frames: 用于估计噪声的起始帧数
alpha: 平滑系数(0~1),越大平滑越强
"""
# 读取音频
x, sr = sf.read(input_file)
if x.ndim > 1:
x = x.mean(axis=1) # 转单声道
# 分帧参数
win_len = 512
hop = 128
win = np.hanning(win_len)
# 计算帧数并填充
n_frames = 1 + (len(x) - win_len) // hop
x_pad = np.pad(x, (0, n_frames * hop + win_len - len(x)), mode='reflect')
# 分帧
frames = np.stack([x_pad[i*hop:i*hop+win_len] * win
for i in range(n_frames)])
# 逐帧FFT
X = np.fft.rfft(frames, axis=1)
mag = np.abs(X)
phase = np.angle(X)
# 估计噪声功率谱(前noise_frames帧)
noise_power = np.mean(mag[:noise_frames]**2, axis=0)
# 逐帧维纳滤波
est = np.zeros_like(mag)
smooth_power = mag[0]**2 # 初始功率谱
for i in range(n_frames):
# 功率谱平滑
smooth_power = alpha * smooth_power + (1 - alpha) * mag[i]**2
# 估计纯净信号功率谱(半波整流)
speech_power = np.maximum(smooth_power - noise_power, 0)
# 维纳增益
gain = speech_power / (speech_power + noise_power)
gain = np.clip(gain, 0.01, 1.0)
est[i] = mag[i] * gain
# 重建信号
Y = est * np.exp(1j * phase)
frames_out = np.fft.irfft(Y, axis=1)
# 重叠相加
out = np.zeros(len(x_pad))
win_sum = np.zeros(len(x_pad))
for i in range(n_frames):
out[i*hop:i*hop+win_len] += frames_out[i] * win
win_sum[i*hop:i*hop+win_len] += win**2
# 归一化并去除填充
out = out / (win_sum + 1e-8)
out = out[:len(x)]
# 写入文件
sf.write(output_file, out, sr)
return out
if __name__ == "__main__":
# 使用示例
wiener_filter("noisy.wav", "enhanced.wav")
运行与验证
依赖安装:
pip install numpy soundfile
运行方式:将带噪语音文件命名为 noisy.wav 置于脚本同目录,执行脚本即可生成 enhanced.wav。
验证方法:
- 准备一段干净语音,叠加高斯白噪声(信噪比约5dB)生成测试文件;
- 运行脚本后,播放
enhanced.wav,主观听感噪声应明显降低,语音可懂度保持; - 客观验证:分别计算输入/输出信噪比,输出信噪比应高于输入(例如输入5dB,输出可达10dB以上);
- 观察波形:降噪后波形中噪声底电平明显降低,语音段幅值基本保留。
关键参数说明
- 噪声估计帧数(noise_frames=10):假设前10帧(约0.16秒@8kHz)为纯噪声段,适用于平稳噪声;若噪声非平稳需改为噪声跟踪算法。
- 平滑系数(alpha=0.98):控制功率谱估计的方差,过大则跟踪慢,过小则残留音乐噪声。
- 增益下限(0.01):防止深衰减导致语音失真,同时保留残余噪声作为掩蔽。
常见问题
- 输出有“音乐噪声”:增大alpha值(如0.99),或对增益做时域平滑。
- 语音段被过度衰减:检查噪声估计是否偏高,可减少noise_frames或改用前导静音检测。
- 采样率不匹配:代码自动读取文件采样率,无需手动设置;但窗长512在低采样率(8kHz)下对应64ms,在高采样率(44.1kHz)下仅12ms,建议按采样率调整窗长(如
win_len = int(0.032 * sr))。