forked from Github/frigate
Convert detectors to factory pattern, ability to set different model for each detector (#4635)
* refactor detectors * move create_detector and DetectorTypeEnum * fixed code formatting * add detector model config models * fix detector unit tests * adjust SharedMemory size to largest detector model shape * fix detector model config defaults * enable auto-discovery of detectors * simplify config * simplify config changes further * update detectors docs; detect detector configs dynamic * add suggested changes * remove custom detector doc * fix grammar, adjust device defaults
This commit is contained in:
@@ -5,9 +5,9 @@ from pydantic import ValidationError
|
||||
from frigate.config import (
|
||||
BirdseyeModeEnum,
|
||||
FrigateConfig,
|
||||
DetectorTypeEnum,
|
||||
)
|
||||
from frigate.util import load_config_with_no_duplicates
|
||||
from frigate.detectors import DetectorTypeEnum
|
||||
from frigate.util import deep_merge, load_config_with_no_duplicates
|
||||
|
||||
|
||||
class TestConfig(unittest.TestCase):
|
||||
@@ -37,6 +37,50 @@ class TestConfig(unittest.TestCase):
|
||||
runtime_config = frigate_config.runtime_config
|
||||
assert "cpu" in runtime_config.detectors.keys()
|
||||
assert runtime_config.detectors["cpu"].type == DetectorTypeEnum.cpu
|
||||
assert runtime_config.detectors["cpu"].model.width == 320
|
||||
|
||||
def test_detector_custom_model_path(self):
|
||||
config = {
|
||||
"detectors": {
|
||||
"cpu": {
|
||||
"type": "cpu",
|
||||
"model": {"path": "/cpu_model.tflite"},
|
||||
},
|
||||
"edgetpu": {
|
||||
"type": "edgetpu",
|
||||
"model": {"path": "/edgetpu_model.tflite", "width": 160},
|
||||
},
|
||||
"openvino": {
|
||||
"type": "openvino",
|
||||
},
|
||||
},
|
||||
"model": {"path": "/default.tflite", "width": 512},
|
||||
}
|
||||
|
||||
frigate_config = FrigateConfig(**(deep_merge(config, self.minimal)))
|
||||
runtime_config = frigate_config.runtime_config
|
||||
|
||||
assert "cpu" in runtime_config.detectors.keys()
|
||||
assert "edgetpu" in runtime_config.detectors.keys()
|
||||
assert "openvino" in runtime_config.detectors.keys()
|
||||
|
||||
assert runtime_config.detectors["cpu"].type == DetectorTypeEnum.cpu
|
||||
assert runtime_config.detectors["edgetpu"].type == DetectorTypeEnum.edgetpu
|
||||
assert runtime_config.detectors["openvino"].type == DetectorTypeEnum.openvino
|
||||
|
||||
assert runtime_config.detectors["cpu"].num_threads == 3
|
||||
assert runtime_config.detectors["edgetpu"].device is None
|
||||
assert runtime_config.detectors["openvino"].device is None
|
||||
|
||||
assert runtime_config.model.path == "/default.tflite"
|
||||
assert runtime_config.detectors["cpu"].model.path == "/cpu_model.tflite"
|
||||
assert runtime_config.detectors["edgetpu"].model.path == "/edgetpu_model.tflite"
|
||||
assert runtime_config.detectors["openvino"].model.path == "/default.tflite"
|
||||
|
||||
assert runtime_config.model.width == 512
|
||||
assert runtime_config.detectors["cpu"].model.width == 512
|
||||
assert runtime_config.detectors["edgetpu"].model.width == 160
|
||||
assert runtime_config.detectors["openvino"].model.width == 512
|
||||
|
||||
def test_invalid_mqtt_config(self):
|
||||
config = {
|
||||
|
||||
@@ -1,53 +1,49 @@
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import numpy as np
|
||||
from frigate.config import DetectorTypeEnum, InputTensorEnum, ModelConfig
|
||||
from pydantic import parse_obj_as
|
||||
|
||||
from frigate.config import DetectorConfig, InputTensorEnum, ModelConfig
|
||||
from frigate.detectors import DetectorTypeEnum
|
||||
import frigate.detectors as detectors
|
||||
import frigate.object_detection
|
||||
|
||||
|
||||
class TestLocalObjectDetector(unittest.TestCase):
|
||||
@patch("frigate.object_detection.EdgeTpuTfl")
|
||||
@patch("frigate.object_detection.CpuTfl")
|
||||
def test_localdetectorprocess_given_type_cpu_should_call_cputfl_init(
|
||||
self, mock_cputfl, mock_edgetputfl
|
||||
):
|
||||
test_cfg = ModelConfig()
|
||||
test_cfg.path = "/test/modelpath"
|
||||
test_obj = frigate.object_detection.LocalObjectDetector(
|
||||
det_type=DetectorTypeEnum.cpu, model_config=test_cfg, num_threads=6
|
||||
)
|
||||
def test_localdetectorprocess_should_only_create_specified_detector_type(self):
|
||||
for det_type in detectors.api_types:
|
||||
with self.subTest(det_type=det_type):
|
||||
with patch.dict(
|
||||
"frigate.detectors.api_types",
|
||||
{det_type: Mock() for det_type in DetectorTypeEnum},
|
||||
):
|
||||
test_cfg = parse_obj_as(
|
||||
DetectorConfig, ({"type": det_type, "model": {}})
|
||||
)
|
||||
test_cfg.model.path = "/test/modelpath"
|
||||
test_obj = frigate.object_detection.LocalObjectDetector(
|
||||
detector_config=test_cfg
|
||||
)
|
||||
|
||||
assert test_obj is not None
|
||||
mock_edgetputfl.assert_not_called()
|
||||
mock_cputfl.assert_called_once_with(model_config=test_cfg, num_threads=6)
|
||||
assert test_obj is not None
|
||||
for api_key, mock_detector in detectors.api_types.items():
|
||||
if test_cfg.type == api_key:
|
||||
mock_detector.assert_called_once_with(test_cfg)
|
||||
else:
|
||||
mock_detector.assert_not_called()
|
||||
|
||||
@patch("frigate.object_detection.EdgeTpuTfl")
|
||||
@patch("frigate.object_detection.CpuTfl")
|
||||
def test_localdetectorprocess_given_type_edgtpu_should_call_edgtpu_init(
|
||||
self, mock_cputfl, mock_edgetputfl
|
||||
):
|
||||
test_cfg = ModelConfig()
|
||||
test_cfg.path = "/test/modelpath"
|
||||
@patch.dict(
|
||||
"frigate.detectors.api_types",
|
||||
{det_type: Mock() for det_type in DetectorTypeEnum},
|
||||
)
|
||||
def test_detect_raw_given_tensor_input_should_return_api_detect_raw_result(self):
|
||||
mock_cputfl = detectors.api_types[DetectorTypeEnum.cpu]
|
||||
|
||||
test_obj = frigate.object_detection.LocalObjectDetector(
|
||||
det_type=DetectorTypeEnum.edgetpu,
|
||||
det_device="usb",
|
||||
model_config=test_cfg,
|
||||
)
|
||||
|
||||
assert test_obj is not None
|
||||
mock_cputfl.assert_not_called()
|
||||
mock_edgetputfl.assert_called_once_with(det_device="usb", model_config=test_cfg)
|
||||
|
||||
@patch("frigate.object_detection.CpuTfl")
|
||||
def test_detect_raw_given_tensor_input_should_return_api_detect_raw_result(
|
||||
self, mock_cputfl
|
||||
):
|
||||
TEST_DATA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
TEST_DETECT_RESULT = np.ndarray([1, 2, 4, 8, 16, 32])
|
||||
test_obj_detect = frigate.object_detection.LocalObjectDetector(
|
||||
det_device=DetectorTypeEnum.cpu
|
||||
detector_config=parse_obj_as(DetectorConfig, {"type": "cpu", "model": {}})
|
||||
)
|
||||
|
||||
mock_det_api = mock_cputfl.return_value
|
||||
@@ -58,18 +54,23 @@ class TestLocalObjectDetector(unittest.TestCase):
|
||||
mock_det_api.detect_raw.assert_called_once_with(tensor_input=TEST_DATA)
|
||||
assert test_result is mock_det_api.detect_raw.return_value
|
||||
|
||||
@patch("frigate.object_detection.CpuTfl")
|
||||
@patch.dict(
|
||||
"frigate.detectors.api_types",
|
||||
{det_type: Mock() for det_type in DetectorTypeEnum},
|
||||
)
|
||||
def test_detect_raw_given_tensor_input_should_call_api_detect_raw_with_transposed_tensor(
|
||||
self, mock_cputfl
|
||||
self,
|
||||
):
|
||||
mock_cputfl = detectors.api_types[DetectorTypeEnum.cpu]
|
||||
|
||||
TEST_DATA = np.zeros((1, 32, 32, 3), np.uint8)
|
||||
TEST_DETECT_RESULT = np.ndarray([1, 2, 4, 8, 16, 32])
|
||||
|
||||
test_cfg = ModelConfig()
|
||||
test_cfg.input_tensor = InputTensorEnum.nchw
|
||||
test_cfg = parse_obj_as(DetectorConfig, {"type": "cpu", "model": {}})
|
||||
test_cfg.model.input_tensor = InputTensorEnum.nchw
|
||||
|
||||
test_obj_detect = frigate.object_detection.LocalObjectDetector(
|
||||
det_device=DetectorTypeEnum.cpu, model_config=test_cfg
|
||||
detector_config=test_cfg
|
||||
)
|
||||
|
||||
mock_det_api = mock_cputfl.return_value
|
||||
@@ -85,11 +86,16 @@ class TestLocalObjectDetector(unittest.TestCase):
|
||||
|
||||
assert test_result is mock_det_api.detect_raw.return_value
|
||||
|
||||
@patch("frigate.object_detection.CpuTfl")
|
||||
@patch.dict(
|
||||
"frigate.detectors.api_types",
|
||||
{det_type: Mock() for det_type in DetectorTypeEnum},
|
||||
)
|
||||
@patch("frigate.object_detection.load_labels")
|
||||
def test_detect_given_tensor_input_should_return_lfiltered_detections(
|
||||
self, mock_load_labels, mock_cputfl
|
||||
self, mock_load_labels
|
||||
):
|
||||
mock_cputfl = detectors.api_types[DetectorTypeEnum.cpu]
|
||||
|
||||
TEST_DATA = np.zeros((1, 32, 32, 3), np.uint8)
|
||||
TEST_DETECT_RAW = [
|
||||
[2, 0.9, 5, 4, 3, 2],
|
||||
@@ -109,9 +115,10 @@ class TestLocalObjectDetector(unittest.TestCase):
|
||||
"label-5",
|
||||
]
|
||||
|
||||
test_cfg = parse_obj_as(DetectorConfig, {"type": "cpu", "model": {}})
|
||||
test_cfg.model = ModelConfig()
|
||||
test_obj_detect = frigate.object_detection.LocalObjectDetector(
|
||||
det_device=DetectorTypeEnum.cpu,
|
||||
model_config=ModelConfig(),
|
||||
detector_config=test_cfg,
|
||||
labels=TEST_LABEL_FILE,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user