mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 10:33:11 +03:00
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* backend * frontend * i18n * docs * add test * clean up * clean up motion detection docs * formatting * make optional
92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
import unittest
|
|
|
|
import numpy as np
|
|
|
|
from frigate.config.camera.motion import MotionConfig
|
|
from frigate.motion.improved_motion import ImprovedMotionDetector
|
|
|
|
|
|
class TestImprovedMotionDetector(unittest.TestCase):
|
|
def setUp(self):
|
|
# small frame for testing; actual frames are grayscale
|
|
self.frame_shape = (100, 100) # height, width
|
|
self.config = MotionConfig()
|
|
# motion detector assumes a rasterized_mask attribute exists on config
|
|
# when update_mask() is called; add one manually by bypassing pydantic.
|
|
object.__setattr__(
|
|
self.config,
|
|
"rasterized_mask",
|
|
np.ones((self.frame_shape[0], self.frame_shape[1]), dtype=np.uint8),
|
|
)
|
|
|
|
# create minimal PTZ metrics stub to satisfy detector checks
|
|
class _Stub:
|
|
def __init__(self, value=False):
|
|
self.value = value
|
|
|
|
def is_set(self):
|
|
return bool(self.value)
|
|
|
|
class DummyPTZ:
|
|
def __init__(self):
|
|
self.autotracker_enabled = _Stub(False)
|
|
self.motor_stopped = _Stub(False)
|
|
self.stop_time = _Stub(0)
|
|
|
|
self.detector = ImprovedMotionDetector(
|
|
self.frame_shape, self.config, fps=30, ptz_metrics=DummyPTZ()
|
|
)
|
|
|
|
# establish a baseline frame (all zeros)
|
|
base_frame = np.zeros(
|
|
(self.frame_shape[0], self.frame_shape[1]), dtype=np.uint8
|
|
)
|
|
self.detector.detect(base_frame)
|
|
|
|
def _half_change_frame(self) -> np.ndarray:
|
|
"""Produce a frame where roughly half of the pixels are different."""
|
|
frame = np.zeros((self.frame_shape[0], self.frame_shape[1]), dtype=np.uint8)
|
|
# flip the top half to white
|
|
frame[: self.frame_shape[0] // 2, :] = 255
|
|
return frame
|
|
|
|
def test_skip_motion_threshold_default(self):
|
|
"""With the default (None) setting, motion should always be reported."""
|
|
frame = self._half_change_frame()
|
|
boxes = self.detector.detect(frame)
|
|
self.assertTrue(
|
|
boxes, "Expected motion boxes when skip threshold is unset (disabled)"
|
|
)
|
|
|
|
def test_skip_motion_threshold_applied(self):
|
|
"""Setting a low skip threshold should prevent any boxes from being returned."""
|
|
# change the config and update the detector reference
|
|
self.config.skip_motion_threshold = 0.4
|
|
self.detector.config = self.config
|
|
self.detector.update_mask()
|
|
|
|
frame = self._half_change_frame()
|
|
boxes = self.detector.detect(frame)
|
|
self.assertEqual(
|
|
boxes,
|
|
[],
|
|
"Motion boxes should be empty when scene change exceeds skip threshold",
|
|
)
|
|
|
|
def test_skip_motion_threshold_does_not_affect_calibration(self):
|
|
"""Even when skipping, the detector should go into calibrating state."""
|
|
self.config.skip_motion_threshold = 0.4
|
|
self.detector.config = self.config
|
|
self.detector.update_mask()
|
|
|
|
frame = self._half_change_frame()
|
|
_ = self.detector.detect(frame)
|
|
self.assertTrue(
|
|
self.detector.calibrating,
|
|
"Detector should be in calibrating state after skip event",
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|