diff --git a/docs/docs/configuration/license_plate_recognition.md b/docs/docs/configuration/license_plate_recognition.md index a602857d5..e75d0ce1d 100644 --- a/docs/docs/configuration/license_plate_recognition.md +++ b/docs/docs/configuration/license_plate_recognition.md @@ -184,7 +184,7 @@ cameras: ffmpeg: ... # add your streams detect: enabled: True - fps: 5 # increase to 10 if vehicles move quickly across your frame. Higher than 10 is unnecessary and is not recommended. + fps: 5 # increase to 10 if vehicles move quickly across your frame. Higher than 15 is unnecessary and is not recommended. min_initialized: 2 width: 1920 height: 1080 @@ -267,7 +267,7 @@ With this setup: - Review items will always be classified as a `detection`. - Snapshots will always be saved. - Zones and object masks are **not** used. -- The `frigate/events` MQTT topic will **not** publish tracked object updates, though `frigate/reviews` will if recordings are enabled. +- The `frigate/events` MQTT topic will **not** publish tracked object updates with the license plate bounding box and score, though `frigate/reviews` will publish if recordings are enabled. If a plate is recognized as a known plate, publishing will occur with an updated `sub_label` field. If characters are recognized, publishing will occur with an updated `recognized_license_plate` field. - License plate snapshots are saved at the highest-scoring moment and appear in Explore. - Debug view will not show `license_plate` bounding boxes. @@ -280,7 +280,7 @@ With this setup: | Object Detection | Standard Frigate+ detection applies | Bypasses standard object detection | | Zones & Object Masks | Supported | Not supported | | Debug View | May show `license_plate` bounding boxes | May **not** show `license_plate` bounding boxes | -| MQTT `frigate/events` | Publishes tracked object updates | Does **not** publish tracked object updates | +| MQTT `frigate/events` | Publishes tracked object updates | Publishes limited updates | | Explore | Recognized plates available in More Filters | Recognized plates available in More Filters | By selecting the appropriate configuration, users can optimize their dedicated LPR cameras based on whether they are using a Frigate+ model or the secondary LPR pipeline. diff --git a/frigate/config/config.py b/frigate/config/config.py index 7c0b669c8..373e08376 100644 --- a/frigate/config/config.py +++ b/frigate/config/config.py @@ -513,10 +513,14 @@ class FrigateConfig(FrigateBaseModel): ) # Warn if detect fps > 10 - if camera_config.detect.fps > 10: + if camera_config.detect.fps > 10 and camera_config.type != "lpr": logger.warning( f"{camera_config.name} detect fps is set to {camera_config.detect.fps}. This does NOT need to match your camera's frame rate. High values could lead to reduced performance. Recommended value is 5." ) + if camera_config.detect.fps > 15 and camera_config.type == "lpr": + logger.warning( + f"{camera_config.name} detect fps is set to {camera_config.detect.fps}. This does NOT need to match your camera's frame rate. High values could lead to reduced performance. Recommended value for LPR cameras are between 5-15." + ) # Default min_initialized configuration min_initialized = int(camera_config.detect.fps / 2) diff --git a/frigate/data_processing/common/license_plate/mixin.py b/frigate/data_processing/common/license_plate/mixin.py index 9ce702d61..afd5c8280 100644 --- a/frigate/data_processing/common/license_plate/mixin.py +++ b/frigate/data_processing/common/license_plate/mixin.py @@ -490,10 +490,6 @@ class LicensePlateProcessingMixin: merged_boxes.append(current_box) current_box = next_box - logger.debug( - f"Provided plate_width: {plate_width}, max_gap: {max_gap}, horizontal_gap: {horizontal_gap}" - ) - # Add the last box merged_boxes.append(current_box) @@ -1133,7 +1129,7 @@ class LicensePlateProcessingMixin: # 4. Log the comparison logger.debug( f"Plate comparison - Current: {top_plate} (score: {curr_score:.3f}, min_conf: {curr_min_conf:.2f}) vs " - f"Previous: {prev_plate} (score: {prev_score:.3f}, min_conf: {prev_min_conf:.2f})\n" + f"Previous: {prev_plate} (score: {prev_score:.3f}, min_conf: {prev_min_conf:.2f}) " f"Metrics - Length: {len(top_plate)} vs {len(prev_plate)} (scores: {curr_length_score:.2f} vs {prev_length_score:.2f}), " f"Area: {top_area} vs {prev_area}, " f"Avg Conf: {avg_confidence:.2f} vs {prev_avg_confidence:.2f}, " @@ -1263,6 +1259,15 @@ class LicensePlateProcessingMixin: ) return + # don't run for objects with no position changes + # this is the initial state after registering a new tracked object + # LPR will run 2 frames after detect.min_initialized is reached + if obj_data.get("position_changes", 0) == 0: + logger.debug( + f"{camera}: Plate detected in {self.config.cameras[camera].detect.min_initialized + 1} concurrent frames, LPR frame threshold ({self.config.cameras[camera].detect.min_initialized + 2})" + ) + return + license_plate: Optional[dict[str, any]] = None if "license_plate" not in self.config.cameras[camera].objects.track: @@ -1401,6 +1406,8 @@ class LicensePlateProcessingMixin: license_plate_frame, ) + logger.debug(f"{camera}: Running plate recognition.") + # run detection, returns results sorted by confidence, best first start = datetime.datetime.now().timestamp() license_plates, confidences, areas = self._process_license_plate( diff --git a/frigate/data_processing/post/license_plate.py b/frigate/data_processing/post/license_plate.py index c78e56b9d..e439fb763 100644 --- a/frigate/data_processing/post/license_plate.py +++ b/frigate/data_processing/post/license_plate.py @@ -54,6 +54,9 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi): Returns: None. """ + # don't run LPR post processing for now + return + event_id = data["event_id"] camera_name = data["camera"] diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index b1ac72a3f..f5511b7bc 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -138,11 +138,13 @@ class TrackedObject: if not self.false_positive and has_valid_frame: # determine if this frame is a better thumbnail - if self.thumbnail_data is None or is_better_thumbnail( - self.obj_data["label"], - self.thumbnail_data, - obj_data, - self.camera_config.frame_shape, + if self.thumbnail_data is None or ( + better_thumb := is_better_thumbnail( + self.obj_data["label"], + self.thumbnail_data, + obj_data, + self.camera_config.frame_shape, + ) ): # use the current frame time if the object's frame time isn't in the frame cache selected_frame_time = ( @@ -150,6 +152,13 @@ class TrackedObject: if obj_data["frame_time"] not in self.frame_cache.keys() else obj_data["frame_time"] ) + if ( + obj_data["frame_time"] not in self.frame_cache.keys() + and not better_thumb + ): + logger.warning( + f"Frame time {obj_data['frame_time']} not in frame cache, using current frame time {selected_frame_time}" + ) self.thumbnail_data = { "frame_time": selected_frame_time, "box": obj_data["box"],