mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-04 04:27:42 +03:00
Merge branch 'blakeblackshear:dev' into dev-docs-i18n
This commit is contained in:
commit
726f31c0ad
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -39,14 +39,14 @@ jobs:
|
|||||||
STABLE_TAG=${BASE}:stable
|
STABLE_TAG=${BASE}:stable
|
||||||
PULL_TAG=${BASE}:${BUILD_TAG}
|
PULL_TAG=${BASE}:${BUILD_TAG}
|
||||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG} docker://${VERSION_TAG}
|
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG} docker://${VERSION_TAG}
|
||||||
for variant in standard-arm64 tensorrt tensorrt-jp5 tensorrt-jp6 rk h8l rocm; do
|
for variant in standard-arm64 tensorrt tensorrt-jp6 rk rocm; do
|
||||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG}-${variant} docker://${VERSION_TAG}-${variant}
|
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG}-${variant} docker://${VERSION_TAG}-${variant}
|
||||||
done
|
done
|
||||||
|
|
||||||
# stable tag
|
# stable tag
|
||||||
if [[ "${BUILD_TYPE}" == "stable" ]]; then
|
if [[ "${BUILD_TYPE}" == "stable" ]]; then
|
||||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG} docker://${STABLE_TAG}
|
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG} docker://${STABLE_TAG}
|
||||||
for variant in standard-arm64 tensorrt tensorrt-jp5 tensorrt-jp6 rk h8l rocm; do
|
for variant in standard-arm64 tensorrt tensorrt-jp6 rk rocm; do
|
||||||
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG}-${variant} docker://${STABLE_TAG}-${variant}
|
docker run --rm -v $HOME/.docker/config.json:/config.json quay.io/skopeo/stable:latest copy --authfile /config.json --multi-arch all docker://${PULL_TAG}-${variant} docker://${STABLE_TAG}-${variant}
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -37,9 +37,9 @@ opencv-python-headless == 4.11.0.*
|
|||||||
opencv-contrib-python == 4.11.0.*
|
opencv-contrib-python == 4.11.0.*
|
||||||
scipy == 1.14.*
|
scipy == 1.14.*
|
||||||
# OpenVino & ONNX
|
# OpenVino & ONNX
|
||||||
openvino == 2024.4.*
|
openvino == 2025.0.*
|
||||||
onnxruntime-openvino == 1.20.* ; platform_machine == 'x86_64'
|
onnxruntime-openvino == 1.21.0 ; platform_machine == 'x86_64'
|
||||||
onnxruntime == 1.20.* ; platform_machine == 'aarch64'
|
onnxruntime == 1.21.0 ; platform_machine == 'aarch64'
|
||||||
# Embeddings
|
# Embeddings
|
||||||
transformers == 4.45.*
|
transformers == 4.45.*
|
||||||
# Generative AI
|
# Generative AI
|
||||||
|
|||||||
@ -4,6 +4,11 @@
|
|||||||
|
|
||||||
set -o errexit -o nounset -o pipefail
|
set -o errexit -o nounset -o pipefail
|
||||||
|
|
||||||
|
# opt out of openvino telemetry
|
||||||
|
if [ -e /usr/local/bin/opt_in_out ]; then
|
||||||
|
/usr/local/bin/opt_in_out --opt_out
|
||||||
|
fi
|
||||||
|
|
||||||
# Logs should be sent to stdout so that s6 can collect them
|
# Logs should be sent to stdout so that s6 can collect them
|
||||||
|
|
||||||
# Tell S6-Overlay not to restart this service
|
# Tell S6-Overlay not to restart this service
|
||||||
|
|||||||
@ -53,7 +53,7 @@ elif go2rtc_config["api"].get("origin") is None:
|
|||||||
|
|
||||||
# Need to set default location for HA config
|
# Need to set default location for HA config
|
||||||
if go2rtc_config.get("hass") is None:
|
if go2rtc_config.get("hass") is None:
|
||||||
go2rtc_config["hass"] = {"config": "/config"}
|
go2rtc_config["hass"] = {"config": "/homeassistant"}
|
||||||
|
|
||||||
# we want to ensure that logs are easy to read
|
# we want to ensure that logs are easy to read
|
||||||
if go2rtc_config.get("log") is None:
|
if go2rtc_config.get("log") is None:
|
||||||
|
|||||||
@ -26,7 +26,7 @@ COPY --from=rootfs / /
|
|||||||
COPY docker/rockchip/COCO /COCO
|
COPY docker/rockchip/COCO /COCO
|
||||||
COPY docker/rockchip/conv2rknn.py /opt/conv2rknn.py
|
COPY docker/rockchip/conv2rknn.py /opt/conv2rknn.py
|
||||||
|
|
||||||
ADD https://github.com/MarcA711/rknn-toolkit2/releases/download/v2.3.0/librknnrt.so /usr/lib/
|
ADD https://github.com/MarcA711/rknn-toolkit2/releases/download/v2.3.2/librknnrt.so /usr/lib/
|
||||||
|
|
||||||
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-7/ffmpeg /usr/lib/ffmpeg/6.0/bin/
|
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-7/ffmpeg /usr/lib/ffmpeg/6.0/bin/
|
||||||
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-7/ffprobe /usr/lib/ffmpeg/6.0/bin/
|
ADD --chmod=111 https://github.com/MarcA711/Rockchip-FFmpeg-Builds/releases/download/6.1-7/ffprobe /usr/lib/ffmpeg/6.0/bin/
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
rknn-toolkit2 == 2.3.0
|
rknn-toolkit2 == 2.3.2
|
||||||
rknn-toolkit-lite2 == 2.3.0
|
rknn-toolkit-lite2 == 2.3.2
|
||||||
@ -13,5 +13,5 @@ nvidia-cudnn-cu12 == 9.5.0.*; platform_machine == 'x86_64'
|
|||||||
nvidia-cufft-cu11==10.*; platform_machine == 'x86_64'
|
nvidia-cufft-cu11==10.*; platform_machine == 'x86_64'
|
||||||
nvidia-cufft-cu12==11.*; platform_machine == 'x86_64'
|
nvidia-cufft-cu12==11.*; platform_machine == 'x86_64'
|
||||||
onnx==1.16.*; platform_machine == 'x86_64'
|
onnx==1.16.*; platform_machine == 'x86_64'
|
||||||
onnxruntime-gpu==1.20.*; platform_machine == 'x86_64'
|
onnxruntime-gpu==1.21.0; platform_machine == 'x86_64'
|
||||||
protobuf==3.20.3; platform_machine == 'x86_64'
|
protobuf==3.20.3; platform_machine == 'x86_64'
|
||||||
|
|||||||
@ -77,7 +77,7 @@ Changing the secret will invalidate current tokens.
|
|||||||
|
|
||||||
Frigate can be configured to leverage features of common upstream authentication proxies such as Authelia, Authentik, oauth2_proxy, or traefik-forward-auth.
|
Frigate can be configured to leverage features of common upstream authentication proxies such as Authelia, Authentik, oauth2_proxy, or traefik-forward-auth.
|
||||||
|
|
||||||
If you are leveraging the authentication of an upstream proxy, you likely want to disable Frigate's authentication. Optionally, if communication between the reverse proxy and Frigate is over an untrusted network, you should set an `auth_secret` in the `proxy` config and configure the proxy to send the secret value as a header named `X-Proxy-Secret`. Assuming this is an untrusted network, you will also want to [configure a real TLS certificate](tls.md) to ensure the traffic can't simply be sniffed to steal the secret.
|
If you are leveraging the authentication of an upstream proxy, you likely want to disable Frigate's authentication as there is no correspondence between users in Frigate's database and users authenticated via the proxy. Optionally, if communication between the reverse proxy and Frigate is over an untrusted network, you should set an `auth_secret` in the `proxy` config and configure the proxy to send the secret value as a header named `X-Proxy-Secret`. Assuming this is an untrusted network, you will also want to [configure a real TLS certificate](tls.md) to ensure the traffic can't simply be sniffed to steal the secret.
|
||||||
|
|
||||||
Here is an example of how to disable Frigate's authentication and also ensure the requests come only from your known proxy.
|
Here is an example of how to disable Frigate's authentication and also ensure the requests come only from your known proxy.
|
||||||
|
|
||||||
@ -109,6 +109,14 @@ proxy:
|
|||||||
|
|
||||||
Frigate supports both `admin` and `viewer` roles (see below). When using port `8971`, Frigate validates these headers and subsequent requests use the headers `remote-user` and `remote-role` for authorization.
|
Frigate supports both `admin` and `viewer` roles (see below). When using port `8971`, Frigate validates these headers and subsequent requests use the headers `remote-user` and `remote-role` for authorization.
|
||||||
|
|
||||||
|
A default role can be provided. Any value in the mapped `role` header will override the default.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
proxy:
|
||||||
|
...
|
||||||
|
default_role: viewer
|
||||||
|
```
|
||||||
|
|
||||||
#### Port Considerations
|
#### Port Considerations
|
||||||
|
|
||||||
**Authenticated Port (8971)**
|
**Authenticated Port (8971)**
|
||||||
|
|||||||
@ -15,6 +15,17 @@ Many cameras support encoding options which greatly affect the live view experie
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## H.265 Cameras via Safari
|
||||||
|
|
||||||
|
Some cameras support h265 with different formats, but Safari only supports the annexb format. When using h265 camera streams for recording with devices that use the Safari browser, the `apple_compatibility` option should be used.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cameras:
|
||||||
|
h265_cam: # <------ Doesn't matter what the camera is called
|
||||||
|
ffmpeg:
|
||||||
|
apple_compatibility: true # <- Adds compatibility with MacOS and iPhone
|
||||||
|
```
|
||||||
|
|
||||||
## MJPEG Cameras
|
## MJPEG Cameras
|
||||||
|
|
||||||
Note that mjpeg cameras require encoding the video into h264 for recording, and restream roles. This will use significantly more CPU than if the cameras supported h264 feeds directly. It is recommended to use the restream role to create an h264 restream and then use that as the source for ffmpeg.
|
Note that mjpeg cameras require encoding the video into h264 for recording, and restream roles. This will use significantly more CPU than if the cameras supported h264 feeds directly. It is recommended to use the restream role to create an h264 restream and then use that as the source for ffmpeg.
|
||||||
|
|||||||
@ -3,7 +3,7 @@ id: face_recognition
|
|||||||
title: Face Recognition
|
title: Face Recognition
|
||||||
---
|
---
|
||||||
|
|
||||||
Face recognition identifies known individuals by matching detected faces with previously learned facial data. When a known person is recognized, their name will be added as a `sub_label`. This information is included in the UI, filters, as well as in notifications.
|
Face recognition identifies known individuals by matching detected faces with previously learned facial data. When a known `person` is recognized, their name will be added as a `sub_label`. This information is included in the UI, filters, as well as in notifications.
|
||||||
|
|
||||||
## Model Requirements
|
## Model Requirements
|
||||||
|
|
||||||
@ -13,6 +13,12 @@ When running a Frigate+ model (or any custom model that natively detects faces)
|
|||||||
|
|
||||||
When running a default COCO model or another model that does not include `face` as a detectable label, face detection will run via CV2 using a lightweight DNN model that runs on the CPU. In this case, you should _not_ define `face` in your list of objects to track.
|
When running a default COCO model or another model that does not include `face` as a detectable label, face detection will run via CV2 using a lightweight DNN model that runs on the CPU. In this case, you should _not_ define `face` in your list of objects to track.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
Frigate needs to first detect a `person` before it can detect and recognize a face.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### Face Recognition
|
### Face Recognition
|
||||||
|
|
||||||
Frigate has support for two face recognition model types:
|
Frigate has support for two face recognition model types:
|
||||||
@ -22,11 +28,13 @@ Frigate has support for two face recognition model types:
|
|||||||
|
|
||||||
In both cases, a lightweight face landmark detection model is also used to align faces before running recognition.
|
In both cases, a lightweight face landmark detection model is also used to align faces before running recognition.
|
||||||
|
|
||||||
|
All of these features run locally on your system.
|
||||||
|
|
||||||
## Minimum System Requirements
|
## Minimum System Requirements
|
||||||
|
|
||||||
The `small` model is optimized for efficiency and runs on the CPU, most CPUs should run the model efficiently.
|
The `small` model is optimized for efficiency and runs on the CPU, most CPUs should run the model efficiently.
|
||||||
|
|
||||||
The `large` model is optimized for accuracy, an integrated or discrete GPU is highly recommended.
|
The `large` model is optimized for accuracy, an integrated or discrete GPU is highly recommended. See the [Hardware Accelerated Enrichments](/configuration/hardware_acceleration_enrichments.md) documentation.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
@ -39,7 +47,7 @@ face_recognition:
|
|||||||
|
|
||||||
## Advanced Configuration
|
## Advanced Configuration
|
||||||
|
|
||||||
Fine-tune face recognition with these optional parameters:
|
Fine-tune face recognition with these optional parameters at the global level of your config. The only optional parameters that can be set at the camera level are `enabled` and `min_area`.
|
||||||
|
|
||||||
### Detection
|
### Detection
|
||||||
|
|
||||||
@ -62,6 +70,13 @@ Fine-tune face recognition with these optional parameters:
|
|||||||
- `blur_confidence_filter`: Enables a filter that calculates how blurry the face is and adjusts the confidence based on this.
|
- `blur_confidence_filter`: Enables a filter that calculates how blurry the face is and adjusts the confidence based on this.
|
||||||
- Default: `True`.
|
- Default: `True`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. **Enable face recognition** in your configuration file and restart Frigate.
|
||||||
|
2. **Upload your face** using the **Add Face** button's wizard in the Face Library section of the Frigate UI.
|
||||||
|
3. When Frigate detects and attempts to recognize a face, it will appear in the **Train** tab of the Face Library, along with its associated recognition confidence.
|
||||||
|
4. From the **Train** tab, you can **assign the face** to a new or existing person to improve recognition accuracy for the future.
|
||||||
|
|
||||||
## Creating a Robust Training Set
|
## Creating a Robust Training Set
|
||||||
|
|
||||||
The number of images needed for a sufficient training set for face recognition varies depending on several factors:
|
The number of images needed for a sufficient training set for face recognition varies depending on several factors:
|
||||||
@ -133,6 +148,7 @@ No, using another face recognition service will interfere with Frigate's built i
|
|||||||
### Does face recognition run on the recording stream?
|
### Does face recognition run on the recording stream?
|
||||||
|
|
||||||
Face recognition does not run on the recording stream, this would be suboptimal for many reasons:
|
Face recognition does not run on the recording stream, this would be suboptimal for many reasons:
|
||||||
|
|
||||||
1. The latency of accessing the recordings means the notifications would not include the names of recognized people because recognition would not complete until after.
|
1. The latency of accessing the recordings means the notifications would not include the names of recognized people because recognition would not complete until after.
|
||||||
2. The embedding models used run on a set image size, so larger images will be scaled down to match this anyway.
|
2. The embedding models used run on a set image size, so larger images will be scaled down to match this anyway.
|
||||||
3. Motion clarity is much more important than extra pixels, over-compression and motion blur are much more detrimental to results than resolution.
|
3. Motion clarity is much more important than extra pixels, over-compression and motion blur are much more detrimental to results than resolution.
|
||||||
|
|||||||
@ -9,7 +9,7 @@ Some presets of FFmpeg args are provided by default to make the configuration ea
|
|||||||
|
|
||||||
It is highly recommended to use hwaccel presets in the config. These presets not only replace the longer args, but they also give Frigate hints of what hardware is available and allows Frigate to make other optimizations using the GPU such as when encoding the birdseye restream or when scaling a stream that has a size different than the native stream size.
|
It is highly recommended to use hwaccel presets in the config. These presets not only replace the longer args, but they also give Frigate hints of what hardware is available and allows Frigate to make other optimizations using the GPU such as when encoding the birdseye restream or when scaling a stream that has a size different than the native stream size.
|
||||||
|
|
||||||
See [the hwaccel docs](/configuration/hardware_acceleration.md) for more info on how to setup hwaccel for your GPU / iGPU.
|
See [the hwaccel docs](/configuration/hardware_acceleration_video.md) for more info on how to setup hwaccel for your GPU / iGPU.
|
||||||
|
|
||||||
| Preset | Usage | Other Notes |
|
| Preset | Usage | Other Notes |
|
||||||
| --------------------- | ------------------------------ | ----------------------------------------------------- |
|
| --------------------- | ------------------------------ | ----------------------------------------------------- |
|
||||||
|
|||||||
32
docs/docs/configuration/hardware_acceleration_enrichments.md
Normal file
32
docs/docs/configuration/hardware_acceleration_enrichments.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
id: hardware_acceleration_enrichments
|
||||||
|
title: Enrichments
|
||||||
|
---
|
||||||
|
|
||||||
|
# Enrichments
|
||||||
|
|
||||||
|
Some of Frigate's enrichments can use a discrete GPU for accelerated processing.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Object detection and enrichments (like Semantic Search, Face Recognition, and License Plate Recognition) are independent features. To use a GPU for object detection, see the [Object Detectors](/configuration/object_detectors.md) documentation. If you want to use your GPU for any supported enrichments, you must choose the appropriate Frigate Docker image for your GPU and configure the enrichment according to its specific documentation.
|
||||||
|
|
||||||
|
- **AMD**
|
||||||
|
|
||||||
|
- ROCm will automatically be detected and used for enrichments in the `-rocm` Frigate image.
|
||||||
|
|
||||||
|
- **Intel**
|
||||||
|
|
||||||
|
- OpenVINO will automatically be detected and used for enrichments in the default Frigate image.
|
||||||
|
|
||||||
|
- **Nvidia**
|
||||||
|
- Nvidia GPUs will automatically be detected and used for enrichments in the `-tensorrt` Frigate image.
|
||||||
|
- Jetson devices will automatically be detected and used for enrichments in the `-tensorrt-jp6` Frigate image.
|
||||||
|
|
||||||
|
Utilizing a GPU for enrichments does not require you to use the same GPU for object detection. For example, you can run the `tensorrt` Docker image for enrichments and still use other dedicated hardware for object detection.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
A Google Coral is a TPU (Tensor Processing Unit), not a dedicated GPU (Graphics Processing Unit) and therefore does not provide any kind of acceleration for Frigate's enrichments.
|
||||||
|
|
||||||
|
:::
|
||||||
@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
id: hardware_acceleration
|
id: hardware_acceleration_video
|
||||||
title: Hardware Acceleration
|
title: Video Decoding
|
||||||
---
|
---
|
||||||
|
|
||||||
# Hardware Acceleration
|
# Video Decoding
|
||||||
|
|
||||||
It is highly recommended to use a GPU for hardware acceleration in Frigate. Some types of hardware acceleration are detected and used automatically, but you may need to update your configuration to enable hardware accelerated decoding in ffmpeg.
|
It is highly recommended to use a GPU for hardware acceleration video decoding in Frigate. Some types of hardware acceleration are detected and used automatically, but you may need to update your configuration to enable hardware accelerated decoding in ffmpeg.
|
||||||
|
|
||||||
Depending on your system, these parameters may not be compatible. More information on hardware accelerated decoding for ffmpeg can be found here: https://trac.ffmpeg.org/wiki/HWAccelIntro
|
Depending on your system, these parameters may not be compatible. More information on hardware accelerated decoding for ffmpeg can be found here: https://trac.ffmpeg.org/wiki/HWAccelIntro
|
||||||
|
|
||||||
# Officially Supported
|
# Object Detection
|
||||||
|
|
||||||
## Raspberry Pi 3/4
|
## Raspberry Pi 3/4
|
||||||
|
|
||||||
@ -70,11 +70,11 @@ Or map in all the `/dev/video*` devices.
|
|||||||
**Recommended hwaccel Preset**
|
**Recommended hwaccel Preset**
|
||||||
|
|
||||||
| CPU Generation | Intel Driver | Recommended Preset | Notes |
|
| CPU Generation | Intel Driver | Recommended Preset | Notes |
|
||||||
| -------------- | ------------ | ------------------ | ----------------------------------- |
|
| -------------- | ------------ | ------------------- | ------------------------------------ |
|
||||||
| gen1 - gen7 | i965 | preset-vaapi | qsv is not supported |
|
| gen1 - gen7 | i965 | preset-vaapi | qsv is not supported |
|
||||||
| gen8 - gen12 | iHD | preset-vaapi | preset-intel-qsv-* can also be used |
|
| gen8 - gen12 | iHD | preset-vaapi | preset-intel-qsv-\* can also be used |
|
||||||
| gen13+ | iHD / Xe | preset-intel-qsv-* | |
|
| gen13+ | iHD / Xe | preset-intel-qsv-\* | |
|
||||||
| Intel Arc GPU | iHD / Xe | preset-intel-qsv-* | |
|
| Intel Arc GPU | iHD / Xe | preset-intel-qsv-\* | |
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@ -3,17 +3,18 @@ id: license_plate_recognition
|
|||||||
title: License Plate Recognition (LPR)
|
title: License Plate Recognition (LPR)
|
||||||
---
|
---
|
||||||
|
|
||||||
Frigate can recognize license plates on vehicles and automatically add the detected characters to the `recognized_license_plate` field or a known name as a `sub_label` to tracked objects of type `car`. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street.
|
Frigate can recognize license plates on vehicles and automatically add the detected characters to the `recognized_license_plate` field or a known name as a `sub_label` to tracked objects of type `car` or `motorcycle`. A common use case may be to read the license plates of cars pulling into a driveway or cars passing by on a street.
|
||||||
|
|
||||||
LPR works best when the license plate is clearly visible to the camera. For moving vehicles, Frigate continuously refines the recognition process, keeping the most confident result. However, LPR does not run on stationary vehicles.
|
LPR works best when the license plate is clearly visible to the camera. For moving vehicles, Frigate continuously refines the recognition process, keeping the most confident result. However, LPR does not run on stationary vehicles.
|
||||||
|
|
||||||
When a plate is recognized, the recognized name is:
|
When a plate is recognized, the details are:
|
||||||
|
|
||||||
- Added as a `sub_label` (if known) or the `recognized_license_plate` field (if unknown) to a tracked object.
|
- Added as a `sub_label` (if known) or the `recognized_license_plate` field (if unknown) to a tracked object.
|
||||||
- Viewable in the Review Item Details pane in Review (sub labels).
|
- Viewable in the Review Item Details pane in Review (sub labels).
|
||||||
- Viewable in the Tracked Object Details pane in Explore (sub labels and recognized license plates).
|
- Viewable in the Tracked Object Details pane in Explore (sub labels and recognized license plates).
|
||||||
- Filterable through the More Filters menu in Explore.
|
- Filterable through the More Filters menu in Explore.
|
||||||
- Published via the `frigate/events` MQTT topic as a `sub_label` (known) or `recognized_license_plate` (unknown) for the `car` tracked object.
|
- Published via the `frigate/events` MQTT topic as a `sub_label` (known) or `recognized_license_plate` (unknown) for the `car` or `motorcycle` tracked object.
|
||||||
|
- Published via the `frigate/tracked_object_update` MQTT topic with `name` (if known) and `plate`.
|
||||||
|
|
||||||
## Model Requirements
|
## Model Requirements
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ Users without a model that detects license plates can still run LPR. Frigate use
|
|||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
||||||
In the default mode, Frigate's LPR needs to first detect a `car` before it can recognize a license plate. If you're using a dedicated LPR camera and have a zoomed-in view where a `car` will not be detected, you can still run LPR, but the configuration parameters will differ from the default mode. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section below.
|
In the default mode, Frigate's LPR needs to first detect a `car` or `motorcycle` before it can recognize a license plate. If you're using a dedicated LPR camera and have a zoomed-in view where a `car` or `motorcycle` will not be detected, you can still run LPR, but the configuration parameters will differ from the default mode. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section below.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@ -50,13 +51,13 @@ cameras:
|
|||||||
enabled: False
|
enabled: False
|
||||||
```
|
```
|
||||||
|
|
||||||
For non-dedicated LPR cameras, ensure that your camera is configured to detect objects of type `car`, and that a car is actually being detected by Frigate. Otherwise, LPR will not run.
|
For non-dedicated LPR cameras, ensure that your camera is configured to detect objects of type `car` or `motorcycle`, and that a car or motorcycle is actually being detected by Frigate. Otherwise, LPR will not run.
|
||||||
|
|
||||||
Like the other real-time processors in Frigate, license plate recognition runs on the camera stream defined by the `detect` role in your config. To ensure optimal performance, select a suitable resolution for this stream in your camera's firmware that fits your specific scene and requirements.
|
Like the other real-time processors in Frigate, license plate recognition runs on the camera stream defined by the `detect` role in your config. To ensure optimal performance, select a suitable resolution for this stream in your camera's firmware that fits your specific scene and requirements.
|
||||||
|
|
||||||
## Advanced Configuration
|
## Advanced Configuration
|
||||||
|
|
||||||
Fine-tune the LPR feature using these optional parameters at the global level of your config. The only optional parameters that should be set at the camera level are `enabled`, `min_area`, and `enhancement`.
|
Fine-tune the LPR feature using these optional parameters at the global level of your config. The only optional parameters that can be set at the camera level are `enabled`, `min_area`, and `enhancement`.
|
||||||
|
|
||||||
### Detection
|
### Detection
|
||||||
|
|
||||||
@ -68,10 +69,10 @@ Fine-tune the LPR feature using these optional parameters at the global level of
|
|||||||
- Depending on the resolution of your camera's `detect` stream, you can increase this value to ignore small or distant plates.
|
- Depending on the resolution of your camera's `detect` stream, you can increase this value to ignore small or distant plates.
|
||||||
- **`device`**: Device to use to run license plate recognition models.
|
- **`device`**: Device to use to run license plate recognition models.
|
||||||
- Default: `CPU`
|
- Default: `CPU`
|
||||||
- This can be `CPU` or `GPU`. For users without a model that detects license plates natively, using a GPU may increase performance of the models, especially the YOLOv9 license plate detector model.
|
- This can be `CPU` or `GPU`. For users without a model that detects license plates natively, using a GPU may increase performance of the models, especially the YOLOv9 license plate detector model. See the [Hardware Accelerated Enrichments](/configuration/hardware_acceleration_enrichments.md) documentation.
|
||||||
- **`model_size`**: The size of the model used to detect text on plates.
|
- **`model_size`**: The size of the model used to detect text on plates.
|
||||||
- Default: `small`
|
- Default: `small`
|
||||||
- This can be `small` or `large`. The `large` model uses an enhanced text detector and is more accurate at finding text on plates but slower than the `small` model. For most users, the small model is recommended. For users in countries with multiple lines of text on plates, the large model is recommended. Note that using the large does not improve _text recognition_, but it may improve _text detection_.
|
- This can be `small` or `large`. The `large` model uses an enhanced text detector and is more accurate at finding text on plates but slower than the `small` model. For most users, the small model is recommended. For users in countries with multiple lines of text on plates, the large model is recommended. Note that using the large model does not improve _text recognition_, but it may improve _text detection_.
|
||||||
|
|
||||||
### Recognition
|
### Recognition
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ Fine-tune the LPR feature using these optional parameters at the global level of
|
|||||||
|
|
||||||
### Matching
|
### Matching
|
||||||
|
|
||||||
- **`known_plates`**: List of strings or regular expressions that assign custom a `sub_label` to `car` objects when a recognized plate matches a known value.
|
- **`known_plates`**: List of strings or regular expressions that assign custom a `sub_label` to `car` and `motorcycle` objects when a recognized plate matches a known value.
|
||||||
- These labels appear in the UI, filters, and notifications.
|
- These labels appear in the UI, filters, and notifications.
|
||||||
- Unknown plates are still saved but are added to the `recognized_license_plate` field rather than the `sub_label`.
|
- Unknown plates are still saved but are added to the `recognized_license_plate` field rather than the `sub_label`.
|
||||||
- **`match_distance`**: Allows for minor variations (missing/incorrect characters) when matching a detected plate to a known plate.
|
- **`match_distance`**: Allows for minor variations (missing/incorrect characters) when matching a detected plate to a known plate.
|
||||||
@ -184,7 +185,7 @@ cameras:
|
|||||||
ffmpeg: ... # add your streams
|
ffmpeg: ... # add your streams
|
||||||
detect:
|
detect:
|
||||||
enabled: True
|
enabled: True
|
||||||
fps: 5 # increase to 10 if vehicles move quickly across your frame. Higher than 15 is unnecessary and is not recommended.
|
fps: 5 # increase to 10 if vehicles move quickly across your frame. Higher than 10 is unnecessary and is not recommended.
|
||||||
min_initialized: 2
|
min_initialized: 2
|
||||||
width: 1920
|
width: 1920
|
||||||
height: 1080
|
height: 1080
|
||||||
@ -216,7 +217,7 @@ With this setup:
|
|||||||
- Snapshots will have license plate bounding boxes on them.
|
- Snapshots will have license plate bounding boxes on them.
|
||||||
- The `frigate/events` MQTT topic will publish tracked object updates.
|
- The `frigate/events` MQTT topic will publish tracked object updates.
|
||||||
- Debug view will display `license_plate` bounding boxes.
|
- Debug view will display `license_plate` bounding boxes.
|
||||||
- If you are using a Frigate+ model and want to submit images from your dedicated LPR camera for model training and fine-tuning, annotate both the `car` and the `license_plate` in the snapshots on the Frigate+ website, even if the car is barely visible.
|
- If you are using a Frigate+ model and want to submit images from your dedicated LPR camera for model training and fine-tuning, annotate both the `car` / `motorcycle` and the `license_plate` in the snapshots on the Frigate+ website, even if the car is barely visible.
|
||||||
|
|
||||||
### Using the Secondary LPR Pipeline (Without Frigate+)
|
### Using the Secondary LPR Pipeline (Without Frigate+)
|
||||||
|
|
||||||
@ -228,7 +229,7 @@ An example configuration for a dedicated LPR camera using the secondary pipeline
|
|||||||
# LPR global configuration
|
# LPR global configuration
|
||||||
lpr:
|
lpr:
|
||||||
enabled: True
|
enabled: True
|
||||||
device: CPU # can also be GPU if available
|
device: CPU # can also be GPU if available and correct Docker image is used
|
||||||
detection_threshold: 0.7 # change if necessary
|
detection_threshold: 0.7 # change if necessary
|
||||||
|
|
||||||
# Dedicated LPR camera configuration
|
# Dedicated LPR camera configuration
|
||||||
@ -310,9 +311,9 @@ Recognized plates will show as object labels in the debug view and will appear i
|
|||||||
|
|
||||||
If you are still having issues detecting plates, start with a basic configuration and see the debugging tips below.
|
If you are still having issues detecting plates, start with a basic configuration and see the debugging tips below.
|
||||||
|
|
||||||
### Can I run LPR without detecting `car` objects?
|
### Can I run LPR without detecting `car` or `motorcycle` objects?
|
||||||
|
|
||||||
In normal LPR mode, Frigate requires a `car` to be detected first before recognizing a license plate. If you have a dedicated LPR camera, you can change the camera `type` to `"lpr"` to use the Dedicated LPR Camera algorithm. This comes with important caveats, though. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section above.
|
In normal LPR mode, Frigate requires a `car` or `motorcycle` to be detected first before recognizing a license plate. If you have a dedicated LPR camera, you can change the camera `type` to `"lpr"` to use the Dedicated LPR Camera algorithm. This comes with important caveats, though. See the [Dedicated LPR Cameras](#dedicated-lpr-cameras) section above.
|
||||||
|
|
||||||
### How can I improve detection accuracy?
|
### How can I improve detection accuracy?
|
||||||
|
|
||||||
@ -335,8 +336,8 @@ Use `match_distance` to allow small character mismatches. Alternatively, define
|
|||||||
### How do I debug LPR issues?
|
### How do I debug LPR issues?
|
||||||
|
|
||||||
- View MQTT messages for `frigate/events` to verify detected plates.
|
- View MQTT messages for `frigate/events` to verify detected plates.
|
||||||
- If you are using a Frigate+ model or a model that detects license plates, watch the debug view (Settings --> Debug) to ensure that `license_plate` is being detected with a `car`.
|
- If you are using a Frigate+ model or a model that detects license plates, watch the debug view (Settings --> Debug) to ensure that `license_plate` is being detected with a `car` or `motorcycle`.
|
||||||
- Watch the debug view to see plates recognized in real-time. For non-dedicated LPR cameras, the `car` label will change to the recognized plate when LPR is enabled and working.
|
- Watch the debug view to see plates recognized in real-time. For non-dedicated LPR cameras, the `car` or `motorcycle` label will change to the recognized plate when LPR is enabled and working.
|
||||||
- Adjust `detection_threshold` and `recognition_threshold` settings per the suggestions [above](#advanced-configuration).
|
- Adjust `detection_threshold` and `recognition_threshold` settings per the suggestions [above](#advanced-configuration).
|
||||||
- Enable `debug_save_plates` to save images of detected text on plates to the clips directory (`/media/frigate/clips/lpr`). Ensure these images are readable and the text is clear.
|
- Enable `debug_save_plates` to save images of detected text on plates to the clips directory (`/media/frigate/clips/lpr`). Ensure these images are readable and the text is clear.
|
||||||
- Enable debug logs for LPR by adding `frigate.data_processing.common.license_plate: debug` to your `logger` configuration. These logs are _very_ verbose, so only enable this when necessary.
|
- Enable debug logs for LPR by adding `frigate.data_processing.common.license_plate: debug` to your `logger` configuration. These logs are _very_ verbose, so only enable this when necessary.
|
||||||
@ -356,4 +357,12 @@ LPR's performance impact depends on your hardware. Ensure you have at least 4GB
|
|||||||
|
|
||||||
The YOLOv9 license plate detector model will run (and the metric will appear) if you've enabled LPR but haven't defined `license_plate` as an object to track, either at the global or camera level.
|
The YOLOv9 license plate detector model will run (and the metric will appear) if you've enabled LPR but haven't defined `license_plate` as an object to track, either at the global or camera level.
|
||||||
|
|
||||||
If you are detecting `car` on cameras where you don't want to run LPR, make sure you disable LPR it at the camera level. And if you do want to run LPR on those cameras, make sure you define `license_plate` as an object to track.
|
If you are detecting `car` or `motorcycle` on cameras where you don't want to run LPR, make sure you disable LPR it at the camera level. And if you do want to run LPR on those cameras, make sure you define `license_plate` as an object to track.
|
||||||
|
|
||||||
|
### It looks like Frigate picked up my camera's timestamp as the license plate. How can I prevent this?
|
||||||
|
|
||||||
|
This could happen if cars or motorcycles travel close to your camera's timestamp. You could either move the timestamp through your camera's firmware, or apply a mask to it in Frigate.
|
||||||
|
|
||||||
|
If you are using a model that natively detects `license_plate`, add an _object mask_ of type `license_plate` and a _motion mask_ over your timestamp.
|
||||||
|
|
||||||
|
If you are using dedicated LPR camera mode, only a _motion mask_ over your timestamp is required.
|
||||||
|
|||||||
@ -152,7 +152,7 @@ Use this configuration for YOLO-based models. When no custom model path or URL i
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
detectors:
|
detectors:
|
||||||
hailo8l:
|
hailo:
|
||||||
type: hailo8l
|
type: hailo8l
|
||||||
device: PCIe
|
device: PCIe
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ For SSD-based models, provide either a model path or URL to your compiled SSD mo
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
detectors:
|
detectors:
|
||||||
hailo8l:
|
hailo:
|
||||||
type: hailo8l
|
type: hailo8l
|
||||||
device: PCIe
|
device: PCIe
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ The Hailo detector supports all YOLO models compiled for Hailo hardware that inc
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
detectors:
|
detectors:
|
||||||
hailo8l:
|
hailo:
|
||||||
type: hailo8l
|
type: hailo8l
|
||||||
device: PCIe
|
device: PCIe
|
||||||
|
|
||||||
@ -484,7 +484,7 @@ frigate:
|
|||||||
|
|
||||||
### Configuration Parameters
|
### Configuration Parameters
|
||||||
|
|
||||||
The TensorRT detector can be selected by specifying `tensorrt` as the model type. The GPU will need to be passed through to the docker container using the same methods described in the [Hardware Acceleration](hardware_acceleration.md#nvidia-gpus) section. If you pass through multiple GPUs, you can select which GPU is used for a detector with the `device` configuration parameter. The `device` parameter is an integer value of the GPU index, as shown by `nvidia-smi` within the container.
|
The TensorRT detector can be selected by specifying `tensorrt` as the model type. The GPU will need to be passed through to the docker container using the same methods described in the [Hardware Acceleration](hardware_acceleration_video.md#nvidia-gpus) section. If you pass through multiple GPUs, you can select which GPU is used for a detector with the `device` configuration parameter. The `device` parameter is an integer value of the GPU index, as shown by `nvidia-smi` within the container.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
@ -610,7 +610,7 @@ If the correct build is used for your GPU then the GPU will be detected and used
|
|||||||
|
|
||||||
- **Nvidia**
|
- **Nvidia**
|
||||||
- Nvidia GPUs will automatically be detected and used with the ONNX detector in the `-tensorrt` Frigate image.
|
- Nvidia GPUs will automatically be detected and used with the ONNX detector in the `-tensorrt` Frigate image.
|
||||||
- Jetson devices will automatically be detected and used with the ONNX detector in the `-tensorrt-jp(4/5)` Frigate image.
|
- Jetson devices will automatically be detected and used with the ONNX detector in the `-tensorrt-jp6` Frigate image.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@ -811,7 +811,23 @@ Hardware accelerated object detection is supported on the following SoCs:
|
|||||||
- RK3576
|
- RK3576
|
||||||
- RK3588
|
- RK3588
|
||||||
|
|
||||||
This implementation uses the [Rockchip's RKNN-Toolkit2](https://github.com/airockchip/rknn-toolkit2/), version v2.3.0. Currently, only [Yolo-NAS](https://github.com/Deci-AI/super-gradients/blob/master/YOLONAS.md) is supported as object detection model.
|
This implementation uses the [Rockchip's RKNN-Toolkit2](https://github.com/airockchip/rknn-toolkit2/), version v2.3.2.
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
When using many cameras one detector may not be enough to keep up. Multiple detectors can be defined assuming NPU resources are available. An example configuration would be:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
detectors:
|
||||||
|
rknn_0:
|
||||||
|
type: rknn
|
||||||
|
num_cores: 0
|
||||||
|
rknn_1:
|
||||||
|
type: rknn
|
||||||
|
num_cores: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
@ -845,13 +861,13 @@ detectors: # required
|
|||||||
The inference time was determined on a rk3588 with 3 NPU cores.
|
The inference time was determined on a rk3588 with 3 NPU cores.
|
||||||
|
|
||||||
| Model | Size in mb | Inference time in ms |
|
| Model | Size in mb | Inference time in ms |
|
||||||
| ------------------- | ---------- | -------------------- |
|
| --------------------- | ---------- | -------------------- |
|
||||||
| deci-fp16-yolonas_s | 24 | 25 |
|
| deci-fp16-yolonas_s | 24 | 25 |
|
||||||
| deci-fp16-yolonas_m | 62 | 35 |
|
| deci-fp16-yolonas_m | 62 | 35 |
|
||||||
| deci-fp16-yolonas_l | 81 | 45 |
|
| deci-fp16-yolonas_l | 81 | 45 |
|
||||||
| yolov9_tiny | 8 | 35 |
|
| frigate-fp16-yolov9-t | 6 | 35 |
|
||||||
| yolox_nano | 3 | 16 |
|
| rock-i8-yolox_nano | 3 | 14 |
|
||||||
| yolox_tiny | 6 | 20 |
|
| rock-i8_yolox_tiny | 6 | 18 |
|
||||||
|
|
||||||
- All models are automatically downloaded and stored in the folder `config/model_cache/rknn_cache`. After upgrading Frigate, you should remove older models to free up space.
|
- All models are automatically downloaded and stored in the folder `config/model_cache/rknn_cache`. After upgrading Frigate, you should remove older models to free up space.
|
||||||
- You can also provide your own `.rknn` model. You should not save your own models in the `rknn_cache` folder, store them directly in the `model_cache` folder or another subfolder. To convert a model to `.rknn` format see the `rknn-toolkit2` (requires a x86 machine). Note, that there is only post-processing for the supported models.
|
- You can also provide your own `.rknn` model. You should not save your own models in the `rknn_cache` folder, store them directly in the `model_cache` folder or another subfolder. To convert a model to `.rknn` format see the `rknn-toolkit2` (requires a x86 machine). Note, that there is only post-processing for the supported models.
|
||||||
@ -887,10 +903,13 @@ The pre-trained YOLO-NAS weights from DeciAI are subject to their license and ca
|
|||||||
model: # required
|
model: # required
|
||||||
# name of model (will be automatically downloaded) or path to your own .rknn model file
|
# name of model (will be automatically downloaded) or path to your own .rknn model file
|
||||||
# possible values are:
|
# possible values are:
|
||||||
# - yolov9-t
|
# - frigate-fp16-yolov9-t
|
||||||
# - yolov9-s
|
# - frigate-fp16-yolov9-s
|
||||||
|
# - frigate-fp16-yolov9-m
|
||||||
|
# - frigate-fp16-yolov9-c
|
||||||
|
# - frigate-fp16-yolov9-e
|
||||||
# your yolo_model.rknn
|
# your yolo_model.rknn
|
||||||
path: /config/model_cache/rknn_cache/yolov9-t.rknn
|
path: frigate-fp16-yolov9-t
|
||||||
model_type: yolo-generic
|
model_type: yolo-generic
|
||||||
width: 320
|
width: 320
|
||||||
height: 320
|
height: 320
|
||||||
@ -905,10 +924,12 @@ model: # required
|
|||||||
model: # required
|
model: # required
|
||||||
# name of model (will be automatically downloaded) or path to your own .rknn model file
|
# name of model (will be automatically downloaded) or path to your own .rknn model file
|
||||||
# possible values are:
|
# possible values are:
|
||||||
# - yolox_nano
|
# - rock-i8-yolox_nano
|
||||||
# - yolox_tiny
|
# - rock-i8-yolox_tiny
|
||||||
|
# - rock-fp16-yolox_nano
|
||||||
|
# - rock-fp16-yolox_tiny
|
||||||
# your yolox_model.rknn
|
# your yolox_model.rknn
|
||||||
path: yolox_tiny
|
path: rock-i8-yolox_nano
|
||||||
model_type: yolox
|
model_type: yolox
|
||||||
width: 416
|
width: 416
|
||||||
height: 416
|
height: 416
|
||||||
@ -948,7 +969,7 @@ Explanation of the paramters:
|
|||||||
- `soc`: the SoC this model was build for (e.g. "rk3588")
|
- `soc`: the SoC this model was build for (e.g. "rk3588")
|
||||||
- `tk_version`: Version of `rknn-toolkit2` (e.g. "2.3.0")
|
- `tk_version`: Version of `rknn-toolkit2` (e.g. "2.3.0")
|
||||||
- **example**: Specifying `output_name = "frigate-{quant}-{input_basename}-{soc}-v{tk_version}"` could result in a model called `frigate-i8-my_model-rk3588-v2.3.0.rknn`.
|
- **example**: Specifying `output_name = "frigate-{quant}-{input_basename}-{soc}-v{tk_version}"` could result in a model called `frigate-i8-my_model-rk3588-v2.3.0.rknn`.
|
||||||
- `config`: Configuration passed to `rknn-toolkit2` for model conversion. For an explanation of all available parameters have a look at section "2.2. Model configuration" of [this manual](https://github.com/MarcA711/rknn-toolkit2/releases/download/v2.3.0/03_Rockchip_RKNPU_API_Reference_RKNN_Toolkit2_V2.3.0_EN.pdf).
|
- `config`: Configuration passed to `rknn-toolkit2` for model conversion. For an explanation of all available parameters have a look at section "2.2. Model configuration" of [this manual](https://github.com/MarcA711/rknn-toolkit2/releases/download/v2.3.2/03_Rockchip_RKNPU_API_Reference_RKNN_Toolkit2_V2.3.2_EN.pdf).
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
|
|
||||||
|
|||||||
@ -174,6 +174,10 @@ To reduce the output file size the ffmpeg parameter `-qp n` can be utilized (whe
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Apple Compatibility with H.265 Streams
|
||||||
|
|
||||||
|
Apple devices running the Safari browser may fail to playback h.265 recordings. The [apple compatibility option](../configuration/camera_specific.md#h265-cameras-via-safari) should be used to ensure seamless playback on Apple devices.
|
||||||
|
|
||||||
## Syncing Recordings With Disk
|
## Syncing Recordings With Disk
|
||||||
|
|
||||||
In some cases the recordings files may be deleted but Frigate will not know this has happened. Recordings sync can be enabled which will tell Frigate to check the file system and delete any db entries for files which don't exist.
|
In some cases the recordings files may be deleted but Frigate will not know this has happened. Recordings sync can be enabled which will tell Frigate to check the file system and delete any db entries for files which don't exist.
|
||||||
|
|||||||
@ -78,16 +78,19 @@ proxy:
|
|||||||
# Optional: Mapping for headers from upstream proxies. Only used if Frigate's auth
|
# Optional: Mapping for headers from upstream proxies. Only used if Frigate's auth
|
||||||
# is disabled.
|
# is disabled.
|
||||||
# NOTE: Many authentication proxies pass a header downstream with the authenticated
|
# NOTE: Many authentication proxies pass a header downstream with the authenticated
|
||||||
# user name. Not all values are supported. It must be a whitelisted header.
|
# user name and role. Not all values are supported. It must be a whitelisted header.
|
||||||
# See the docs for more info.
|
# See the docs for more info.
|
||||||
header_map:
|
header_map:
|
||||||
user: x-forwarded-user
|
user: x-forwarded-user
|
||||||
|
role: x-forwarded-role
|
||||||
# Optional: Url for logging out a user. This sets the location of the logout url in
|
# Optional: Url for logging out a user. This sets the location of the logout url in
|
||||||
# the UI.
|
# the UI.
|
||||||
logout_url: /api/logout
|
logout_url: /api/logout
|
||||||
# Optional: Auth secret that is checked against the X-Proxy-Secret header sent from
|
# Optional: Auth secret that is checked against the X-Proxy-Secret header sent from
|
||||||
# the proxy. If not set, all requests are trusted regardless of origin.
|
# the proxy. If not set, all requests are trusted regardless of origin.
|
||||||
auth_secret: None
|
auth_secret: None
|
||||||
|
# Optional: The default role to use for proxy auth. Must be "admin" or "viewer"
|
||||||
|
default_role: viewer
|
||||||
|
|
||||||
# Optional: Authentication configuration
|
# Optional: Authentication configuration
|
||||||
auth:
|
auth:
|
||||||
@ -543,9 +546,9 @@ semantic_search:
|
|||||||
model_size: "small"
|
model_size: "small"
|
||||||
|
|
||||||
# Optional: Configuration for face recognition capability
|
# Optional: Configuration for face recognition capability
|
||||||
# NOTE: Can (enabled, min_area) be overridden at the camera level
|
# NOTE: enabled, min_area can be overridden at the camera level
|
||||||
face_recognition:
|
face_recognition:
|
||||||
# Optional: Enable semantic search (default: shown below)
|
# Optional: Enable face recognition (default: shown below)
|
||||||
enabled: False
|
enabled: False
|
||||||
# Optional: Minimum face distance score required to mark as a potential match (default: shown below)
|
# Optional: Minimum face distance score required to mark as a potential match (default: shown below)
|
||||||
unknown_score: 0.8
|
unknown_score: 0.8
|
||||||
@ -560,6 +563,8 @@ face_recognition:
|
|||||||
save_attempts: 100
|
save_attempts: 100
|
||||||
# Optional: Apply a blur quality filter to adjust confidence based on the blur level of the image (default: shown below)
|
# Optional: Apply a blur quality filter to adjust confidence based on the blur level of the image (default: shown below)
|
||||||
blur_confidence_filter: True
|
blur_confidence_filter: True
|
||||||
|
# Optional: Set the model size used face recognition. (default: shown below)
|
||||||
|
model_size: small
|
||||||
|
|
||||||
# Optional: Configuration for license plate recognition capability
|
# Optional: Configuration for license plate recognition capability
|
||||||
# NOTE: enabled, min_area, and enhancement can be overridden at the camera level
|
# NOTE: enabled, min_area, and enhancement can be overridden at the camera level
|
||||||
|
|||||||
@ -90,19 +90,7 @@ semantic_search:
|
|||||||
|
|
||||||
If the correct build is used for your GPU and the `large` model is configured, then the GPU will be detected and used automatically.
|
If the correct build is used for your GPU and the `large` model is configured, then the GPU will be detected and used automatically.
|
||||||
|
|
||||||
**NOTE:** Object detection and Semantic Search are independent features. If you want to use your GPU with Semantic Search, you must choose the appropriate Frigate Docker image for your GPU.
|
See the [Hardware Accelerated Enrichments](/configuration/hardware_acceleration_enrichments.md) documentation.
|
||||||
|
|
||||||
- **AMD**
|
|
||||||
|
|
||||||
- ROCm will automatically be detected and used for Semantic Search in the `-rocm` Frigate image.
|
|
||||||
|
|
||||||
- **Intel**
|
|
||||||
|
|
||||||
- OpenVINO will automatically be detected and used for Semantic Search in the default Frigate image.
|
|
||||||
|
|
||||||
- **Nvidia**
|
|
||||||
- Nvidia GPUs will automatically be detected and used for Semantic Search in the `-tensorrt` Frigate image.
|
|
||||||
- Jetson devices will automatically be detected and used for Semantic Search in the `-tensorrt-jp(4/5)` Frigate image.
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,13 @@ Only car objects can trigger the `front_yard_street` zone and only person can tr
|
|||||||
|
|
||||||
### Zone Loitering
|
### Zone Loitering
|
||||||
|
|
||||||
Sometimes objects are expected to be passing through a zone, but an object loitering in an area is unexpected. Zones can be configured to have a minimum loitering time before the object will be considered in the zone.
|
Sometimes objects are expected to be passing through a zone, but an object loitering in an area is unexpected. Zones can be configured to have a minimum loitering time after which the object will be considered in the zone.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
When using loitering zones, a review item will remain active until the object leaves. Loitering zones are only meant to be used in areas where loitering is not expected behavior.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
cameras:
|
cameras:
|
||||||
|
|||||||
@ -91,4 +91,4 @@ The `CODEOWNERS` file should be updated to include the `docker/board` along with
|
|||||||
|
|
||||||
# Docs
|
# Docs
|
||||||
|
|
||||||
At a minimum the `installation`, `object_detectors`, `hardware_acceleration`, and `ffmpeg-presets` docs should be updated (if applicable) to reflect the configuration of this community board.
|
At a minimum the `installation`, `object_detectors`, `hardware_acceleration_video`, and `ffmpeg-presets` docs should be updated (if applicable) to reflect the configuration of this community board.
|
||||||
|
|||||||
@ -38,6 +38,7 @@ Frigate supports multiple different detectors that work on different types of ha
|
|||||||
**Most Hardware**
|
**Most Hardware**
|
||||||
|
|
||||||
- [Hailo](#hailo-8): The Hailo8 and Hailo8L AI Acceleration module is available in m.2 format with a HAT for RPi devices offering a wide range of compatibility with devices.
|
- [Hailo](#hailo-8): The Hailo8 and Hailo8L AI Acceleration module is available in m.2 format with a HAT for RPi devices offering a wide range of compatibility with devices.
|
||||||
|
|
||||||
- [Supports many model architectures](../../configuration/object_detectors#configuration)
|
- [Supports many model architectures](../../configuration/object_detectors#configuration)
|
||||||
- Runs best with tiny or small size models
|
- Runs best with tiny or small size models
|
||||||
|
|
||||||
@ -73,10 +74,10 @@ Frigate supports multiple different detectors that work on different types of ha
|
|||||||
|
|
||||||
### Hailo-8
|
### Hailo-8
|
||||||
|
|
||||||
|
|
||||||
Frigate supports both the Hailo-8 and Hailo-8L AI Acceleration Modules on compatible hardware platforms—including the Raspberry Pi 5 with the PCIe hat from the AI kit. The Hailo detector integration in Frigate automatically identifies your hardware type and selects the appropriate default model when a custom model isn’t provided.
|
Frigate supports both the Hailo-8 and Hailo-8L AI Acceleration Modules on compatible hardware platforms—including the Raspberry Pi 5 with the PCIe hat from the AI kit. The Hailo detector integration in Frigate automatically identifies your hardware type and selects the appropriate default model when a custom model isn’t provided.
|
||||||
|
|
||||||
**Default Model Configuration:**
|
**Default Model Configuration:**
|
||||||
|
|
||||||
- **Hailo-8L:** Default model is **YOLOv6n**.
|
- **Hailo-8L:** Default model is **YOLOv6n**.
|
||||||
- **Hailo-8:** Default model is **YOLOv6n**.
|
- **Hailo-8:** Default model is **YOLOv6n**.
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ In real-world deployments, even with multiple cameras running concurrently, Frig
|
|||||||
### Google Coral TPU
|
### Google Coral TPU
|
||||||
|
|
||||||
Frigate supports both the USB and M.2 versions of the Google Coral.
|
Frigate supports both the USB and M.2 versions of the Google Coral.
|
||||||
|
|
||||||
- The USB version is compatible with the widest variety of hardware and does not require a driver on the host machine. However, it does lack the automatic throttling features of the other versions.
|
- The USB version is compatible with the widest variety of hardware and does not require a driver on the host machine. However, it does lack the automatic throttling features of the other versions.
|
||||||
- The PCIe and M.2 versions require installation of a driver on the host. Follow the instructions for your version from https://coral.ai
|
- The PCIe and M.2 versions require installation of a driver on the host. Follow the instructions for your version from https://coral.ai
|
||||||
|
|
||||||
@ -108,17 +110,14 @@ More information is available [in the detector docs](/configuration/object_detec
|
|||||||
Inference speeds vary greatly depending on the CPU or GPU used, some known examples of GPU inference times are below:
|
Inference speeds vary greatly depending on the CPU or GPU used, some known examples of GPU inference times are below:
|
||||||
|
|
||||||
| Name | MobileNetV2 Inference Time | YOLO-NAS Inference Time | RF-DETR Inference Time | Notes |
|
| Name | MobileNetV2 Inference Time | YOLO-NAS Inference Time | RF-DETR Inference Time | Notes |
|
||||||
| -------------------- | -------------------------- | ------------------------- | ------------------------- | -------------------------------------- |
|
| -------------- | -------------------------- | ------------------------- | ---------------------- | ---------------------------------- |
|
||||||
| Intel i3 6100T | 15 - 35 ms | | | Can only run one detector instance |
|
| Intel HD 530 | 15 - 35 ms | | | Can only run one detector instance |
|
||||||
| Intel i5 6500 | ~ 15 ms | | | |
|
| Intel HD 620 | 15 - 25 ms | 320: ~ 35 ms | | |
|
||||||
| Intel i5 7200u | 15 - 25 ms | | | |
|
| Intel HD 630 | ~ 15 ms | 320: ~ 30 ms | | |
|
||||||
| Intel i5 7500 | ~ 15 ms | | | |
|
| Intel UHD 730 | ~ 10 ms | 320: ~ 19 ms 640: ~ 54 ms | | |
|
||||||
| Intel i3 8100 | ~ 15 ms | | | |
|
| Intel UHD 770 | ~ 15 ms | 320: ~ 20 ms 640: ~ 46 ms | | |
|
||||||
| Intel i5 1135G7 | 10 - 15 ms | | | |
|
|
||||||
| Intel i3 12000 | | 320: ~ 19 ms 640: ~ 54 ms | | |
|
|
||||||
| Intel i5 12600K | ~ 15 ms | 320: ~ 20 ms 640: ~ 46 ms | | |
|
|
||||||
| Intel i7 12650H | ~ 15 ms | 320: ~ 20 ms 640: ~ 42 ms | 336: 50 ms | |
|
|
||||||
| Intel N100 | ~ 15 ms | 320: ~ 20 ms | | |
|
| Intel N100 | ~ 15 ms | 320: ~ 20 ms | | |
|
||||||
|
| Intel Iris XE | ~ 10 ms | 320: ~ 18 ms 640: ~ 50 ms | | |
|
||||||
| Intel Arc A380 | ~ 6 ms | 320: ~ 10 ms 640: ~ 22 ms | 336: 20 ms 448: 27 ms | |
|
| Intel Arc A380 | ~ 6 ms | 320: ~ 10 ms 640: ~ 22 ms | 336: 20 ms 448: 27 ms | |
|
||||||
| Intel Arc A750 | ~ 4 ms | 320: ~ 8 ms | | |
|
| Intel Arc A750 | ~ 4 ms | 320: ~ 8 ms | | |
|
||||||
|
|
||||||
@ -145,14 +144,14 @@ Inference speeds will vary greatly depending on the GPU and the model used.
|
|||||||
With the [rocm](../configuration/object_detectors.md#amdrocm-gpu-detector) detector Frigate can take advantage of many discrete AMD GPUs.
|
With the [rocm](../configuration/object_detectors.md#amdrocm-gpu-detector) detector Frigate can take advantage of many discrete AMD GPUs.
|
||||||
|
|
||||||
| Name | YOLOv9 Inference Time | YOLO-NAS Inference Time |
|
| Name | YOLOv9 Inference Time | YOLO-NAS Inference Time |
|
||||||
| --------------- | --------------------- | ------------------------- |
|
| -------- | --------------------- | ------------------------- |
|
||||||
| AMD 780M | ~ 14 ms | 320: ~ 30 ms 640: ~ 60 ms |
|
| AMD 780M | ~ 14 ms | 320: ~ 30 ms 640: ~ 60 ms |
|
||||||
|
|
||||||
## Community Supported Detectors
|
## Community Supported Detectors
|
||||||
|
|
||||||
### Nvidia Jetson
|
### Nvidia Jetson
|
||||||
|
|
||||||
Frigate supports all Jetson boards, from the inexpensive Jetson Nano to the powerful Jetson Orin AGX. It will [make use of the Jetson's hardware media engine](/configuration/hardware_acceleration#nvidia-jetson-orin-agx-orin-nx-orin-nano-xavier-agx-xavier-nx-tx2-tx1-nano) when configured with the [appropriate presets](/configuration/ffmpeg_presets#hwaccel-presets), and will make use of the Jetson's GPU and DLA for object detection when configured with the [TensorRT detector](/configuration/object_detectors#nvidia-tensorrt-detector).
|
Frigate supports all Jetson boards, from the inexpensive Jetson Nano to the powerful Jetson Orin AGX. It will [make use of the Jetson's hardware media engine](/configuration/hardware_acceleration_video#nvidia-jetson-orin-agx-orin-nx-orin-nano-xavier-agx-xavier-nx-tx2-tx1-nano) when configured with the [appropriate presets](/configuration/ffmpeg_presets#hwaccel-presets), and will make use of the Jetson's GPU and DLA for object detection when configured with the [TensorRT detector](/configuration/object_detectors#nvidia-tensorrt-detector).
|
||||||
|
|
||||||
Inference speed will vary depending on the YOLO model, jetson platform and jetson nvpmodel (GPU/DLA/EMC clock speed). It is typically 20-40 ms for most models. The DLA is more efficient than the GPU, but not faster, so using the DLA will reduce power consumption but will slightly increase inference time.
|
Inference speed will vary depending on the YOLO model, jetson platform and jetson nvpmodel (GPU/DLA/EMC clock speed). It is typically 20-40 ms for most models. The DLA is more efficient than the GPU, but not faster, so using the DLA will reduce power consumption but will slightly increase inference time.
|
||||||
|
|
||||||
@ -167,11 +166,10 @@ Frigate supports hardware video processing on all Rockchip boards. However, hard
|
|||||||
- RK3588
|
- RK3588
|
||||||
|
|
||||||
| Name | YOLOv9 Inference Time | YOLO-NAS Inference Time | YOLOx Inference Time |
|
| Name | YOLOv9 Inference Time | YOLO-NAS Inference Time | YOLOx Inference Time |
|
||||||
| --------------- | --------------------- | --------------------------- | ------------------------- |
|
| -------------- | --------------------- | --------------------------- | ----------------------- |
|
||||||
| rk3588 3 cores | ~ 35 ms | small: ~ 20 ms med: ~ 30 ms | nano: 18 ms tiny: 20 ms |
|
| rk3588 3 cores | tiny: ~ 35 ms | small: ~ 20 ms med: ~ 30 ms | nano: 14 ms tiny: 18 ms |
|
||||||
| rk3566 1 core | | small: ~ 96 ms | |
|
| rk3566 1 core | | small: ~ 96 ms | |
|
||||||
|
|
||||||
|
|
||||||
The inference time of a rk3588 with all 3 cores enabled is typically 25-30 ms for yolo-nas s.
|
The inference time of a rk3588 with all 3 cores enabled is typically 25-30 ms for yolo-nas s.
|
||||||
|
|
||||||
## What does Frigate use the CPU for and what does it use a detector for? (ELI5 Version)
|
## What does Frigate use the CPU for and what does it use a detector for? (ELI5 Version)
|
||||||
|
|||||||
@ -183,7 +183,7 @@ or add these options to your `docker run` command:
|
|||||||
|
|
||||||
#### Configuration
|
#### Configuration
|
||||||
|
|
||||||
Next, you should configure [hardware object detection](/configuration/object_detectors#rockchip-platform) and [hardware video processing](/configuration/hardware_acceleration#rockchip-platform).
|
Next, you should configure [hardware object detection](/configuration/object_detectors#rockchip-platform) and [hardware video processing](/configuration/hardware_acceleration_video#rockchip-platform).
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
@ -316,7 +316,8 @@ If you choose to run Frigate via LXC in Proxmox the setup can be complex so be p
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Suggestions include:
|
Suggestions include:
|
||||||
|
|
||||||
- For Intel-based hardware acceleration, to allow access to the `/dev/dri/renderD128` device with major number 226 and minor number 128, add the following lines to the `/etc/pve/lxc/<id>.conf` LXC configuration:
|
- For Intel-based hardware acceleration, to allow access to the `/dev/dri/renderD128` device with major number 226 and minor number 128, add the following lines to the `/etc/pve/lxc/<id>.conf` LXC configuration:
|
||||||
- `lxc.cgroup2.devices.allow: c 226:128 rwm`
|
- `lxc.cgroup2.devices.allow: c 226:128 rwm`
|
||||||
- `lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file`
|
- `lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file`
|
||||||
@ -407,7 +408,7 @@ mkdir -p /share/share_vol2/frigate/media
|
|||||||
# Also replace the time zone value for 'TZ' in the sample command.
|
# Also replace the time zone value for 'TZ' in the sample command.
|
||||||
# Example command will create a docker container that uses at most 2 CPUs and 4G RAM.
|
# Example command will create a docker container that uses at most 2 CPUs and 4G RAM.
|
||||||
# You may need to add "--env=LIBVA_DRIVER_NAME=i965 \" to the following docker run command if you
|
# You may need to add "--env=LIBVA_DRIVER_NAME=i965 \" to the following docker run command if you
|
||||||
# have certain CPU (e.g., J4125). See https://docs.frigate.video/configuration/hardware_acceleration.
|
# have certain CPU (e.g., J4125). See https://docs.frigate.video/configuration/hardware_acceleration_video.
|
||||||
docker run \
|
docker run \
|
||||||
--name=frigate \
|
--name=frigate \
|
||||||
--shm-size=256m \
|
--shm-size=256m \
|
||||||
|
|||||||
@ -162,7 +162,7 @@ FFmpeg arguments for other types of cameras can be found [here](../configuration
|
|||||||
|
|
||||||
### Step 3: Configure hardware acceleration (recommended)
|
### Step 3: Configure hardware acceleration (recommended)
|
||||||
|
|
||||||
Now that you have a working camera configuration, you want to setup hardware acceleration to minimize the CPU required to decode your video streams. See the [hardware acceleration](../configuration/hardware_acceleration.md) config reference for examples applicable to your hardware.
|
Now that you have a working camera configuration, you want to setup hardware acceleration to minimize the CPU required to decode your video streams. See the [hardware acceleration](../configuration/hardware_acceleration_video.md) config reference for examples applicable to your hardware.
|
||||||
|
|
||||||
Here is an example configuration with hardware acceleration configured to work with most Intel processors with an integrated GPU using the [preset](../configuration/ffmpeg_presets.md):
|
Here is an example configuration with hardware acceleration configured to work with most Intel processors with an integrated GPU using the [preset](../configuration/ffmpeg_presets.md):
|
||||||
|
|
||||||
@ -303,6 +303,7 @@ By default, Frigate will retain video of all tracked objects for 10 days. The fu
|
|||||||
### Step 7: Complete config
|
### Step 7: Complete config
|
||||||
|
|
||||||
At this point you have a complete config with basic functionality.
|
At this point you have a complete config with basic functionality.
|
||||||
|
|
||||||
- View [common configuration examples](../configuration/index.md#common-configuration-examples) for a list of common configuration examples.
|
- View [common configuration examples](../configuration/index.md#common-configuration-examples) for a list of common configuration examples.
|
||||||
- View [full config reference](../configuration/reference.md) for a complete list of configuration options.
|
- View [full config reference](../configuration/reference.md) for a complete list of configuration options.
|
||||||
|
|
||||||
|
|||||||
@ -104,7 +104,9 @@ Message published for each changed tracked object. The first message is publishe
|
|||||||
|
|
||||||
### `frigate/tracked_object_update`
|
### `frigate/tracked_object_update`
|
||||||
|
|
||||||
Message published for updates to tracked object metadata, for example when GenAI runs and returns a tracked object description.
|
Message published for updates to tracked object metadata, for example:
|
||||||
|
|
||||||
|
#### Generative AI Description Update
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -114,6 +116,33 @@ Message published for updates to tracked object metadata, for example when GenAI
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Face Recognition Update
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "face",
|
||||||
|
"id": "1607123955.475377-mxklsc",
|
||||||
|
"name": "John",
|
||||||
|
"score": 0.95,
|
||||||
|
"camera": "front_door_cam",
|
||||||
|
"timestamp": 1607123958.748393,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### License Plate Recognition Update
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "lpr",
|
||||||
|
"id": "1607123955.475377-mxklsc",
|
||||||
|
"name": "John's Car",
|
||||||
|
"plate": "123ABC",
|
||||||
|
"score": 0.95,
|
||||||
|
"camera": "driveway_cam",
|
||||||
|
"timestamp": 1607123958.748393,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `frigate/reviews`
|
### `frigate/reviews`
|
||||||
|
|
||||||
Message published for each changed review item. The first message is published when the `detection` or `alert` is initiated. When additional objects are detected or when a zone change occurs, it will publish a, `update` message with the same id. When the review activity has ended a final `end` message is published.
|
Message published for each changed review item. The first message is published when the `detection` or `alert` is initiated. When additional objects are detected or when a zone change occurs, it will publish a, `update` message with the same id. When the review activity has ended a final `end` message is published.
|
||||||
|
|||||||
@ -34,7 +34,7 @@ Frigate generally [recommends cameras with configurable sub streams](/frigate/ha
|
|||||||
To do this efficiently the following setup is required:
|
To do this efficiently the following setup is required:
|
||||||
|
|
||||||
1. A GPU or iGPU must be available to do the scaling.
|
1. A GPU or iGPU must be available to do the scaling.
|
||||||
2. [ffmpeg presets for hwaccel](/configuration/hardware_acceleration.md) must be used
|
2. [ffmpeg presets for hwaccel](/configuration/hardware_acceleration_video.md) must be used
|
||||||
3. Set the desired detection resolution for `detect -> width` and `detect -> height`.
|
3. Set the desired detection resolution for `detect -> width` and `detect -> height`.
|
||||||
|
|
||||||
When this is done correctly, the GPU will do the decoding and scaling which will result in a small increase in CPU usage but with better results.
|
When this is done correctly, the GPU will do the decoding and scaling which will result in a small increase in CPU usage but with better results.
|
||||||
|
|||||||
7812
docs/package-lock.json
generated
7812
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,10 +17,10 @@
|
|||||||
"write-heading-ids": "docusaurus write-heading-ids"
|
"write-heading-ids": "docusaurus write-heading-ids"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "^3.6.3",
|
"@docusaurus/core": "^3.7.0",
|
||||||
"@docusaurus/preset-classic": "^3.6.3",
|
|
||||||
"@docusaurus/theme-mermaid": "^3.6.3",
|
|
||||||
"@docusaurus/plugin-content-docs": "^3.6.3",
|
"@docusaurus/plugin-content-docs": "^3.6.3",
|
||||||
|
"@docusaurus/preset-classic": "^3.7.0",
|
||||||
|
"@docusaurus/theme-mermaid": "^3.6.3",
|
||||||
"@mdx-js/react": "^3.1.0",
|
"@mdx-js/react": "^3.1.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"docusaurus-plugin-openapi-docs": "^4.3.1",
|
"docusaurus-plugin-openapi-docs": "^4.3.1",
|
||||||
|
|||||||
@ -59,10 +59,13 @@ const sidebars: SidebarsConfig = {
|
|||||||
"configuration/objects",
|
"configuration/objects",
|
||||||
"configuration/stationary_objects",
|
"configuration/stationary_objects",
|
||||||
],
|
],
|
||||||
|
"Hardware Acceleration": [
|
||||||
|
"configuration/hardware_acceleration_video",
|
||||||
|
"configuration/hardware_acceleration_enrichments",
|
||||||
|
],
|
||||||
"Extra Configuration": [
|
"Extra Configuration": [
|
||||||
"configuration/authentication",
|
"configuration/authentication",
|
||||||
"configuration/notifications",
|
"configuration/notifications",
|
||||||
"configuration/hardware_acceleration",
|
|
||||||
"configuration/ffmpeg_presets",
|
"configuration/ffmpeg_presets",
|
||||||
"configuration/pwa",
|
"configuration/pwa",
|
||||||
"configuration/tls",
|
"configuration/tls",
|
||||||
|
|||||||
@ -74,7 +74,7 @@ def go2rtc_streams():
|
|||||||
)
|
)
|
||||||
stream_data = r.json()
|
stream_data = r.json()
|
||||||
for data in stream_data.values():
|
for data in stream_data.values():
|
||||||
for producer in data.get("producers", []):
|
for producer in data.get("producers") or []:
|
||||||
producer["url"] = clean_camera_user_pass(producer.get("url", ""))
|
producer["url"] = clean_camera_user_pass(producer.get("url", ""))
|
||||||
return JSONResponse(content=stream_data)
|
return JSONResponse(content=stream_data)
|
||||||
|
|
||||||
|
|||||||
@ -261,14 +261,14 @@ def auth(request: Request):
|
|||||||
|
|
||||||
role_header = proxy_config.header_map.role
|
role_header = proxy_config.header_map.role
|
||||||
role = (
|
role = (
|
||||||
request.headers.get(role_header, default="viewer")
|
request.headers.get(role_header, default=proxy_config.default_role)
|
||||||
if role_header
|
if role_header
|
||||||
else "viewer"
|
else proxy_config.default_role
|
||||||
)
|
)
|
||||||
|
|
||||||
# if comma-separated with "admin", use "admin", else "viewer"
|
# if comma-separated with "admin", use "admin", else use default role
|
||||||
success_response.headers["remote-role"] = (
|
success_response.headers["remote-role"] = (
|
||||||
"admin" if role and "admin" in role else "viewer"
|
"admin" if role and "admin" in role else proxy_config.default_role
|
||||||
)
|
)
|
||||||
|
|
||||||
return success_response
|
return success_response
|
||||||
|
|||||||
@ -14,6 +14,7 @@ from peewee import DoesNotExist
|
|||||||
from playhouse.shortcuts import model_to_dict
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from frigate.api.auth import require_role
|
from frigate.api.auth import require_role
|
||||||
|
from frigate.api.defs.request.classification_body import RenameFaceBody
|
||||||
from frigate.api.defs.tags import Tags
|
from frigate.api.defs.tags import Tags
|
||||||
from frigate.config.camera import DetectConfig
|
from frigate.config.camera import DetectConfig
|
||||||
from frigate.const import FACE_DIR
|
from frigate.const import FACE_DIR
|
||||||
@ -260,6 +261,35 @@ def deregister_faces(request: Request, name: str, body: dict = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/faces/{old_name}/rename", dependencies=[Depends(require_role(["admin"]))])
|
||||||
|
def rename_face(request: Request, old_name: str, body: RenameFaceBody):
|
||||||
|
if not request.app.frigate_config.face_recognition.enabled:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content={"message": "Face recognition is not enabled.", "success": False},
|
||||||
|
)
|
||||||
|
|
||||||
|
context: EmbeddingsContext = request.app.embeddings
|
||||||
|
try:
|
||||||
|
context.rename_face(old_name, body.new_name)
|
||||||
|
return JSONResponse(
|
||||||
|
content={
|
||||||
|
"success": True,
|
||||||
|
"message": f"Successfully renamed face to {body.new_name}.",
|
||||||
|
},
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
except ValueError as e:
|
||||||
|
logger.error(e)
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=400,
|
||||||
|
content={
|
||||||
|
"message": "Error renaming face. Check Frigate logs.",
|
||||||
|
"success": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.put("/lpr/reprocess")
|
@router.put("/lpr/reprocess")
|
||||||
def reprocess_license_plate(request: Request, event_id: str):
|
def reprocess_license_plate(request: Request, event_id: str):
|
||||||
if not request.app.frigate_config.lpr.enabled:
|
if not request.app.frigate_config.lpr.enabled:
|
||||||
|
|||||||
5
frigate/api/defs/request/classification_body.py
Normal file
5
frigate/api/defs/request/classification_body.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class RenameFaceBody(BaseModel):
|
||||||
|
new_name: str
|
||||||
@ -58,13 +58,9 @@ async def review(
|
|||||||
)
|
)
|
||||||
|
|
||||||
clauses = [
|
clauses = [
|
||||||
(
|
|
||||||
(ReviewSegment.start_time > after)
|
(ReviewSegment.start_time > after)
|
||||||
& (
|
& (ReviewSegment.start_time < before)
|
||||||
(ReviewSegment.end_time.is_null(True))
|
& ((ReviewSegment.end_time.is_null(True)) | (ReviewSegment.end_time < before))
|
||||||
| (ReviewSegment.end_time < before)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if cameras != "all":
|
if cameras != "all":
|
||||||
|
|||||||
@ -135,6 +135,7 @@ class Dispatcher:
|
|||||||
"type": TrackedObjectUpdateTypesEnum.description,
|
"type": TrackedObjectUpdateTypesEnum.description,
|
||||||
"id": event.id,
|
"id": event.id,
|
||||||
"description": event.data["description"],
|
"description": event.data["description"],
|
||||||
|
"camera": event.camera,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -39,9 +39,6 @@ class EventMetadataSubscriber(Subscriber):
|
|||||||
def __init__(self, topic: EventMetadataTypeEnum) -> None:
|
def __init__(self, topic: EventMetadataTypeEnum) -> None:
|
||||||
super().__init__(topic.value)
|
super().__init__(topic.value)
|
||||||
|
|
||||||
def check_for_update(self, timeout: float = 1) -> tuple | None:
|
|
||||||
return super().check_for_update(timeout)
|
|
||||||
|
|
||||||
def _return_object(self, topic: str, payload: tuple) -> tuple:
|
def _return_object(self, topic: str, payload: tuple) -> tuple:
|
||||||
if payload is None:
|
if payload is None:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|||||||
@ -6,6 +6,8 @@ from typing import Optional
|
|||||||
|
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
|
from frigate.const import FAST_QUEUE_TIMEOUT
|
||||||
|
|
||||||
SOCKET_PUB = "ipc:///tmp/cache/proxy_pub"
|
SOCKET_PUB = "ipc:///tmp/cache/proxy_pub"
|
||||||
SOCKET_SUB = "ipc:///tmp/cache/proxy_sub"
|
SOCKET_SUB = "ipc:///tmp/cache/proxy_sub"
|
||||||
|
|
||||||
@ -77,7 +79,9 @@ class Subscriber:
|
|||||||
self.socket.setsockopt_string(zmq.SUBSCRIBE, self.topic)
|
self.socket.setsockopt_string(zmq.SUBSCRIBE, self.topic)
|
||||||
self.socket.connect(SOCKET_SUB)
|
self.socket.connect(SOCKET_SUB)
|
||||||
|
|
||||||
def check_for_update(self, timeout: float = 1) -> Optional[tuple[str, any]]:
|
def check_for_update(
|
||||||
|
self, timeout: float = FAST_QUEUE_TIMEOUT
|
||||||
|
) -> Optional[tuple[str, any]]:
|
||||||
"""Returns message or None if no update."""
|
"""Returns message or None if no update."""
|
||||||
try:
|
try:
|
||||||
has_update, _, _ = zmq.select([self.socket], [], [], timeout)
|
has_update, _, _ = zmq.select([self.socket], [], [], timeout)
|
||||||
|
|||||||
@ -63,9 +63,9 @@ class PtzAutotrackConfig(FrigateBaseModel):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Invalid type for movement_weights")
|
raise ValueError("Invalid type for movement_weights")
|
||||||
|
|
||||||
if len(weights) != 5:
|
if len(weights) != 6:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"movement_weights must have exactly 5 floats, remove this line from your config and run autotracking calibration"
|
"movement_weights must have exactly 6 floats, remove this line from your config and run autotracking calibration"
|
||||||
)
|
)
|
||||||
|
|
||||||
return weights
|
return weights
|
||||||
|
|||||||
@ -94,7 +94,7 @@ class CameraFaceRecognitionConfig(FrigateBaseModel):
|
|||||||
default=500, title="Min area of face box to consider running face recognition."
|
default=500, title="Min area of face box to consider running face recognition."
|
||||||
)
|
)
|
||||||
|
|
||||||
model_config = ConfigDict(extra="ignore", protected_namespaces=())
|
model_config = ConfigDict(extra="forbid", protected_namespaces=())
|
||||||
|
|
||||||
|
|
||||||
class LicensePlateRecognitionConfig(FrigateBaseModel):
|
class LicensePlateRecognitionConfig(FrigateBaseModel):
|
||||||
@ -168,4 +168,4 @@ class CameraLicensePlateRecognitionConfig(FrigateBaseModel):
|
|||||||
le=10,
|
le=10,
|
||||||
)
|
)
|
||||||
|
|
||||||
model_config = ConfigDict(extra="ignore", protected_namespaces=())
|
model_config = ConfigDict(extra="forbid", protected_namespaces=())
|
||||||
|
|||||||
@ -472,8 +472,24 @@ class FrigateConfig(FrigateBaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for name, camera in self.cameras.items():
|
for name, camera in self.cameras.items():
|
||||||
|
modified_global_config = global_config.copy()
|
||||||
|
|
||||||
|
# only populate some fields down to the camera level for specific keys
|
||||||
|
allowed_fields_map = {
|
||||||
|
"face_recognition": ["enabled", "min_area"],
|
||||||
|
"lpr": ["enabled", "expire_time", "min_area", "enhancement"],
|
||||||
|
}
|
||||||
|
|
||||||
|
for section in allowed_fields_map:
|
||||||
|
if section in modified_global_config:
|
||||||
|
modified_global_config[section] = {
|
||||||
|
k: v
|
||||||
|
for k, v in modified_global_config[section].items()
|
||||||
|
if k in allowed_fields_map[section]
|
||||||
|
}
|
||||||
|
|
||||||
merged_config = deep_merge(
|
merged_config = deep_merge(
|
||||||
camera.model_dump(exclude_unset=True), global_config
|
camera.model_dump(exclude_unset=True), modified_global_config
|
||||||
)
|
)
|
||||||
camera_config: CameraConfig = CameraConfig.model_validate(
|
camera_config: CameraConfig = CameraConfig.model_validate(
|
||||||
{"name": name, **merged_config}
|
{"name": name, **merged_config}
|
||||||
|
|||||||
@ -30,3 +30,6 @@ class ProxyConfig(FrigateBaseModel):
|
|||||||
default=None,
|
default=None,
|
||||||
title="Secret value for proxy authentication.",
|
title="Secret value for proxy authentication.",
|
||||||
)
|
)
|
||||||
|
default_role: Optional[str] = Field(
|
||||||
|
default="viewer", title="Default role for proxy users."
|
||||||
|
)
|
||||||
|
|||||||
@ -129,3 +129,7 @@ AUTOTRACKING_ZOOM_EDGE_THRESHOLD = 0.05
|
|||||||
|
|
||||||
JWT_SECRET_ENV_VAR = "FRIGATE_JWT_SECRET"
|
JWT_SECRET_ENV_VAR = "FRIGATE_JWT_SECRET"
|
||||||
PASSWORD_HASH_ALGORITHM = "pbkdf2_sha256"
|
PASSWORD_HASH_ALGORITHM = "pbkdf2_sha256"
|
||||||
|
|
||||||
|
# Queues
|
||||||
|
|
||||||
|
FAST_QUEUE_TIMEOUT = 0.00001 # seconds
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@ -23,6 +24,7 @@ from frigate.comms.event_metadata_updater import (
|
|||||||
)
|
)
|
||||||
from frigate.const import CLIPS_DIR
|
from frigate.const import CLIPS_DIR
|
||||||
from frigate.embeddings.onnx.lpr_embedding import LPR_EMBEDDING_SIZE
|
from frigate.embeddings.onnx.lpr_embedding import LPR_EMBEDDING_SIZE
|
||||||
|
from frigate.types import TrackedObjectUpdateTypesEnum
|
||||||
from frigate.util.builtin import EventsPerSecond
|
from frigate.util.builtin import EventsPerSecond
|
||||||
from frigate.util.image import area
|
from frigate.util.image import area
|
||||||
|
|
||||||
@ -1221,7 +1223,7 @@ class LicensePlateProcessingMixin:
|
|||||||
license_plate_area = (license_plate[2] - license_plate[0]) * (
|
license_plate_area = (license_plate[2] - license_plate[0]) * (
|
||||||
license_plate[3] - license_plate[1]
|
license_plate[3] - license_plate[1]
|
||||||
)
|
)
|
||||||
if license_plate_area < self.lpr_config.min_area:
|
if license_plate_area < self.config.cameras[camera].lpr.min_area:
|
||||||
logger.debug(f"{camera}: License plate area below minimum threshold.")
|
logger.debug(f"{camera}: License plate area below minimum threshold.")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1315,10 +1317,7 @@ class LicensePlateProcessingMixin:
|
|||||||
|
|
||||||
# check that license plate is valid
|
# check that license plate is valid
|
||||||
# double the value because we've doubled the size of the car
|
# double the value because we've doubled the size of the car
|
||||||
if (
|
if license_plate_area < self.config.cameras[camera].lpr.min_area * 2:
|
||||||
license_plate_area
|
|
||||||
< self.config.cameras[obj_data["camera"]].lpr.min_area * 2
|
|
||||||
):
|
|
||||||
logger.debug(f"{camera}: License plate is less than min_area")
|
logger.debug(f"{camera}: License plate is less than min_area")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1362,10 +1361,10 @@ class LicensePlateProcessingMixin:
|
|||||||
if (
|
if (
|
||||||
not license_plate_box
|
not license_plate_box
|
||||||
or area(license_plate_box)
|
or area(license_plate_box)
|
||||||
< self.config.cameras[obj_data["camera"]].lpr.min_area
|
< self.config.cameras[camera].lpr.min_area
|
||||||
):
|
):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{camera}: Area for license plate box {area(license_plate_box)} is less than min_area {self.config.cameras[obj_data['camera']].lpr.min_area}"
|
f"{camera}: Area for license plate box {area(license_plate_box)} is less than min_area {self.config.cameras[camera].lpr.min_area}"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1513,6 +1512,20 @@ class LicensePlateProcessingMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# always publish to recognized_license_plate field
|
# always publish to recognized_license_plate field
|
||||||
|
self.requestor.send_data(
|
||||||
|
"tracked_object_update",
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"type": TrackedObjectUpdateTypesEnum.lpr,
|
||||||
|
"name": sub_label,
|
||||||
|
"plate": top_plate,
|
||||||
|
"score": avg_confidence,
|
||||||
|
"id": id,
|
||||||
|
"camera": camera,
|
||||||
|
"timestamp": start,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
self.sub_label_publisher.publish(
|
self.sub_label_publisher.publish(
|
||||||
EventMetadataTypeEnum.recognized_license_plate,
|
EventMetadataTypeEnum.recognized_license_plate,
|
||||||
(id, top_plate, avg_confidence),
|
(id, top_plate, avg_confidence),
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from peewee import DoesNotExist
|
|||||||
|
|
||||||
from frigate.comms.embeddings_updater import EmbeddingsRequestEnum
|
from frigate.comms.embeddings_updater import EmbeddingsRequestEnum
|
||||||
from frigate.comms.event_metadata_updater import EventMetadataPublisher
|
from frigate.comms.event_metadata_updater import EventMetadataPublisher
|
||||||
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.data_processing.common.license_plate.mixin import (
|
from frigate.data_processing.common.license_plate.mixin import (
|
||||||
WRITE_DEBUG_IMAGES,
|
WRITE_DEBUG_IMAGES,
|
||||||
@ -31,11 +32,13 @@ class LicensePlatePostProcessor(LicensePlateProcessingMixin, PostProcessorApi):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
|
requestor: InterProcessRequestor,
|
||||||
sub_label_publisher: EventMetadataPublisher,
|
sub_label_publisher: EventMetadataPublisher,
|
||||||
metrics: DataProcessorMetrics,
|
metrics: DataProcessorMetrics,
|
||||||
model_runner: LicensePlateModelRunner,
|
model_runner: LicensePlateModelRunner,
|
||||||
detected_license_plates: dict[str, dict[str, any]],
|
detected_license_plates: dict[str, dict[str, any]],
|
||||||
):
|
):
|
||||||
|
self.requestor = requestor
|
||||||
self.detected_license_plates = detected_license_plates
|
self.detected_license_plates = detected_license_plates
|
||||||
self.model_runner = model_runner
|
self.model_runner = model_runner
|
||||||
self.lpr_config = config.lpr
|
self.lpr_config = config.lpr
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
@ -17,6 +18,7 @@ from frigate.comms.event_metadata_updater import (
|
|||||||
EventMetadataPublisher,
|
EventMetadataPublisher,
|
||||||
EventMetadataTypeEnum,
|
EventMetadataTypeEnum,
|
||||||
)
|
)
|
||||||
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.const import FACE_DIR, MODEL_CACHE_DIR
|
from frigate.const import FACE_DIR, MODEL_CACHE_DIR
|
||||||
from frigate.data_processing.common.face.model import (
|
from frigate.data_processing.common.face.model import (
|
||||||
@ -24,6 +26,7 @@ from frigate.data_processing.common.face.model import (
|
|||||||
FaceNetRecognizer,
|
FaceNetRecognizer,
|
||||||
FaceRecognizer,
|
FaceRecognizer,
|
||||||
)
|
)
|
||||||
|
from frigate.types import TrackedObjectUpdateTypesEnum
|
||||||
from frigate.util.builtin import EventsPerSecond
|
from frigate.util.builtin import EventsPerSecond
|
||||||
from frigate.util.image import area
|
from frigate.util.image import area
|
||||||
|
|
||||||
@ -42,11 +45,13 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
|
requestor: InterProcessRequestor,
|
||||||
sub_label_publisher: EventMetadataPublisher,
|
sub_label_publisher: EventMetadataPublisher,
|
||||||
metrics: DataProcessorMetrics,
|
metrics: DataProcessorMetrics,
|
||||||
):
|
):
|
||||||
super().__init__(config, metrics)
|
super().__init__(config, metrics)
|
||||||
self.face_config = config.face_recognition
|
self.face_config = config.face_recognition
|
||||||
|
self.requestor = requestor
|
||||||
self.sub_label_publisher = sub_label_publisher
|
self.sub_label_publisher = sub_label_publisher
|
||||||
self.face_detector: cv2.FaceDetectorYN = None
|
self.face_detector: cv2.FaceDetectorYN = None
|
||||||
self.requires_face_detection = "face" not in self.config.objects.all_objects
|
self.requires_face_detection = "face" not in self.config.objects.all_objects
|
||||||
@ -157,8 +162,9 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
|||||||
def process_frame(self, obj_data: dict[str, any], frame: np.ndarray):
|
def process_frame(self, obj_data: dict[str, any], frame: np.ndarray):
|
||||||
"""Look for faces in image."""
|
"""Look for faces in image."""
|
||||||
self.metrics.face_rec_fps.value = self.faces_per_second.eps()
|
self.metrics.face_rec_fps.value = self.faces_per_second.eps()
|
||||||
|
camera = obj_data["camera"]
|
||||||
|
|
||||||
if not self.config.cameras[obj_data["camera"]].face_recognition.enabled:
|
if not self.config.cameras[camera].face_recognition.enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
start = datetime.datetime.now().timestamp()
|
start = datetime.datetime.now().timestamp()
|
||||||
@ -245,7 +251,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
|||||||
if (
|
if (
|
||||||
not face_box
|
not face_box
|
||||||
or area(face_box)
|
or area(face_box)
|
||||||
< self.config.cameras[obj_data["camera"]].face_recognition.min_area
|
< self.config.cameras[camera].face_recognition.min_area
|
||||||
):
|
):
|
||||||
logger.debug(f"Invalid face box {face}")
|
logger.debug(f"Invalid face box {face}")
|
||||||
return
|
return
|
||||||
@ -286,6 +292,20 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
|||||||
self.person_face_history[id]
|
self.person_face_history[id]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.requestor.send_data(
|
||||||
|
"tracked_object_update",
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"type": TrackedObjectUpdateTypesEnum.face,
|
||||||
|
"name": weighted_sub_label,
|
||||||
|
"score": weighted_score,
|
||||||
|
"id": id,
|
||||||
|
"camera": camera,
|
||||||
|
"timestamp": start,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if weighted_score >= self.face_config.recognition_threshold:
|
if weighted_score >= self.face_config.recognition_threshold:
|
||||||
self.sub_label_publisher.publish(
|
self.sub_label_publisher.publish(
|
||||||
EventMetadataTypeEnum.sub_label,
|
EventMetadataTypeEnum.sub_label,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import logging
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from frigate.comms.event_metadata_updater import EventMetadataPublisher
|
from frigate.comms.event_metadata_updater import EventMetadataPublisher
|
||||||
|
from frigate.comms.inter_process import InterProcessRequestor
|
||||||
from frigate.config import FrigateConfig
|
from frigate.config import FrigateConfig
|
||||||
from frigate.data_processing.common.license_plate.mixin import (
|
from frigate.data_processing.common.license_plate.mixin import (
|
||||||
LicensePlateProcessingMixin,
|
LicensePlateProcessingMixin,
|
||||||
@ -23,11 +24,13 @@ class LicensePlateRealTimeProcessor(LicensePlateProcessingMixin, RealTimeProcess
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
config: FrigateConfig,
|
config: FrigateConfig,
|
||||||
|
requestor: InterProcessRequestor,
|
||||||
sub_label_publisher: EventMetadataPublisher,
|
sub_label_publisher: EventMetadataPublisher,
|
||||||
metrics: DataProcessorMetrics,
|
metrics: DataProcessorMetrics,
|
||||||
model_runner: LicensePlateModelRunner,
|
model_runner: LicensePlateModelRunner,
|
||||||
detected_license_plates: dict[str, dict[str, any]],
|
detected_license_plates: dict[str, dict[str, any]],
|
||||||
):
|
):
|
||||||
|
self.requestor = requestor
|
||||||
self.detected_license_plates = detected_license_plates
|
self.detected_license_plates = detected_license_plates
|
||||||
self.model_runner = model_runner
|
self.model_runner = model_runner
|
||||||
self.lpr_config = config.lpr
|
self.lpr_config = config.lpr
|
||||||
|
|||||||
@ -19,7 +19,11 @@ DETECTOR_KEY = "rknn"
|
|||||||
|
|
||||||
supported_socs = ["rk3562", "rk3566", "rk3568", "rk3576", "rk3588"]
|
supported_socs = ["rk3562", "rk3566", "rk3568", "rk3576", "rk3588"]
|
||||||
|
|
||||||
supported_models = {ModelTypeEnum.yolonas: "^deci-fp16-yolonas_[sml]$"}
|
supported_models = {
|
||||||
|
ModelTypeEnum.yologeneric: "^frigate-fp16-yolov9-[cemst]$",
|
||||||
|
ModelTypeEnum.yolonas: "^deci-fp16-yolonas_[sml]$",
|
||||||
|
ModelTypeEnum.yolox: "^rock-(fp16|i8)-yolox_(nano|tiny)$",
|
||||||
|
}
|
||||||
|
|
||||||
model_cache_dir = os.path.join(MODEL_CACHE_DIR, "rknn_cache/")
|
model_cache_dir = os.path.join(MODEL_CACHE_DIR, "rknn_cache/")
|
||||||
|
|
||||||
@ -115,7 +119,7 @@ class Rknn(DetectionApi):
|
|||||||
model_props["model_type"] = model_type
|
model_props["model_type"] = model_type
|
||||||
|
|
||||||
if model_matched:
|
if model_matched:
|
||||||
model_props["filename"] = model_path + f"-{soc}-v2.3.0-1.rknn"
|
model_props["filename"] = model_path + f"-{soc}-v2.3.2-1.rknn"
|
||||||
|
|
||||||
model_props["path"] = model_cache_dir + model_props["filename"]
|
model_props["path"] = model_cache_dir + model_props["filename"]
|
||||||
|
|
||||||
@ -136,7 +140,7 @@ class Rknn(DetectionApi):
|
|||||||
os.mkdir(model_cache_dir)
|
os.mkdir(model_cache_dir)
|
||||||
|
|
||||||
urllib.request.urlretrieve(
|
urllib.request.urlretrieve(
|
||||||
f"https://github.com/MarcA711/rknn-models/releases/download/v2.3.0/{filename}",
|
f"https://github.com/MarcA711/rknn-models/releases/download/v2.3.2/{filename}",
|
||||||
model_cache_dir + filename,
|
model_cache_dir + filename,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -5,11 +5,13 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import multiprocessing as mp
|
import multiprocessing as mp
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import signal
|
import signal
|
||||||
import threading
|
import threading
|
||||||
from types import FrameType
|
from types import FrameType
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from pathvalidate import ValidationError, sanitize_filename
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
||||||
from frigate.comms.embeddings_updater import EmbeddingsRequestEnum, EmbeddingsRequestor
|
from frigate.comms.embeddings_updater import EmbeddingsRequestEnum, EmbeddingsRequestor
|
||||||
@ -240,6 +242,42 @@ class EmbeddingsContext:
|
|||||||
EmbeddingsRequestEnum.clear_face_classifier.value, None
|
EmbeddingsRequestEnum.clear_face_classifier.value, None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def rename_face(self, old_name: str, new_name: str) -> None:
|
||||||
|
valid_name_pattern = r"^[a-zA-Z0-9\s_-]{1,50}$"
|
||||||
|
|
||||||
|
try:
|
||||||
|
sanitized_old_name = sanitize_filename(old_name, replacement_text="_")
|
||||||
|
sanitized_new_name = sanitize_filename(new_name, replacement_text="_")
|
||||||
|
except ValidationError as e:
|
||||||
|
raise ValueError(f"Invalid face name: {str(e)}")
|
||||||
|
|
||||||
|
if not re.match(valid_name_pattern, old_name):
|
||||||
|
raise ValueError(f"Invalid old face name: {old_name}")
|
||||||
|
if not re.match(valid_name_pattern, new_name):
|
||||||
|
raise ValueError(f"Invalid new face name: {new_name}")
|
||||||
|
if sanitized_old_name != old_name:
|
||||||
|
raise ValueError(f"Old face name contains invalid characters: {old_name}")
|
||||||
|
if sanitized_new_name != new_name:
|
||||||
|
raise ValueError(f"New face name contains invalid characters: {new_name}")
|
||||||
|
|
||||||
|
old_path = os.path.normpath(os.path.join(FACE_DIR, old_name))
|
||||||
|
new_path = os.path.normpath(os.path.join(FACE_DIR, new_name))
|
||||||
|
|
||||||
|
# Prevent path traversal
|
||||||
|
if not old_path.startswith(
|
||||||
|
os.path.normpath(FACE_DIR)
|
||||||
|
) or not new_path.startswith(os.path.normpath(FACE_DIR)):
|
||||||
|
raise ValueError("Invalid path detected")
|
||||||
|
|
||||||
|
if not os.path.exists(old_path):
|
||||||
|
raise ValueError(f"Face {old_name} not found.")
|
||||||
|
|
||||||
|
os.rename(old_path, new_path)
|
||||||
|
|
||||||
|
self.requestor.send_data(
|
||||||
|
EmbeddingsRequestEnum.clear_face_classifier.value, None
|
||||||
|
)
|
||||||
|
|
||||||
def update_description(self, event_id: str, description: str) -> None:
|
def update_description(self, event_id: str, description: str) -> None:
|
||||||
self.requestor.send_data(
|
self.requestor.send_data(
|
||||||
EmbeddingsRequestEnum.embed_description.value,
|
EmbeddingsRequestEnum.embed_description.value,
|
||||||
|
|||||||
@ -120,7 +120,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
if self.config.face_recognition.enabled:
|
if self.config.face_recognition.enabled:
|
||||||
self.realtime_processors.append(
|
self.realtime_processors.append(
|
||||||
FaceRealTimeProcessor(
|
FaceRealTimeProcessor(
|
||||||
self.config, self.event_metadata_publisher, metrics
|
self.config, self.requestor, self.event_metadata_publisher, metrics
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -135,6 +135,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
self.realtime_processors.append(
|
self.realtime_processors.append(
|
||||||
LicensePlateRealTimeProcessor(
|
LicensePlateRealTimeProcessor(
|
||||||
self.config,
|
self.config,
|
||||||
|
self.requestor,
|
||||||
self.event_metadata_publisher,
|
self.event_metadata_publisher,
|
||||||
metrics,
|
metrics,
|
||||||
lpr_model_runner,
|
lpr_model_runner,
|
||||||
@ -149,6 +150,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
self.post_processors.append(
|
self.post_processors.append(
|
||||||
LicensePlatePostProcessor(
|
LicensePlatePostProcessor(
|
||||||
self.config,
|
self.config,
|
||||||
|
self.requestor,
|
||||||
self.event_metadata_publisher,
|
self.event_metadata_publisher,
|
||||||
metrics,
|
metrics,
|
||||||
lpr_model_runner,
|
lpr_model_runner,
|
||||||
@ -229,7 +231,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
|
|
||||||
def _process_updates(self) -> None:
|
def _process_updates(self) -> None:
|
||||||
"""Process event updates"""
|
"""Process event updates"""
|
||||||
update = self.event_subscriber.check_for_update(timeout=0.01)
|
update = self.event_subscriber.check_for_update()
|
||||||
|
|
||||||
if update is None:
|
if update is None:
|
||||||
return
|
return
|
||||||
@ -322,7 +324,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
def _process_finalized(self) -> None:
|
def _process_finalized(self) -> None:
|
||||||
"""Process the end of an event."""
|
"""Process the end of an event."""
|
||||||
while True:
|
while True:
|
||||||
ended = self.event_end_subscriber.check_for_update(timeout=0.01)
|
ended = self.event_end_subscriber.check_for_update()
|
||||||
|
|
||||||
if ended == None:
|
if ended == None:
|
||||||
break
|
break
|
||||||
@ -418,7 +420,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
def _process_recordings_updates(self) -> None:
|
def _process_recordings_updates(self) -> None:
|
||||||
"""Process recordings updates."""
|
"""Process recordings updates."""
|
||||||
while True:
|
while True:
|
||||||
recordings_data = self.recordings_subscriber.check_for_update(timeout=0.01)
|
recordings_data = self.recordings_subscriber.check_for_update()
|
||||||
|
|
||||||
if recordings_data == None:
|
if recordings_data == None:
|
||||||
break
|
break
|
||||||
@ -435,7 +437,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
|
|
||||||
def _process_event_metadata(self):
|
def _process_event_metadata(self):
|
||||||
# Check for regenerate description requests
|
# Check for regenerate description requests
|
||||||
(topic, payload) = self.event_metadata_subscriber.check_for_update(timeout=0.01)
|
(topic, payload) = self.event_metadata_subscriber.check_for_update()
|
||||||
|
|
||||||
if topic is None:
|
if topic is None:
|
||||||
return
|
return
|
||||||
@ -449,7 +451,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
|
|
||||||
def _process_dedicated_lpr(self) -> None:
|
def _process_dedicated_lpr(self) -> None:
|
||||||
"""Process event updates"""
|
"""Process event updates"""
|
||||||
(topic, data) = self.detection_subscriber.check_for_update(timeout=0.01)
|
(topic, data) = self.detection_subscriber.check_for_update()
|
||||||
|
|
||||||
if topic is None:
|
if topic is None:
|
||||||
return
|
return
|
||||||
@ -583,6 +585,7 @@ class EmbeddingMaintainer(threading.Thread):
|
|||||||
"type": TrackedObjectUpdateTypesEnum.description,
|
"type": TrackedObjectUpdateTypesEnum.description,
|
||||||
"id": event.id,
|
"id": event.id,
|
||||||
"description": description,
|
"description": description,
|
||||||
|
"camera": event.camera,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -36,11 +36,12 @@ class JinaV1TextEmbedding(BaseEmbedding):
|
|||||||
requestor: InterProcessRequestor,
|
requestor: InterProcessRequestor,
|
||||||
device: str = "AUTO",
|
device: str = "AUTO",
|
||||||
):
|
):
|
||||||
|
HF_ENDPOINT = os.environ.get("HF_ENDPOINT", "https://huggingface.co")
|
||||||
super().__init__(
|
super().__init__(
|
||||||
model_name="jinaai/jina-clip-v1",
|
model_name="jinaai/jina-clip-v1",
|
||||||
model_file="text_model_fp16.onnx",
|
model_file="text_model_fp16.onnx",
|
||||||
download_urls={
|
download_urls={
|
||||||
"text_model_fp16.onnx": "https://huggingface.co/jinaai/jina-clip-v1/resolve/main/onnx/text_model_fp16.onnx",
|
"text_model_fp16.onnx": f"{HF_ENDPOINT}/jinaai/jina-clip-v1/resolve/main/onnx/text_model_fp16.onnx",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.tokenizer_file = "tokenizer"
|
self.tokenizer_file = "tokenizer"
|
||||||
@ -156,12 +157,13 @@ class JinaV1ImageEmbedding(BaseEmbedding):
|
|||||||
if model_size == "large"
|
if model_size == "large"
|
||||||
else "vision_model_quantized.onnx"
|
else "vision_model_quantized.onnx"
|
||||||
)
|
)
|
||||||
|
HF_ENDPOINT = os.environ.get("HF_ENDPOINT", "https://huggingface.co")
|
||||||
super().__init__(
|
super().__init__(
|
||||||
model_name="jinaai/jina-clip-v1",
|
model_name="jinaai/jina-clip-v1",
|
||||||
model_file=model_file,
|
model_file=model_file,
|
||||||
download_urls={
|
download_urls={
|
||||||
model_file: f"https://huggingface.co/jinaai/jina-clip-v1/resolve/main/onnx/{model_file}",
|
model_file: f"{HF_ENDPOINT}/jinaai/jina-clip-v1/resolve/main/onnx/{model_file}",
|
||||||
"preprocessor_config.json": "https://huggingface.co/jinaai/jina-clip-v1/resolve/main/preprocessor_config.json",
|
"preprocessor_config.json": f"{HF_ENDPOINT}/jinaai/jina-clip-v1/resolve/main/preprocessor_config.json",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.requestor = requestor
|
self.requestor = requestor
|
||||||
|
|||||||
@ -34,12 +34,13 @@ class JinaV2Embedding(BaseEmbedding):
|
|||||||
model_file = (
|
model_file = (
|
||||||
"model_fp16.onnx" if model_size == "large" else "model_quantized.onnx"
|
"model_fp16.onnx" if model_size == "large" else "model_quantized.onnx"
|
||||||
)
|
)
|
||||||
|
HF_ENDPOINT = os.environ.get("HF_ENDPOINT", "https://huggingface.co")
|
||||||
super().__init__(
|
super().__init__(
|
||||||
model_name="jinaai/jina-clip-v2",
|
model_name="jinaai/jina-clip-v2",
|
||||||
model_file=model_file,
|
model_file=model_file,
|
||||||
download_urls={
|
download_urls={
|
||||||
model_file: f"https://huggingface.co/jinaai/jina-clip-v2/resolve/main/onnx/{model_file}",
|
model_file: f"{HF_ENDPOINT}/jinaai/jina-clip-v2/resolve/main/onnx/{model_file}",
|
||||||
"preprocessor_config.json": "https://huggingface.co/jinaai/jina-clip-v2/resolve/main/preprocessor_config.json",
|
"preprocessor_config.json": f"{HF_ENDPOINT}/jinaai/jina-clip-v2/resolve/main/preprocessor_config.json",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.tokenizer_file = "tokenizer"
|
self.tokenizer_file = "tokenizer"
|
||||||
|
|||||||
@ -75,7 +75,7 @@ class EventProcessor(threading.Thread):
|
|||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.is_set():
|
||||||
update = self.event_receiver.check_for_update()
|
update = self.event_receiver.check_for_update(timeout=1)
|
||||||
|
|
||||||
if update == None:
|
if update == None:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import queue
|
|||||||
import signal
|
import signal
|
||||||
import threading
|
import threading
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from multiprocessing import Queue, Value
|
||||||
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
@ -15,6 +17,7 @@ from frigate.detectors import create_detector
|
|||||||
from frigate.detectors.detector_config import (
|
from frigate.detectors.detector_config import (
|
||||||
BaseDetectorConfig,
|
BaseDetectorConfig,
|
||||||
InputDTypeEnum,
|
InputDTypeEnum,
|
||||||
|
ModelConfig,
|
||||||
)
|
)
|
||||||
from frigate.util.builtin import EventsPerSecond, load_labels
|
from frigate.util.builtin import EventsPerSecond, load_labels
|
||||||
from frigate.util.image import SharedMemoryFrameManager, UntrackedSharedMemory
|
from frigate.util.image import SharedMemoryFrameManager, UntrackedSharedMemory
|
||||||
@ -85,11 +88,11 @@ class LocalObjectDetector(ObjectDetector):
|
|||||||
|
|
||||||
def run_detector(
|
def run_detector(
|
||||||
name: str,
|
name: str,
|
||||||
detection_queue: mp.Queue,
|
detection_queue: Queue,
|
||||||
out_events: dict[str, mp.Event],
|
out_events: dict[str, MpEvent],
|
||||||
avg_speed,
|
avg_speed: Value,
|
||||||
start,
|
start: Value,
|
||||||
detector_config,
|
detector_config: BaseDetectorConfig,
|
||||||
):
|
):
|
||||||
threading.current_thread().name = f"detector:{name}"
|
threading.current_thread().name = f"detector:{name}"
|
||||||
logger = logging.getLogger(f"detector.{name}")
|
logger = logging.getLogger(f"detector.{name}")
|
||||||
@ -97,7 +100,7 @@ def run_detector(
|
|||||||
setproctitle(f"frigate.detector.{name}")
|
setproctitle(f"frigate.detector.{name}")
|
||||||
listen()
|
listen()
|
||||||
|
|
||||||
stop_event = mp.Event()
|
stop_event: MpEvent = mp.Event()
|
||||||
|
|
||||||
def receiveSignal(signalNumber, frame):
|
def receiveSignal(signalNumber, frame):
|
||||||
stop_event.set()
|
stop_event.set()
|
||||||
@ -145,17 +148,17 @@ def run_detector(
|
|||||||
class ObjectDetectProcess:
|
class ObjectDetectProcess:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name,
|
name: str,
|
||||||
detection_queue,
|
detection_queue: Queue,
|
||||||
out_events,
|
out_events: dict[str, MpEvent],
|
||||||
detector_config,
|
detector_config: BaseDetectorConfig,
|
||||||
):
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.out_events = out_events
|
self.out_events = out_events
|
||||||
self.detection_queue = detection_queue
|
self.detection_queue = detection_queue
|
||||||
self.avg_inference_speed = mp.Value("d", 0.01)
|
self.avg_inference_speed = Value("d", 0.01)
|
||||||
self.detection_start = mp.Value("d", 0.0)
|
self.detection_start = Value("d", 0.0)
|
||||||
self.detect_process = None
|
self.detect_process: util.Process | None = None
|
||||||
self.detector_config = detector_config
|
self.detector_config = detector_config
|
||||||
self.start_or_restart()
|
self.start_or_restart()
|
||||||
|
|
||||||
@ -193,7 +196,15 @@ class ObjectDetectProcess:
|
|||||||
|
|
||||||
|
|
||||||
class RemoteObjectDetector:
|
class RemoteObjectDetector:
|
||||||
def __init__(self, name, labels, detection_queue, event, model_config, stop_event):
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
labels: dict[int, str],
|
||||||
|
detection_queue: Queue,
|
||||||
|
event: MpEvent,
|
||||||
|
model_config: ModelConfig,
|
||||||
|
stop_event: MpEvent,
|
||||||
|
):
|
||||||
self.labels = labels
|
self.labels = labels
|
||||||
self.name = name
|
self.name = name
|
||||||
self.fps = EventsPerSecond()
|
self.fps = EventsPerSecond()
|
||||||
|
|||||||
@ -206,6 +206,7 @@ class PtzAutoTracker:
|
|||||||
self.calibrating: dict[str, object] = {}
|
self.calibrating: dict[str, object] = {}
|
||||||
self.intercept: dict[str, object] = {}
|
self.intercept: dict[str, object] = {}
|
||||||
self.move_coefficients: dict[str, object] = {}
|
self.move_coefficients: dict[str, object] = {}
|
||||||
|
self.zoom_time: dict[str, float] = {}
|
||||||
self.zoom_factor: dict[str, object] = {}
|
self.zoom_factor: dict[str, object] = {}
|
||||||
|
|
||||||
# if cam is set to autotrack, onvif should be set up
|
# if cam is set to autotrack, onvif should be set up
|
||||||
@ -272,7 +273,12 @@ class PtzAutoTracker:
|
|||||||
|
|
||||||
move_status_supported = self.onvif.get_service_capabilities(camera)
|
move_status_supported = self.onvif.get_service_capabilities(camera)
|
||||||
|
|
||||||
if move_status_supported is None or move_status_supported.lower() != "true":
|
if not (
|
||||||
|
isinstance(move_status_supported, bool) and move_status_supported
|
||||||
|
) and not (
|
||||||
|
isinstance(move_status_supported, str)
|
||||||
|
and move_status_supported.lower() == "true"
|
||||||
|
):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Disabling autotracking for {camera}: ONVIF MoveStatus not supported"
|
f"Disabling autotracking for {camera}: ONVIF MoveStatus not supported"
|
||||||
)
|
)
|
||||||
@ -292,7 +298,7 @@ class PtzAutoTracker:
|
|||||||
self.move_threads[camera].start()
|
self.move_threads[camera].start()
|
||||||
|
|
||||||
if camera_config.onvif.autotracking.movement_weights:
|
if camera_config.onvif.autotracking.movement_weights:
|
||||||
if len(camera_config.onvif.autotracking.movement_weights) == 5:
|
if len(camera_config.onvif.autotracking.movement_weights) == 6:
|
||||||
camera_config.onvif.autotracking.movement_weights = [
|
camera_config.onvif.autotracking.movement_weights = [
|
||||||
float(val)
|
float(val)
|
||||||
for val in camera_config.onvif.autotracking.movement_weights
|
for val in camera_config.onvif.autotracking.movement_weights
|
||||||
@ -311,7 +317,10 @@ class PtzAutoTracker:
|
|||||||
camera_config.onvif.autotracking.movement_weights[2]
|
camera_config.onvif.autotracking.movement_weights[2]
|
||||||
)
|
)
|
||||||
self.move_coefficients[camera] = (
|
self.move_coefficients[camera] = (
|
||||||
camera_config.onvif.autotracking.movement_weights[3:]
|
camera_config.onvif.autotracking.movement_weights[3:5]
|
||||||
|
)
|
||||||
|
self.zoom_time[camera] = (
|
||||||
|
camera_config.onvif.autotracking.movement_weights[5]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
camera_config.onvif.autotracking.enabled = False
|
camera_config.onvif.autotracking.enabled = False
|
||||||
@ -360,6 +369,7 @@ class PtzAutoTracker:
|
|||||||
!= ZoomingModeEnum.disabled
|
!= ZoomingModeEnum.disabled
|
||||||
):
|
):
|
||||||
logger.info(f"Calibration for {camera} in progress: 0% complete")
|
logger.info(f"Calibration for {camera} in progress: 0% complete")
|
||||||
|
self.zoom_time[camera] = 0
|
||||||
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
# absolute move to 0 - fully zoomed out
|
# absolute move to 0 - fully zoomed out
|
||||||
@ -403,6 +413,7 @@ class PtzAutoTracker:
|
|||||||
|
|
||||||
zoom_out_values.append(self.ptz_metrics[camera].zoom_level.value)
|
zoom_out_values.append(self.ptz_metrics[camera].zoom_level.value)
|
||||||
|
|
||||||
|
zoom_start_time = time.time()
|
||||||
# relative move to 0.01
|
# relative move to 0.01
|
||||||
self.onvif._move_relative(
|
self.onvif._move_relative(
|
||||||
camera,
|
camera,
|
||||||
@ -415,13 +426,45 @@ class PtzAutoTracker:
|
|||||||
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
||||||
self.onvif.get_camera_status(camera)
|
self.onvif.get_camera_status(camera)
|
||||||
|
|
||||||
|
zoom_stop_time = time.time()
|
||||||
|
|
||||||
|
full_relative_start_time = time.time()
|
||||||
|
|
||||||
|
self.onvif._move_relative(
|
||||||
|
camera,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
-1e-2,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
||||||
|
self.onvif.get_camera_status(camera)
|
||||||
|
|
||||||
|
full_relative_stop_time = time.time()
|
||||||
|
|
||||||
|
self.onvif._move_relative(
|
||||||
|
camera,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1e-2,
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
|
||||||
|
while not self.ptz_metrics[camera].motor_stopped.is_set():
|
||||||
|
self.onvif.get_camera_status(camera)
|
||||||
|
|
||||||
|
self.zoom_time[camera] = (
|
||||||
|
full_relative_stop_time - full_relative_start_time
|
||||||
|
) - (zoom_stop_time - zoom_start_time)
|
||||||
|
|
||||||
zoom_in_values.append(self.ptz_metrics[camera].zoom_level.value)
|
zoom_in_values.append(self.ptz_metrics[camera].zoom_level.value)
|
||||||
|
|
||||||
self.ptz_metrics[camera].max_zoom.value = max(zoom_in_values)
|
self.ptz_metrics[camera].max_zoom.value = max(zoom_in_values)
|
||||||
self.ptz_metrics[camera].min_zoom.value = min(zoom_out_values)
|
self.ptz_metrics[camera].min_zoom.value = min(zoom_out_values)
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{camera}: Calibration values: max zoom: {self.ptz_metrics[camera].max_zoom.value}, min zoom: {self.ptz_metrics[camera].min_zoom.value}"
|
f"{camera}: Calibration values: max zoom: {self.ptz_metrics[camera].max_zoom.value}, min zoom: {self.ptz_metrics[camera].min_zoom.value}, zoom time: {self.zoom_time[camera]}"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -537,6 +580,7 @@ class PtzAutoTracker:
|
|||||||
self.ptz_metrics[camera].max_zoom.value,
|
self.ptz_metrics[camera].max_zoom.value,
|
||||||
self.intercept[camera],
|
self.intercept[camera],
|
||||||
*self.move_coefficients[camera],
|
*self.move_coefficients[camera],
|
||||||
|
self.zoom_time[camera],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1061,6 +1105,7 @@ class PtzAutoTracker:
|
|||||||
|
|
||||||
average_velocity = np.zeros((4,))
|
average_velocity = np.zeros((4,))
|
||||||
predicted_box = obj.obj_data["box"]
|
predicted_box = obj.obj_data["box"]
|
||||||
|
zoom_predicted_box = obj.obj_data["box"]
|
||||||
|
|
||||||
centroid_x = obj.obj_data["centroid"][0]
|
centroid_x = obj.obj_data["centroid"][0]
|
||||||
centroid_y = obj.obj_data["centroid"][1]
|
centroid_y = obj.obj_data["centroid"][1]
|
||||||
@ -1069,11 +1114,6 @@ class PtzAutoTracker:
|
|||||||
pan = ((centroid_x / camera_width) - 0.5) * 2
|
pan = ((centroid_x / camera_width) - 0.5) * 2
|
||||||
tilt = (0.5 - (centroid_y / camera_height)) * 2
|
tilt = (0.5 - (centroid_y / camera_height)) * 2
|
||||||
|
|
||||||
if (
|
|
||||||
camera_config.onvif.autotracking.movement_weights
|
|
||||||
): # use estimates if we have available coefficients
|
|
||||||
predicted_movement_time = self._predict_movement_time(camera, pan, tilt)
|
|
||||||
|
|
||||||
_, average_velocity = (
|
_, average_velocity = (
|
||||||
self._get_valid_velocity(camera, obj)
|
self._get_valid_velocity(camera, obj)
|
||||||
if "velocity" not in self.tracked_object_metrics[camera]
|
if "velocity" not in self.tracked_object_metrics[camera]
|
||||||
@ -1083,6 +1123,11 @@ class PtzAutoTracker:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
camera_config.onvif.autotracking.movement_weights
|
||||||
|
): # use estimates if we have available coefficients
|
||||||
|
predicted_movement_time = self._predict_movement_time(camera, pan, tilt)
|
||||||
|
|
||||||
if np.any(average_velocity):
|
if np.any(average_velocity):
|
||||||
# this box could exceed the frame boundaries if velocity is high
|
# this box could exceed the frame boundaries if velocity is high
|
||||||
# but we'll handle that in _enqueue_move() as two separate moves
|
# but we'll handle that in _enqueue_move() as two separate moves
|
||||||
@ -1111,6 +1156,34 @@ class PtzAutoTracker:
|
|||||||
camera, obj, predicted_box, predicted_movement_time, debug_zoom=True
|
camera, obj, predicted_box, predicted_movement_time, debug_zoom=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
camera_config.onvif.autotracking.movement_weights
|
||||||
|
and camera_config.onvif.autotracking.zooming == ZoomingModeEnum.relative
|
||||||
|
and zoom != 0
|
||||||
|
):
|
||||||
|
zoom_predicted_movement_time = 0
|
||||||
|
|
||||||
|
if np.any(average_velocity):
|
||||||
|
zoom_predicted_movement_time = abs(zoom) * self.zoom_time[camera]
|
||||||
|
|
||||||
|
zoom_predicted_box = (
|
||||||
|
predicted_box
|
||||||
|
+ camera_fps * zoom_predicted_movement_time * average_velocity
|
||||||
|
)
|
||||||
|
|
||||||
|
zoom_predicted_box = np.round(zoom_predicted_box).astype(int)
|
||||||
|
|
||||||
|
centroid_x = round((zoom_predicted_box[0] + zoom_predicted_box[2]) / 2)
|
||||||
|
centroid_y = round((zoom_predicted_box[1] + zoom_predicted_box[3]) / 2)
|
||||||
|
|
||||||
|
# recalculate pan and tilt with new centroid
|
||||||
|
pan = ((centroid_x / camera_width) - 0.5) * 2
|
||||||
|
tilt = (0.5 - (centroid_y / camera_height)) * 2
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"{camera}: Zoom amount: {zoom}, zoom predicted time: {zoom_predicted_movement_time}, zoom predicted box: {tuple(zoom_predicted_box)}"
|
||||||
|
)
|
||||||
|
|
||||||
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, zoom)
|
self._enqueue_move(camera, obj.obj_data["frame_time"], pan, tilt, zoom)
|
||||||
|
|
||||||
def _autotrack_move_zoom_only(self, camera, obj):
|
def _autotrack_move_zoom_only(self, camera, obj):
|
||||||
@ -1242,7 +1315,7 @@ class PtzAutoTracker:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# this is a brand new object that's on our camera, has our label, entered the zone,
|
# this is a brand new object that's on our camera, has our label, entered the zone,
|
||||||
# is not a false positive, and is not initially motionless
|
# is not a false positive, and is active
|
||||||
if (
|
if (
|
||||||
# new object
|
# new object
|
||||||
self.tracked_object[camera] is None
|
self.tracked_object[camera] is None
|
||||||
@ -1252,7 +1325,7 @@ class PtzAutoTracker:
|
|||||||
and not obj.previous["false_positive"]
|
and not obj.previous["false_positive"]
|
||||||
and not obj.false_positive
|
and not obj.false_positive
|
||||||
and not self.tracked_object_history[camera]
|
and not self.tracked_object_history[camera]
|
||||||
and obj.obj_data["motionless_count"] == 0
|
and obj.active
|
||||||
):
|
):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
|
f"{camera}: New object: {obj.obj_data['id']} {obj.obj_data['box']} {obj.obj_data['frame_time']}"
|
||||||
|
|||||||
@ -74,7 +74,7 @@ class OnvifController:
|
|||||||
"features": [],
|
"features": [],
|
||||||
"presets": {},
|
"presets": {},
|
||||||
}
|
}
|
||||||
except ONVIFError as e:
|
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||||
logger.error(f"Failed to create ONVIF camera instance for {cam_name}: {e}")
|
logger.error(f"Failed to create ONVIF camera instance for {cam_name}: {e}")
|
||||||
# track initial failures
|
# track initial failures
|
||||||
self.failed_cams[cam_name] = {
|
self.failed_cams[cam_name] = {
|
||||||
@ -100,7 +100,7 @@ class OnvifController:
|
|||||||
# this will fire an exception if camera is not a ptz
|
# this will fire an exception if camera is not a ptz
|
||||||
capabilities = onvif.get_definition("ptz")
|
capabilities = onvif.get_definition("ptz")
|
||||||
logger.debug(f"Onvif capabilities for {camera_name}: {capabilities}")
|
logger.debug(f"Onvif capabilities for {camera_name}: {capabilities}")
|
||||||
except (ONVIFError, Fault, TransportError) as e:
|
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Unable to get Onvif capabilities for camera: {camera_name}: {e}"
|
f"Unable to get Onvif capabilities for camera: {camera_name}: {e}"
|
||||||
)
|
)
|
||||||
@ -109,7 +109,7 @@ class OnvifController:
|
|||||||
try:
|
try:
|
||||||
profiles = await media.GetProfiles()
|
profiles = await media.GetProfiles()
|
||||||
logger.debug(f"Onvif profiles for {camera_name}: {profiles}")
|
logger.debug(f"Onvif profiles for {camera_name}: {profiles}")
|
||||||
except (ONVIFError, Fault, TransportError) as e:
|
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Unable to get Onvif media profiles for camera: {camera_name}: {e}"
|
f"Unable to get Onvif media profiles for camera: {camera_name}: {e}"
|
||||||
)
|
)
|
||||||
@ -263,7 +263,7 @@ class OnvifController:
|
|||||||
# setup existing presets
|
# setup existing presets
|
||||||
try:
|
try:
|
||||||
presets: list[dict] = await ptz.GetPresets({"ProfileToken": profile.token})
|
presets: list[dict] = await ptz.GetPresets({"ProfileToken": profile.token})
|
||||||
except ONVIFError as e:
|
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||||
logger.warning(f"Unable to get presets from camera: {camera_name}: {e}")
|
logger.warning(f"Unable to get presets from camera: {camera_name}: {e}")
|
||||||
presets = []
|
presets = []
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ class OnvifController:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
asyncio.run(self.cams[camera_name]["ptz"].ContinuousMove(move_request))
|
asyncio.run(self.cams[camera_name]["ptz"].ContinuousMove(move_request))
|
||||||
except ONVIFError as e:
|
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||||
logger.warning(f"Onvif sending move request to {camera_name} failed: {e}")
|
logger.warning(f"Onvif sending move request to {camera_name} failed: {e}")
|
||||||
|
|
||||||
def _move_relative(self, camera_name: str, pan, tilt, zoom, speed) -> None:
|
def _move_relative(self, camera_name: str, pan, tilt, zoom, speed) -> None:
|
||||||
@ -593,7 +593,7 @@ class OnvifController:
|
|||||||
self._zoom(camera_name, command)
|
self._zoom(camera_name, command)
|
||||||
else:
|
else:
|
||||||
self._move(camera_name, command)
|
self._move(camera_name, command)
|
||||||
except ONVIFError as e:
|
except (Fault, ONVIFError, TransportError, Exception) as e:
|
||||||
logger.error(f"Unable to handle onvif command: {e}")
|
logger.error(f"Unable to handle onvif command: {e}")
|
||||||
|
|
||||||
async def get_camera_info(self, camera_name: str) -> dict[str, any]:
|
async def get_camera_info(self, camera_name: str) -> dict[str, any]:
|
||||||
|
|||||||
@ -27,6 +27,7 @@ from frigate.config import FrigateConfig, RetainModeEnum
|
|||||||
from frigate.const import (
|
from frigate.const import (
|
||||||
CACHE_DIR,
|
CACHE_DIR,
|
||||||
CACHE_SEGMENT_FORMAT,
|
CACHE_SEGMENT_FORMAT,
|
||||||
|
FAST_QUEUE_TIMEOUT,
|
||||||
INSERT_MANY_RECORDINGS,
|
INSERT_MANY_RECORDINGS,
|
||||||
MAX_SEGMENT_DURATION,
|
MAX_SEGMENT_DURATION,
|
||||||
MAX_SEGMENTS_IN_CACHE,
|
MAX_SEGMENTS_IN_CACHE,
|
||||||
@ -38,8 +39,6 @@ from frigate.util.services import get_video_properties
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
QUEUE_READ_TIMEOUT = 0.00001 # seconds
|
|
||||||
|
|
||||||
|
|
||||||
class SegmentInfo:
|
class SegmentInfo:
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -536,7 +535,7 @@ class RecordingMaintainer(threading.Thread):
|
|||||||
# empty the object recordings info queue
|
# empty the object recordings info queue
|
||||||
while True:
|
while True:
|
||||||
(topic, data) = self.detection_subscriber.check_for_update(
|
(topic, data) = self.detection_subscriber.check_for_update(
|
||||||
timeout=QUEUE_READ_TIMEOUT
|
timeout=FAST_QUEUE_TIMEOUT
|
||||||
)
|
)
|
||||||
|
|
||||||
if not topic:
|
if not topic:
|
||||||
|
|||||||
@ -1491,7 +1491,9 @@ class TestConfig(unittest.TestCase):
|
|||||||
"fps": 5,
|
"fps": 5,
|
||||||
},
|
},
|
||||||
"onvif": {
|
"onvif": {
|
||||||
"autotracking": {"movement_weights": "0, 1, 1.23, 2.34, 0.50"}
|
"autotracking": {
|
||||||
|
"movement_weights": "0, 1, 1.23, 2.34, 0.50, 1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1504,6 +1506,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
"1.23",
|
"1.23",
|
||||||
"2.34",
|
"2.34",
|
||||||
"0.5",
|
"0.5",
|
||||||
|
"1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_fails_invalid_movement_weights(self):
|
def test_fails_invalid_movement_weights(self):
|
||||||
|
|||||||
@ -28,7 +28,7 @@ from frigate.config import (
|
|||||||
RecordConfig,
|
RecordConfig,
|
||||||
SnapshotsConfig,
|
SnapshotsConfig,
|
||||||
)
|
)
|
||||||
from frigate.const import UPDATE_CAMERA_ACTIVITY
|
from frigate.const import FAST_QUEUE_TIMEOUT, UPDATE_CAMERA_ACTIVITY
|
||||||
from frigate.events.types import EventStateEnum, EventTypeEnum
|
from frigate.events.types import EventStateEnum, EventTypeEnum
|
||||||
from frigate.models import Event, Timeline
|
from frigate.models import Event, Timeline
|
||||||
from frigate.track.tracked_object import TrackedObject
|
from frigate.track.tracked_object import TrackedObject
|
||||||
@ -682,7 +682,9 @@ class TrackedObjectProcessor(threading.Thread):
|
|||||||
|
|
||||||
# cleanup event finished queue
|
# cleanup event finished queue
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.is_set():
|
||||||
update = self.event_end_subscriber.check_for_update(timeout=0.01)
|
update = self.event_end_subscriber.check_for_update(
|
||||||
|
timeout=FAST_QUEUE_TIMEOUT
|
||||||
|
)
|
||||||
|
|
||||||
if not update:
|
if not update:
|
||||||
break
|
break
|
||||||
|
|||||||
@ -25,3 +25,5 @@ class ModelStatusTypesEnum(str, Enum):
|
|||||||
|
|
||||||
class TrackedObjectUpdateTypesEnum(str, Enum):
|
class TrackedObjectUpdateTypesEnum(str, Enum):
|
||||||
description = "description"
|
description = "description"
|
||||||
|
face = "face"
|
||||||
|
lpr = "lpr"
|
||||||
|
|||||||
@ -319,6 +319,21 @@ def migrate_016_0(config: dict[str, dict[str, any]]) -> dict[str, dict[str, any]
|
|||||||
|
|
||||||
camera_config["live"] = live_config
|
camera_config["live"] = live_config
|
||||||
|
|
||||||
|
# add another value to movement_weights for autotracking cams
|
||||||
|
onvif_config = camera_config.get("onvif", {})
|
||||||
|
if "autotracking" in onvif_config:
|
||||||
|
movement_weights = (
|
||||||
|
camera_config.get("onvif", {})
|
||||||
|
.get("autotracking")
|
||||||
|
.get("movement_weights", {})
|
||||||
|
)
|
||||||
|
|
||||||
|
if movement_weights and len(movement_weights.split(",")) == 5:
|
||||||
|
onvif_config["autotracking"]["movement_weights"] = (
|
||||||
|
movement_weights + ", 0"
|
||||||
|
)
|
||||||
|
camera_config["onvif"] = onvif_config
|
||||||
|
|
||||||
new_config["cameras"][name] = camera_config
|
new_config["cameras"][name] = camera_config
|
||||||
|
|
||||||
new_config["version"] = "0.16-0"
|
new_config["version"] = "0.16-0"
|
||||||
|
|||||||
@ -340,7 +340,6 @@ def get_ort_providers(
|
|||||||
providers.append(provider)
|
providers.append(provider)
|
||||||
options.append(
|
options.append(
|
||||||
{
|
{
|
||||||
"arena_extend_strategy": "kSameAsRequested",
|
|
||||||
"cache_dir": os.path.join(MODEL_CACHE_DIR, "openvino/ort"),
|
"cache_dir": os.path.join(MODEL_CACHE_DIR, "openvino/ort"),
|
||||||
"device_type": device,
|
"device_type": device,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -408,7 +408,13 @@ def get_rockchip_npu_stats() -> dict[str, str]:
|
|||||||
try:
|
try:
|
||||||
with open("/sys/kernel/debug/rknpu/load", "r") as f:
|
with open("/sys/kernel/debug/rknpu/load", "r") as f:
|
||||||
npu_output = f.read()
|
npu_output = f.read()
|
||||||
|
|
||||||
|
if "Core0:" in npu_output:
|
||||||
|
# multi core NPU
|
||||||
core_loads = re.findall(r"Core\d+:\s*(\d+)%", npu_output)
|
core_loads = re.findall(r"Core\d+:\s*(\d+)%", npu_output)
|
||||||
|
else:
|
||||||
|
# single core NPU
|
||||||
|
core_loads = re.findall(r"NPU load:\s+(\d+)%", npu_output)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
core_loads = None
|
core_loads = None
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,9 @@ import signal
|
|||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
from multiprocessing import Queue, Value
|
||||||
|
from multiprocessing.synchronize import Event as MpEvent
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
@ -99,10 +102,10 @@ def capture_frames(
|
|||||||
frame_shape: tuple[int, int],
|
frame_shape: tuple[int, int],
|
||||||
frame_manager: FrameManager,
|
frame_manager: FrameManager,
|
||||||
frame_queue,
|
frame_queue,
|
||||||
fps: mp.Value,
|
fps: Value,
|
||||||
skipped_fps: mp.Value,
|
skipped_fps: Value,
|
||||||
current_frame: mp.Value,
|
current_frame: Value,
|
||||||
stop_event: mp.Event,
|
stop_event: MpEvent,
|
||||||
):
|
):
|
||||||
frame_size = frame_shape[0] * frame_shape[1]
|
frame_size = frame_shape[0] * frame_shape[1]
|
||||||
frame_rate = EventsPerSecond()
|
frame_rate = EventsPerSecond()
|
||||||
@ -167,7 +170,7 @@ class CameraWatchdog(threading.Thread):
|
|||||||
camera_name,
|
camera_name,
|
||||||
config: CameraConfig,
|
config: CameraConfig,
|
||||||
shm_frame_count: int,
|
shm_frame_count: int,
|
||||||
frame_queue: mp.Queue,
|
frame_queue: Queue,
|
||||||
camera_fps,
|
camera_fps,
|
||||||
skipped_fps,
|
skipped_fps,
|
||||||
ffmpeg_pid,
|
ffmpeg_pid,
|
||||||
@ -402,10 +405,10 @@ class CameraCapture(threading.Thread):
|
|||||||
frame_index: int,
|
frame_index: int,
|
||||||
ffmpeg_process,
|
ffmpeg_process,
|
||||||
frame_shape: tuple[int, int],
|
frame_shape: tuple[int, int],
|
||||||
frame_queue: mp.Queue,
|
frame_queue: Queue,
|
||||||
fps,
|
fps: Value,
|
||||||
skipped_fps,
|
skipped_fps: Value,
|
||||||
stop_event,
|
stop_event: MpEvent,
|
||||||
):
|
):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.name = f"capture:{config.name}"
|
self.name = f"capture:{config.name}"
|
||||||
@ -419,7 +422,7 @@ class CameraCapture(threading.Thread):
|
|||||||
self.skipped_fps = skipped_fps
|
self.skipped_fps = skipped_fps
|
||||||
self.frame_manager = SharedMemoryFrameManager()
|
self.frame_manager = SharedMemoryFrameManager()
|
||||||
self.ffmpeg_process = ffmpeg_process
|
self.ffmpeg_process = ffmpeg_process
|
||||||
self.current_frame = mp.Value("d", 0.0)
|
self.current_frame = Value("d", 0.0)
|
||||||
self.last_frame = 0
|
self.last_frame = 0
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -469,14 +472,14 @@ def capture_camera(
|
|||||||
def track_camera(
|
def track_camera(
|
||||||
name,
|
name,
|
||||||
config: CameraConfig,
|
config: CameraConfig,
|
||||||
model_config,
|
model_config: ModelConfig,
|
||||||
labelmap,
|
labelmap: dict[int, str],
|
||||||
detection_queue,
|
detection_queue: Queue,
|
||||||
result_connection,
|
result_connection: MpEvent,
|
||||||
detected_objects_queue,
|
detected_objects_queue,
|
||||||
camera_metrics: CameraMetrics,
|
camera_metrics: CameraMetrics,
|
||||||
ptz_metrics: PTZMetrics,
|
ptz_metrics: PTZMetrics,
|
||||||
region_grid,
|
region_grid: list[list[dict[str, Any]]],
|
||||||
):
|
):
|
||||||
stop_event = mp.Event()
|
stop_event = mp.Event()
|
||||||
|
|
||||||
@ -584,8 +587,8 @@ def detect(
|
|||||||
def process_frames(
|
def process_frames(
|
||||||
camera_name: str,
|
camera_name: str,
|
||||||
requestor: InterProcessRequestor,
|
requestor: InterProcessRequestor,
|
||||||
frame_queue: mp.Queue,
|
frame_queue: Queue,
|
||||||
frame_shape,
|
frame_shape: tuple[int, int],
|
||||||
model_config: ModelConfig,
|
model_config: ModelConfig,
|
||||||
camera_config: CameraConfig,
|
camera_config: CameraConfig,
|
||||||
detect_config: DetectConfig,
|
detect_config: DetectConfig,
|
||||||
@ -593,13 +596,13 @@ def process_frames(
|
|||||||
motion_detector: MotionDetector,
|
motion_detector: MotionDetector,
|
||||||
object_detector: RemoteObjectDetector,
|
object_detector: RemoteObjectDetector,
|
||||||
object_tracker: ObjectTracker,
|
object_tracker: ObjectTracker,
|
||||||
detected_objects_queue: mp.Queue,
|
detected_objects_queue: Queue,
|
||||||
camera_metrics: CameraMetrics,
|
camera_metrics: CameraMetrics,
|
||||||
objects_to_track: list[str],
|
objects_to_track: list[str],
|
||||||
object_filters,
|
object_filters,
|
||||||
stop_event,
|
stop_event: MpEvent,
|
||||||
ptz_metrics: PTZMetrics,
|
ptz_metrics: PTZMetrics,
|
||||||
region_grid,
|
region_grid: list[list[dict[str, Any]]],
|
||||||
exit_on_empty: bool = False,
|
exit_on_empty: bool = False,
|
||||||
):
|
):
|
||||||
next_region_update = get_tomorrow_at_time(2)
|
next_region_update = get_tomorrow_at_time(2)
|
||||||
|
|||||||
1
web/public/locales/ca/audio.json
Normal file
1
web/public/locales/ca/audio.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/common.json
Normal file
1
web/public/locales/ca/common.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/auth.json
Normal file
1
web/public/locales/ca/components/auth.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/camera.json
Normal file
1
web/public/locales/ca/components/camera.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/dialog.json
Normal file
1
web/public/locales/ca/components/dialog.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/filter.json
Normal file
1
web/public/locales/ca/components/filter.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/icons.json
Normal file
1
web/public/locales/ca/components/icons.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/input.json
Normal file
1
web/public/locales/ca/components/input.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/components/player.json
Normal file
1
web/public/locales/ca/components/player.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/objects.json
Normal file
1
web/public/locales/ca/objects.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/configEditor.json
Normal file
1
web/public/locales/ca/views/configEditor.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/events.json
Normal file
1
web/public/locales/ca/views/events.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/explore.json
Normal file
1
web/public/locales/ca/views/explore.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/exports.json
Normal file
1
web/public/locales/ca/views/exports.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/faceLibrary.json
Normal file
1
web/public/locales/ca/views/faceLibrary.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/live.json
Normal file
1
web/public/locales/ca/views/live.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/recording.json
Normal file
1
web/public/locales/ca/views/recording.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/search.json
Normal file
1
web/public/locales/ca/views/search.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/settings.json
Normal file
1
web/public/locales/ca/views/settings.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
web/public/locales/ca/views/system.json
Normal file
1
web/public/locales/ca/views/system.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"disabled": "Vypnuto",
|
"disabled": "Vypnuto",
|
||||||
"disable": "Vypni",
|
"disable": "Vypni",
|
||||||
"save": "Uložit",
|
"save": "Uložit",
|
||||||
"saving": "Ukládám...",
|
"saving": "Ukládám…",
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"close": "Zavři",
|
"close": "Zavři",
|
||||||
"copy": "Zkopíruj",
|
"copy": "Zkopíruj",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"iconPicker": {
|
"iconPicker": {
|
||||||
"selectIcon": "Zvolte ikonu",
|
"selectIcon": "Zvolte ikonu",
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "Hledejte ikonu...."
|
"placeholder": "Hledejte ikonu…"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
"value": "{{seconds}} sekund",
|
"value": "{{seconds}} sekund",
|
||||||
"short": {
|
"short": {
|
||||||
"title": "Latence",
|
"title": "Latence",
|
||||||
"value": "{{seconds}} sek."
|
"value": "{{seconds}} sek"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"totalFrames": "Celkový počet snímků:",
|
"totalFrames": "Celkový počet snímků:",
|
||||||
|
|||||||
@ -56,6 +56,27 @@
|
|||||||
"formattedTimestampExcludeSeconds": {
|
"formattedTimestampExcludeSeconds": {
|
||||||
"24hour": "%-d %b, %H:%M",
|
"24hour": "%-d %b, %H:%M",
|
||||||
"12hour": "%-d %b, %H:%M"
|
"12hour": "%-d %b, %H:%M"
|
||||||
|
},
|
||||||
|
"formattedTimestampHourMinute": {
|
||||||
|
"12hour": "h:mm aaa",
|
||||||
|
"24hour": "HH:mm"
|
||||||
|
},
|
||||||
|
"formattedTimestampHourMinuteSecond": {
|
||||||
|
"12hour": "h:mm:ss aaa",
|
||||||
|
"24hour": "HH:mm:ss"
|
||||||
|
},
|
||||||
|
"formattedTimestampMonthDayHourMinute": {
|
||||||
|
"12hour": "MMM d, h:mm aaa",
|
||||||
|
"24hour": "MMM d, HH:mm"
|
||||||
|
},
|
||||||
|
"formattedTimestampMonthDayYearHourMinute": {
|
||||||
|
"12hour": "MMM d yyyy, h:mm aaa",
|
||||||
|
"24hour": "MMM d yyyy, HH:mm"
|
||||||
|
},
|
||||||
|
"formattedTimestampMonthDay": "MMM d",
|
||||||
|
"formattedTimestampFilename": {
|
||||||
|
"12hour": "MM-dd-yy-h-mm-ss-a",
|
||||||
|
"24hour": "MM-dd-yy-HH-mm-ss"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
@ -66,7 +87,7 @@
|
|||||||
"enable": "Aktivieren",
|
"enable": "Aktivieren",
|
||||||
"disabled": "deaktiviert",
|
"disabled": "deaktiviert",
|
||||||
"disable": "deaktivieren",
|
"disable": "deaktivieren",
|
||||||
"saving": "Speichere...",
|
"saving": "Speichere…",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
"back": "Zurück",
|
"back": "Zurück",
|
||||||
"history": "Historie",
|
"history": "Historie",
|
||||||
@ -132,7 +153,8 @@
|
|||||||
"fa": "Persisch",
|
"fa": "Persisch",
|
||||||
"uk": "Ukrainisch",
|
"uk": "Ukrainisch",
|
||||||
"he": "Hebräisch",
|
"he": "Hebräisch",
|
||||||
"sk": "Slowakisch"
|
"sk": "Slowakisch",
|
||||||
|
"yue": "粵語 (Kantonesisch)"
|
||||||
},
|
},
|
||||||
"appearance": "Erscheinung",
|
"appearance": "Erscheinung",
|
||||||
"theme": {
|
"theme": {
|
||||||
@ -142,7 +164,8 @@
|
|||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"nord": "Norden",
|
"nord": "Norden",
|
||||||
"red": "Rot",
|
"red": "Rot",
|
||||||
"contrast": "Hoher Kontrast"
|
"contrast": "Hoher Kontrast",
|
||||||
|
"highcontrast": "Hoher Kontrast"
|
||||||
},
|
},
|
||||||
"help": "Hilfe",
|
"help": "Hilfe",
|
||||||
"documentation": {
|
"documentation": {
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"placeholder": "Gib einen Namen ein...",
|
"placeholder": "Gib einen Namen ein…",
|
||||||
"errorMessage": {
|
"errorMessage": {
|
||||||
"exists": "Name der Kameragruppe bereits vorhanden.",
|
"exists": "Name der Kameragruppe bereits vorhanden.",
|
||||||
"nameMustNotPeriod": "Name einer Kameragruppe darf keinen Punkt enthalten.",
|
"nameMustNotPeriod": "Name einer Kameragruppe darf keinen Punkt enthalten.",
|
||||||
|
|||||||
@ -23,6 +23,12 @@
|
|||||||
"false_one": "Das ist kein(e) {{label}}",
|
"false_one": "Das ist kein(e) {{label}}",
|
||||||
"false_other": "Das sind kein(e) {{label}}",
|
"false_other": "Das sind kein(e) {{label}}",
|
||||||
"label": "Bestätige dieses Label nicht für Frigate Plus"
|
"label": "Bestätige dieses Label nicht für Frigate Plus"
|
||||||
|
},
|
||||||
|
"question": {
|
||||||
|
"label": "Bestätige diese Beschriftung für Frigate Plus",
|
||||||
|
"ask_a": "Ist dieses Objekt ein <code>{{label}}</code>?",
|
||||||
|
"ask_an": "Ist dieses Objekt ein <code>{{label}}</code>?",
|
||||||
|
"ask_full": "Ist dieses Objekt ein <code>{{untranslatedLabel}}</code> ({{translatedLabel}})?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"submitToPlus": {
|
"submitToPlus": {
|
||||||
@ -72,7 +78,7 @@
|
|||||||
"restreaming": {
|
"restreaming": {
|
||||||
"disabled": "Für diese Kamera ist das Restreaming nicht aktiviert.",
|
"disabled": "Für diese Kamera ist das Restreaming nicht aktiviert.",
|
||||||
"desc": {
|
"desc": {
|
||||||
"readTheDocumentation": "Weitere Informationen in der Dokumentation ",
|
"readTheDocumentation": "Weitere Informationen in der Dokumentation",
|
||||||
"title": "Konfiguriere go2rtc, um erweiterte Live-Ansichtsoptionen und Audio für diese Kamera zu nutzen."
|
"title": "Konfiguriere go2rtc, um erweiterte Live-Ansichtsoptionen und Audio für diese Kamera zu nutzen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -118,8 +118,8 @@
|
|||||||
"noLicensePlatesFound": "Keine Kennzeichen gefunden.",
|
"noLicensePlatesFound": "Keine Kennzeichen gefunden.",
|
||||||
"title": "Bekannte Kennzeichen",
|
"title": "Bekannte Kennzeichen",
|
||||||
"loadFailed": "Bekannte Nummernschilder konnten nicht geladen werden.",
|
"loadFailed": "Bekannte Nummernschilder konnten nicht geladen werden.",
|
||||||
"loading": "Lade bekannte Nummernschilder...",
|
"loading": "Lade bekannte Nummernschilder…",
|
||||||
"placeholder": "Tippe, um Kennzeichen zu suchen...",
|
"placeholder": "Tippe, um Kennzeichen zu suchen…",
|
||||||
"selectPlatesFromList": "Wählen eine oder mehrere Kennzeichen aus der Liste aus."
|
"selectPlatesFromList": "Wählen eine oder mehrere Kennzeichen aus der Liste aus."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"iconPicker": {
|
"iconPicker": {
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "Suche nach einem Icon..."
|
"placeholder": "Suche nach einem Icon…"
|
||||||
},
|
},
|
||||||
"selectIcon": "Wähle ein Icon"
|
"selectIcon": "Wähle ein Icon"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,5 +31,7 @@
|
|||||||
"markTheseItemsAsReviewed": "Diese Objekte als geprüft kennzeichnen",
|
"markTheseItemsAsReviewed": "Diese Objekte als geprüft kennzeichnen",
|
||||||
"camera": "Kamera",
|
"camera": "Kamera",
|
||||||
"allCameras": "Alle Kameras",
|
"allCameras": "Alle Kameras",
|
||||||
"markAsReviewed": "Als geprüft kennzeichnen"
|
"markAsReviewed": "Als geprüft kennzeichnen",
|
||||||
|
"selected_one": "{{count}} ausgewählt",
|
||||||
|
"selected_other": "{{count}} ausgewählt"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,7 +75,7 @@
|
|||||||
"title": "Erkunden ist nicht Verfügbar",
|
"title": "Erkunden ist nicht Verfügbar",
|
||||||
"embeddingsReindexing": {
|
"embeddingsReindexing": {
|
||||||
"context": "Erkunden kann nach der Re-Indexierung der verfolgten Objekte verwendet werden.",
|
"context": "Erkunden kann nach der Re-Indexierung der verfolgten Objekte verwendet werden.",
|
||||||
"startingUp": "Startet...",
|
"startingUp": "Startet…",
|
||||||
"estimatedTime": "Voraussichtlich verbleibende Zeit:",
|
"estimatedTime": "Voraussichtlich verbleibende Zeit:",
|
||||||
"finishingShortly": "Bald erledigt",
|
"finishingShortly": "Bald erledigt",
|
||||||
"step": {
|
"step": {
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
"selectItem": "Wähle {{item}}",
|
"selectItem": "Wähle {{item}}",
|
||||||
"selectFace": "Wähle Gesicht",
|
"selectFace": "Wähle Gesicht",
|
||||||
"imageEntry": {
|
"imageEntry": {
|
||||||
"dropActive": "Ziehe das Bild hierher ...",
|
"dropActive": "Ziehe das Bild hierher…",
|
||||||
"dropInstructions": "Ziehe ein Bild hier her oder klicke um eines auszuwählen",
|
"dropInstructions": "Ziehe ein Bild hier her oder klicke um eines auszuwählen",
|
||||||
"maxSize": "Maximale Größe: {{size}} MB",
|
"maxSize": "Maximale Größe: {{size}} MB",
|
||||||
"validation": {
|
"validation": {
|
||||||
@ -35,7 +35,9 @@
|
|||||||
"addFace": "Gesicht hinzufügen",
|
"addFace": "Gesicht hinzufügen",
|
||||||
"uploadImage": "Bild hochladen",
|
"uploadImage": "Bild hochladen",
|
||||||
"deleteFaceAttempts": "Lösche Gesichtsversuche",
|
"deleteFaceAttempts": "Lösche Gesichtsversuche",
|
||||||
"reprocessFace": "Gesichter erneut verarbeiten"
|
"reprocessFace": "Gesichter erneut verarbeiten",
|
||||||
|
"renameFace": "Gesicht umbenennen",
|
||||||
|
"deleteFace": "Lösche Gesicht"
|
||||||
},
|
},
|
||||||
"train": {
|
"train": {
|
||||||
"title": "Trainiere",
|
"title": "Trainiere",
|
||||||
@ -72,5 +74,9 @@
|
|||||||
"uploadFace": "Lade Bild des Gesichts hoch",
|
"uploadFace": "Lade Bild des Gesichts hoch",
|
||||||
"nextSteps": "Nächste Schritte",
|
"nextSteps": "Nächste Schritte",
|
||||||
"faceName": "Gib den Namen zum Gesicht ein"
|
"faceName": "Gib den Namen zum Gesicht ein"
|
||||||
|
},
|
||||||
|
"renameFace": {
|
||||||
|
"title": "Gesicht umbenennen",
|
||||||
|
"desc": "Gib den neuen Namen für das Gesicht von {{name}} ein."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,6 @@
|
|||||||
},
|
},
|
||||||
"search": "Suche",
|
"search": "Suche",
|
||||||
"placeholder": {
|
"placeholder": {
|
||||||
"search": "Suchen..."
|
"search": "Suchen…"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -86,7 +86,7 @@
|
|||||||
"semanticSearch": {
|
"semanticSearch": {
|
||||||
"title": "Semantische Suche",
|
"title": "Semantische Suche",
|
||||||
"desc": "Die semantische Suche in Frigate ermöglicht es, verfolgte Objekte innerhalb der Überprüfungselemente zu finden, indem entweder das Bild selbst, eine benutzerdefinierte Textbeschreibung oder eine automatisch generierte Beschreibung verwendet wird.",
|
"desc": "Die semantische Suche in Frigate ermöglicht es, verfolgte Objekte innerhalb der Überprüfungselemente zu finden, indem entweder das Bild selbst, eine benutzerdefinierte Textbeschreibung oder eine automatisch generierte Beschreibung verwendet wird.",
|
||||||
"readTheDocumentation": "Lesen Sie die Dokumentation.",
|
"readTheDocumentation": "Lesen Sie die Dokumentation",
|
||||||
"reindexNow": {
|
"reindexNow": {
|
||||||
"alreadyInProgress": "Neu-Indizierung läufts bereits.",
|
"alreadyInProgress": "Neu-Indizierung läufts bereits.",
|
||||||
"label": "Neuindizieren",
|
"label": "Neuindizieren",
|
||||||
@ -221,7 +221,7 @@
|
|||||||
"add": "Zone hinzufügen",
|
"add": "Zone hinzufügen",
|
||||||
"name": {
|
"name": {
|
||||||
"title": "Name",
|
"title": "Name",
|
||||||
"inputPlaceHolder": "Geben Sie einen Namen ein...",
|
"inputPlaceHolder": "Geben Sie einen Namen ein…",
|
||||||
"tips": "Der Name muss aus mindestens 2 Zeichen bestehen und sollte nicht den Namen einer Kamera oder anderen Zone entsprechen."
|
"tips": "Der Name muss aus mindestens 2 Zeichen bestehen und sollte nicht den Namen einer Kamera oder anderen Zone entsprechen."
|
||||||
},
|
},
|
||||||
"objects": {
|
"objects": {
|
||||||
|
|||||||
@ -29,14 +29,17 @@
|
|||||||
"gpuUsage": "GPU Auslastung",
|
"gpuUsage": "GPU Auslastung",
|
||||||
"gpuMemory": "Grafikspeicher",
|
"gpuMemory": "Grafikspeicher",
|
||||||
"gpuDecoder": "GPU Decoder",
|
"gpuDecoder": "GPU Decoder",
|
||||||
"gpuEncoder": "GPU Encoder"
|
"gpuEncoder": "GPU Encoder",
|
||||||
|
"npuUsage": "NPU Verwendung",
|
||||||
|
"npuMemory": "NPU Speicher"
|
||||||
},
|
},
|
||||||
"title": "Allgemein",
|
"title": "Allgemein",
|
||||||
"detector": {
|
"detector": {
|
||||||
"title": "Detektoren",
|
"title": "Detektoren",
|
||||||
"cpuUsage": "CPU-Auslastung des Detektors",
|
"cpuUsage": "CPU-Auslastung des Detektors",
|
||||||
"memoryUsage": "Arbeitsspeichernutzung des Detektors",
|
"memoryUsage": "Arbeitsspeichernutzung des Detektors",
|
||||||
"inferenceSpeed": "Detektoren Inferenzgeschwindigkeit"
|
"inferenceSpeed": "Detektoren Inferenzgeschwindigkeit",
|
||||||
|
"temperature": "Temperatur des Detektors"
|
||||||
},
|
},
|
||||||
"otherProcesses": {
|
"otherProcesses": {
|
||||||
"title": "Andere Prozesse",
|
"title": "Andere Prozesse",
|
||||||
@ -142,7 +145,11 @@
|
|||||||
"image_embedding_speed": "Geschwindigkeit der Bildeinbettung",
|
"image_embedding_speed": "Geschwindigkeit der Bildeinbettung",
|
||||||
"face_embedding_speed": "Geschwindigkeit der Gesichtseinbettung",
|
"face_embedding_speed": "Geschwindigkeit der Gesichtseinbettung",
|
||||||
"plate_recognition_speed": "Geschwindigkeit der Kennzeichenerkennung",
|
"plate_recognition_speed": "Geschwindigkeit der Kennzeichenerkennung",
|
||||||
"text_embedding_speed": "Geschwindigkeit der Texteinbettung"
|
"text_embedding_speed": "Geschwindigkeit der Texteinbettung",
|
||||||
|
"plate_recognition": "Kennzeichen Erkennung",
|
||||||
|
"face_recognition_speed": "Gesichts Erkennungs Geschwindigkeit",
|
||||||
|
"text_embedding": "Einbettung von Bildern",
|
||||||
|
"face_recognition": "Gesichts Erkennung"
|
||||||
},
|
},
|
||||||
"title": "Optimierungen",
|
"title": "Optimierungen",
|
||||||
"infPerSecond": "Rückschlüsse pro Sekunde"
|
"infPerSecond": "Rückschlüsse pro Sekunde"
|
||||||
@ -151,7 +158,10 @@
|
|||||||
"healthy": "Das System läuft problemlos",
|
"healthy": "Das System läuft problemlos",
|
||||||
"ffmpegHighCpuUsage": "{{camera}} hat eine hohe FFMPEG CPU Auslastung ({{ffmpegAvg}}%)",
|
"ffmpegHighCpuUsage": "{{camera}} hat eine hohe FFMPEG CPU Auslastung ({{ffmpegAvg}}%)",
|
||||||
"detectHighCpuUsage": "{{camera}} hat eine hohe CPU Auslastung bei der Erkennung ({{detectAvg}}%)",
|
"detectHighCpuUsage": "{{camera}} hat eine hohe CPU Auslastung bei der Erkennung ({{detectAvg}}%)",
|
||||||
"reindexingEmbeddings": "Neuindizierung von Einbettungen ({{processed}}% erledigt)"
|
"reindexingEmbeddings": "Neuindizierung von Einbettungen ({{processed}}% erledigt)",
|
||||||
|
"detectIsSlow": "{{detect}} ist langsam ({{speed}} ms)",
|
||||||
|
"detectIsVerySlow": "{{detect}} ist sehr langsam ({{speed}} ms)",
|
||||||
|
"cameraIsOffline": "{{camera}} ist offline"
|
||||||
},
|
},
|
||||||
"lastRefreshed": "Zuletzt aktualisiert: "
|
"lastRefreshed": "Zuletzt aktualisiert: "
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"saving": "Saving...",
|
"saving": "Saving…",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
@ -153,6 +153,7 @@
|
|||||||
"fi": "Suomi (Finnish)",
|
"fi": "Suomi (Finnish)",
|
||||||
"da": "Dansk (Danish)",
|
"da": "Dansk (Danish)",
|
||||||
"sk": "Slovenčina (Slovak)",
|
"sk": "Slovenčina (Slovak)",
|
||||||
|
"yue": "粵語 (Cantonese)",
|
||||||
"withSystem": {
|
"withSystem": {
|
||||||
"label": "Use the system settings for language"
|
"label": "Use the system settings for language"
|
||||||
}
|
}
|
||||||
@ -173,7 +174,7 @@
|
|||||||
"green": "Green",
|
"green": "Green",
|
||||||
"nord": "Nord",
|
"nord": "Nord",
|
||||||
"red": "Red",
|
"red": "Red",
|
||||||
"contrast": "High Contrast",
|
"highcontrast": "High Contrast",
|
||||||
"default": "Default"
|
"default": "Default"
|
||||||
},
|
},
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
},
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"label": "Name",
|
"label": "Name",
|
||||||
"placeholder": "Enter a name...",
|
"placeholder": "Enter a name…",
|
||||||
"errorMessage": {
|
"errorMessage": {
|
||||||
"mustLeastCharacters": "Camera group name must be at least 2 characters.",
|
"mustLeastCharacters": "Camera group name must be at least 2 characters.",
|
||||||
"exists": "Camera group name already exists.",
|
"exists": "Camera group name already exists.",
|
||||||
|
|||||||
@ -70,7 +70,7 @@
|
|||||||
"disabled": "Restreaming is not enabled for this camera.",
|
"disabled": "Restreaming is not enabled for this camera.",
|
||||||
"desc": {
|
"desc": {
|
||||||
"title": "Set up go2rtc for additional live view options and audio for this camera.",
|
"title": "Set up go2rtc for additional live view options and audio for this camera.",
|
||||||
"readTheDocumentation": "Read the documentation "
|
"readTheDocumentation": "Read the documentation"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"showStats": {
|
"showStats": {
|
||||||
|
|||||||
@ -117,8 +117,8 @@
|
|||||||
"recognizedLicensePlates": {
|
"recognizedLicensePlates": {
|
||||||
"title": "Recognized License Plates",
|
"title": "Recognized License Plates",
|
||||||
"loadFailed": "Failed to load recognized license plates.",
|
"loadFailed": "Failed to load recognized license plates.",
|
||||||
"loading": "Loading recognized license plates...",
|
"loading": "Loading recognized license plates…",
|
||||||
"placeholder": "Type to search license plates...",
|
"placeholder": "Type to search license plates…",
|
||||||
"noLicensePlatesFound": "No license plates found.",
|
"noLicensePlatesFound": "No license plates found.",
|
||||||
"selectPlatesFromList": "Select one or more plates from the list."
|
"selectPlatesFromList": "Select one or more plates from the list."
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user