You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python为录音添加低通滤波并利用FFT去除白噪声?

嘿,我来帮你搞定录音白噪声降噪和低通滤波的问题!先从你提到的FFT频域降噪说起,再给你演示更专业的低通滤波实现,顺便把你的代码调整得更完善些~

一、利用FFT频域数据实现白噪声降噪

白噪声在频域通常是均匀分布的,而语音信号的能量大多集中在200-3000Hz这个区间。所以我们的核心思路是:把频域中属于噪声的分量(比如高频或者能量极低的部分)置0,再通过逆FFT转回时域信号。

先给你修正并补充后的代码,里面加了详细的注释:

from scipy.io import wavfile
import matplotlib.pyplot as plt
import numpy as np  # 你之前漏了这个关键导入哦
from numpy.fft import fft, fftfreq, ifft

# 读取wav文件:注意第二个参数"wb"是写文件用的,读文件不需要,直接传路径就行
fs, data = wavfile.read("a.wav")
n = len(data)

# 如果是立体声音频,先转成单声道(取左右声道的平均值)
if len(data.shape) > 1:
    data = data.mean(axis=1)

# 计算实际频率值(之前的freqs是归一化频率,加上1/fs才是Hz单位)
freqs = fftfreq(n, 1/fs)
mask = freqs > 0  # 只取正频率部分用于绘图

# 计算原信号的FFT
fft_vals = fft(data)

# --- 核心:频域降噪处理 ---
# 假设我们观察频谱后,确定3000Hz以上是主要噪声频段,创建掩码保留低频
cutoff_freq = 3000
noise_mask = np.abs(freqs) <= cutoff_freq  # 保留<=3000Hz的频率分量
filtered_fft_vals = fft_vals * noise_mask  # 把噪声频段的FFT值置0

# 逆FFT转回时域,取实部(计算过程中会产生微小虚部,需要去掉)
filtered_data = np.real(ifft(filtered_fft_vals))
# 转成原音频的整数类型(比如int16,避免保存时出错)
filtered_data = filtered_data.astype(data.dtype)

# 可视化原信号和滤波后的频谱对比
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(freqs[mask], 2 * np.abs(fft_vals/n)[mask])
plt.title("Original Signal Spectrum")
plt.xlabel("Frequency (Hz)")
plt.ylabel("Amplitude")

plt.subplot(2, 1, 2)
plt.plot(freqs[mask], 2 * np.abs(filtered_fft_vals/n)[mask])
plt.title("Filtered Signal Spectrum")
plt.xlabel("Frequency (Hz)")
plt.ylabel("Amplitude")
plt.tight_layout()
plt.show()

# 可选:播放滤波后的音频
# import simpleaudio as sa
# play_obj = sa.play_buffer(filtered_data, 1, 2, fs)
# play_obj.wait_done()

# 保存降噪后的音频文件
wavfile.write("denoised_a.wav", fs, filtered_data)

如果你的噪声不是简单的高频噪声,还可以用噪声样本减法:先录一段没有语音的纯噪声,计算它的平均频谱,然后从原信号频谱中减去这个噪声频谱(要注意避免出现负幅值,取最大值0),这样降噪效果会更精准。

二、用Python实现专业的低通滤波

刚才用FFT手动置0是一种快速方法,但专业场景下更推荐用scipy.signal模块设计平滑的低通滤波器(比如巴特沃斯滤波器),这样能避免频谱泄漏和信号失真。

给你完整的实现代码:

from scipy.io import wavfile
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import butter, lfilter, freqz

# 定义巴特沃斯低通滤波器的系数生成函数
def butter_lowpass(cutoff, fs, order=5):
    nyq = 0.5 * fs  # 奈奎斯特频率,是采样率的一半
    normal_cutoff = cutoff / nyq  # 归一化截止频率
    # 生成滤波器的分子(b)和分母(a)系数
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return b, a

# 定义滤波函数,应用滤波器到信号上
def butter_lowpass_filter(data, cutoff, fs, order=5):
    b, a = butter_lowpass(cutoff, fs, order=order)
    filtered_data = lfilter(b, a, data)
    return filtered_data

# 读取音频文件
fs, data = wavfile.read("a.wav")
if len(data.shape) > 1:
    data = data.mean(axis=1)

# 设置滤波参数
cutoff_freq = 3000  # 低通截止频率,单位Hz
order = 4  # 滤波器阶数:阶数越高,过渡带越陡,但可能引入更多相位失真

# 应用低通滤波
filtered_data = butter_lowpass_filter(data, cutoff_freq, fs, order)
# 转成原音频的数据类型
filtered_data = filtered_data.astype(data.dtype)

# 可视化滤波器的频率响应(可选,帮你理解滤波器效果)
b, a = butter_lowpass(cutoff_freq, fs, order)
w, h = freqz(b, a, worN=8000)
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(0.5 * fs * w / np.pi, np.abs(h), 'b')
plt.plot(cutoff_freq, 0.5 * np.sqrt(2), 'ko')  # 巴特沃斯滤波器的-3dB点
plt.axvline(cutoff_freq, color='k', linestyle='--')
plt.xlim(0, 0.5 * fs)
plt.title("Lowpass Filter Frequency Response")
plt.xlabel("Frequency (Hz)")
plt.grid()

# 对比原信号和滤波后信号的波形(取前1000个样本)
plt.subplot(2, 1, 2)
plt.plot(data[:1000], label='Original Signal')
plt.plot(filtered_data[:1000], label='Filtered Signal')
plt.legend()
plt.title("Signal Waveform (First 1000 Samples)")
plt.tight_layout()
plt.show()

# 保存滤波后的音频
wavfile.write("lowpass_filtered_a.wav", fs, filtered_data)

这个方法的优势是滤波器的过渡带更平滑,不会像手动FFT置0那样产生明显的频谱突变,能更好地保留语音的自然感。

内容的提问来源于stack exchange,提问作者umuterc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:45:25