From b03959e45cd987499c18b8dcc496cdf40b0ea8c8 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 7 May 2026 12:59:16 -0500 Subject: [PATCH] fix double event publishes for stationary objects with attributes --- frigate/track/tracked_object.py | 7 +++++- frigate/video/detect.py | 40 ++++++++++++++++----------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index 4fda92afd0..a041e5802d 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -330,7 +330,12 @@ class TrackedObject: if self.obj_data["position_changes"] != obj_data["position_changes"]: significant_change = True - if self.obj_data["attributes"] != obj_data["attributes"]: + # disappearance of a per-frame attribute can be caused by detection + # skipping the object on a frame (stationary objects on non-interval + # frames), so only flag when a new attribute label appears + prev_labels = {a["label"] for a in self.obj_data["attributes"]} + curr_labels = {a["label"] for a in obj_data["attributes"]} + if curr_labels - prev_labels: significant_change = True # if the state changed between stationary and active diff --git a/frigate/video/detect.py b/frigate/video/detect.py index 339b11e534..89124a75de 100644 --- a/frigate/video/detect.py +++ b/frigate/video/detect.py @@ -438,34 +438,32 @@ def process_frames( else: object_tracker.update_frame_times(frame_name, frame_time) - # group the attribute detections based on what label they apply to - attribute_detections: dict[str, list[TrackedObjectAttribute]] = {} - for label, attribute_labels in attributes_map.items(): - attribute_detections[label] = [ - TrackedObjectAttribute(d) - for d in consolidated_detections - if d[0] in attribute_labels - ] - # build detections detections = {} for obj in object_tracker.tracked_objects.values(): detections[obj["id"]] = {**obj, "attributes": []} - # find the best object for each attribute to be assigned to + # assign each detected attribute to the best matching object. + # iterate consolidated_detections once so attributes that appear under + # multiple parent labels in attributes_map (e.g. license_plate is in + # both "car" and "motorcycle") are not appended more than once all_objects: list[dict[str, Any]] = object_tracker.tracked_objects.values() - for attributes in attribute_detections.values(): - for attribute in attributes: - filtered_objects = filter( - lambda o: attribute.label in attributes_map.get(o["label"], []), - all_objects, - ) - selected_object_id = attribute.find_best_object(filtered_objects) + detected_attributes = [ + TrackedObjectAttribute(d) + for d in consolidated_detections + if d[0] in all_attributes + ] + for attribute in detected_attributes: + filtered_objects = filter( + lambda o: attribute.label in attributes_map.get(o["label"], []), + all_objects, + ) + selected_object_id = attribute.find_best_object(filtered_objects) - if selected_object_id is not None: - detections[selected_object_id]["attributes"].append( - attribute.get_tracking_data() - ) + if selected_object_id is not None: + detections[selected_object_id]["attributes"].append( + attribute.get_tracking_data() + ) # debug object tracking if False: