limit zoom with max target box

This commit is contained in:
Josh Hawkins 2023-10-19 08:30:09 -05:00
parent b624134ad3
commit 7ed399cf9e
2 changed files with 66 additions and 26 deletions

View File

@ -54,6 +54,7 @@ INSERT_MANY_RECORDINGS = "insert_many_recordings"
# Autotracking # Autotracking
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

View File

@ -20,6 +20,7 @@ from norfair.camera_motion import (
from frigate.config import CameraConfig, FrigateConfig, ZoomingModeEnum from frigate.config import CameraConfig, FrigateConfig, ZoomingModeEnum
from frigate.const import ( from frigate.const import (
AUTOTRACKING_MAX_AREA_RATIO,
AUTOTRACKING_MAX_MOVE_METRICS, AUTOTRACKING_MAX_MOVE_METRICS,
AUTOTRACKING_MOTION_MAX_POINTS, AUTOTRACKING_MOTION_MAX_POINTS,
AUTOTRACKING_MOTION_MIN_DISTANCE, AUTOTRACKING_MOTION_MIN_DISTANCE,
@ -42,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)
) )
@ -213,7 +214,10 @@ class PtzAutoTracker:
self.tracked_object_history[camera] = deque( self.tracked_object_history[camera] = deque(
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
** (1 / self.zoom_factor[camera])
}
self.calibrating[camera] = False self.calibrating[camera] = False
self.move_metrics[camera] = [] self.move_metrics[camera] = []
@ -408,7 +412,12 @@ class PtzAutoTracker:
IQR = Q3 - Q1 IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR upper_bound = Q3 + 1.5 * IQR
return [x for x in data if lower_bound <= x <= upper_bound] filtered_data = [x for x in data if lower_bound <= x <= upper_bound]
removed_values = [x for x in data if x not in filtered_data]
logger.debug(f"{camera}: Removed area outliers: {removed_values}")
return filtered_data
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]
camera_width = camera_config.frame_shape[1] camera_width = camera_config.frame_shape[1]
@ -416,7 +425,13 @@ class PtzAutoTracker:
# Extract areas and calculate weighted average # Extract areas and calculate weighted average
areas = [obj["area"] for obj in self.tracked_object_history[camera]] areas = [obj["area"] for obj in self.tracked_object_history[camera]]
filtered_areas = remove_outliers(areas)
filtered_areas = (
remove_outliers(areas)
if len(areas) >= self.config.cameras[camera].detect.fps / 2
else areas
)
weights = np.arange(1, len(filtered_areas) + 1) weights = np.arange(1, len(filtered_areas) + 1)
weighted_area = np.average(filtered_areas, weights=weights) weighted_area = np.average(filtered_areas, weights=weights)
@ -424,10 +439,6 @@ class PtzAutoTracker:
weighted_area / (camera_width * camera_height) weighted_area / (camera_width * camera_height)
) ** self.zoom_factor[camera] ) ** self.zoom_factor[camera]
self.tracked_object_metrics[camera]["max_target_box"] = self.zoom_factor[
camera
] ** (1 / self.zoom_factor[camera])
# self.tracked_object_metrics[camera]["target_box"] = statistics.median( # self.tracked_object_metrics[camera]["target_box"] = statistics.median(
# [obj["area"] for obj in self.tracked_object_history[camera]] # [obj["area"] for obj in self.tracked_object_history[camera]]
# ) / (camera_width * camera_height) # ) / (camera_width * camera_height)
@ -584,8 +595,10 @@ class PtzAutoTracker:
valid_arrays = [array for array in arrays if is_valid(array[1] - array[0])] valid_arrays = [array for array in arrays if is_valid(array[1] - array[0])]
return valid_arrays return valid_arrays
bbox_start = obj.previous["box"] centroid_x = int((obj.previous["box"][0] + obj.previous["box"][2]) / 2.0)
bbox_end = obj.obj_data["box"] centroid_y = int((obj.previous["box"][1] + obj.previous["box"][3]) / 2.0)
bbox_start = [centroid_x, centroid_y]
bbox_end = obj.obj_data["centroid"]
start_time = obj.previous["frame_time"] start_time = obj.previous["frame_time"]
end_time = obj.obj_data["frame_time"] end_time = obj.obj_data["frame_time"]
if ptz_moving_at_frame_time( if ptz_moving_at_frame_time(
@ -594,7 +607,7 @@ class PtzAutoTracker:
self.ptz_metrics[camera]["ptz_stop_time"].value, self.ptz_metrics[camera]["ptz_stop_time"].value,
): ):
logger.debug( logger.debug(
f"{camera} ptz moving at velocity check start time: True {start_time}" f"{camera}: ptz moving at velocity check start time: True {start_time}"
) )
if ptz_moving_at_frame_time( if ptz_moving_at_frame_time(
end_time, end_time,
@ -602,14 +615,16 @@ class PtzAutoTracker:
self.ptz_metrics[camera]["ptz_stop_time"].value, self.ptz_metrics[camera]["ptz_stop_time"].value,
): ):
logger.debug( logger.debug(
f"{camera} ptz moving at velocity check end time: True {start_time}" f"{camera}: ptz moving at velocity check end time: True {start_time}"
) )
# add small amount to avoid division by zero # add small amount to avoid division by zero
velocity = [ velocity = [
round((end - start) / ((end_time - start_time) * camera_fps + 1e-6)) round((end - start) / ((end_time - start_time) * camera_fps + 1e-6))
for start, end in zip(bbox_start, bbox_end) for start, end in zip(bbox_start, bbox_end)
] ]
logger.debug(f"{camera}: Calculated velocity: {velocity}") logger.debug(
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"] velocities = obj.obj_data["estimate_velocity"]
diff = np.abs(velocities[1] - velocities[0]) diff = np.abs(velocities[1] - velocities[0])
@ -726,12 +741,24 @@ class PtzAutoTracker:
) )
# introduce some hysteresis to prevent a yo-yo zooming effect # introduce some hysteresis to prevent a yo-yo zooming effect
zoom_out_hysteresis = self.tracked_object_metrics[camera]["target_box"] > ( zoom_out_hysteresis = (
self.tracked_object_metrics[camera]["original_target_box"] self.tracked_object_metrics[camera]["target_box"]
> (
self.tracked_object_metrics[camera]["original_target_box"]
* AUTOTRACKING_ZOOM_OUT_HYSTERESIS
)
or self.tracked_object_metrics[camera]["target_box"]
> self.tracked_object_metrics[camera]["max_target_box"]
* AUTOTRACKING_ZOOM_OUT_HYSTERESIS * AUTOTRACKING_ZOOM_OUT_HYSTERESIS
) )
zoom_in_hysteresis = self.tracked_object_metrics[camera]["target_box"] < ( zoom_in_hysteresis = (
self.tracked_object_metrics[camera]["original_target_box"] self.tracked_object_metrics[camera]["target_box"]
< (
self.tracked_object_metrics[camera]["original_target_box"]
* AUTOTRACKING_ZOOM_IN_HYSTERESIS
)
or self.tracked_object_metrics[camera]["target_box"]
< self.tracked_object_metrics[camera]["max_target_box"]
* AUTOTRACKING_ZOOM_IN_HYSTERESIS * AUTOTRACKING_ZOOM_IN_HYSTERESIS
) )
@ -741,7 +768,7 @@ class PtzAutoTracker:
# debug zooming # debug zooming
if debug_zooming: if debug_zooming:
logger.debug( logger.debug(
f"{camera}: Zoom test: far from edges: count: {touching_frame_edges} left: {bb_left < edge_threshold * camera_width}, right: {bb_right > (1 - edge_threshold) * camera_width}, top: {bb_top < edge_threshold * camera_height}, bottom: {bb_bottom > (1 - edge_threshold) * camera_height}" f"{camera}: Zoom test: touching edges: count: {touching_frame_edges} left: {bb_left < edge_threshold * camera_width}, right: {bb_right > (1 - edge_threshold) * camera_width}, top: {bb_top < edge_threshold * camera_height}, bottom: {bb_bottom > (1 - edge_threshold) * camera_height}"
) )
logger.debug( logger.debug(
f"{camera}: Zoom test: below distance threshold: {(below_distance_threshold)}" f"{camera}: Zoom test: below distance threshold: {(below_distance_threshold)}"
@ -758,10 +785,10 @@ class PtzAutoTracker:
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}")
logger.debug( logger.debug(
f'{camera}: Zoom test: zoom in hysteresis limit: {zoom_in_hysteresis} value: {AUTOTRACKING_ZOOM_IN_HYSTERESIS} original: {self.tracked_object_metrics[camera]["original_target_box"]} target: {self.tracked_object_metrics[camera]["target_box"]}' f'{camera}: Zoom test: zoom in hysteresis limit: {zoom_in_hysteresis} value: {AUTOTRACKING_ZOOM_IN_HYSTERESIS} original: {self.tracked_object_metrics[camera]["original_target_box"]} max: {self.tracked_object_metrics[camera]["max_target_box"]} target: {self.tracked_object_metrics[camera]["target_box"]}'
) )
logger.debug( logger.debug(
f'{camera}: Zoom test: zoom out hysteresis limit: {zoom_out_hysteresis} value: {AUTOTRACKING_ZOOM_OUT_HYSTERESIS} original: {self.tracked_object_metrics[camera]["original_target_box"]} target: {self.tracked_object_metrics[camera]["target_box"]}' f'{camera}: Zoom test: zoom out hysteresis limit: {zoom_out_hysteresis} value: {AUTOTRACKING_ZOOM_OUT_HYSTERESIS} original: {self.tracked_object_metrics[camera]["original_target_box"]} max: {self.tracked_object_metrics[camera]["max_target_box"]} target: {self.tracked_object_metrics[camera]["target_box"]}'
) )
# Zoom in conditions (and) # Zoom in conditions (and)
@ -888,8 +915,13 @@ class PtzAutoTracker:
# relative zooming concurrently with pan/tilt # relative zooming concurrently with pan/tilt
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.relative: if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.relative:
# this is our initial zoom in on a new object # this is our initial zoom in on a new object
if not self.tracked_object_metrics[camera]: if "target_box" not in self.tracked_object_metrics[camera]:
zoom = target_box ** self.zoom_factor[camera] zoom = target_box ** self.zoom_factor[camera]
if target_box > self.tracked_object_metrics[camera]["max_target_box"]:
zoom = -(1 - zoom)
logger.debug(
f"{camera}: target box: {target_box}, max: {self.tracked_object_metrics[camera]['max_target_box']}, calc zoom: {zoom}"
)
else: else:
if ( if (
result := self._should_zoom_in( result := self._should_zoom_in(
@ -902,15 +934,19 @@ class PtzAutoTracker:
) )
) is not None: ) is not None:
# zoom value # zoom value
limit = (
self.tracked_object_metrics[camera]["original_target_box"]
if self.tracked_object_metrics[camera]["target_box"]
< self.tracked_object_metrics[camera]["max_target_box"]
else self.tracked_object_metrics[camera]["max_target_box"]
)
zoom = ( zoom = (
2 2
* ( * (
self.tracked_object_metrics[camera]["original_target_box"] limit
/ ( / (
self.tracked_object_metrics[camera]["target_box"] self.tracked_object_metrics[camera]["target_box"]
+ self.tracked_object_metrics[camera][ + limit
"original_target_box"
]
) )
) )
- 1 - 1
@ -1038,7 +1074,10 @@ class PtzAutoTracker:
f"{camera}: End object: {obj.obj_data['id']} {obj.obj_data['box']}" f"{camera}: End object: {obj.obj_data['id']} {obj.obj_data['box']}"
) )
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
** (1 / self.zoom_factor[camera])
}
def camera_maintenance(self, camera): def camera_maintenance(self, camera):
# bail and don't check anything if we're calibrating or tracking an object # bail and don't check anything if we're calibrating or tracking an object