如何使用Python从混合WAV音频中单独提取脚步声?
提取WAV文件中脚步声的Python实现方案
先明确各声音的频率特征,选对滤波器
拆解三种声音的频段分布,这是选滤波器的核心依据:
- 脚步声:核心能量集中在50-500Hz(硬底鞋、不同地面会略有浮动,但整体属于中低频区间)
- 鸟鸣声:典型高频信号,集中在2kHz-10kHz,极易过滤
- 拖拉机声:包含低频引擎振动(10-200Hz)和中高频机械噪声,但低频部分和脚步声有重叠
所以单纯低通/高通都无法满足需求,得用带通滤波器先保留脚步声的核心频段(比如40-600Hz),先滤掉绝大多数鸟鸣高频噪声,再处理和拖拉机的重叠部分。
用Python实现带通滤波(scipy/librosa均可)
这里用scipy的signal模块做示例,代码简单直观:
先安装依赖:
pip install scipy librosa soundfile
代码示例:
import numpy as np import scipy.signal as signal import soundfile as sf # 读取目标WAV文件 audio, sample_rate = sf.read("your_input_audio.wav") # 计算奈奎斯特频率,设计4阶巴特沃斯带通滤波器 nyquist_freq = 0.5 * sample_rate low_cut = 40 / nyquist_freq high_cut = 600 / nyquist_freq b, a = signal.butter(4, [low_cut, high_cut], btype='band') # 双向滤波避免相位偏移 filtered_audio = signal.filtfilt(b, a, audio) # 保存初步滤波后的音频 sf.write("filtered_audio.wav", filtered_audio, sample_rate)
解决降噪误滤脚步声的问题:放弃普通降噪,用特征分离
普通降噪(比如谱减法、维纳滤波)会把和噪声频段重叠的脚步声误删,换个思路——抓住脚步声的独特特征做分离:
方法1:利用脚步声的冲击性特征
脚步声是周期性的冲击信号,时域上有明显的能量峰值,而拖拉机是持续的低频振动,鸟鸣已被滤除得差不多。用峰值检测+掩码提取:
import librosa from librosa.util import peak_pick # 提取音频的短时能量(RMS) rms = librosa.feature.rms(y=filtered_audio)[0] # 检测能量峰值(对应脚步声的冲击) # 参数需根据音频调整:pre/post_max是峰值前后的比较窗口,delta是峰值阈值 peaks = peak_pick(rms, pre_max=20, post_max=20, pre_avg=20, post_avg=20, delta=0.1, wait=100) # 生成掩码,只保留峰值附近的信号(每个脚步声持续约0.2秒) mask = np.zeros_like(filtered_audio) window_size = int(sample_rate * 0.2) hop_length = 512 # librosa计算RMS时的默认步长 for peak_idx in peaks: start = max(0, peak_idx * hop_length - window_size) end = min(len(filtered_audio), peak_idx * hop_length + window_size) mask[start:end] = 1 # 应用掩码得到脚步声信号 footstep_audio = filtered_audio * mask sf.write("footstep_only.wav", footstep_audio, sample_rate)
方法2:用预训练模型做源分离(效果更稳定)
如果手动调参麻烦,直接用OpenUnmix这种预训练的音频源分离模型,专门分离不同类型的音频源:
import torch import openunmix # 加载预训练的高质量分离模型 model = openunmix.umxhq() # 把音频转成模型需要的张量格式(单通道示例) audio_tensor = torch.tensor(filtered_audio).unsqueeze(0).unsqueeze(0) # 形状:(1, 1, 采样点数) estimates = model(audio_tensor) # 提取低音源(对应脚步声+拖拉机),再用带通滤波细化 low_source = estimates['bass'].squeeze().numpy() # 再次用带通滤波过滤拖拉机的极端低频 b, a = signal.butter(4, [60/nyquist_freq, 500/nyquist_freq], btype='band') final_footsteps = signal.filtfilt(b, a, low_source) sf.write("final_footsteps.wav", final_footsteps, sample_rate)
关键调参提示
- 带通滤波器频段:如果是高跟鞋脚步声,上限可调到800Hz;如果拖拉机低频干扰极强,把下限提到60Hz
- 峰值检测参数:先画出RMS曲线(
plt.plot(rms)),再调整delta(峰值与周围的差值阈值)和wait(避免连续检测同一脚步声) - 禁用普通降噪算法:比如谱减法会把和拖拉机重叠的脚步声当成噪声删掉,完全不适用
内容的提问来源于stack exchange,提问作者Astha
相关产品推荐
相关产品推荐

