From ca7853c0871bdeda914c277bdf56f91f924622dd Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Fri, 16 Jun 2023 07:32:43 -0500 Subject: [PATCH] Fix max_frames, improve stationary objects in masked areas (#6815) * fix issue with max_frames * dont consider stationary until the threshold * require a stationary interval * try to fix formatter issues --- .devcontainer/devcontainer.json | 8 ++++++-- docs/docs/configuration/index.md | 6 +++--- docs/docs/configuration/stationary_objects.md | 6 +++--- frigate/config.py | 8 ++++---- frigate/object_detection.py | 2 +- frigate/test/test_object_detector.py | 3 ++- frigate/track/norfair_tracker.py | 11 +++++++---- frigate/video.py | 7 ++++--- 8 files changed, 30 insertions(+), 21 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a8264a227..7769737a7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -73,7 +73,11 @@ "isort.args": ["--settings-path=./pyproject.toml"], "[python]": { "editor.defaultFormatter": "ms-python.black-formatter", - "editor.formatOnSave": true + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll": true, + "source.organizeImports": true + } }, "[json][jsonc]": { "editor.defaultFormatter": "esbenp.prettier-vscode" @@ -86,7 +90,7 @@ "editor.tabSize": 2 }, "cSpell.ignoreWords": ["rtmp"], - "cSpell.words": ["preact"] + "cSpell.words": ["preact", "astype", "hwaccel", "mqtt"] } } } diff --git a/docs/docs/configuration/index.md b/docs/docs/configuration/index.md index 94eec0a90..b66ff15f5 100644 --- a/docs/docs/configuration/index.md +++ b/docs/docs/configuration/index.md @@ -206,10 +206,10 @@ detect: max_disappeared: 25 # Optional: Configuration for stationary object tracking stationary: - # Optional: Frequency for confirming stationary objects (default: shown below) - # When set to 0, object detection will not confirm stationary objects until movement is detected. + # Optional: Frequency for confirming stationary objects (default: same as threshold) + # When set to 1, object detection will run to confirm the object still exists on every frame. # If set to 10, object detection will run to confirm the object still exists on every 10th frame. - interval: 0 + interval: 50 # Optional: Number of frames without a position change for an object to be considered stationary (default: 10x the frame rate or 10s) threshold: 50 # Optional: Define a maximum number of frames for tracking a stationary object (default: not set, track forever) diff --git a/docs/docs/configuration/stationary_objects.md b/docs/docs/configuration/stationary_objects.md index deeee9ffd..530c41aa4 100644 --- a/docs/docs/configuration/stationary_objects.md +++ b/docs/docs/configuration/stationary_objects.md @@ -1,6 +1,6 @@ # Stationary Objects -An object is considered stationary when it is being tracked and has been in a very similar position for a certain number of frames. This number is defined in the configuration under `detect -> stationary -> threshold`, and is 10x the frame rate (or 10 seconds) by default. Once an object is considered stationary, it will remain stationary until motion occurs near the object at which point object detection will start running again. If the object changes location, it will be considered active. +An object is considered stationary when it is being tracked and has been in a very similar position for a certain number of frames. This number is defined in the configuration under `detect -> stationary -> threshold`, and is 10x the frame rate (or 10 seconds) by default. Once an object is considered stationary, it will remain stationary until motion occurs within the object at which point object detection will start running again. If the object changes location, it will be considered active. ## Why does it matter if an object is stationary? @@ -13,11 +13,11 @@ The default config is: ```yaml detect: stationary: - interval: 0 + interval: 50 threshold: 50 ``` -`interval` is defined as the frequency for running detection on stationary objects. This means that by default once an object is considered stationary, detection will not be run on it until motion is detected. With `interval > 0`, every nth frames detection will be run to make sure the object is still there. +`interval` is defined as the frequency for running detection on stationary objects. This means that by default once an object is considered stationary, detection will not be run on it until motion is detected or until the interval (every 50th frame by default). With `interval >= 1`, every nth frames detection will be run to make sure the object is still there. NOTE: There is no way to disable stationary object tracking with this value. diff --git a/frigate/config.py b/frigate/config.py index cb9d1f602..9b434ca1e 100644 --- a/frigate/config.py +++ b/frigate/config.py @@ -13,8 +13,6 @@ from pydantic.fields import PrivateAttr from frigate.const import CACHE_DIR, DEFAULT_DB_PATH, REGEX_CAMERA_NAME, YAML_EXT from frigate.detectors import DetectorConfig, ModelConfig -from frigate.detectors.detector_config import InputTensorEnum # noqa: F401 -from frigate.detectors.detector_config import PixelFormatEnum # noqa: F401 from frigate.detectors.detector_config import BaseDetectorConfig from frigate.ffmpeg_presets import ( parse_preset_hardware_acceleration_decode, @@ -251,9 +249,8 @@ class StationaryMaxFramesConfig(FrigateBaseModel): class StationaryConfig(FrigateBaseModel): interval: Optional[int] = Field( - default=0, title="Frame interval for checking stationary objects.", - ge=0, + gt=0, ) threshold: Optional[int] = Field( title="Number of frames without a position change for an object to be considered stationary", @@ -963,6 +960,9 @@ class FrigateConfig(FrigateBaseModel): stationary_threshold = camera_config.detect.fps * 10 if camera_config.detect.stationary.threshold is None: camera_config.detect.stationary.threshold = stationary_threshold + # default to the stationary_threshold if not defined + if camera_config.detect.stationary.interval is None: + camera_config.detect.stationary.interval = stationary_threshold # FFMPEG input substitution for input in camera_config.ffmpeg.inputs: diff --git a/frigate/object_detection.py b/frigate/object_detection.py index 9201dfcd1..0a2a7059c 100644 --- a/frigate/object_detection.py +++ b/frigate/object_detection.py @@ -10,8 +10,8 @@ from abc import ABC, abstractmethod import numpy as np from setproctitle import setproctitle -from frigate.config import InputTensorEnum from frigate.detectors import create_detector +from frigate.detectors.detector_config import InputTensorEnum from frigate.util import EventsPerSecond, SharedMemoryFrameManager, listen, load_labels logger = logging.getLogger(__name__) diff --git a/frigate/test/test_object_detector.py b/frigate/test/test_object_detector.py index 1e1efa957..40a9fac14 100644 --- a/frigate/test/test_object_detector.py +++ b/frigate/test/test_object_detector.py @@ -6,8 +6,9 @@ from pydantic import parse_obj_as import frigate.detectors as detectors import frigate.object_detection -from frigate.config import DetectorConfig, InputTensorEnum, ModelConfig +from frigate.config import DetectorConfig, ModelConfig from frigate.detectors import DetectorTypeEnum +from frigate.detectors.detector_config import InputTensorEnum class TestLocalObjectDetector(unittest.TestCase): diff --git a/frigate/track/norfair_tracker.py b/frigate/track/norfair_tracker.py index bd6312521..b0c4621b4 100644 --- a/frigate/track/norfair_tracker.py +++ b/frigate/track/norfair_tracker.py @@ -91,9 +91,13 @@ class NorfairTracker(ObjectTracker): "ymax": self.detect_config.height, } - def deregister(self, id): + def deregister(self, id, track_id): del self.tracked_objects[id] del self.disappeared[id] + self.tracker.tracked_objects = [ + o for o in self.tracker.tracked_objects if o.global_id != track_id + ] + del self.track_id_map[track_id] # tracks the current position of the object based on the last N bounding boxes # returns False if the object has moved outside its previous position @@ -167,7 +171,7 @@ class NorfairTracker(ObjectTracker): if self.update_position(id, obj["box"]): self.tracked_objects[id]["motionless_count"] += 1 if self.is_expired(id): - self.deregister(id) + self.deregister(id, track_id) return else: # register the first position change and then only increment if @@ -261,8 +265,7 @@ class NorfairTracker(ObjectTracker): # clear expired tracks expired_ids = [k for k in self.track_id_map.keys() if k not in active_ids] for e_id in expired_ids: - self.deregister(self.track_id_map[e_id]) - del self.track_id_map[e_id] + self.deregister(self.track_id_map[e_id], e_id) def debug_draw(self, frame, frame_time): active_detections = [ diff --git a/frigate/video.py b/frigate/video.py index 291347ba3..25ae35664 100755 --- a/frigate/video.py +++ b/frigate/video.py @@ -14,8 +14,9 @@ import cv2 import numpy as np from setproctitle import setproctitle -from frigate.config import CameraConfig, DetectConfig, PixelFormatEnum +from frigate.config import CameraConfig, DetectConfig from frigate.const import CACHE_DIR +from frigate.detectors.detector_config import PixelFormatEnum from frigate.log import LogPipe from frigate.motion import MotionDetector from frigate.motion.improved_motion import ImprovedMotionDetector @@ -769,8 +770,8 @@ def process_frames( stationary_object_ids = [ obj["id"] for obj in object_tracker.tracked_objects.values() - # if there hasn't been motion for 10 frames - if obj["motionless_count"] >= 10 + # if it has exceeded the stationary threshold + if obj["motionless_count"] >= detect_config.stationary.threshold # and it isn't due for a periodic check and ( detect_config.stationary.interval == 0