Skip to content

Commit df6c3b1

Browse files
authored
Make stationary detection more resilient to inaccurate boxes (#10597)
1 parent e5595eb commit df6c3b1

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

frigate/track/norfair_tracker.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
from frigate.track import ObjectTracker
1818
from frigate.types import PTZMetricsTypes
1919
from frigate.util.image import intersection_over_union
20-
from frigate.util.object import average_boxes
20+
from frigate.util.object import average_boxes, median_of_boxes
2121

2222
logger = logging.getLogger(__name__)
2323

2424

25-
THRESHOLD_STATIONARY_IOU_AVERAGE = 0.6
25+
THRESHOLD_ACTIVE_IOU = 0.2
26+
THRESHOLD_STATIONARY_IOU = 0.6
2627
MAX_STATIONARY_HISTORY = 10
2728

2829

@@ -146,6 +147,7 @@ def deregister(self, id, track_id):
146147
# tracks the current position of the object based on the last N bounding boxes
147148
# returns False if the object has moved outside its previous position
148149
def update_position(self, id: str, box: list[int, int, int, int]):
150+
xmin, ymin, xmax, ymax = box
149151
position = self.positions[id]
150152
self.stationary_box_history[id].append(box)
151153

@@ -158,11 +160,9 @@ def update_position(self, id: str, box: list[int, int, int, int]):
158160
box, average_boxes(self.stationary_box_history[id])
159161
)
160162

161-
xmin, ymin, xmax, ymax = box
162-
163-
# if the iou drops below the threshold
164-
# assume the object has moved to a new position and reset the computed box
165-
if avg_iou < THRESHOLD_STATIONARY_IOU_AVERAGE:
163+
# object has minimal or zero iou
164+
# assume object is active
165+
if avg_iou < THRESHOLD_ACTIVE_IOU:
166166
self.positions[id] = {
167167
"xmins": [xmin],
168168
"ymins": [ymin],
@@ -175,6 +175,33 @@ def update_position(self, id: str, box: list[int, int, int, int]):
175175
}
176176
return False
177177

178+
# object has iou below threshold, check median to reduce outliers
179+
if avg_iou < THRESHOLD_STATIONARY_IOU:
180+
median_iou = intersection_over_union(
181+
(
182+
position["xmin"],
183+
position["ymin"],
184+
position["xmax"],
185+
position["ymax"],
186+
),
187+
median_of_boxes(self.stationary_box_history[id]),
188+
)
189+
190+
# if the median iou drops below the threshold
191+
# assume object is no longer stationary
192+
if median_iou < THRESHOLD_STATIONARY_IOU:
193+
self.positions[id] = {
194+
"xmins": [xmin],
195+
"ymins": [ymin],
196+
"xmaxs": [xmax],
197+
"ymaxs": [ymax],
198+
"xmin": xmin,
199+
"ymin": ymin,
200+
"xmax": xmax,
201+
"ymax": ymax,
202+
}
203+
return False
204+
178205
# if there are less than 10 entries for the position, add the bounding box
179206
# and recompute the position box
180207
if len(position["xmins"]) < 10:

frigate/util/object.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ def average_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int,
339339
return [np.mean(x_mins), np.mean(y_mins), np.mean(x_max), np.mean(y_max)]
340340

341341

342+
def median_of_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int, int]:
343+
"""Return a box that is the median of a list of boxes."""
344+
sorted_boxes = sorted(boxes, key=lambda x: area(x))
345+
return sorted_boxes[int(len(sorted_boxes) / 2.0)]
346+
347+
342348
def intersects_any(box_a, boxes):
343349
for box in boxes:
344350
if box_overlaps(box_a, box):

0 commit comments

Comments
 (0)