如何使用Python从*.wav格式心跳音频的指定时间窗口中提取心率
Python实现wav心跳音频心率计算方案
前置依赖
首先安装需要的第三方库:pip install librosa numpy scipy
核心实现逻辑
- 加载wav音频文件,获取采样率和原始音频序列
- 按指定窗口长度截取对应长度的音频片段,不足窗口长度时可抛出提示或全量处理
- 对音频片段做带通滤波,保留心跳对应的频率范围(0.67Hz ~ 3Hz,对应心率40~180次/分钟),过滤环境噪音
- 检测心跳峰值数量,通过峰值数和窗口长度换算得到每分钟心率
完整代码示例
import librosa import numpy as np from scipy.signal import butter, filtfilt, find_peaks def butter_bandpass(lowcut, highcut, sr, order=5): nyq = 0.5 * sr low = lowcut / nyq high = highcut / nyq b, a = butter(order, [low, high], btype='band') return b, a def bandpass_filter(data, lowcut, highcut, sr, order=5): b, a = butter_bandpass(lowcut, highcut, sr, order=order) y = filtfilt(b, a, data) return y def calculate_heart_rate(wav_path, window_seconds=15): # 加载音频,sr为采样率,y为单声道音频序列 y, sr = librosa.load(wav_path, sr=None, mono=True) total_duration = librosa.get_duration(y=y, sr=sr) # 截取指定窗口的音频 if total_duration < window_seconds: print(f"音频时长不足{window_seconds}秒,将使用全量音频计算") window_samples = len(y) used_window_seconds = total_duration else: # 这里默认从音频开头截取,需要调整起始位置可以修改offset参数 offset_samples = 0 window_samples = int(window_seconds * sr) y = y[offset_samples:offset_samples + window_samples] used_window_seconds = window_seconds # 带通滤波,保留心跳频率范围 lowcut = 0.67 # 对应最低心率40次/分 highcut = 3.0 # 对应最高心率180次/分 y_filtered = bandpass_filter(y, lowcut, highcut, sr) # 检测心跳峰值 min_interval = 60 / 180 # 最快心跳间隔,单位秒 min_distance = int(min_interval * sr) peaks, _ = find_peaks(y_filtered, height=np.mean(y_filtered)*1.5, distance=min_distance) # 计算心率:峰值数 / 窗口时长(秒) * 60 = 每分钟心跳数 heart_rate = round(len(peaks) / used_window_seconds * 60, 1) return heart_rate # 调用示例 if __name__ == "__main__": hr = calculate_heart_rate("test_heartbeat.wav", window_seconds=15) print(f"计算得到的心率为:{hr} 次/分钟")
参数调整建议
- 如果你的音频噪音较多,可以适当调高
find_peaks里的height阈值,或者提升带通滤波的order参数 - 如果你需要从音频的中间位置截取窗口,修改
offset_samples为对应起始位置的样本数即可,比如从第5秒开始就是offset_samples = int(5 * sr) - 可以先使用matplotlib绘制滤波前后的波形和峰值位置,方便调整参数,示例代码如下:
import matplotlib.pyplot as plt # 绘制波形和峰值示例 plt.plot(y_filtered) plt.plot(peaks, y_filtered[peaks], "x") plt.show()
内容的提问来源于stack exchange,提问作者user7260398
相关产品推荐
相关产品推荐

