Compare commits

...

5 Commits

Author SHA1 Message Date
Jonathan Gilbert
c2beda98ad
Merge 4359aca5bf into 704ee9667c 2026-05-05 18:38:17 -07:00
Vinnie Esposito
704ee9667c
fix(face_recognition): feed BGR (not RGB) to FaceDetectorYN in manual detection branch (#23123)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* fix(face_recognition): feed BGR (not RGB) to FaceDetectorYN in manual detection branch

Frigate's `requires_face_detection` branch in `FaceRealTimeProcessor.process_frame`
converts the YUV camera frame to RGB and passes it to `cv2.FaceDetectorYN`.
YuNet is trained on BGR — feeding it RGB silently degrades detection
confidence by ~10× on typical person crops, causing face_recognition to
emit no `sub_label` and produce no `train/` entries. There is no log signal
because the detector simply returns 0 faces; from outside the box it looks
like nobody is walking past any camera.

The same file already does the YUV→BGR conversion correctly in the
else-branch (was line 271, now line 285) — only the manual-detection
branch was missed.

## Reproduction

Verified in-pod against the running Frigate's models on identical
person crops (snapshot pulled from a real person event):

    BGR (correct):  cv2.FaceDetectorYN ←  confidence 0.744 ✓
    RGB (current):  cv2.FaceDetectorYN ←  confidence 0.047 ✗

The `score_threshold=0.5` set on `FaceDetectorYN.create()` filters anything
under 0.5 at the detector layer, so the RGB-degraded crops never reach
the user-configurable `detection_threshold`. Result: silent outage.

## Fix

Three changes in `frigate/data_processing/real_time/face.py`:

1. `cv2.COLOR_YUV2RGB_I420` → `cv2.COLOR_YUV2BGR_I420`
2. Variable rename `rgb` → `bgr` to match
3. Remove the now-redundant `cv2.cvtColor(face_frame, cv2.COLOR_RGB2BGR)`
   block — `face_frame` is already BGR after the upstream conversion change

Net diff: +6 / -7. Pure Python, no new dependencies.

## How a deployment confirms the fix

After this change, walking past a camera produces:
- `data.attributes` with a `face` entry on the person event (currently empty)
- New entries in `/api/faces` `train/` array (currently frozen)
- `sub_label` populated on subsequent person events for trained faces

Signed-off-by: Vinnie Esposito <vespo21@gmail.com>

* Cleanup comment

---------

Signed-off-by: Vinnie Esposito <vespo21@gmail.com>
Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2026-05-05 19:10:24 -05:00
Nicolas Mowen
76a1230885
ROCm Optimizations (#23118)
* Update to ROCm 7.2.3

* Add inference time for 9060XT

* Update times

* Update hardware info for latest ROCm

* Add env vars to save kernels and miopen database

* re-enable face recognition for ROCm

* Update

* Save LLVM cache
2026-05-05 16:33:43 -05:00
Josh Hawkins
52a3301726
Miscellaneous fixes (#23111)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* return 404 from /api/login if auth is disabled

* locale sort object label switches

* enable search on object switches field

* add profiles docs link
2026-05-05 09:03:49 -06:00
Jonathan Gilbert
4359aca5bf feat: native support for rootless container execution 2026-05-02 09:14:18 +10:00
18 changed files with 88 additions and 29 deletions

View File

@ -287,6 +287,9 @@ RUN --mount=type=bind,source=docker/main/install_memryx.sh,target=/deps/install_
COPY --from=deps-rootfs / / COPY --from=deps-rootfs / /
RUN mkdir -p /etc/letsencrypt/www /etc/letsencrypt/live/frigate /usr/local/nginx/logs /config /run /tmp/cache /media/frigate \
&& chmod -R a+rwX /etc/letsencrypt /usr/local/nginx /config /run /tmp/cache /media/frigate
RUN ldconfig RUN ldconfig
EXPOSE 5000 EXPOSE 5000
@ -297,6 +300,8 @@ EXPOSE 8555/tcp 8555/udp
ENV S6_LOGGING_SCRIPT="T 1 n0 s10000000 T" ENV S6_LOGGING_SCRIPT="T 1 n0 s10000000 T"
# Do not fail on long-running download scripts # Do not fail on long-running download scripts
ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
# Set HOME to cache directory so rootless users can cache downloaded models
ENV HOME=/tmp/cache
ENTRYPOINT ["/init"] ENTRYPOINT ["/init"]
CMD [] CMD []

View File

@ -1,4 +1,4 @@
#!/command/with-contenv bash #!/command/with-contenv bash
# shellcheck shell=bash # shellcheck shell=bash
exec logutil-service /dev/shm/logs/certsync exec /usr/local/bin/logutil /dev/shm/logs/certsync/

View File

@ -1,4 +1,4 @@
#!/command/with-contenv bash #!/command/with-contenv bash
# shellcheck shell=bash # shellcheck shell=bash
exec logutil-service /dev/shm/logs/frigate exec /usr/local/bin/logutil /dev/shm/logs/frigate/

View File

@ -1,4 +1,4 @@
#!/command/with-contenv bash #!/command/with-contenv bash
# shellcheck shell=bash # shellcheck shell=bash
exec logutil-service /dev/shm/logs/go2rtc exec /usr/local/bin/logutil /dev/shm/logs/go2rtc/

View File

@ -7,5 +7,7 @@ set -o errexit -o nounset -o pipefail
dirs=(/dev/shm/logs/frigate /dev/shm/logs/go2rtc /dev/shm/logs/nginx /dev/shm/logs/certsync) dirs=(/dev/shm/logs/frigate /dev/shm/logs/go2rtc /dev/shm/logs/nginx /dev/shm/logs/certsync)
mkdir -p "${dirs[@]}" mkdir -p "${dirs[@]}"
chown nobody:nogroup "${dirs[@]}" if [ "$(id -u)" = "0" ]; then
chown nobody:nogroup "${dirs[@]}"
fi
chmod 02755 "${dirs[@]}" chmod 02755 "${dirs[@]}"

View File

@ -1,4 +1,4 @@
#!/command/with-contenv bash #!/command/with-contenv bash
# shellcheck shell=bash # shellcheck shell=bash
exec logutil-service /dev/shm/logs/nginx exec /usr/local/bin/logutil /dev/shm/logs/nginx/

View File

@ -65,6 +65,11 @@ function set_worker_processes() {
set_worker_processes set_worker_processes
# NGINX cannot switch users if running rootless; strip the directive
if [ "$(id -u)" != "0" ]; then
sed -i '/^user root;/d' /usr/local/nginx/conf/nginx.conf || true
fi
# ensure the directory for ACME challenges exists # ensure the directory for ACME challenges exists
mkdir -p /etc/letsencrypt/www mkdir -p /etc/letsencrypt/www

View File

@ -0,0 +1,17 @@
#!/command/with-contenv bash
# shellcheck shell=bash
LOG_DIR=$1
if [ -z "$LOG_DIR" ]; then
echo "Usage: $0 <log-dir>"
exit 1
fi
CMD=(s6-log -b -- ${S6_LOGGING_SCRIPT:-n20 s1000000 T} "$LOG_DIR")
if [ "$(id -u)" = "0" ]; then
exec /command/s6-envuidgid nobody /command/s6-applyuidgid -U -- "${CMD[@]}"
else
exec "${CMD[@]}"
fi

View File

@ -13,7 +13,7 @@ ARG ROCM
RUN apt update -qq && \ RUN apt update -qq && \
apt install -y wget gpg && \ apt install -y wget gpg && \
wget -O rocm.deb https://repo.radeon.com/amdgpu-install/7.2/ubuntu/jammy/amdgpu-install_7.2.70200-1_all.deb && \ wget -O rocm.deb https://repo.radeon.com/amdgpu-install/7.2.3/ubuntu/jammy/amdgpu-install_7.2.3.70203-1_all.deb && \
apt install -y ./rocm.deb && \ apt install -y ./rocm.deb && \
apt update && \ apt update && \
apt install -qq -y rocm apt install -qq -y rocm
@ -78,6 +78,10 @@ ENV MIGRAPHX_DISABLE_MIOPEN_FUSION=1
ENV MIGRAPHX_DISABLE_SCHEDULE_PASS=1 ENV MIGRAPHX_DISABLE_SCHEDULE_PASS=1
ENV MIGRAPHX_DISABLE_REDUCE_FUSION=1 ENV MIGRAPHX_DISABLE_REDUCE_FUSION=1
ENV MIGRAPHX_ENABLE_HIPRTC_WORKAROUNDS=1 ENV MIGRAPHX_ENABLE_HIPRTC_WORKAROUNDS=1
ENV MIOPEN_CUSTOM_CACHE_DIR=/config/model_cache/migraphx
ENV MIOPEN_USER_DB_PATH=/config/model_cache/migraphx
ENV AMD_COMGR_CACHE=1
ENV AMD_COMGR_CACHE_DIR=/config/model_cache/migraphx
COPY --from=rocm-dist / / COPY --from=rocm-dist / /

View File

@ -1 +1 @@
onnxruntime-migraphx @ https://github.com/NickM-27/frigate-onnxruntime-rocm/releases/download/v7.2.0/onnxruntime_migraphx-1.23.1-cp311-cp311-linux_x86_64.whl onnxruntime-migraphx @ https://github.com/NickM-27/frigate-onnxruntime-rocm/releases/download/v7.2.3-1/onnxruntime_migraphx-1.24.4-cp311-cp311-linux_x86_64.whl

View File

@ -1,5 +1,5 @@
variable "ROCM" { variable "ROCM" {
default = "7.2.0" default = "7.2.3"
} }
variable "HSA_OVERRIDE_GFX_VERSION" { variable "HSA_OVERRIDE_GFX_VERSION" {
default = "" default = ""

View File

@ -1023,9 +1023,9 @@ detectors:
### ONNX Supported Models ### ONNX Supported Models
| Model | Nvidia GPU | AMD GPU | Notes | | Model | Nvidia GPU | AMD GPU | Notes |
| ----------------------------- | ---------- | ------- | --------------------------------------------------- | | ------------------------------------ | ---------- | ------- | --------------------------------------------------- |
| [YOLOv9](#yolo-v3-v4-v7-v9-2) | ✅ | ✅ | Supports CUDA Graphs for optimal Nvidia performance | | [YOLOv9](#yolo-v3-v4-v7-v9-2) | ✅ | ✅ | Supports CUDA Graphs for optimal Nvidia performance |
| [RF-DETR](#rf-detr) | ✅ | ❌ | Supports CUDA Graphs for optimal Nvidia performance | | [RF-DETR](#rf-detr) | ✅ | ⚠️ | Supports CUDA Graphs for optimal Nvidia performance |
| [YOLO-NAS](#yolo-nas-1) | ⚠️ | ⚠️ | Not supported by CUDA Graphs | | [YOLO-NAS](#yolo-nas-1) | ⚠️ | ⚠️ | Not supported by CUDA Graphs |
| [YOLOX](#yolox-1) | ✅ | ✅ | Supports CUDA Graphs for optimal Nvidia performance | | [YOLOX](#yolox-1) | ✅ | ✅ | Supports CUDA Graphs for optimal Nvidia performance |
| [D-FINE / DEIMv2](#d-fine--deimv2-1) | ⚠️ | ❌ | Not supported by CUDA Graphs | | [D-FINE / DEIMv2](#d-fine--deimv2-1) | ⚠️ | ❌ | Not supported by CUDA Graphs |

View File

@ -223,10 +223,11 @@ Apple Silicon can not run within a container, so a ZMQ proxy is utilized to comm
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 | RF-DETR Inference Time |
| --------- | --------------------------- | ------------------------- | | -------------- | --------------------------- | ------------------------- | ---------------------- |
| AMD 780M | t-320: ~ 14 ms s-320: 20 ms | 320: ~ 25 ms 640: ~ 50 ms | | AMD 780M | t-320: ~ 14 ms s-320: 20 ms | 320: ~ 25 ms 640: ~ 50 ms | |
| AMD 8700G | | 320: ~ 20 ms 640: ~ 40 ms | | AMD 8700G | | 320: ~ 20 ms 640: ~ 40 ms | |
| AMD 9060XT 16G | t-320: ~ 4 ms s-320: 5 ms | 320: ~ 6 ms | Nano-320: ~ 90 ms |
## Community Supported Detectors ## Community Supported Detectors

View File

@ -812,6 +812,11 @@ limiter = Limiter(key_func=get_remote_addr)
) )
@limiter.limit(limit_value=rateLimiter.get_limit) @limiter.limit(limit_value=rateLimiter.get_limit)
def login(request: Request, body: AppPostLoginBody): def login(request: Request, body: AppPostLoginBody):
if not request.app.frigate_config.auth.enabled:
return JSONResponse(
content={"message": "Authentication is disabled"}, status_code=404
)
JWT_COOKIE_NAME = request.app.frigate_config.auth.cookie_name JWT_COOKIE_NAME = request.app.frigate_config.auth.cookie_name
JWT_COOKIE_SECURE = request.app.frigate_config.auth.cookie_secure JWT_COOKIE_SECURE = request.app.frigate_config.auth.cookie_secure
JWT_SESSION_LENGTH = request.app.frigate_config.auth.session_length JWT_SESSION_LENGTH = request.app.frigate_config.auth.session_length

View File

@ -229,9 +229,10 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
logger.debug(f"No person box available for {id}") logger.debug(f"No person box available for {id}")
return return
rgb = cv2.cvtColor(frame, cv2.COLOR_YUV2RGB_I420) # YuNet (cv2.FaceDetectorYN) is trained on BGR
bgr = cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_I420)
left, top, right, bottom = person_box left, top, right, bottom = person_box
person = rgb[top:bottom, left:right] person = bgr[top:bottom, left:right]
face_box = self.__detect_face(person, self.face_config.detection_threshold) face_box = self.__detect_face(person, self.face_config.detection_threshold)
if not face_box: if not face_box:
@ -250,11 +251,6 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
) )
return return
try:
face_frame = cv2.cvtColor(face_frame, cv2.COLOR_RGB2BGR)
except Exception as e:
logger.debug(f"Failed to convert face frame color for {id}: {e}")
return
else: else:
# don't run for object without attributes # don't run for object without attributes
if not obj_data.get("current_attributes"): if not obj_data.get("current_attributes"):

View File

@ -132,7 +132,6 @@ class ONNXModelRunner(BaseModelRunner):
return model_type in [ return model_type in [
EnrichmentModelTypeEnum.paddleocr.value, EnrichmentModelTypeEnum.paddleocr.value,
EnrichmentModelTypeEnum.jina_v2.value, EnrichmentModelTypeEnum.jina_v2.value,
EnrichmentModelTypeEnum.arcface.value,
ModelTypeEnum.rfdetr.value, ModelTypeEnum.rfdetr.value,
ModelTypeEnum.dfine.value, ModelTypeEnum.dfine.value,
] ]

View File

@ -2,7 +2,7 @@
import { WidgetProps } from "@rjsf/utils"; import { WidgetProps } from "@rjsf/utils";
import { SwitchesWidget } from "./SwitchesWidget"; import { SwitchesWidget } from "./SwitchesWidget";
import { FormContext } from "./SwitchesWidget"; import { FormContext } from "./SwitchesWidget";
import { getTranslatedLabel } from "@/utils/i18n"; import i18n, { getTranslatedLabel } from "@/utils/i18n";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { JsonObject } from "@/types/configForm"; import { JsonObject } from "@/types/configForm";
@ -76,7 +76,12 @@ function getObjectLabels(context: FormContext): string[] {
...sourceLabels, ...sourceLabels,
...formDataLabels, ...formDataLabels,
]); ]);
return [...combinedLabels].sort(); return [...combinedLabels].sort((a, b) =>
getObjectLabelDisplayName(a).localeCompare(
getObjectLabelDisplayName(b),
i18n.language,
),
);
} }
function getObjectLabelDisplayName(label: string): string { function getObjectLabelDisplayName(label: string): string {
@ -94,6 +99,7 @@ export function ObjectLabelSwitchesWidget(props: WidgetProps) {
i18nKey: "objectLabels", i18nKey: "objectLabels",
listClassName: listClassName:
"relative max-h-none overflow-visible md:max-h-64 md:overflow-y-auto md:overscroll-contain md:scrollbar-container", "relative max-h-none overflow-visible md:max-h-64 md:overflow-y-auto md:overscroll-contain md:scrollbar-container",
enableSearch: true,
}} }}
/> />
); );

View File

@ -7,13 +7,20 @@ import useSWR from "swr";
import axios from "axios"; import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import { Pencil, Trash2 } from "lucide-react"; import { Pencil, Trash2 } from "lucide-react";
import { LuChevronDown, LuChevronRight, LuPlus } from "react-icons/lu"; import {
LuChevronDown,
LuChevronRight,
LuExternalLink,
LuPlus,
} from "react-icons/lu";
import { Link } from "react-router-dom";
import type { FrigateConfig } from "@/types/frigateConfig"; import type { FrigateConfig } from "@/types/frigateConfig";
import type { JsonObject } from "@/types/configForm"; import type { JsonObject } from "@/types/configForm";
import type { ProfileState, ProfilesApiResponse } from "@/types/profile"; import type { ProfileState, ProfilesApiResponse } from "@/types/profile";
import { getProfileColor } from "@/utils/profileColors"; import { getProfileColor } from "@/utils/profileColors";
import { PROFILE_ELIGIBLE_SECTIONS } from "@/utils/configUtil"; import { PROFILE_ELIGIBLE_SECTIONS } from "@/utils/configUtil";
import { resolveCameraName } from "@/hooks/use-camera-friendly-name"; import { resolveCameraName } from "@/hooks/use-camera-friendly-name";
import { useDocDomain } from "@/hooks/use-doc-domain";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -66,6 +73,7 @@ export default function ProfilesView({
setProfilesUIEnabled, setProfilesUIEnabled,
}: ProfilesViewProps) { }: ProfilesViewProps) {
const { t } = useTranslation(["views/settings", "common"]); const { t } = useTranslation(["views/settings", "common"]);
const { getLocaleDocUrl } = useDocDomain();
const { data: config, mutate: updateConfig } = const { data: config, mutate: updateConfig } =
useSWR<FrigateConfig>("config"); useSWR<FrigateConfig>("config");
const { data: profilesData, mutate: updateProfiles } = const { data: profilesData, mutate: updateProfiles } =
@ -360,6 +368,17 @@ export default function ProfilesView({
<div className="my-1 text-sm text-muted-foreground"> <div className="my-1 text-sm text-muted-foreground">
{t("profiles.disabledDescription", { ns: "views/settings" })} {t("profiles.disabledDescription", { ns: "views/settings" })}
</div> </div>
<div className="flex items-center text-sm text-primary-variant">
<Link
to={getLocaleDocUrl("configuration/profiles")}
target="_blank"
rel="noopener noreferrer"
className="inline"
>
{t("readTheDocumentation", { ns: "common" })}
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
{/* Enable Profiles Toggle — shown only when no profiles exist */} {/* Enable Profiles Toggle — shown only when no profiles exist */}
{!hasProfiles && setProfilesUIEnabled && ( {!hasProfiles && setProfilesUIEnabled && (