mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-26 13:47:03 +02:00
More LPR improvements (#16587)
* define a format option and adjust thresholds * config updates * docs * docs clarity
This commit is contained in:
parent
0b65137831
commit
9a07505075
@ -5,9 +5,11 @@ title: License Plate Recognition (LPR)
|
||||
|
||||
Frigate can recognize license plates on vehicles and automatically add the detected characters as a `sub_label` to objects that are of type `car`. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street with a dedicated LPR camera.
|
||||
|
||||
Users running a Frigate+ model should ensure that `license_plate` is added to the [list of objects to track](https://docs.frigate.video/plus/#available-label-types) either globally or for a specific camera. This will improve the accuracy and performance of the LPR model.
|
||||
Users running a Frigate+ model (or any custom model that natively detects license plates) should ensure that `license_plate` is added to the [list of objects to track](https://docs.frigate.video/plus/#available-label-types) either globally or for a specific camera. This will improve the accuracy and performance of the LPR model.
|
||||
|
||||
LPR is most effective when the vehicle’s license plate is fully visible to the camera. For moving vehicles, Frigate will attempt to read the plate continuously, refining its detection and keeping the most confident result. LPR will not run on stationary vehicles.
|
||||
Users without a model that detects license plates can still run LPR. A small, CPU inference, YOLOv9 license plate detection model will be used instead. You should _not_ define `license_plate` in your list of objects to track.
|
||||
|
||||
LPR is most effective when the vehicle’s license plate is fully visible to the camera. For moving vehicles, Frigate will attempt to read the plate continuously, refining recognition and keeping the most confident result. LPR will not run on stationary vehicles.
|
||||
|
||||
## Minimum System Requirements
|
||||
|
||||
@ -19,33 +21,68 @@ License plate recognition is disabled by default. Enable it in your config file:
|
||||
|
||||
```yaml
|
||||
lpr:
|
||||
enabled: true
|
||||
enabled: True
|
||||
```
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
Several options are available to fine-tune the LPR feature. For example, you can adjust the `min_area` setting, which defines the minimum size in pixels a license plate must be before LPR runs. The default is 1000 pixels.
|
||||
Fine-tune the LPR feature using these optional parameters:
|
||||
|
||||
The `min_plate_length` field specifies the minimum number of characters a license plate must have to be added to the object as a sub label.
|
||||
### Detection
|
||||
|
||||
If you want to allow a number of number of missing/incorrect characters to still cause a detected plate to match a known plate, set the `match_distance` field. For example, setting `match_distance` to 1 would cause a detected plate of ABCDE to match ABCBE or ABCD.
|
||||
- **`detection_threshold`**: License plate object detection confidence score required before recognition runs.
|
||||
- Default: `0.7`
|
||||
- Note: If you are using a Frigate+ model and you set the `threshold` in your objects config for `license_plate` higher than this value, recognition will never run. It's best to ensure these values match, or this `detection_threshold` is lower than your object config `threshold`.
|
||||
- **`min_area`**: Defines the minimum size (in pixels) a license plate must be before recognition runs.
|
||||
- Default: `1000` pixels.
|
||||
- Depending on the resolution of your cameras, you can increase this value to ignore small or distant plates.
|
||||
|
||||
Additionally, you can define `known_plates` as strings or regular expressions, allowing Frigate to label tracked vehicles with custom sub_labels when a recognized plate is detected. This information is then accessible in the UI, filters, and notifications.
|
||||
### Recognition
|
||||
|
||||
- **`recognition_threshold`**: Recognition confidence score required to add the plate to the object as a sub label.
|
||||
- Default: `0.9`.
|
||||
- **`min_plate_length`**: Specifies the minimum number of characters a detected license plate must have to be added as a sub-label to an object.
|
||||
- Use this to filter out short, incomplete, or incorrect detections.
|
||||
- **`format`**: A regular expression defining the expected format of detected plates. Plates that do not match this format will be discarded.
|
||||
- `"^[A-Z]{1,3} [A-Z]{1,2} [0-9]{1,4}$"` matches plates like "B AB 1234" or "M X 7"
|
||||
- `"^[A-Z]{2}[0-9]{2} [A-Z]{3}$"` matches plates like "AB12 XYZ" or "XY68 ABC"
|
||||
|
||||
### Matching
|
||||
|
||||
- **`known_plates`**: List of strings or regular expressions that assign custom a `sub_label` to `car` objects when a recognized plate matches a known value.
|
||||
- These labels appear in the UI, filters, and notifications.
|
||||
- **`match_distance`**: Allows for minor variations (missing/incorrect characters) when matching a detected plate to a known plate.
|
||||
- For example, setting `match_distance: 1` allows a plate `ABCDE` to match `ABCBE` or `ABCD`.
|
||||
- This parameter will not operate on known plates that are defined as regular expressions. You should define the full string of your plate in `known_plates` in order to use `match_distance`.
|
||||
|
||||
### Examples
|
||||
|
||||
```yaml
|
||||
lpr:
|
||||
enabled: true
|
||||
min_area: 1500
|
||||
min_plate_length: 4
|
||||
match_distance: 1
|
||||
enabled: True
|
||||
min_area: 1500 # Ignore plates smaller than 1500 pixels
|
||||
min_plate_length: 4 # Only recognize plates with 4 or more characters
|
||||
known_plates:
|
||||
Wife's Car:
|
||||
- "ABC-1234"
|
||||
- "ABC-I234"
|
||||
- "ABC-I234" # Accounts for potential confusion between the number one (1) and capital letter I
|
||||
Johnny:
|
||||
- "J*N-*234" # Using wildcards for H/M and 1/I
|
||||
- "J*N-*234" # Matches JHN-1234 and JMN-I234, but also note that "*" matches any number of characters
|
||||
Sally:
|
||||
- "[S5]LL-1234" # Matches SLL-1234 and 5LL-1234
|
||||
- "[S5]LL-1234" # Matches both SLL-1234 and 5LL-1234
|
||||
```
|
||||
|
||||
In this example, "Wife's Car" will appear as the label for any vehicle matching the plate "ABC-1234." The model might occasionally interpret the digit 1 as a capital I (e.g., "ABC-I234"), so both variations are listed. Similarly, multiple possible variations are specified for Johnny and Sally.
|
||||
```yaml
|
||||
lpr:
|
||||
enabled: True
|
||||
min_area: 4000 # Run recognition on larger plates only
|
||||
recognition_threshold: 0.85
|
||||
format: "^[A-Z]{3}-[0-9]{4}$" # Only recognize plates that are three letters, followed by a dash, followed by 4 numbers
|
||||
match_distance: 1 # Allow one character variation in plate matching
|
||||
known_plates:
|
||||
Delivery Van:
|
||||
- "RJK-5678"
|
||||
- "UPS-1234"
|
||||
Employee Parking:
|
||||
- "EMP-[0-9]{3}[A-Z]" # Matches plates like EMP-123A, EMP-456Z
|
||||
```
|
||||
|
@ -57,7 +57,7 @@ You can configure Frigate to allow manual selection of the stream you want to vi
|
||||
|
||||
Additionally, when creating and editing camera groups in the UI, you can choose the stream you want to use for your camera group's Live dashboard.
|
||||
|
||||
::: note
|
||||
:::note
|
||||
|
||||
Frigate's default dashboard ("All Cameras") will always use the first entry you've defined in `streams:` when playing live streams from your cameras.
|
||||
|
||||
|
@ -546,6 +546,25 @@ face_recognition:
|
||||
# NOTE: small model runs on CPU and large model runs on GPU
|
||||
model_size: "small"
|
||||
|
||||
# Optional: Configuration for license plate recognition capability
|
||||
lpr:
|
||||
# Optional: Enable license plate recognition (default: shown below)
|
||||
enabled: False
|
||||
# Optional: License plate object confidence score required to begin running recognition (default: shown below)
|
||||
detection_threshold: 0.7
|
||||
# Optional: Minimum area of license plate to begin running recognition (default: shown below)
|
||||
min_area: 1000
|
||||
# Optional: Recognition confidence score required to add the plate to the object as a sub label (default: shown below)
|
||||
recognition_threshold: 0.9
|
||||
# Optional: Minimum number of characters a license plate must have to be added to the object as a sub label (default: shown below)
|
||||
min_plate_length: 4
|
||||
# Optional: Regular expression for the expected format of a license plate (default: shown below)
|
||||
format: None
|
||||
# Optional: Allow this number of missing/incorrect characters to still cause a detected plate to match a known plate
|
||||
match_distance: 1
|
||||
# Optional: Known plates to track (strings or regular expressions) (default: shown below)
|
||||
known_plates: {}
|
||||
|
||||
# Optional: Configuration for AI generated tracked object descriptions
|
||||
# NOTE: Semantic Search must be enabled for this to do anything.
|
||||
# WARNING: Depending on the provider, this will send thumbnails over the internet
|
||||
|
@ -61,22 +61,35 @@ class FaceRecognitionConfig(FrigateBaseModel):
|
||||
|
||||
class LicensePlateRecognitionConfig(FrigateBaseModel):
|
||||
enabled: bool = Field(default=False, title="Enable license plate recognition.")
|
||||
threshold: float = Field(
|
||||
default=0.9,
|
||||
title="License plate confidence score required to be added to the object as a sub label.",
|
||||
detection_threshold: float = Field(
|
||||
default=0.7,
|
||||
title="License plate object confidence score required to begin running recognition.",
|
||||
gt=0.0,
|
||||
le=1.0,
|
||||
)
|
||||
min_area: int = Field(
|
||||
default=1000,
|
||||
title="Minimum area of license plate to consider running license plate recognition.",
|
||||
title="Minimum area of license plate to begin running recognition.",
|
||||
)
|
||||
recognition_threshold: float = Field(
|
||||
default=0.9,
|
||||
title="Recognition confidence score required to add the plate to the object as a sub label.",
|
||||
gt=0.0,
|
||||
le=1.0,
|
||||
)
|
||||
min_plate_length: int = Field(
|
||||
default=4,
|
||||
title="Minimum number of characters a license plate must have to be added to the object as a sub label.",
|
||||
)
|
||||
format: Optional[str] = Field(
|
||||
default=None,
|
||||
title="Regular expression for the expected format of license plate.",
|
||||
)
|
||||
match_distance: int = Field(
|
||||
default=1,
|
||||
title="Allow this number of missing/incorrect characters to still cause a detected plate to match a known plate.",
|
||||
ge=0,
|
||||
)
|
||||
known_plates: Optional[Dict[str, List[str]]] = Field(
|
||||
default={}, title="Known plates to track."
|
||||
default={}, title="Known plates to track (strings or regular expressions)."
|
||||
)
|
||||
|
@ -282,16 +282,27 @@ class LicensePlateProcessor(RealTimeProcessorApi):
|
||||
average_confidences[original_idx] = average_confidence
|
||||
areas[original_idx] = area
|
||||
|
||||
# Filter out plates that have a length of less than 3 characters
|
||||
# Filter out plates that have a length of less than min_plate_length characters
|
||||
# or that don't match the expected format (if defined)
|
||||
# Sort by area, then by plate length, then by confidence all desc
|
||||
sorted_data = sorted(
|
||||
[
|
||||
(plate, conf, area)
|
||||
for plate, conf, area in zip(
|
||||
license_plates, average_confidences, areas
|
||||
filtered_data = []
|
||||
for plate, conf, area in zip(license_plates, average_confidences, areas):
|
||||
if len(plate) < self.lpr_config.min_plate_length:
|
||||
logger.debug(
|
||||
f"Filtered out '{plate}' due to length ({len(plate)} < {self.lpr_config.min_plate_length})"
|
||||
)
|
||||
if len(plate) >= self.lpr_config.min_plate_length
|
||||
],
|
||||
continue
|
||||
|
||||
if self.lpr_config.format and not re.fullmatch(
|
||||
self.lpr_config.format, plate
|
||||
):
|
||||
logger.debug(f"Filtered out '{plate}' due to format mismatch")
|
||||
continue
|
||||
|
||||
filtered_data.append((plate, conf, area))
|
||||
|
||||
sorted_data = sorted(
|
||||
filtered_data,
|
||||
key=lambda x: (x[2], len(x[0]), x[1]),
|
||||
reverse=True,
|
||||
)
|
||||
@ -753,7 +764,7 @@ class LicensePlateProcessor(RealTimeProcessorApi):
|
||||
"""
|
||||
predictions = self.yolov9_detection_model(input)
|
||||
|
||||
confidence_threshold = self.lpr_config.threshold
|
||||
confidence_threshold = self.lpr_config.detection_threshold
|
||||
|
||||
top_score = -1
|
||||
top_box = None
|
||||
@ -968,6 +979,12 @@ class LicensePlateProcessor(RealTimeProcessorApi):
|
||||
if not license_plate:
|
||||
return
|
||||
|
||||
if license_plate.get("score") < self.lpr_config.detection_threshold:
|
||||
logger.debug(
|
||||
f"Plate detection score is less than the threshold ({license_plate['score']:0.2f} < {self.lpr_config.detection_threshold})"
|
||||
)
|
||||
return
|
||||
|
||||
license_plate_box = license_plate.get("box")
|
||||
|
||||
# check that license plate is valid
|
||||
@ -1043,7 +1060,7 @@ class LicensePlateProcessor(RealTimeProcessorApi):
|
||||
return
|
||||
|
||||
# Check against minimum confidence threshold
|
||||
if avg_confidence < self.lpr_config.threshold:
|
||||
if avg_confidence < self.lpr_config.recognition_threshold:
|
||||
logger.debug(
|
||||
f"Average confidence {avg_confidence} is less than threshold ({self.lpr_config.threshold})"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user