mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 03:35:26 +03:00
limit zoom with max target box
This commit is contained in:
parent
b624134ad3
commit
7ed399cf9e
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user