better invalid velocity checks

This commit is contained in:
Josh Hawkins 2023-10-22 08:14:49 -05:00
parent a961c8d77f
commit 0cd0ad6b8a
2 changed files with 71 additions and 77 deletions

View File

@ -58,6 +58,6 @@ AUTOTRACKING_MAX_AREA_RATIO = 0.5
AUTOTRACKING_MOTION_MIN_DISTANCE = 20 AUTOTRACKING_MOTION_MIN_DISTANCE = 20
AUTOTRACKING_MOTION_MAX_POINTS = 500 AUTOTRACKING_MOTION_MAX_POINTS = 500
AUTOTRACKING_MAX_MOVE_METRICS = 500 AUTOTRACKING_MAX_MOVE_METRICS = 500
AUTOTRACKING_ZOOM_OUT_HYSTERESIS = 1.5 AUTOTRACKING_ZOOM_OUT_HYSTERESIS = 1.2
AUTOTRACKING_ZOOM_IN_HYSTERESIS = 0.8 AUTOTRACKING_ZOOM_IN_HYSTERESIS = 0.9
AUTOTRACKING_ZOOM_EDGE_THRESHOLD = 0.05 AUTOTRACKING_ZOOM_EDGE_THRESHOLD = 0.05

View File

@ -43,7 +43,7 @@ def ptz_moving_at_frame_time(frame_time, ptz_start_time, ptz_stop_time):
# ptz_start_time is initialized to 0 on startup and only changes # ptz_start_time is initialized to 0 on startup and only changes
# when autotracking movements are made # when autotracking movements are made
return (ptz_start_time != 0.0 and frame_time > ptz_start_time) and ( return (ptz_start_time != 0.0 and frame_time > ptz_start_time) and (
ptz_stop_time == 0.0 or (ptz_start_time <= frame_time < ptz_stop_time) ptz_stop_time == 0.0 or (ptz_start_time <= frame_time <= ptz_stop_time)
) )
@ -215,8 +215,8 @@ class PtzAutoTracker:
maxlen=round(camera_config.detect.fps * 1.5) maxlen=round(camera_config.detect.fps * 1.5)
) )
self.tracked_object_metrics[camera] = { self.tracked_object_metrics[camera] = {
"max_target_box": AUTOTRACKING_MAX_AREA_RATIO "max_target_box": 1
** (1 / self.zoom_factor[camera]) - (AUTOTRACKING_MAX_AREA_RATIO ** self.zoom_factor[camera])
} }
self.calibrating[camera] = False self.calibrating[camera] = False
@ -665,70 +665,67 @@ class PtzAutoTracker:
camera_height = camera_config.frame_shape[0] camera_height = camera_config.frame_shape[0]
camera_fps = camera_config.detect.fps camera_fps = camera_config.detect.fps
def remove_outliers_iqr(arrays): velocities = obj.obj_data["estimate_velocity"]
def is_valid(array): logger.debug(f"{camera}: Velocities from norfair: {velocities}")
Q1 = np.percentile(array, 25)
Q3 = np.percentile(array, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return (lower_bound <= array) & (array <= upper_bound)
valid_arrays = [array for array in arrays if is_valid(array[1] - array[0])] # Thresholds
return valid_arrays x_mags_thresh = camera_width / camera_fps / 2
y_mags_thresh = camera_height / camera_fps / 2
dir_thresh = 0.96
delta_thresh = 10
var_thresh = 3
centroid_x = int((obj.previous["box"][0] + obj.previous["box"][2]) / 2.0) # Check magnitude
centroid_y = int((obj.previous["box"][1] + obj.previous["box"][3]) / 2.0) x_mags = np.abs(velocities[:, 0])
bbox_start = [centroid_x, centroid_y] y_mags = np.abs(velocities[:, 1])
bbox_end = obj.obj_data["centroid"] invalid_x_mags = np.any(x_mags > x_mags_thresh)
start_time = obj.previous["frame_time"] invalid_y_mags = np.any(y_mags > y_mags_thresh)
end_time = obj.obj_data["frame_time"]
if ptz_moving_at_frame_time( # Check delta
start_time, delta = np.abs(velocities[0] - velocities[1])
self.ptz_metrics[camera]["ptz_start_time"].value, invalid_delta = np.any(delta > delta_thresh)
self.ptz_metrics[camera]["ptz_stop_time"].value,
): # Check variance
logger.debug( stdevs = np.std(velocities, axis=0)
f"{camera}: ptz moving at velocity check start time: True {start_time}" high_variances = np.any(stdevs > var_thresh)
)
if ptz_moving_at_frame_time( # Check direction difference
end_time, cosine_sim = np.dot(velocities[0], velocities[1]) / (
self.ptz_metrics[camera]["ptz_start_time"].value, np.linalg.norm(velocities[0]) * np.linalg.norm(velocities[1])
self.ptz_metrics[camera]["ptz_stop_time"].value, )
): dir_thresh = 0.6 if np.all(delta < delta_thresh / 2) else dir_thresh
logger.debug( invalid_dirs = cosine_sim < dir_thresh
f"{camera}: ptz moving at velocity check end time: True {start_time}"
) # Combine
# add small amount to avoid division by zero invalid = (
velocity = [ invalid_x_mags
round((end - start) / ((end_time - start_time) * camera_fps + 1e-6)) or invalid_y_mags
for start, end in zip(bbox_start, bbox_end) or invalid_dirs
] or invalid_delta
logger.debug( or high_variances
f"{camera}: Calculated velocity: {velocity}, start: {start_time}, end: {end_time}, diff: {end_time-start_time}, frames: {int((end_time-start_time)*camera_fps)}"
) )
velocities = obj.obj_data["estimate_velocity"] if invalid:
diff = np.abs(velocities[1] - velocities[0]) logger.debug(
f"{camera}: Invalid velocity: {tuple(np.round(velocities, 2).flatten().astype(int))}: Invalid because: "
if (diff > 8).any(): + ", ".join(
[
var_name
for var_name, is_invalid in [
("invalid_x_mags", invalid_x_mags),
("invalid_y_mags", invalid_y_mags),
("invalid_dirs", invalid_dirs),
("invalid_delta", invalid_delta),
("high_variances", high_variances),
]
if is_invalid
]
)
)
# invalid velocity # invalid velocity
return np.zeros(2), -1 return np.zeros((2, 2))
average_velocity = np.mean(velocities, axis=0)
# get euclidean distance of the points, sometimes the estimate is way off
velocity_distance = np.linalg.norm(velocities[0] - velocities[1])
if (
0 <= abs(average_velocity[0]) < (camera_width / camera_fps / 2)
and 0 <= abs(average_velocity[1]) < (camera_height / camera_fps / 2)
and velocity_distance <= 10
):
return average_velocity, velocity_distance
else: else:
# invalid velocity return np.mean(velocities, axis=0)
return np.zeros(2), -1
def _below_distance_threshold(self, camera, obj): def _below_distance_threshold(self, camera, obj):
# Returns true if Euclidean distance from object to center of frame is # Returns true if Euclidean distance from object to center of frame is
@ -777,7 +774,7 @@ class PtzAutoTracker:
camera_height = camera_config.frame_shape[0] camera_height = camera_config.frame_shape[0]
camera_fps = camera_config.detect.fps camera_fps = camera_config.detect.fps
average_velocity, distance = self._get_valid_velocity(camera, obj) average_velocity = self._get_valid_velocity(camera, obj)
bb_left, bb_top, bb_right, bb_bottom = box bb_left, bb_top, bb_right, bb_bottom = box
@ -809,13 +806,10 @@ class PtzAutoTracker:
) and (bb_bottom - bb_top) <= camera_height * (self.zoom_factor[camera] + 0.1) ) and (bb_bottom - bb_top) <= camera_height * (self.zoom_factor[camera] + 0.1)
# ensure object is not moving quickly # ensure object is not moving quickly
below_velocity_threshold = ( below_velocity_threshold = np.all(
np.all( np.abs(average_velocity)
abs(average_velocity) < np.array([velocity_threshold_x, velocity_threshold_y])
< np.array([velocity_threshold_x, velocity_threshold_y]) ) or np.all(average_velocity == 0)
)
or distance == -1
)
below_area_threshold = ( below_area_threshold = (
self.tracked_object_metrics[camera]["target_box"] self.tracked_object_metrics[camera]["target_box"]
@ -868,7 +862,7 @@ class PtzAutoTracker:
f"{camera}: Zoom test: below dimension threshold: {below_dimension_threshold} width: {bb_right - bb_left}, max width: {camera_width * (self.zoom_factor[camera] + 0.1)}, height: {bb_bottom - bb_top}, max height: {camera_height * (self.zoom_factor[camera] + 0.1)}" f"{camera}: Zoom test: below dimension threshold: {below_dimension_threshold} width: {bb_right - bb_left}, max width: {camera_width * (self.zoom_factor[camera] + 0.1)}, height: {bb_bottom - bb_top}, max height: {camera_height * (self.zoom_factor[camera] + 0.1)}"
) )
logger.debug( logger.debug(
f"{camera}: Zoom test: below velocity threshold: {below_velocity_threshold} velocity x: {abs(average_velocity[0])}, x threshold: {velocity_threshold_x}, velocity y: {abs(average_velocity[1])}, y threshold: {velocity_threshold_y}" f"{camera}: Zoom test: below velocity threshold: {below_velocity_threshold} velocity x: {abs(average_velocity[0])}, x threshold: {velocity_threshold_x}, velocity y: {abs(average_velocity[0])}, y threshold: {velocity_threshold_y}"
) )
logger.debug(f"{camera}: Zoom test: at max zoom: {at_max_zoom}") logger.debug(f"{camera}: Zoom test: at max zoom: {at_max_zoom}")
logger.debug(f"{camera}: Zoom test: at min zoom: {at_min_zoom}") logger.debug(f"{camera}: Zoom test: at min zoom: {at_min_zoom}")
@ -931,9 +925,9 @@ class PtzAutoTracker:
): # use estimates if we have available coefficients ): # use estimates if we have available coefficients
predicted_movement_time = self._predict_movement_time(camera, pan, tilt) predicted_movement_time = self._predict_movement_time(camera, pan, tilt)
average_velocity, distance = self._get_valid_velocity(camera, obj) average_velocity = self._get_valid_velocity(camera, obj)
if distance != -1: if np.any(average_velocity):
# this box could exceed the frame boundaries if velocity is high # this box could exceed the frame boundaries if velocity is high
# but we'll handle that in _enqueue_move() as two separate moves # but we'll handle that in _enqueue_move() as two separate moves
current_box = np.array(obj.obj_data["box"]) current_box = np.array(obj.obj_data["box"])
@ -955,7 +949,7 @@ class PtzAutoTracker:
logger.debug(f'{camera}: Original box: {obj.obj_data["box"]}') logger.debug(f'{camera}: Original box: {obj.obj_data["box"]}')
logger.debug(f"{camera}: Predicted box: {tuple(predicted_box)}") logger.debug(f"{camera}: Predicted box: {tuple(predicted_box)}")
logger.debug( logger.debug(
f'{camera}: Velocity: {tuple(np.round(obj.obj_data["estimate_velocity"]).flatten().astype(int))}' f"{camera}: Velocity: {tuple(np.round(average_velocity).flatten().astype(int))}"
) )
zoom = self._get_zoom_amount(camera, obj, predicted_box, debug_zoom=True) zoom = self._get_zoom_amount(camera, obj, predicted_box, debug_zoom=True)
@ -1170,8 +1164,8 @@ class PtzAutoTracker:
) )
self.tracked_object[camera] = None self.tracked_object[camera] = None
self.tracked_object_metrics[camera] = { self.tracked_object_metrics[camera] = {
"max_target_box": AUTOTRACKING_MAX_AREA_RATIO "max_target_box": 1
** (1 / self.zoom_factor[camera]) - (AUTOTRACKING_MAX_AREA_RATIO ** self.zoom_factor[camera])
} }
def camera_maintenance(self, camera): def camera_maintenance(self, camera):