mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-06 19:07:20 +03:00
Dedicated LPR improvements (#17453)
* remove license plate from attributes for dedicated lpr cameras * ensure we always have a color * use frigate+ models with dedicated lpr cameras * docs * docs clarity * docs enrichments * use license_plate as object type
This commit is contained in:
@@ -88,7 +88,9 @@ class CameraState:
|
||||
thickness = 1
|
||||
else:
|
||||
thickness = 2
|
||||
color = self.config.model.colormap[obj["label"]]
|
||||
color = self.config.model.colormap.get(
|
||||
obj["label"], (255, 255, 255)
|
||||
)
|
||||
else:
|
||||
thickness = 1
|
||||
color = (255, 0, 0)
|
||||
@@ -110,7 +112,9 @@ class CameraState:
|
||||
and obj["frame_time"] == frame_time
|
||||
):
|
||||
thickness = 5
|
||||
color = self.config.model.colormap[obj["label"]]
|
||||
color = self.config.model.colormap.get(
|
||||
obj["label"], (255, 255, 255)
|
||||
)
|
||||
|
||||
# debug autotracking zooming - show the zoom factor box
|
||||
if (
|
||||
|
||||
@@ -21,7 +21,6 @@ from frigate.comms.event_metadata_updater import (
|
||||
EventMetadataPublisher,
|
||||
EventMetadataTypeEnum,
|
||||
)
|
||||
from frigate.config.camera.camera import CameraTypeEnum
|
||||
from frigate.const import CLIPS_DIR
|
||||
from frigate.embeddings.onnx.lpr_embedding import LPR_EMBEDDING_SIZE
|
||||
from frigate.util.builtin import EventsPerSecond
|
||||
@@ -972,7 +971,7 @@ class LicensePlateProcessingMixin:
|
||||
(
|
||||
now,
|
||||
camera,
|
||||
"car",
|
||||
"license_plate",
|
||||
event_id,
|
||||
True,
|
||||
plate_score,
|
||||
@@ -994,9 +993,7 @@ class LicensePlateProcessingMixin:
|
||||
if not self.config.cameras[camera].lpr.enabled:
|
||||
return
|
||||
|
||||
if not dedicated_lpr and self.config.cameras[camera].type == CameraTypeEnum.lpr:
|
||||
return
|
||||
|
||||
# dedicated LPR cam without frigate+
|
||||
if dedicated_lpr:
|
||||
id = "dedicated-lpr"
|
||||
|
||||
@@ -1050,8 +1047,11 @@ class LicensePlateProcessingMixin:
|
||||
else:
|
||||
id = obj_data["id"]
|
||||
|
||||
# don't run for non car objects
|
||||
if obj_data.get("label") != "car":
|
||||
# don't run for non car or non license plate (dedicated lpr with frigate+) objects
|
||||
if (
|
||||
obj_data.get("label") != "car"
|
||||
and obj_data.get("label") != "license_plate"
|
||||
):
|
||||
logger.debug(
|
||||
f"{camera}: Not a processing license plate for non car object."
|
||||
)
|
||||
@@ -1131,26 +1131,34 @@ class LicensePlateProcessingMixin:
|
||||
license_plate[0] : license_plate[2],
|
||||
]
|
||||
else:
|
||||
# don't run for object without attributes
|
||||
if not obj_data.get("current_attributes"):
|
||||
# don't run for object without attributes if this isn't dedicated lpr with frigate+
|
||||
if (
|
||||
not obj_data.get("current_attributes")
|
||||
and obj_data.get("label") != "license_plate"
|
||||
):
|
||||
logger.debug(f"{camera}: No attributes to parse.")
|
||||
return
|
||||
|
||||
attributes: list[dict[str, any]] = obj_data.get(
|
||||
"current_attributes", []
|
||||
)
|
||||
for attr in attributes:
|
||||
if attr.get("label") != "license_plate":
|
||||
continue
|
||||
if obj_data.get("label") == "car":
|
||||
attributes: list[dict[str, any]] = obj_data.get(
|
||||
"current_attributes", []
|
||||
)
|
||||
for attr in attributes:
|
||||
if attr.get("label") != "license_plate":
|
||||
continue
|
||||
|
||||
if license_plate is None or attr.get(
|
||||
"score", 0.0
|
||||
) > license_plate.get("score", 0.0):
|
||||
license_plate = attr
|
||||
if license_plate is None or attr.get(
|
||||
"score", 0.0
|
||||
) > license_plate.get("score", 0.0):
|
||||
license_plate = attr
|
||||
|
||||
# no license plates detected in this frame
|
||||
if not license_plate:
|
||||
return
|
||||
# no license plates detected in this frame
|
||||
if not license_plate:
|
||||
return
|
||||
|
||||
# we are using dedicated lpr with frigate+
|
||||
if obj_data.get("label") == "license_plate":
|
||||
license_plate = obj_data
|
||||
|
||||
license_plate_box = license_plate.get("box")
|
||||
|
||||
@@ -1160,7 +1168,9 @@ class LicensePlateProcessingMixin:
|
||||
or area(license_plate_box)
|
||||
< self.config.cameras[obj_data["camera"]].lpr.min_area
|
||||
):
|
||||
logger.debug(f"{camera}: Invalid license plate box {license_plate}")
|
||||
logger.debug(
|
||||
f"{camera}: Area for license plate box {area(license_plate_box)} is less than min_area {self.config.cameras[obj_data['camera']].lpr.min_area}"
|
||||
)
|
||||
return
|
||||
|
||||
license_plate_frame = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420)
|
||||
@@ -1239,8 +1249,11 @@ class LicensePlateProcessingMixin:
|
||||
)
|
||||
return
|
||||
|
||||
# For LPR cameras, match or assign plate ID using Jaro-Winkler distance
|
||||
if dedicated_lpr:
|
||||
# For dedicated LPR cameras, match or assign plate ID using Jaro-Winkler distance
|
||||
if (
|
||||
dedicated_lpr
|
||||
and "license_plate" not in self.config.cameras[camera].objects.track
|
||||
):
|
||||
plate_id = None
|
||||
|
||||
for existing_id, data in self.detected_license_plates.items():
|
||||
@@ -1306,8 +1319,11 @@ class LicensePlateProcessingMixin:
|
||||
(id, top_plate, avg_confidence),
|
||||
)
|
||||
|
||||
if dedicated_lpr:
|
||||
# save the best snapshot
|
||||
# save the best snapshot for dedicated lpr cams not using frigate+
|
||||
if (
|
||||
dedicated_lpr
|
||||
and "license_plate" not in self.config.cameras[camera].objects.track
|
||||
):
|
||||
logger.debug(
|
||||
f"{camera}: Writing snapshot for {id}, {top_plate}, {current_time}"
|
||||
)
|
||||
|
||||
@@ -457,7 +457,11 @@ class EmbeddingMaintainer(threading.Thread):
|
||||
|
||||
camera_config = self.config.cameras[camera]
|
||||
|
||||
if not camera_config.type == CameraTypeEnum.lpr:
|
||||
if (
|
||||
camera_config.type != CameraTypeEnum.lpr
|
||||
or "license_plate" in camera_config.objects.track
|
||||
):
|
||||
# we're not a dedicated lpr camera or we are one but we're using frigate+
|
||||
return
|
||||
|
||||
try:
|
||||
|
||||
@@ -442,7 +442,7 @@ class TrackedObject:
|
||||
|
||||
if bounding_box:
|
||||
thickness = 2
|
||||
color = self.colormap[self.obj_data["label"]]
|
||||
color = self.colormap.get(self.obj_data["label"], (255, 255, 255))
|
||||
|
||||
# draw the bounding boxes on the frame
|
||||
box = self.thumbnail_data["box"]
|
||||
|
||||
+30
-7
@@ -15,6 +15,7 @@ from frigate.camera import CameraMetrics, PTZMetrics
|
||||
from frigate.comms.config_updater import ConfigSubscriber
|
||||
from frigate.comms.inter_process import InterProcessRequestor
|
||||
from frigate.config import CameraConfig, DetectConfig, ModelConfig
|
||||
from frigate.config.camera.camera import CameraTypeEnum
|
||||
from frigate.const import (
|
||||
CACHE_DIR,
|
||||
CACHE_SEGMENT_FORMAT,
|
||||
@@ -519,6 +520,7 @@ def track_camera(
|
||||
frame_queue,
|
||||
frame_shape,
|
||||
model_config,
|
||||
config,
|
||||
config.detect,
|
||||
frame_manager,
|
||||
motion_detector,
|
||||
@@ -585,6 +587,7 @@ def process_frames(
|
||||
frame_queue: mp.Queue,
|
||||
frame_shape,
|
||||
model_config: ModelConfig,
|
||||
camera_config: CameraConfig,
|
||||
detect_config: DetectConfig,
|
||||
frame_manager: FrameManager,
|
||||
motion_detector: MotionDetector,
|
||||
@@ -612,6 +615,29 @@ def process_frames(
|
||||
|
||||
region_min_size = get_min_region_size(model_config)
|
||||
|
||||
attributes_map = model_config.attributes_map
|
||||
all_attributes = model_config.all_attributes
|
||||
|
||||
# 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
|
||||
|
||||
all_attributes = [
|
||||
attr for attr in model_config.all_attributes if attr != "license_plate"
|
||||
]
|
||||
|
||||
while not stop_event.is_set():
|
||||
_, updated_enabled_config = enabled_config_subscriber.check_for_update()
|
||||
|
||||
@@ -805,9 +831,7 @@ def process_frames(
|
||||
# if detection was run on this frame, consolidate
|
||||
if len(regions) > 0:
|
||||
tracked_detections = [
|
||||
d
|
||||
for d in consolidated_detections
|
||||
if d[0] not in model_config.all_attributes
|
||||
d for d in consolidated_detections if d[0] not in all_attributes
|
||||
]
|
||||
# now that we have refined our detections, we need to track objects
|
||||
object_tracker.match_and_update(
|
||||
@@ -819,7 +843,7 @@ def process_frames(
|
||||
|
||||
# group the attribute detections based on what label they apply to
|
||||
attribute_detections: dict[str, list[TrackedObjectAttribute]] = {}
|
||||
for label, attribute_labels in model_config.attributes_map.items():
|
||||
for label, attribute_labels in attributes_map.items():
|
||||
attribute_detections[label] = [
|
||||
TrackedObjectAttribute(d)
|
||||
for d in consolidated_detections
|
||||
@@ -836,8 +860,7 @@ def process_frames(
|
||||
for attributes in attribute_detections.values():
|
||||
for attribute in attributes:
|
||||
filtered_objects = filter(
|
||||
lambda o: attribute.label
|
||||
in model_config.attributes_map.get(o["label"], []),
|
||||
lambda o: attribute.label in attributes_map.get(o["label"], []),
|
||||
all_objects,
|
||||
)
|
||||
selected_object_id = attribute.find_best_object(filtered_objects)
|
||||
@@ -885,7 +908,7 @@ def process_frames(
|
||||
for obj in object_tracker.tracked_objects.values():
|
||||
if obj["frame_time"] == frame_time:
|
||||
thickness = 2
|
||||
color = model_config.colormap[obj["label"]]
|
||||
color = model_config.colormap.get(obj["label"], (255, 255, 255))
|
||||
else:
|
||||
thickness = 1
|
||||
color = (255, 0, 0)
|
||||
|
||||
Reference in New Issue
Block a user