refactor thresholds and reduce a duplicate call

This commit is contained in:
Josh Hawkins 2023-10-26 12:03:53 -05:00
parent 5a46c36380
commit 1a7344603e

View File

@ -502,7 +502,7 @@ class PtzAutoTracker:
return np.dot(self.move_coefficients[camera], input_data) return np.dot(self.move_coefficients[camera], input_data)
def _calculate_tracked_object_metrics(self, camera): def _calculate_tracked_object_metrics(self, camera, obj):
def remove_outliers(data): def remove_outliers(data):
Q1 = np.percentile(data, 25) Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75) Q3 = np.percentile(data, 75)
@ -541,6 +541,27 @@ class PtzAutoTracker:
"original_target_box" "original_target_box"
] = self.tracked_object_metrics[camera]["target_box"] ] = self.tracked_object_metrics[camera]["target_box"]
(
self.tracked_object_metrics[camera]["valid_velocity"],
self.tracked_object_metrics[camera]["velocity"],
) = self._get_valid_velocity(camera, obj)
self.tracked_object_metrics[camera]["distance"] = self._get_distance_threshold(
camera, obj
)
centroid_distance = np.linalg.norm(
[
obj.obj_data["centroid"][0] - camera_config.detect.width / 2,
obj.obj_data["centroid"][1] - camera_config.detect.height / 2,
]
)
logger.debug(f"{camera}: Centroid distance: {centroid_distance}")
self.tracked_object_metrics[camera]["below_distance_threshold"] = (
centroid_distance < self.tracked_object_metrics[camera]["distance"]
)
def _process_move_queue(self, camera): def _process_move_queue(self, camera):
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]
camera_config.frame_shape[1] camera_config.frame_shape[1]
@ -668,12 +689,16 @@ class PtzAutoTracker:
velocities = obj.obj_data["estimate_velocity"] velocities = obj.obj_data["estimate_velocity"]
logger.debug(f"{camera}: Velocities from norfair: {velocities}") 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))
# Thresholds # Thresholds
x_mags_thresh = camera_width / camera_fps / 2 x_mags_thresh = camera_width / camera_fps / 2
y_mags_thresh = camera_height / camera_fps / 2 y_mags_thresh = camera_height / camera_fps / 2
dir_thresh = 0.96 dir_thresh = 0.93
delta_thresh = 10 delta_thresh = 12
var_thresh = 3 var_thresh = 5
# Check magnitude # Check magnitude
x_mags = np.abs(velocities[:, 0]) x_mags = np.abs(velocities[:, 0])
@ -690,11 +715,15 @@ class PtzAutoTracker:
high_variances = np.any(stdevs > var_thresh) high_variances = np.any(stdevs > var_thresh)
# Check direction difference # Check direction difference
cosine_sim = np.dot(velocities[0], velocities[1]) / ( velocities = np.round(velocities)
np.linalg.norm(velocities[0]) * np.linalg.norm(velocities[1]) invalid_dirs = False
) if not np.any(np.linalg.norm(velocities, axis=1)):
dir_thresh = 0.6 if np.all(delta < delta_thresh / 2) else dir_thresh cosine_sim = np.dot(velocities[0], velocities[1]) / (
invalid_dirs = cosine_sim < dir_thresh 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 # Combine
invalid = ( invalid = (
@ -723,11 +752,12 @@ class PtzAutoTracker:
) )
) )
# invalid velocity # invalid velocity
return np.zeros((2, 2)) return False, np.zeros((2, 2))
else: else:
return np.mean(velocities, axis=0) logger.debug(f"{camera}: Valid velocity ")
return True, np.mean(velocities, axis=0)
def _below_distance_threshold(self, camera, obj): def _get_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
# less than 10% of the of the larger dimension (width or height) of the frame, # less than 10% of the of the larger dimension (width or height) of the frame,
# multiplied by a scaling factor for object size. # multiplied by a scaling factor for object size.
@ -738,13 +768,6 @@ class PtzAutoTracker:
# TODO: there's probably a better way to approach this # TODO: there's probably a better way to approach this
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]
centroid_distance = np.linalg.norm(
[
obj.obj_data["centroid"][0] - camera_config.detect.width / 2,
obj.obj_data["centroid"][1] - camera_config.detect.height / 2,
]
)
obj_width = obj.obj_data["box"][2] - obj.obj_data["box"][0] obj_width = obj.obj_data["box"][2] - obj.obj_data["box"][0]
obj_height = obj.obj_data["box"][3] - obj.obj_data["box"][1] obj_height = obj.obj_data["box"][3] - obj.obj_data["box"][1]
@ -758,14 +781,17 @@ class PtzAutoTracker:
# larger objects should lower the threshold, smaller objects should raise it # larger objects should lower the threshold, smaller objects should raise it
scaling_factor = 1 - np.log(max_obj / max_frame) scaling_factor = 1 - np.log(max_obj / max_frame)
percentage = 0.1 if camera_config.onvif.autotracking.movement_weights else 0.03 percentage = (
0.08
if camera_config.onvif.autotracking.movement_weights
and self.tracked_object_metrics[camera]["valid_velocity"]
else 0.03
)
distance_threshold = percentage * max_frame * scaling_factor distance_threshold = percentage * max_frame * scaling_factor
logger.debug( logger.debug(f"{camera}: Distance threshold: {distance_threshold}")
f"{camera}: Distance: {centroid_distance}, threshold: {distance_threshold}"
)
return centroid_distance < distance_threshold return distance_threshold
def _should_zoom_in(self, camera, obj, box, debug_zooming=False): def _should_zoom_in(self, camera, obj, box, debug_zooming=False):
# returns True if we should zoom in, False if we should zoom out, None to do nothing # returns True if we should zoom in, False if we should zoom out, None to do nothing
@ -774,7 +800,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 = self._get_valid_velocity(camera, obj) average_velocity = self.tracked_object_metrics[camera]["velocity"]
bb_left, bb_top, bb_right, bb_bottom = box bb_left, bb_top, bb_right, bb_bottom = box
@ -799,7 +825,9 @@ class PtzAutoTracker:
) )
# make sure object is centered in the frame # make sure object is centered in the frame
below_distance_threshold = self._below_distance_threshold(camera, obj) below_distance_threshold = self.tracked_object_metrics[camera][
"below_distance_threshold"
]
below_dimension_threshold = (bb_right - bb_left) <= camera_width * ( below_dimension_threshold = (bb_right - bb_left) <= camera_width * (
self.zoom_factor[camera] + 0.1 self.zoom_factor[camera] + 0.1
@ -925,7 +953,14 @@ 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 = self._get_valid_velocity(camera, obj) _, average_velocity = (
self._get_valid_velocity(camera, obj)
if "velocity" not in self.tracked_object_metrics[camera]
else (
self.tracked_object_metrics[camera]["valid_velocity"],
self.tracked_object_metrics[camera]["velocity"],
)
)
if np.any(average_velocity): 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
@ -992,7 +1027,7 @@ class PtzAutoTracker:
level = ( level = (
self.ptz_metrics[camera]["ptz_max_zoom"].value self.ptz_metrics[camera]["ptz_max_zoom"].value
- self.ptz_metrics[camera]["ptz_min_zoom"].value - self.ptz_metrics[camera]["ptz_min_zoom"].value
) / 10 ) / 20
if result: if result:
zoom = min(1.0, current_zoom_level + level) zoom = min(1.0, current_zoom_level + level)
else: else:
@ -1103,9 +1138,9 @@ class PtzAutoTracker:
) )
): ):
self.tracked_object_history[camera].append(copy.deepcopy(obj.obj_data)) self.tracked_object_history[camera].append(copy.deepcopy(obj.obj_data))
self._calculate_tracked_object_metrics(camera) self._calculate_tracked_object_metrics(camera, obj)
if self._below_distance_threshold(camera, obj): if self.tracked_object_metrics[camera]["below_distance_threshold"]:
logger.debug( logger.debug(
f"{camera}: Existing object (do NOT move ptz): {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}" f"{camera}: Existing object (do NOT move ptz): {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
) )
@ -1148,7 +1183,7 @@ class PtzAutoTracker:
self.tracked_object_history[camera].append( self.tracked_object_history[camera].append(
copy.deepcopy(obj.obj_data) copy.deepcopy(obj.obj_data)
) )
self._calculate_tracked_object_metrics(camera) self._calculate_tracked_object_metrics(camera, obj)
self._autotrack_move_ptz(camera, obj) self._autotrack_move_ptz(camera, obj)
return return