Skip to content

Commit

Permalink
Merge pull request #36 from hawkeye217/object-speed
Browse files Browse the repository at this point in the history
Object speed
  • Loading branch information
hawkeye217 authored Dec 20, 2024
2 parents ec56e92 + c708e3b commit 5221c75
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 21 deletions.
2 changes: 1 addition & 1 deletion docs/docs/configuration/zones.md
Original file line number Diff line number Diff line change
Expand Up @@ -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). These speed values are output as a number in miles per hour (mph) or kilometers per hour (kph), depending on how `unit_system` is configured in your `ui` config.

#### Best practices and caveats

Expand Down
21 changes: 19 additions & 2 deletions frigate/api/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,15 @@ def process_events():
k: v
for k, v in event.data.items()
if k
in ["type", "score", "top_score", "description", "sub_label_score"]
in [
"type",
"score",
"top_score",
"description",
"sub_label_score",
"average_estimated_speed",
"max_estimated_speed",
]
},
"event_count": label_counts[event.label],
}
Expand Down Expand Up @@ -581,7 +589,16 @@ def events_search(request: Request, params: EventsSearchQueryParams = Depends())
processed_event["data"] = {
k: v
for k, v in event["data"].items()
if k in ["type", "score", "top_score", "description"]
if k
in [
"type",
"score",
"top_score",
"description",
"sub_label_score",
"average_estimated_speed",
"max_estimated_speed",
]
}

if event["id"] in search_results:
Expand Down
3 changes: 3 additions & 0 deletions frigate/events/maintainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -211,6 +213,7 @@ def handle_object_detection(
"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"),
Expand Down
26 changes: 18 additions & 8 deletions frigate/track/tracked_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ def __init__(
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()
Expand Down Expand Up @@ -193,20 +195,27 @@ def update(self, current_frame_time: float, obj_data, has_valid_frame: bool):
self.camera_config.detect.fps,
)
if self.active
else 0
else (0, 0)
)
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
if self.active:
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
Expand Down Expand Up @@ -289,7 +298,8 @@ def to_dict(self, include_thumbnail: bool = False):
"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,
}
Expand Down
34 changes: 29 additions & 5 deletions web/src/components/overlay/detail/SearchDetailDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -439,12 +451,24 @@ function ObjectDetailsTab({
{score}%{subLabelScore && ` (${subLabelScore}%)`}
</div>
</div>
{maxEstimatedSpeed && (
{(averageEstimatedSpeed || maxEstimatedSpeed) && (
<div className="flex flex-col gap-1.5">
<div className="text-sm text-primary/40">Max Estimated Speed</div>
<div className="text-sm">
{maxEstimatedSpeed}{" "}
{config?.ui.unit_system == "imperial" ? "mph" : "kph"}
<div className="text-sm text-primary/40">Estimated Speeds</div>
<div className="flex flex-col space-y-0.5 text-sm">
{averageEstimatedSpeed && (
<div>
{averageEstimatedSpeed}{" "}
{config?.ui.unit_system == "imperial" ? "mph" : "kph"}{" "}
<span className="text-primary/40">(average)</span>
</div>
)}
{maxEstimatedSpeed && (
<div>
{maxEstimatedSpeed}{" "}
{config?.ui.unit_system == "imperial" ? "mph" : "kph"}{" "}
<span className="text-primary/40">(maximum)</span>
</div>
)}
</div>
</div>
)}
Expand Down
32 changes: 28 additions & 4 deletions web/src/components/settings/ZoneEditPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,13 @@ export default function ZoneEditPane({
name="lineA"
render={({ field }) => (
<FormItem>
<FormLabel>Line A distance</FormLabel>
<FormLabel>
Line A distance (
{config?.ui.unit_system == "imperial"
? "feet"
: "meters"}
)
</FormLabel>
<FormControl>
<Input
{...field}
Expand All @@ -612,7 +618,13 @@ export default function ZoneEditPane({
name="lineB"
render={({ field }) => (
<FormItem>
<FormLabel>Line B distance</FormLabel>
<FormLabel>
Line B distance (
{config?.ui.unit_system == "imperial"
? "feet"
: "meters"}
)
</FormLabel>
<FormControl>
<Input
{...field}
Expand All @@ -628,7 +640,13 @@ export default function ZoneEditPane({
name="lineC"
render={({ field }) => (
<FormItem>
<FormLabel>Line C distance</FormLabel>
<FormLabel>
Line C distance (
{config?.ui.unit_system == "imperial"
? "feet"
: "meters"}
)
</FormLabel>
<FormControl>
<Input
{...field}
Expand All @@ -644,7 +662,13 @@ export default function ZoneEditPane({
name="lineD"
render={({ field }) => (
<FormItem>
<FormLabel>Line D distance</FormLabel>
<FormLabel>
Line D distance (
{config?.ui.unit_system == "imperial"
? "feet"
: "meters"}
)
</FormLabel>
<FormControl>
<Input
{...field}
Expand Down
1 change: 1 addition & 0 deletions web/src/types/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type SearchResult = {
ratio: number;
type: "object" | "audio" | "manual";
description?: string;
average_estimated_speed: number;
max_estimated_speed: number;
};
};
Expand Down
3 changes: 2 additions & 1 deletion web/src/views/settings/MasksAndZonesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ export default function MasksAndZonesView({
scaledWidth,
scaledHeight,
),
distances: zoneData.distances.map((distance) => parseFloat(distance)),
distances:
zoneData.distances?.map((distance) => parseFloat(distance)) ?? [],
isFinished: true,
color: zoneData.color,
}),
Expand Down

0 comments on commit 5221c75

Please sign in to comment.