From 039ab1ccd759314cc8c11e68478348bc069ec2a5 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sat, 5 Oct 2024 14:51:05 -0500 Subject: [PATCH 01/23] add docs for yolonas plus models (#14161) * add docs for yolonas plus models * typo --- docs/docs/plus/first_model.md | 4 ++-- docs/docs/plus/improving_model.md | 31 +++++++++++++++---------------- docs/docs/plus/index.md | 25 ++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/docs/docs/plus/first_model.md b/docs/docs/plus/first_model.md index bbaf9cacbe..6978bb491d 100644 --- a/docs/docs/plus/first_model.md +++ b/docs/docs/plus/first_model.md @@ -5,7 +5,7 @@ title: Requesting your first model ## Step 1: Upload and annotate your images -Before requesting your first model, you will need to upload at least 10 images to Frigate+. But for the best results, you should provide at least 100 verified images per camera. Keep in mind that varying conditions should be included. You will want images from cloudy days, sunny days, dawn, dusk, and night. Refer to the [integration docs](../integrations/plus.md#generate-an-api-key) for instructions on how to easily submit images to Frigate+ directly from Frigate. +Before requesting your first model, you will need to upload and verify at least 1 image to Frigate+. The more images you upload, annotate, and verify the better your results will be. Most users start to see very good results once they have at least 100 verified images per camera. Keep in mind that varying conditions should be included. You will want images from cloudy days, sunny days, dawn, dusk, and night. Refer to the [integration docs](../integrations/plus.md#generate-an-api-key) for instructions on how to easily submit images to Frigate+ directly from Frigate. It is recommended to submit **both** true positives and false positives. This will help the model differentiate between what is and isn't correct. You should aim for a target of 80% true positive submissions and 20% false positives across all of your images. If you are experiencing false positives in a specific area, submitting true positives for any object type near that area in similar lighting conditions will help teach the model what that area looks like when no objects are present. @@ -13,7 +13,7 @@ For more detailed recommendations, you can refer to the docs on [improving your ## Step 2: Submit a model request -Once you have an initial set of verified images, you can request a model on the Models page. Each model request requires 1 of the 12 trainings that you receive with your annual subscription. This model will support all [label types available](./index.md#available-label-types) even if you do not submit any examples for those labels. Model creation can take up to 36 hours. +Once you have an initial set of verified images, you can request a model on the Models page. For guidance on choosing a model type, refer to [this part of the documentation](./index.md#available-model-types). Each model request requires 1 of the 12 trainings that you receive with your annual subscription. This model will support all [label types available](./index.md#available-label-types) even if you do not submit any examples for those labels. Model creation can take up to 36 hours. ![Plus Models Page](/img/plus/plus-models.jpg) ## Step 3: Set your model id in the config diff --git a/docs/docs/plus/improving_model.md b/docs/docs/plus/improving_model.md index 0e97b21a54..37a765994b 100644 --- a/docs/docs/plus/improving_model.md +++ b/docs/docs/plus/improving_model.md @@ -3,7 +3,7 @@ id: improving_model title: Improving your model --- -You may find that Frigate+ models result in more false positives initially, but by submitting true and false positives, the model will improve. Because a limited number of users submitted images to Frigate+ prior to this launch, you may need to submit several hundred images per camera to see good results. With all the new images now being submitted, future base models will improve as more and more users (including you) submit examples to Frigate+. Note that only verified images will be used when training your model. Submitting an image from Frigate as a true or false positive will not verify the image. You still must verify the image in Frigate+ in order for it to be used in training. +You may find that Frigate+ models result in more false positives initially, but by submitting true and false positives, the model will improve. With all the new images now being submitted by subscribers, future base models will improve as more and more examples are incorporated. Note that only images with at least one verified label will be used when training your model. Submitting an image from Frigate as a true or false positive will not verify the image. You still must verify the image in Frigate+ in order for it to be used in training. - **Submit both true positives and false positives**. This will help the model differentiate between what is and isn't correct. You should aim for a target of 80% true positive submissions and 20% false positives across all of your images. If you are experiencing false positives in a specific area, submitting true positives for any object type near that area in similar lighting conditions will help teach the model what that area looks like when no objects are present. - **Lower your thresholds a little in order to generate more false/true positives near the threshold value**. For example, if you have some false positives that are scoring at 68% and some true positives scoring at 72%, you can try lowering your threshold to 65% and submitting both true and false positives within that range. This will help the model learn and widen the gap between true and false positive scores. @@ -36,18 +36,17 @@ Misidentified objects should have a correct label added. For example, if a perso ## Shortcuts for a faster workflow -|Shortcut Key|Description| -|-----|--------| -|`?`|Show all keyboard shortcuts| -|`w`|Add box| -|`d`|Toggle difficult| -|`s`|Switch to the next label| -|`tab`|Select next largest box| -|`del`|Delete current box| -|`esc`|Deselect/Cancel| -|`← ↑ → ↓`|Move box| -|`Shift + ← ↑ → ↓`|Resize box| -|`-`|Zoom out| -|`=`|Zoom in| -|`f`|Hide/show all but current box| -|`spacebar`|Verify and save| +| Shortcut Key | Description | +| ----------------- | ----------------------------- | +| `?` | Show all keyboard shortcuts | +| `w` | Add box | +| `d` | Toggle difficult | +| `s` | Switch to the next label | +| `tab` | Select next largest box | +| `del` | Delete current box | +| `esc` | Deselect/Cancel | +| `← ↑ → ↓` | Move box | +| `Shift + ← ↑ → ↓` | Resize box | +| `scrollwheel` | Zoom in/out | +| `f` | Hide/show all but current box | +| `spacebar` | Verify and save | diff --git a/docs/docs/plus/index.md b/docs/docs/plus/index.md index 35189ed3fc..b05f4f306f 100644 --- a/docs/docs/plus/index.md +++ b/docs/docs/plus/index.md @@ -15,17 +15,36 @@ With a subscription, 12 model trainings per year are included. If you cancel you Information on how to integrate Frigate+ with Frigate can be found in the [integration docs](../integrations/plus.md). +## Available model types + +There are two model types offered in Frigate+: `mobiledet` and `yolonas`. Both of these models are object detection models and are trained to detect the same set of labels [listed below](#available-label-types). + +Not all model types are supported by all detectors, so it's important to choose a model type to match your detector as shown in the table under [supported detector types](#supported-detector-types). + +| Model Type | Description | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `mobiledet` | Based on the same architecture as the default model included with Frigate. Runs on Google Coral devices and CPUs. | +| `yolonas` | A newer architecture that offers slightly higher accuracy and improved detection of small objects. Runs on Intel, NVidia GPUs, and AMD GPUs. | + ## Supported detector types +Currently, Frigate+ models support CPU (`cpu`), Google Coral (`edgetpu`), OpenVino (`openvino`), ONNX (`onnx`), and ROCm (`rocm`) detectors. + :::warning -Frigate+ models are not supported for TensorRT or OpenVino yet. +Using Frigate+ models with `onnx` and `rocm` is only available with Frigate 0.15, which is still under development. ::: -Currently, Frigate+ models only support CPU (`cpu`) and Coral (`edgetpu`) models. OpenVino is next in line to gain support. +| Hardware | Recommended Detector Type | Recommended Model Type | +| ---------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ---------------------- | +| [CPU](/configuration/object_detectors.md#cpu-detector-not-recommended) | `cpu` | `mobiledet` | +| [Coral (all form factors)](/configuration/object_detectors.md#edge-tpu-detector) | `edgetpu` | `mobiledet` | +| [Intel](/configuration/object_detectors.md#openvino-detector) | `openvino` | `yolonas` | +| [NVidia GPU](https://deploy-preview-13787--frigate-docs.netlify.app/configuration/object_detectors#onnx)\* | `onnx` | `yolonas` | +| [AMD ROCm GPU](https://deploy-preview-13787--frigate-docs.netlify.app/configuration/object_detectors#amdrocm-gpu-detector)\* | `rocm` | `yolonas` | -The models are created using the same MobileDet architecture as the default model. Additional architectures will be added in future releases as needed. +_\* Requires Frigate 0.15_ ## Available label types From 2a15b95f18dd7d21f3a3d2bf260be12c5f3cc6a9 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 7 Oct 2024 14:28:24 -0600 Subject: [PATCH 02/23] Docs updates (#14202) * Clarify live docs * Link out to common config examples in getting started guide * Add tip for go2rtc name configuration * direct link --- docs/docs/configuration/reference.md | 8 +++++--- docs/docs/guides/configuring_go2rtc.md | 10 +++++++++- docs/docs/guides/getting_started.md | 4 +++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/docs/configuration/reference.md b/docs/docs/configuration/reference.md index 76a9e6158b..e9b1d605f2 100644 --- a/docs/docs/configuration/reference.md +++ b/docs/docs/configuration/reference.md @@ -480,10 +480,12 @@ snapshots: # Uses https://github.com/AlexxIT/go2rtc (v1.9.2) go2rtc: -# Optional: jsmpeg stream configuration for WebUI +# Optional: Live stream configuration for WebUI. +# NOTE: Can be overridden at the camera level live: - # Optional: Set the name of the stream that should be used for live view - # in frigate WebUI. (default: name of camera) + # Optional: Set the name of the stream configured in go2rtc + # that should be used for live view in frigate WebUI. (default: name of camera) + # NOTE: In most cases this should be set at the camera level only. stream_name: camera_name # Optional: Set the height of the jsmpeg stream. (default: 720) # This must be less than or equal to the height of the detect stream. Lower resolutions diff --git a/docs/docs/guides/configuring_go2rtc.md b/docs/docs/guides/configuring_go2rtc.md index 8316376f2f..5c85f7d11a 100644 --- a/docs/docs/guides/configuring_go2rtc.md +++ b/docs/docs/guides/configuring_go2rtc.md @@ -13,7 +13,15 @@ Use of the bundled go2rtc is optional. You can still configure FFmpeg to connect # Setup a go2rtc stream -First, you will want to configure go2rtc to connect to your camera stream by adding the stream you want to use for live view in your Frigate config file. For the best experience, you should set the stream name under go2rtc to match the name of your camera so that Frigate will automatically map it and be able to use better live view options for the camera. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#module-streams), not just rtsp. +First, you will want to configure go2rtc to connect to your camera stream by adding the stream you want to use for live view in your Frigate config file. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#module-streams), not just rtsp. + +:::tip + +For the best experience, you should set the stream name under `go2rtc` to match the name of your camera so that Frigate will automatically map it and be able to use better live view options for the camera. + +See [the live view docs](../configuration/live.md#setting-stream-for-live-ui) for more information. + +::: ```yaml go2rtc: diff --git a/docs/docs/guides/getting_started.md b/docs/docs/guides/getting_started.md index e2a4420a38..79082970ba 100644 --- a/docs/docs/guides/getting_started.md +++ b/docs/docs/guides/getting_started.md @@ -306,7 +306,9 @@ By default, Frigate will retain video of all events for 10 days. The full set of ### Step 7: Complete config -At this point you have a complete config with basic functionality. You can see the [full config reference](../configuration/reference.md) for a complete list of configuration options. +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 [full config reference](../configuration/reference.md) for a complete list of configuration options. ### Follow up From f86957e5e19b321fd76579b39609410bc49fc782 Mon Sep 17 00:00:00 2001 From: JC Date: Wed, 9 Oct 2024 01:15:10 +0100 Subject: [PATCH 03/23] Improve docs on exports API endpoints (#14224) * Add (optional) export name to the create-export API endpoint docs * Add the exports list endpoint to the docs --- docs/docs/integrations/api.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/docs/integrations/api.md b/docs/docs/integrations/api.md index 0c48c0c82c..f0a8448880 100644 --- a/docs/docs/integrations/api.md +++ b/docs/docs/integrations/api.md @@ -411,17 +411,37 @@ HTTP Live Streaming Video on Demand URL for the specified event. Can be viewed i HTTP Live Streaming Video on Demand URL for the camera with the specified time range. Can be viewed in an application like VLC. +### `GET /api/exports` + +Fetch a list of all export recordings + +Sample response: +```json +[ + { + "camera": "doorbell", + "date": 12800057, + "id": "doorbell_pjis54", + "in_progress": false, + "name": "2024-10-04 fox visit", + "thumb_path": "/media/frigate/clips/export/doorbell_pjis54.webp", + "video_path": "/media/frigate/exports/doorbell_pjis54.mp4" + } +] +``` + ### `POST /api/export//start//end/` Export recordings from `start-timestamp` to `end-timestamp` for `camera` as a single mp4 file. These recordings will be exported to the `/media/frigate/exports` folder. -It is also possible to export this recording as a time-lapse. +It is also possible to export this recording as a time-lapse using the "playback" key in the json body, or specify a custom export filename, using the "name" key. **Optional Body:** ```json { - "playback": "realtime" // playback factor: realtime or timelapse_25x + "playback": "realtime", // playback factor: realtime or timelapse_25x + "name": "custom export name" // override the default export filename with a custom name } ``` From 51509760e31b1986291b0b6df4a04b749f434013 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 12 Oct 2024 06:13:00 -0600 Subject: [PATCH 04/23] Update object docs (#14295) --- docs/docs/configuration/objects.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/configuration/objects.md b/docs/docs/configuration/objects.md index 1a93f97048..796d312581 100644 --- a/docs/docs/configuration/objects.md +++ b/docs/docs/configuration/objects.md @@ -5,7 +5,7 @@ title: Available Objects import labels from "../../../labelmap.txt"; -Frigate includes the object models listed below from the Google Coral test data. +Frigate includes the object labels listed below from the Google Coral test data. Please note: From af844ea9d5c0ec54166df98c88b763affe81dc95 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 15 Oct 2024 09:39:31 -0600 Subject: [PATCH 05/23] Update coral troubleshooting docs (#14370) * Update coral docs for latest ubuntu * capitalization Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> --------- Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> --- docs/docs/troubleshooting/edgetpu.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/docs/troubleshooting/edgetpu.md b/docs/docs/troubleshooting/edgetpu.md index 8c917fa2f5..33e00f11a0 100644 --- a/docs/docs/troubleshooting/edgetpu.md +++ b/docs/docs/troubleshooting/edgetpu.md @@ -49,7 +49,10 @@ The USB Coral can become stuck and need to be restarted, this can happen for a n ## PCIe Coral Not Detected -The most common reason for the PCIe coral not being detected is that the driver has not been installed. See [the coral docs](https://coral.ai/docs/m2/get-started/#2-install-the-pcie-driver-and-edge-tpu-runtime) for how to install the driver for the PCIe based coral. +The most common reason for the PCIe Coral not being detected is that the driver has not been installed. This process varies based on what OS and kernel that is being run. + +- In most cases [the Coral docs](https://coral.ai/docs/m2/get-started/#2-install-the-pcie-driver-and-edge-tpu-runtime) show how to install the driver for the PCIe based Coral. +- For Ubuntu 22.04+ https://github.com/jnicolson/gasket-builder can be used to build and install the latest version of the driver. ## Only One PCIe Coral Is Detected With Coral Dual EdgeTPU From 9e1a50c3beadfdfed40838869ccb9fdf723c8d65 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:48:26 -0500 Subject: [PATCH 06/23] Clean up copy output (#14705) * Remove extra spacing for next/prev carousel buttons * Clarify ollama genai docs * Clean up copied gpu info output * Clean up copied gpu info output * Better display when manually copying/pasting log data --- web/src/components/overlay/GPUInfoDialog.tsx | 10 +-- web/src/pages/Logs.tsx | 86 +++++++++++++++++++- web/src/pages/System.tsx | 2 + 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/web/src/components/overlay/GPUInfoDialog.tsx b/web/src/components/overlay/GPUInfoDialog.tsx index 957d1e681c..3821579e2d 100644 --- a/web/src/components/overlay/GPUInfoDialog.tsx +++ b/web/src/components/overlay/GPUInfoDialog.tsx @@ -10,6 +10,7 @@ import ActivityIndicator from "../indicators/activity-indicator"; import { GpuInfo, Nvinfo, Vainfo } from "@/types/stats"; import { Button } from "../ui/button"; import copy from "copy-to-clipboard"; +import { toast } from "sonner"; type GPUInfoDialogProps = { showGpuInfo: boolean; @@ -30,12 +31,11 @@ export default function GPUInfoDialog({ const onCopyInfo = async () => { copy( - JSON.stringify(gpuType == "vainfo" ? vainfo : nvinfo).replace( - /[\\\s]+/gi, - "", - ), + JSON.stringify(gpuType == "vainfo" ? vainfo : nvinfo) + .replace(/\\t/g, "\t") + .replace(/\\n/g, "\n"), ); - setShowGpuInfo(false); + toast.success("Copied GPU info to clipboard."); }; if (gpuType == "vainfo") { diff --git a/web/src/pages/Logs.tsx b/web/src/pages/Logs.tsx index 8178e9e903..949fffb8ae 100644 --- a/web/src/pages/Logs.tsx +++ b/web/src/pages/Logs.tsx @@ -300,6 +300,84 @@ function Logs() { }, ); + useEffect(() => { + const handleCopy = (e: ClipboardEvent) => { + e.preventDefault(); + if (!contentRef.current) return; + + const selection = window.getSelection(); + if (!selection) return; + + const range = selection.getRangeAt(0); + const fragment = range.cloneContents(); + + const extractLogData = (element: Element) => { + const severity = + element.querySelector(".log-severity")?.textContent?.trim() || ""; + const dateStamp = + element.querySelector(".log-timestamp")?.textContent?.trim() || ""; + const section = + element.querySelector(".log-section")?.textContent?.trim() || ""; + const content = + element.querySelector(".log-content")?.textContent?.trim() || ""; + + return { severity, dateStamp, section, content }; + }; + + let copyData: { + severity: string; + dateStamp: string; + section: string; + content: string; + }[] = []; + + if (fragment.querySelectorAll(".grid").length > 0) { + // Multiple grid elements + copyData = Array.from(fragment.querySelectorAll(".grid")).map( + extractLogData, + ); + } else { + // Try to find the closest grid element or use the first child element + const gridElement = + fragment.querySelector(".grid") || (fragment.firstChild as Element); + + if (gridElement) { + const data = extractLogData(gridElement); + if (data.severity || data.dateStamp || data.section || data.content) { + copyData.push(data); + } + } + } + + if (copyData.length === 0) return; // No valid data to copy + + // Calculate maximum widths for each column + const maxWidths = { + severity: Math.max(...copyData.map((d) => d.severity.length)), + dateStamp: Math.max(...copyData.map((d) => d.dateStamp.length)), + section: Math.max(...copyData.map((d) => d.section.length)), + }; + + const pad = (str: string, length: number) => str.padEnd(length, " "); + + // Create the formatted copy text + const copyText = copyData + .map( + (d) => + `${pad(d.severity, maxWidths.severity)} | ${pad(d.dateStamp, maxWidths.dateStamp)} | ${pad(d.section, maxWidths.section)} | ${d.content}`, + ) + .join("\n"); + + e.clipboardData?.setData("text/plain", copyText); + }; + + const content = contentRef.current; + content?.addEventListener("copy", handleCopy); + return () => { + content?.removeEventListener("copy", handleCopy); + }; + }, []); + return (
@@ -467,18 +545,18 @@ function LogLineData({ )} onClick={onSelect} > -
+
-
+
{line.dateStamp}
-
+
{line.section}
-
+
{line.content}
diff --git a/web/src/pages/System.tsx b/web/src/pages/System.tsx index ab5c86f6a2..23d1b7e6ae 100644 --- a/web/src/pages/System.tsx +++ b/web/src/pages/System.tsx @@ -13,6 +13,7 @@ import useOptimisticState from "@/hooks/use-optimistic-state"; import CameraMetrics from "@/views/system/CameraMetrics"; import { useHashState } from "@/hooks/use-overlay-state"; import { capitalizeFirstLetter } from "@/utils/stringUtil"; +import { Toaster } from "@/components/ui/sonner"; const metrics = ["general", "storage", "cameras"] as const; type SystemMetric = (typeof metrics)[number]; @@ -42,6 +43,7 @@ function System() { return (
+
{isMobile && ( From 8c2c07fd18ea68d6da1b72ebd1307a556767c2ff Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 1 Nov 2024 07:37:52 -0500 Subject: [PATCH 07/23] UI tweaks (#14719) * Show activity indicator when search grid is revalidating * improve frigate+ button title grammar --- web/src/components/overlay/detail/SearchDetailDialog.tsx | 8 ++++++-- web/src/components/overlay/dialog/FrigatePlusDialog.tsx | 6 ++++-- web/src/pages/Explore.tsx | 4 +++- web/src/views/search/SearchView.tsx | 5 ++--- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index 1ff784e6dd..f158df3291 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -655,7 +655,9 @@ function ObjectSnapshotTab({ onSubmitToPlus(false); }} > - This is a {search?.label} + This is{" "} + {/^[aeiou]/i.test(search?.label || "") ? "an" : "a"}{" "} + {search?.label} )} diff --git a/web/src/components/overlay/dialog/FrigatePlusDialog.tsx b/web/src/components/overlay/dialog/FrigatePlusDialog.tsx index 18ada20fce..e98a4164af 100644 --- a/web/src/components/overlay/dialog/FrigatePlusDialog.tsx +++ b/web/src/components/overlay/dialog/FrigatePlusDialog.tsx @@ -144,7 +144,8 @@ export function FrigatePlusDialog({ onSubmitToPlus(false); }} > - This is a {upload?.label} + This is {/^[aeiou]/i.test(upload?.label || "") ? "an" : "a"}{" "} + {upload?.label} )} diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 8989c7b05c..9f80241c14 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -428,7 +428,9 @@ export default function Explore() { searchTerm={searchTerm} searchFilter={searchFilter} searchResults={searchResults} - isLoading={(isLoadingInitialData || isLoadingMore) ?? true} + isLoading={ + (isLoadingInitialData || isLoadingMore || isValidating) ?? true + } hasMore={!isReachingEnd} columns={gridColumns} defaultView={defaultView} diff --git a/web/src/views/search/SearchView.tsx b/web/src/views/search/SearchView.tsx index 4d81a40f76..cf6640f181 100644 --- a/web/src/views/search/SearchView.tsx +++ b/web/src/views/search/SearchView.tsx @@ -397,11 +397,10 @@ export default function SearchView({
)} - {uniqueResults?.length == 0 && - isLoading && + {isLoading && (searchTerm || (searchFilter && Object.keys(searchFilter).length !== 0)) && ( - + )} {uniqueResults && ( From e5ebf938f6c45c607a9393935901ea7680e7d7e7 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Fri, 1 Nov 2024 06:55:55 -0600 Subject: [PATCH 08/23] Fix float input (#14720) --- frigate/object_detection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frigate/object_detection.py b/frigate/object_detection.py index 0af32034e0..cc4641696d 100644 --- a/frigate/object_detection.py +++ b/frigate/object_detection.py @@ -90,6 +90,7 @@ def detect_raw(self, tensor_input: np.ndarray): if self.dtype == InputDTypeEnum.float: tensor_input = tensor_input.astype(np.float32) + tensor_input /= 255 return self.detect_api.detect_raw(tensor_input=tensor_input) From 1234003527b0a65a3e0c5eebf3ef975dc3af16eb Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 1 Nov 2024 19:30:40 -0500 Subject: [PATCH 09/23] Fix width of object lifecycle buttons (#14729) --- web/src/components/overlay/detail/ObjectLifecycle.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/web/src/components/overlay/detail/ObjectLifecycle.tsx b/web/src/components/overlay/detail/ObjectLifecycle.tsx index bebf5e2f58..d687915ca0 100644 --- a/web/src/components/overlay/detail/ObjectLifecycle.tsx +++ b/web/src/components/overlay/detail/ObjectLifecycle.tsx @@ -521,10 +521,7 @@ export default function ObjectLifecycle({ {eventSequence.map((item, index) => ( handleThumbnailClick(index)} >
From 11068aa9d035c3833b104954819686cc91f1c153 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:52:00 -0500 Subject: [PATCH 10/23] Fix validation activity indicator (#14730) * Don't show two spinners when loading/revalidating search results * clarify --- web/src/pages/Explore.tsx | 5 ++--- web/src/views/search/SearchView.tsx | 7 +++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 9f80241c14..4efcb81b83 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -428,9 +428,8 @@ export default function Explore() { searchTerm={searchTerm} searchFilter={searchFilter} searchResults={searchResults} - isLoading={ - (isLoadingInitialData || isLoadingMore || isValidating) ?? true - } + isLoading={(isLoadingInitialData || isLoadingMore) ?? true} + isValidating={isValidating} hasMore={!isReachingEnd} columns={gridColumns} defaultView={defaultView} diff --git a/web/src/views/search/SearchView.tsx b/web/src/views/search/SearchView.tsx index cf6640f181..836ae8f4f2 100644 --- a/web/src/views/search/SearchView.tsx +++ b/web/src/views/search/SearchView.tsx @@ -37,6 +37,7 @@ type SearchViewProps = { searchFilter?: SearchFilter; searchResults?: SearchResult[]; isLoading: boolean; + isValidating: boolean; hasMore: boolean; columns: number; defaultView?: string; @@ -55,6 +56,7 @@ export default function SearchView({ searchFilter, searchResults, isLoading, + isValidating, hasMore, columns, defaultView = "summary", @@ -397,8 +399,9 @@ export default function SearchView({
)} - {isLoading && - (searchTerm || + {((isLoading && uniqueResults?.length == 0) || // show on initial load + (isValidating && !isLoading)) && // or revalidation + (searchTerm || // or change of filter/search term (searchFilter && Object.keys(searchFilter).length !== 0)) && ( )} From d7935abc14245eef33592dae1b4585cf9142596c Mon Sep 17 00:00:00 2001 From: joshjryan Date: Fri, 1 Nov 2024 20:01:38 -0600 Subject: [PATCH 11/23] Set the loglevel for OpenCV ffmpeg messages to fatal (#14728) * Set the loglevel for OpenCV ffmpeg messages to fatal * Set OPENCV_FFMPEG_LOGLEVEL in Dockerfile --- docker/main/Dockerfile | 3 +++ frigate/util/services.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docker/main/Dockerfile b/docker/main/Dockerfile index ac4d277bdd..9d2f442f08 100644 --- a/docker/main/Dockerfile +++ b/docker/main/Dockerfile @@ -211,6 +211,9 @@ ENV TOKENIZERS_PARALLELISM=true # https://github.com/huggingface/transformers/issues/27214 ENV TRANSFORMERS_NO_ADVISORY_WARNINGS=1 +# Set OpenCV ffmpeg loglevel to fatal: https://ffmpeg.org/doxygen/trunk/log_8h.html +ENV OPENCV_FFMPEG_LOGLEVEL=8 + ENV PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}" ENV LIBAVFORMAT_VERSION_MAJOR=60 diff --git a/frigate/util/services.py b/frigate/util/services.py index a717292631..9ee6da9996 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -584,7 +584,7 @@ async def calculate_duration(video: Optional[any]) -> float: width = height = 0 try: - # Open the video stream + # Open the video stream using OpenCV video = cv2.VideoCapture(url) # Check if the video stream was opened successfully From 27ef661fec25ff6d42fcafba11f238eaedc8a8c6 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Sat, 2 Nov 2024 07:13:28 -0500 Subject: [PATCH 12/23] simplify hailort (#14734) --- docker/hailo8l/Dockerfile | 58 +++---------------------------- docker/hailo8l/h8l.hcl | 7 ++++ docker/hailo8l/install_hailort.sh | 21 +++++++++++ 3 files changed, 33 insertions(+), 53 deletions(-) create mode 100755 docker/hailo8l/install_hailort.sh diff --git a/docker/hailo8l/Dockerfile b/docker/hailo8l/Dockerfile index 701c81d0f6..959e7692ed 100644 --- a/docker/hailo8l/Dockerfile +++ b/docker/hailo8l/Dockerfile @@ -2,9 +2,6 @@ ARG DEBIAN_FRONTEND=noninteractive -# NOTE: also update user_installation.sh -ARG HAILO_VERSION=4.19.0 - # Build Python wheels FROM wheels AS h8l-wheels @@ -19,63 +16,18 @@ RUN mkdir /h8l-wheels # Build the wheels RUN pip3 wheel --wheel-dir=/h8l-wheels -c /requirements-wheels.txt -r /requirements-wheels-h8l.txt -# Build HailoRT and create wheel -FROM wheels AS build-hailort +FROM wget AS hailort ARG TARGETARCH -ARG HAILO_VERSION - -SHELL ["/bin/bash", "-c"] - -# Install necessary APT packages -RUN apt-get -qq update \ - && apt-get -qq install -y \ - apt-transport-https \ - gnupg \ - wget \ - # the key fingerprint can be obtained from https://ftp-master.debian.org/keys.html - && wget -qO- "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xA4285295FC7B1A81600062A9605C66F00D6C9793" | \ - gpg --dearmor > /usr/share/keyrings/debian-archive-bullseye-stable.gpg \ - && echo "deb [signed-by=/usr/share/keyrings/debian-archive-bullseye-stable.gpg] http://deb.debian.org/debian bullseye main contrib non-free" | \ - tee /etc/apt/sources.list.d/debian-bullseye-nonfree.list \ - && apt-get -qq update \ - && apt-get -qq install -y \ - python3.9 \ - python3.9-dev \ - build-essential cmake git \ - && rm -rf /var/lib/apt/lists/* - -# Extract Python version and set environment variables -RUN PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}' | cut -d. -f1,2) && \ - PYTHON_VERSION_NO_DOT=$(echo $PYTHON_VERSION | sed 's/\.//') && \ - echo "PYTHON_VERSION=$PYTHON_VERSION" > /etc/environment && \ - echo "PYTHON_VERSION_NO_DOT=$PYTHON_VERSION_NO_DOT" >> /etc/environment - -# Clone and build HailoRT -RUN . /etc/environment && \ - git clone https://github.com/hailo-ai/hailort.git /opt/hailort && \ - cd /opt/hailort && \ - git checkout v${HAILO_VERSION} && \ - cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release && \ - cmake --build build --config release --target libhailort && \ - cmake --build build --config release --target hailortcli && \ - cmake --build build --config release --target install - -# Create a wheel file using pip3 wheel -RUN cd /opt/hailort/hailort/libhailort/bindings/python/platform && \ - python3 setup.py bdist_wheel --dist-dir /hailo-wheels - -RUN mkdir -p /rootfs/usr/local/lib /rootfs/usr/local/bin && \ - cp /usr/local/lib/libhailort.so* /rootfs/usr/local/lib && \ - cp /usr/local/bin/hailortcli /rootfs/usr/local/bin +RUN --mount=type=bind,source=docker/hailo8l/install_hailort.sh,target=/deps/install_hailort.sh \ + /deps/install_hailort.sh # Use deps as the base image FROM deps AS h8l-frigate -ARG HAILO_VERSION # Copy the wheels from the wheels stage COPY --from=h8l-wheels /h8l-wheels /deps/h8l-wheels -COPY --from=build-hailort /hailo-wheels /deps/hailo-wheels -COPY --from=build-hailort /rootfs/ / +COPY --from=hailort /hailo-wheels /deps/hailo-wheels +COPY --from=hailort /rootfs/ / # Install the wheels RUN pip3 install -U /deps/h8l-wheels/*.whl diff --git a/docker/hailo8l/h8l.hcl b/docker/hailo8l/h8l.hcl index a1eb82fb5e..91f6d13c6e 100644 --- a/docker/hailo8l/h8l.hcl +++ b/docker/hailo8l/h8l.hcl @@ -1,3 +1,9 @@ +target wget { + dockerfile = "docker/main/Dockerfile" + platforms = ["linux/arm64","linux/amd64"] + target = "wget" +} + target wheels { dockerfile = "docker/main/Dockerfile" platforms = ["linux/arm64","linux/amd64"] @@ -19,6 +25,7 @@ target rootfs { target h8l { dockerfile = "docker/hailo8l/Dockerfile" contexts = { + wget = "target:wget" wheels = "target:wheels" deps = "target:deps" rootfs = "target:rootfs" diff --git a/docker/hailo8l/install_hailort.sh b/docker/hailo8l/install_hailort.sh new file mode 100755 index 0000000000..004db86c93 --- /dev/null +++ b/docker/hailo8l/install_hailort.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -euxo pipefail + +hailo_version="4.19.0" + +if [[ "${TARGETARCH}" == "amd64" ]]; then + arch="x86_64" +elif [[ "${TARGETARCH}" == "arm64" ]]; then + arch="aarch64" +fi + +mkdir -p /rootfs + +wget -qO- "https://github.com/frigate-nvr/hailort/releases/download/v${hailo_version}/hailort-${TARGETARCH}.tar.gz" | + tar -C /rootfs/ -xzf - + +mkdir -p /hailo-wheels + +wget -P /hailo-wheels/ "https://github.com/frigate-nvr/hailort/releases/download/v${hailo_version}/hailort-${hailo_version}-cp39-cp39-linux_${arch}.whl" + From 7d3313e7322f99beb308c07b1a06a23c1e99e28c Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sat, 2 Nov 2024 18:16:07 -0500 Subject: [PATCH 13/23] Add ability to view tracked objects in Explore from review item details pane (#14744) --- frigate/api/defs/events_query_parameters.py | 1 + frigate/api/event.py | 4 ++++ .../overlay/detail/ReviewDetailDialog.tsx | 18 ++++++++++++++++++ web/src/pages/Explore.tsx | 1 + 4 files changed, 24 insertions(+) diff --git a/frigate/api/defs/events_query_parameters.py b/frigate/api/defs/events_query_parameters.py index fe1d2b8b21..4639b7f598 100644 --- a/frigate/api/defs/events_query_parameters.py +++ b/frigate/api/defs/events_query_parameters.py @@ -28,6 +28,7 @@ class EventsQueryParams(BaseModel): is_submitted: Optional[int] = None min_length: Optional[float] = None max_length: Optional[float] = None + event_id: Optional[str] = None sort: Optional[str] = None timezone: Optional[str] = "utc" diff --git a/frigate/api/event.py b/frigate/api/event.py index 7f4f14610b..869b61aafe 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -88,6 +88,7 @@ def events(params: EventsQueryParams = Depends()): is_submitted = params.is_submitted min_length = params.min_length max_length = params.max_length + event_id = params.event_id sort = params.sort @@ -230,6 +231,9 @@ def events(params: EventsQueryParams = Depends()): elif is_submitted > 0: clauses.append((Event.plus_id != "")) + if event_id is not None: + clauses.append((Event.id == event_id)) + if len(clauses) == 0: clauses.append((True)) diff --git a/web/src/components/overlay/detail/ReviewDetailDialog.tsx b/web/src/components/overlay/detail/ReviewDetailDialog.tsx index 2230046f39..d40f68a1e9 100644 --- a/web/src/components/overlay/detail/ReviewDetailDialog.tsx +++ b/web/src/components/overlay/detail/ReviewDetailDialog.tsx @@ -40,6 +40,7 @@ import { import { useOverlayState } from "@/hooks/use-overlay-state"; import { DownloadVideoButton } from "@/components/button/DownloadVideoButton"; import { TooltipPortal } from "@radix-ui/react-tooltip"; +import { LuSearch } from "react-icons/lu"; type ReviewDetailDialogProps = { review?: ReviewSegment; @@ -53,6 +54,8 @@ export default function ReviewDetailDialog({ revalidateOnFocus: false, }); + const navigate = useNavigate(); + // upload const [upload, setUpload] = useState(); @@ -219,6 +222,21 @@ export default function ReviewDetailDialog({ )} {event.sub_label ?? event.label} ( {Math.round(event.data.top_score * 100)}%) + + +
{ + navigate(`/explore?event_id=${event.id}`); + }} + > + +
+
+ + View in Explore + +
); })} diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 4efcb81b83..e8889e3eec 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -114,6 +114,7 @@ export default function Explore() { max_score: searchSearchParams["max_score"], has_snapshot: searchSearchParams["has_snapshot"], has_clip: searchSearchParams["has_clip"], + event_id: searchSearchParams["event_id"], limit: Object.keys(searchSearchParams).length == 0 ? API_LIMIT : undefined, timezone, From 44f40966e7b8273eb86fa3ad4389c3ffb8e7dc0e Mon Sep 17 00:00:00 2001 From: leccelecce <24962424+leccelecce@users.noreply.github.com> Date: Sun, 3 Nov 2024 12:16:59 +0000 Subject: [PATCH 14/23] Docs: correct go2rtc version used (#14753) --- docs/docs/configuration/camera_specific.md | 2 +- docs/docs/configuration/restream.md | 4 ++-- docs/docs/guides/configuring_go2rtc.md | 6 +++--- docs/sidebars.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/docs/configuration/camera_specific.md b/docs/docs/configuration/camera_specific.md index 70638b69e1..072c20bb58 100644 --- a/docs/docs/configuration/camera_specific.md +++ b/docs/docs/configuration/camera_specific.md @@ -181,7 +181,7 @@ go2rtc: - rtspx://192.168.1.1:7441/abcdefghijk ``` -[See the go2rtc docs for more information](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#source-rtsp) +[See the go2rtc docs for more information](https://github.com/AlexxIT/go2rtc/tree/v1.9.2#source-rtsp) In the Unifi 2.0 update Unifi Protect Cameras had a change in audio sample rate which causes issues for ffmpeg. The input rate needs to be set for record if used directly with unifi protect. diff --git a/docs/docs/configuration/restream.md b/docs/docs/configuration/restream.md index 2110509725..1ad09cc8d6 100644 --- a/docs/docs/configuration/restream.md +++ b/docs/docs/configuration/restream.md @@ -7,7 +7,7 @@ title: Restream Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://:8554/`. Port 8554 must be open. [This allows you to use a video feed for detection in Frigate and Home Assistant live view at the same time without having to make two separate connections to the camera](#reduce-connections-to-camera). The video feed is copied from the original video feed directly to avoid re-encoding. This feed does not include any annotation by Frigate. -Frigate uses [go2rtc](https://github.com/AlexxIT/go2rtc/tree/v1.9.4) to provide its restream and MSE/WebRTC capabilities. The go2rtc config is hosted at the `go2rtc` in the config, see [go2rtc docs](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#configuration) for more advanced configurations and features. +Frigate uses [go2rtc](https://github.com/AlexxIT/go2rtc/tree/v1.9.2) to provide its restream and MSE/WebRTC capabilities. The go2rtc config is hosted at the `go2rtc` in the config, see [go2rtc docs](https://github.com/AlexxIT/go2rtc/tree/v1.9.2#configuration) for more advanced configurations and features. :::note @@ -134,7 +134,7 @@ cameras: ## Advanced Restream Configurations -The [exec](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#source-exec) source in go2rtc can be used for custom ffmpeg commands. An example is below: +The [exec](https://github.com/AlexxIT/go2rtc/tree/v1.9.2#source-exec) source in go2rtc can be used for custom ffmpeg commands. An example is below: NOTE: The output will need to be passed with two curly braces `{{output}}` diff --git a/docs/docs/guides/configuring_go2rtc.md b/docs/docs/guides/configuring_go2rtc.md index 5c85f7d11a..8667ead772 100644 --- a/docs/docs/guides/configuring_go2rtc.md +++ b/docs/docs/guides/configuring_go2rtc.md @@ -13,7 +13,7 @@ Use of the bundled go2rtc is optional. You can still configure FFmpeg to connect # Setup a go2rtc stream -First, you will want to configure go2rtc to connect to your camera stream by adding the stream you want to use for live view in your Frigate config file. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#module-streams), not just rtsp. +First, you will want to configure go2rtc to connect to your camera stream by adding the stream you want to use for live view in your Frigate config file. Avoid changing any other parts of your config at this step. Note that go2rtc supports [many different stream types](https://github.com/AlexxIT/go2rtc/tree/v1.9.2#module-streams), not just rtsp. :::tip @@ -47,8 +47,8 @@ After adding this to the config, restart Frigate and try to watch the live strea - Check Video Codec: - If the camera stream works in go2rtc but not in your browser, the video codec might be unsupported. - - If using H265, switch to H264. Refer to [video codec compatibility](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#codecs-madness) in go2rtc documentation. - - If unable to switch from H265 to H264, or if the stream format is different (e.g., MJPEG), re-encode the video using [FFmpeg parameters](https://github.com/AlexxIT/go2rtc/tree/v1.9.4#source-ffmpeg). It supports rotating and resizing video feeds and hardware acceleration. Keep in mind that transcoding video from one format to another is a resource intensive task and you may be better off using the built-in jsmpeg view. + - If using H265, switch to H264. Refer to [video codec compatibility](https://github.com/AlexxIT/go2rtc/tree/v1.9.2#codecs-madness) in go2rtc documentation. + - If unable to switch from H265 to H264, or if the stream format is different (e.g., MJPEG), re-encode the video using [FFmpeg parameters](https://github.com/AlexxIT/go2rtc/tree/v1.9.2#source-ffmpeg). It supports rotating and resizing video feeds and hardware acceleration. Keep in mind that transcoding video from one format to another is a resource intensive task and you may be better off using the built-in jsmpeg view. ```yaml go2rtc: streams: diff --git a/docs/sidebars.ts b/docs/sidebars.ts index f8e8780b6c..4ed41d2ade 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -26,7 +26,7 @@ const sidebars: SidebarsConfig = { { type: 'link', label: 'Go2RTC Configuration Reference', - href: 'https://github.com/AlexxIT/go2rtc/tree/v1.9.4#configuration', + href: 'https://github.com/AlexxIT/go2rtc/tree/v1.9.2#configuration', } as PropSidebarItemLink, ], Detectors: [ From 189d4b459fa83f93b782e4a4f00f039ba76d3371 Mon Sep 17 00:00:00 2001 From: leccelecce <24962424+leccelecce@users.noreply.github.com> Date: Sun, 3 Nov 2024 15:28:19 +0000 Subject: [PATCH 15/23] Avoid divide by zero in shm_frame_count (#14750) --- frigate/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frigate/app.py b/frigate/app.py index bc4f626e03..2f771ec4d1 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -512,6 +512,9 @@ def shm_frame_count(self) -> int: 1, ) + if cam_total_frame_size == 0.0: + return 0 + shm_frame_count = min(50, int(available_shm / (cam_total_frame_size))) logger.debug( From 77ec86d31afb48770b49945d43ca972facc7a075 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Sun, 3 Nov 2024 12:52:27 -0300 Subject: [PATCH 16/23] Fix devcontainer when there is no ~/.ssh/know_hosts file (#14758) --- .devcontainer/post_create.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.devcontainer/post_create.sh b/.devcontainer/post_create.sh index ee08880163..ec33ffb867 100755 --- a/.devcontainer/post_create.sh +++ b/.devcontainer/post_create.sh @@ -3,10 +3,12 @@ set -euxo pipefail # Cleanup the old github host key -sed -i -e '/AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31\/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi\/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==/d' ~/.ssh/known_hosts -# Add new github host key -curl -L https://api.github.com/meta | jq -r '.ssh_keys | .[]' | \ - sed -e 's/^/github.com /' >> ~/.ssh/known_hosts +if [[ -f ~/.ssh/known_hosts ]]; then + # Add new github host key + sed -i -e '/AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31\/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi\/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==/d' ~/.ssh/known_hosts + curl -L https://api.github.com/meta | jq -r '.ssh_keys | .[]' | \ + sed -e 's/^/github.com /' >> ~/.ssh/known_hosts +fi # Frigate normal container runs as root, so it have permission to create # the folders. But the devcontainer runs as the host user, so we need to From 9755fa05376c7f183bcad60563d3b77dd0a2d57a Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Sun, 3 Nov 2024 14:00:12 -0300 Subject: [PATCH 17/23] Fix exports migration when there is none (#14761) --- frigate/util/config.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/frigate/util/config.py b/frigate/util/config.py index a9a9666d95..3f3c45aa6b 100644 --- a/frigate/util/config.py +++ b/frigate/util/config.py @@ -50,14 +50,15 @@ def migrate_frigate_config(config_file: str): previous_version = "0.14" logger.info("Migrating export file names...") - for file in os.listdir(EXPORT_DIR): - if "@" not in file: - continue - - new_name = file.replace("@", "_") - os.rename( - os.path.join(EXPORT_DIR, file), os.path.join(EXPORT_DIR, new_name) - ) + if os.path.isdir(EXPORT_DIR): + for file in os.listdir(EXPORT_DIR): + if "@" not in file: + continue + + new_name = file.replace("@", "_") + os.rename( + os.path.join(EXPORT_DIR, file), os.path.join(EXPORT_DIR, new_name) + ) if previous_version < "0.15-0": logger.info(f"Migrating frigate config from {previous_version} to 0.15-0...") From 959ca0f412f3fd4b117b32c07c2141e8634c6a91 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:41:31 -0600 Subject: [PATCH 18/23] Fix object processing logic for detections (#14766) --- frigate/object_processing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frigate/object_processing.py b/frigate/object_processing.py index fab7bbc6ca..ca7ea47b85 100644 --- a/frigate/object_processing.py +++ b/frigate/object_processing.py @@ -714,7 +714,8 @@ def should_retain_recording(self, camera: str, obj: TrackedObject): ) and ( not review_config.detections.required_zones - or set(obj.entered_zones) & set(review_config.alerts.required_zones) + or set(obj.entered_zones) + & set(review_config.detections.required_zones) ) ) ): From 156e7cc628a1b60ce299e063652cf02ef294d95e Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sun, 3 Nov 2024 18:49:13 -0700 Subject: [PATCH 19/23] Clarify semantic search GPU (#14767) * Clarify semantic search GPU * clarity Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> * fix wording --------- Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> --- docs/docs/configuration/semantic_search.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/docs/configuration/semantic_search.md b/docs/docs/configuration/semantic_search.md index 8abd761a8e..61873478df 100644 --- a/docs/docs/configuration/semantic_search.md +++ b/docs/docs/configuration/semantic_search.md @@ -60,15 +60,17 @@ The CLIP models are downloaded in ONNX format, and the `large` model can be acce If the correct build is used for your GPU and the `large` model is configured, then the GPU will be detected and used automatically. -**AMD** -- ROCm will automatically be detected and used for semantic search in the `-rocm` Frigate image. +**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. -**Intel** -- OpenVINO will automatically be detected and used as a detector in the default Frigate image. +- **AMD** + - ROCm will automatically be detected and used for semantic search in the `-rocm` Frigate image. -**Nvidia** -- Nvidia GPUs will automatically be detected and used as a detector in the `-tensorrt` Frigate image. -- Jetson devices will automatically be detected and used as a detector in the `-tensorrt-jp(4/5)` 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. ::: From a13b9815f61089e13d678599e1cae398a18437d3 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 4 Nov 2024 07:07:57 -0700 Subject: [PATCH 20/23] Various fixes (#14786) * Catch openvino error * Remove clip deletion * Update deletion text * Fix timeline not respecting timezone config * Tweaks * More timezone fixes * Fix * More timezone fixes * Fix shm docs --- docs/docs/frigate/installation.md | 14 +- frigate/api/event.py | 3 - frigate/app.py | 4 +- frigate/util/model.py | 33 ++- web/src/components/bar/TimelineBar.tsx | 191 ------------------ web/src/components/graph/CameraGraph.tsx | 15 +- web/src/components/graph/SystemGraph.tsx | 17 +- .../components/menu/SearchResultActions.tsx | 8 +- .../overlay/detail/ObjectLifecycle.tsx | 1 + .../components/timeline/segment-metadata.tsx | 46 +++-- web/src/hooks/use-draggable-element.ts | 28 ++- 11 files changed, 103 insertions(+), 257 deletions(-) delete mode 100644 web/src/components/bar/TimelineBar.tsx diff --git a/docs/docs/frigate/installation.md b/docs/docs/frigate/installation.md index 10c83b0132..e3599e6289 100644 --- a/docs/docs/frigate/installation.md +++ b/docs/docs/frigate/installation.md @@ -81,15 +81,15 @@ You can calculate the **minimum** shm size for each camera with the following fo ```console # Replace and -$ python -c 'print("{:.2f}MB".format(( * * 1.5 * 10 + 270480) / 1048576))' +$ python -c 'print("{:.2f}MB".format(( * * 1.5 * 20 + 270480) / 1048576))' -# Example for 1280x720 -$ python -c 'print("{:.2f}MB".format((1280 * 720 * 1.5 * 10 + 270480) / 1048576))' -13.44MB +# Example for 1280x720, including logs +$ python -c 'print("{:.2f}MB".format((1280 * 720 * 1.5 * 20 + 270480) / 1048576)) + 40' +46.63MB # Example for eight cameras detecting at 1280x720, including logs -$ python -c 'print("{:.2f}MB".format(((1280 * 720 * 1.5 * 10 + 270480) / 1048576) * 8 + 40))' -136.99MB +$ python -c 'print("{:.2f}MB".format(((1280 * 720 * 1.5 * 20 + 270480) / 1048576) * 8 + 40))' +253MB ``` The shm size cannot be set per container for Home Assistant add-ons. However, this is probably not required since by default Home Assistant Supervisor allocates `/dev/shm` with half the size of your total memory. If your machine has 8GB of memory, chances are that Frigate will have access to up to 4GB without any additional configuration. @@ -194,7 +194,7 @@ services: privileged: true # this may not be necessary for all setups restart: unless-stopped image: ghcr.io/blakeblackshear/frigate:stable - shm_size: "64mb" # update for your cameras based on calculation above + shm_size: "512mb" # update for your cameras based on calculation above devices: - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions - /dev/apex_0:/dev/apex_0 # Passes a PCIe Coral, follow driver instructions here https://coral.ai/docs/m2/get-started/#2a-on-linux diff --git a/frigate/api/event.py b/frigate/api/event.py index 869b61aafe..ac414cdde6 100644 --- a/frigate/api/event.py +++ b/frigate/api/event.py @@ -1042,9 +1042,6 @@ def delete_event(request: Request, event_id: str): media.unlink(missing_ok=True) media = Path(f"{os.path.join(CLIPS_DIR, media_name)}-clean.png") media.unlink(missing_ok=True) - if event.has_clip: - media = Path(f"{os.path.join(CLIPS_DIR, media_name)}.mp4") - media.unlink(missing_ok=True) event.delete_instance() Timeline.delete().where(Timeline.source_id == event_id).execute() diff --git a/frigate/app.py b/frigate/app.py index 2f771ec4d1..d0d3ab8a11 100644 --- a/frigate/app.py +++ b/frigate/app.py @@ -521,9 +521,9 @@ def shm_frame_count(self) -> int: f"Calculated total camera size {available_shm} / {cam_total_frame_size} :: {shm_frame_count} frames for each camera in SHM" ) - if shm_frame_count < 10: + if shm_frame_count < 20: logger.warning( - f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size * 10)}MB." + f"The current SHM size of {total_shm}MB is too small, recommend increasing it to at least {round(min_req_shm + cam_total_frame_size * 20)}MB." ) return shm_frame_count diff --git a/frigate/util/model.py b/frigate/util/model.py index 2aa06d0b26..091bb08339 100644 --- a/frigate/util/model.py +++ b/frigate/util/model.py @@ -1,5 +1,6 @@ """Model Utils""" +import logging import os from typing import Any @@ -11,6 +12,8 @@ # openvino is not included pass +logger = logging.getLogger(__name__) + def get_ort_providers( force_cpu: bool = False, device: str = "AUTO", requires_fp16: bool = False @@ -89,19 +92,27 @@ def __init__(self, model_path: str, device: str, requires_fp16: bool = False): self.ort: ort.InferenceSession = None self.ov: ov.Core = None providers, options = get_ort_providers(device == "CPU", device, requires_fp16) + self.interpreter = None if "OpenVINOExecutionProvider" in providers: - # use OpenVINO directly - self.type = "ov" - self.ov = ov.Core() - self.ov.set_property( - {ov.properties.cache_dir: "/config/model_cache/openvino"} - ) - self.interpreter = self.ov.compile_model( - model=model_path, device_name=device - ) - else: - # Use ONNXRuntime + try: + # use OpenVINO directly + self.type = "ov" + self.ov = ov.Core() + self.ov.set_property( + {ov.properties.cache_dir: "/config/model_cache/openvino"} + ) + self.interpreter = self.ov.compile_model( + model=model_path, device_name=device + ) + except Exception as e: + logger.warning( + f"OpenVINO failed to build model, using CPU instead: {e}" + ) + self.interpreter = None + + # Use ONNXRuntime + if self.interpreter is None: self.type = "ort" self.ort = ort.InferenceSession( model_path, diff --git a/web/src/components/bar/TimelineBar.tsx b/web/src/components/bar/TimelineBar.tsx deleted file mode 100644 index fe05b876f5..0000000000 --- a/web/src/components/bar/TimelineBar.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import { FrigateConfig } from "@/types/frigateConfig"; -import { GraphDataPoint } from "@/types/graph"; -import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; -import useSWR from "swr"; -import ActivityIndicator from "../indicators/activity-indicator"; - -type TimelineBarProps = { - startTime: number; - graphData: - | { - objects: number[]; - motion: GraphDataPoint[]; - } - | undefined; - onClick?: () => void; -}; -export default function TimelineBar({ - startTime, - graphData, - onClick, -}: TimelineBarProps) { - const { data: config } = useSWR("config"); - - if (!config) { - return ; - } - - return ( -
- {graphData != undefined && ( -
- {getHourBlocks().map((idx) => { - return ( -
- ); - })} -
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:00" : "%I:00%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:05" : "%I:05%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:10" : "%I:10%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:15" : "%I:15%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:20" : "%I:20%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:25" : "%I:25%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:30" : "%I:30%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:35" : "%I:35%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:40" : "%I:40%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:45" : "%I:45%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:50" : "%I:50%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
-
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config?.ui.time_format == "24hour" ? "%H:55" : "%I:55%P", - time_style: "medium", - date_style: "medium", - })} -
-
-
- )} -
- {formatUnixTimestampToDateTime(startTime, { - strftime_fmt: - config.ui.time_format == "24hour" ? "%m/%d %H:%M" : "%m/%d %I:%M%P", - time_style: "medium", - date_style: "medium", - })} -
-
- ); -} - -function getHourBlocks() { - const arr = []; - - for (let x = 0; x <= 59; x++) { - arr.push(x); - } - - return arr; -} diff --git a/web/src/components/graph/CameraGraph.tsx b/web/src/components/graph/CameraGraph.tsx index 3289887c51..ab5d6e03f9 100644 --- a/web/src/components/graph/CameraGraph.tsx +++ b/web/src/components/graph/CameraGraph.tsx @@ -1,5 +1,6 @@ import { useTheme } from "@/context/theme-provider"; import { FrigateConfig } from "@/types/frigateConfig"; +import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { useCallback, useEffect, useMemo } from "react"; import Chart from "react-apexcharts"; import { isMobileOnly } from "react-device-detect"; @@ -42,12 +43,14 @@ export function CameraLineGraph({ const formatTime = useCallback( (val: unknown) => { - const date = new Date(updateTimes[Math.round(val as number)] * 1000); - return date.toLocaleTimeString([], { - hour12: config?.ui.time_format != "24hour", - hour: "2-digit", - minute: "2-digit", - }); + return formatUnixTimestampToDateTime( + updateTimes[Math.round(val as number)], + { + timezone: config?.ui.timezone, + strftime_fmt: + config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p", + }, + ); }, [config, updateTimes], ); diff --git a/web/src/components/graph/SystemGraph.tsx b/web/src/components/graph/SystemGraph.tsx index 572eae5cd5..aaf838763d 100644 --- a/web/src/components/graph/SystemGraph.tsx +++ b/web/src/components/graph/SystemGraph.tsx @@ -1,6 +1,7 @@ import { useTheme } from "@/context/theme-provider"; import { FrigateConfig } from "@/types/frigateConfig"; import { Threshold } from "@/types/graph"; +import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { useCallback, useEffect, useMemo } from "react"; import Chart from "react-apexcharts"; import { isMobileOnly } from "react-device-detect"; @@ -50,17 +51,17 @@ export function ThresholdBarGraph({ let timeOffset = 0; if (dateIndex < 0) { - timeOffset = 5000 * Math.abs(dateIndex); + timeOffset = 5 * Math.abs(dateIndex); } - const date = new Date( - updateTimes[Math.max(1, dateIndex) - 1] * 1000 - timeOffset, + return formatUnixTimestampToDateTime( + updateTimes[Math.max(1, dateIndex) - 1] - timeOffset, + { + timezone: config?.ui.timezone, + strftime_fmt: + config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p", + }, ); - return date.toLocaleTimeString([], { - hour12: config?.ui.time_format != "24hour", - hour: "2-digit", - minute: "2-digit", - }); }, [config, updateTimes], ); diff --git a/web/src/components/menu/SearchResultActions.tsx b/web/src/components/menu/SearchResultActions.tsx index 8a9373bccb..a07d27240e 100644 --- a/web/src/components/menu/SearchResultActions.tsx +++ b/web/src/components/menu/SearchResultActions.tsx @@ -159,7 +159,13 @@ export default function SearchResultActions({ Confirm Delete - Are you sure you want to delete this tracked object? + Deleting this tracked object removes the snapshot, any saved + embeddings, and any associated object lifecycle entries. Recorded + footage of this tracked object in History view will NOT be + deleted. +
+
+ Are you sure you want to proceed?
Cancel diff --git a/web/src/components/overlay/detail/ObjectLifecycle.tsx b/web/src/components/overlay/detail/ObjectLifecycle.tsx index d687915ca0..7a667529e8 100644 --- a/web/src/components/overlay/detail/ObjectLifecycle.tsx +++ b/web/src/components/overlay/detail/ObjectLifecycle.tsx @@ -427,6 +427,7 @@ export default function ObjectLifecycle({
{formatUnixTimestampToDateTime(item.timestamp, { + timezone: config.ui.timezone, strftime_fmt: config.ui.time_format == "24hour" ? "%d %b %H:%M:%S" diff --git a/web/src/components/timeline/segment-metadata.tsx b/web/src/components/timeline/segment-metadata.tsx index 6ca71af249..3e3c99393b 100644 --- a/web/src/components/timeline/segment-metadata.tsx +++ b/web/src/components/timeline/segment-metadata.tsx @@ -1,4 +1,6 @@ import { FrigateConfig } from "@/types/frigateConfig"; +import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; +import { useMemo } from "react"; import useSWR from "swr"; type MinimapSegmentProps = { @@ -40,22 +42,22 @@ export function MinimapBounds({ className="pointer-events-none absolute inset-0 -bottom-7 z-20 flex w-full select-none scroll-mt-8 items-center justify-center text-center text-[10px] font-medium text-primary" ref={firstMinimapSegmentRef} > - {new Date(alignedMinimapStartTime * 1000).toLocaleTimeString([], { - hour12: config?.ui.time_format != "24hour", - hour: "2-digit", - minute: "2-digit", - ...(!dense && { month: "short", day: "2-digit" }), + {formatUnixTimestampToDateTime(alignedMinimapStartTime, { + timezone: config?.ui.timezone, + strftime_fmt: !dense + ? `%b %d, ${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}` + : `${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}`, })}
)} {isLastSegmentInMinimap && (
- {new Date(alignedMinimapEndTime * 1000).toLocaleTimeString([], { - hour12: config?.ui.time_format != "24hour", - hour: "2-digit", - minute: "2-digit", - ...(!dense && { month: "short", day: "2-digit" }), + {formatUnixTimestampToDateTime(alignedMinimapEndTime, { + timezone: config?.ui.timezone, + strftime_fmt: !dense + ? `%b %d, ${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}` + : `${config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p"}`, })}
)} @@ -92,6 +94,22 @@ export function Timestamp({ }: TimestampSegmentProps) { const { data: config } = useSWR("config"); + const formattedTimestamp = useMemo(() => { + if ( + !( + timestamp.getMinutes() % timestampSpread === 0 && + timestamp.getSeconds() === 0 + ) + ) { + return undefined; + } + + return formatUnixTimestampToDateTime(timestamp.getTime() / 1000, { + timezone: config?.ui.timezone, + strftime_fmt: config?.ui.time_format == "24hour" ? "%H:%M" : "%I:%M %p", + }); + }, [config, timestamp, timestampSpread]); + return (
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && ( @@ -99,13 +117,7 @@ export function Timestamp({ key={`${segmentKey}_timestamp`} className="pointer-events-none select-none text-[8px] text-neutral_variant dark:text-neutral" > - {timestamp.getMinutes() % timestampSpread === 0 && - timestamp.getSeconds() === 0 && - timestamp.toLocaleTimeString([], { - hour12: config?.ui.time_format != "24hour", - hour: "2-digit", - minute: "2-digit", - })} + {formattedTimestamp}
)}
diff --git a/web/src/hooks/use-draggable-element.ts b/web/src/hooks/use-draggable-element.ts index 0168cd5a24..73013de589 100644 --- a/web/src/hooks/use-draggable-element.ts +++ b/web/src/hooks/use-draggable-element.ts @@ -10,6 +10,7 @@ import scrollIntoView from "scroll-into-view-if-needed"; import { useTimelineUtils } from "./use-timeline-utils"; import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; +import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; type DraggableElementProps = { contentRef: React.RefObject; @@ -168,6 +169,19 @@ function useDraggableElement({ [segmentDuration, timelineStartAligned, segmentHeight], ); + const getFormattedTimestamp = useCallback( + (segmentStartTime: number) => { + return formatUnixTimestampToDateTime(segmentStartTime, { + timezone: config?.ui.timezone, + strftime_fmt: + config?.ui.time_format == "24hour" + ? `%H:%M${segmentDuration < 60 && !dense ? ":%S" : ""}` + : `%I:%M${segmentDuration < 60 && !dense ? ":%S" : ""} %p`, + }); + }, + [config, dense, segmentDuration], + ); + const updateDraggableElementPosition = useCallback( ( newElementPosition: number, @@ -184,14 +198,8 @@ function useDraggableElement({ } if (draggableElementTimeRef.current) { - draggableElementTimeRef.current.textContent = new Date( - segmentStartTime * 1000, - ).toLocaleTimeString([], { - hour12: config?.ui.time_format != "24hour", - hour: "2-digit", - minute: "2-digit", - ...(segmentDuration < 60 && !dense && { second: "2-digit" }), - }); + draggableElementTimeRef.current.textContent = + getFormattedTimestamp(segmentStartTime); if (scrollTimeline && !userInteracting) { scrollIntoView(thumb, { block: "center", @@ -208,13 +216,11 @@ function useDraggableElement({ } }, [ - segmentDuration, draggableElementTimeRef, draggableElementRef, setDraggableElementTime, setDraggableElementPosition, - dense, - config, + getFormattedTimestamp, userInteracting, ], ); From 553676aade8f66b3f8a1a05cd822c8bdbdae53cd Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Mon, 4 Nov 2024 12:04:33 -0700 Subject: [PATCH 21/23] Fix missing tensor_input (#14790) --- docs/docs/configuration/object_detectors.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/configuration/object_detectors.md b/docs/docs/configuration/object_detectors.md index 5896260f4e..7fefa74a40 100644 --- a/docs/docs/configuration/object_detectors.md +++ b/docs/docs/configuration/object_detectors.md @@ -457,6 +457,7 @@ model: width: 320 # <--- should match whatever was set in notebook height: 320 # <--- should match whatever was set in notebook input_pixel_format: bgr + input_tensor: nchw path: /config/yolo_nas_s.onnx labelmap_path: /labelmap/coco-80.txt ``` From ac762762c39c1885397ee14431d178877cf1b62e Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:50:05 -0600 Subject: [PATCH 22/23] Overwrite existing saved search (#14792) * Overwrite existing saved search * simplify --- web/src/components/input/InputWithTags.tsx | 22 +++++++++++++------ web/src/components/input/SaveSearchDialog.tsx | 15 ++++++++++++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/web/src/components/input/InputWithTags.tsx b/web/src/components/input/InputWithTags.tsx index becc0f4e1e..ff46375fdb 100644 --- a/web/src/components/input/InputWithTags.tsx +++ b/web/src/components/input/InputWithTags.tsx @@ -1,4 +1,10 @@ -import React, { useState, useRef, useEffect, useCallback } from "react"; +import React, { + useState, + useRef, + useEffect, + useCallback, + useMemo, +} from "react"; import { LuX, LuFilter, @@ -88,6 +94,11 @@ export default function InputWithTags({ const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [searchToDelete, setSearchToDelete] = useState(null); + const searchHistoryNames = useMemo( + () => searchHistory?.map((item) => item.name) ?? [], + [searchHistory], + ); + const handleSetSearchHistory = useCallback(() => { setIsSaveDialogOpen(true); }, []); @@ -96,12 +107,8 @@ export default function InputWithTags({ (name: string) => { if (searchHistoryLoaded) { setSearchHistory([ - ...(searchHistory ?? []), - { - name: name, - search: search, - filter: filters, - }, + ...(searchHistory ?? []).filter((item) => item.name !== name), + { name, search, filter: filters }, ]); } }, @@ -835,6 +842,7 @@ export default function InputWithTags({ setIsSaveDialogOpen(false)} onSave={handleSaveSearch} diff --git a/web/src/components/input/SaveSearchDialog.tsx b/web/src/components/input/SaveSearchDialog.tsx index 89e9217d7e..322a76421d 100644 --- a/web/src/components/input/SaveSearchDialog.tsx +++ b/web/src/components/input/SaveSearchDialog.tsx @@ -9,17 +9,19 @@ import { import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { isMobile } from "react-device-detect"; import { toast } from "sonner"; type SaveSearchDialogProps = { + existingNames: string[]; isOpen: boolean; onClose: () => void; onSave: (name: string) => void; }; export function SaveSearchDialog({ + existingNames, isOpen, onClose, onSave, @@ -37,6 +39,11 @@ export function SaveSearchDialog({ } }; + const overwrite = useMemo( + () => existingNames.includes(searchName), + [existingNames, searchName], + ); + return ( setSearchName(e.target.value)} placeholder="Enter a name for your search" /> + {overwrite && ( +
+ {searchName} already exists. Saving will overwrite the existing + value. +
+ )}