1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
| import numpy as np from scipy import signal from scipy.fft import fft, fftfreq
class RemotePPGExtractor: """ 远程光电容积描记(rPPG)信号提取 基于面部视频提取心率信号 """ def __init__(self, fps=30): self.fps = fps self.face_detector = FaceDetector() self.landmark_detector = LandmarkDetector() self.ROI_REGIONS = { 'forehead': [10, 33], 'left_cheek': [2, 3, 4], 'right_cheek': [14, 15, 16] } def extract_heart_rate(self, video_frames): """ 从视频帧序列提取心率 Args: video_frames: 帧序列,shape=(N, H, W, 3) Returns: heart_rate: 心率(BPM) confidence: 置信度 signal_quality: 信号质量评分 """ aligned_faces = self._align_faces(video_frames) roi_signals = self._extract_roi_signals(aligned_faces) filtered_signals = self._filter_signals(roi_signals) rppg_signal = self._chrom_algorithm(filtered_signals) heart_rate, confidence = self._extract_heart_rate_from_spectrum( rppg_signal ) signal_quality = self._assess_signal_quality(rppg_signal) return heart_rate, confidence, signal_quality def _chrom_algorithm(self, rgb_signals): """ CHROM(Chrominance-based)算法 最先进的 rPPG 信号提取算法 原理: - 利用肤色在 RGB 空间的线性组合消除运动伪影 - 构建色度信号突出血液容积变化 Args: rgb_signals: RGB 信号,shape=(N, 3) Returns: rppg_signal: rPPG 信号,shape=(N,) """ rgb_norm = rgb_signals / np.mean(rgb_signals, axis=0) R = rgb_norm[:, 0] G = rgb_norm[:, 1] B = rgb_norm[:, 2] Xs = 3 * R - 2 * G Ys = 1.5 * R + G - 1.5 * B Xs = (Xs - np.mean(Xs)) / np.std(Xs) Ys = (Ys - np.mean(Ys)) / np.std(Ys) alpha = np.std(Xs) / np.std(Ys) rppg_signal = Xs - alpha * Ys b, a = signal.butter(4, [0.7, 4], btype='band', fs=self.fps) rppg_signal = signal.filtfilt(b, a, rppg_signal) return rppg_signal def _extract_heart_rate_from_spectrum(self, rppg_signal): """ 从频谱提取心率 Args: rppg_signal: rPPG 信号 Returns: heart_rate: 心率(BPM) confidence: 置信度 """ N = len(rppg_signal) yf = fft(rppg_signal) xf = fftfreq(N, 1/self.fps) pos_freqs = xf[:N//2] pos_powers = np.abs(yf[:N//2]) hr_range = (pos_freqs >= 0.67) & (pos_freqs <= 3.0) hr_freqs = pos_freqs[hr_range] hr_powers = pos_powers[hr_range] peak_idx = np.argmax(hr_powers) peak_freq = hr_freqs[peak_idx] heart_rate = peak_freq * 60.0 confidence = hr_powers[peak_idx] / np.mean(hr_powers) return heart_rate, confidence def _extract_roi_signals(self, aligned_faces): """ 从面部 ROI 提取 RGB 信号 Args: aligned_faces: 对齐后的面部图像序列 Returns: rgb_signals: RGB 平均信号,shape=(N, 3) """ rgb_signals = [] for face in aligned_faces: forehead_roi = self._extract_forehead_roi(face) mean_rgb = np.mean(forehead_roi, axis=(0, 1)) rgb_signals.append(mean_rgb) return np.array(rgb_signals)
|