Fix lpr metrics and add yolov9 plate detection metric (#16827)

This commit is contained in:
Josh Hawkins 2025-02-26 08:29:34 -06:00 committed by GitHub
parent 7eb3c87fa0
commit 447f26e1b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 32 additions and 20 deletions

View File

@ -816,6 +816,20 @@ class LicensePlateProcessingMixin:
# 5. Return True if we should keep the previous plate (i.e., if it scores higher) # 5. Return True if we should keep the previous plate (i.e., if it scores higher)
return prev_score > curr_score 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): def lpr_process(self, obj_data: dict[str, any], frame: np.ndarray):
"""Look for license plates in image.""" """Look for license plates in image."""
@ -843,6 +857,7 @@ class LicensePlateProcessingMixin:
if self.requires_license_plate_detection: if self.requires_license_plate_detection:
logger.debug("Running manual license_plate detection.") logger.debug("Running manual license_plate detection.")
car_box = obj_data.get("box") car_box = obj_data.get("box")
if not car_box: if not car_box:
@ -867,6 +882,9 @@ class LicensePlateProcessingMixin:
logger.debug( logger.debug(
f"YOLOv9 LPD inference time: {(datetime.datetime.now().timestamp() - yolov9_start) * 1000:.2f} ms" 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: if not license_plate:
logger.debug("Detected no license plates for car object.") logger.debug("Detected no license plates for car object.")
@ -945,11 +963,15 @@ class LicensePlateProcessingMixin:
license_plate_frame, license_plate_frame,
) )
start = datetime.datetime.now().timestamp()
# run detection, returns results sorted by confidence, best first # run detection, returns results sorted by confidence, best first
license_plates, confidences, areas = self._process_license_plate( license_plates, confidences, areas = self._process_license_plate(
license_plate_frame license_plate_frame
) )
self.__update_lpr_metrics(datetime.datetime.now().timestamp() - start)
logger.debug(f"Text boxes: {license_plates}") logger.debug(f"Text boxes: {license_plates}")
logger.debug(f"Confidences: {confidences}") logger.debug(f"Confidences: {confidences}")
logger.debug(f"Areas: {areas}") logger.debug(f"Areas: {areas}")

View File

@ -40,12 +40,6 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi):
self.config = config self.config = config
super().__init__(config, metrics, model_runner) 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( def process_data(
self, data: dict[str, any], data_type: PostProcessDataEnum self, data: dict[str, any], data_type: PostProcessDataEnum
) -> None: ) -> None:
@ -57,8 +51,6 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi):
Returns: Returns:
None. None.
""" """
start = datetime.datetime.now().timestamp()
event_id = data["event_id"] event_id = data["event_id"]
camera_name = data["camera"] camera_name = data["camera"]
@ -128,7 +120,10 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi):
return return
if WRITE_DEBUG_IMAGES: 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 # convert to yuv for processing
frame = cv2.cvtColor(image, cv2.COLOR_BGR2YUV_I420) 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}") logger.debug(f"Post processing plate: {event_id}, {frame_time}")
self.lpr_process(keyframe_obj_data, frame) 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: def handle_request(self, topic, request_data) -> dict[str, any] | None:
if topic == EmbeddingsRequestEnum.reprocess_plate.value: if topic == EmbeddingsRequestEnum.reprocess_plate.value:
event = request_data["event"] event = request_data["event"]

View File

@ -1,6 +1,5 @@
"""Handle processing images for face detection and recognition.""" """Handle processing images for face detection and recognition."""
import datetime
import logging import logging
import numpy as np import numpy as np
@ -33,17 +32,9 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess
self.config = config self.config = config
super().__init__(config, metrics) 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): def process_frame(self, obj_data: dict[str, any], frame: np.ndarray):
"""Look for license plates in image.""" """Look for license plates in image."""
start = datetime.datetime.now().timestamp()
self.lpr_process(obj_data, frame) 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: def handle_request(self, topic, request_data) -> dict[str, any] | None:
return return

View File

@ -10,12 +10,14 @@ class DataProcessorMetrics:
text_embeddings_sps: Synchronized text_embeddings_sps: Synchronized
face_rec_fps: Synchronized face_rec_fps: Synchronized
alpr_pps: Synchronized alpr_pps: Synchronized
yolov9_lpr_fps: Synchronized
def __init__(self): def __init__(self):
self.image_embeddings_fps = mp.Value("d", 0.01) self.image_embeddings_fps = mp.Value("d", 0.01)
self.text_embeddings_sps = mp.Value("d", 0.01) self.text_embeddings_sps = mp.Value("d", 0.01)
self.face_rec_fps = mp.Value("d", 0.01) self.face_rec_fps = mp.Value("d", 0.01)
self.alpr_pps = mp.Value("d", 0.01) self.alpr_pps = mp.Value("d", 0.01)
self.yolov9_lpr_fps = mp.Value("d", 0.01)
class DataProcessorModelRunner: class DataProcessorModelRunner:

View File

@ -302,6 +302,10 @@ def stats_snapshot(
stats["embeddings"]["plate_recognition_speed"] = round( stats["embeddings"]["plate_recognition_speed"] = round(
embeddings_metrics.alpr_pps.value * 1000, 2 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) get_processing_stats(config, stats, hwaccel_errors)