Autotracking tweaks (#8400)

* optimize motion and velocity estimation

* change recommended fps and fix config validate

* remove unneeded var

* process at most 3 objects per second

* fix test
This commit is contained in:
Josh Hawkins
2023-11-01 06:12:43 -05:00
committed by GitHub
parent d1620b4e39
commit af24eb7dbf
6 changed files with 39 additions and 40 deletions

View File

@@ -171,7 +171,7 @@ class PtzAutotrackConfig(FrigateBaseModel):
timeout: int = Field(
default=10, title="Seconds to delay before returning to preset."
)
movement_weights: Optional[Union[float, List[float]]] = Field(
movement_weights: Optional[Union[str, List[str]]] = Field(
default=[],
title="Internal value used for PTZ movements based on the speed of your camera's motor.",
)

View File

@@ -60,7 +60,7 @@ REQUEST_REGION_GRID = "request_region_grid"
# Autotracking
AUTOTRACKING_MAX_AREA_RATIO = 0.5
AUTOTRACKING_MAX_AREA_RATIO = 0.6
AUTOTRACKING_MOTION_MIN_DISTANCE = 20
AUTOTRACKING_MOTION_MAX_POINTS = 500
AUTOTRACKING_MAX_MOVE_METRICS = 500

View File

@@ -248,10 +248,8 @@ class TrackedObject:
if self.obj_data["frame_time"] - self.previous["frame_time"] > 60:
significant_change = True
# update autotrack at half fps
if self.obj_data["frame_time"] - self.previous["frame_time"] > (
1 / (self.camera_config.detect.fps / 2)
):
# update autotrack at most 3 objects per second
if self.obj_data["frame_time"] - self.previous["frame_time"] >= (1 / 3):
autotracker_update = True
self.obj_data.update(obj_data)

View File

@@ -215,8 +215,8 @@ class PtzAutoTracker:
maxlen=round(camera_config.detect.fps * 1.5)
)
self.tracked_object_metrics[camera] = {
"max_target_box": 1
- (AUTOTRACKING_MAX_AREA_RATIO ** self.zoom_factor[camera])
"max_target_box": AUTOTRACKING_MAX_AREA_RATIO
** (1 / self.zoom_factor[camera])
}
self.calibrating[camera] = False
@@ -268,6 +268,10 @@ class PtzAutoTracker:
if camera_config.onvif.autotracking.movement_weights:
if len(camera_config.onvif.autotracking.movement_weights) == 5:
camera_config.onvif.autotracking.movement_weights = [
float(val)
for val in camera_config.onvif.autotracking.movement_weights
]
self.ptz_metrics[camera][
"ptz_min_zoom"
].value = camera_config.onvif.autotracking.movement_weights[0]
@@ -521,7 +525,11 @@ class PtzAutoTracker:
camera_height = camera_config.frame_shape[0]
# Extract areas and calculate weighted average
areas = [obj["area"] for obj in self.tracked_object_history[camera]]
# grab the largest dimension of the bounding box and create a square from that
areas = [
max(obj["box"][2] - obj["box"][0], obj["box"][3] - obj["box"][1]) ** 2
for obj in self.tracked_object_history[camera]
]
filtered_areas = (
remove_outliers(areas)
@@ -686,19 +694,20 @@ class PtzAutoTracker:
camera_height = camera_config.frame_shape[0]
camera_fps = camera_config.detect.fps
# estimate_velocity is a numpy array of bbox top,left and bottom,right velocities
velocities = obj.obj_data["estimate_velocity"]
logger.debug(f"{camera}: Velocities from norfair: {velocities}")
# if we are close enough to zero, return right away
if np.all(np.round(velocities) == 0):
return True, np.zeros((2, 2))
return True, np.zeros((4,))
# Thresholds
x_mags_thresh = camera_width / camera_fps / 2
y_mags_thresh = camera_height / camera_fps / 2
dir_thresh = 0.93
delta_thresh = 12
var_thresh = 5
delta_thresh = 20
var_thresh = 10
# Check magnitude
x_mags = np.abs(velocities[:, 0])
@@ -722,7 +731,6 @@ class PtzAutoTracker:
np.linalg.norm(velocities[0]) * np.linalg.norm(velocities[1])
)
dir_thresh = 0.6 if np.all(delta < delta_thresh / 2) else dir_thresh
print(f"cosine sim: {cosine_sim}")
invalid_dirs = cosine_sim < dir_thresh
# Combine
@@ -752,10 +760,10 @@ class PtzAutoTracker:
)
)
# invalid velocity
return False, np.zeros((2, 2))
return False, np.zeros((4,))
else:
logger.debug(f"{camera}: Valid velocity ")
return True, np.mean(velocities, axis=0)
return True, velocities.flatten()
def _get_distance_threshold(self, camera, obj):
# Returns true if Euclidean distance from object to center of frame is
@@ -836,7 +844,7 @@ class PtzAutoTracker:
# ensure object is not moving quickly
below_velocity_threshold = np.all(
np.abs(average_velocity)
< np.array([velocity_threshold_x, velocity_threshold_y])
< np.tile([velocity_threshold_x, velocity_threshold_y], 2)
) or np.all(average_velocity == 0)
below_area_threshold = (
@@ -938,7 +946,7 @@ class PtzAutoTracker:
camera_height = camera_config.frame_shape[0]
camera_fps = camera_config.detect.fps
average_velocity = np.zeros((2, 2))
average_velocity = np.zeros((4,))
predicted_box = obj.obj_data["box"]
centroid_x = obj.obj_data["centroid"][0]
@@ -966,7 +974,6 @@ class PtzAutoTracker:
# this box could exceed the frame boundaries if velocity is high
# but we'll handle that in _enqueue_move() as two separate moves
current_box = np.array(obj.obj_data["box"])
average_velocity = np.tile(average_velocity, 2)
predicted_box = (
current_box
+ camera_fps * predicted_movement_time * average_velocity
@@ -1010,7 +1017,10 @@ class PtzAutoTracker:
zoom = 0
result = None
current_zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value
target_box = obj.obj_data["area"] / (camera_width * camera_height)
target_box = max(
obj.obj_data["box"][2] - obj.obj_data["box"][0],
obj.obj_data["box"][3] - obj.obj_data["box"][1],
) ** 2 / (camera_width * camera_height)
# absolute zooming separately from pan/tilt
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.absolute:
@@ -1061,24 +1071,15 @@ class PtzAutoTracker:
< self.tracked_object_metrics[camera]["max_target_box"]
else self.tracked_object_metrics[camera]["max_target_box"]
)
zoom = (
2
* (
limit
/ (
self.tracked_object_metrics[camera]["target_box"]
+ limit
)
)
- 1
)
ratio = limit / self.tracked_object_metrics[camera]["target_box"]
zoom = (ratio - 1) / (ratio + 1)
logger.debug(f"{camera}: Zoom calculation: {zoom}")
if not result:
# zoom out with special condition if zooming out because of velocity, edges, etc.
zoom = -(1 - zoom) if zoom > 0 else -(zoom + 1)
zoom = -(1 - zoom) if zoom > 0 else -(zoom * 2 + 1)
if result:
# zoom in
zoom = 1 - zoom if zoom > 0 else (zoom + 1)
zoom = 1 - zoom if zoom > 0 else (zoom * 2 + 1)
logger.debug(f"{camera}: Zooming: {result} Zoom amount: {zoom}")
@@ -1199,8 +1200,8 @@ class PtzAutoTracker:
)
self.tracked_object[camera] = None
self.tracked_object_metrics[camera] = {
"max_target_box": 1
- (AUTOTRACKING_MAX_AREA_RATIO ** self.zoom_factor[camera])
"max_target_box": AUTOTRACKING_MAX_AREA_RATIO
** (1 / self.zoom_factor[camera])
}
def camera_maintenance(self, camera):

View File

@@ -1651,11 +1651,11 @@ class TestConfig(unittest.TestCase):
runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].onvif.autotracking.movement_weights == [
0,
1,
1.23,
2.34,
0.50,
"0.0",
"1.0",
"1.23",
"2.34",
"0.5",
]
def test_fails_invalid_movement_weights(self):