remove motion and detection camera metrics

This commit is contained in:
Nicolas Mowen 2024-02-16 08:25:00 -07:00
parent e95004c211
commit 154a810592
12 changed files with 94 additions and 124 deletions

View File

@ -22,7 +22,7 @@ from frigate.comms.dispatcher import Communicator, Dispatcher
from frigate.comms.inter_process import InterProcessCommunicator from frigate.comms.inter_process import InterProcessCommunicator
from frigate.comms.mqtt import MqttClient from frigate.comms.mqtt import MqttClient
from frigate.comms.ws import WebSocketClient from frigate.comms.ws import WebSocketClient
from frigate.config import BirdseyeModeEnum, FrigateConfig from frigate.config import FrigateConfig
from frigate.const import ( from frigate.const import (
CACHE_DIR, CACHE_DIR,
CLIPS_DIR, CLIPS_DIR,
@ -129,35 +129,6 @@ class FrigateApp:
# issue https://github.com/python/typeshed/issues/8799 # issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards # from mypy 0.981 onwards
"process_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] "process_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"detection_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].detect.enabled,
),
"motion_enabled": mp.Value("i", True), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards
"improve_contrast_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].motion.improve_contrast,
),
"motion_threshold": 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].motion.threshold,
),
"motion_contour_area": 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].motion.contour_area,
),
"detection_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item] "detection_fps": mp.Value("d", 0.0), # type: ignore[typeddict-item]
# issue https://github.com/python/typeshed/issues/8799 # issue https://github.com/python/typeshed/issues/8799
# from mypy 0.981 onwards # from mypy 0.981 onwards
@ -387,7 +358,6 @@ class FrigateApp:
self.config, self.config,
self.inter_config_updater, self.inter_config_updater,
self.onvif_controller, self.onvif_controller,
self.camera_metrics,
self.ptz_metrics, self.ptz_metrics,
comms, comms,
) )

View File

@ -3,7 +3,7 @@
import multiprocessing as mp import multiprocessing as mp
import os import os
from multiprocessing.synchronize import Event as MpEvent from multiprocessing.synchronize import Event as MpEvent
from typing import Callable, Optional from typing import Optional
import zmq import zmq
@ -46,9 +46,7 @@ class ConfigSubscriber:
def check_for_update(self) -> Optional[tuple[str, any]]: def check_for_update(self) -> Optional[tuple[str, any]]:
"""Returns updated config or None if no update.""" """Returns updated config or None if no update."""
try: try:
topic = self.socket.recv_string( topic = self.socket.recv_string(flags=zmq.NOBLOCK)
flags=zmq.NOBLOCK
)
return (topic, self.socket.recv_pyobj()) return (topic, self.socket.recv_pyobj())
except zmq.ZMQError: except zmq.ZMQError:
return (None, None) return (None, None)

View File

@ -9,7 +9,7 @@ from frigate.config import BirdseyeModeEnum, FrigateConfig
from frigate.const import INSERT_MANY_RECORDINGS, INSERT_PREVIEW, REQUEST_REGION_GRID from frigate.const import INSERT_MANY_RECORDINGS, INSERT_PREVIEW, REQUEST_REGION_GRID
from frigate.models import Previews, Recordings from frigate.models import Previews, Recordings
from frigate.ptz.onvif import OnvifCommandEnum, OnvifController from frigate.ptz.onvif import OnvifCommandEnum, OnvifController
from frigate.types import CameraMetricsTypes, PTZMetricsTypes from frigate.types import PTZMetricsTypes
from frigate.util.object import get_camera_regions_grid from frigate.util.object import get_camera_regions_grid
from frigate.util.services import restart_frigate from frigate.util.services import restart_frigate
@ -43,14 +43,12 @@ class Dispatcher:
config: FrigateConfig, config: FrigateConfig,
config_updater: ConfigPublisher, config_updater: ConfigPublisher,
onvif: OnvifController, onvif: OnvifController,
camera_metrics: dict[str, CameraMetricsTypes],
ptz_metrics: dict[str, PTZMetricsTypes], ptz_metrics: dict[str, PTZMetricsTypes],
communicators: list[Communicator], communicators: list[Communicator],
) -> None: ) -> None:
self.config = config self.config = config
self.config_updater = config_updater self.config_updater = config_updater
self.onvif = onvif self.onvif = onvif
self.camera_metrics = camera_metrics
self.ptz_metrics = ptz_metrics self.ptz_metrics = ptz_metrics
self.comms = communicators self.comms = communicators
@ -119,44 +117,51 @@ class Dispatcher:
def _on_detect_command(self, camera_name: str, payload: str) -> None: def _on_detect_command(self, camera_name: str, payload: str) -> None:
"""Callback for detect topic.""" """Callback for detect topic."""
detect_settings = self.config.cameras[camera_name].detect detect_settings = self.config.cameras[camera_name].detect
motion_settings = self.config.cameras[camera_name].motion
if payload == "ON": if payload == "ON":
if not self.camera_metrics[camera_name]["detection_enabled"].value: if not detect_settings.enabled:
logger.info(f"Turning on detection for {camera_name}") logger.info(f"Turning on detection for {camera_name}")
self.camera_metrics[camera_name]["detection_enabled"].value = True
detect_settings.enabled = True detect_settings.enabled = True
if not self.camera_metrics[camera_name]["motion_enabled"].value: if not motion_settings.enabled:
logger.info( logger.info(
f"Turning on motion for {camera_name} due to detection being enabled." f"Turning on motion for {camera_name} due to detection being enabled."
) )
self.camera_metrics[camera_name]["motion_enabled"].value = True motion_settings.enabled = True
self.config_updater.publish(
f"config/motion/{camera_name}", motion_settings
)
self.publish(f"{camera_name}/motion/state", payload, retain=True) self.publish(f"{camera_name}/motion/state", payload, retain=True)
elif payload == "OFF": elif payload == "OFF":
if self.camera_metrics[camera_name]["detection_enabled"].value: if detect_settings.enabled:
logger.info(f"Turning off detection for {camera_name}") logger.info(f"Turning off detection for {camera_name}")
self.camera_metrics[camera_name]["detection_enabled"].value = False
detect_settings.enabled = False detect_settings.enabled = False
self.config_updater.publish(f"config/detect/{camera_name}", detect_settings)
self.publish(f"{camera_name}/detect/state", payload, retain=True) self.publish(f"{camera_name}/detect/state", payload, retain=True)
def _on_motion_command(self, camera_name: str, payload: str) -> None: def _on_motion_command(self, camera_name: str, payload: str) -> None:
"""Callback for motion topic.""" """Callback for motion topic."""
detect_settings = self.config.cameras[camera_name].detect
motion_settings = self.config.cameras[camera_name].motion
if payload == "ON": if payload == "ON":
if not self.camera_metrics[camera_name]["motion_enabled"].value: if not motion_settings.enabled:
logger.info(f"Turning on motion for {camera_name}") logger.info(f"Turning on motion for {camera_name}")
self.camera_metrics[camera_name]["motion_enabled"].value = True motion_settings.enabled = True
elif payload == "OFF": elif payload == "OFF":
if self.camera_metrics[camera_name]["detection_enabled"].value: if detect_settings.enabled:
logger.error( logger.error(
"Turning off motion is not allowed when detection is enabled." "Turning off motion is not allowed when detection is enabled."
) )
return return
if self.camera_metrics[camera_name]["motion_enabled"].value: if motion_settings.enabled:
logger.info(f"Turning off motion for {camera_name}") logger.info(f"Turning off motion for {camera_name}")
self.camera_metrics[camera_name]["motion_enabled"].value = False motion_settings.enabled = False
self.config_updater.publish(f"config/motion/{camera_name}", motion_settings)
self.publish(f"{camera_name}/motion/state", payload, retain=True) self.publish(f"{camera_name}/motion/state", payload, retain=True)
def _on_motion_improve_contrast_command( def _on_motion_improve_contrast_command(
@ -166,20 +171,15 @@ class Dispatcher:
motion_settings = self.config.cameras[camera_name].motion motion_settings = self.config.cameras[camera_name].motion
if payload == "ON": if payload == "ON":
if not self.camera_metrics[camera_name]["improve_contrast_enabled"].value: if not motion_settings.improve_contrast:
logger.info(f"Turning on improve contrast for {camera_name}") logger.info(f"Turning on improve contrast for {camera_name}")
self.camera_metrics[camera_name][
"improve_contrast_enabled"
].value = True
motion_settings.improve_contrast = True # type: ignore[union-attr] motion_settings.improve_contrast = True # type: ignore[union-attr]
elif payload == "OFF": elif payload == "OFF":
if self.camera_metrics[camera_name]["improve_contrast_enabled"].value: if motion_settings.improve_contrast:
logger.info(f"Turning off improve contrast for {camera_name}") logger.info(f"Turning off improve contrast for {camera_name}")
self.camera_metrics[camera_name][
"improve_contrast_enabled"
].value = False
motion_settings.improve_contrast = False # type: ignore[union-attr] motion_settings.improve_contrast = False # type: ignore[union-attr]
self.config_updater.publish(f"config/motion/{camera_name}", motion_settings)
self.publish(f"{camera_name}/improve_contrast/state", payload, retain=True) self.publish(f"{camera_name}/improve_contrast/state", payload, retain=True)
def _on_ptz_autotracker_command(self, camera_name: str, payload: str) -> None: def _on_ptz_autotracker_command(self, camera_name: str, payload: str) -> None:
@ -218,8 +218,8 @@ class Dispatcher:
motion_settings = self.config.cameras[camera_name].motion motion_settings = self.config.cameras[camera_name].motion
logger.info(f"Setting motion contour area for {camera_name}: {payload}") logger.info(f"Setting motion contour area for {camera_name}: {payload}")
self.camera_metrics[camera_name]["motion_contour_area"].value = payload
motion_settings.contour_area = payload # type: ignore[union-attr] motion_settings.contour_area = payload # type: ignore[union-attr]
self.config_updater.publish(f"config/motion/{camera_name}", motion_settings)
self.publish(f"{camera_name}/motion_contour_area/state", payload, retain=True) self.publish(f"{camera_name}/motion_contour_area/state", payload, retain=True)
def _on_motion_threshold_command(self, camera_name: str, payload: int) -> None: def _on_motion_threshold_command(self, camera_name: str, payload: int) -> None:
@ -232,8 +232,8 @@ class Dispatcher:
motion_settings = self.config.cameras[camera_name].motion motion_settings = self.config.cameras[camera_name].motion
logger.info(f"Setting motion threshold for {camera_name}: {payload}") logger.info(f"Setting motion threshold for {camera_name}: {payload}")
self.camera_metrics[camera_name]["motion_threshold"].value = payload
motion_settings.threshold = payload # type: ignore[union-attr] motion_settings.threshold = payload # type: ignore[union-attr]
self.config_updater.publish(f"config/motion/{camera_name}", motion_settings)
self.publish(f"{camera_name}/motion_threshold/state", payload, retain=True) self.publish(f"{camera_name}/motion_threshold/state", payload, retain=True)
def _on_audio_command(self, camera_name: str, payload: str) -> None: def _on_audio_command(self, camera_name: str, payload: str) -> None:
@ -255,9 +255,7 @@ class Dispatcher:
logger.info(f"Turning off audio detection for {camera_name}") logger.info(f"Turning off audio detection for {camera_name}")
audio_settings.enabled = False audio_settings.enabled = False
self.config_updater.publish( self.config_updater.publish(f"config/audio/{camera_name}", audio_settings)
f"config/audio/{camera_name}", self.config.cameras[camera_name].audio
)
self.publish(f"{camera_name}/audio/state", payload, retain=True) self.publish(f"{camera_name}/audio/state", payload, retain=True)
def _on_recordings_command(self, camera_name: str, payload: str) -> None: def _on_recordings_command(self, camera_name: str, payload: str) -> None:
@ -279,9 +277,7 @@ class Dispatcher:
logger.info(f"Turning off recordings for {camera_name}") logger.info(f"Turning off recordings for {camera_name}")
record_settings.enabled = False record_settings.enabled = False
self.config_updater.publish( self.config_updater.publish(f"config/record/{camera_name}", record_settings)
f"config/record/{camera_name}", self.config.cameras[camera_name].record
)
self.publish(f"{camera_name}/recordings/state", payload, retain=True) self.publish(f"{camera_name}/recordings/state", payload, retain=True)
def _on_snapshots_command(self, camera_name: str, payload: str) -> None: def _on_snapshots_command(self, camera_name: str, payload: str) -> None:
@ -328,9 +324,7 @@ class Dispatcher:
logger.info(f"Turning off birdseye for {camera_name}") logger.info(f"Turning off birdseye for {camera_name}")
birdseye_settings.enabled = False birdseye_settings.enabled = False
self.config_updater.publish( self.config_updater.publish(f"config/birdseye/{camera_name}", birdseye_settings)
f"config/birdseye/{camera_name}", self.config.cameras[camera_name].birdseye
)
self.publish(f"{camera_name}/birdseye/state", payload, retain=True) self.publish(f"{camera_name}/birdseye/state", payload, retain=True)
def _on_birdseye_mode_command(self, camera_name: str, payload: str) -> None: def _on_birdseye_mode_command(self, camera_name: str, payload: str) -> None:
@ -340,18 +334,16 @@ class Dispatcher:
logger.info(f"Invalid birdseye_mode command: {payload}") logger.info(f"Invalid birdseye_mode command: {payload}")
return return
birdseye_config = self.config.cameras[camera_name].birdseye birdseye_settings = self.config.cameras[camera_name].birdseye
if not birdseye_config.enabled: if not birdseye_settings.enabled:
logger.info(f"Birdseye mode not enabled for {camera_name}") logger.info(f"Birdseye mode not enabled for {camera_name}")
return return
birdseye_config.mode = BirdseyeModeEnum(payload.lower()) birdseye_settings.mode = BirdseyeModeEnum(payload.lower())
logger.info( logger.info(
f"Setting birdseye mode for {camera_name} to {birdseye_config.mode}" f"Setting birdseye mode for {camera_name} to {birdseye_settings.mode}"
) )
self.config_updater.publish( self.config_updater.publish(f"config/birdseye/{camera_name}", birdseye_settings)
f"config/birdseye/{camera_name}", self.config.cameras[camera_name].birdseye
)
self.publish(f"{camera_name}/birdseye_mode/state", payload, retain=True) self.publish(f"{camera_name}/birdseye_mode/state", payload, retain=True)

View File

@ -32,7 +32,7 @@ class InterProcessCommunicator(Communicator):
self.reader_thread.start() self.reader_thread.start()
def read(self) -> None: def read(self) -> None:
while not self.stop_event.wait(0.5): while not self.stop_event.wait(0.1):
while True: # load all messages that are queued while True: # load all messages that are queued
try: try:
(topic, value) = self.socket.recv_pyobj(flags=zmq.NOBLOCK) (topic, value) = self.socket.recv_pyobj(flags=zmq.NOBLOCK)

View File

@ -300,6 +300,7 @@ class RecordConfig(FrigateBaseModel):
class MotionConfig(FrigateBaseModel): class MotionConfig(FrigateBaseModel):
enabled: bool = Field(default=True, title="Enable motion on all cameras.")
threshold: int = Field( threshold: int = Field(
default=30, default=30,
title="Motion detection threshold (1-255).", title="Motion detection threshold (1-255).",
@ -321,6 +322,9 @@ class MotionConfig(FrigateBaseModel):
default=30, default=30,
title="Delay for updating MQTT with no motion detected.", title="Delay for updating MQTT with no motion detected.",
) )
enabled_in_config: Optional[bool] = Field(
title="Keep track of original state of motion detection."
)
class RuntimeMotionConfig(MotionConfig): class RuntimeMotionConfig(MotionConfig):
@ -1041,6 +1045,14 @@ def verify_autotrack_zones(camera_config: CameraConfig) -> ValueError | None:
) )
def verify_motion_and_detect(camera_config: CameraConfig) -> ValueError | None:
"""Verify that required_zones are specified when autotracking is enabled."""
if camera_config.detect.enabled and not camera_config.motion.enabled:
raise ValueError(
f"Camera {camera_config.name} has motion detection disabled and object detection enabled but object detection requires motion detection ."
)
class FrigateConfig(FrigateBaseModel): class FrigateConfig(FrigateBaseModel):
mqtt: MqttConfig = Field(title="MQTT Configuration.") mqtt: MqttConfig = Field(title="MQTT Configuration.")
database: DatabaseConfig = Field( database: DatabaseConfig = Field(
@ -1202,8 +1214,8 @@ class FrigateConfig(FrigateBaseModel):
**FRIGATE_ENV_VARS **FRIGATE_ENV_VARS
) )
# set config pre-value # set config pre-value
camera_config.record.enabled_in_config = camera_config.record.enabled
camera_config.audio.enabled_in_config = camera_config.audio.enabled camera_config.audio.enabled_in_config = camera_config.audio.enabled
camera_config.record.enabled_in_config = camera_config.record.enabled
camera_config.onvif.autotracking.enabled_in_config = ( camera_config.onvif.autotracking.enabled_in_config = (
camera_config.onvif.autotracking.enabled camera_config.onvif.autotracking.enabled
) )
@ -1250,6 +1262,7 @@ class FrigateConfig(FrigateBaseModel):
raw_mask=camera_config.motion.mask, raw_mask=camera_config.motion.mask,
**camera_config.motion.dict(exclude_unset=True), **camera_config.motion.dict(exclude_unset=True),
) )
camera_config.motion.enabled_in_config = camera_config.motion.enabled
# Set live view stream if none is set # Set live view stream if none is set
if not camera_config.live.stream_name: if not camera_config.live.stream_name:
@ -1261,6 +1274,7 @@ class FrigateConfig(FrigateBaseModel):
verify_recording_segments_setup_with_reasonable_time(camera_config) verify_recording_segments_setup_with_reasonable_time(camera_config)
verify_zone_objects_are_tracked(camera_config) verify_zone_objects_are_tracked(camera_config)
verify_autotrack_zones(camera_config) verify_autotrack_zones(camera_config)
verify_motion_and_detect(camera_config)
# generate the ffmpeg commands # generate the ffmpeg commands
camera_config.create_ffmpeg_cmds() camera_config.create_ffmpeg_cmds()

View File

@ -338,9 +338,10 @@ class AudioEventMaintainer(threading.Thread):
while not self.stop_event.is_set(): while not self.stop_event.is_set():
# check if there is an updated config # check if there is an updated config
updated_topic, updated_audio_config = ( (
self.config_subscriber.check_for_update() updated_topic,
) updated_audio_config,
) = self.config_subscriber.check_for_update()
if updated_topic: if updated_topic:
self.config.audio = updated_audio_config self.config.audio = updated_audio_config

View File

@ -5,6 +5,7 @@ import imutils
import numpy as np import numpy as np
from scipy.ndimage import gaussian_filter from scipy.ndimage import gaussian_filter
from frigate.comms.config_updater import ConfigSubscriber
from frigate.config import MotionConfig from frigate.config import MotionConfig
from frigate.motion import MotionDetector from frigate.motion import MotionDetector
@ -17,9 +18,6 @@ class ImprovedMotionDetector(MotionDetector):
frame_shape, frame_shape,
config: MotionConfig, config: MotionConfig,
fps: int, fps: int,
improve_contrast,
threshold,
contour_area,
name="improved", name="improved",
blur_radius=1, blur_radius=1,
interpolation=cv2.INTER_NEAREST, interpolation=cv2.INTER_NEAREST,
@ -44,14 +42,12 @@ class ImprovedMotionDetector(MotionDetector):
self.mask = np.where(resized_mask == [0]) self.mask = np.where(resized_mask == [0])
self.save_images = False self.save_images = False
self.calibrating = True self.calibrating = True
self.improve_contrast = improve_contrast
self.threshold = threshold
self.contour_area = contour_area
self.blur_radius = blur_radius self.blur_radius = blur_radius
self.interpolation = interpolation self.interpolation = interpolation
self.contrast_values = np.zeros((contrast_frame_history, 2), np.uint8) self.contrast_values = np.zeros((contrast_frame_history, 2), np.uint8)
self.contrast_values[:, 1:2] = 255 self.contrast_values[:, 1:2] = 255
self.contrast_values_index = 0 self.contrast_values_index = 0
self.config_subscriber = ConfigSubscriber(f"config/motion/{name}")
def is_calibrating(self): def is_calibrating(self):
return self.calibrating return self.calibrating
@ -59,6 +55,15 @@ class ImprovedMotionDetector(MotionDetector):
def detect(self, frame): def detect(self, frame):
motion_boxes = [] motion_boxes = []
# check for updated motion config
_, updated_motion_config = self.config_subscriber.check_for_update()
if updated_motion_config:
self.config = updated_motion_config
if not self.config.enabled:
return motion_boxes
gray = frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]] gray = frame[0 : self.frame_shape[0], 0 : self.frame_shape[1]]
# resize frame # resize frame
@ -72,7 +77,7 @@ class ImprovedMotionDetector(MotionDetector):
resized_saved = resized_frame.copy() resized_saved = resized_frame.copy()
# Improve contrast # Improve contrast
if self.improve_contrast.value: if self.config.improve_contrast:
# TODO tracking moving average of min/max to avoid sudden contrast changes # TODO tracking moving average of min/max to avoid sudden contrast changes
minval = np.percentile(resized_frame, 4).astype(np.uint8) minval = np.percentile(resized_frame, 4).astype(np.uint8)
maxval = np.percentile(resized_frame, 96).astype(np.uint8) maxval = np.percentile(resized_frame, 96).astype(np.uint8)
@ -110,7 +115,7 @@ class ImprovedMotionDetector(MotionDetector):
# compute the threshold image for the current frame # compute the threshold image for the current frame
thresh = cv2.threshold( thresh = cv2.threshold(
frameDelta, self.threshold.value, 255, cv2.THRESH_BINARY frameDelta, self.config.threshold, 255, cv2.THRESH_BINARY
)[1] )[1]
# dilate the thresholded image to fill in holes, then find contours # dilate the thresholded image to fill in holes, then find contours
@ -127,7 +132,7 @@ class ImprovedMotionDetector(MotionDetector):
# if the contour is big enough, count it as motion # if the contour is big enough, count it as motion
contour_area = cv2.contourArea(c) contour_area = cv2.contourArea(c)
total_contour_area += contour_area total_contour_area += contour_area
if contour_area > self.contour_area.value: if contour_area > self.config.contour_area:
x, y, w, h = cv2.boundingRect(c) x, y, w, h = cv2.boundingRect(c)
motion_boxes.append( motion_boxes.append(
( (
@ -170,9 +175,11 @@ class ImprovedMotionDetector(MotionDetector):
] ]
cv2.imwrite( cv2.imwrite(
f"debug/frames/{self.name}-{self.frame_counter}.jpg", f"debug/frames/{self.name}-{self.frame_counter}.jpg",
cv2.hconcat(frames) (
if self.frame_shape[0] > self.frame_shape[1] cv2.hconcat(frames)
else cv2.vconcat(frames), if self.frame_shape[0] > self.frame_shape[1]
else cv2.vconcat(frames)
),
) )
if len(motion_boxes) > 0: if len(motion_boxes) > 0:

View File

@ -433,10 +433,7 @@ class BirdsEyeFrameManager:
# check if we need to reset the layout because there is a different number of cameras # check if we need to reset the layout because there is a different number of cameras
if len(self.active_cameras) - len(active_cameras) == 0: if len(self.active_cameras) - len(active_cameras) == 0:
if ( if len(self.active_cameras) == 1 and self.active_cameras != active_cameras:
len(self.active_cameras) == 1
and self.active_cameras[0] == active_cameras[0]
):
reset_layout = True reset_layout = True
elif max_camera_refresh: elif max_camera_refresh:
reset_layout = True reset_layout = True
@ -758,9 +755,10 @@ class Birdseye:
) -> None: ) -> None:
# check if there is an updated config # check if there is an updated config
while True: while True:
updated_topic, updated_birdseye_config = ( (
self.config_subscriber.check_for_update() updated_topic,
) updated_birdseye_config,
) = self.config_subscriber.check_for_update()
if not updated_topic: if not updated_topic:
break break

View File

@ -444,9 +444,10 @@ class RecordingMaintainer(threading.Thread):
# check if there is an updated config # check if there is an updated config
while True: while True:
updated_topic, updated_record_config = ( (
self.config_subscriber.check_for_update() updated_topic,
) updated_record_config,
) = self.config_subscriber.check_for_update()
if not updated_topic: if not updated_topic:
break break

View File

@ -265,7 +265,7 @@ def stats_snapshot(
"process_fps": round(camera_stats["process_fps"].value, 2), "process_fps": round(camera_stats["process_fps"].value, 2),
"skipped_fps": round(camera_stats["skipped_fps"].value, 2), "skipped_fps": round(camera_stats["skipped_fps"].value, 2),
"detection_fps": round(camera_stats["detection_fps"].value, 2), "detection_fps": round(camera_stats["detection_fps"].value, 2),
"detection_enabled": camera_stats["detection_enabled"].value, "detection_enabled": config.cameras[name].detect.enabled,
"pid": pid, "pid": pid,
"capture_pid": cpid, "capture_pid": cpid,
"ffmpeg_pid": ffmpeg_pid, "ffmpeg_pid": ffmpeg_pid,

View File

@ -10,23 +10,16 @@ from frigate.object_detection import ObjectDetectProcess
class CameraMetricsTypes(TypedDict): class CameraMetricsTypes(TypedDict):
camera_fps: Synchronized camera_fps: Synchronized
capture_process: Optional[Process] capture_process: Optional[Process]
detection_enabled: Synchronized
detection_fps: Synchronized detection_fps: Synchronized
detection_frame: Synchronized detection_frame: Synchronized
ffmpeg_pid: Synchronized ffmpeg_pid: Synchronized
frame_queue: Queue frame_queue: Queue
motion_enabled: Synchronized
improve_contrast_enabled: Synchronized
motion_threshold: Synchronized
motion_contour_area: Synchronized
process: Optional[Process] process: Optional[Process]
process_fps: Synchronized process_fps: Synchronized
read_start: Synchronized read_start: Synchronized
skipped_fps: Synchronized skipped_fps: Synchronized
audio_rms: Synchronized audio_rms: Synchronized
audio_dBFS: Synchronized audio_dBFS: Synchronized
birdseye_enabled: Synchronized
birdseye_mode: Synchronized
class PTZMetricsTypes(TypedDict): class PTZMetricsTypes(TypedDict):

View File

@ -11,6 +11,7 @@ import time
import cv2 import cv2
from setproctitle import setproctitle from setproctitle import setproctitle
from frigate.comms.config_updater import ConfigSubscriber
from frigate.comms.inter_process import InterProcessRequestor from frigate.comms.inter_process import InterProcessRequestor
from frigate.config import CameraConfig, DetectConfig, ModelConfig from frigate.config import CameraConfig, DetectConfig, ModelConfig
from frigate.const import ( from frigate.const import (
@ -406,11 +407,6 @@ def track_camera(
listen() listen()
frame_queue = process_info["frame_queue"] frame_queue = process_info["frame_queue"]
detection_enabled = process_info["detection_enabled"]
motion_enabled = process_info["motion_enabled"]
improve_contrast_enabled = process_info["improve_contrast_enabled"]
motion_threshold = process_info["motion_threshold"]
motion_contour_area = process_info["motion_contour_area"]
frame_shape = config.frame_shape frame_shape = config.frame_shape
objects_to_track = config.objects.track objects_to_track = config.objects.track
@ -420,9 +416,6 @@ def track_camera(
frame_shape, frame_shape,
config.motion, config.motion,
config.detect.fps, config.detect.fps,
improve_contrast_enabled,
motion_threshold,
motion_contour_area,
) )
object_detector = RemoteObjectDetector( object_detector = RemoteObjectDetector(
name, labelmap, detection_queue, result_connection, model_config, stop_event name, labelmap, detection_queue, result_connection, model_config, stop_event
@ -450,8 +443,6 @@ def track_camera(
process_info, process_info,
objects_to_track, objects_to_track,
object_filters, object_filters,
detection_enabled,
motion_enabled,
stop_event, stop_event,
ptz_metrics, ptz_metrics,
region_grid, region_grid,
@ -519,8 +510,6 @@ def process_frames(
process_info: dict, process_info: dict,
objects_to_track: list[str], objects_to_track: list[str],
object_filters, object_filters,
detection_enabled: mp.Value,
motion_enabled: mp.Value,
stop_event, stop_event,
ptz_metrics: PTZMetricsTypes, ptz_metrics: PTZMetricsTypes,
region_grid, region_grid,
@ -530,6 +519,7 @@ def process_frames(
detection_fps = process_info["detection_fps"] detection_fps = process_info["detection_fps"]
current_frame_time = process_info["detection_frame"] current_frame_time = process_info["detection_frame"]
next_region_update = get_tomorrow_at_time(2) next_region_update = get_tomorrow_at_time(2)
config_subscriber = ConfigSubscriber(f"config/detect/{camera_name}")
fps_tracker = EventsPerSecond() fps_tracker = EventsPerSecond()
fps_tracker.start() fps_tracker.start()
@ -540,6 +530,12 @@ def process_frames(
region_min_size = get_min_region_size(model_config) region_min_size = get_min_region_size(model_config)
while not stop_event.is_set(): while not stop_event.is_set():
# check for updated detect config
_, updated_detect_config = config_subscriber.check_for_update()
if updated_detect_config:
detect_config = updated_detect_config
if ( if (
datetime.datetime.now().astimezone(datetime.timezone.utc) datetime.datetime.now().astimezone(datetime.timezone.utc)
> next_region_update > next_region_update
@ -570,13 +566,13 @@ def process_frames(
continue continue
# look for motion if enabled # look for motion if enabled
motion_boxes = motion_detector.detect(frame) if motion_enabled.value else [] motion_boxes = motion_detector.detect(frame)
regions = [] regions = []
consolidated_detections = [] consolidated_detections = []
# if detection is disabled # if detection is disabled
if not detection_enabled.value: if not detect_config.enabled:
object_tracker.match_and_update(frame_time, []) object_tracker.match_and_update(frame_time, [])
else: else:
# get stationary object ids # get stationary object ids