diff --git a/frigate/data_processing/common/license_plate/mixin.py b/frigate/data_processing/common/license_plate/mixin.py index b62239491..e94c1b564 100644 --- a/frigate/data_processing/common/license_plate/mixin.py +++ b/frigate/data_processing/common/license_plate/mixin.py @@ -1552,6 +1552,12 @@ class LicensePlateProcessingMixin: (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] = { "plate": top_plate, "char_confidences": top_char_confidences, @@ -1564,7 +1570,7 @@ class LicensePlateProcessingMixin: def handle_request(self, topic, request_data) -> dict[str, any] | None: 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: self.detected_license_plates.pop(object_id) diff --git a/frigate/data_processing/real_time/api.py b/frigate/data_processing/real_time/api.py index 1ba01d5da..3d8e735c1 100644 --- a/frigate/data_processing/real_time/api.py +++ b/frigate/data_processing/real_time/api.py @@ -50,10 +50,11 @@ class RealTimeProcessorApi(ABC): pass @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. Args: object_id (str): id of object that is no longer detected. + camera (str): name of camera that object was detected on. Returns: None. diff --git a/frigate/data_processing/real_time/bird.py b/frigate/data_processing/real_time/bird.py index ea181d6c3..7fd2a5edb 100644 --- a/frigate/data_processing/real_time/bird.py +++ b/frigate/data_processing/real_time/bird.py @@ -152,6 +152,6 @@ class BirdRealTimeProcessor(RealTimeProcessorApi): def handle_request(self, topic, request_data): return None - def expire_object(self, object_id): + def expire_object(self, object_id, camera): if object_id in self.detected_birds: self.detected_birds.pop(object_id) diff --git a/frigate/data_processing/real_time/face.py b/frigate/data_processing/real_time/face.py index a7e1a63ba..92845303b 100644 --- a/frigate/data_processing/real_time/face.py +++ b/frigate/data_processing/real_time/face.py @@ -54,6 +54,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi): self.face_detector: cv2.FaceDetectorYN = None self.requires_face_detection = "face" not in self.config.objects.all_objects 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.faces_per_second = EventsPerSecond() self.inference_speed = InferenceSpeed(self.metrics.face_rec_speed) @@ -282,9 +283,13 @@ class FaceRealTimeProcessor(RealTimeProcessorApi): if id not in self.person_face_history: self.person_face_history[id] = [] + if camera not in self.camera_current_people: + self.camera_current_people[camera] = [] + self.person_face_history[id].append( (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( self.person_face_history[id] ) @@ -420,10 +425,26 @@ class FaceRealTimeProcessor(RealTimeProcessorApi): ) 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: 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( self, results_list: list[tuple[str, float, int]], max_weight: int = 4000 ): diff --git a/frigate/data_processing/real_time/license_plate.py b/frigate/data_processing/real_time/license_plate.py index 864968073..677398d82 100644 --- a/frigate/data_processing/real_time/license_plate.py +++ b/frigate/data_processing/real_time/license_plate.py @@ -1,5 +1,6 @@ """Handle processing images for face detection and recognition.""" +import json import logging 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 ( LicensePlateModelRunner, ) +from frigate.types import TrackedObjectUpdateTypesEnum from ..types import DataProcessorMetrics from .api import RealTimeProcessorApi @@ -36,6 +38,7 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess self.lpr_config = config.lpr self.config = config self.sub_label_publisher = sub_label_publisher + self.camera_current_cars: dict[str, list[str]] = {} super().__init__(config, metrics) def process_frame( @@ -50,6 +53,22 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess def handle_request(self, topic, request_data) -> dict[str, any] | None: 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: 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, + } + ), + ) diff --git a/frigate/embeddings/maintainer.py b/frigate/embeddings/maintainer.py index f62a5476b..487d5dbc4 100644 --- a/frigate/embeddings/maintainer.py +++ b/frigate/embeddings/maintainer.py @@ -359,7 +359,7 @@ class EmbeddingMaintainer(threading.Thread): # expire in realtime processors for processor in self.realtime_processors: - processor.expire_object(event_id) + processor.expire_object(event_id, camera) if updated_db: try: