diff --git a/frigate/api/event.py b/frigate/api/event.py index ebf8524ca..6857cbd3f 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -323,6 +323,7 @@ def events_explore(limit: int = 10): "description", "sub_label_score", "average_estimated_speed", + "velocity_angle", ] }, "event_count": label_counts[event.label], @@ -596,6 +597,7 @@ def events_search(request: Request, params: EventsSearchQueryParams = Depends()) "description", "sub_label_score", "average_estimated_speed", + "velocity_angle", ] } diff --git a/frigate/events/maintainer.py b/frigate/events/maintainer.py index 6573f5a81..44c86633f 100644 --- a/frigate/events/maintainer.py +++ b/frigate/events/maintainer.py @@ -27,6 +27,7 @@ def should_update_db(prev_event: Event, current_event: Event) -> bool: or prev_event["end_time"] != current_event["end_time"] or prev_event["average_estimated_speed"] != current_event["average_estimated_speed"] + or prev_event["velocity_angle"] != current_event["velocity_angle"] ): return True return False @@ -212,6 +213,7 @@ class EventProcessor(threading.Thread): "top_score": event_data["top_score"], "attributes": attributes, "average_estimated_speed": event_data["average_estimated_speed"], + "velocity_angle": event_data["velocity_angle"], "type": "object", "max_severity": event_data.get("max_severity"), }, diff --git a/frigate/track/tracked_object.py b/frigate/track/tracked_object.py index e88abbc9f..52e78fe95 100644 --- a/frigate/track/tracked_object.py +++ b/frigate/track/tracked_object.py @@ -138,6 +138,7 @@ class TrackedObject: "score": obj_data["score"], "attributes": obj_data["attributes"], "current_estimated_speed": self.current_estimated_speed, + "velocity_angle": self.velocity_angle, } thumb_update = True @@ -217,7 +218,7 @@ class TrackedObject: self.speed_history ) logger.debug( - f"Camera: {self.camera_config.name}, tracked object ID: {self.obj_data['id']}, zone: {name}, pixel velocity: {str(tuple(np.round(self.obj_data['estimate_velocity']).flatten().astype(int)))}, speed magnitude: {speed_magnitude}, estimated speed: {self.current_estimated_speed:.1f}, average speed: {self.average_estimated_speed:.1f}, length: {len(self.speed_history)}" + f"Camera: {self.camera_config.name}, tracked object ID: {self.obj_data['id']}, zone: {name}, pixel velocity: {str(tuple(np.round(self.obj_data['estimate_velocity']).flatten().astype(int)))}, speed magnitude: {speed_magnitude}, velocity angle: {self.velocity_angle}, estimated speed: {self.current_estimated_speed:.1f}, average speed: {self.average_estimated_speed:.1f}, length: {len(self.speed_history)}" ) # update loitering status diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index d10cc11e3..d8aa0a3a6 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -25,6 +25,7 @@ import { baseUrl } from "@/api/baseUrl"; import { cn } from "@/lib/utils"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { + FaArrowRight, FaCheckCircle, FaChevronDown, FaDownload, @@ -328,6 +329,18 @@ function ObjectDetailsTab({ } }, [search]); + const velocityAngle = useMemo(() => { + if (!search || !search.data?.velocity_angle) { + return undefined; + } + + if (search.data?.velocity_angle != 0) { + return search.data?.velocity_angle.toFixed(1); + } else { + return undefined; + } + }, [search]); + const updateDescription = useCallback(() => { if (!search) { return; @@ -441,13 +454,22 @@ function ObjectDetailsTab({ {averageEstimatedSpeed && (