diff --git a/config/algorithm_config.yaml b/config/algorithm_config.yaml index b87cb3b..393fc51 100644 --- a/config/algorithm_config.yaml +++ b/config/algorithm_config.yaml @@ -85,3 +85,14 @@ logging: # 调试图像保存路径 debug_image_path: "debug/" + +# 性能保护配置 +performance: + # 处理帧率上限(每秒最多处理多少帧),0 或 null 表示不限制 + max_processing_fps: 0 + + # 帧间隔,1 表示每帧处理,2 表示处理 1 帧跳过 1 帧 + frame_stride: 1 + + # 检测时间间隔(秒),0 表示每帧都检测 + detection_interval_seconds: 0.1 # 每秒检测10次 diff --git a/main.py b/main.py index ebc6c95..7b734d6 100644 --- a/main.py +++ b/main.py @@ -77,6 +77,7 @@ class YantaiVisionXSystem: self.alarm_manager = AlarmManager(self.logger) self.tech_ui = TechUI() self.performance_limiter = PerformanceLimiter() + self._detection_interval_seconds: float = 0.0 self._window_internal_name = "YantaiVisionX Monitor" self._window_display_name = "烟台蓬莱国际机场低能见度识别软件" @@ -118,16 +119,24 @@ class YantaiVisionXSystem: max_fps = performance_cfg.get("max_processing_fps") frame_stride = performance_cfg.get("frame_stride", 1) + detection_interval = performance_cfg.get("detection_interval_seconds", 0) self.performance_limiter.update_settings(max_processing_fps=max_fps, frame_stride=frame_stride) + try: + detection_interval_value = float(detection_interval) + except (TypeError, ValueError): + detection_interval_value = 0.0 + self._detection_interval_seconds = detection_interval_value if detection_interval_value and detection_interval_value > 0 else 0.0 self.logger.log_info( f"性能限制器配置: max_fps={self.performance_limiter.max_processing_fps}, " - f"frame_stride={self.performance_limiter.frame_stride}" + f"frame_stride={self.performance_limiter.frame_stride}, " + f"detection_interval={self._detection_interval_seconds}s" ) def update_performance_settings( self, max_processing_fps: Optional[float] = None, frame_stride: Optional[int] = None, + detection_interval_seconds: Optional[float] = None, ) -> None: """运行中动态调整性能限制参数""" @@ -135,6 +144,12 @@ class YantaiVisionXSystem: max_processing_fps=max_processing_fps, frame_stride=frame_stride, ) + if detection_interval_seconds is not None: + try: + value = float(detection_interval_seconds) + except (TypeError, ValueError): + value = 0.0 + self._detection_interval_seconds = value if value > 0 else 0.0 def initialize_system(self, camera_config=None) -> bool: """ @@ -210,10 +225,24 @@ class YantaiVisionXSystem: self.logger.log_error("摄像头打开失败") return - self.logger.log_info("开始 LED 检测...") + # 获取视频/摄像头帧率,用于控制播放速度 + video_fps = self.camera.get_fps() + if video_fps <= 0: + video_fps = 30.0 + frame_interval = 1.0 / video_fps # 每帧间隔(秒) + + self.logger.log_info(f"开始 LED 检测... 帧率: {video_fps:.1f}fps") limiter = self.performance_limiter frame_index = 0 + last_detection_result = None + last_detection_time: Optional[float] = None + display_frame_interval = 5 # 每5帧显示一次,降低UI绑定开销 + + # 性能统计 + loop_start = time.perf_counter() + total_display_time = 0.0 + display_count = 0 try: while self.is_running: @@ -225,45 +254,77 @@ class YantaiVisionXSystem: current_frame_index = frame_index frame_index += 1 + current_time = time.perf_counter() + + # 每100帧输出一次性能统计 + if frame_index % 100 == 0: + elapsed = current_time - loop_start + actual_fps = frame_index / elapsed if elapsed > 0 else 0 + avg_display = (total_display_time / display_count * 1000) if display_count > 0 else 0 + print(f"[性能] 帧:{frame_index} 实际FPS:{actual_fps:.1f} 显示次数:{display_count} 平均显示耗时:{avg_display:.1f}ms") + + should_process = True if limiter and not limiter.should_process_frame(current_frame_index): - continue - - # 图像预处理 - enhanced_frame = self.image_enhancer.preprocess_frame( - frame, mode="normal" - ) - - # LED检测 - detection_result = self.led_detector.detect_leds(enhanced_frame) - - # 记录结果 - self.logger.log_detection_result(detection_result) + should_process = False + if last_detection_result is None: + should_process = True - alarm_result = self.alarm_manager.process_detection(detection_result) - for event in alarm_result.events: - self.logger.log_alarm_event(event) - for roi_name in alarm_result.recoveries: - self.logger.log_recovery_event( - roi_name, - detection_result.frame_count, - detection_result.timestamp + if should_process and self._detection_interval_seconds > 0: + now = time.perf_counter() + if ( + last_detection_time is not None + and (now - last_detection_time) < self._detection_interval_seconds + ): + should_process = False + else: + last_detection_time = now + elif should_process: + last_detection_time = time.perf_counter() + + detection_result = None + if should_process: + # 图像预处理 + enhanced_frame = self.image_enhancer.preprocess_frame( + frame, mode="normal" ) - - # 保存结果 - if ( - save_results - and self.logger.save_results_enabled - and detection_result.frame_count % self.logger.save_results_interval == 0 - ): - self.logger.save_result_to_file(detection_result) - - # 显示结果 - if self.display_enabled: - self._display_results(frame, detection_result) - - if limiter: - limiter.enforce_rate_limit() + # LED检测 + detection_result = self.led_detector.detect_leds(enhanced_frame) + last_detection_result = detection_result + + # 记录结果 + self.logger.log_detection_result(detection_result) + + alarm_result = self.alarm_manager.process_detection(detection_result) + for event in alarm_result.events: + self.logger.log_alarm_event(event) + for roi_name in alarm_result.recoveries: + self.logger.log_recovery_event( + roi_name, + detection_result.frame_count, + detection_result.timestamp + ) + + # 保存结果 + if ( + save_results + and self.logger.save_results_enabled + and detection_result.frame_count % self.logger.save_results_interval == 0 + ): + self.logger.save_result_to_file(detection_result) + + display_detection_result = last_detection_result if last_detection_result is not None else detection_result + + # 控制显示帧率:每N帧显示一次,避免UI绑定拖慢速度 + should_display = (current_frame_index % display_frame_interval == 0) + + # 显示结果 + if self.display_enabled and display_detection_result is not None and should_display: + t0 = time.perf_counter() + self._display_results(frame, display_detection_result) + total_display_time += time.perf_counter() - t0 + display_count += 1 + # 检查退出条件 if cv2.waitKey(1) & 0xFF == ord('q'): break @@ -310,14 +371,23 @@ class YantaiVisionXSystem: results = [] if self.logger.save_results_enabled else None last_frame = None last_detection_result = None + last_detection_time: Optional[float] = None + display_frame_interval = 5 # 每5帧显示一次,降低UI绑定开销 self.logger.log_info(f"开始处理视频: {video_path}") limiter = self.performance_limiter frame_index = 0 + + # 性能统计 + loop_start = time.perf_counter() + total_display_time = 0.0 + display_count = 0 try: while self.is_running: + current_time = time.perf_counter() + success, frame = self.camera.read_frame() if not success: if self.display_enabled and last_frame is not None: @@ -326,46 +396,78 @@ class YantaiVisionXSystem: current_frame_index = frame_index frame_index += 1 + + # 每100帧输出一次性能统计 + if frame_index % 100 == 0: + elapsed = current_time - loop_start + actual_fps = frame_index / elapsed if elapsed > 0 else 0 + avg_display = (total_display_time / display_count * 1000) if display_count > 0 else 0 + print(f"[性能] 帧:{frame_index} 实际FPS:{actual_fps:.1f} 显示次数:{display_count} 平均显示耗时:{avg_display:.1f}ms") + + should_process = True if limiter and not limiter.should_process_frame(current_frame_index): - continue - - # 图像预处理 - enhanced_frame = self.image_enhancer.preprocess_frame( - frame, mode="normal" - ) - - # LED检测 - detection_result = self.led_detector.detect_leds(enhanced_frame) - if results is not None: - results.append(detection_result) + should_process = False + if last_detection_result is None: + should_process = True + + if should_process and self._detection_interval_seconds > 0: + now = time.perf_counter() + if ( + last_detection_time is not None + and (now - last_detection_time) < self._detection_interval_seconds + ): + should_process = False + else: + last_detection_time = now + elif should_process: + last_detection_time = time.perf_counter() + + detection_result = None + if should_process: + # 图像预处理 + enhanced_frame = self.image_enhancer.preprocess_frame( + frame, mode="normal" + ) + + # LED检测 + detection_result = self.led_detector.detect_leds(enhanced_frame) + last_detection_result = detection_result + + if results is not None: + results.append(detection_result) + + # 记录结果 + if detection_result.frame_count % 100 == 0: + self.logger.log_detection_result(detection_result) + + alarm_result = self.alarm_manager.process_detection(detection_result) + for event in alarm_result.events: + self.logger.log_alarm_event(event) + for roi_name in alarm_result.recoveries: + self.logger.log_recovery_event( + roi_name, + detection_result.frame_count, + detection_result.timestamp + ) + if self.display_enabled and frame is not None: last_frame = frame.copy() else: last_frame = None - last_detection_result = detection_result - - # 记录结果 - if detection_result.frame_count % 100 == 0: - self.logger.log_detection_result(detection_result) - alarm_result = self.alarm_manager.process_detection(detection_result) - for event in alarm_result.events: - self.logger.log_alarm_event(event) - for roi_name in alarm_result.recoveries: - self.logger.log_recovery_event( - roi_name, - detection_result.frame_count, - detection_result.timestamp - ) - + display_detection_result = last_detection_result if last_detection_result is not None else detection_result + + # 控制显示帧率:每N帧显示一次,避免UI绑定拖慢速度 + should_display = (current_frame_index % display_frame_interval == 0) + # 显示结果 - if self.display_enabled: - self._display_results(frame, detection_result) + if self.display_enabled and display_detection_result is not None and should_display: + t0 = time.perf_counter() + self._display_results(frame, display_detection_result) + total_display_time += time.perf_counter() - t0 + display_count += 1 if cv2.waitKey(1) & 0xFF == ord('q'): break - - if limiter: - limiter.enforce_rate_limit() # 保存批量结果 if results: