diff --git a/docs/docs/configuration/zones.md b/docs/docs/configuration/zones.md index 0edfea299..6570c78af 100644 --- a/docs/docs/configuration/zones.md +++ b/docs/docs/configuration/zones.md @@ -153,7 +153,7 @@ ui: unit_system: metric ``` -The maximum speed during the object's lifetime is saved in Frigate's database and can be seen in the UI in the Tracked Object Details pane in Explore. Current estimated speed can also be seen on the debug view as the third value in the object label. Current estimated speed, max estimated speed, and velocity angle (the angle of the direction the object is moving relative to the frame) of tracked objects is also sent through the `events` MQTT topic. See the [MQTT docs](../integrations/mqtt.md#frigateevents). +The average and maximum speed during the object's lifetime is saved in Frigate's database and can be seen in the UI in the Tracked Object Details pane in Explore. Current estimated speed can also be seen on the debug view as the third value in the object label. Current estimated speed, average estimated speed, max estimated speed, and velocity angle (the angle of the direction the object is moving relative to the frame) of tracked objects is also sent through the `events` MQTT topic. See the [MQTT docs](../integrations/mqtt.md#frigateevents). #### Best practices and caveats diff --git a/frigate/api/event.py b/frigate/api/event.py index d362a82bc..6c77d20fd 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -322,6 +322,7 @@ def events_explore(limit: int = 10): "top_score", "description", "sub_label_score", + "average_estimated_speed", "max_estimated_speed", ] }, @@ -595,6 +596,7 @@ def events_search(request: Request, params: EventsSearchQueryParams = Depends()) "top_score", "description", "sub_label_score", + "average_estimated_speed", "max_estimated_speed", ] } diff --git a/frigate/events/maintainer.py b/frigate/events/maintainer.py index 0dcfad509..a3d770f87 100644 --- a/frigate/events/maintainer.py +++ b/frigate/events/maintainer.py @@ -25,6 +25,8 @@ def should_update_db(prev_event: Event, current_event: Event) -> bool: or prev_event["entered_zones"] != current_event["entered_zones"] or prev_event["thumbnail"] != current_event["thumbnail"] or prev_event["end_time"] != current_event["end_time"] + or prev_event["average_estimated_speed"] + != current_event["average_estimated_speed"] or prev_event["max_estimated_speed"] != current_event["max_estimated_speed"] ): return True @@ -210,6 +212,7 @@ class EventProcessor(threading.Thread): "score": score, "top_score": event_data["top_score"], "attributes": attributes, + "average_estimated_speed": event_data["average_estimated_speed"], "max_estimated_speed": event_data["max_estimated_speed"], "type": "object", "max_severity": event_data.get("max_severity"), diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index e3bc62c5b..73ce8e541 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -62,7 +62,9 @@ class TrackedObject: self.frame = None self.active = True self.pending_loitering = False - self.estimated_speed = 0 + self.speed_history = [] + self.current_estimated_speed = 0 + self.average_estimated_speed = 0 self.max_estimated_speed = 0 self.velocity_angle = 0 self.previous = self.to_dict() @@ -197,16 +199,22 @@ class TrackedObject: ) if self.ui_config.unit_system == "metric": # Convert m/s to km/h - self.estimated_speed = speed_magnitude * 3.6 + self.current_estimated_speed = speed_magnitude * 3.6 elif self.ui_config.unit_system == "imperial": # Convert ft/s to mph - self.estimated_speed = speed_magnitude * 0.681818 + self.current_estimated_speed = speed_magnitude * 0.681818 + logger.debug( - f"Camera: {self.camera_config.name}, zone: {name}, tracked object ID: {self.obj_data['id']}, pixel velocity: {str(tuple(np.round(self.obj_data['estimate_velocity']).flatten().astype(int)))} estimated speed: {self.estimated_speed:.1f}" + f"Camera: {self.camera_config.name}, zone: {name}, tracked object ID: {self.obj_data['id']}, pixel velocity: {str(tuple(np.round(self.obj_data['estimate_velocity']).flatten().astype(int)))} estimated speed: {self.current_estimated_speed:.1f}" ) - if self.estimated_speed > self.max_estimated_speed: - self.max_estimated_speed = self.estimated_speed + self.speed_history.append(self.current_estimated_speed) + self.average_estimated_speed = sum(self.speed_history) / len( + self.speed_history + ) + + if self.current_estimated_speed > self.max_estimated_speed: + self.max_estimated_speed = self.current_estimated_speed # update loitering status self.pending_loitering = in_loitering_zone @@ -289,7 +297,8 @@ class TrackedObject: "current_attributes": self.obj_data["attributes"], "pending_loitering": self.pending_loitering, "max_severity": self.max_severity, - "estimated_speed": self.estimated_speed, + "current_estimated_speed": self.current_estimated_speed, + "average_estimated_speed": self.average_estimated_speed, "max_estimated_speed": self.max_estimated_speed, "velocity_angle": self.velocity_angle, } diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index dc03eef5a..6ad388b4d 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -316,6 +316,18 @@ function ObjectDetailsTab({ } }, [search]); + const averageEstimatedSpeed = useMemo(() => { + if (!search || !search.data?.average_estimated_speed) { + return undefined; + } + + if (search.data?.average_estimated_speed != 0) { + return search.data?.average_estimated_speed.toFixed(1); + } else { + return undefined; + } + }, [search]); + const maxEstimatedSpeed = useMemo(() => { if (!search || !search.data?.max_estimated_speed) { return undefined; @@ -439,12 +451,24 @@ function ObjectDetailsTab({ {score}%{subLabelScore && ` (${subLabelScore}%)`} - {maxEstimatedSpeed && ( + {(averageEstimatedSpeed || maxEstimatedSpeed) && (