From f9f8c4cceb087549fca1a786de403f555d14b09b Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 18 Sep 2025 07:51:03 -0500 Subject: [PATCH] implement replacement rules for cleaning up and normalizing plates --- frigate/config/classification.py | 11 ++++++++++ .../common/license_plate/mixin.py | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/frigate/config/classification.py b/frigate/config/classification.py index 63e89421c..98e0db046 100644 --- a/frigate/config/classification.py +++ b/frigate/config/classification.py @@ -217,6 +217,13 @@ class CameraFaceRecognitionConfig(FrigateBaseModel): model_config = ConfigDict(extra="forbid", protected_namespaces=()) +class ReplaceRule(FrigateBaseModel): + pattern: str = Field(..., title="Regex pattern to match.") + replacement: str = Field( + ..., title="Replacement string (supports backrefs like '\\1')." + ) + + class LicensePlateRecognitionConfig(FrigateBaseModel): enabled: bool = Field(default=False, title="Enable license plate recognition.") model_size: str = Field( @@ -269,6 +276,10 @@ class LicensePlateRecognitionConfig(FrigateBaseModel): title="The device key to use for LPR.", description="This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", ) + replace_rules: List[ReplaceRule] = Field( + default_factory=list, + title="List of regex replacement rules for normalizing detected plates. Each rule has 'pattern' and 'replacement'.", + ) class CameraLicensePlateRecognitionConfig(FrigateBaseModel): diff --git a/frigate/data_processing/common/license_plate/mixin.py b/frigate/data_processing/common/license_plate/mixin.py index 83019efc3..d2083afec 100644 --- a/frigate/data_processing/common/license_plate/mixin.py +++ b/frigate/data_processing/common/license_plate/mixin.py @@ -360,6 +360,27 @@ class LicensePlateProcessingMixin: conf for conf_list in qualifying_confidences for conf in conf_list ] + # Apply replace rules to combined_plate if configured + original_combined = combined_plate + if self.lpr_config.replace_rules: + for rule in self.lpr_config.replace_rules: + try: + pattern = getattr(rule, "pattern", "") + replacement = getattr(rule, "replacement", "") + if pattern: + combined_plate = re.sub( + pattern, replacement, combined_plate + ) + except re.error as e: + logger.warning( + f"{camera}: Invalid regex in replace_rules '{pattern}': {e}" + ) + + if combined_plate != original_combined: + logger.debug( + f"{camera}: Rules applied: '{original_combined}' -> '{combined_plate}'" + ) + # Compute the combined area for qualifying boxes qualifying_boxes = [boxes[i] for i in qualifying_indices] qualifying_plate_images = [