update zoom conditions

This commit is contained in:
Josh Hawkins 2023-10-16 07:47:01 -05:00
parent 7c7615b291
commit cd227b2158
2 changed files with 41 additions and 45 deletions

View File

@ -141,7 +141,7 @@ In security and surveillance, it's common to use "spotter" cameras in combinatio
### The autotracker loses track of my object. Why? ### The autotracker loses track of my object. Why?
There are many reasons this could be the case. If you are using experimental zooming, your `zoom_factor` value might be too high. Camera motion might be causing Frigate to lose track of the object. The object might be traveling too quickly. The scene might be too dark, motion settings are not sensitive enough or are too sensitive, or is less than optimal for Frigate to maintain tracking. There are many reasons this could be the case. If you are using experimental zooming, your `zoom_factor` value might be too high, the object might be traveling too quickly, the scene might be too dark, there are not enough details in the scene (for example, a PTZ looking down on a driveway or other monotone background without any hard lines), or the scene is otherwise less than optimal for Frigate to maintain tracking.
Watching Frigate's debug view can help to determine a possible cause. The autotracked object will have a thicker colored box around it. Watching Frigate's debug view can help to determine a possible cause. The autotracked object will have a thicker colored box around it.

View File

@ -191,6 +191,7 @@ class PtzAutoTracker:
self.intercept: dict[str, object] = {} self.intercept: dict[str, object] = {}
self.move_coefficients: dict[str, object] = {} self.move_coefficients: dict[str, object] = {}
self.zoom_factor: dict[str, object] = {} self.zoom_factor: dict[str, object] = {}
self.original_target_box: dict[str, object] = {}
self.previous_target_box: dict[str, object] = {} self.previous_target_box: dict[str, object] = {}
# if cam is set to autotrack, onvif should be set up # if cam is set to autotrack, onvif should be set up
@ -216,7 +217,7 @@ class PtzAutoTracker:
self.move_metrics[camera] = [] self.move_metrics[camera] = []
self.intercept[camera] = None self.intercept[camera] = None
self.move_coefficients[camera] = [] self.move_coefficients[camera] = []
self.previous_target_box[camera] = 0.5 self.previous_target_box[camera] = 1
self.move_queues[camera] = queue.Queue() self.move_queues[camera] = queue.Queue()
self.move_queue_locks[camera] = threading.Lock() self.move_queue_locks[camera] = threading.Lock()
@ -424,15 +425,17 @@ class PtzAutoTracker:
continue continue
else: else:
if zoom != 0:
self.previous_target_box[camera] = self.tracked_object[
camera
].obj_data["area"] / (camera_width * camera_height)
if ( if (
self.config.cameras[camera].onvif.autotracking.zooming self.config.cameras[camera].onvif.autotracking.zooming
== ZoomingModeEnum.relative == ZoomingModeEnum.relative
# this enables us to absolutely zoom if we lost an object # this enables us to absolutely zoom if we lost an object
and self.tracked_object[camera] is not None and self.tracked_object[camera] is not None
): ):
self.previous_target_box[camera] = self.tracked_object[
camera
].obj_data["area"] / (camera_width * camera_height)
self.onvif._move_relative(camera, pan, tilt, zoom, 1) self.onvif._move_relative(camera, pan, tilt, zoom, 1)
else: else:
@ -447,9 +450,6 @@ class PtzAutoTracker:
zoom > 0 zoom > 0
and self.ptz_metrics[camera]["ptz_zoom_level"].value != zoom and self.ptz_metrics[camera]["ptz_zoom_level"].value != zoom
): ):
self.previous_target_box[camera] = self.tracked_object[
camera
].obj_data["area"] / (camera_width * camera_height)
self.onvif._zoom_absolute(camera, zoom, 1) self.onvif._zoom_absolute(camera, zoom, 1)
# Wait until the camera finishes moving # Wait until the camera finishes moving
@ -598,6 +598,13 @@ 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
# save target box area of initial zoom to be a limiter for the session
if (
self.original_target_box[camera] == 1
and self.previous_target_box[camera] != 1
):
self.original_target_box[camera] = self.previous_target_box[camera]
average_velocity, distance = self._get_valid_velocity(camera, obj) average_velocity, distance = self._get_valid_velocity(camera, obj)
bb_left, bb_top, bb_right, bb_bottom = box bb_left, bb_top, bb_right, bb_bottom = box
@ -608,12 +615,8 @@ class PtzAutoTracker:
# calculate a velocity threshold based on movement coefficients if available # calculate a velocity threshold based on movement coefficients if available
if camera_config.onvif.autotracking.movement_weights: if camera_config.onvif.autotracking.movement_weights:
predicted_movement_time = self._predict_movement_time(camera, 1, 1) predicted_movement_time = self._predict_movement_time(camera, 1, 1)
velocity_threshold_x = ( velocity_threshold_x = camera_width / predicted_movement_time / camera_fps
camera_width / predicted_movement_time / camera_fps * 0.7 velocity_threshold_y = camera_height / predicted_movement_time / camera_fps
)
velocity_threshold_y = (
camera_height / predicted_movement_time / camera_fps * 0.7
)
else: else:
# use a generic velocity threshold # use a generic velocity threshold
velocity_threshold_x = camera_width * 0.02 velocity_threshold_x = camera_width * 0.02
@ -629,7 +632,7 @@ 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._below_distance_threshold(camera, obj)
# ensure object isn't too big in frame # ensure object isn't taking up too much of the frame
below_dimension_threshold = (bb_right - bb_left) / camera_width < 0.8 and ( below_dimension_threshold = (bb_right - bb_left) / camera_width < 0.8 and (
bb_bottom - bb_top bb_bottom - bb_top
) / camera_height < 0.8 ) / camera_height < 0.8
@ -645,10 +648,9 @@ class PtzAutoTracker:
target_box = obj.obj_data["area"] / (camera_width * camera_height) target_box = obj.obj_data["area"] / (camera_width * camera_height)
below_area_threshold = target_box < 0.05 below_area_threshold = target_box < self.original_target_box[camera]
# introduce some hysteresis to prevent a yo-yo zooming effect # introduce some hysteresis to prevent a yo-yo zooming effect
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.absolute:
zoom_out_hysteresis = target_box > ( zoom_out_hysteresis = target_box > (
self.previous_target_box[camera] * AUTOTRACKING_ZOOM_OUT_HYSTERESIS self.previous_target_box[camera] * AUTOTRACKING_ZOOM_OUT_HYSTERESIS
) )
@ -656,11 +658,6 @@ class PtzAutoTracker:
self.previous_target_box[camera] * AUTOTRACKING_ZOOM_IN_HYSTERESIS self.previous_target_box[camera] * AUTOTRACKING_ZOOM_IN_HYSTERESIS
) )
# relative target boxes deadzone is much smaller
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.relative:
zoom_out_hysteresis = target_box > (self.previous_target_box[camera] * 1.1)
zoom_in_hysteresis = target_box < (self.previous_target_box[camera] * 0.9)
at_max_zoom = self.ptz_metrics[camera]["ptz_zoom_level"].value == 1 at_max_zoom = self.ptz_metrics[camera]["ptz_zoom_level"].value == 1
at_min_zoom = self.ptz_metrics[camera]["ptz_zoom_level"].value == 0 at_min_zoom = self.ptz_metrics[camera]["ptz_zoom_level"].value == 0
@ -682,7 +679,7 @@ class PtzAutoTracker:
f"{camera}: Zoom test: below distance threshold: {(below_distance_threshold)}" f"{camera}: Zoom test: below distance threshold: {(below_distance_threshold)}"
) )
logger.debug( logger.debug(
f"{camera}: Zoom test: below area threshold: {(below_area_threshold)} target box: {target_box}" f"{camera}: Zoom test: below area threshold: {(below_area_threshold)} original: {self.original_target_box[camera]} target box: {target_box}"
) )
logger.debug( logger.debug(
f"{camera}: Zoom test: below dimension threshold: {below_dimension_threshold} width: {(bb_right - bb_left) / camera_width}, height: { (bb_bottom - bb_top) / camera_height}" f"{camera}: Zoom test: below dimension threshold: {below_dimension_threshold} width: {(bb_right - bb_left) / camera_width}, height: { (bb_bottom - bb_top) / camera_height}"
@ -703,30 +700,28 @@ class PtzAutoTracker:
# object area could be larger # object area could be larger
# object is away from the edge of the frame # object is away from the edge of the frame
# object is not moving too quickly # object is not moving too quickly
# object is centered or small in the frame # object is small in the frame
# object area is below initial area
# camera is not fully zoomed in # camera is not fully zoomed in
if ( if (
zoom_in_hysteresis zoom_in_hysteresis
and away_from_edge and away_from_edge
and below_velocity_threshold and below_velocity_threshold
and below_distance_threshold
and below_dimension_threshold and below_dimension_threshold
and below_area_threshold
and not at_max_zoom and not at_max_zoom
): ):
return True return True
# Zoom out conditions (or) # Zoom out conditions (or)
# object area got too large, is touching an edge, and centered # object area got too large
# object area got too large and has at least one dimension that is too large # object area has at least one dimension that is too large
# object is touching an edge and either centered or has too large of an area # object is touching an edge and either centered
# object is moving too quickly # object is moving too quickly
if ( if (
(zoom_out_hysteresis and not away_from_edge and below_distance_threshold) zoom_out_hysteresis
or (zoom_out_hysteresis and not below_dimension_threshold) or not below_dimension_threshold
or ( or (not away_from_edge and below_distance_threshold)
not away_from_edge
and (below_distance_threshold or not below_area_threshold)
)
or not below_velocity_threshold or not below_velocity_threshold
) and not at_min_zoom: ) and not at_min_zoom:
return False return False
@ -792,7 +787,6 @@ class PtzAutoTracker:
camera_width = camera_config.frame_shape[1] camera_width = camera_config.frame_shape[1]
camera_height = camera_config.frame_shape[0] camera_height = camera_config.frame_shape[0]
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.relative:
target_box = obj.obj_data["area"] / (camera_width * camera_height) target_box = obj.obj_data["area"] / (camera_width * camera_height)
self.previous_target_box[camera] = target_box ** self.zoom_factor[camera] self.previous_target_box[camera] = target_box ** self.zoom_factor[camera]
@ -812,6 +806,8 @@ class PtzAutoTracker:
result = None result = None
current_zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value current_zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value
target_box = obj.obj_data["area"] / (camera_width * camera_height) target_box = obj.obj_data["area"] / (camera_width * camera_height)
if self.tracked_object_previous[camera] is None:
self.original_target_box[camera] = 1
# absolute zooming separately from pan/tilt # absolute zooming separately from pan/tilt
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.absolute: if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.absolute:
@ -853,7 +849,7 @@ class PtzAutoTracker:
logger.debug(f"{camera}: Zoom calculation: {zoom}") logger.debug(f"{camera}: Zoom calculation: {zoom}")
if not result: if not result:
# zoom out # zoom out
zoom = -(1 - abs(zoom)) if zoom > 0 else zoom zoom = -(1 - abs(zoom)) if zoom > 0 else -(zoom + 1)
logger.debug(f"{camera}: Zooming: {result} Zoom amount: {zoom}") logger.debug(f"{camera}: Zooming: {result} Zoom amount: {zoom}")
@ -997,7 +993,7 @@ class PtzAutoTracker:
# clear tracked object and reset zoom level # clear tracked object and reset zoom level
self.tracked_object[camera] = None self.tracked_object[camera] = None
self.tracked_object_previous[camera] = None self.tracked_object_previous[camera] = None
self.previous_target_box[camera] = 0.5 self.previous_target_box[camera] = 1
# empty move queue # empty move queue
while not self.move_queues[camera].empty(): while not self.move_queues[camera].empty():