make zooming configurable

This commit is contained in:
Josh Hawkins 2023-09-01 08:58:50 -05:00
parent 2b52028d15
commit 6c310e11c6
3 changed files with 91 additions and 59 deletions

View File

@ -139,6 +139,7 @@ class MqttConfig(FrigateBaseModel):
class PtzAutotrackConfig(FrigateBaseModel):
enabled: bool = Field(default=False, title="Enable PTZ object autotracking.")
zooming: bool = Field(default=False, title="Enable zooming on autotracked object.")
track: List[str] = Field(default=DEFAULT_TRACKED_OBJECTS, title="Objects to track.")
required_zones: List[str] = Field(
default_factory=list,

View File

@ -11,7 +11,11 @@ from multiprocessing.synchronize import Event as MpEvent
import cv2
import numpy as np
from norfair.camera_motion import HomographyTransformationGetter, MotionEstimator
from norfair.camera_motion import (
HomographyTransformationGetter,
MotionEstimator,
TranslationTransformationGetter,
)
from frigate.config import CameraConfig, FrigateConfig
from frigate.ptz.onvif import OnvifController
@ -54,13 +58,22 @@ class PtzMotionEstimator:
# If we've just started up or returned to our preset, reset motion estimator for new tracking session
if self.ptz_metrics["ptz_reset"].is_set():
self.ptz_metrics["ptz_reset"].clear()
logger.debug("Motion estimator reset")
# homography is nice (zooming) but slow, translation is pan/tilt only but fast.
self.norfair_motion_estimator = MotionEstimator(
transformations_getter=HomographyTransformationGetter(),
min_distance=30,
max_points=900,
)
if self.camera_config.onvif.autotracking.zooming:
logger.debug("Motion estimator reset - homography")
self.norfair_motion_estimator = MotionEstimator(
transformations_getter=HomographyTransformationGetter(),
min_distance=30,
max_points=900,
)
else:
logger.debug("Motion estimator reset - translation")
self.norfair_motion_estimator = MotionEstimator(
transformations_getter=TranslationTransformationGetter(),
min_distance=30,
max_points=900,
)
self.coord_transformations = None
if ptz_moving_at_frame_time(
@ -97,10 +110,11 @@ class PtzMotionEstimator:
self.frame_manager.close(frame_id)
# doesn't work with homography
# logger.debug(
# f"Motion estimator transformation: {self.coord_transformations.rel_to_abs((0,0))}"
# )
if not self.camera_config.onvif.autotracking.zooming:
# doesn't work with homography
logger.debug(
f"Motion estimator transformation: {self.coord_transformations.rel_to_abs((0,0))}"
)
return self.coord_transformations
@ -265,26 +279,30 @@ class PtzAutoTracker:
def _autotrack_zoom_ptz(self, camera, obj):
camera_config = self.config.cameras[camera]
# frame width and height
camera_width = camera_config.frame_shape[1]
camera_height = camera_config.frame_shape[0]
if camera_config.onvif.autotracking.zooming:
# frame width and height
camera_width = camera_config.frame_shape[1]
camera_height = camera_config.frame_shape[0]
bb_left, bb_top, bb_right, bb_bottom = obj.obj_data["box"]
bb_left, bb_top, bb_right, bb_bottom = obj.obj_data["box"]
zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value
zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value
if -1 <= zoom_level < 1:
if (
bb_left > 0.1 * camera_width
and bb_right < 0.9 * camera_width
and bb_top > 0.1 * camera_height
and bb_bottom < 0.9 * camera_height
):
zoom = 0.1 # Zoom in
else:
zoom = -0.1 # Zoom out
# ensure zooming level is in range
# if so, check if bounding box is 10% of an edge
# if so, try zooming in, otherwise try zooming out
if -1 <= zoom_level < 1:
if (
bb_left > 0.1 * camera_width
and bb_right < 0.9 * camera_width
and bb_top > 0.1 * camera_height
and bb_bottom < 0.9 * camera_height
):
zoom = 0.1 # Zoom in
else:
zoom = -0.1 # Zoom out
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 autotrack_object(self, camera, obj):
camera_config = self.config.cameras[camera]

View File

@ -33,6 +33,7 @@ class OnvifController:
self, config: FrigateConfig, ptz_metrics: dict[str, PTZMetricsTypes]
) -> None:
self.cams: dict[str, ONVIFCamera] = {}
self.config = config
self.ptz_metrics = ptz_metrics
for cam_name, cam in config.cameras.items():
@ -93,16 +94,17 @@ class OnvifController:
None,
)
zoom_space_id = next(
(
i
for i, space in enumerate(
ptz_config.Spaces.RelativeZoomTranslationSpace
)
if "TranslationGenericSpace" in space["URI"]
),
None,
)
if self.config.cameras[camera_name].onvif.autotracking.zooming:
zoom_space_id = next(
(
i
for i, space in enumerate(
ptz_config.Spaces.RelativeZoomTranslationSpace
)
if "TranslationGenericSpace" in space["URI"]
),
None,
)
# setup continuous moving request
move_request = ptz.create_type("ContinuousMove")
@ -121,12 +123,17 @@ class OnvifController:
][fov_space_id]["URI"]
try:
if zoom_space_id is not None:
move_request.Translation.Zoom.space = ptz_config["Spaces"][
"RelativeZoomTranslationSpace"
][0]["URI"]
if self.config.cameras[camera_name].onvif.autotracking.zooming:
if zoom_space_id is not None:
move_request.Translation.Zoom.space = ptz_config["Spaces"][
"RelativeZoomTranslationSpace"
][0]["URI"]
except Exception:
# camera does not support relative zoom
self.config.cameras[camera_name].onvif.autotracking.zooming = False
logger.warning(
f"Disabling autotracking zooming for {camera_name}: Relative zoom not supported"
)
pass
if move_request.Speed is None:
@ -351,7 +358,7 @@ class OnvifController:
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["relative_move_request"]
# function takes in -1 to 1 for zoom, interpolate to the values of the camera.
# function takes in -1 to 1 for zoom, interpolate to the relative values of the camera.
zoom = numpy.interp(
zoom,
[-1, 1],
@ -449,26 +456,32 @@ class OnvifController:
].value = datetime.datetime.now().timestamp()
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
logger.debug(f"PTZ zoom level: {status.Position.Zoom.x}")
self.ptz_metrics[camera_name]["ptz_zoom_level"].value = numpy.interp(
status.Position.Zoom.x,
[
self.cams[camera_name]["zoom_limits"]["Range"]["XRange"]["Min"],
self.cams[camera_name]["zoom_limits"]["Range"]["XRange"]["Max"],
],
[
self.cams[camera_name]["relative_zoom_range"]["XRange"]["Min"],
self.cams[camera_name]["relative_zoom_range"]["XRange"]["Max"],
],
)
logger.debug(
f"Relative zoom level: {self.ptz_metrics[camera_name]['ptz_zoom_level'].value}"
)
if self.config.cameras[camera_name].onvif.autotracking.zooming:
# interpolate the actual zoom level reported by the camera into the relative zoom range
self.ptz_metrics[camera_name]["ptz_zoom_level"].value = numpy.interp(
status.Position.Zoom.x,
[
self.cams[camera_name]["zoom_limits"]["Range"]["XRange"]["Min"],
self.cams[camera_name]["zoom_limits"]["Range"]["XRange"]["Max"],
],
[
self.cams[camera_name]["relative_zoom_range"]["XRange"]["Min"],
self.cams[camera_name]["relative_zoom_range"]["XRange"]["Max"],
],
)
logger.debug(f"Actual zoom level: {status.Position.Zoom.x}")
logger.debug(
f"Relative zoom level: {self.ptz_metrics[camera_name]['ptz_zoom_level'].value}"
)
return {
"pan": status.Position.PanTilt.x,
"tilt": status.Position.PanTilt.y,
"zoom": status.Position.Zoom.x,
"zoom": status.Position.Zoom.x
if self.config.cameras[camera_name].onvif.autotracking.zooming
else 0,
"pantilt_moving": status.MoveStatus.PanTilt,
"zoom_moving": status.MoveStatus.Zoom,
"zoom_moving": status.MoveStatus.Zoom
if self.config.cameras[camera_name].onvif.autotracking.zooming
else "IDLE",
}