add plus integration for models (#6328)

This commit is contained in:
Blake Blackshear 2023-04-30 13:32:36 -05:00 committed by GitHub
parent ad52e238ce
commit 9bf98f908d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 166 additions and 63 deletions

View File

@ -5,13 +5,11 @@ title: Frigate+
:::info :::info
Frigate+ is under active development and currently only offers the ability to submit your examples with annotations. Models will be available after enough examples are submitted to train a robust model. It is free to create an account and upload your examples. Frigate+ is under active development. Models are available as a part of an invitation only beta. It is free to create an account and upload/annotate your examples.
::: :::
Frigate+ offers models trained from scratch and specifically designed for the way Frigate NVR analyzes video footage. They offer higher accuracy with less resources. By uploading your own labeled examples, your model can be uniquely tuned for accuracy in your specific conditions. After tuning, performance is evaluated against a broad dataset and real world examples submitted by other Frigate+ users to prevent overfitting. Frigate+ offers models trained from scratch and specifically designed for the way Frigate NVR analyzes video footage. They offer higher accuracy with less resources and include a more relevant set of objects for security cameras. By uploading your own labeled examples, your model can be uniquely tuned for accuracy in your specific conditions. After tuning, performance is evaluated against a broad dataset and real world examples submitted by other Frigate+ users to prevent overfitting.
Custom models also include a more relevant set of objects for security cameras such as person, face, car, license plate, delivery truck, package, dog, cat, deer, and more. Interested in detecting an object unique to you? Upload examples to incorporate your own objects without worrying that you are reducing the accuracy of other object types in the model.
## Setup ## Setup
@ -35,7 +33,7 @@ You cannot use the `environment_vars` section of your configuration file to set
::: :::
### Submit examples ## Submit examples
Once your API key is configured, you can submit examples directly from the events page in Frigate using the `SEND TO FRIGATE+` button. Once your API key is configured, you can submit examples directly from the events page in Frigate using the `SEND TO FRIGATE+` button.
@ -52,3 +50,25 @@ Snapshots must be enabled to be able to submit examples to Frigate+
You can view all of your submitted images at [https://plus.frigate.video](https://plus.frigate.video). Annotations can be added by clicking an image. You can view all of your submitted images at [https://plus.frigate.video](https://plus.frigate.video). Annotations can be added by clicking an image.
![Annotate](/img/annotate.png) ![Annotate](/img/annotate.png)
## Use Models
Models available in Frigate+ can be used with a special model path. No other information needs to be configured for Frigate+ models because it fetches the remaining config from Frigate+ automatically.
```yaml
model:
path: plus://e63b7345cc83a84ed79dedfc99c16616
```
Models are downloaded into the `/config/model_cache` folder and only downloaded if needed.
You can override the labelmap for Frigate+ models like this:
```yaml
model:
path: plus://e63b7345cc83a84ed79dedfc99c16616
labelmap:
3: animal
4: animal
5: animal
```

View File

@ -18,7 +18,14 @@ from frigate.comms.dispatcher import Communicator, Dispatcher
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 FrigateConfig from frigate.config import FrigateConfig
from frigate.const import CACHE_DIR, CLIPS_DIR, CONFIG_DIR, DEFAULT_DB_PATH, RECORD_DIR from frigate.const import (
CACHE_DIR,
CLIPS_DIR,
CONFIG_DIR,
DEFAULT_DB_PATH,
MODEL_CACHE_DIR,
RECORD_DIR,
)
from frigate.object_detection import ObjectDetectProcess from frigate.object_detection import ObjectDetectProcess
from frigate.events import EventCleanup, EventProcessor from frigate.events import EventCleanup, EventProcessor
from frigate.http import create_app from frigate.http import create_app
@ -57,7 +64,7 @@ class FrigateApp:
os.environ[key] = value os.environ[key] = value
def ensure_dirs(self) -> None: def ensure_dirs(self) -> None:
for d in [CONFIG_DIR, RECORD_DIR, CLIPS_DIR, CACHE_DIR]: for d in [CONFIG_DIR, RECORD_DIR, CLIPS_DIR, CACHE_DIR, MODEL_CACHE_DIR]:
if not os.path.exists(d) and not os.path.islink(d): if not os.path.exists(d) and not os.path.islink(d):
logger.info(f"Creating directory: {d}") logger.info(f"Creating directory: {d}")
os.makedirs(d) os.makedirs(d)
@ -81,7 +88,7 @@ class FrigateApp:
config_file = config_file_yaml config_file = config_file_yaml
user_config = FrigateConfig.parse_file(config_file) user_config = FrigateConfig.parse_file(config_file)
self.config = user_config.runtime_config self.config = user_config.runtime_config(self.plus_api)
for camera_name in self.config.cameras.keys(): for camera_name in self.config.cameras.keys():
# create camera_metrics # create camera_metrics
@ -379,6 +386,7 @@ class FrigateApp:
self.init_logger() self.init_logger()
logger.info(f"Starting Frigate ({VERSION})") logger.info(f"Starting Frigate ({VERSION})")
try: try:
self.ensure_dirs()
try: try:
self.init_config() self.init_config()
except Exception as e: except Exception as e:
@ -399,7 +407,6 @@ class FrigateApp:
self.log_process.terminate() self.log_process.terminate()
sys.exit(1) sys.exit(1)
self.set_environment_vars() self.set_environment_vars()
self.ensure_dirs()
self.set_log_levels() self.set_log_levels()
self.init_queues() self.init_queues()
self.init_database() self.init_database()

View File

@ -19,6 +19,7 @@ from frigate.const import (
YAML_EXT, YAML_EXT,
) )
from frigate.detectors.detector_config import BaseDetectorConfig from frigate.detectors.detector_config import BaseDetectorConfig
from frigate.plus import PlusApi
from frigate.util import ( from frigate.util import (
create_mask, create_mask,
deep_merge, deep_merge,
@ -906,8 +907,7 @@ class FrigateConfig(FrigateBaseModel):
title="Global timestamp style configuration.", title="Global timestamp style configuration.",
) )
@property def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
def runtime_config(self) -> FrigateConfig:
"""Merge camera config with globals.""" """Merge camera config with globals."""
config = self.copy(deep=True) config = self.copy(deep=True)
@ -1031,6 +1031,7 @@ class FrigateConfig(FrigateBaseModel):
enabled_labels.update(camera.objects.track) enabled_labels.update(camera.objects.track)
config.model.create_colormap(sorted(enabled_labels)) config.model.create_colormap(sorted(enabled_labels))
config.model.check_and_load_plus_model(plus_api)
for key, detector in config.detectors.items(): for key, detector in config.detectors.items():
detector_config: DetectorConfig = parse_obj_as(DetectorConfig, detector) detector_config: DetectorConfig = parse_obj_as(DetectorConfig, detector)
@ -1063,6 +1064,9 @@ class FrigateConfig(FrigateBaseModel):
merged_model["path"] = "/edgetpu_model.tflite" merged_model["path"] = "/edgetpu_model.tflite"
detector_config.model = ModelConfig.parse_obj(merged_model) detector_config.model = ModelConfig.parse_obj(merged_model)
detector_config.model.check_and_load_plus_model(
plus_api, detector_config.type
)
detector_config.model.compute_model_hash() detector_config.model.compute_model_hash()
config.detectors[key] = detector_config config.detectors[key] = detector_config

View File

@ -1,5 +1,6 @@
CONFIG_DIR = "/config" CONFIG_DIR = "/config"
DEFAULT_DB_PATH = f"{CONFIG_DIR}/frigate.db" DEFAULT_DB_PATH = f"{CONFIG_DIR}/frigate.db"
MODEL_CACHE_DIR = f"{CONFIG_DIR}/model_cache"
BASE_DIR = "/media/frigate" BASE_DIR = "/media/frigate"
CLIPS_DIR = f"{BASE_DIR}/clips" CLIPS_DIR = f"{BASE_DIR}/clips"
RECORD_DIR = f"{BASE_DIR}/recordings" RECORD_DIR = f"{BASE_DIR}/recordings"

View File

@ -1,11 +1,16 @@
import hashlib import hashlib
import json
import logging import logging
from enum import Enum from enum import Enum
import os
from typing import Dict, List, Optional, Tuple, Union, Literal from typing import Dict, List, Optional, Tuple, Union, Literal
import requests
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from pydantic import BaseModel, Extra, Field, validator from pydantic import BaseModel, Extra, Field, validator
from pydantic.fields import PrivateAttr from pydantic.fields import PrivateAttr
from frigate.plus import PlusApi
from frigate.util import load_labels from frigate.util import load_labels
@ -73,6 +78,45 @@ class ModelConfig(BaseModel):
} }
self._colormap = {} self._colormap = {}
def check_and_load_plus_model(
self, plus_api: PlusApi, detector: str = None
) -> None:
if not self.path or not self.path.startswith("plus://"):
return
model_id = self.path[7:]
self.path = f"/config/model_cache/{model_id}"
model_info_path = f"{self.path}.json"
# download the model if it doesn't exist
if not os.path.isfile(self.path):
download_url = plus_api.get_model_download_url(model_id)
r = requests.get(download_url)
with open(self.path, "wb") as f:
f.write(r.content)
# download the model info if it doesn't exist
if not os.path.isfile(model_info_path):
model_info = plus_api.get_model_info(model_id)
with open(model_info_path, "w") as f:
json.dump(model_info, f)
else:
with open(model_info_path, "r") as f:
model_info = json.load(f)
if detector and detector not in model_info["supportedDetectors"]:
raise ValueError(f"Model does not support detector type of {detector}")
self.width = model_info["width"]
self.height = model_info["height"]
self.input_tensor = model_info["inputShape"]
self.input_pixel_format = model_info["pixelFormat"]
self.model_type = model_info["type"]
self._merged_labelmap = {
**{int(key): val for key, val in model_info["labelMap"].items()},
**self.labelmap,
}
def compute_model_hash(self) -> None: def compute_model_hash(self) -> None:
with open(self.path, "rb") as f: with open(self.path, "rb") as f:
file_hash = hashlib.md5() file_hash = hashlib.md5()

View File

@ -866,6 +866,11 @@ def config():
config["plus"] = {"enabled": current_app.plus_api.is_active()} config["plus"] = {"enabled": current_app.plus_api.is_active()}
for detector, detector_config in config["detectors"].items():
detector_config["model"]["labelmap"] = current_app.frigate_config.detectors[
detector
].model.merged_labelmap
return jsonify(config) return jsonify(config)

View File

@ -3,7 +3,7 @@ import json
import logging import logging
import os import os
import re import re
from typing import List from typing import Any, Dict, List
import requests import requests
from frigate.const import PLUS_ENV_VAR, PLUS_API_HOST from frigate.const import PLUS_ENV_VAR, PLUS_API_HOST
from requests.models import Response from requests.models import Response
@ -187,3 +187,24 @@ class PlusApi:
if not r.ok: if not r.ok:
raise Exception(r.text) raise Exception(r.text)
def get_model_download_url(
self,
model_id: str,
) -> str:
r = self._get(f"model/{model_id}/signed_url")
if not r.ok:
raise Exception(r.text)
presigned_url = r.json()
return str(presigned_url.get("url"))
def get_model_info(self, model_id: str) -> Any:
r = self._get(f"model/{model_id}")
if not r.ok:
raise Exception(r.text)
return r.json()

View File

@ -34,7 +34,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**self.minimal) frigate_config = FrigateConfig(**self.minimal)
assert self.minimal == frigate_config.dict(exclude_unset=True) assert self.minimal == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "cpu" in runtime_config.detectors.keys() assert "cpu" in runtime_config.detectors.keys()
assert runtime_config.detectors["cpu"].type == DetectorTypeEnum.cpu assert runtime_config.detectors["cpu"].type == DetectorTypeEnum.cpu
assert runtime_config.detectors["cpu"].model.width == 320 assert runtime_config.detectors["cpu"].model.width == 320
@ -59,7 +59,7 @@ class TestConfig(unittest.TestCase):
} }
frigate_config = FrigateConfig(**(deep_merge(config, self.minimal))) frigate_config = FrigateConfig(**(deep_merge(config, self.minimal)))
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "cpu" in runtime_config.detectors.keys() assert "cpu" in runtime_config.detectors.keys()
assert "edgetpu" in runtime_config.detectors.keys() assert "edgetpu" in runtime_config.detectors.keys()
@ -125,7 +125,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "dog" in runtime_config.cameras["back"].objects.track assert "dog" in runtime_config.cameras["back"].objects.track
def test_override_birdseye(self): def test_override_birdseye(self):
@ -151,7 +151,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert not runtime_config.cameras["back"].birdseye.enabled assert not runtime_config.cameras["back"].birdseye.enabled
assert runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.motion assert runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.motion
@ -177,7 +177,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].birdseye.enabled assert runtime_config.cameras["back"].birdseye.enabled
def test_inherit_birdseye(self): def test_inherit_birdseye(self):
@ -202,7 +202,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].birdseye.enabled assert runtime_config.cameras["back"].birdseye.enabled
assert ( assert (
runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.continuous runtime_config.cameras["back"].birdseye.mode is BirdseyeModeEnum.continuous
@ -231,7 +231,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "cat" in runtime_config.cameras["back"].objects.track assert "cat" in runtime_config.cameras["back"].objects.track
def test_default_object_filters(self): def test_default_object_filters(self):
@ -256,7 +256,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "dog" in runtime_config.cameras["back"].objects.filters assert "dog" in runtime_config.cameras["back"].objects.filters
def test_inherit_object_filters(self): def test_inherit_object_filters(self):
@ -284,7 +284,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "dog" in runtime_config.cameras["back"].objects.filters assert "dog" in runtime_config.cameras["back"].objects.filters
assert runtime_config.cameras["back"].objects.filters["dog"].threshold == 0.7 assert runtime_config.cameras["back"].objects.filters["dog"].threshold == 0.7
@ -313,7 +313,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "dog" in runtime_config.cameras["back"].objects.filters assert "dog" in runtime_config.cameras["back"].objects.filters
assert runtime_config.cameras["back"].objects.filters["dog"].threshold == 0.7 assert runtime_config.cameras["back"].objects.filters["dog"].threshold == 0.7
@ -343,7 +343,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
back_camera = runtime_config.cameras["back"] back_camera = runtime_config.cameras["back"]
assert "dog" in back_camera.objects.filters assert "dog" in back_camera.objects.filters
assert len(back_camera.objects.filters["dog"].raw_mask) == 2 assert len(back_camera.objects.filters["dog"].raw_mask) == 2
@ -374,7 +374,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "-rtsp_transport" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "-rtsp_transport" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
def test_ffmpeg_params_global(self): def test_ffmpeg_params_global(self):
@ -403,7 +403,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
def test_ffmpeg_params_camera(self): def test_ffmpeg_params_camera(self):
@ -433,7 +433,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
assert "test" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "test" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
@ -468,7 +468,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "-re" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
assert "test" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "test" in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
assert "test2" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"] assert "test2" not in runtime_config.cameras["back"].ffmpeg_cmds[0]["cmd"]
@ -498,7 +498,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert ( assert (
runtime_config.cameras["back"].record.events.retain.objects["person"] == 30 runtime_config.cameras["back"].record.events.retain.objects["person"] == 30
) )
@ -576,7 +576,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert isinstance( assert isinstance(
runtime_config.cameras["back"].zones["test"].contour, np.ndarray runtime_config.cameras["back"].zones["test"].contour, np.ndarray
) )
@ -608,7 +608,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
back_camera = runtime_config.cameras["back"] back_camera = runtime_config.cameras["back"]
assert back_camera.record.events.objects is None assert back_camera.record.events.objects is None
assert back_camera.record.events.retain.objects["person"] == 30 assert back_camera.record.events.retain.objects["person"] == 30
@ -639,7 +639,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
ffmpeg_cmds = runtime_config.cameras["back"].ffmpeg_cmds ffmpeg_cmds = runtime_config.cameras["back"].ffmpeg_cmds
assert len(ffmpeg_cmds) == 1 assert len(ffmpeg_cmds) == 1
assert not "clips" in ffmpeg_cmds[0]["roles"] assert not "clips" in ffmpeg_cmds[0]["roles"]
@ -670,7 +670,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].detect.max_disappeared == 5 * 5 assert runtime_config.cameras["back"].detect.max_disappeared == 5 * 5
def test_motion_frame_height_wont_go_below_120(self): def test_motion_frame_height_wont_go_below_120(self):
@ -698,7 +698,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].motion.frame_height == 50 assert runtime_config.cameras["back"].motion.frame_height == 50
def test_motion_contour_area_dynamic(self): def test_motion_contour_area_dynamic(self):
@ -726,7 +726,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert round(runtime_config.cameras["back"].motion.contour_area) == 30 assert round(runtime_config.cameras["back"].motion.contour_area) == 30
def test_merge_labelmap(self): def test_merge_labelmap(self):
@ -755,7 +755,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.model.merged_labelmap[7] == "truck" assert runtime_config.model.merged_labelmap[7] == "truck"
def test_default_labelmap_empty(self): def test_default_labelmap_empty(self):
@ -783,7 +783,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.model.merged_labelmap[0] == "person" assert runtime_config.model.merged_labelmap[0] == "person"
def test_default_labelmap(self): def test_default_labelmap(self):
@ -812,7 +812,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.model.merged_labelmap[0] == "person" assert runtime_config.model.merged_labelmap[0] == "person"
def test_fails_on_invalid_role(self): def test_fails_on_invalid_role(self):
@ -871,7 +871,7 @@ class TestConfig(unittest.TestCase):
} }
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
self.assertRaises(ValueError, lambda: frigate_config.runtime_config) self.assertRaises(ValueError, lambda: frigate_config.runtime_config())
def test_works_on_missing_role_multiple_cams(self): def test_works_on_missing_role_multiple_cams(self):
config = { config = {
@ -919,7 +919,7 @@ class TestConfig(unittest.TestCase):
} }
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
def test_global_detect(self): def test_global_detect(self):
config = { config = {
@ -946,7 +946,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].detect.max_disappeared == 1 assert runtime_config.cameras["back"].detect.max_disappeared == 1
assert runtime_config.cameras["back"].detect.height == 1080 assert runtime_config.cameras["back"].detect.height == 1080
@ -969,7 +969,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].detect.max_disappeared == 25 assert runtime_config.cameras["back"].detect.max_disappeared == 25
assert runtime_config.cameras["back"].detect.height == 720 assert runtime_config.cameras["back"].detect.height == 720
@ -998,7 +998,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].detect.max_disappeared == 1 assert runtime_config.cameras["back"].detect.max_disappeared == 1
assert runtime_config.cameras["back"].detect.height == 1080 assert runtime_config.cameras["back"].detect.height == 1080
assert runtime_config.cameras["back"].detect.width == 1920 assert runtime_config.cameras["back"].detect.width == 1920
@ -1026,7 +1026,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].snapshots.enabled assert runtime_config.cameras["back"].snapshots.enabled
assert runtime_config.cameras["back"].snapshots.height == 100 assert runtime_config.cameras["back"].snapshots.height == 100
@ -1049,7 +1049,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].snapshots.bounding_box assert runtime_config.cameras["back"].snapshots.bounding_box
assert runtime_config.cameras["back"].snapshots.quality == 70 assert runtime_config.cameras["back"].snapshots.quality == 70
@ -1077,7 +1077,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].snapshots.bounding_box == False assert runtime_config.cameras["back"].snapshots.bounding_box == False
assert runtime_config.cameras["back"].snapshots.height == 150 assert runtime_config.cameras["back"].snapshots.height == 150
assert runtime_config.cameras["back"].snapshots.enabled assert runtime_config.cameras["back"].snapshots.enabled
@ -1101,7 +1101,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert not runtime_config.cameras["back"].rtmp.enabled assert not runtime_config.cameras["back"].rtmp.enabled
def test_default_not_rtmp(self): def test_default_not_rtmp(self):
@ -1123,7 +1123,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert not runtime_config.cameras["back"].rtmp.enabled assert not runtime_config.cameras["back"].rtmp.enabled
def test_global_rtmp_merge(self): def test_global_rtmp_merge(self):
@ -1149,7 +1149,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].rtmp.enabled assert runtime_config.cameras["back"].rtmp.enabled
def test_global_rtmp_default(self): def test_global_rtmp_default(self):
@ -1175,7 +1175,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert not runtime_config.cameras["back"].rtmp.enabled assert not runtime_config.cameras["back"].rtmp.enabled
def test_global_jsmpeg(self): def test_global_jsmpeg(self):
@ -1198,7 +1198,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].live.quality == 4 assert runtime_config.cameras["back"].live.quality == 4
def test_default_live(self): def test_default_live(self):
@ -1220,7 +1220,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].live.quality == 8 assert runtime_config.cameras["back"].live.quality == 8
def test_global_live_merge(self): def test_global_live_merge(self):
@ -1246,7 +1246,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].live.quality == 7 assert runtime_config.cameras["back"].live.quality == 7
assert runtime_config.cameras["back"].live.height == 480 assert runtime_config.cameras["back"].live.height == 480
@ -1270,7 +1270,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].timestamp_style.position == "bl" assert runtime_config.cameras["back"].timestamp_style.position == "bl"
def test_default_timestamp_style(self): def test_default_timestamp_style(self):
@ -1292,7 +1292,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].timestamp_style.position == "tl" assert runtime_config.cameras["back"].timestamp_style.position == "tl"
def test_global_timestamp_style_merge(self): def test_global_timestamp_style_merge(self):
@ -1317,7 +1317,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].timestamp_style.position == "bl" assert runtime_config.cameras["back"].timestamp_style.position == "bl"
assert runtime_config.cameras["back"].timestamp_style.thickness == 4 assert runtime_config.cameras["back"].timestamp_style.thickness == 4
@ -1341,7 +1341,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert runtime_config.cameras["back"].snapshots.retain.default == 1.5 assert runtime_config.cameras["back"].snapshots.retain.default == 1.5
def test_fails_on_bad_camera_name(self): def test_fails_on_bad_camera_name(self):
@ -1365,7 +1365,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
self.assertRaises( self.assertRaises(
ValidationError, lambda: frigate_config.runtime_config.cameras ValidationError, lambda: frigate_config.runtime_config().cameras
) )
def test_fails_on_bad_segment_time(self): def test_fails_on_bad_segment_time(self):
@ -1392,7 +1392,8 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
self.assertRaises( self.assertRaises(
ValueError, lambda: frigate_config.runtime_config.ffmpeg.output_args.record ValueError,
lambda: frigate_config.runtime_config().ffmpeg.output_args.record,
) )
def test_fails_zone_defines_untracked_object(self): def test_fails_zone_defines_untracked_object(self):
@ -1421,7 +1422,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
self.assertRaises(ValueError, lambda: frigate_config.runtime_config.cameras) self.assertRaises(ValueError, lambda: frigate_config.runtime_config().cameras)
def test_fails_duplicate_keys(self): def test_fails_duplicate_keys(self):
raw_config = """ raw_config = """
@ -1465,7 +1466,7 @@ class TestConfig(unittest.TestCase):
frigate_config = FrigateConfig(**config) frigate_config = FrigateConfig(**config)
assert config == frigate_config.dict(exclude_unset=True) assert config == frigate_config.dict(exclude_unset=True)
runtime_config = frigate_config.runtime_config runtime_config = frigate_config.runtime_config()
assert "dog" in runtime_config.cameras["back"].objects.filters assert "dog" in runtime_config.cameras["back"].objects.filters
assert runtime_config.cameras["back"].objects.filters["dog"].min_ratio == 0.2 assert runtime_config.cameras["back"].objects.filters["dog"].min_ratio == 0.2
assert runtime_config.cameras["back"].objects.filters["dog"].max_ratio == 10.1 assert runtime_config.cameras["back"].objects.filters["dog"].max_ratio == 10.1

View File

@ -292,7 +292,7 @@ class TestHttp(unittest.TestCase):
def test_config(self): def test_config(self):
app = create_app( app = create_app(
FrigateConfig(**self.minimal_config).runtime_config, FrigateConfig(**self.minimal_config).runtime_config(),
self.db, self.db,
None, None,
None, None,
@ -308,7 +308,7 @@ class TestHttp(unittest.TestCase):
def test_recordings(self): def test_recordings(self):
app = create_app( app = create_app(
FrigateConfig(**self.minimal_config).runtime_config, FrigateConfig(**self.minimal_config).runtime_config(),
self.db, self.db,
None, None,
None, None,
@ -327,7 +327,7 @@ class TestHttp(unittest.TestCase):
@patch("frigate.http.stats_snapshot") @patch("frigate.http.stats_snapshot")
def test_stats(self, mock_stats): def test_stats(self, mock_stats):
app = create_app( app = create_app(
FrigateConfig(**self.minimal_config).runtime_config, FrigateConfig(**self.minimal_config).runtime_config(),
self.db, self.db,
None, None,
None, None,