From 6c310e11c6848070cfbcbcd0183dba6b758c78e4 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 1 Sep 2023 08:58:50 -0500 Subject: [PATCH] make zooming configurable --- frigate/config.py | 1 + frigate/ptz/autotrack.py | 72 +++++++++++++++++++++++-------------- frigate/ptz/onvif.py | 77 +++++++++++++++++++++++----------------- 3 files changed, 91 insertions(+), 59 deletions(-) diff --git a/frigate/config.py b/frigate/config.py index f98da3855..4e8996e60 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -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, diff --git a/frigate/ptz/autotrack.py b/frigate/ptz/autotrack.py index b13d20059..9739acb56 100644 --- a/frigate/ptz/autotrack.py +++ b/frigate/ptz/autotrack.py @@ -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] diff --git a/frigate/ptz/onvif.py b/frigate/ptz/onvif.py index 340063992..9e25270c6 100644 --- a/frigate/ptz/onvif.py +++ b/frigate/ptz/onvif.py @@ -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", }