From 8b651bec5a2a411cf8d70d5650581728fc329fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=93=E6=99=BA=E8=88=AA?= <23373333@buaa.edu.cn> Date: Tue, 27 Jan 2026 00:09:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BF=83=E7=8E=87=E9=83=A8?= =?UTF-8?q?=E5=88=86=EF=BC=8C=E5=87=8F=E5=B0=8F=E5=85=B6=E8=B4=9F=E8=8D=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reproject/HeartRateMonitor.py | 21 +++++++-- reproject/main.py | 85 +++++++++++++++++------------------ 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/reproject/HeartRateMonitor.py b/reproject/HeartRateMonitor.py index ab30e39..2fec1c5 100644 --- a/reproject/HeartRateMonitor.py +++ b/reproject/HeartRateMonitor.py @@ -14,11 +14,17 @@ class HeartRateMonitor: self.b_buffer = collections.deque(maxlen=window_size) # 滤波器状态 - self.bp_b, self.bp_a = self._create_bandpass_filter(0.75, 2.5, fps) # 45-150 BPM + # 修改: 将最低频率从 0.75(45 BPM) 提高到 0.9(54 BPM) 以过滤低频噪声 + self.bp_b, self.bp_a = self._create_bandpass_filter(0.9, 2.5, fps) # 54-150 BPM # 平滑结果用的 self.bpm_history = collections.deque(maxlen=10) + # 优化: 降频计算 + self.frame_counter = 0 + self.process_interval = 15 # 每15帧(约0.5s)计算一次,其他时间收集数据 + self.last_bpm = None + def _create_bandpass_filter(self, lowcut, highcut, fs, order=5): """创建巴特沃斯带通滤波器""" nyq = 0.5 * fs @@ -92,6 +98,12 @@ class HeartRateMonitor: progress = int(len(self.r_buffer) / self.buffer_size * 100) return None # 或者返回 progress 表示进度 + # [优化] 降频计算策略 + # 我们每帧都需要收集数据(Buffer append),但不需要每帧都做 FFT + self.frame_counter += 1 + if self.frame_counter % self.process_interval != 0: + return self.last_bpm + # --- 3. 信号处理 (核心升级部分) --- r = np.array(self.r_buffer) g = np.array(self.g_buffer) @@ -119,13 +131,13 @@ class HeartRateMonitor: mag = np.abs(fft_res) # D. 寻找峰值 - # 限制频率范围 (45 BPM - 180 BPM) - interest_idx = np.where((freqs >= 0.75) & (freqs <= 3.0)) + # 限制频率范围 (54 BPM - 180 BPM) + interest_idx = np.where((freqs >= 0.9) & (freqs <= 3.0)) valid_freqs = freqs[interest_idx] valid_mags = mag[interest_idx] if len(valid_mags) == 0: - return None + return self.last_bpm max_idx = np.argmax(valid_mags) peak_freq = valid_freqs[max_idx] @@ -136,4 +148,5 @@ class HeartRateMonitor: self.bpm_history.append(bpm) avg_bpm = np.mean(self.bpm_history) + self.last_bpm = int(avg_bpm) return int(avg_bpm) \ No newline at end of file diff --git a/reproject/main.py b/reproject/main.py index d580aaf..cc0ce45 100644 --- a/reproject/main.py +++ b/reproject/main.py @@ -92,7 +92,7 @@ def capture_thread(): """ 采集线程:优化了分发逻辑,对视频流进行降频处理 """ - cap = cv2.VideoCapture(1) + cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) @@ -149,7 +149,7 @@ def analysis_thread(): status = 0 # 0:open 1:close last_time = time.time() last_freq = 0 - # heart_monitor = HeartRateMonitor() + heart_monitor = HeartRateMonitor() while not stop_event.is_set(): try: frame = frame_queue.get(timeout=1) @@ -220,10 +220,10 @@ def analysis_thread(): last_time = time.time() result["eye_close_freq"] = last_freq payload["eye_close_freq"] = last_freq - # bpm = heart_monitor.process_frame(frame, result["landmark"]) - # if bpm != None: - # result["heart_rate_bpm"] = bpm - # payload["heart_rate_bpm"] = bpm + bpm = heart_monitor.process_frame(frame, result["landmark"]) + if bpm != None: + result["heart_rate_bpm"] = bpm + payload["heart_rate_bpm"] = bpm if data_queue.full(): try: _ = data_queue.get_nowait() @@ -263,15 +263,12 @@ def video_stream_thread(): # f"filesink location={filename} " # ) # out = cv2.VideoWriter(gst_pipeline, cv2.CAP_GSTREAMER, 0, fps, (width, height)) - # out1 = cv2.VideoWriter('output1.mp4', fourcc, 30.0, (1280, 720)) + out1 = cv2.VideoWriter('output1.mp4', fourcc, 30.0, (1280, 720)) # out2 = cv2.VideoWriter('output2.mp4', fourcc, 30.0, (1280, 720)) while not stop_event.is_set(): try: frame = video_queue.get(timeout=1) - # small_frame = cv2.resize(apply_soft_roi(frame), (1280, 720)) - # print("[Video] 获取一帧视频") server.provide_frame(frame) - # print("[Video] 已提供给 WebRTC 服务器") # out1.write(frame) # out2.write(frame) except queue.Empty: @@ -280,50 +277,50 @@ def video_stream_thread(): print(f"[Video] 发送错误: {e}") continue # while not stop_event.is_set(): - # try: - # with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - # s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + # try: + # with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + # s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - # s.connect((SERVER_HOST, SERVER_PORT)) - # print(f"[Video] 已连接") + # s.connect((SERVER_HOST, SERVER_PORT)) + # print(f"[Video] 已连接") - # camera_id_bytes = CAMERA_ID.encode("utf-8") + # camera_id_bytes = CAMERA_ID.encode("utf-8") - # while not stop_event.is_set(): - # try: - # frame = video_queue.get(timeout=1) + # while not stop_event.is_set(): + # try: + # frame = video_queue.get(timeout=1) - # small_frame = cv2.resize(apply_soft_roi(frame), (1280, 720)) + # small_frame = cv2.resize(apply_soft_roi(frame), (1280, 720)) - # ret, buffer = cv2.imencode( - # ".jpg", small_frame, [cv2.IMWRITE_JPEG_QUALITY, 50] - # ) + # ret, buffer = cv2.imencode( + # ".jpg", small_frame, [cv2.IMWRITE_JPEG_QUALITY, 50] + # ) - # if not ret: - # continue + # if not ret: + # continue - # frame_bytes = buffer.tobytes() - # header_id_len = len(camera_id_bytes).to_bytes(4, "big") - # header_frame_len = len(frame_bytes).to_bytes(4, "big") + # frame_bytes = buffer.tobytes() + # header_id_len = len(camera_id_bytes).to_bytes(4, "big") + # header_frame_len = len(frame_bytes).to_bytes(4, "big") - # packet = ( - # header_id_len - # + camera_id_bytes - # + header_frame_len - # + frame_bytes - # ) - # s.sendall(packet) + # packet = ( + # header_id_len + # + camera_id_bytes + # + header_frame_len + # + frame_bytes + # ) + # s.sendall(packet) - # except queue.Empty: - # continue - # except Exception as e: - # print(f"[Video] 发送断开: {e}") - # break + # except queue.Empty: + # continue + # except Exception as e: + # print(f"[Video] 发送断开: {e}") + # break - # except Exception as e: - # print(f"[Video] 重连中... {e}") - # time.sleep(3) - # out1.release() + # except Exception as e: + # print(f"[Video] 重连中... {e}") + # time.sleep(3) + out1.release() # out2.release() print("[Video] 线程结束")