diff --git a/docs/docs/configuration/license_plate_recognition.md b/docs/docs/configuration/license_plate_recognition.md
index eae92a9815..2c7c8e4cbb 100644
--- a/docs/docs/configuration/license_plate_recognition.md
+++ b/docs/docs/configuration/license_plate_recognition.md
@@ -8,7 +8,7 @@ import TabItem from "@theme/TabItem";
import NavPath from "@site/src/components/NavPath";
import FaqItem from "@site/src/components/FaqItem";
-Frigate can recognize license plates on vehicles and automatically add the detected characters to the `recognized_license_plate` field or a [known](#matching) name as a `sub_label` to tracked objects of type `car` or `motorcycle`. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street.
+Frigate can recognize license plates on vehicles and automatically add the detected characters to the `recognized_license_plate` field or a [known](#matching) name as a `sub_label` to tracked objects of type `car`, `motorcycle`, `bus`, `truck`, `school_bus`, or `garbage_truck`, depending on which of those labels your model detects. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street.
LPR works best when the license plate is clearly visible to the camera. For moving vehicles, Frigate continuously refines the recognition process, keeping the most confident result. When a vehicle becomes stationary, LPR continues to run for a short time after to attempt recognition.
@@ -24,7 +24,7 @@ When a plate is recognized, the details are:
- Viewable in the Details pane in Review/History.
- Viewable in the Tracked Object Details pane in Explore (sub labels and recognized license plates).
- Filterable through the More Filters menu in Explore.
-- Published via the `frigate/events` MQTT topic as a `sub_label` ([known](#matching)) or `recognized_license_plate` (unknown) for the `car` or `motorcycle` tracked object.
+- Published via the `frigate/events` MQTT topic as a `sub_label` ([known](#matching)) or `recognized_license_plate` (unknown) for the vehicle tracked object.
- Published via the `frigate/tracked_object_update` MQTT topic with `name` (if [known](#matching)) and `plate`.
## Model Requirements
@@ -35,7 +35,7 @@ Users without a model that detects license plates can still run LPR. Frigate use
:::note
-In the default mode, Frigate's LPR needs to first detect a `car` or `motorcycle` before it can recognize a license plate. If you're using a dedicated LPR camera and have a zoomed-in view where a `car` or `motorcycle` will not be detected, you can still run LPR, but the configuration parameters will differ from the default mode. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section below.
+In the default mode, Frigate's LPR needs to first detect a vehicle before it can recognize a license plate. If you're using a dedicated LPR camera and have a zoomed-in view where a vehicle will not be detected, you can still run LPR, but the configuration parameters will differ from the default mode. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section below.
:::
@@ -86,7 +86,7 @@ cameras:
-For non-dedicated LPR cameras, ensure that your camera is configured to detect objects of type `car` or `motorcycle`, and that a car or motorcycle is actually being detected by Frigate. Otherwise, LPR will not run.
+For non-dedicated LPR cameras, ensure that your camera is configured to detect vehicle objects, and that a vehicle is actually being detected by Frigate. Otherwise, LPR will not run. The object types that can carry a plate are defined by your model's `attributes_map`, so if your model detects other vehicle labels, you can add them there.
Like the other real-time processors in Frigate, license plate recognition runs on the camera stream defined by the `detect` role in your config. To ensure optimal performance, select a suitable resolution for this stream in your camera's firmware that fits your specific scene and requirements.
@@ -158,7 +158,7 @@ lpr:
Navigate to .
-- **Known plates**: Assign custom `sub_label` values to `car` and `motorcycle` objects when a recognized plate matches a known value. These labels appear in the UI, filters, and notifications. Unknown plates are still saved but are added to the `recognized_license_plate` field rather than the `sub_label`.
+- **Known plates**: Assign custom `sub_label` values to vehicle objects when a recognized plate matches a known value. These labels appear in the UI, filters, and notifications. Unknown plates are still saved but are added to the `recognized_license_plate` field rather than the `sub_label`.
- **Match distance**: Allows for minor variations (missing/incorrect characters) when matching a detected plate to a known plate. For example, setting to `1` allows a plate `ABCDE` to match `ABCBE` or `ABCD`. This parameter will _not_ operate on known plates that are defined as regular expressions.
@@ -316,7 +316,7 @@ lpr:
:::note
-If a camera is configured to detect `car` or `motorcycle` but you don't want Frigate to run LPR for that camera, disable LPR at the camera level:
+If a camera is configured to detect vehicles but you don't want Frigate to run LPR for that camera, disable LPR at the camera level:
@@ -456,7 +456,7 @@ With this setup:
- Snapshots will have license plate bounding boxes on them.
- The `frigate/events` MQTT topic will publish tracked object updates.
- Debug view will display `license_plate` bounding boxes.
-- If you are using a Frigate+ model and want to submit images from your dedicated LPR camera for model training and fine-tuning, annotate both the `car` / `motorcycle` and the `license_plate` in the snapshots on the Frigate+ website, even if the car is barely visible.
+- If you are using a Frigate+ model and want to submit images from your dedicated LPR camera for model training and fine-tuning, annotate both the vehicle and the `license_plate` in the snapshots on the Frigate+ website, even if the vehicle is barely visible.
### Using the Secondary LPR Pipeline (Without Frigate+)
@@ -611,9 +611,9 @@ If you are still having issues detecting plates, start with a basic configuratio
-Can I run LPR without detecting car or motorcycle objects?>}>
+Can I run LPR without detecting vehicle objects?>}>
-In normal LPR mode, Frigate requires a `car` or `motorcycle` to be detected first before recognizing a license plate. If you have a dedicated LPR camera, you can change the camera `type` to `"lpr"` to use the Dedicated LPR Camera algorithm. This comes with important caveats, though. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section above.
+In normal LPR mode, Frigate requires a vehicle to be detected first before recognizing a license plate. If you have a dedicated LPR camera, you can change the camera `type` to `"lpr"` to use the Dedicated LPR Camera algorithm. This comes with important caveats, though. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section above.
@@ -699,7 +699,7 @@ lpr:
4. Ensure the characters on detected plates are being _recognized_.
- Check the **Plate recognition** inference time in Enrichment metrics (). High inference times (> 100ms) could lead to poor recognition results, especially for dedicated LPR cameras where the plate crosses the frame quickly.
- Enable `debug_save_plates` to save images of detected text on plates to the clips directory (`/media/frigate/clips/lpr`). Ensure these images are readable and the text is clear.
- - Watch the debug view to see plates recognized in real-time. For non-dedicated LPR cameras, the `car` or `motorcycle` label will change to the recognized plate when LPR is enabled and working.
+ - Watch the debug view to see plates recognized in real-time. For non-dedicated LPR cameras, the vehicle's label will change to the recognized plate when LPR is enabled and working.
- Adjust `recognition_threshold` settings per the suggestions [above](#advanced-configuration).
@@ -714,13 +714,13 @@ LPR's performance impact depends on your hardware. Ensure you have at least 4GB
The YOLOv9 license plate detector model will run (and the metric will appear) if you've enabled LPR but haven't defined `license_plate` as an object to track, either at the global or camera level.
-If you are detecting `car` or `motorcycle` on cameras where you don't want to run LPR, make sure you disable LPR it at the camera level. And if you do want to run LPR on those cameras, make sure you define `license_plate` as an object to track.
+If you are detecting vehicles on cameras where you don't want to run LPR, make sure you disable LPR it at the camera level. And if you do want to run LPR on those cameras, make sure you define `license_plate` as an object to track.
-This could happen if cars or motorcycles travel close to your camera's timestamp or overlay text. You could either move the text through your camera's firmware, or apply a mask to it in Frigate.
+This could happen if vehicles travel close to your camera's timestamp or overlay text. You could either move the text through your camera's firmware, or apply a mask to it in Frigate.
If you are using a model that natively detects `license_plate`, add an _object mask_ of type `license_plate` and a _motion mask_ over your text.
diff --git a/frigate/camera/state.py b/frigate/camera/state.py
index fed819154e..c94aa5654d 100644
--- a/frigate/camera/state.py
+++ b/frigate/camera/state.py
@@ -60,6 +60,11 @@ class CameraState:
# face/LPR pipelines when using a model without built-in detection.
self.face_recognition_min_obj_area: int = 0
self.lpr_min_obj_area: int = 0
+ self.lp_objects = {
+ label
+ for label, attributes in config.model.attributes_map.items()
+ if "license_plate" in attributes
+ }
if (
self.camera_config.face_recognition.enabled
@@ -452,7 +457,7 @@ class CameraState:
and obj_area >= self.face_recognition_min_obj_area
and updated_obj.obj_data.get("sub_label") is None
) or (
- obj_label in ("car", "motorcycle")
+ obj_label in self.lp_objects
and self.lpr_min_obj_area > 0
and obj_area >= self.lpr_min_obj_area
and updated_obj.obj_data.get("sub_label") is None
@@ -548,7 +553,7 @@ class CameraState:
current_best.thumbnail_data is not None
and obj.thumbnail_data is not None
and is_better_thumbnail(
- object_type,
+ obj.thumbnail_attributes,
current_best.thumbnail_data,
obj.thumbnail_data,
self.camera_config.frame_shape,
diff --git a/frigate/const.py b/frigate/const.py
index dac04c4f1a..5ca1b2d3f0 100644
--- a/frigate/const.py
+++ b/frigate/const.py
@@ -44,7 +44,11 @@ DEFAULT_ATTRIBUTE_LABEL_MAP = {
"ups",
"usps",
],
+ "truck": ["license_plate"],
+ "garbage_truck": ["license_plate"],
"motorcycle": ["license_plate"],
+ "bus": ["license_plate"],
+ "school_bus": ["license_plate"],
}
ATTRIBUTE_LABEL_DISPLAY_MAP = {
"amazon": "Amazon",
diff --git a/frigate/data_processing/common/license_plate/mixin.py b/frigate/data_processing/common/license_plate/mixin.py
index e61d7daa07..a7c42b9124 100644
--- a/frigate/data_processing/common/license_plate/mixin.py
+++ b/frigate/data_processing/common/license_plate/mixin.py
@@ -1290,7 +1290,7 @@ class LicensePlateProcessingMixin:
and obj_data.get("label") != "license_plate"
):
logger.debug(
- f"{camera}: Not a processing license plate for non car/motorcycle object."
+ f"{camera}: Not a processing license plate for {obj_data.get('label', 'unknown')}."
)
return
@@ -1367,7 +1367,7 @@ class LicensePlateProcessingMixin:
if not license_plate:
logger.debug(
- f"{camera}: Detected no license plates for car/motorcycle object."
+ f"{camera}: Detected no license plates for {obj_data.get('label', 'unknown')} object."
)
return
diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py
index 0a9c6f74f0..75234c3a87 100644
--- a/frigate/track/tracked_object.py
+++ b/frigate/track/tracked_object.py
@@ -54,6 +54,11 @@ class TrackedObject:
self.obj_data = obj_data
self.colormap = model_config.colormap
self.logos = model_config.all_attribute_logos
+ self.thumbnail_attributes = [
+ attr
+ for attr in model_config.attributes_map.get(obj_data["label"], [])
+ if attr in model_config.non_logo_attributes
+ ]
self.camera_config = camera_config
self.ui_config = ui_config
self.frame_cache = frame_cache
@@ -149,7 +154,7 @@ 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_attributes,
self.thumbnail_data,
obj_data,
self.camera_config.frame_shape,
diff --git a/frigate/util/image.py b/frigate/util/image.py
index dde25e2425..b403f9750e 100644
--- a/frigate/util/image.py
+++ b/frigate/util/image.py
@@ -67,7 +67,7 @@ def has_better_attr(current_thumb, new_obj, attr_label) -> bool:
def is_better_thumbnail(
- label: str,
+ label_attributes: list[str],
current_thumb: dict[str, Any],
new_obj: dict[str, Any],
frame_shape: tuple[int, int],
@@ -76,20 +76,12 @@ def is_better_thumbnail(
# cutoff images are less ideal, but they should also be smaller?
# better scores are obviously better too
- # check face on person
- if label == "person":
- if has_better_attr(current_thumb, new_obj, "face"):
+ for attr_label in label_attributes:
+ if has_better_attr(current_thumb, new_obj, attr_label):
return True
- # if the current thumb has a face attr, dont update unless it gets better
- if any([a["label"] == "face" for a in current_thumb["attributes"]]):
- return False
- # check license_plate on car
- if label in ["car", "motorcycle"]:
- if has_better_attr(current_thumb, new_obj, "license_plate"):
- return True
- # if the current thumb has a license_plate attr, dont update unless it gets better
- if any([a["label"] == "license_plate" for a in current_thumb["attributes"]]):
+ # if the current thumb has the attr, dont update unless it gets better
+ if any([a["label"] == attr_label for a in current_thumb["attributes"]]):
return False
# if the new_thumb is on an edge, and the current thumb is not
diff --git a/frigate/video/detect.py b/frigate/video/detect.py
index 6ca4aed548..2fca30debb 100644
--- a/frigate/video/detect.py
+++ b/frigate/video/detect.py
@@ -216,20 +216,10 @@ def process_frames(
# remove license_plate from attributes if this camera is a dedicated LPR cam
if camera_config.type == CameraTypeEnum.lpr:
- modified_attributes_map = model_config.attributes_map.copy()
-
- if (
- "car" in modified_attributes_map
- and "license_plate" in modified_attributes_map["car"]
- ):
- modified_attributes_map["car"] = [
- attr
- for attr in modified_attributes_map["car"]
- if attr != "license_plate"
- ]
-
- attributes_map = modified_attributes_map
-
+ attributes_map = {
+ label: [attr for attr in attributes if attr != "license_plate"]
+ for label, attributes in model_config.attributes_map.items()
+ }
all_attributes = [
attr for attr in model_config.all_attributes if attr != "license_plate"
]
diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json
index 233af9c9d1..91d3e06c81 100644
--- a/web/public/locales/en/views/settings.json
+++ b/web/public/locales/en/views/settings.json
@@ -1948,7 +1948,7 @@
},
"lpr": {
"globalDisabled": "The license plate recognition enrichment must be enabled for LPR features to function on this camera.",
- "vehicleNotTracked": "License plate recognition requires 'car' or 'motorcycle' to be tracked. Enable 'car' or 'motorcycle' in Objects for this camera.",
+ "vehicleNotTracked": "License plate recognition requires a vehicle to be tracked. Enable 'car' or another vehicle type in Objects for this camera.",
"modelSizeLarge": "The 'large' model is optimized for multi-line license plates. The 'small' model provides better performance over 'large' and should be used unless your region uses multi-line plate formats."
},
"record": {
diff --git a/web/src/components/config-form/section-configs/lpr.ts b/web/src/components/config-form/section-configs/lpr.ts
index 5d2a91179e..46781e610a 100644
--- a/web/src/components/config-form/section-configs/lpr.ts
+++ b/web/src/components/config-form/section-configs/lpr.ts
@@ -21,7 +21,12 @@ const lpr: SectionConfigOverrides = {
if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false;
if (ctx.fullCameraConfig.type === "lpr") return false;
const tracked = ctx.fullCameraConfig.objects?.track ?? [];
- return !tracked.some((o) => ["car", "motorcycle"].includes(o));
+ const vehicles = Object.entries(
+ ctx.fullConfig.model?.attributes_map ?? {},
+ )
+ .filter(([, attributes]) => attributes.includes("license_plate"))
+ .map(([label]) => label);
+ return !tracked.some((o) => vehicles.includes(o));
},
},
],