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.
self.norfair_motion_estimator = MotionEstimator( if self.camera_config.onvif.autotracking.zooming:
transformations_getter=HomographyTransformationGetter(), logger.debug("Motion estimator reset - homography")
min_distance=30, self.norfair_motion_estimator = MotionEstimator(
max_points=900, 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 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)
# doesn't work with homography if not self.camera_config.onvif.autotracking.zooming:
# logger.debug( # doesn't work with homography
# f"Motion estimator transformation: {self.coord_transformations.rel_to_abs((0,0))}" logger.debug(
# ) f"Motion estimator transformation: {self.coord_transformations.rel_to_abs((0,0))}"
)
return self.coord_transformations return self.coord_transformations
@ -265,26 +279,30 @@ 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]
# frame width and height if camera_config.onvif.autotracking.zooming:
camera_width = camera_config.frame_shape[1] # frame width and height
camera_height = camera_config.frame_shape[0] 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: # ensure zooming level is in range
if ( # if so, check if bounding box is 10% of an edge
bb_left > 0.1 * camera_width # if so, try zooming in, otherwise try zooming out
and bb_right < 0.9 * camera_width if -1 <= zoom_level < 1:
and bb_top > 0.1 * camera_height if (
and bb_bottom < 0.9 * camera_height bb_left > 0.1 * camera_width
): and bb_right < 0.9 * camera_width
zoom = 0.1 # Zoom in and bb_top > 0.1 * camera_height
else: and bb_bottom < 0.9 * camera_height
zoom = -0.1 # Zoom out ):
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): def autotrack_object(self, camera, obj):
camera_config = self.config.cameras[camera] camera_config = self.config.cameras[camera]

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,16 +94,17 @@ class OnvifController:
None, None,
) )
zoom_space_id = next( if self.config.cameras[camera_name].onvif.autotracking.zooming:
( zoom_space_id = next(
i (
for i, space in enumerate( i
ptz_config.Spaces.RelativeZoomTranslationSpace for i, space in enumerate(
) ptz_config.Spaces.RelativeZoomTranslationSpace
if "TranslationGenericSpace" in space["URI"] )
), if "TranslationGenericSpace" in space["URI"]
None, ),
) None,
)
# setup continuous moving request # setup continuous moving request
move_request = ptz.create_type("ContinuousMove") move_request = ptz.create_type("ContinuousMove")
@ -121,12 +123,17 @@ class OnvifController:
][fov_space_id]["URI"] ][fov_space_id]["URI"]
try: try:
if zoom_space_id is not None: if self.config.cameras[camera_name].onvif.autotracking.zooming:
move_request.Translation.Zoom.space = ptz_config["Spaces"][ if zoom_space_id is not None:
"RelativeZoomTranslationSpace" move_request.Translation.Zoom.space = ptz_config["Spaces"][
][0]["URI"] "RelativeZoomTranslationSpace"
][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,26 +456,32 @@ 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:
self.ptz_metrics[camera_name]["ptz_zoom_level"].value = numpy.interp( # interpolate the actual zoom level reported by the camera into the relative zoom range
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]["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"], 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}" 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 { 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",
} }