Make ptz_metrics into a class

This commit is contained in:
George Tsiamasiotis 2024-09-25 10:02:26 +03:00
parent 618b9ef37b
commit 1e73147359
8 changed files with 172 additions and 184 deletions

View File

@ -17,7 +17,7 @@ from playhouse.sqliteq import SqliteQueueDatabase
import frigate.util as util
from frigate.api.auth import hash_password
from frigate.api.fastapi_app import create_fastapi_app
from frigate.camera import CameraMetrics
from frigate.camera import CameraMetrics, PTZMetrics
from frigate.comms.config_updater import ConfigPublisher
from frigate.comms.dispatcher import Communicator, Dispatcher
from frigate.comms.event_metadata_updater import (
@ -67,7 +67,6 @@ from frigate.stats.emitter import StatsEmitter
from frigate.stats.util import stats_init
from frigate.storage import StorageMaintainer
from frigate.timeline import TimelineProcessor
from frigate.types import PTZMetricsTypes
from frigate.util.builtin import empty_and_close_queue
from frigate.util.object import get_camera_regions_grid
from frigate.version import VERSION
@ -87,7 +86,7 @@ class FrigateApp:
self.detection_shms: list[mp.shared_memory.SharedMemory] = []
self.log_queue: Queue = mp.Queue()
self.camera_metrics: dict[str, CameraMetrics] = {}
self.ptz_metrics: dict[str, PTZMetricsTypes] = {}
self.ptz_metrics: dict[str, PTZMetrics] = {}
self.processes: dict[str, int] = {}
self.region_grids: dict[str, list[list[dict[str, int]]]] = {}
self.config = config
@ -111,36 +110,11 @@ class FrigateApp:
# create camera_metrics
for camera_name in self.config.cameras.keys():
self.camera_metrics[camera_name] = CameraMetrics()
self.ptz_metrics[camera_name] = {
"ptz_autotracker_enabled": mp.Value( # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"i",
self.config.cameras[camera_name].onvif.autotracking.enabled,
),
"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
# from mypy 0.981 onwards
"ptz_stop_time": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"ptz_frame_time": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"ptz_zoom_level": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"ptz_max_zoom": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"ptz_min_zoom": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
}
self.ptz_metrics[camera_name]["ptz_motor_stopped"].set()
self.ptz_metrics[camera_name] = PTZMetrics(
autotracker_enabled=self.config.cameras[
camera_name
].onvif.autotracking.enabled
)
def init_queues(self) -> None:
# Queue for cameras to push tracked objects to

View File

@ -36,3 +36,33 @@ class CameraMetrics:
self.capture_process = None
self.ffmpeg_pid = mp.Value("i", 0)
class PTZMetrics:
autotracker_enabled: Synchronized
start_time: Synchronized
stop_time: Synchronized
frame_time: Synchronized
zoom_level: Synchronized
max_zoom: Synchronized
min_zoom: Synchronized
tracking_active: Event
motor_stopped: Event
reset: Event
def __init__(self, *, autotracker_enabled: bool):
self.autotracker_enabled = mp.Value("i", autotracker_enabled)
self.start_time = mp.Value("d", 0)
self.stop_time = mp.Value("d", 0)
self.frame_time = mp.Value("d", 0)
self.zoom_level = mp.Value("d", 0)
self.max_zoom = mp.Value("d", 0)
self.min_zoom = mp.Value("d", 0)
self.tracking_active = mp.Event()
self.motor_stopped = mp.Event()
self.reset = mp.Event()
self.motor_stopped.set()

View File

@ -6,6 +6,7 @@ import logging
from abc import ABC, abstractmethod
from typing import Any, Callable, Optional
from frigate.camera import PTZMetrics
from frigate.comms.config_updater import ConfigPublisher
from frigate.config import BirdseyeModeEnum, FrigateConfig
from frigate.const import (
@ -19,7 +20,6 @@ from frigate.const import (
)
from frigate.models import Event, Previews, Recordings, ReviewSegment
from frigate.ptz.onvif import OnvifCommandEnum, OnvifController
from frigate.types import PTZMetricsTypes
from frigate.util.object import get_camera_regions_grid
from frigate.util.services import restart_frigate
@ -53,7 +53,7 @@ class Dispatcher:
config: FrigateConfig,
config_updater: ConfigPublisher,
onvif: OnvifController,
ptz_metrics: dict[str, PTZMetricsTypes],
ptz_metrics: dict[str, PTZMetrics],
communicators: list[Communicator],
) -> None:
self.config = config
@ -251,16 +251,16 @@ class Dispatcher:
"Autotracking must be enabled in the config to be turned on via MQTT."
)
return
if not self.ptz_metrics[camera_name]["ptz_autotracker_enabled"].value:
if not self.ptz_metrics[camera_name].autotracker_enabled.value:
logger.info(f"Turning on ptz autotracker for {camera_name}")
self.ptz_metrics[camera_name]["ptz_autotracker_enabled"].value = True
self.ptz_metrics[camera_name]["ptz_start_time"].value = 0
self.ptz_metrics[camera_name].autotracker_enabled.value = True
self.ptz_metrics[camera_name].start_time.value = 0
ptz_autotracker_settings.enabled = True
elif payload == "OFF":
if self.ptz_metrics[camera_name]["ptz_autotracker_enabled"].value:
if self.ptz_metrics[camera_name].autotracker_enabled.value:
logger.info(f"Turning off ptz autotracker for {camera_name}")
self.ptz_metrics[camera_name]["ptz_autotracker_enabled"].value = False
self.ptz_metrics[camera_name]["ptz_start_time"].value = 0
self.ptz_metrics[camera_name].autotracker_enabled.value = False
self.ptz_metrics[camera_name].start_time.value = 0
ptz_autotracker_settings.enabled = False
self.publish(f"{camera_name}/ptz_autotracker/state", payload, retain=True)

View File

@ -18,6 +18,7 @@ from norfair.camera_motion import (
TranslationTransformationGetter,
)
from frigate.camera import PTZMetrics
from frigate.comms.dispatcher import Dispatcher
from frigate.config import CameraConfig, FrigateConfig, ZoomingModeEnum
from frigate.const import (
@ -31,7 +32,6 @@ from frigate.const import (
CONFIG_DIR,
)
from frigate.ptz.onvif import OnvifController
from frigate.types import PTZMetricsTypes
from frigate.util.builtin import update_yaml_file
from frigate.util.image import SharedMemoryFrameManager, intersection_over_union
@ -50,23 +50,23 @@ def ptz_moving_at_frame_time(frame_time, ptz_start_time, ptz_stop_time):
class PtzMotionEstimator:
def __init__(
self, config: CameraConfig, ptz_metrics: dict[str, PTZMetricsTypes]
self, config: CameraConfig, ptz_metrics: dict[str, PTZMetrics]
) -> None:
self.frame_manager = SharedMemoryFrameManager()
self.norfair_motion_estimator = None
self.camera_config = config
self.coord_transformations = None
self.ptz_metrics = ptz_metrics
self.ptz_start_time = self.ptz_metrics["ptz_start_time"]
self.ptz_stop_time = self.ptz_metrics["ptz_stop_time"]
self.ptz_start_time = self.ptz_metrics.start_time
self.ptz_stop_time = self.ptz_metrics.stop_time
self.ptz_metrics["ptz_reset"].set()
self.ptz_metrics.reset.set()
logger.debug(f"{config.name}: Motion estimator init")
def motion_estimator(self, detections, frame_time, camera):
# 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()
if self.ptz_metrics.reset.is_set():
self.ptz_metrics.reset.clear()
# homography is nice (zooming) but slow, translation is pan/tilt only but fast.
if (
@ -148,7 +148,7 @@ class PtzAutoTrackerThread(threading.Thread):
self,
config: FrigateConfig,
onvif: OnvifController,
ptz_metrics: dict[str, PTZMetricsTypes],
ptz_metrics: dict[str, PTZMetrics],
dispatcher: Dispatcher,
stop_event: MpEvent,
) -> None:
@ -182,7 +182,7 @@ class PtzAutoTracker:
self,
config: FrigateConfig,
onvif: OnvifController,
ptz_metrics: PTZMetricsTypes,
ptz_metrics: PTZMetrics,
dispatcher: Dispatcher,
stop_event: MpEvent,
) -> None:
@ -248,7 +248,7 @@ class PtzAutoTracker:
f"Disabling autotracking for {camera}: onvif connection failed"
)
camera_config.onvif.autotracking.enabled = False
self.ptz_metrics[camera]["ptz_autotracker_enabled"].value = False
self.ptz_metrics[camera].autotracker_enabled.value = False
return
if not self.onvif.cams[camera]["init"]:
@ -257,7 +257,7 @@ class PtzAutoTracker:
f"Disabling autotracking for {camera}: Unable to initialize onvif"
)
camera_config.onvif.autotracking.enabled = False
self.ptz_metrics[camera]["ptz_autotracker_enabled"].value = False
self.ptz_metrics[camera].autotracker_enabled.value = False
return
if "pt-r-fov" not in self.onvif.cams[camera]["features"]:
@ -265,7 +265,7 @@ class PtzAutoTracker:
f"Disabling autotracking for {camera}: FOV relative movement not supported"
)
camera_config.onvif.autotracking.enabled = False
self.ptz_metrics[camera]["ptz_autotracker_enabled"].value = False
self.ptz_metrics[camera].autotracker_enabled.value = False
return
move_status_supported = self.onvif.get_service_capabilities(camera)
@ -275,7 +275,7 @@ class PtzAutoTracker:
f"Disabling autotracking for {camera}: ONVIF MoveStatus not supported"
)
camera_config.onvif.autotracking.enabled = False
self.ptz_metrics[camera]["ptz_autotracker_enabled"].value = False
self.ptz_metrics[camera].autotracker_enabled.value = False
return
if self.onvif.cams[camera]["init"]:
@ -295,12 +295,16 @@ class PtzAutoTracker:
float(val)
for val in camera_config.onvif.autotracking.movement_weights
]
self.ptz_metrics[camera][
"ptz_min_zoom"
].value = camera_config.onvif.autotracking.movement_weights[0]
self.ptz_metrics[camera][
"ptz_max_zoom"
].value = camera_config.onvif.autotracking.movement_weights[1]
self.ptz_metrics[
camera
].min_zoom.value = (
camera_config.onvif.autotracking.movement_weights[0]
)
self.ptz_metrics[
camera
].max_zoom.value = (
camera_config.onvif.autotracking.movement_weights[1]
)
self.intercept[camera] = (
camera_config.onvif.autotracking.movement_weights[2]
)
@ -309,7 +313,7 @@ class PtzAutoTracker:
)
else:
camera_config.onvif.autotracking.enabled = False
self.ptz_metrics[camera]["ptz_autotracker_enabled"].value = False
self.ptz_metrics[camera].autotracker_enabled.value = False
logger.warning(
f"Autotracker recalibration is required for {camera}. Disabling autotracking."
)
@ -317,7 +321,7 @@ class PtzAutoTracker:
if camera_config.onvif.autotracking.calibrate_on_startup:
self._calibrate_camera(camera)
self.ptz_metrics[camera]["ptz_tracking_active"].clear()
self.ptz_metrics[camera].tracking_active.clear()
self.dispatcher.publish(f"{camera}/ptz_autotracker/active", "OFF", retain=False)
self.autotracker_init[camera] = True
@ -363,10 +367,10 @@ class PtzAutoTracker:
1,
)
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
zoom_out_values.append(self.ptz_metrics[camera]["ptz_zoom_level"].value)
zoom_out_values.append(self.ptz_metrics[camera].zoom_level.value)
self.onvif._zoom_absolute(
camera,
@ -374,10 +378,10 @@ class PtzAutoTracker:
1,
)
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
zoom_in_values.append(self.ptz_metrics[camera]["ptz_zoom_level"].value)
zoom_in_values.append(self.ptz_metrics[camera].zoom_level.value)
if (
self.config.cameras[camera].onvif.autotracking.zooming
@ -392,12 +396,10 @@ class PtzAutoTracker:
1,
)
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
zoom_out_values.append(
self.ptz_metrics[camera]["ptz_zoom_level"].value
)
zoom_out_values.append(self.ptz_metrics[camera].zoom_level.value)
# relative move to 0.01
self.onvif._move_relative(
@ -408,33 +410,31 @@ class PtzAutoTracker:
1,
)
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
zoom_in_values.append(
self.ptz_metrics[camera]["ptz_zoom_level"].value
)
zoom_in_values.append(self.ptz_metrics[camera].zoom_level.value)
self.ptz_metrics[camera]["ptz_max_zoom"].value = max(zoom_in_values)
self.ptz_metrics[camera]["ptz_min_zoom"].value = min(zoom_out_values)
self.ptz_metrics[camera].max_zoom.value = max(zoom_in_values)
self.ptz_metrics[camera].min_zoom.value = min(zoom_out_values)
logger.debug(
f'{camera}: Calibration values: max zoom: {self.ptz_metrics[camera]["ptz_max_zoom"].value}, min zoom: {self.ptz_metrics[camera]["ptz_min_zoom"].value}'
f"{camera}: Calibration values: max zoom: {self.ptz_metrics[camera].max_zoom.value}, min zoom: {self.ptz_metrics[camera].min_zoom.value}"
)
else:
self.ptz_metrics[camera]["ptz_max_zoom"].value = 1
self.ptz_metrics[camera]["ptz_min_zoom"].value = 0
self.ptz_metrics[camera].max_zoom.value = 1
self.ptz_metrics[camera].min_zoom.value = 0
self.onvif._move_to_preset(
camera,
self.config.cameras[camera].onvif.autotracking.return_preset.lower(),
)
self.ptz_metrics[camera]["ptz_reset"].set()
self.ptz_metrics[camera]["ptz_motor_stopped"].clear()
self.ptz_metrics[camera].reset.set()
self.ptz_metrics[camera].motor_stopped.clear()
# Wait until the camera finishes moving
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
for step in range(num_steps):
@ -445,7 +445,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_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
stop_time = time.time()
@ -462,11 +462,11 @@ class PtzAutoTracker:
camera,
self.config.cameras[camera].onvif.autotracking.return_preset.lower(),
)
self.ptz_metrics[camera]["ptz_reset"].set()
self.ptz_metrics[camera]["ptz_motor_stopped"].clear()
self.ptz_metrics[camera].reset.set()
self.ptz_metrics[camera].motor_stopped.clear()
# Wait until the camera finishes moving
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
logger.info(
@ -512,8 +512,8 @@ class PtzAutoTracker:
self.config.cameras[camera].onvif.autotracking.movement_weights = ", ".join(
str(v)
for v in [
self.ptz_metrics[camera]["ptz_min_zoom"].value,
self.ptz_metrics[camera]["ptz_max_zoom"].value,
self.ptz_metrics[camera].min_zoom.value,
self.ptz_metrics[camera].max_zoom.value,
self.intercept[camera],
*self.move_coefficients[camera],
]
@ -649,8 +649,8 @@ class PtzAutoTracker:
# if we're receiving move requests during a PTZ move, ignore them
if ptz_moving_at_frame_time(
frame_time,
self.ptz_metrics[camera]["ptz_start_time"].value,
self.ptz_metrics[camera]["ptz_stop_time"].value,
self.ptz_metrics[camera].start_time.value,
self.ptz_metrics[camera].stop_time.value,
):
# instead of dequeueing this might be a good place to preemptively move based
# on an estimate - for fast moving objects, etc.
@ -671,19 +671,17 @@ class PtzAutoTracker:
self.onvif._move_relative(camera, pan, tilt, 0, 1)
# Wait until the camera finishes moving
while not self.ptz_metrics[camera][
"ptz_motor_stopped"
].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
if (
zoom > 0
and self.ptz_metrics[camera]["ptz_zoom_level"].value != zoom
and self.ptz_metrics[camera].zoom_level.value != zoom
):
self.onvif._zoom_absolute(camera, zoom, 1)
# Wait until the camera finishes moving
while not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
while not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
if self.config.cameras[camera].onvif.autotracking.movement_weights:
@ -691,7 +689,7 @@ class PtzAutoTracker:
f"{camera}: Predicted movement time: {self._predict_movement_time(camera, pan, tilt)}"
)
logger.debug(
f'{camera}: Actual movement time: {self.ptz_metrics[camera]["ptz_stop_time"].value-self.ptz_metrics[camera]["ptz_start_time"].value}'
f"{camera}: Actual movement time: {self.ptz_metrics[camera].stop_time.value-self.ptz_metrics[camera].start_time.value}"
)
# save metrics for better estimate calculations
@ -709,12 +707,12 @@ class PtzAutoTracker:
{
"pan": pan,
"tilt": tilt,
"start_timestamp": self.ptz_metrics[camera][
"ptz_start_time"
].value,
"end_timestamp": self.ptz_metrics[camera][
"ptz_stop_time"
].value,
"start_timestamp": self.ptz_metrics[
camera
].start_time.value,
"end_timestamp": self.ptz_metrics[
camera
].stop_time.value,
}
)
@ -734,8 +732,8 @@ class PtzAutoTracker:
return clipped, diff
if (
frame_time > self.ptz_metrics[camera]["ptz_start_time"].value
and frame_time > self.ptz_metrics[camera]["ptz_stop_time"].value
frame_time > self.ptz_metrics[camera].start_time.value
and frame_time > self.ptz_metrics[camera].stop_time.value
and not self.move_queue_locks[camera].locked()
):
# we can split up any large moves caused by velocity estimated movements if necessary
@ -954,12 +952,12 @@ class PtzAutoTracker:
)
at_max_zoom = (
self.ptz_metrics[camera]["ptz_zoom_level"].value
== self.ptz_metrics[camera]["ptz_max_zoom"].value
self.ptz_metrics[camera].zoom_level.value
== self.ptz_metrics[camera].max_zoom.value
)
at_min_zoom = (
self.ptz_metrics[camera]["ptz_zoom_level"].value
== self.ptz_metrics[camera]["ptz_min_zoom"].value
self.ptz_metrics[camera].zoom_level.value
== self.ptz_metrics[camera].min_zoom.value
)
# debug zooming
@ -1100,7 +1098,7 @@ class PtzAutoTracker:
zoom = 0
result = None
current_zoom_level = self.ptz_metrics[camera]["ptz_zoom_level"].value
current_zoom_level = self.ptz_metrics[camera].zoom_level.value
target_box = max(
obj.obj_data["box"][2] - obj.obj_data["box"][0],
obj.obj_data["box"][3] - obj.obj_data["box"][1],
@ -1123,8 +1121,8 @@ class PtzAutoTracker:
) is not None:
# divide zoom in 10 increments and always zoom out more than in
level = (
self.ptz_metrics[camera]["ptz_max_zoom"].value
- self.ptz_metrics[camera]["ptz_min_zoom"].value
self.ptz_metrics[camera].max_zoom.value
- self.ptz_metrics[camera].min_zoom.value
) / 20
if result:
zoom = min(1.0, current_zoom_level + level)
@ -1219,7 +1217,7 @@ 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.ptz_metrics[camera].tracking_active.set()
self.dispatcher.publish(
f"{camera}/ptz_autotracker/active", "ON", retain=False
)
@ -1243,8 +1241,8 @@ class PtzAutoTracker:
if not ptz_moving_at_frame_time(
obj.obj_data["frame_time"],
self.ptz_metrics[camera]["ptz_start_time"].value,
self.ptz_metrics[camera]["ptz_stop_time"].value,
self.ptz_metrics[camera].start_time.value,
self.ptz_metrics[camera].stop_time.value,
):
if self.tracked_object_metrics[camera]["below_distance_threshold"]:
logger.debug(
@ -1325,7 +1323,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_motor_stopped"].is_set():
if not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
# return to preset if tracking is over
@ -1334,7 +1332,7 @@ class PtzAutoTracker:
and self.tracked_object_history[camera]
and (
# might want to use a different timestamp here?
self.ptz_metrics[camera]["ptz_frame_time"].value
self.ptz_metrics[camera].frame_time.value
- self.tracked_object_history[camera][-1]["frame_time"]
>= autotracker_config.timeout
)
@ -1348,9 +1346,9 @@ class PtzAutoTracker:
while not self.move_queues[camera].empty():
self.move_queues[camera].get()
self.ptz_metrics[camera]["ptz_motor_stopped"].wait()
self.ptz_metrics[camera].motor_stopped.wait()
logger.debug(
f"{camera}: Time is {self.ptz_metrics[camera]['ptz_frame_time'].value}, returning to preset: {autotracker_config.return_preset}"
f"{camera}: Time is {self.ptz_metrics[camera].frame_time.value}, returning to preset: {autotracker_config.return_preset}"
)
self.onvif._move_to_preset(
camera,
@ -1358,11 +1356,11 @@ class PtzAutoTracker:
)
# update stored zoom level from preset
if not self.ptz_metrics[camera]["ptz_motor_stopped"].is_set():
if not self.ptz_metrics[camera].motor_stopped.is_set():
self.onvif.get_camera_status(camera)
self.ptz_metrics[camera]["ptz_tracking_active"].clear()
self.ptz_metrics[camera].tracking_active.clear()
self.dispatcher.publish(
f"{camera}/ptz_autotracker/active", "OFF", retain=False
)
self.ptz_metrics[camera]["ptz_reset"].set()
self.ptz_metrics[camera].reset.set()

View File

@ -10,8 +10,8 @@ from onvif import ONVIFCamera, ONVIFError
from zeep.exceptions import Fault, TransportError
from zeep.transports import Transport
from frigate.camera import PTZMetrics
from frigate.config import FrigateConfig, ZoomingModeEnum
from frigate.types import PTZMetricsTypes
from frigate.util.builtin import find_by_key
logger = logging.getLogger(__name__)
@ -33,8 +33,10 @@ class OnvifCommandEnum(str, Enum):
class OnvifController:
ptz_metrics: dict[str, PTZMetrics]
def __init__(
self, config: FrigateConfig, ptz_metrics: dict[str, PTZMetricsTypes]
self, config: FrigateConfig, ptz_metrics: dict[str, PTZMetrics]
) -> None:
self.cams: dict[str, ONVIFCamera] = {}
self.config = config
@ -389,14 +391,14 @@ class OnvifController:
return
self.cams[camera_name]["active"] = True
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
self.ptz_metrics[camera_name].motor_stopped.clear()
logger.debug(
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name].frame_time.value}"
)
self.ptz_metrics[camera_name]["ptz_start_time"].value = self.ptz_metrics[
self.ptz_metrics[camera_name].start_time.value = self.ptz_metrics[
camera_name
]["ptz_frame_time"].value
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
].frame_time.value
self.ptz_metrics[camera_name].stop_time.value = 0
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["relative_move_request"]
@ -464,9 +466,9 @@ class OnvifController:
return
self.cams[camera_name]["active"] = True
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
self.ptz_metrics[camera_name].motor_stopped.clear()
self.ptz_metrics[camera_name].start_time.value = 0
self.ptz_metrics[camera_name].stop_time.value = 0
move_request = self.cams[camera_name]["move_request"]
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
preset_token = self.cams[camera_name]["presets"][preset]
@ -515,14 +517,14 @@ class OnvifController:
return
self.cams[camera_name]["active"] = True
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
self.ptz_metrics[camera_name].motor_stopped.clear()
logger.debug(
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name].frame_time.value}"
)
self.ptz_metrics[camera_name]["ptz_start_time"].value = self.ptz_metrics[
self.ptz_metrics[camera_name].start_time.value = self.ptz_metrics[
camera_name
]["ptz_frame_time"].value
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
].frame_time.value
self.ptz_metrics[camera_name].stop_time.value = 0
onvif: ONVIFCamera = self.cams[camera_name]["onvif"]
move_request = self.cams[camera_name]["absolute_move_request"]
@ -653,36 +655,36 @@ class OnvifController:
if pan_tilt_status == "IDLE" and (zoom_status is None or zoom_status == "IDLE"):
self.cams[camera_name]["active"] = False
if not self.ptz_metrics[camera_name]["ptz_motor_stopped"].is_set():
self.ptz_metrics[camera_name]["ptz_motor_stopped"].set()
if not self.ptz_metrics[camera_name].motor_stopped.is_set():
self.ptz_metrics[camera_name].motor_stopped.set()
logger.debug(
f"{camera_name}: PTZ stop time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
f"{camera_name}: PTZ stop time: {self.ptz_metrics[camera_name].frame_time.value}"
)
self.ptz_metrics[camera_name]["ptz_stop_time"].value = self.ptz_metrics[
self.ptz_metrics[camera_name].stop_time.value = self.ptz_metrics[
camera_name
]["ptz_frame_time"].value
].frame_time.value
else:
self.cams[camera_name]["active"] = True
if self.ptz_metrics[camera_name]["ptz_motor_stopped"].is_set():
self.ptz_metrics[camera_name]["ptz_motor_stopped"].clear()
if self.ptz_metrics[camera_name].motor_stopped.is_set():
self.ptz_metrics[camera_name].motor_stopped.clear()
logger.debug(
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name]['ptz_frame_time'].value}"
f"{camera_name}: PTZ start time: {self.ptz_metrics[camera_name].frame_time.value}"
)
self.ptz_metrics[camera_name][
"ptz_start_time"
].value = self.ptz_metrics[camera_name]["ptz_frame_time"].value
self.ptz_metrics[camera_name]["ptz_stop_time"].value = 0
self.ptz_metrics[camera_name].start_time.value = self.ptz_metrics[
camera_name
].frame_time.value
self.ptz_metrics[camera_name].stop_time.value = 0
if (
self.config.cameras[camera_name].onvif.autotracking.zooming
!= ZoomingModeEnum.disabled
):
# 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(
self.ptz_metrics[camera_name].zoom_level.value = numpy.interp(
round(status.Position.Zoom.x, 2),
[0, 1],
[
@ -691,23 +693,23 @@ class OnvifController:
],
)
logger.debug(
f'{camera_name}: Camera zoom level: {self.ptz_metrics[camera_name]["ptz_zoom_level"].value}'
f"{camera_name}: Camera zoom level: {self.ptz_metrics[camera_name].zoom_level.value}"
)
# some hikvision cams won't update MoveStatus, so warn if it hasn't changed
if (
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
> (self.ptz_metrics[camera_name]["ptz_start_time"].value + 10)
and self.ptz_metrics[camera_name]["ptz_stop_time"].value == 0
not self.ptz_metrics[camera_name].motor_stopped.is_set()
and not self.ptz_metrics[camera_name].reset.is_set()
and self.ptz_metrics[camera_name].start_time.value != 0
and self.ptz_metrics[camera_name].frame_time.value
> (self.ptz_metrics[camera_name].start_time.value + 10)
and self.ptz_metrics[camera_name].stop_time.value == 0
):
logger.debug(
f'Start time: {self.ptz_metrics[camera_name]["ptz_start_time"].value}, Stop time: {self.ptz_metrics[camera_name]["ptz_stop_time"].value}, Frame time: {self.ptz_metrics[camera_name]["ptz_frame_time"].value}'
f"Start time: {self.ptz_metrics[camera_name].start_time.value}, Stop time: {self.ptz_metrics[camera_name].stop_time.value}, Frame time: {self.ptz_metrics[camera_name].frame_time.value}"
)
# set the stop time so we don't come back into this again and spam the logs
self.ptz_metrics[camera_name]["ptz_stop_time"].value = self.ptz_metrics[
self.ptz_metrics[camera_name].stop_time.value = self.ptz_metrics[
camera_name
]["ptz_frame_time"].value
].frame_time.value
logger.warning(f"Camera {camera_name} is still in ONVIF 'MOVING' status.")

View File

@ -12,10 +12,10 @@ from norfair import (
)
from norfair.drawing.drawer import Drawer
from frigate.camera import PTZMetrics
from frigate.config import CameraConfig
from frigate.ptz.autotrack import PtzMotionEstimator
from frigate.track import ObjectTracker
from frigate.types import PTZMetricsTypes
from frigate.util.image import intersection_over_union
from frigate.util.object import average_boxes, median_of_boxes
@ -75,7 +75,7 @@ class NorfairTracker(ObjectTracker):
def __init__(
self,
config: CameraConfig,
ptz_metrics: PTZMetricsTypes,
ptz_metrics: PTZMetrics,
):
self.tracked_objects = {}
self.untracked_object_boxes: list[list[int]] = []
@ -85,7 +85,7 @@ class NorfairTracker(ObjectTracker):
self.camera_config = config
self.detect_config = config.detect
self.ptz_metrics = ptz_metrics
self.ptz_autotracker_enabled = ptz_metrics["ptz_autotracker_enabled"]
self.ptz_autotracker_enabled = ptz_metrics.autotracker_enabled
self.ptz_motion_estimator = {}
self.camera_name = config.name
self.track_id_map = {}

View File

@ -1,24 +1,9 @@
from multiprocessing.sharedctypes import Synchronized
from multiprocessing.synchronize import Event
from typing import TypedDict
from frigate.camera import CameraMetrics
from frigate.object_detection import ObjectDetectProcess
class PTZMetricsTypes(TypedDict):
ptz_autotracker_enabled: Synchronized
ptz_tracking_active: Event
ptz_motor_stopped: Event
ptz_reset: Event
ptz_start_time: Synchronized
ptz_stop_time: Synchronized
ptz_frame_time: Synchronized
ptz_zoom_level: Synchronized
ptz_max_zoom: Synchronized
ptz_min_zoom: Synchronized
class StatsTrackingTypes(TypedDict):
camera_metrics: dict[str, CameraMetrics]
detectors: dict[str, ObjectDetectProcess]

View File

@ -11,7 +11,7 @@ import time
import cv2
from setproctitle import setproctitle
from frigate.camera import CameraMetrics
from frigate.camera import CameraMetrics, PTZMetrics
from frigate.comms.config_updater import ConfigSubscriber
from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import CameraConfig, DetectConfig, ModelConfig
@ -29,7 +29,6 @@ from frigate.object_detection import RemoteObjectDetector
from frigate.ptz.autotrack import ptz_moving_at_frame_time
from frigate.track import ObjectTracker
from frigate.track.norfair_tracker import NorfairTracker
from frigate.types import PTZMetricsTypes
from frigate.util.builtin import EventsPerSecond, get_tomorrow_at_time
from frigate.util.image import (
FrameManager,
@ -424,7 +423,7 @@ def track_camera(
result_connection,
detected_objects_queue,
camera_metrics: CameraMetrics,
ptz_metrics,
ptz_metrics: PTZMetrics,
region_grid,
):
stop_event = mp.Event()
@ -548,7 +547,7 @@ def process_frames(
objects_to_track: list[str],
object_filters,
stop_event,
ptz_metrics: PTZMetricsTypes,
ptz_metrics: PTZMetrics,
region_grid,
exit_on_empty: bool = False,
):
@ -589,7 +588,7 @@ def process_frames(
continue
camera_metrics.detection_frame.value = frame_time
ptz_metrics["ptz_frame_time"].value = frame_time
ptz_metrics.frame_time.value = frame_time
frame = frame_manager.get(
f"{camera_name}{frame_time}", (frame_shape[0] * 3 // 2, frame_shape[1])
@ -659,8 +658,8 @@ def process_frames(
# ptz_moving_at_frame_time() always returns False for non-autotracking cameras
if not motion_detector.is_calibrating() and not ptz_moving_at_frame_time(
frame_time,
ptz_metrics["ptz_start_time"].value,
ptz_metrics["ptz_stop_time"].value,
ptz_metrics.start_time.value,
ptz_metrics.stop_time.value,
):
# find motion boxes that are not inside tracked object regions
standalone_motion_boxes = [