mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 03:35:26 +03:00
prevent estimate clipping when autotracking
This commit is contained in:
parent
8a9b26df4e
commit
e49f034721
@ -221,6 +221,10 @@ Topic to turn the PTZ autotracker for a camera on and off. Expected values are `
|
||||
|
||||
Topic with current state of the PTZ autotracker for a camera. Published values are `ON` and `OFF`.
|
||||
|
||||
### `frigate/<camera_name>/ptz_autotracker/active`
|
||||
|
||||
Topic to determine if PTZ autotracker is actively tracking an object. Published values are `ON` and `OFF`.
|
||||
|
||||
### `frigate/<camera_name>/birdseye/set`
|
||||
|
||||
Topic to turn Birdseye for a camera on and off. Expected values are `ON` and `OFF`. Birdseye mode
|
||||
|
||||
@ -191,7 +191,8 @@ class FrigateApp:
|
||||
"i",
|
||||
self.config.cameras[camera_name].onvif.autotracking.enabled,
|
||||
),
|
||||
"ptz_stopped": mp.Event(),
|
||||
"ptz_tracking_active": mp.Event(),
|
||||
"ptz_motor_stopped": mp.Event(),
|
||||
"ptz_reset": mp.Event(),
|
||||
"ptz_start_time": mp.Value("d", 0.0), # type: ignore[typeddict-item]
|
||||
# issue https://github.com/python/typeshed/issues/8799
|
||||
@ -212,7 +213,7 @@ class FrigateApp:
|
||||
# issue https://github.com/python/typeshed/issues/8799
|
||||
# from mypy 0.981 onwards
|
||||
}
|
||||
self.ptz_metrics[camera_name]["ptz_stopped"].set()
|
||||
self.ptz_metrics[camera_name]["ptz_motor_stopped"].set()
|
||||
self.feature_metrics[camera_name] = {
|
||||
"audio_enabled": mp.Value( # type: ignore[typeddict-item]
|
||||
# issue https://github.com/python/typeshed/issues/8799
|
||||
@ -444,6 +445,7 @@ class FrigateApp:
|
||||
self.config,
|
||||
self.onvif_controller,
|
||||
self.ptz_metrics,
|
||||
self.dispatcher,
|
||||
self.stop_event,
|
||||
)
|
||||
self.ptz_autotracker_thread.start()
|
||||
|
||||
@ -18,6 +18,7 @@ from norfair.camera_motion import (
|
||||
TranslationTransformationGetter,
|
||||
)
|
||||
|
||||
from frigate.comms.dispatcher import Dispatcher
|
||||
from frigate.config import CameraConfig, FrigateConfig, ZoomingModeEnum
|
||||
from frigate.const import (
|
||||
AUTOTRACKING_MAX_AREA_RATIO,
|
||||
@ -144,11 +145,12 @@ class PtzAutoTrackerThread(threading.Thread):
|
||||
config: FrigateConfig,
|
||||
onvif: OnvifController,
|
||||
ptz_metrics: dict[str, PTZMetricsTypes],
|
||||
dispatcher: Dispatcher,
|
||||
stop_event: MpEvent,
|
||||
) -> None:
|
||||
threading.Thread.__init__(self)
|
||||
self.name = "ptz_autotracker"
|
||||
self.ptz_autotracker = PtzAutoTracker(config, onvif, ptz_metrics)
|
||||
self.ptz_autotracker = PtzAutoTracker(config, onvif, ptz_metrics, dispatcher)
|
||||
self.stop_event = stop_event
|
||||
self.config = config
|
||||
|
||||
@ -175,10 +177,12 @@ class PtzAutoTracker:
|
||||
config: FrigateConfig,
|
||||
onvif: OnvifController,
|
||||
ptz_metrics: PTZMetricsTypes,
|
||||
dispatcher: Dispatcher,
|
||||
) -> None:
|
||||
self.config = config
|
||||
self.onvif = onvif
|
||||
self.ptz_metrics = ptz_metrics
|
||||
self.dispatcher = dispatcher
|
||||
self.tracked_object: dict[str, object] = {}
|
||||
self.tracked_object_history: dict[str, object] = {}
|
||||
self.tracked_object_metrics: dict[str, object] = {}
|
||||
@ -338,7 +342,7 @@ class PtzAutoTracker:
|
||||
1,
|
||||
)
|
||||
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
zoom_out_values.append(self.ptz_metrics[camera]["ptz_zoom_level"].value)
|
||||
@ -349,7 +353,7 @@ class PtzAutoTracker:
|
||||
1,
|
||||
)
|
||||
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
zoom_in_values.append(self.ptz_metrics[camera]["ptz_zoom_level"].value)
|
||||
@ -367,7 +371,7 @@ class PtzAutoTracker:
|
||||
1,
|
||||
)
|
||||
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
zoom_out_values.append(
|
||||
@ -383,7 +387,7 @@ class PtzAutoTracker:
|
||||
1,
|
||||
)
|
||||
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
zoom_in_values.append(
|
||||
@ -406,10 +410,10 @@ class PtzAutoTracker:
|
||||
self.config.cameras[camera].onvif.autotracking.return_preset.lower(),
|
||||
)
|
||||
self.ptz_metrics[camera]["ptz_reset"].set()
|
||||
self.ptz_metrics[camera]["ptz_stopped"].clear()
|
||||
self.ptz_metrics[camera]["ptz_motor_stopped"].clear()
|
||||
|
||||
# Wait until the camera finishes moving
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
for step in range(num_steps):
|
||||
@ -420,7 +424,7 @@ class PtzAutoTracker:
|
||||
self.onvif._move_relative(camera, pan, tilt, 0, 1)
|
||||
|
||||
# Wait until the camera finishes moving
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
stop_time = time.time()
|
||||
|
||||
@ -438,10 +442,10 @@ class PtzAutoTracker:
|
||||
self.config.cameras[camera].onvif.autotracking.return_preset.lower(),
|
||||
)
|
||||
self.ptz_metrics[camera]["ptz_reset"].set()
|
||||
self.ptz_metrics[camera]["ptz_stopped"].clear()
|
||||
self.ptz_metrics[camera]["ptz_motor_stopped"].clear()
|
||||
|
||||
# Wait until the camera finishes moving
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
logger.info(
|
||||
@ -606,7 +610,9 @@ class PtzAutoTracker:
|
||||
self.onvif._move_relative(camera, pan, tilt, 0, 1)
|
||||
|
||||
# Wait until the camera finishes moving
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera][
|
||||
"ptz_motor_stopped"
|
||||
].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
if (
|
||||
@ -616,7 +622,7 @@ class PtzAutoTracker:
|
||||
self.onvif._zoom_absolute(camera, zoom, 1)
|
||||
|
||||
# Wait until the camera finishes moving
|
||||
while not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
if self.config.cameras[camera].onvif.autotracking.movement_weights:
|
||||
@ -1118,6 +1124,10 @@ class PtzAutoTracker:
|
||||
logger.debug(
|
||||
f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
|
||||
)
|
||||
self.ptz_metrics[camera]["ptz_tracking_active"].set()
|
||||
self.dispatcher.publish(
|
||||
f"{camera}/ptz_autotracker/active", "ON", retain=False
|
||||
)
|
||||
self.tracked_object[camera] = obj
|
||||
|
||||
self.tracked_object_history[camera].append(copy.deepcopy(obj.obj_data))
|
||||
@ -1220,7 +1230,7 @@ class PtzAutoTracker:
|
||||
if not self.autotracker_init[camera]:
|
||||
self._autotracker_setup(self.config.cameras[camera], camera)
|
||||
# regularly update camera status
|
||||
if not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
if not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
# return to preset if tracking is over
|
||||
@ -1243,7 +1253,7 @@ class PtzAutoTracker:
|
||||
while not self.move_queues[camera].empty():
|
||||
self.move_queues[camera].get()
|
||||
|
||||
self.ptz_metrics[camera]["ptz_stopped"].wait()
|
||||
self.ptz_metrics[camera]["ptz_motor_stopped"].wait()
|
||||
logger.debug(
|
||||
f"{camera}: Time is {self.ptz_metrics[camera]['ptz_frame_time'].value}, returning to preset: {autotracker_config.return_preset}"
|
||||
)
|
||||
@ -1253,7 +1263,11 @@ class PtzAutoTracker:
|
||||
)
|
||||
|
||||
# update stored zoom level from preset
|
||||
if not self.ptz_metrics[camera]["ptz_stopped"].is_set():
|
||||
if not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
|
||||
self.onvif.get_camera_status(camera)
|
||||
|
||||
self.ptz_metrics[camera]["ptz_tracking_active"].clear()
|
||||
self.dispatcher.publish(
|
||||
f"{camera}/ptz_autotracker/active", "OFF", retain=False
|
||||
)
|
||||
self.ptz_metrics[camera]["ptz_reset"].set()
|
||||
|
||||
@ -299,7 +299,7 @@ class OnvifController:
|
||||
return
|
||||
|
||||
self.cams[camera_name]["active"] = True
|
||||
self.ptz_metrics[camera_name]["ptz_stopped"].clear()
|
||||
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
|
||||
logger.debug(
|
||||
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
|
||||
)
|
||||
@ -366,7 +366,7 @@ class OnvifController:
|
||||
return
|
||||
|
||||
self.cams[camera_name]["active"] = True
|
||||
self.ptz_metrics[camera_name]["ptz_stopped"].clear()
|
||||
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
|
||||
self.ptz_metrics[camera_name]["ptz_start_time"].value = 0
|
||||
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
|
||||
move_request = self.cams[camera_name]["move_request"]
|
||||
@ -413,7 +413,7 @@ class OnvifController:
|
||||
return
|
||||
|
||||
self.cams[camera_name]["active"] = True
|
||||
self.ptz_metrics[camera_name]["ptz_stopped"].clear()
|
||||
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
|
||||
logger.debug(
|
||||
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
|
||||
)
|
||||
@ -543,8 +543,8 @@ class OnvifController:
|
||||
zoom_status is None or zoom_status.lower() == "idle"
|
||||
):
|
||||
self.cams[camera_name]["active"] = False
|
||||
if not self.ptz_metrics[camera_name]["ptz_stopped"].is_set():
|
||||
self.ptz_metrics[camera_name]["ptz_stopped"].set()
|
||||
if not self.ptz_metrics[camera_name]["ptz_motor_stopped"].is_set():
|
||||
self.ptz_metrics[camera_name]["ptz_motor_stopped"].set()
|
||||
|
||||
logger.debug(
|
||||
f"{camera_name}: PTZ stop time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
|
||||
@ -555,8 +555,8 @@ class OnvifController:
|
||||
]["ptz_frame_time"].value
|
||||
else:
|
||||
self.cams[camera_name]["active"] = True
|
||||
if self.ptz_metrics[camera_name]["ptz_stopped"].is_set():
|
||||
self.ptz_metrics[camera_name]["ptz_stopped"].clear()
|
||||
if self.ptz_metrics[camera_name]["ptz_motor_stopped"].is_set():
|
||||
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
|
||||
|
||||
logger.debug(
|
||||
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
|
||||
@ -586,7 +586,7 @@ class OnvifController:
|
||||
|
||||
# some hikvision cams won't update MoveStatus, so warn if it hasn't changed
|
||||
if (
|
||||
not self.ptz_metrics[camera_name]["ptz_stopped"].is_set()
|
||||
not self.ptz_metrics[camera_name]["ptz_motor_stopped"].is_set()
|
||||
and not self.ptz_metrics[camera_name]["ptz_reset"].is_set()
|
||||
and self.ptz_metrics[camera_name]["ptz_start_time"].value != 0
|
||||
and self.ptz_metrics[camera_name]["ptz_frame_time"].value
|
||||
|
||||
@ -72,6 +72,7 @@ class NorfairTracker(ObjectTracker):
|
||||
self.detect_config = config.detect
|
||||
self.ptz_metrics = ptz_metrics
|
||||
self.ptz_autotracker_enabled = ptz_metrics["ptz_autotracker_enabled"]
|
||||
self.ptz_tracking_active = ptz_metrics["ptz_tracking_active"]
|
||||
self.ptz_motion_estimator = {}
|
||||
self.camera_name = config.name
|
||||
self.track_id_map = {}
|
||||
@ -275,13 +276,16 @@ class NorfairTracker(ObjectTracker):
|
||||
active_ids = []
|
||||
for t in tracked_objects:
|
||||
estimate = tuple(t.estimate.flatten().astype(int))
|
||||
# keep the estimate within the bounds of the image
|
||||
estimate = (
|
||||
max(0, estimate[0]),
|
||||
max(0, estimate[1]),
|
||||
min(self.detect_config.width - 1, estimate[2]),
|
||||
min(self.detect_config.height - 1, estimate[3]),
|
||||
)
|
||||
if not self.ptz_tracking_active.is_set():
|
||||
# keep the estimate within the bounds of the image
|
||||
# always for non-autotracking cams
|
||||
# and only for autotracking cams when not actively autotracking
|
||||
estimate = (
|
||||
max(0, estimate[0]),
|
||||
max(0, estimate[1]),
|
||||
min(self.detect_config.width - 1, estimate[2]),
|
||||
min(self.detect_config.height - 1, estimate[3]),
|
||||
)
|
||||
obj = {
|
||||
**t.last_detection.data,
|
||||
"estimate": estimate,
|
||||
|
||||
@ -31,7 +31,8 @@ class CameraMetricsTypes(TypedDict):
|
||||
|
||||
class PTZMetricsTypes(TypedDict):
|
||||
ptz_autotracker_enabled: Synchronized
|
||||
ptz_stopped: Event
|
||||
ptz_tracking_active: Event
|
||||
ptz_motor_stopped: Event
|
||||
ptz_reset: Event
|
||||
ptz_start_time: Synchronized
|
||||
ptz_stop_time: Synchronized
|
||||
|
||||
Loading…
Reference in New Issue
Block a user