revamp zooming to use area moving average

This commit is contained in:
Josh Hawkins 2023-10-18 10:12:01 -05:00
parent 2b20e7e972
commit 18b9540e4f
2 changed files with 157 additions and 91 deletions

View File

@ -141,7 +141,9 @@ 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, 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. 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 a sufficient number of hard edges or corners), or the scene is otherwise less than optimal for Frigate to maintain tracking.
Your camera's shutter speed may also be set too low so that blurring occurs with motion. Check your camera's firmware to see if you can increase the shutter speed.
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

@ -6,6 +6,7 @@ import os
import queue import queue
import threading import threading
import time import time
from collections import deque
from functools import partial from functools import partial
from multiprocessing.synchronize import Event as MpEvent from multiprocessing.synchronize import Event as MpEvent
@ -162,7 +163,7 @@ class PtzAutoTrackerThread(threading.Thread):
# disabled dynamically by mqtt # disabled dynamically by mqtt
if self.ptz_autotracker.tracked_object.get(camera): if self.ptz_autotracker.tracked_object.get(camera):
self.ptz_autotracker.tracked_object[camera] = None self.ptz_autotracker.tracked_object[camera] = None
self.ptz_autotracker.tracked_object_previous[camera] = None self.ptz_autotracker.tracked_object_history[camera].clear()
logger.info("Exiting autotracker...") logger.info("Exiting autotracker...")
@ -178,8 +179,8 @@ class PtzAutoTracker:
self.onvif = onvif self.onvif = onvif
self.ptz_metrics = ptz_metrics self.ptz_metrics = ptz_metrics
self.tracked_object: dict[str, object] = {} self.tracked_object: dict[str, object] = {}
self.tracked_object_previous: dict[str, object] = {} self.tracked_object_history: dict[str, object] = {}
self.previous_frame_time: dict[str, object] = {} self.tracked_object_metrics: dict[str, object] = {}
self.object_types: dict[str, object] = {} self.object_types: dict[str, object] = {}
self.required_zones: dict[str, object] = {} self.required_zones: dict[str, object] = {}
self.move_queues: dict[str, object] = {} self.move_queues: dict[str, object] = {}
@ -191,8 +192,6 @@ 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] = {}
# if cam is set to autotrack, onvif should be set up # if cam is set to autotrack, onvif should be set up
for camera, camera_config in self.config.cameras.items(): for camera, camera_config in self.config.cameras.items():
@ -211,13 +210,15 @@ class PtzAutoTracker:
self.zoom_factor[camera] = camera_config.onvif.autotracking.zoom_factor self.zoom_factor[camera] = camera_config.onvif.autotracking.zoom_factor
self.tracked_object[camera] = None self.tracked_object[camera] = None
self.tracked_object_previous[camera] = None self.tracked_object_history[camera] = deque(
maxlen=round(camera_config.detect.fps * 1.5)
)
self.tracked_object_metrics[camera] = {}
self.calibrating[camera] = False self.calibrating[camera] = False
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] = 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()
@ -400,11 +401,43 @@ class PtzAutoTracker:
return np.dot(self.move_coefficients[camera], input_data) return np.dot(self.move_coefficients[camera], input_data)
def _process_move_queue(self, camera): def _calculate_tracked_object_metrics(self, camera):
def remove_outliers(data):
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return [x for x in data if lower_bound <= x <= upper_bound]
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]
camera_height = camera_config.frame_shape[0] camera_height = camera_config.frame_shape[0]
# Extract areas and calculate weighted average
areas = [obj["area"] for obj in self.tracked_object_history[camera]]
filtered_areas = remove_outliers(areas)
weights = np.arange(1, len(filtered_areas) + 1)
weighted_area = np.average(filtered_areas, weights=weights)
self.tracked_object_metrics[camera]["target_box"] = weighted_area / (
camera_width * camera_height
)
# self.tracked_object_metrics[camera]["target_box"] = statistics.median(
# [obj["area"] for obj in self.tracked_object_history[camera]]
# ) / (camera_width * camera_height)
if "original_target_box" not in self.tracked_object_metrics[camera]:
self.tracked_object_metrics[camera][
"original_target_box"
] = self.tracked_object_metrics[camera]["target_box"]
def _process_move_queue(self, camera):
camera_config = self.config.cameras[camera]
camera_config.frame_shape[1]
camera_config.frame_shape[0]
while True: while True:
move_data = self.move_queues[camera].get() move_data = self.move_queues[camera].get()
@ -426,9 +459,13 @@ class PtzAutoTracker:
else: else:
if zoom != 0: if zoom != 0:
self.previous_target_box[camera] = self.tracked_object[ self.tracked_object_metrics[camera][
camera "last_zoom_time"
].obj_data["area"] / (camera_width * camera_height) ] = frame_time
if pan != 0 or tilt != 0:
self.tracked_object_metrics[camera][
"last_move_time"
] = frame_time
if ( if (
self.config.cameras[camera].onvif.autotracking.zooming self.config.cameras[camera].onvif.autotracking.zooming
@ -531,6 +568,28 @@ 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):
def is_valid(array):
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])]
return valid_arrays
bbox_start = obj.previous["box"]
bbox_end = obj.obj_data["box"]
start_time = obj.previous["frame_time"]
end_time = obj.obj_data["frame_time"]
velocity = [
round((end - start) / ((end_time - start_time) * camera_fps))
for start, end in zip(bbox_start, bbox_end)
]
logger.debug(f"{camera}: Calculated velocity: {velocity}")
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])
@ -583,7 +642,7 @@ 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)
distance_threshold = 0.1 * max_frame * (scaling_factor / 2) distance_threshold = 0.1 * max_frame * scaling_factor
logger.debug( logger.debug(
f"{camera}: Distance: {centroid_distance}, threshold: {distance_threshold}" f"{camera}: Distance: {centroid_distance}, threshold: {distance_threshold}"
@ -598,15 +657,6 @@ 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] ** self.zoom_factor[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
@ -624,20 +674,19 @@ class PtzAutoTracker:
velocity_threshold_x = camera_width * 0.02 velocity_threshold_x = camera_width * 0.02
velocity_threshold_y = camera_height * 0.02 velocity_threshold_y = camera_height * 0.02
away_from_edge = ( touching_frame_edges = int(
bb_left > edge_threshold * camera_width (bb_left < edge_threshold * camera_width)
and bb_right < (1 - edge_threshold) * camera_width + (bb_right > (1 - edge_threshold) * camera_width)
and bb_top > edge_threshold * camera_height + (bb_top < edge_threshold * camera_height)
and bb_bottom < (1 - edge_threshold) * camera_height + (bb_bottom > (1 - edge_threshold) * camera_height)
) )
# 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 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
# ensure object is not moving quickly # ensure object is not moving quickly
below_velocity_threshold = ( below_velocity_threshold = (
@ -648,16 +697,25 @@ class PtzAutoTracker:
or distance == -1 or distance == -1
) )
target_box = obj.obj_data["area"] / (camera_width * camera_height) below_area_threshold = (
self.tracked_object_metrics[camera]["target_box"]
< self.tracked_object_metrics[camera]["original_target_box"]
)
below_area_threshold = target_box < self.original_target_box[camera] below_zoom_factored_threshold = (
self.tracked_object_metrics[camera]["original_target_box"]
** self.zoom_factor[camera]
< 0.75
)
# introduce some hysteresis to prevent a yo-yo zooming effect # introduce some hysteresis to prevent a yo-yo zooming effect
zoom_out_hysteresis = target_box > ( zoom_out_hysteresis = self.tracked_object_metrics[camera]["target_box"] > (
self.previous_target_box[camera] * AUTOTRACKING_ZOOM_OUT_HYSTERESIS self.tracked_object_metrics[camera]["original_target_box"]
* AUTOTRACKING_ZOOM_OUT_HYSTERESIS
) )
zoom_in_hysteresis = target_box < ( zoom_in_hysteresis = self.tracked_object_metrics[camera]["target_box"] < (
self.previous_target_box[camera] * AUTOTRACKING_ZOOM_IN_HYSTERESIS self.tracked_object_metrics[camera]["original_target_box"]
* AUTOTRACKING_ZOOM_IN_HYSTERESIS
) )
at_max_zoom = self.ptz_metrics[camera]["ptz_zoom_level"].value == 1 at_max_zoom = self.ptz_metrics[camera]["ptz_zoom_level"].value == 1
@ -666,25 +724,19 @@ class PtzAutoTracker:
# debug zooming # debug zooming
if debug_zooming: if debug_zooming:
logger.debug( logger.debug(
f"{camera}: Zoom test: far from left edge: {bb_left > edge_threshold * camera_width}" 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}"
)
logger.debug(
f"{camera}: Zoom test: far from right edge: {bb_right < (1 - edge_threshold) * camera_width}"
)
logger.debug(
f"{camera}: Zoom test: far from top edge: {bb_top > edge_threshold * camera_height}"
)
logger.debug(
f"{camera}: Zoom test: far from bottom edge: {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)}"
) )
logger.debug( logger.debug(
f"{camera}: Zoom test: below area threshold: {(below_area_threshold)} original: {self.original_target_box[camera]} target box: {target_box}" f"{camera}: Zoom test: below area threshold: {(below_area_threshold)} original: {self.tracked_object_metrics[camera]['original_target_box']} target box: {self.tracked_object_metrics[camera]['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 zoom factored threshold: {(below_zoom_factored_threshold)} target box: {self.tracked_object_metrics[camera]['target_box'] ** self.zoom_factor[camera]}"
)
logger.debug(
f"{camera}: Zoom test: below dimension threshold: {below_dimension_threshold} width: {bb_right - bb_left} height: {bb_bottom - bb_top}"
) )
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[1])}, y threshold: {velocity_threshold_y}"
@ -692,22 +744,16 @@ 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} previous: {self.previous_target_box[camera]} target: {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"]} target: {self.tracked_object_metrics[camera]["target_box"]}'
) )
logger.debug( logger.debug(
f"{camera}: Zoom test: zoom out hysteresis limit: {zoom_out_hysteresis} previous: {self.previous_target_box[camera]} target: {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"]} target: {self.tracked_object_metrics[camera]["target_box"]}'
) )
# Zoom in conditions (and) # Zoom in conditions (and)
# object area could be larger
# object is away from the edge of the frame
# object is not moving too quickly
# object is small in the frame
# object area is below initial area
# camera is not fully zoomed in
if ( if (
zoom_in_hysteresis zoom_in_hysteresis
and away_from_edge and touching_frame_edges == 0
and below_velocity_threshold and below_velocity_threshold
and below_dimension_threshold and below_dimension_threshold
and below_area_threshold and below_area_threshold
@ -716,15 +762,21 @@ class PtzAutoTracker:
return True return True
# Zoom out conditions (or) # Zoom out conditions (or)
# object area got too large
# object area has at least one dimension that is too large
# object is touching an edge and either centered
# object is moving too quickly
if ( if (
(
zoom_out_hysteresis
and not at_max_zoom
# and not below_zoom_factored_threshold
and (not below_area_threshold or not below_dimension_threshold)
)
or (
zoom_out_hysteresis zoom_out_hysteresis
and not below_area_threshold and not below_area_threshold
or not below_dimension_threshold and at_max_zoom
or (not away_from_edge and below_distance_threshold) # and not below_zoom_factored_threshold
)
or (touching_frame_edges == 1 and below_distance_threshold)
or touching_frame_edges > 1
or not below_velocity_threshold or not below_velocity_threshold
) and not at_min_zoom: ) and not at_min_zoom:
return False return False
@ -786,12 +838,15 @@ class PtzAutoTracker:
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, zoom) self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, zoom)
def _autotrack_move_zoom_only(self, camera, obj): def _autotrack_move_zoom_only(self, camera, obj):
camera_config = self.config.cameras[camera]
if camera_config.onvif.autotracking.zooming == ZoomingModeEnum.absolute:
zoom = self._get_zoom_amount(camera, obj, obj.obj_data["box"]) zoom = self._get_zoom_amount(camera, obj, obj.obj_data["box"])
if zoom != 0: if zoom != 0:
self._enqueue_move(camera, obj.obj_data["frame_time"], 0, 0, zoom) self._enqueue_move(camera, obj.obj_data["frame_time"], 0, 0, zoom)
def _get_zoom_amount(self, camera, obj, predicted_box, debug_zoom=False): def _get_zoom_amount(self, camera, obj, predicted_box, debug_zoom=True):
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]
# frame width and height # frame width and height
@ -802,13 +857,11 @@ 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:
# don't zoom on initial move # don't zoom on initial move
if self.tracked_object_previous[camera] is None: if not self.tracked_object_history[camera]:
zoom = current_zoom_level zoom = current_zoom_level
else: else:
if ( if (
@ -823,7 +876,8 @@ 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:
if self.tracked_object_previous[camera] is None: # this is our initial zoom in on a new object
if not self.tracked_object_metrics[camera]:
zoom = target_box ** self.zoom_factor[camera] zoom = target_box ** self.zoom_factor[camera]
else: else:
if ( if (
@ -840,8 +894,13 @@ class PtzAutoTracker:
zoom = ( zoom = (
2 2
* ( * (
self.previous_target_box[camera] self.tracked_object_metrics[camera]["original_target_box"]
/ (target_box + self.previous_target_box[camera]) / (
self.tracked_object_metrics[camera]["target_box"]
+ self.tracked_object_metrics[camera][
"original_target_box"
]
)
) )
- 1 - 1
) )
@ -875,31 +934,35 @@ class PtzAutoTracker:
and set(obj.entered_zones) & set(self.required_zones[camera]) and set(obj.entered_zones) & set(self.required_zones[camera])
and not obj.previous["false_positive"] and not obj.previous["false_positive"]
and not obj.false_positive and not obj.false_positive
and self.tracked_object_previous[camera] is None and not self.tracked_object_history[camera]
and obj.obj_data["motionless_count"] == 0 and obj.obj_data["motionless_count"] == 0
): ):
logger.debug( logger.debug(
f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}" f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
) )
self.tracked_object[camera] = obj self.tracked_object[camera] = obj
self.tracked_object_history[camera].append(copy.deepcopy(obj.obj_data))
self._autotrack_move_ptz(camera, obj) self._autotrack_move_ptz(camera, obj)
self.tracked_object_previous[camera] = copy.deepcopy(obj)
self.previous_frame_time[camera] = obj.obj_data["frame_time"]
return return
if ( if (
# already tracking an object # already tracking an object
self.tracked_object[camera] is not None self.tracked_object[camera] is not None
and self.tracked_object_previous[camera] is not None and self.tracked_object_history[camera]
and obj.obj_data["id"] == self.tracked_object[camera].obj_data["id"] and obj.obj_data["id"] == self.tracked_object[camera].obj_data["id"]
and obj.obj_data["frame_time"] != self.previous_frame_time and obj.obj_data["frame_time"]
!= self.tracked_object_history[camera][-1]["frame_time"]
and not ptz_moving_at_frame_time( and not ptz_moving_at_frame_time(
obj.obj_data["frame_time"], obj.obj_data["frame_time"],
self.ptz_metrics[camera]["ptz_start_time"].value, self.ptz_metrics[camera]["ptz_start_time"].value,
self.ptz_metrics[camera]["ptz_stop_time"].value, self.ptz_metrics[camera]["ptz_stop_time"].value,
) )
): ):
self.tracked_object_history[camera].append(copy.deepcopy(obj.obj_data))
self._calculate_tracked_object_metrics(camera)
if self._below_distance_threshold(camera, obj): if self._below_distance_threshold(camera, obj):
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']}"
@ -914,9 +977,6 @@ class PtzAutoTracker:
self._autotrack_move_ptz(camera, obj) self._autotrack_move_ptz(camera, obj)
self.tracked_object_previous[camera] = copy.deepcopy(obj)
self.previous_frame_time[camera] = obj.obj_data["frame_time"]
return return
if ( if (
@ -928,11 +988,11 @@ class PtzAutoTracker:
and obj.obj_data["label"] in self.object_types[camera] and obj.obj_data["label"] in self.object_types[camera]
and not obj.previous["false_positive"] and not obj.previous["false_positive"]
and not obj.false_positive and not obj.false_positive
and self.tracked_object_previous[camera] is not None and self.tracked_object_history[camera]
): ):
if ( if (
intersection_over_union( intersection_over_union(
self.tracked_object_previous[camera].obj_data["region"], self.tracked_object_history[camera][-1]["region"],
obj.obj_data["box"], obj.obj_data["box"],
) )
< 0.2 < 0.2
@ -941,9 +1001,13 @@ class PtzAutoTracker:
f"{camera}: Reacquired object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}" f"{camera}: Reacquired object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
) )
self.tracked_object[camera] = obj self.tracked_object[camera] = obj
self.tracked_object_history[camera].clear()
self.tracked_object_history[camera].append(
copy.deepcopy(obj.obj_data)
)
self._calculate_tracked_object_metrics(camera)
self._autotrack_move_ptz(camera, obj) self._autotrack_move_ptz(camera, obj)
self.tracked_object_previous[camera] = copy.deepcopy(obj)
self.previous_frame_time[camera] = obj.obj_data["frame_time"]
return return
@ -957,6 +1021,7 @@ 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] = {}
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
@ -980,19 +1045,18 @@ class PtzAutoTracker:
# return to preset if tracking is over # return to preset if tracking is over
if ( if (
self.tracked_object[camera] is None self.tracked_object[camera] is None
and self.tracked_object_previous[camera] is not None and self.tracked_object_history[camera]
and ( and (
# might want to use a different timestamp here? # might want to use a different timestamp here?
self.ptz_metrics[camera]["ptz_frame_time"].value self.ptz_metrics[camera]["ptz_frame_time"].value
- self.tracked_object_previous[camera].obj_data["frame_time"] - self.tracked_object_history[camera][-1]["frame_time"]
>= autotracker_config.timeout >= autotracker_config.timeout
) )
and autotracker_config.return_preset and autotracker_config.return_preset
): ):
# 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_history[camera].clear()
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():