From 30ac868757f6ee9344522fdd30fe186caf47414e Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Thu, 10 Apr 2025 15:33:51 -0600 Subject: [PATCH] Quick fixes (#17639) * Use mobile drawer for face selection * Convert face selection to separate component * Cleanup dialogs * Add FAQ for record resolution * Update image name * Remove unused * Cleanup --- docs/docs/configuration/face_recognition.md | 7 ++ .../configuration/hardware_acceleration.md | 7 +- docs/docs/configuration/object_detectors.md | 2 +- .../overlay/FaceSelectionDialog.tsx | 117 ++++++++++++++++++ .../overlay/detail/SearchDetailDialog.tsx | 38 ++---- .../overlay/dialog/TextEntryDialog.tsx | 4 +- web/src/pages/FaceLibrary.tsx | 51 ++------ 7 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 web/src/components/overlay/FaceSelectionDialog.tsx diff --git a/docs/docs/configuration/face_recognition.md b/docs/docs/configuration/face_recognition.md index 328fc5fc3..e652bb4a0 100644 --- a/docs/docs/configuration/face_recognition.md +++ b/docs/docs/configuration/face_recognition.md @@ -129,3 +129,10 @@ The Frigate considers the recognition scores across all recognition attempts for ### Can I use other face recognition software like DoubleTake at the same time as the built in face recognition? No, using another face recognition service will interfere with Frigate's built in face recognition. When using double-take the sub_label feature must be disabled if the built in face recognition is also desired. + +### Does face recognition run on the recording stream? + +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. +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. diff --git a/docs/docs/configuration/hardware_acceleration.md b/docs/docs/configuration/hardware_acceleration.md index e05a76e62..c6e97c09f 100644 --- a/docs/docs/configuration/hardware_acceleration.md +++ b/docs/docs/configuration/hardware_acceleration.md @@ -295,8 +295,7 @@ These instructions were originally based on the [Jellyfin documentation](https:/ ## NVIDIA Jetson (Orin AGX, Orin NX, Orin Nano\*, Xavier AGX, Xavier NX, TX2, TX1, Nano) A separate set of docker images is available that is based on Jetpack/L4T. They come with an `ffmpeg` build -with codecs that use the Jetson's dedicated media engine. If your Jetson host is running Jetpack 5.0+ use the `stable-tensorrt-jp5` -tagged image, or if your Jetson host is running Jetpack 6.0+ use the `stable-tensorrt-jp6` tagged image. Note that the Orin Nano has no video encoder, so frigate will use software encoding on this platform, but the image will still allow hardware decoding and tensorrt object detection. +with codecs that use the Jetson's dedicated media engine. If your Jetson host is running Jetpack 6.0+ use the `stable-tensorrt-jp6` tagged image. Note that the Orin Nano has no video encoder, so frigate will use software encoding on this platform, but the image will still allow hardware decoding and tensorrt object detection. You will need to use the image with the nvidia container runtime: @@ -306,7 +305,7 @@ You will need to use the image with the nvidia container runtime: docker run -d \ ... --runtime nvidia - ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp5 + ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp6 ``` ### Docker Compose - Jetson @@ -315,7 +314,7 @@ docker run -d \ services: frigate: ... - image: ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp5 + image: ghcr.io/blakeblackshear/frigate:stable-tensorrt-jp6 runtime: nvidia # Add this ``` diff --git a/docs/docs/configuration/object_detectors.md b/docs/docs/configuration/object_detectors.md index aadd0d053..2f3ebc397 100644 --- a/docs/docs/configuration/object_detectors.md +++ b/docs/docs/configuration/object_detectors.md @@ -27,7 +27,7 @@ Frigate supports multiple different detectors that work on different types of ha **Nvidia** - [TensortRT](#nvidia-tensorrt-detector): TensorRT can run on Nvidia GPUs and Jetson devices, using one of many default models. -- [ONNX](#onnx): TensorRT will automatically be detected and used as a detector in the `-tensorrt` or `-tensorrt-jp(4/5)` Frigate images when a supported ONNX model is configured. +- [ONNX](#onnx): TensorRT will automatically be detected and used as a detector in the `-tensorrt` or `-tensorrt-jp6` Frigate images when a supported ONNX model is configured. **Rockchip** diff --git a/web/src/components/overlay/FaceSelectionDialog.tsx b/web/src/components/overlay/FaceSelectionDialog.tsx new file mode 100644 index 000000000..c524469b0 --- /dev/null +++ b/web/src/components/overlay/FaceSelectionDialog.tsx @@ -0,0 +1,117 @@ +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/ui/drawer"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { isDesktop, isMobile } from "react-device-detect"; +import { LuPlus, LuScanFace } from "react-icons/lu"; +import { useTranslation } from "react-i18next"; +import { cn } from "@/lib/utils"; +import React, { ReactNode, useMemo, useState } from "react"; +import TextEntryDialog from "./dialog/TextEntryDialog"; +import { Button } from "../ui/button"; + +type FaceSelectionDialogProps = { + className?: string; + faceNames: string[]; + onTrainAttempt: (name: string) => void; + children: ReactNode; +}; +export default function FaceSelectionDialog({ + className, + faceNames, + onTrainAttempt, + children, +}: FaceSelectionDialogProps) { + const { t } = useTranslation(["views/faceLibrary"]); + + const isChildButton = useMemo( + () => React.isValidElement(children) && children.type === Button, + [children], + ); + + // control + const [newFace, setNewFace] = useState(false); + + // components + const Selector = isDesktop ? DropdownMenu : Drawer; + const SelectorTrigger = isDesktop ? DropdownMenuTrigger : DrawerTrigger; + const SelectorContent = isDesktop ? DropdownMenuContent : DrawerContent; + const SelectorItem = isDesktop ? DropdownMenuItem : DrawerClose; + + return ( +
+ {newFace && ( + onTrainAttempt(newName)} + /> + )} + + + + + {children} + + + {isMobile && ( + + Log Details + Log details + + )} + {t("trainFaceAs")} +
+ setNewFace(true)} + > + + {t("createFaceLibrary.new")} + + {faceNames.map((faceName) => ( + onTrainAttempt(faceName)} + > + + {faceName} + + ))} +
+
+
+ {t("trainFace")} +
+
+ ); +} diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 6762890d4..afd52b629 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -57,7 +57,6 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuLabel, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch"; @@ -77,6 +76,7 @@ import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog"; import { useTranslation } from "react-i18next"; import { TbFaceId } from "react-icons/tb"; import { useIsAdmin } from "@/hooks/use-is-admin"; +import FaceSelectionDialog from "../FaceSelectionDialog"; const SEARCH_TABS = [ "details", @@ -844,30 +844,18 @@ function ObjectDetailsTab({ )} {hasFace && ( - - - - - - - {t("trainFaceAs", { ns: "views/faceLibrary" })} - - {faceNames.map((faceName) => ( - onTrainFace(faceName)} - > - {faceName} - - ))} - - + + + )} diff --git a/web/src/components/overlay/dialog/TextEntryDialog.tsx b/web/src/components/overlay/dialog/TextEntryDialog.tsx index 6fc1f9ad3..4cb500d15 100644 --- a/web/src/components/overlay/dialog/TextEntryDialog.tsx +++ b/web/src/components/overlay/dialog/TextEntryDialog.tsx @@ -8,6 +8,8 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { cn } from "@/lib/utils"; +import { isMobile } from "react-device-detect"; import { useTranslation } from "react-i18next"; type TextEntryDialogProps = { @@ -43,7 +45,7 @@ export default function TextEntryDialog({ allowEmpty={allowEmpty} onSave={onSave} > - + diff --git a/web/src/pages/FaceLibrary.tsx b/web/src/pages/FaceLibrary.tsx index 30606808e..19c77a96f 100644 --- a/web/src/pages/FaceLibrary.tsx +++ b/web/src/pages/FaceLibrary.tsx @@ -3,8 +3,8 @@ import TimeAgo from "@/components/dynamic/TimeAgo"; import AddFaceIcon from "@/components/icons/AddFaceIcon"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import CreateFaceWizardDialog from "@/components/overlay/detail/FaceCreateWizardDialog"; -import TextEntryDialog from "@/components/overlay/dialog/TextEntryDialog"; import UploadImageDialog from "@/components/overlay/dialog/UploadImageDialog"; +import FaceSelectionDialog from "@/components/overlay/FaceSelectionDialog"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -17,7 +17,6 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuLabel, DropdownMenuTrigger, DropdownMenuSeparator, } from "@/components/ui/dropdown-menu"; @@ -42,7 +41,6 @@ import { isDesktop, isMobile } from "react-device-detect"; import { useTranslation } from "react-i18next"; import { LuImagePlus, - LuPlus, LuRefreshCw, LuScanFace, LuSearch, @@ -783,8 +781,6 @@ function FaceAttempt({ // interaction - const [newFace, setNewFace] = useState(false); - const imgRef = useRef(null); useContextMenu(imgRef, () => { @@ -844,15 +840,6 @@ function FaceAttempt({ return ( <> - {newFace && ( - onTrainAttempt(newName)} - /> - )} -
- - - - - - - - - {t("trainFaceAs")} - setNewFace(true)} - > - - {t("createFaceLibrary.new")} - - {faceNames.map((faceName) => ( - onTrainAttempt(faceName)} - > - - {faceName} - - ))} - - - {t("trainFace")} - + + +