@@ -466,13 +466,31 @@ def __init__(self, parent: Optional[QObject] = None) -> None:
466466 self ._input_size = None
467467 self ._last_detect = 0.0
468468 self ._last_load_attempt = 0.0
469+ self ._pending_frame = None
470+ self ._detect_timer : Optional [QTimer ] = None
471+ self ._detect_scheduled = False
472+ self ._detect_inflight = False
473+ self ._target_interval_s = 0.08
474+ self ._min_interval_s = 0.08
475+ self ._max_interval_s = 0.14
476+ self ._detect_interval_s = self ._target_interval_s
477+ self ._recent_detect_cost_s = self ._target_interval_s
469478
470479 self ._detector_state = None
471480 self ._cv2 = None
472481
473482 @Slot (bool )
474483 def set_enabled (self , enabled : bool ) -> None :
475484 self ._enabled = bool (enabled )
485+ if not self ._enabled :
486+ self ._pending_frame = None
487+ self ._detect_scheduled = False
488+ timer = self ._detect_timer
489+ if timer is not None and timer .isActive ():
490+ timer .stop ()
491+ return
492+ if self ._pending_frame is not None :
493+ self ._schedule_detect (0 )
476494
477495 @Slot (object )
478496 def set_model_filename (self , model_filename : object ) -> None :
@@ -560,23 +578,72 @@ def ensure_loaded(self) -> None:
560578
561579 @Slot (object )
562580 def process_frame (self , frame_bgr ) -> None :
581+ if frame_bgr is None :
582+ return
583+ self ._pending_frame = frame_bgr
584+ if self ._enabled :
585+ self ._schedule_detect (0 )
586+
587+ def _ensure_detect_timer (self ) -> QTimer :
588+ timer = self ._detect_timer
589+ if timer is not None :
590+ return timer
591+ timer = QTimer (self )
592+ timer .setSingleShot (True )
593+ timer .timeout .connect (self ._consume_pending_frame )
594+ self ._detect_timer = timer
595+ return timer
596+
597+ def _schedule_detect (self , delay_ms : int ) -> None :
563598 if not self ._enabled :
564599 return
565- if self ._cv2 is None :
600+ timer = self ._ensure_detect_timer ()
601+ delay = max (0 , int (delay_ms ))
602+ if timer .isActive ():
603+ try :
604+ remaining = int (timer .remainingTime ())
605+ except Exception :
606+ remaining = delay
607+ if remaining <= delay :
608+ return
609+ timer .stop ()
610+ self ._detect_scheduled = True
611+ timer .start (delay )
612+
613+ @Slot ()
614+ def _consume_pending_frame (self ) -> None :
615+ self ._detect_scheduled = False
616+ if not self ._enabled or self ._detect_inflight :
617+ return
618+
619+ frame = self ._pending_frame
620+ if frame is None :
566621 return
567622
568623 now = time .monotonic ()
569- if now - self ._last_detect < 0.05 :
624+ elapsed = now - self ._last_detect
625+ if self ._last_detect > 0.0 and elapsed < self ._detect_interval_s :
626+ wait_ms = int (max (1.0 , (self ._detect_interval_s - elapsed ) * 1000.0 ))
627+ self ._schedule_detect (wait_ms )
628+ return
629+
630+ self ._pending_frame = None
631+ self ._detect_inflight = True
632+ started_at = time .perf_counter ()
633+ emitted_error = False
634+ if not self ._enabled :
635+ self ._detect_inflight = False
570636 return
571- self ._last_detect = now
572637
573638 self .ensure_loaded ()
574639
575640 try :
641+ if self ._cv2 is None :
642+ return
576643 state = self ._detector_state
577644 if state is None :
578645 return
579- results = detect_faces_onnx (frame_bgr , detector_state = state )
646+ results = detect_faces_onnx (frame , detector_state = state )
580647 except Exception as exc :
581648 logger .exception ("人脸检测失败: {}" , exc )
582649 key = "detect_failed"
@@ -587,6 +654,28 @@ def process_frame(self, frame_bgr) -> None:
587654 ):
588655 key = "model_incompatible"
589656 self .error_occurred .emit (key , "Face detection failed" , msg )
657+ emitted_error = True
590658 return
659+ finally :
660+ cost_s = max (0.0 , time .perf_counter () - started_at )
661+ self ._recent_detect_cost_s = self ._recent_detect_cost_s * 0.7 + cost_s * 0.3
662+ if self ._recent_detect_cost_s > self ._detect_interval_s * 1.05 :
663+ desired = min (
664+ self ._max_interval_s ,
665+ max (0.10 , self ._recent_detect_cost_s * 1.2 ),
666+ )
667+ else :
668+ desired = max (
669+ self ._target_interval_s ,
670+ self ._detect_interval_s * 0.92 ,
671+ )
672+ self ._detect_interval_s = min (
673+ self ._max_interval_s ,
674+ max (self ._min_interval_s , desired ),
675+ )
676+ self ._last_detect = time .monotonic ()
677+ self ._detect_inflight = False
678+ if self ._pending_frame is not None and not emitted_error :
679+ self ._schedule_detect (0 )
591680
592681 self .faces_ready .emit (results )
0 commit comments