mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-06 19:25:22 +03:00
relative zooming option for dahua/amcrest cams
This commit is contained in:
parent
6ae9faf5a8
commit
0eb90f6e6d
@ -52,6 +52,8 @@ cameras:
|
||||
enabled: False
|
||||
# Optional: enable/disable camera zooming in/out on objects during autotracking. (default: shown below)
|
||||
zooming: False
|
||||
# Optional: enable/disable relative zooming for the camera (default: shown below)
|
||||
zoom_relative: False
|
||||
# Optional: list of objects to track from labelmap.txt (default: shown below)
|
||||
track:
|
||||
- person
|
||||
@ -76,6 +78,8 @@ The autotracker will add PTZ motion requests to a queue while the motor is movin
|
||||
|
||||
Zooming is an experimental feature and may use significantly more CPU when tracking objects than panning/tilting only. It may be helpful to tweak your camera's autofocus settings if you are noticing focus problems when using zooming.
|
||||
|
||||
Relative zooming makes a zoom movement concurrently with any pan/tilt movements and was tested to work with some Dahua and Amcrest PTZs. If zooming behavior is erratic or relative zooming is unsupported, the autotracker will fall back to absolute zooming where any zoom movements are separate from pan/tilt movements.
|
||||
|
||||
## Usage applications
|
||||
|
||||
In security and surveillance, it's common to use "spotter" cameras in combination with your PTZ. When your fixed spotter camera detects an object, you could use an automation platform like Home Assistant to move the PTZ to a specific preset so that Frigate can begin automatically tracking the object. For example: a residence may have fixed cameras on the east and west side of the property, capturing views up and down a street. When the spotter camera on the west side detects a person, a Home Assistant automation could move the PTZ to a camera preset aimed toward the west. When the object enters the specified zone, Frigate's autotracker could then continue to track the person as it moves out of view of any of the fixed cameras.
|
||||
|
||||
@ -575,6 +575,8 @@ cameras:
|
||||
enabled: False
|
||||
# Optional: enable/disable camera zooming in/out on objects during autotracking. (default: shown below)
|
||||
zooming: False
|
||||
# Optional: enable/disable relative zooming for the camera (default: shown below)
|
||||
zoom_relative: False
|
||||
# Optional: list of objects to track from labelmap.txt (default: shown below)
|
||||
track:
|
||||
- person
|
||||
|
||||
@ -140,6 +140,9 @@ 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.")
|
||||
zoom_relative: bool = Field(
|
||||
default=False, title="Use relative zooming instead of absolute."
|
||||
)
|
||||
track: List[str] = Field(default=DEFAULT_TRACKED_OBJECTS, title="Objects to track.")
|
||||
required_zones: List[str] = Field(
|
||||
default_factory=list,
|
||||
|
||||
@ -232,17 +232,23 @@ class PtzAutoTracker:
|
||||
continue
|
||||
|
||||
else:
|
||||
if zoom > 0:
|
||||
self.onvif._zoom_absolute(camera, zoom, 1)
|
||||
|
||||
# on some cameras with cheaper motors it seems like small values can cause jerky movement
|
||||
# TODO: double check, might not need this
|
||||
if abs(pan) > 0.02 or abs(tilt) > 0.02:
|
||||
self.onvif._move_relative(camera, pan, tilt, 1)
|
||||
if (
|
||||
self.config.cameras[camera].onvif.autotracking.zooming
|
||||
and self.config.cameras[camera].onvif.autotracking.zoom_relative
|
||||
):
|
||||
self.onvif._move_relative(camera, pan, tilt, zoom, 1)
|
||||
else:
|
||||
logger.debug(
|
||||
f"Not moving, pan and tilt too small: {pan}, {tilt}"
|
||||
)
|
||||
if zoom > 0:
|
||||
self.onvif._zoom_absolute(camera, zoom, 1)
|
||||
|
||||
# on some cameras with cheaper motors it seems like small values can cause jerky movement
|
||||
# TODO: double check, might not need this
|
||||
if abs(pan) > 0.02 or abs(tilt) > 0.02:
|
||||
self.onvif._move_relative(camera, pan, tilt, 0, 1)
|
||||
else:
|
||||
logger.debug(
|
||||
f"Not moving, pan and tilt too small: {pan}, {tilt}"
|
||||
)
|
||||
|
||||
# Wait until the camera finishes moving
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
@ -263,6 +269,30 @@ class PtzAutoTracker:
|
||||
)
|
||||
self.move_queues[camera].put(move_data)
|
||||
|
||||
def _should_zoom_in(self, obj, camera):
|
||||
camera_config = self.config.cameras[camera]
|
||||
camera_width = camera_config.frame_shape[1]
|
||||
camera_height = camera_config.frame_shape[0]
|
||||
camera_area = camera_width * camera_height
|
||||
|
||||
bb_left, bb_top, bb_right, bb_bottom = obj.obj_data["box"]
|
||||
|
||||
# If bounding box is not within 5% of an edge
|
||||
# If object area is less than 70% of frame
|
||||
# Then zoom in, otherwise try zooming out
|
||||
# should we make these configurable?
|
||||
edge_threshold = 0.05
|
||||
area_threshold = 0.7
|
||||
|
||||
# returns True to zoom in, False to zoom out
|
||||
return (
|
||||
bb_left > edge_threshold * camera_width
|
||||
and bb_right < (1 - edge_threshold) * camera_width
|
||||
and bb_top > edge_threshold * camera_height
|
||||
and bb_bottom < (1 - edge_threshold) * camera_height
|
||||
and obj.obj_data["area"] < area_threshold * camera_area
|
||||
)
|
||||
|
||||
def _autotrack_move_ptz(self, camera, obj):
|
||||
camera_config = self.config.cameras[camera]
|
||||
|
||||
@ -275,35 +305,38 @@ class PtzAutoTracker:
|
||||
tilt = (0.5 - (obj.obj_data["centroid"][1] / camera_height)) * 2
|
||||
|
||||
# ideas: check object velocity for camera speed?
|
||||
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, 0)
|
||||
if (
|
||||
camera_config.onvif.autotracking.zooming
|
||||
and camera_config.onvif.autotracking.zoom_relative
|
||||
):
|
||||
zoom = obj.obj_data["area"] / (camera_width * camera_height)
|
||||
|
||||
# test if we need to zoom out
|
||||
if not self._should_zoom_in(obj, camera):
|
||||
zoom = -(1 - zoom)
|
||||
|
||||
self._enqueue_move(
|
||||
camera,
|
||||
obj.obj_data["frame_time"],
|
||||
pan,
|
||||
tilt,
|
||||
zoom,
|
||||
)
|
||||
else:
|
||||
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, 0)
|
||||
|
||||
def _autotrack_zoom_ptz(self, camera, obj):
|
||||
camera_config = self.config.cameras[camera]
|
||||
|
||||
if camera_config.onvif.autotracking.zooming:
|
||||
camera_width = camera_config.frame_shape[1]
|
||||
camera_height = camera_config.frame_shape[0]
|
||||
camera_area = camera_width * camera_height
|
||||
|
||||
bb_left, bb_top, bb_right, bb_bottom = obj.obj_data["box"]
|
||||
|
||||
if (
|
||||
camera_config.onvif.autotracking.zooming
|
||||
and not camera_config.onvif.autotracking.zoom_relative
|
||||
):
|
||||
# absolute zooming
|
||||
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
|
||||
# should we make these configurable?
|
||||
edge_threshold = 0.1
|
||||
area_threshold = 0.6
|
||||
|
||||
if 0 < zoom_level <= 1:
|
||||
if (
|
||||
bb_left > edge_threshold * camera_width
|
||||
and bb_right < (1 - edge_threshold) * camera_width
|
||||
and bb_top > edge_threshold * camera_height
|
||||
and bb_bottom < (1 - edge_threshold) * camera_height
|
||||
and obj.obj_data["area"] < area_threshold * camera_area
|
||||
):
|
||||
if self._should_zoom_in(obj, camera):
|
||||
zoom = min(1.0, zoom_level + 0.1)
|
||||
else:
|
||||
zoom = max(0.0, zoom_level - 0.1)
|
||||
|
||||
@ -95,6 +95,7 @@ class OnvifController:
|
||||
)
|
||||
|
||||
# autoracking relative panning/tilting needs a relative zoom value set to 0
|
||||
# if camera supports relative movement
|
||||
if self.config.cameras[camera_name].onvif.autotracking.zooming:
|
||||
zoom_space_id = next(
|
||||
(
|
||||
@ -131,7 +132,13 @@ class OnvifController:
|
||||
"RelativeZoomTranslationSpace"
|
||||
][0]["URI"]
|
||||
except Exception:
|
||||
pass
|
||||
if self.config.cameras[camera_name].onvif.autotracking.zoom_relative:
|
||||
self.config.cameras[
|
||||
camera_name
|
||||
].onvif.autotracking.zoom_relative = False
|
||||
logger.warning(
|
||||
f"Disabling autotracking zooming for {camera_name}: Absolute zoom not supported"
|
||||
)
|
||||
|
||||
if move_request.Speed is None:
|
||||
move_request.Speed = ptz.GetStatus({"ProfileToken": profile.token}).Position
|
||||
@ -173,7 +180,6 @@ class OnvifController:
|
||||
if ptz_config.Spaces and ptz_config.Spaces.RelativeZoomTranslationSpace:
|
||||
supported_features.append("zoom-r")
|
||||
|
||||
# autotracker uses absolute zooming
|
||||
if ptz_config.Spaces and ptz_config.Spaces.AbsoluteZoomPositionSpace:
|
||||
supported_features.append("zoom-a")
|
||||
try:
|
||||
@ -245,7 +251,7 @@ class OnvifController:
|
||||
|
||||
onvif.get_service("ptz").ContinuousMove(move_request)
|
||||
|
||||
def _move_relative(self, camera_name: str, pan, tilt, speed) -> None:
|
||||
def _move_relative(self, camera_name: str, pan, tilt, zoom, speed) -> None:
|
||||
if "pt-r-fov" not in self.cams[camera_name]["features"]:
|
||||
logger.error(f"{camera_name} does not support ONVIF RelativeMove (FOV).")
|
||||
return
|
||||
@ -298,7 +304,14 @@ class OnvifController:
|
||||
move_request.Translation.PanTilt.y = tilt
|
||||
|
||||
if "zoom-r" in self.cams[camera_name]["features"]:
|
||||
move_request.Translation.Zoom.x = 0
|
||||
move_request.Speed = {
|
||||
"PanTilt": {
|
||||
"x": speed,
|
||||
"y": speed,
|
||||
},
|
||||
"Zoom": {"x": speed},
|
||||
}
|
||||
move_request.Translation.Zoom.x = zoom
|
||||
|
||||
onvif.get_service("ptz").RelativeMove(move_request)
|
||||
|
||||
@ -306,6 +319,9 @@ class OnvifController:
|
||||
move_request.Translation.PanTilt.x = 0
|
||||
move_request.Translation.PanTilt.y = 0
|
||||
|
||||
if "zoom-r" in self.cams[camera_name]["features"]:
|
||||
move_request.Translation.Zoom.x = 0
|
||||
|
||||
self.cams[camera_name]["active"] = False
|
||||
|
||||
def _move_to_preset(self, camera_name: str, preset: str) -> None:
|
||||
@ -465,7 +481,7 @@ class OnvifController:
|
||||
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
|
||||
|
||||
if self.config.cameras[camera_name].onvif.autotracking.zooming:
|
||||
# store zoom level as 0 to 1 interpolated from the values of the camera
|
||||
# store absolute zoom level as 0 to 1 interpolated from the values of the camera
|
||||
self.ptz_metrics[camera_name]["ptz_zoom_level"].value = numpy.interp(
|
||||
round(status.Position.Zoom.x, 2),
|
||||
[0, 1],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user