forked from Github/frigate
Compare commits
12 Commits
v0.15.0-be
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
727bddac03 | ||
|
|
d57a61b50f | ||
|
|
4fc9106c17 | ||
|
|
38e098ca31 | ||
|
|
e7ad38d827 | ||
|
|
a1ce9aacf2 | ||
|
|
322b847356 | ||
|
|
98338e4c7f | ||
|
|
171a89f37b | ||
|
|
8114b541a8 | ||
|
|
c48396c5c6 | ||
|
|
00371546a3 |
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
|||||||
${{ steps.setup.outputs.image-name }}-standard-arm64
|
${{ steps.setup.outputs.image-name }}-standard-arm64
|
||||||
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
cache-from: type=registry,ref=${{ steps.setup.outputs.cache-name }}-arm64
|
||||||
- name: Build and push RPi build
|
- name: Build and push RPi build
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: rpi
|
targets: rpi
|
||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
BASE_IMAGE: timongentzsch/l4t-ubuntu20-opencv:latest
|
BASE_IMAGE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||||
SLIM_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
SLIM_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||||
TRT_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
TRT_BASE: timongentzsch/l4t-ubuntu20-opencv:latest
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: tensorrt
|
targets: tensorrt
|
||||||
@@ -122,7 +122,7 @@ jobs:
|
|||||||
BASE_IMAGE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
BASE_IMAGE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||||
SLIM_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
SLIM_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||||
TRT_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
TRT_BASE: nvcr.io/nvidia/l4t-tensorrt:r8.5.2-runtime
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: tensorrt
|
targets: tensorrt
|
||||||
@@ -149,7 +149,7 @@ jobs:
|
|||||||
- name: Build and push TensorRT (x86 GPU)
|
- name: Build and push TensorRT (x86 GPU)
|
||||||
env:
|
env:
|
||||||
COMPUTE_LEVEL: "50 60 70 80 90"
|
COMPUTE_LEVEL: "50 60 70 80 90"
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: tensorrt
|
targets: tensorrt
|
||||||
@@ -174,7 +174,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push Rockchip build
|
- name: Build and push Rockchip build
|
||||||
uses: docker/bake-action@v3
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: rk
|
targets: rk
|
||||||
@@ -199,7 +199,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push Hailo-8l build
|
- name: Build and push Hailo-8l build
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: h8l
|
targets: h8l
|
||||||
@@ -212,7 +212,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
AMDGPU: gfx
|
AMDGPU: gfx
|
||||||
HSA_OVERRIDE: 0
|
HSA_OVERRIDE: 0
|
||||||
uses: docker/bake-action@v3
|
uses: docker/bake-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
targets: rocm
|
targets: rocm
|
||||||
|
|||||||
@@ -203,14 +203,13 @@ detectors:
|
|||||||
ov:
|
ov:
|
||||||
type: openvino
|
type: openvino
|
||||||
device: AUTO
|
device: AUTO
|
||||||
model:
|
|
||||||
path: /openvino-model/ssdlite_mobilenet_v2.xml
|
|
||||||
|
|
||||||
model:
|
model:
|
||||||
width: 300
|
width: 300
|
||||||
height: 300
|
height: 300
|
||||||
input_tensor: nhwc
|
input_tensor: nhwc
|
||||||
input_pixel_format: bgr
|
input_pixel_format: bgr
|
||||||
|
path: /openvino-model/ssdlite_mobilenet_v2.xml
|
||||||
labelmap_path: /openvino-model/coco_91cl_bkgr.txt
|
labelmap_path: /openvino-model/coco_91cl_bkgr.txt
|
||||||
|
|
||||||
record:
|
record:
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ The default video and audio codec on your camera may not always be compatible wi
|
|||||||
|
|
||||||
### Audio Support
|
### Audio Support
|
||||||
|
|
||||||
MSE Requires AAC audio, WebRTC requires PCMU/PCMA, or opus audio. If you want to support both MSE and WebRTC then your restream config needs to make sure both are enabled.
|
MSE Requires PCMA/PCMU or AAC audio, WebRTC requires PCMA/PCMU or opus audio. If you want to support both MSE and WebRTC then your restream config needs to make sure both are enabled.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
go2rtc:
|
go2rtc:
|
||||||
|
|||||||
@@ -144,7 +144,9 @@ detectors:
|
|||||||
|
|
||||||
#### SSDLite MobileNet v2
|
#### SSDLite MobileNet v2
|
||||||
|
|
||||||
An OpenVINO model is provided in the container at `/openvino-model/ssdlite_mobilenet_v2.xml` and is used by this detector type by default. The model comes from Intel's Open Model Zoo [SSDLite MobileNet V2](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/ssdlite_mobilenet_v2) and is converted to an FP16 precision IR model. Use the model configuration shown below when using the OpenVINO detector with the default model.
|
An OpenVINO model is provided in the container at `/openvino-model/ssdlite_mobilenet_v2.xml` and is used by this detector type by default. The model comes from Intel's Open Model Zoo [SSDLite MobileNet V2](https://github.com/openvinotoolkit/open_model_zoo/tree/master/models/public/ssdlite_mobilenet_v2) and is converted to an FP16 precision IR model.
|
||||||
|
|
||||||
|
Use the model configuration shown below when using the OpenVINO detector with the default OpenVINO model:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
detectors:
|
detectors:
|
||||||
@@ -254,6 +256,7 @@ yolov4x-mish-640
|
|||||||
yolov7-tiny-288
|
yolov7-tiny-288
|
||||||
yolov7-tiny-416
|
yolov7-tiny-416
|
||||||
yolov7-640
|
yolov7-640
|
||||||
|
yolov7-416
|
||||||
yolov7-320
|
yolov7-320
|
||||||
yolov7x-640
|
yolov7x-640
|
||||||
yolov7x-320
|
yolov7x-320
|
||||||
@@ -282,6 +285,8 @@ The TensorRT detector can be selected by specifying `tensorrt` as the model type
|
|||||||
|
|
||||||
The TensorRT detector uses `.trt` model files that are located in `/config/model_cache/tensorrt` by default. These model path and dimensions used will depend on which model you have generated.
|
The TensorRT detector uses `.trt` model files that are located in `/config/model_cache/tensorrt` by default. These model path and dimensions used will depend on which model you have generated.
|
||||||
|
|
||||||
|
Use the config below to work with generated TRT models:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
detectors:
|
detectors:
|
||||||
tensorrt:
|
tensorrt:
|
||||||
@@ -501,11 +506,12 @@ detectors:
|
|||||||
cpu1:
|
cpu1:
|
||||||
type: cpu
|
type: cpu
|
||||||
num_threads: 3
|
num_threads: 3
|
||||||
model:
|
|
||||||
path: "/custom_model.tflite"
|
|
||||||
cpu2:
|
cpu2:
|
||||||
type: cpu
|
type: cpu
|
||||||
num_threads: 3
|
num_threads: 3
|
||||||
|
|
||||||
|
model:
|
||||||
|
path: "/custom_model.tflite"
|
||||||
```
|
```
|
||||||
|
|
||||||
When using CPU detectors, you can add one CPU detector per camera. Adding more detectors than the number of cameras should not improve performance.
|
When using CPU detectors, you can add one CPU detector per camera. Adding more detectors than the number of cameras should not improve performance.
|
||||||
@@ -632,8 +638,6 @@ detectors:
|
|||||||
hailo8l:
|
hailo8l:
|
||||||
type: hailo8l
|
type: hailo8l
|
||||||
device: PCIe
|
device: PCIe
|
||||||
model:
|
|
||||||
path: /config/model_cache/h8l_cache/ssd_mobilenet_v1.hef
|
|
||||||
|
|
||||||
model:
|
model:
|
||||||
width: 300
|
width: 300
|
||||||
@@ -641,4 +645,5 @@ model:
|
|||||||
input_tensor: nhwc
|
input_tensor: nhwc
|
||||||
input_pixel_format: bgr
|
input_pixel_format: bgr
|
||||||
model_type: ssd
|
model_type: ssd
|
||||||
|
path: /config/model_cache/h8l_cache/ssd_mobilenet_v1.hef
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ detectors:
|
|||||||
# Required: name of the detector
|
# Required: name of the detector
|
||||||
detector_name:
|
detector_name:
|
||||||
# Required: type of the detector
|
# Required: type of the detector
|
||||||
# Frigate provided types include 'cpu', 'edgetpu', 'openvino' and 'tensorrt' (default: shown below)
|
# Frigate provides many types, see https://docs.frigate.video/configuration/object_detectors for more details (default: shown below)
|
||||||
# Additional detector types can also be plugged in.
|
# Additional detector types can also be plugged in.
|
||||||
# Detectors may require additional configuration.
|
# Detectors may require additional configuration.
|
||||||
# Refer to the Detectors configuration page for more information.
|
# Refer to the Detectors configuration page for more information.
|
||||||
@@ -117,25 +117,27 @@ auth:
|
|||||||
hash_iterations: 600000
|
hash_iterations: 600000
|
||||||
|
|
||||||
# Optional: model modifications
|
# Optional: model modifications
|
||||||
|
# NOTE: The default values are for the EdgeTPU detector.
|
||||||
|
# Other detectors will require the model config to be set.
|
||||||
model:
|
model:
|
||||||
# Optional: path to the model (default: automatic based on detector)
|
# Required: path to the model (default: automatic based on detector)
|
||||||
path: /edgetpu_model.tflite
|
path: /edgetpu_model.tflite
|
||||||
# Optional: path to the labelmap (default: shown below)
|
# Required: path to the labelmap (default: shown below)
|
||||||
labelmap_path: /labelmap.txt
|
labelmap_path: /labelmap.txt
|
||||||
# Required: Object detection model input width (default: shown below)
|
# Required: Object detection model input width (default: shown below)
|
||||||
width: 320
|
width: 320
|
||||||
# Required: Object detection model input height (default: shown below)
|
# Required: Object detection model input height (default: shown below)
|
||||||
height: 320
|
height: 320
|
||||||
# Optional: Object detection model input colorspace
|
# Required: Object detection model input colorspace
|
||||||
# Valid values are rgb, bgr, or yuv. (default: shown below)
|
# Valid values are rgb, bgr, or yuv. (default: shown below)
|
||||||
input_pixel_format: rgb
|
input_pixel_format: rgb
|
||||||
# Optional: Object detection model input tensor format
|
# Required: Object detection model input tensor format
|
||||||
# Valid values are nhwc or nchw (default: shown below)
|
# Valid values are nhwc or nchw (default: shown below)
|
||||||
input_tensor: nhwc
|
input_tensor: nhwc
|
||||||
# Optional: Object detection model type, currently only used with the OpenVINO detector
|
# Required: Object detection model type, currently only used with the OpenVINO detector
|
||||||
# Valid values are ssd, yolox, yolonas (default: shown below)
|
# Valid values are ssd, yolox, yolonas (default: shown below)
|
||||||
model_type: ssd
|
model_type: ssd
|
||||||
# Optional: Label name modifications. These are merged into the standard labelmap.
|
# Required: Label name modifications. These are merged into the standard labelmap.
|
||||||
labelmap:
|
labelmap:
|
||||||
2: vehicle
|
2: vehicle
|
||||||
# Optional: Map of object labels to their attribute labels (default: depends on model)
|
# Optional: Map of object labels to their attribute labels (default: depends on model)
|
||||||
@@ -760,6 +762,8 @@ cameras:
|
|||||||
- cat
|
- cat
|
||||||
# Optional: Restrict generation to objects that entered any of the listed zones (default: none, all zones qualify)
|
# Optional: Restrict generation to objects that entered any of the listed zones (default: none, all zones qualify)
|
||||||
required_zones: []
|
required_zones: []
|
||||||
|
# Optional: Save thumbnails sent to generative AI for review/debugging purposes (default: shown below)
|
||||||
|
debug_save_thumbnails: False
|
||||||
|
|
||||||
# Optional
|
# Optional
|
||||||
ui:
|
ui:
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ def config(request: Request):
|
|||||||
mode="json", warnings="none", exclude_none=True
|
mode="json", warnings="none", exclude_none=True
|
||||||
)
|
)
|
||||||
for stream_name, stream in go2rtc.get("streams", {}).items():
|
for stream_name, stream in go2rtc.get("streams", {}).items():
|
||||||
|
if stream is None:
|
||||||
|
continue
|
||||||
if isinstance(stream, str):
|
if isinstance(stream, str):
|
||||||
cleaned = clean_camera_user_pass(stream)
|
cleaned = clean_camera_user_pass(stream)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ class GenAICameraConfig(BaseModel):
|
|||||||
default_factory=list,
|
default_factory=list,
|
||||||
title="List of required zones to be entered in order to run generative AI.",
|
title="List of required zones to be entered in order to run generative AI.",
|
||||||
)
|
)
|
||||||
|
debug_save_thumbnails: bool = Field(
|
||||||
|
default=False,
|
||||||
|
title="Save thumbnails sent to generative AI for debugging purposes.",
|
||||||
|
)
|
||||||
|
|
||||||
@field_validator("required_zones", mode="before")
|
@field_validator("required_zones", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -594,35 +594,27 @@ class FrigateConfig(FrigateBaseModel):
|
|||||||
if isinstance(detector, dict)
|
if isinstance(detector, dict)
|
||||||
else detector.model_dump(warnings="none")
|
else detector.model_dump(warnings="none")
|
||||||
)
|
)
|
||||||
detector_config: DetectorConfig = adapter.validate_python(model_dict)
|
detector_config: BaseDetectorConfig = adapter.validate_python(model_dict)
|
||||||
if detector_config.model is None:
|
|
||||||
detector_config.model = self.model.model_copy()
|
|
||||||
else:
|
|
||||||
path = detector_config.model.path
|
|
||||||
detector_config.model = self.model.model_copy()
|
|
||||||
detector_config.model.path = path
|
|
||||||
|
|
||||||
if "path" not in model_dict or len(model_dict.keys()) > 1:
|
# users should not set model themselves
|
||||||
logger.warning(
|
if detector_config.model:
|
||||||
"Customizing more than a detector model path is unsupported."
|
detector_config.model = None
|
||||||
)
|
|
||||||
|
|
||||||
merged_model = deep_merge(
|
model_config = self.model.model_dump(exclude_unset=True, warnings="none")
|
||||||
detector_config.model.model_dump(exclude_unset=True, warnings="none"),
|
|
||||||
self.model.model_dump(exclude_unset=True, warnings="none"),
|
|
||||||
)
|
|
||||||
|
|
||||||
if "path" not in merged_model:
|
if detector_config.model_path:
|
||||||
|
model_config["path"] = detector_config.model_path
|
||||||
|
|
||||||
|
if "path" not in model_config:
|
||||||
if detector_config.type == "cpu":
|
if detector_config.type == "cpu":
|
||||||
merged_model["path"] = "/cpu_model.tflite"
|
model_config["path"] = "/cpu_model.tflite"
|
||||||
elif detector_config.type == "edgetpu":
|
elif detector_config.type == "edgetpu":
|
||||||
merged_model["path"] = "/edgetpu_model.tflite"
|
model_config["path"] = "/edgetpu_model.tflite"
|
||||||
|
|
||||||
detector_config.model = ModelConfig.model_validate(merged_model)
|
model = ModelConfig.model_validate(model_config)
|
||||||
detector_config.model.check_and_load_plus_model(
|
model.check_and_load_plus_model(self.plus_api, detector_config.type)
|
||||||
self.plus_api, detector_config.type
|
model.compute_model_hash()
|
||||||
)
|
detector_config.model = model
|
||||||
detector_config.model.compute_model_hash()
|
|
||||||
self.detectors[key] = detector_config
|
self.detectors[key] = detector_config
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -194,6 +194,9 @@ class BaseDetectorConfig(BaseModel):
|
|||||||
model: Optional[ModelConfig] = Field(
|
model: Optional[ModelConfig] = Field(
|
||||||
default=None, title="Detector specific model configuration."
|
default=None, title="Detector specific model configuration."
|
||||||
)
|
)
|
||||||
|
model_path: Optional[str] = Field(
|
||||||
|
default=None, title="Detector specific model path."
|
||||||
|
)
|
||||||
model_config = ConfigDict(
|
model_config = ConfigDict(
|
||||||
extra="allow", arbitrary_types_allowed=True, protected_namespaces=()
|
extra="allow", arbitrary_types_allowed=True, protected_namespaces=()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
from multiprocessing.synchronize import Event as MpEvent
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
@@ -217,6 +218,8 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
_, buffer = cv2.imencode(".jpg", cropped_image)
|
_, buffer = cv2.imencode(".jpg", cropped_image)
|
||||||
snapshot_image = buffer.tobytes()
|
snapshot_image = buffer.tobytes()
|
||||||
|
|
||||||
|
num_thumbnails = len(self.tracked_events.get(event_id, []))
|
||||||
|
|
||||||
embed_image = (
|
embed_image = (
|
||||||
[snapshot_image]
|
[snapshot_image]
|
||||||
if event.has_snapshot and camera_config.genai.use_snapshot
|
if event.has_snapshot and camera_config.genai.use_snapshot
|
||||||
@@ -225,11 +228,37 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
data["thumbnail"]
|
data["thumbnail"]
|
||||||
for data in self.tracked_events[event_id]
|
for data in self.tracked_events[event_id]
|
||||||
]
|
]
|
||||||
if len(self.tracked_events.get(event_id, [])) > 0
|
if num_thumbnails > 0
|
||||||
else [thumbnail]
|
else [thumbnail]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if camera_config.genai.debug_save_thumbnails and num_thumbnails > 0:
|
||||||
|
logger.debug(
|
||||||
|
f"Saving {num_thumbnails} thumbnails for event {event.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
Path(
|
||||||
|
os.path.join(CLIPS_DIR, f"genai-requests/{event.id}")
|
||||||
|
).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for idx, data in enumerate(self.tracked_events[event_id], 1):
|
||||||
|
jpg_bytes: bytes = data["thumbnail"]
|
||||||
|
|
||||||
|
if jpg_bytes is None:
|
||||||
|
logger.warning(
|
||||||
|
f"Unable to save thumbnail {idx} for {event.id}."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
with open(
|
||||||
|
os.path.join(
|
||||||
|
CLIPS_DIR,
|
||||||
|
f"genai-requests/{event.id}/{idx}.jpg",
|
||||||
|
),
|
||||||
|
"wb",
|
||||||
|
) as j:
|
||||||
|
j.write(jpg_bytes)
|
||||||
|
|
||||||
# Generate the description. Call happens in a thread since it is network bound.
|
# Generate the description. Call happens in a thread since it is network bound.
|
||||||
threading.Thread(
|
threading.Thread(
|
||||||
target=self._embed_description,
|
target=self._embed_description,
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ class EventCleanup(threading.Thread):
|
|||||||
|
|
||||||
events_to_update = []
|
events_to_update = []
|
||||||
|
|
||||||
for batch in query.iterator():
|
for event in query.iterator():
|
||||||
events_to_update.extend([event.id for event in batch])
|
events_to_update.append(event.id)
|
||||||
if len(events_to_update) >= CHUNK_SIZE:
|
if len(events_to_update) >= CHUNK_SIZE:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Updating {update_params} for {len(events_to_update)} events"
|
f"Updating {update_params} for {len(events_to_update)} events"
|
||||||
@@ -257,7 +257,7 @@ class EventCleanup(threading.Thread):
|
|||||||
events_to_update = []
|
events_to_update = []
|
||||||
|
|
||||||
for event in query.iterator():
|
for event in query.iterator():
|
||||||
events_to_update.append(event)
|
events_to_update.append(event.id)
|
||||||
|
|
||||||
if len(events_to_update) >= CHUNK_SIZE:
|
if len(events_to_update) >= CHUNK_SIZE:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ PRESETS_HW_ACCEL_DECODE = {
|
|||||||
"preset-rpi-64-h264": "-c:v:1 h264_v4l2m2m",
|
"preset-rpi-64-h264": "-c:v:1 h264_v4l2m2m",
|
||||||
"preset-rpi-64-h265": "-c:v:1 hevc_v4l2m2m",
|
"preset-rpi-64-h265": "-c:v:1 hevc_v4l2m2m",
|
||||||
FFMPEG_HWACCEL_VAAPI: f"-hwaccel_flags allow_profile_mismatch -hwaccel vaapi -hwaccel_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format vaapi",
|
FFMPEG_HWACCEL_VAAPI: f"-hwaccel_flags allow_profile_mismatch -hwaccel vaapi -hwaccel_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format vaapi",
|
||||||
"preset-intel-qsv-h264": f"-hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v h264_qsv",
|
"preset-intel-qsv-h264": f"-hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v h264_qsv -bsf:v dump_extra", # https://trac.ffmpeg.org/ticket/9766#comment:17
|
||||||
"preset-intel-qsv-h265": f"-load_plugin hevc_hw -hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v hevc_qsv",
|
"preset-intel-qsv-h265": f"-load_plugin hevc_hw -hwaccel qsv -qsv_device {_gpu_selector.get_selected_gpu()} -hwaccel_output_format qsv -c:v hevc_qsv -bsf:v dump_extra", # https://trac.ffmpeg.org/ticket/9766#comment:17
|
||||||
FFMPEG_HWACCEL_NVIDIA: "-hwaccel cuda -hwaccel_output_format cuda",
|
FFMPEG_HWACCEL_NVIDIA: "-hwaccel cuda -hwaccel_output_format cuda",
|
||||||
"preset-jetson-h264": "-c:v h264_nvmpi -resize {1}x{2}",
|
"preset-jetson-h264": "-c:v h264_nvmpi -resize {1}x{2}",
|
||||||
"preset-jetson-h265": "-c:v hevc_nvmpi -resize {1}x{2}",
|
"preset-jetson-h265": "-c:v hevc_nvmpi -resize {1}x{2}",
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ class TestConfig(unittest.TestCase):
|
|||||||
"detectors": {
|
"detectors": {
|
||||||
"cpu": {
|
"cpu": {
|
||||||
"type": "cpu",
|
"type": "cpu",
|
||||||
"model": {"path": "/cpu_model.tflite"},
|
"model_path": "/cpu_model.tflite",
|
||||||
},
|
},
|
||||||
"edgetpu": {
|
"edgetpu": {
|
||||||
"type": "edgetpu",
|
"type": "edgetpu",
|
||||||
"model": {"path": "/edgetpu_model.tflite"},
|
"model_path": "/edgetpu_model.tflite",
|
||||||
},
|
},
|
||||||
"openvino": {
|
"openvino": {
|
||||||
"type": "openvino",
|
"type": "openvino",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from frigate.util.services import get_video_properties
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CURRENT_CONFIG_VERSION = "0.15-0"
|
CURRENT_CONFIG_VERSION = "0.15-1"
|
||||||
DEFAULT_CONFIG_FILE = "/config/config.yml"
|
DEFAULT_CONFIG_FILE = "/config/config.yml"
|
||||||
|
|
||||||
|
|
||||||
@@ -77,6 +77,13 @@ def migrate_frigate_config(config_file: str):
|
|||||||
yaml.dump(new_config, f)
|
yaml.dump(new_config, f)
|
||||||
previous_version = "0.15-0"
|
previous_version = "0.15-0"
|
||||||
|
|
||||||
|
if previous_version < "0.15-1":
|
||||||
|
logger.info(f"Migrating frigate config from {previous_version} to 0.15-1...")
|
||||||
|
new_config = migrate_015_1(config)
|
||||||
|
with open(config_file, "w") as f:
|
||||||
|
yaml.dump(new_config, f)
|
||||||
|
previous_version = "0.15-1"
|
||||||
|
|
||||||
logger.info("Finished frigate config migration...")
|
logger.info("Finished frigate config migration...")
|
||||||
|
|
||||||
|
|
||||||
@@ -267,6 +274,21 @@ def migrate_015_0(config: dict[str, dict[str, any]]) -> dict[str, dict[str, any]
|
|||||||
return new_config
|
return new_config
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_015_1(config: dict[str, dict[str, any]]) -> dict[str, dict[str, any]]:
|
||||||
|
"""Handle migrating frigate config to 0.15-1"""
|
||||||
|
new_config = config.copy()
|
||||||
|
|
||||||
|
for detector, detector_config in config.get("detectors", {}).items():
|
||||||
|
path = detector_config.get("model", {}).get("path")
|
||||||
|
|
||||||
|
if path:
|
||||||
|
new_config["detectors"][detector]["model_path"] = path
|
||||||
|
del new_config["detectors"][detector]["model"]
|
||||||
|
|
||||||
|
new_config["version"] = "0.15-1"
|
||||||
|
return new_config
|
||||||
|
|
||||||
|
|
||||||
def get_relative_coordinates(
|
def get_relative_coordinates(
|
||||||
mask: Optional[Union[str, list]], frame_shape: tuple[int, int]
|
mask: Optional[Union[str, list]], frame_shape: tuple[int, int]
|
||||||
) -> Union[str, list]:
|
) -> Union[str, list]:
|
||||||
|
|||||||
@@ -755,7 +755,11 @@ export function CameraGroupEdit({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
{[
|
{[
|
||||||
...(birdseyeConfig?.enabled ? ["birdseye"] : []),
|
...(birdseyeConfig?.enabled ? ["birdseye"] : []),
|
||||||
...Object.keys(config?.cameras ?? {}),
|
...Object.keys(config?.cameras ?? {}).sort(
|
||||||
|
(a, b) =>
|
||||||
|
(config?.cameras[a]?.ui?.order ?? 0) -
|
||||||
|
(config?.cameras[b]?.ui?.order ?? 0),
|
||||||
|
),
|
||||||
].map((camera) => (
|
].map((camera) => (
|
||||||
<FormControl key={camera}>
|
<FormControl key={camera}>
|
||||||
<FilterSwitch
|
<FilterSwitch
|
||||||
|
|||||||
@@ -477,7 +477,10 @@ export default function ObjectLifecycle({
|
|||||||
</p>
|
</p>
|
||||||
{Array.isArray(item.data.box) &&
|
{Array.isArray(item.data.box) &&
|
||||||
item.data.box.length >= 4
|
item.data.box.length >= 4
|
||||||
? (item.data.box[2] / item.data.box[3]).toFixed(2)
|
? (
|
||||||
|
aspectRatio *
|
||||||
|
(item.data.box[2] / item.data.box[3])
|
||||||
|
).toFixed(2)
|
||||||
: "N/A"}
|
: "N/A"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -505,45 +505,46 @@ function ObjectDetailsTab({
|
|||||||
|
|
||||||
<div className="flex w-full flex-row justify-end gap-2">
|
<div className="flex w-full flex-row justify-end gap-2">
|
||||||
{config?.cameras[search.camera].genai.enabled && search.end_time && (
|
{config?.cameras[search.camera].genai.enabled && search.end_time && (
|
||||||
<>
|
<div className="flex items-start">
|
||||||
<div className="flex items-start">
|
<Button
|
||||||
<Button
|
className="rounded-r-none border-r-0"
|
||||||
className="rounded-r-none border-r-0"
|
aria-label="Regenerate tracked object description"
|
||||||
aria-label="Regenerate tracked object description"
|
onClick={() => regenerateDescription("thumbnails")}
|
||||||
onClick={() => regenerateDescription("thumbnails")}
|
>
|
||||||
>
|
Regenerate
|
||||||
Regenerate
|
</Button>
|
||||||
</Button>
|
{search.has_snapshot && (
|
||||||
{search.has_snapshot && (
|
<DropdownMenu>
|
||||||
<DropdownMenu>
|
<DropdownMenuTrigger asChild>
|
||||||
<DropdownMenuTrigger asChild>
|
<Button
|
||||||
<Button
|
className="rounded-l-none border-l-0 px-2"
|
||||||
className="rounded-l-none border-l-0 px-2"
|
aria-label="Expand regeneration menu"
|
||||||
aria-label="Expand regeneration menu"
|
>
|
||||||
>
|
<FaChevronDown className="size-3" />
|
||||||
<FaChevronDown className="size-3" />
|
</Button>
|
||||||
</Button>
|
</DropdownMenuTrigger>
|
||||||
</DropdownMenuTrigger>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
className="cursor-pointer"
|
||||||
className="cursor-pointer"
|
aria-label="Regenerate from snapshot"
|
||||||
aria-label="Regenerate from snapshot"
|
onClick={() => regenerateDescription("snapshot")}
|
||||||
onClick={() => regenerateDescription("snapshot")}
|
>
|
||||||
>
|
Regenerate from Snapshot
|
||||||
Regenerate from Snapshot
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem
|
className="cursor-pointer"
|
||||||
className="cursor-pointer"
|
aria-label="Regenerate from thumbnails"
|
||||||
aria-label="Regenerate from thumbnails"
|
onClick={() => regenerateDescription("thumbnails")}
|
||||||
onClick={() => regenerateDescription("thumbnails")}
|
>
|
||||||
>
|
Regenerate from Thumbnails
|
||||||
Regenerate from Thumbnails
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuContent>
|
||||||
</DropdownMenuContent>
|
</DropdownMenu>
|
||||||
</DropdownMenu>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
{(config?.cameras[search.camera].genai.enabled && search.end_time) ||
|
||||||
|
(!config?.cameras[search.camera].genai.enabled && (
|
||||||
<Button
|
<Button
|
||||||
variant="select"
|
variant="select"
|
||||||
aria-label="Save"
|
aria-label="Save"
|
||||||
@@ -551,8 +552,7 @@ function ObjectDetailsTab({
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
))}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function SearchSettings({
|
|||||||
const trigger = (
|
const trigger = (
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center gap-2"
|
className="flex items-center gap-2"
|
||||||
aria-label="Search Settings"
|
aria-label="Explore Settings"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<FaCog className="text-secondary-foreground" />
|
<FaCog className="text-secondary-foreground" />
|
||||||
|
|||||||
@@ -328,12 +328,12 @@ export default function Explore() {
|
|||||||
<div className="flex max-w-96 flex-col items-center justify-center space-y-3 rounded-lg bg-background/50 p-5">
|
<div className="flex max-w-96 flex-col items-center justify-center space-y-3 rounded-lg bg-background/50 p-5">
|
||||||
<div className="my-5 flex flex-col items-center gap-2 text-xl">
|
<div className="my-5 flex flex-col items-center gap-2 text-xl">
|
||||||
<TbExclamationCircle className="mb-3 size-10" />
|
<TbExclamationCircle className="mb-3 size-10" />
|
||||||
<div>Search Unavailable</div>
|
<div>Explore is Unavailable</div>
|
||||||
</div>
|
</div>
|
||||||
{embeddingsReindexing && allModelsLoaded && (
|
{embeddingsReindexing && allModelsLoaded && (
|
||||||
<>
|
<>
|
||||||
<div className="text-center text-primary-variant">
|
<div className="text-center text-primary-variant">
|
||||||
Search can be used after tracked object embeddings have
|
Explore can be used after tracked object embeddings have
|
||||||
finished reindexing.
|
finished reindexing.
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5 text-center">
|
<div className="pt-5 text-center">
|
||||||
@@ -384,8 +384,8 @@ export default function Explore() {
|
|||||||
<>
|
<>
|
||||||
<div className="text-center text-primary-variant">
|
<div className="text-center text-primary-variant">
|
||||||
Frigate is downloading the necessary embeddings models to
|
Frigate is downloading the necessary embeddings models to
|
||||||
support semantic searching. This may take several minutes
|
support the Semantic Search feature. This may take several
|
||||||
depending on the speed of your network connection.
|
minutes depending on the speed of your network connection.
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-96 flex-col gap-2 py-5">
|
<div className="flex w-96 flex-col gap-2 py-5">
|
||||||
<div className="flex flex-row items-center justify-center gap-2">
|
<div className="flex flex-row items-center justify-center gap-2">
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import UiSettingsView from "@/views/settings/UiSettingsView";
|
|||||||
|
|
||||||
const allSettingsViews = [
|
const allSettingsViews = [
|
||||||
"UI settings",
|
"UI settings",
|
||||||
"search settings",
|
"explore settings",
|
||||||
"camera settings",
|
"camera settings",
|
||||||
"masks / zones",
|
"masks / zones",
|
||||||
"motion tuner",
|
"motion tuner",
|
||||||
@@ -175,7 +175,7 @@ export default function Settings() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex h-full w-full flex-col items-start md:h-dvh md:pb-24">
|
<div className="mt-2 flex h-full w-full flex-col items-start md:h-dvh md:pb-24">
|
||||||
{page == "UI settings" && <UiSettingsView />}
|
{page == "UI settings" && <UiSettingsView />}
|
||||||
{page == "search settings" && (
|
{page == "explore settings" && (
|
||||||
<SearchSettingsView setUnsavedChanges={setUnsavedChanges} />
|
<SearchSettingsView setUnsavedChanges={setUnsavedChanges} />
|
||||||
)}
|
)}
|
||||||
{page == "debug" && (
|
{page == "debug" && (
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default function SearchSettingsView({
|
|||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success("Search settings have been saved.", {
|
toast.success("Explore settings have been saved.", {
|
||||||
position: "top-center",
|
position: "top-center",
|
||||||
});
|
});
|
||||||
setChangedValue(false);
|
setChangedValue(false);
|
||||||
@@ -128,7 +128,7 @@ export default function SearchSettingsView({
|
|||||||
if (changedValue) {
|
if (changedValue) {
|
||||||
addMessage(
|
addMessage(
|
||||||
"search_settings",
|
"search_settings",
|
||||||
`Unsaved search settings changes`,
|
`Unsaved Explore settings changes`,
|
||||||
undefined,
|
undefined,
|
||||||
"search_settings",
|
"search_settings",
|
||||||
);
|
);
|
||||||
@@ -140,7 +140,7 @@ export default function SearchSettingsView({
|
|||||||
}, [changedValue]);
|
}, [changedValue]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = "Search Settings - Frigate";
|
document.title = "Explore Settings - Frigate";
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
@@ -152,7 +152,7 @@ export default function SearchSettingsView({
|
|||||||
<Toaster position="top-center" closeButton={true} />
|
<Toaster position="top-center" closeButton={true} />
|
||||||
<div className="scrollbar-container order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0">
|
<div className="scrollbar-container order-last mb-10 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0">
|
||||||
<Heading as="h3" className="my-2">
|
<Heading as="h3" className="my-2">
|
||||||
Search Settings
|
Explore Settings
|
||||||
</Heading>
|
</Heading>
|
||||||
<Separator className="my-2 flex bg-secondary" />
|
<Separator className="my-2 flex bg-secondary" />
|
||||||
<Heading as="h4" className="my-2">
|
<Heading as="h4" className="my-2">
|
||||||
@@ -221,7 +221,7 @@ export default function SearchSettingsView({
|
|||||||
<div className="text-md">Model Size</div>
|
<div className="text-md">Model Size</div>
|
||||||
<div className="space-y-1 text-sm text-muted-foreground">
|
<div className="space-y-1 text-sm text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
The size of the model used for semantic search embeddings.
|
The size of the model used for Semantic Search embeddings.
|
||||||
</p>
|
</p>
|
||||||
<ul className="list-disc pl-5 text-sm">
|
<ul className="list-disc pl-5 text-sm">
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
Reference in New Issue
Block a user