基于视频绿色通道提取心率的Python代码优化求助
优化基于视频绿色通道的心率提取代码
原代码存在几个核心问题导致心率计算不准确,下面给出针对性的优化方案:
原代码的关键问题
- 频率计算错误:
np.fft.fftfreq返回的是归一化频率(单位:周期/采样点),需要乘以**视频帧率(采样率)**才能得到实际物理频率(Hz),原代码直接用归一化频率判断范围,完全偏离实际心率对应的频率区间。 - 心率计算逻辑错误:FFT后的峰值对应信号的频率分量,不是时间域的心跳次数,原代码通过计数峰值个数计算心率的逻辑完全错误,正确方式是取幅度最高的有效频率,再转换为BPM(频率×60)。
- 缺少信号预处理:原始绿色通道信号包含大量噪声,直接FFT会产生无效峰值,影响结果准确性。
- 未过滤负频率:FFT结果是对称的,负频率部分没有实际意义,应该只保留正频率区间。
优化后的代码
import numpy as np from scipy.signal import find_peaks, savgol_filter class ExtractHeartRate: def __init__(self, green_channel, timer, fps): self.green_channel = green_channel # 时间序列的绿色通道数据(每帧的平均亮度值) self.timer = timer self.fps = fps # 视频帧率(每秒采样帧数,即采样率) self.heart_rate = 0 def calc_hr_process(self): # 1. 信号预处理:去均值 + 平滑降噪 signal = self.green_channel - np.mean(self.green_channel) # 去除直流分量 signal = savgol_filter(signal, window_length=5, polyorder=2) # 平滑滤波,减少噪声 # 2. 执行FFT并处理结果 n = len(signal) fft_result = np.fft.fft(signal) fft_amplitude = np.abs(fft_result)[:n//2] # 只保留正频率部分的幅度 fft_freq = np.fft.fftfreq(n, d=1/self.fps)[:n//2] # 计算实际物理频率(Hz) # 3. 定位有效频率范围内的峰值 # 对应心率范围:48~120BPM → 0.8~2Hz(BPM = 频率×60) freq_mask = (fft_freq >= 0.8) & (fft_freq <= 2) filtered_amp = fft_amplitude[freq_mask] filtered_freq = fft_freq[freq_mask] if len(filtered_amp) == 0: self.heart_rate = 0 return self.heart_rate # 4. 找到幅度最高的峰值(对应最强的心率频率分量) peaks, _ = find_peaks(filtered_amp, height=np.max(filtered_amp)*0.5) # 只保留幅度超过最大值50%的峰值 if not peaks.size: self.heart_rate = 0 return self.heart_rate # 取幅度最大的峰值对应的频率 max_peak_idx = peaks[np.argmax(filtered_amp[peaks])] dominant_freq = filtered_freq[max_peak_idx] # 5. 转换为BPM self.heart_rate = round(dominant_freq * 60) # 确保结果在合理范围内 self.heart_rate = max(40, min(self.heart_rate, 180)) return self.heart_rate
优化说明
- 信号预处理:通过去均值去除直流分量,用Savitzky-Golay滤波平滑信号,有效降低噪声对FFT的干扰。
- 正确的频率计算:通过
fps将归一化频率转换为实际Hz,确保频率范围判断符合真实心率区间。 - 峰值选择逻辑:只保留正频率部分,筛选出对应48~120BPM的频率区间,然后取幅度最高的峰值作为心率对应的频率分量,避免无效噪声峰值的干扰。
- 结果修正:最后将心率限制在40~180BPM的合理范围内,避免极端异常值。
内容的提问来源于stack exchange,提问作者daniel
相关产品推荐
相关产品推荐

