From d749cf2e6b0e21463e2caa13302262d57f5077ba Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Fri, 15 Apr 2022 05:59:30 -0600 Subject: [PATCH] Allow birdseye to be overridden at the camera level (#3083) * Add camera level processing for birdseye * Add camera level birdseye configruation * Propogate birdseye from global * Update docs to show that birdseye is overridable * Fix incorrect default factory * Update note to indicate values that can be overridden * Cleanup config accessing * Add tests for birdseye config behavior * Fix mistake on test format * Update tests --- docs/docs/configuration/index.md | 1 + frigate/config.py | 11 ++++++ frigate/output.py | 14 +++++--- frigate/test/test_config.py | 57 ++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index a6ab0985f..28be554d5 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -110,6 +110,7 @@ environment_vars: EXAMPLE_VAR: value # Optional: birdseye configuration +# NOTE: Can (enabled, mode) be overridden at the camera level birdseye: # Optional: Enable birdseye view (default: shown below) enabled: True diff --git a/frigate/config.py b/frigate/config.py index 026e93c73..c3830c4af 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -324,6 +324,13 @@ class BirdseyeConfig(FrigateBaseModel): ) +class BirdseyeCameraConfig(FrigateBaseModel): + enabled: bool = Field(default=True, title="Enable birdseye view for camera.") + mode: BirdseyeModeEnum = Field( + default=BirdseyeModeEnum.objects, title="Tracking mode for camera." + ) + + FFMPEG_GLOBAL_ARGS_DEFAULT = ["-hide_banner", "-loglevel", "warning"] FFMPEG_INPUT_ARGS_DEFAULT = [ "-avoid_negative_ts", @@ -539,6 +546,9 @@ class CameraConfig(FrigateBaseModel): detect: DetectConfig = Field( default_factory=DetectConfig, title="Object detection configuration." ) + birdseye: BirdseyeCameraConfig = Field( + default_factory=BirdseyeCameraConfig, title="Birdseye camera configuration." + ) timestamp_style: TimestampStyleConfig = Field( default_factory=TimestampStyleConfig, title="Timestamp style configuration." ) @@ -775,6 +785,7 @@ class FrigateConfig(FrigateBaseModel): # Global config to propegate down to camera level global_config = config.dict( include={ + "birdseye": ..., "record": ..., "snapshots": ..., "live": ..., diff --git a/frigate/output.py b/frigate/output.py index 62c672172..4676dac83 100644 --- a/frigate/output.py +++ b/frigate/output.py @@ -190,14 +190,14 @@ class BirdsEyeFrameManager: channel_dims, ) - def camera_active(self, object_box_count, motion_box_count): - if self.mode == BirdseyeModeEnum.continuous: + def camera_active(self, mode, object_box_count, motion_box_count): + if mode == BirdseyeModeEnum.continuous: return True - if self.mode == BirdseyeModeEnum.motion and motion_box_count > 0: + if mode == BirdseyeModeEnum.motion and motion_box_count > 0: return True - if self.mode == BirdseyeModeEnum.objects and object_box_count > 0: + if mode == BirdseyeModeEnum.objects and object_box_count > 0: return True def update_frame(self): @@ -311,10 +311,14 @@ class BirdsEyeFrameManager: return True def update(self, camera, object_count, motion_count, frame_time, frame) -> bool: + # don't process if birdseye is disabled for this camera + camera_config = self.config.cameras[camera].birdseye + if not camera_config.enabled: + return False # update the last active frame for the camera self.cameras[camera]["current_frame"] = frame_time - if self.camera_active(object_count, motion_count): + if self.camera_active(camera_config.mode, object_count, motion_count): self.cameras[camera]["last_active_frame"] = frame_time now = datetime.datetime.now().timestamp() diff --git a/frigate/test/test_config.py b/frigate/test/test_config.py index 2e30fab4b..83bd94901 100644 --- a/frigate/test/test_config.py +++ b/frigate/test/test_config.py @@ -2,6 +2,7 @@ import unittest import numpy as np from pydantic import ValidationError from frigate.config import ( + BirdseyeModeEnum, FrigateConfig, DetectorTypeEnum, ) @@ -80,6 +81,62 @@ class TestConfig(unittest.TestCase): runtime_config = frigate_config.runtime_config assert "dog" in runtime_config.cameras["back"].objects.track + def test_override_birdseye(self): + config = { + "mqtt": {"host": "mqtt"}, + "birdseye": { "enabled": True, "mode": "continuous" }, + "cameras": { + "back": { + "ffmpeg": { + "inputs": [ + {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} + ] + }, + "detect": { + "height": 1080, + "width": 1920, + "fps": 5, + }, + "birdseye": { + "enabled": False, + "mode": "motion" + }, + } + }, + } + frigate_config = FrigateConfig(**config) + assert config == frigate_config.dict(exclude_unset=True) + + runtime_config = frigate_config.runtime_config + assert not runtime_config.cameras["back"].birdseye.enabled + assert runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.motion + + def test_inherit_birdseye(self): + config = { + "mqtt": {"host": "mqtt"}, + "birdseye": { "enabled": True, "mode": "continuous" }, + "cameras": { + "back": { + "ffmpeg": { + "inputs": [ + {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} + ] + }, + "detect": { + "height": 1080, + "width": 1920, + "fps": 5, + }, + } + }, + } + frigate_config = FrigateConfig(**config) + assert config == frigate_config.dict(exclude_unset=True) + + runtime_config = frigate_config.runtime_config + assert runtime_config.cameras["back"].birdseye.enabled + assert runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.continuous + def test_override_tracked_objects(self): config = { "mqtt": {"host": "mqtt"},