mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-15 07:35:27 +03:00
Make ptz_metrics into a class
This commit is contained in:
parent
618b9ef37b
commit
1e73147359
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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.")
|
||||
|
||||
@ -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 = {}
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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 = [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user