Implement reset of tracked object update for each camera

This commit is contained in:
Nicolas Mowen 2025-05-09 12:46:26 -06:00
parent 6376fbe8cb
commit 119ec45c2f
6 changed files with 53 additions and 6 deletions

View File

@ -1552,6 +1552,12 @@ class LicensePlateProcessingMixin:
(base64.b64encode(encoded_img).decode("ASCII"), id, camera), (base64.b64encode(encoded_img).decode("ASCII"), id, camera),
) )
if id not in self.detected_license_plates:
if camera not in self.camera_current_cars:
self.camera_current_cars[camera] = []
self.camera_current_cars[camera].append(id)
self.detected_license_plates[id] = { self.detected_license_plates[id] = {
"plate": top_plate, "plate": top_plate,
"char_confidences": top_char_confidences, "char_confidences": top_char_confidences,
@ -1564,7 +1570,7 @@ class LicensePlateProcessingMixin:
def handle_request(self, topic, request_data) -> dict[str, any] | None: def handle_request(self, topic, request_data) -> dict[str, any] | None:
return return
def expire_object(self, object_id: str): def expire_object(self, object_id: str, camera: str):
if object_id in self.detected_license_plates: if object_id in self.detected_license_plates:
self.detected_license_plates.pop(object_id) self.detected_license_plates.pop(object_id)

View File

@ -50,10 +50,11 @@ class RealTimeProcessorApi(ABC):
pass pass
@abstractmethod @abstractmethod
def expire_object(self, object_id: str) -> None: def expire_object(self, object_id: str, camera: str) -> None:
"""Handle objects that are no longer detected. """Handle objects that are no longer detected.
Args: Args:
object_id (str): id of object that is no longer detected. object_id (str): id of object that is no longer detected.
camera (str): name of camera that object was detected on.
Returns: Returns:
None. None.

View File

@ -152,6 +152,6 @@ class BirdRealTimeProcessor(RealTimeProcessorApi):
def handle_request(self, topic, request_data): def handle_request(self, topic, request_data):
return None return None
def expire_object(self, object_id): def expire_object(self, object_id, camera):
if object_id in self.detected_birds: if object_id in self.detected_birds:
self.detected_birds.pop(object_id) self.detected_birds.pop(object_id)

View File

@ -54,6 +54,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
self.face_detector: cv2.FaceDetectorYN = None self.face_detector: cv2.FaceDetectorYN = None
self.requires_face_detection = "face" not in self.config.objects.all_objects self.requires_face_detection = "face" not in self.config.objects.all_objects
self.person_face_history: dict[str, list[tuple[str, float, int]]] = {} self.person_face_history: dict[str, list[tuple[str, float, int]]] = {}
self.camera_current_people: dict[str, list[str]] = {}
self.recognizer: FaceRecognizer | None = None self.recognizer: FaceRecognizer | None = None
self.faces_per_second = EventsPerSecond() self.faces_per_second = EventsPerSecond()
self.inference_speed = InferenceSpeed(self.metrics.face_rec_speed) self.inference_speed = InferenceSpeed(self.metrics.face_rec_speed)
@ -282,9 +283,13 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
if id not in self.person_face_history: if id not in self.person_face_history:
self.person_face_history[id] = [] self.person_face_history[id] = []
if camera not in self.camera_current_people:
self.camera_current_people[camera] = []
self.person_face_history[id].append( self.person_face_history[id].append(
(sub_label, score, face_frame.shape[0] * face_frame.shape[1]) (sub_label, score, face_frame.shape[0] * face_frame.shape[1])
) )
self.camera_current_people[camera].append(id)
(weighted_sub_label, weighted_score) = self.weighted_average( (weighted_sub_label, weighted_score) = self.weighted_average(
self.person_face_history[id] self.person_face_history[id]
) )
@ -420,10 +425,26 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
) )
shutil.move(current_file, new_file) shutil.move(current_file, new_file)
def expire_object(self, object_id: str): def expire_object(self, object_id: str, camera: str):
if object_id in self.person_face_history: if object_id in self.person_face_history:
self.person_face_history.pop(object_id) self.person_face_history.pop(object_id)
if object_id in self.camera_current_people.get(camera, []):
self.camera_current_people[camera].remove(object_id)
if len(self.camera_current_people[camera]) == 0:
self.requestor.send_data(
"tracked_object_update",
json.dumps(
{
"type": TrackedObjectUpdateTypesEnum.face,
"name": None,
"id": id,
"camera": camera,
}
),
)
def weighted_average( def weighted_average(
self, results_list: list[tuple[str, float, int]], max_weight: int = 4000 self, results_list: list[tuple[str, float, int]], max_weight: int = 4000
): ):

View File

@ -1,5 +1,6 @@
"""Handle processing images for face detection and recognition.""" """Handle processing images for face detection and recognition."""
import json
import logging import logging
import numpy as np import numpy as np
@ -13,6 +14,7 @@ from frigate.data_processing.common.license_plate.mixin import (
from frigate.data_processing.common.license_plate.model import ( from frigate.data_processing.common.license_plate.model import (
LicensePlateModelRunner, LicensePlateModelRunner,
) )
from frigate.types import TrackedObjectUpdateTypesEnum
from ..types import DataProcessorMetrics from ..types import DataProcessorMetrics
from .api import RealTimeProcessorApi from .api import RealTimeProcessorApi
@ -36,6 +38,7 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess
self.lpr_config = config.lpr self.lpr_config = config.lpr
self.config = config self.config = config
self.sub_label_publisher = sub_label_publisher self.sub_label_publisher = sub_label_publisher
self.camera_current_cars: dict[str, list[str]] = {}
super().__init__(config, metrics) super().__init__(config, metrics)
def process_frame( def process_frame(
@ -50,6 +53,22 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess
def handle_request(self, topic, request_data) -> dict[str, any] | None: def handle_request(self, topic, request_data) -> dict[str, any] | None:
return return
def expire_object(self, object_id: str): def expire_object(self, object_id: str, camera: str):
if object_id in self.detected_license_plates: if object_id in self.detected_license_plates:
self.detected_license_plates.pop(object_id) self.detected_license_plates.pop(object_id)
if object_id in self.camera_current_cars.get(camera, []):
self.camera_current_cars[camera].remove(object_id)
if len(self.camera_current_cars[camera]) == 0:
self.requestor.send_data(
"tracked_object_update",
json.dumps(
{
"type": TrackedObjectUpdateTypesEnum.face,
"name": None,
"id": id,
"camera": camera,
}
),
)

View File

@ -359,7 +359,7 @@ class EmbeddingMaintainer(threading.Thread):
# expire in realtime processors # expire in realtime processors
for processor in self.realtime_processors: for processor in self.realtime_processors:
processor.expire_object(event_id) processor.expire_object(event_id, camera)
if updated_db: if updated_db:
try: try: