diff --git a/frigate/data_processing/common/license_plate/mixin.py b/frigate/data_processing/common/license_plate/mixin.py index 1723d213e..aa03bc985 100644 --- a/frigate/data_processing/common/license_plate/mixin.py +++ b/frigate/data_processing/common/license_plate/mixin.py @@ -816,6 +816,20 @@ class LicensePlateProcessingMixin: # 5. Return True if we should keep the previous plate (i.e., if it scores higher) return prev_score > curr_score + def __update_yolov9_metrics(self, duration: float) -> None: + """ + Update inference metrics. + """ + self.metrics.yolov9_lpr_fps.value = ( + self.metrics.yolov9_lpr_fps.value * 9 + duration + ) / 10 + + def __update_lpr_metrics(self, duration: float) -> None: + """ + Update inference metrics. + """ + self.metrics.alpr_pps.value = (self.metrics.alpr_pps.value * 9 + duration) / 10 + def lpr_process(self, obj_data: dict[str, any], frame: np.ndarray): """Look for license plates in image.""" @@ -843,6 +857,7 @@ class LicensePlateProcessingMixin: if self.requires_license_plate_detection: logger.debug("Running manual license_plate detection.") + car_box = obj_data.get("box") if not car_box: @@ -867,6 +882,9 @@ class LicensePlateProcessingMixin: logger.debug( f"YOLOv9 LPD inference time: {(datetime.datetime.now().timestamp() - yolov9_start) * 1000:.2f} ms" ) + self.__update_yolov9_metrics( + datetime.datetime.now().timestamp() - yolov9_start + ) if not license_plate: logger.debug("Detected no license plates for car object.") @@ -945,11 +963,15 @@ class LicensePlateProcessingMixin: license_plate_frame, ) + start = datetime.datetime.now().timestamp() + # run detection, returns results sorted by confidence, best first license_plates, confidences, areas = self._process_license_plate( license_plate_frame ) + self.__update_lpr_metrics(datetime.datetime.now().timestamp() - start) + logger.debug(f"Text boxes: {license_plates}") logger.debug(f"Confidences: {confidences}") logger.debug(f"Areas: {areas}") diff --git a/frigate/data_processing/post/license_plate.py b/frigate/data_processing/post/license_plate.py index 9a9974bc7..2c80418c7 100644 --- a/frigate/data_processing/post/license_plate.py +++ b/frigate/data_processing/post/license_plate.py @@ -40,12 +40,6 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi): self.config = config super().__init__(config, metrics, model_runner) - def __update_metrics(self, duration: float) -> None: - """ - Update inference metrics. - """ - self.metrics.alpr_pps.value = (self.metrics.alpr_pps.value * 9 + duration) / 10 - def process_data( self, data: dict[str, any], data_type: PostProcessDataEnum ) -> None: @@ -57,8 +51,6 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi): Returns: None. """ - start = datetime.datetime.now().timestamp() - event_id = data["event_id"] camera_name = data["camera"] @@ -128,7 +120,10 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi): return if WRITE_DEBUG_IMAGES: - cv2.imwrite(f"debug/frames/lpr_post_{start}.jpg", image) + cv2.imwrite( + f"debug/frames/lpr_post_{datetime.datetime.now().timestamp()}.jpg", + image, + ) # convert to yuv for processing frame = cv2.cvtColor(image, cv2.COLOR_BGR2YUV_I420) @@ -210,8 +205,6 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi): logger.debug(f"Post processing plate: {event_id}, {frame_time}") self.lpr_process(keyframe_obj_data, frame) - self.__update_metrics(datetime.datetime.now().timestamp() - start) - def handle_request(self, topic, request_data) -> dict[str, any] | None: if topic == EmbeddingsRequestEnum.reprocess_plate.value: event = request_data["event"] diff --git a/frigate/data_processing/real_time/license_plate.py b/frigate/data_processing/real_time/license_plate.py index 2809e861f..c8f0efa11 100644 --- a/frigate/data_processing/real_time/license_plate.py +++ b/frigate/data_processing/real_time/license_plate.py @@ -1,6 +1,5 @@ """Handle processing images for face detection and recognition.""" -import datetime import logging import numpy as np @@ -33,17 +32,9 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess self.config = config super().__init__(config, metrics) - def __update_metrics(self, duration: float) -> None: - """ - Update inference metrics. - """ - self.metrics.alpr_pps.value = (self.metrics.alpr_pps.value * 9 + duration) / 10 - def process_frame(self, obj_data: dict[str, any], frame: np.ndarray): """Look for license plates in image.""" - start = datetime.datetime.now().timestamp() self.lpr_process(obj_data, frame) - self.__update_metrics(datetime.datetime.now().timestamp() - start) def handle_request(self, topic, request_data) -> dict[str, any] | None: return diff --git a/frigate/data_processing/types.py b/frigate/data_processing/types.py index 6f87f77f9..29abb22d1 100644 --- a/frigate/data_processing/types.py +++ b/frigate/data_processing/types.py @@ -10,12 +10,14 @@ class DataProcessorMetrics: text_embeddings_sps: Synchronized face_rec_fps: Synchronized alpr_pps: Synchronized + yolov9_lpr_fps: Synchronized def __init__(self): self.image_embeddings_fps = mp.Value("d", 0.01) self.text_embeddings_sps = mp.Value("d", 0.01) self.face_rec_fps = mp.Value("d", 0.01) self.alpr_pps = mp.Value("d", 0.01) + self.yolov9_lpr_fps = mp.Value("d", 0.01) class DataProcessorModelRunner: diff --git a/frigate/stats/util.py b/frigate/stats/util.py index 262cec3d2..3d836868e 100644 --- a/frigate/stats/util.py +++ b/frigate/stats/util.py @@ -302,6 +302,10 @@ def stats_snapshot( stats["embeddings"]["plate_recognition_speed"] = round( embeddings_metrics.alpr_pps.value * 1000, 2 ) + if "license_plate" not in config.objects.all_objects: + stats["embeddings"]["yolov9_plate_detection_speed"] = round( + embeddings_metrics.yolov9_lpr_fps.value * 1000, 2 + ) get_processing_stats(config, stats, hwaccel_errors)