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): class PtzAutotrackConfig(FrigateBaseModel):
enabled: bool = Field(default=False, title="Enable PTZ object autotracking.") 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.") track: List[str] = Field(default=DEFAULT_TRACKED_OBJECTS, title="Objects to track.")
required_zones: List[str] = Field( required_zones: List[str] = Field(
default_factory=list, default_factory=list,

View File

@ -11,7 +11,11 @@ from multiprocessing.synchronize import Event as MpEvent
import cv2 import cv2
import numpy as np 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.config import CameraConfig, FrigateConfig
from frigate.ptz.onvif import OnvifController 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 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(): if self.ptz_metrics["ptz_reset"].is_set():
self.ptz_metrics["ptz_reset"].clear() self.ptz_metrics["ptz_reset"].clear()
logger.debug("Motion estimator reset")
# homography is nice (zooming) but slow, translation is pan/tilt only but fast. # homography is nice (zooming) but slow, translation is pan/tilt only but fast.
if self.camera_config.onvif.autotracking.zooming:
logger.debug("Motion estimator reset - homography")
self.norfair_motion_estimator = MotionEstimator( self.norfair_motion_estimator = MotionEstimator(
transformations_getter=HomographyTransformationGetter(), transformations_getter=HomographyTransformationGetter(),
min_distance=30, min_distance=30,
max_points=900, 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 self.coord_transformations = None
if ptz_moving_at_frame_time( if ptz_moving_at_frame_time(
@ -97,10 +110,11 @@ class PtzMotionEstimator:
self.frame_manager.close(frame_id) self.frame_manager.close(frame_id)
if not self.camera_config.onvif.autotracking.zooming:
# doesn't work with homography # doesn't work with homography
# logger.debug( logger.debug(
# f"Motion estimator transformation: {self.coord_transformations.rel_to_abs((0,0))}" f"Motion estimator transformation: {self.coord_transformations.rel_to_abs((0,0))}"
# ) )
return self.coord_transformations return self.coord_transformations
@ -265,6 +279,7 @@ class PtzAutoTracker:
def _autotrack_zoom_ptz(self, camera, obj): def _autotrack_zoom_ptz(self, camera, obj):
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]
if camera_config.onvif.autotracking.zooming:
# frame width and height # frame width and height
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]
@ -273,6 +288,9 @@ class PtzAutoTracker:
zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value
# 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 -1 <= zoom_level < 1:
if ( if (
bb_left > 0.1 * camera_width bb_left > 0.1 * camera_width

View File

@ -33,6 +33,7 @@ class OnvifController:
self, config: FrigateConfig, ptz_metrics: dict[str, PTZMetricsTypes] self, config: FrigateConfig, ptz_metrics: dict[str, PTZMetricsTypes]
) -> None: ) -> None:
self.cams: dict[str, ONVIFCamera] = {} self.cams: dict[str, ONVIFCamera] = {}
self.config = config
self.ptz_metrics = ptz_metrics self.ptz_metrics = ptz_metrics
for cam_name, cam in config.cameras.items(): for cam_name, cam in config.cameras.items():
@ -93,6 +94,7 @@ class OnvifController:
None, None,
) )
if self.config.cameras[camera_name].onvif.autotracking.zooming:
zoom_space_id = next( zoom_space_id = next(
( (
i i
@ -121,12 +123,17 @@ class OnvifController:
][fov_space_id]["URI"] ][fov_space_id]["URI"]
try: try:
if self.config.cameras[camera_name].onvif.autotracking.zooming:
if zoom_space_id is not None: if zoom_space_id is not None:
move_request.Translation.Zoom.space = ptz_config["Spaces"][ move_request.Translation.Zoom.space = ptz_config["Spaces"][
"RelativeZoomTranslationSpace" "RelativeZoomTranslationSpace"
][0]["URI"] ][0]["URI"]
except Exception: except Exception:
# camera does not support relative zoom # 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 pass
if move_request.Speed is None: if move_request.Speed is None:
@ -351,7 +358,7 @@ class OnvifController:
onvif: ONVIFCamera = self.cams[camera_name]["onvif"] onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["relative_move_request"] 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 = numpy.interp(
zoom, zoom,
[-1, 1], [-1, 1],
@ -449,7 +456,8 @@ class OnvifController:
].value = datetime.datetime.now().timestamp() ].value = datetime.datetime.now().timestamp()
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0 self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
logger.debug(f"PTZ zoom level: {status.Position.Zoom.x}") 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( self.ptz_metrics[camera_name]["ptz_zoom_level"].value = numpy.interp(
status.Position.Zoom.x, status.Position.Zoom.x,
[ [
@ -461,6 +469,7 @@ class OnvifController:
self.cams[camera_name]["relative_zoom_range"]["XRange"]["Max"], self.cams[camera_name]["relative_zoom_range"]["XRange"]["Max"],
], ],
) )
logger.debug(f"Actual zoom level: {status.Position.Zoom.x}")
logger.debug( logger.debug(
f"Relative zoom level: {self.ptz_metrics[camera_name]['ptz_zoom_level'].value}" f"Relative zoom level: {self.ptz_metrics[camera_name]['ptz_zoom_level'].value}"
) )
@ -468,7 +477,11 @@ class OnvifController:
return { return {
"pan": status.Position.PanTilt.x, "pan": status.Position.PanTilt.x,
"tilt": status.Position.PanTilt.y, "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, "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",
} }