From 15bca564f25ba58d760433786df0662ca7525c2a Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 25 Feb 2026 08:20:47 -0600 Subject: [PATCH] lpr dynamic config --- frigate/data_processing/post/license_plate.py | 6 +++++ .../real_time/license_plate.py | 6 +++++ frigate/embeddings/maintainer.py | 27 +++++++++++++++++++ .../config-form/section-configs/lpr.ts | 16 +---------- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/frigate/data_processing/post/license_plate.py b/frigate/data_processing/post/license_plate.py index e95cf234e..9f8d72975 100644 --- a/frigate/data_processing/post/license_plate.py +++ b/frigate/data_processing/post/license_plate.py @@ -12,6 +12,7 @@ from frigate.comms.embeddings_updater import EmbeddingsRequestEnum from frigate.comms.event_metadata_updater import EventMetadataPublisher from frigate.comms.inter_process import InterProcessRequestor from frigate.config import FrigateConfig +from frigate.config.classification import LicensePlateRecognitionConfig from frigate.data_processing.common.license_plate.mixin import ( WRITE_DEBUG_IMAGES, LicensePlateProcessingMixin, @@ -47,6 +48,11 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi): self.sub_label_publisher = sub_label_publisher super().__init__(config, metrics, model_runner) + def update_config(self, lpr_config: LicensePlateRecognitionConfig) -> None: + """Update LPR config at runtime.""" + self.lpr_config = lpr_config + logger.debug("LPR config updated dynamically") + def process_data( self, data: dict[str, Any], data_type: PostProcessDataEnum ) -> None: diff --git a/frigate/data_processing/real_time/license_plate.py b/frigate/data_processing/real_time/license_plate.py index 59c625de2..5c098369f 100644 --- a/frigate/data_processing/real_time/license_plate.py +++ b/frigate/data_processing/real_time/license_plate.py @@ -8,6 +8,7 @@ import numpy as np from frigate.comms.event_metadata_updater import EventMetadataPublisher from frigate.comms.inter_process import InterProcessRequestor from frigate.config import FrigateConfig +from frigate.config.classification import LicensePlateRecognitionConfig from frigate.data_processing.common.license_plate.mixin import ( LicensePlateProcessingMixin, ) @@ -40,6 +41,11 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess self.camera_current_cars: dict[str, list[str]] = {} super().__init__(config, metrics) + def update_config(self, lpr_config: LicensePlateRecognitionConfig) -> None: + """Update LPR config at runtime.""" + self.lpr_config = lpr_config + logger.debug("LPR config updated dynamically") + def process_frame( self, obj_data: dict[str, Any], diff --git a/frigate/embeddings/maintainer.py b/frigate/embeddings/maintainer.py index 74b5310ac..816b751fb 100644 --- a/frigate/embeddings/maintainer.py +++ b/frigate/embeddings/maintainer.py @@ -102,6 +102,7 @@ class EmbeddingMaintainer(threading.Thread): self.face_recognition_config_subscriber = ConfigSubscriber( "config/face_recognition", exact=True ) + self.lpr_config_subscriber = ConfigSubscriber("config/lpr", exact=True) # Configure Frigate DB db = SqliteVecQueueDatabase( @@ -277,6 +278,7 @@ class EmbeddingMaintainer(threading.Thread): self.config_updater.check_for_updates() self._check_classification_config_updates() self._check_face_recognition_config_updates() + self._check_lpr_config_updates() self._process_requests() self._process_updates() self._process_recordings_updates() @@ -289,6 +291,7 @@ class EmbeddingMaintainer(threading.Thread): self.config_updater.stop() self.classification_config_subscriber.stop() self.face_recognition_config_subscriber.stop() + self.lpr_config_subscriber.stop() self.event_subscriber.stop() self.event_end_subscriber.stop() self.recordings_subscriber.stop() @@ -381,6 +384,30 @@ class EmbeddingMaintainer(threading.Thread): logger.debug("Applied dynamic face recognition config update") + def _check_lpr_config_updates(self) -> None: + """Check for LPR config updates.""" + topic, lpr_config = self.lpr_config_subscriber.check_for_update() + + if topic is None: + return + + previous_min_area = self.config.lpr.min_area + self.config.lpr = lpr_config + + for camera_config in self.config.cameras.values(): + if camera_config.lpr.min_area == previous_min_area: + camera_config.lpr.min_area = lpr_config.min_area + + for processor in self.realtime_processors: + if isinstance(processor, LicensePlateRealTimeProcessor): + processor.update_config(lpr_config) + + for processor in self.post_processors: + if isinstance(processor, LicensePlatePostProcessor): + processor.update_config(lpr_config) + + logger.debug("Applied dynamic LPR config update") + def _process_requests(self) -> None: """Process embeddings requests""" diff --git a/web/src/components/config-form/section-configs/lpr.ts b/web/src/components/config-form/section-configs/lpr.ts index 3e2561f64..c5e16eb23 100644 --- a/web/src/components/config-form/section-configs/lpr.ts +++ b/web/src/components/config-form/section-configs/lpr.ts @@ -40,21 +40,7 @@ const lpr: SectionConfigOverrides = { "device", "replace_rules", ], - restartRequired: [ - "enabled", - "model_size", - "detection_threshold", - "min_area", - "recognition_threshold", - "min_plate_length", - "format", - "match_distance", - "known_plates", - "enhancement", - "debug_save_plates", - "device", - "replace_rules", - ], + restartRequired: ["model_size", "enhancement", "device"], uiSchema: { format: { "ui:options": { size: "md" },