Make stationary detection more resilient to inaccurate boxes (#10597)

This commit is contained in:
Nicolas Mowen 2024-03-21 16:44:26 -06:00 committed by GitHub
parent e5595ebb2f
commit df6c3b14dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 40 additions and 7 deletions

View File

@ -17,12 +17,13 @@ from frigate.ptz.autotrack import PtzMotionEstimator
from frigate.track import ObjectTracker from frigate.track import ObjectTracker
from frigate.types import PTZMetricsTypes from frigate.types import PTZMetricsTypes
from frigate.util.image import intersection_over_union from frigate.util.image import intersection_over_union
from frigate.util.object import average_boxes from frigate.util.object import average_boxes, median_of_boxes
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
THRESHOLD_STATIONARY_IOU_AVERAGE = 0.6 THRESHOLD_ACTIVE_IOU = 0.2
THRESHOLD_STATIONARY_IOU = 0.6
MAX_STATIONARY_HISTORY = 10 MAX_STATIONARY_HISTORY = 10
@ -146,6 +147,7 @@ class NorfairTracker(ObjectTracker):
# tracks the current position of the object based on the last N bounding boxes # tracks the current position of the object based on the last N bounding boxes
# returns False if the object has moved outside its previous position # returns False if the object has moved outside its previous position
def update_position(self, id: str, box: list[int, int, int, int]): def update_position(self, id: str, box: list[int, int, int, int]):
xmin, ymin, xmax, ymax = box
position = self.positions[id] position = self.positions[id]
self.stationary_box_history[id].append(box) self.stationary_box_history[id].append(box)
@ -158,11 +160,36 @@ class NorfairTracker(ObjectTracker):
box, average_boxes(self.stationary_box_history[id]) box, average_boxes(self.stationary_box_history[id])
) )
xmin, ymin, xmax, ymax = box # object has minimal or zero iou
# assume object is active
if avg_iou < THRESHOLD_ACTIVE_IOU:
self.positions[id] = {
"xmins": [xmin],
"ymins": [ymin],
"xmaxs": [xmax],
"ymaxs": [ymax],
"xmin": xmin,
"ymin": ymin,
"xmax": xmax,
"ymax": ymax,
}
return False
# if the iou drops below the threshold # object has iou below threshold, check median to reduce outliers
# assume the object has moved to a new position and reset the computed box if avg_iou < THRESHOLD_STATIONARY_IOU:
if avg_iou < THRESHOLD_STATIONARY_IOU_AVERAGE: median_iou = intersection_over_union(
(
position["xmin"],
position["ymin"],
position["xmax"],
position["ymax"],
),
median_of_boxes(self.stationary_box_history[id]),
)
# if the median iou drops below the threshold
# assume object is no longer stationary
if median_iou < THRESHOLD_STATIONARY_IOU:
self.positions[id] = { self.positions[id] = {
"xmins": [xmin], "xmins": [xmin],
"ymins": [ymin], "ymins": [ymin],

View File

@ -339,6 +339,12 @@ def average_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int,
return [np.mean(x_mins), np.mean(y_mins), np.mean(x_max), np.mean(y_max)] return [np.mean(x_mins), np.mean(y_mins), np.mean(x_max), np.mean(y_max)]
def median_of_boxes(boxes: list[list[int, int, int, int]]) -> list[int, int, int, int]:
"""Return a box that is the median of a list of boxes."""
sorted_boxes = sorted(boxes, key=lambda x: area(x))
return sorted_boxes[int(len(sorted_boxes) / 2.0)]
def intersects_any(box_a, boxes): def intersects_any(box_a, boxes):
for box in boxes: for box in boxes:
if box_overlaps(box_a, box): if box_overlaps(box_a, box):