mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-03 06:50:58 +00:00
Compare commits
2 Commits
89b54f19c8
...
80a13e43e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80a13e43e9 | ||
|
|
c8e22a3653 |
@ -165,6 +165,8 @@ devices:
|
|||||||
- /dev/dma_heap
|
- /dev/dma_heap
|
||||||
- /dev/rga
|
- /dev/rga
|
||||||
- /dev/mpp_service
|
- /dev/mpp_service
|
||||||
|
volumes:
|
||||||
|
- /sys/:/sys/:ro
|
||||||
```
|
```
|
||||||
|
|
||||||
or add these options to your `docker run` command:
|
or add these options to your `docker run` command:
|
||||||
@ -175,7 +177,8 @@ or add these options to your `docker run` command:
|
|||||||
--device /dev/dri \
|
--device /dev/dri \
|
||||||
--device /dev/dma_heap \
|
--device /dev/dma_heap \
|
||||||
--device /dev/rga \
|
--device /dev/rga \
|
||||||
--device /dev/mpp_service
|
--device /dev/mpp_service \
|
||||||
|
--volume /sys/:/sys/:ro
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Configuration
|
#### Configuration
|
||||||
|
|||||||
@ -24,6 +24,7 @@ from frigate.util.services import (
|
|||||||
get_intel_gpu_stats,
|
get_intel_gpu_stats,
|
||||||
get_jetson_stats,
|
get_jetson_stats,
|
||||||
get_nvidia_gpu_stats,
|
get_nvidia_gpu_stats,
|
||||||
|
get_rockchip_npu_stats,
|
||||||
is_vaapi_amd_driver,
|
is_vaapi_amd_driver,
|
||||||
)
|
)
|
||||||
from frigate.version import VERSION
|
from frigate.version import VERSION
|
||||||
@ -109,6 +110,7 @@ def get_processing_stats(
|
|||||||
stats_tasks = [
|
stats_tasks = [
|
||||||
asyncio.create_task(set_gpu_stats(config, stats, hwaccel_errors)),
|
asyncio.create_task(set_gpu_stats(config, stats, hwaccel_errors)),
|
||||||
asyncio.create_task(set_cpu_stats(stats)),
|
asyncio.create_task(set_cpu_stats(stats)),
|
||||||
|
asyncio.create_task(set_npu_usages(config, stats)),
|
||||||
]
|
]
|
||||||
|
|
||||||
if config.telemetry.stats.network_bandwidth:
|
if config.telemetry.stats.network_bandwidth:
|
||||||
@ -238,6 +240,19 @@ async def set_gpu_stats(
|
|||||||
all_stats["gpu_usages"] = stats
|
all_stats["gpu_usages"] = stats
|
||||||
|
|
||||||
|
|
||||||
|
async def set_npu_usages(config: FrigateConfig, all_stats: dict[str, Any]) -> None:
|
||||||
|
stats: dict[str, dict] = {}
|
||||||
|
|
||||||
|
for detector in config.detectors.values():
|
||||||
|
if detector.type == "rknn":
|
||||||
|
# Rockchip NPU usage
|
||||||
|
rk_usage = get_rockchip_npu_stats()
|
||||||
|
stats["rockchip"] = rk_usage
|
||||||
|
|
||||||
|
if stats:
|
||||||
|
all_stats["npu_usages"] = stats
|
||||||
|
|
||||||
|
|
||||||
def stats_snapshot(
|
def stats_snapshot(
|
||||||
config: FrigateConfig, stats_tracking: StatsTrackingTypes, hwaccel_errors: list[str]
|
config: FrigateConfig, stats_tracking: StatsTrackingTypes, hwaccel_errors: list[str]
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
|||||||
@ -382,6 +382,23 @@ def get_intel_gpu_stats(sriov: bool) -> dict[str, str]:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def get_rockchip_npu_stats() -> dict[str, str]:
|
||||||
|
"""Get stats using rk."""
|
||||||
|
try:
|
||||||
|
with open("/sys/kernel/debug/rknpu/load", "r") as f:
|
||||||
|
npu_output = f.read()
|
||||||
|
core_loads = re.findall(r"Core\d+:\s*(\d+)%", npu_output)
|
||||||
|
except FileNotFoundError:
|
||||||
|
core_loads = None
|
||||||
|
|
||||||
|
if not core_loads:
|
||||||
|
return None
|
||||||
|
|
||||||
|
percentages = [int(load) for load in core_loads]
|
||||||
|
mean = round(sum(percentages) / len(percentages), 2)
|
||||||
|
return {"npu": mean, "mem": "-"}
|
||||||
|
|
||||||
|
|
||||||
def try_get_info(f, h, default="N/A"):
|
def try_get_info(f, h, default="N/A"):
|
||||||
try:
|
try:
|
||||||
if h:
|
if h:
|
||||||
|
|||||||
@ -54,7 +54,12 @@
|
|||||||
},
|
},
|
||||||
"gone": "{{label}} left",
|
"gone": "{{label}} left",
|
||||||
"heard": "{{label}} heard",
|
"heard": "{{label}} heard",
|
||||||
"external": "{{label}} detected"
|
"external": "{{label}} detected",
|
||||||
|
"header": {
|
||||||
|
"zones": "Zones",
|
||||||
|
"ratio": "Ratio",
|
||||||
|
"area": "Area"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"annotationSettings": {
|
"annotationSettings": {
|
||||||
"title": "Annotation Settings",
|
"title": "Annotation Settings",
|
||||||
|
|||||||
@ -72,7 +72,9 @@
|
|||||||
"toast": {
|
"toast": {
|
||||||
"success": "Copied GPU info to clipboard"
|
"success": "Copied GPU info to clipboard"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"npuUsage": "NPU Usage",
|
||||||
|
"npuMemory": "NPU Memory"
|
||||||
},
|
},
|
||||||
"otherProcesses": {
|
"otherProcesses": {
|
||||||
"title": "Other Processes",
|
"title": "Other Processes",
|
||||||
@ -142,7 +144,10 @@
|
|||||||
"ffmpegHighCpuUsage": "{{camera}} has high FFMPEG CPU usage ({{ffmpegAvg}}%)",
|
"ffmpegHighCpuUsage": "{{camera}} has high FFMPEG CPU usage ({{ffmpegAvg}}%)",
|
||||||
"detectHighCpuUsage": "{{camera}} has high detect CPU usage ({{detectAvg}}%)",
|
"detectHighCpuUsage": "{{camera}} has high detect CPU usage ({{detectAvg}}%)",
|
||||||
"healthy": "System is healthy",
|
"healthy": "System is healthy",
|
||||||
"reindexingEmbeddings": "Reindexing embeddings ({{processed}}% complete)"
|
"reindexingEmbeddings": "Reindexing embeddings ({{processed}}% complete)",
|
||||||
|
"cameraIsOffline": "{{camera}} is offline",
|
||||||
|
"detectIsSlow": "{{detect}} is slow ({{speed}} ms)",
|
||||||
|
"detectIsVerySlow": "{{detect}} is very slow ({{speed}} ms)"
|
||||||
},
|
},
|
||||||
"enrichments": {
|
"enrichments": {
|
||||||
"title": "Enrichments",
|
"title": "Enrichments",
|
||||||
@ -150,6 +155,7 @@
|
|||||||
"embeddings": {
|
"embeddings": {
|
||||||
"image_embedding_speed": "Image Embedding Speed",
|
"image_embedding_speed": "Image Embedding Speed",
|
||||||
"face_embedding_speed": "Face Embedding Speed",
|
"face_embedding_speed": "Face Embedding Speed",
|
||||||
|
"face_recognition_speed": "Face Recognition Speed",
|
||||||
"plate_recognition_speed": "Plate Recognition Speed",
|
"plate_recognition_speed": "Plate Recognition Speed",
|
||||||
"text_embedding_speed": "Text Embedding Speed"
|
"text_embedding_speed": "Text Embedding Speed"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -498,7 +498,7 @@ export function GeneralFilterContent({
|
|||||||
{allLabels.map((item) => (
|
{allLabels.map((item) => (
|
||||||
<FilterSwitch
|
<FilterSwitch
|
||||||
key={item}
|
key={item}
|
||||||
label={item.replaceAll("_", " ")}
|
label={t(item, { ns: "objects" })}
|
||||||
isChecked={filter.labels?.includes(item) ?? false}
|
isChecked={filter.labels?.includes(item) ?? false}
|
||||||
onCheckedChange={(isChecked) => {
|
onCheckedChange={(isChecked) => {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
|
|||||||
@ -263,7 +263,9 @@ function GeneralFilterButton({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedLabels.length == 1) {
|
if (selectedLabels.length == 1) {
|
||||||
return t(selectedLabels[0], { ns: "objects" });
|
return t(selectedLabels[0], {
|
||||||
|
ns: "objects",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return t("labels.count", {
|
return t("labels.count", {
|
||||||
|
|||||||
@ -419,7 +419,9 @@ export default function InputWithTags({
|
|||||||
? t("button.yes", { ns: "common" })
|
? t("button.yes", { ns: "common" })
|
||||||
: t("button.no", { ns: "common" });
|
: t("button.no", { ns: "common" });
|
||||||
} else if (filterType === "labels") {
|
} else if (filterType === "labels") {
|
||||||
return t(filterValues as string, { ns: "objects" });
|
return t(filterValues as string, {
|
||||||
|
ns: "objects",
|
||||||
|
});
|
||||||
} else if (filterType === "search_type") {
|
} else if (filterType === "search_type") {
|
||||||
return t("filter.searchType." + (filterValues as string));
|
return t("filter.searchType." + (filterValues as string));
|
||||||
} else {
|
} else {
|
||||||
@ -817,7 +819,11 @@ export default function InputWithTags({
|
|||||||
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800"
|
className="inline-flex items-center whitespace-nowrap rounded-full bg-green-100 px-2 py-0.5 text-sm capitalize text-green-800"
|
||||||
>
|
>
|
||||||
{t("filter.label." + filterType)}:{" "}
|
{t("filter.label." + filterType)}:{" "}
|
||||||
{value.replaceAll("_", " ")}
|
{filterType === "labels"
|
||||||
|
? t(value, {
|
||||||
|
ns: "objects",
|
||||||
|
})
|
||||||
|
: value.replaceAll("_", " ")}
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
removeFilter(filterType as FilterType, value)
|
removeFilter(filterType as FilterType, value)
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { Link } from "react-router-dom";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { isIOS, isMobile } from "react-device-detect";
|
import { isIOS, isMobile } from "react-device-detect";
|
||||||
import { isPWA } from "@/utils/isPWA";
|
import { isPWA } from "@/utils/isPWA";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
function Bottombar() {
|
function Bottombar() {
|
||||||
const navItems = useNavigation("secondary");
|
const navItems = useNavigation("secondary");
|
||||||
@ -43,6 +44,7 @@ type StatusAlertNavProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
function StatusAlertNav({ className }: StatusAlertNavProps) {
|
function StatusAlertNav({ className }: StatusAlertNavProps) {
|
||||||
|
const { t } = useTranslation(["views/system"]);
|
||||||
const { data: initialStats } = useSWR<FrigateStats>("stats", {
|
const { data: initialStats } = useSWR<FrigateStats>("stats", {
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
});
|
});
|
||||||
@ -82,14 +84,19 @@ function StatusAlertNav({ className }: StatusAlertNavProps) {
|
|||||||
clearMessages("embeddings-reindex");
|
clearMessages("embeddings-reindex");
|
||||||
addMessage(
|
addMessage(
|
||||||
"embeddings-reindex",
|
"embeddings-reindex",
|
||||||
`Reindexing embeddings (${Math.floor((reindexState.processed_objects / reindexState.total_objects) * 100)}% complete)`,
|
t("stats.reindexingEmbeddings", {
|
||||||
|
processed: Math.floor(
|
||||||
|
(reindexState.processed_objects / reindexState.total_objects) *
|
||||||
|
100,
|
||||||
|
),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (reindexState.status === "completed") {
|
if (reindexState.status === "completed") {
|
||||||
clearMessages("embeddings-reindex");
|
clearMessages("embeddings-reindex");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [reindexState, addMessage, clearMessages]);
|
}, [reindexState, addMessage, clearMessages, t]);
|
||||||
|
|
||||||
if (!messages || Object.keys(messages).length === 0) {
|
if (!messages || Object.keys(messages).length === 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -596,7 +596,9 @@ export default function ObjectLifecycle({
|
|||||||
<div className="text-md mr-2 w-1/3">
|
<div className="text-md mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-start">
|
<div className="flex flex-col items-end justify-start">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
Zones
|
{t(
|
||||||
|
"objectLifecycle.lifecycleItemDesc.header.zones",
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
{item.class_type === "entered_zone"
|
{item.class_type === "entered_zone"
|
||||||
? item.data.zones.map((zone, index) => (
|
? item.data.zones.map((zone, index) => (
|
||||||
@ -627,7 +629,9 @@ export default function ObjectLifecycle({
|
|||||||
<div className="text-md mr-2 w-1/3">
|
<div className="text-md mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-start">
|
<div className="flex flex-col items-end justify-start">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
Ratio
|
{t(
|
||||||
|
"objectLifecycle.lifecycleItemDesc.header.ratio",
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
{Array.isArray(item.data.box) &&
|
{Array.isArray(item.data.box) &&
|
||||||
item.data.box.length >= 4
|
item.data.box.length >= 4
|
||||||
@ -641,7 +645,7 @@ export default function ObjectLifecycle({
|
|||||||
<div className="text-md mr-2 w-1/3">
|
<div className="text-md mr-2 w-1/3">
|
||||||
<div className="flex flex-col items-end justify-start">
|
<div className="flex flex-col items-end justify-start">
|
||||||
<p className="mb-1.5 text-sm text-primary-variant">
|
<p className="mb-1.5 text-sm text-primary-variant">
|
||||||
Area
|
{t("objectLifecycle.lifecycleItemDesc.header.area")}
|
||||||
</p>
|
</p>
|
||||||
{Array.isArray(item.data.box) &&
|
{Array.isArray(item.data.box) &&
|
||||||
item.data.box.length >= 4 ? (
|
item.data.box.length >= 4 ? (
|
||||||
|
|||||||
@ -320,7 +320,11 @@ export default function ReviewDetailDialog({
|
|||||||
ns="views/explore"
|
ns="views/explore"
|
||||||
values={{
|
values={{
|
||||||
objects: missingObjects
|
objects: missingObjects
|
||||||
.map((x) => t(x, { ns: "objects" }))
|
.map((x) =>
|
||||||
|
t(x, {
|
||||||
|
ns: "objects",
|
||||||
|
}),
|
||||||
|
)
|
||||||
.join(", "),
|
.join(", "),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -709,7 +709,9 @@ function ObjectDetailsTab({
|
|||||||
<div className="text-sm text-primary/40">{t("details.label")}</div>
|
<div className="text-sm text-primary/40">{t("details.label")}</div>
|
||||||
<div className="flex flex-row items-center gap-2 text-sm capitalize">
|
<div className="flex flex-row items-center gap-2 text-sm capitalize">
|
||||||
{getIconForLabel(search.label, "size-4 text-primary")}
|
{getIconForLabel(search.label, "size-4 text-primary")}
|
||||||
{t(search.label, { ns: "objects" })}
|
{t(search.label, {
|
||||||
|
ns: "objects",
|
||||||
|
})}
|
||||||
{search.sub_label && ` (${search.sub_label})`}
|
{search.sub_label && ` (${search.sub_label})`}
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@ -980,7 +982,7 @@ function ObjectDetailsTab({
|
|||||||
description={
|
description={
|
||||||
search.label
|
search.label
|
||||||
? t("details.editSubLabel.desc", {
|
? t("details.editSubLabel.desc", {
|
||||||
label: t(search.label, { ns: "objects" }),
|
label: search.label,
|
||||||
})
|
})
|
||||||
: t("details.editSubLabel.descNoLabel")
|
: t("details.editSubLabel.descNoLabel")
|
||||||
}
|
}
|
||||||
@ -995,7 +997,7 @@ function ObjectDetailsTab({
|
|||||||
description={
|
description={
|
||||||
search.label
|
search.label
|
||||||
? t("details.editLPR.desc", {
|
? t("details.editLPR.desc", {
|
||||||
label: t(search.label, { ns: "objects" }),
|
label: search.label,
|
||||||
})
|
})
|
||||||
: t("details.editLPR.descNoLabel")
|
: t("details.editLPR.descNoLabel")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,13 +36,19 @@ export default function useStats(stats: FrigateStats | undefined) {
|
|||||||
Object.entries(memoizedStats["detectors"]).forEach(([key, det]) => {
|
Object.entries(memoizedStats["detectors"]).forEach(([key, det]) => {
|
||||||
if (det["inference_speed"] > InferenceThreshold.error) {
|
if (det["inference_speed"] > InferenceThreshold.error) {
|
||||||
problems.push({
|
problems.push({
|
||||||
text: `${capitalizeFirstLetter(key)} is very slow (${det["inference_speed"]} ms)`,
|
text: t("stats.detectIsVerySlow", {
|
||||||
|
detect: capitalizeFirstLetter(key),
|
||||||
|
speed: det["inference_speed"],
|
||||||
|
}),
|
||||||
color: "text-danger",
|
color: "text-danger",
|
||||||
relevantLink: "/system#general",
|
relevantLink: "/system#general",
|
||||||
});
|
});
|
||||||
} else if (det["inference_speed"] > InferenceThreshold.warning) {
|
} else if (det["inference_speed"] > InferenceThreshold.warning) {
|
||||||
problems.push({
|
problems.push({
|
||||||
text: `${capitalizeFirstLetter(key)} is slow (${det["inference_speed"]} ms)`,
|
text: t("stats.detectIsSlow", {
|
||||||
|
detect: capitalizeFirstLetter(key),
|
||||||
|
speed: det["inference_speed"],
|
||||||
|
}),
|
||||||
color: "text-orange-400",
|
color: "text-orange-400",
|
||||||
relevantLink: "/system#general",
|
relevantLink: "/system#general",
|
||||||
});
|
});
|
||||||
@ -57,7 +63,9 @@ export default function useStats(stats: FrigateStats | undefined) {
|
|||||||
|
|
||||||
if (config.cameras[name].enabled && cam["camera_fps"] == 0) {
|
if (config.cameras[name].enabled && cam["camera_fps"] == 0) {
|
||||||
problems.push({
|
problems.push({
|
||||||
text: `${capitalizeFirstLetter(name.replaceAll("_", " "))} is offline`,
|
text: t("stats.cameraIsOffline", {
|
||||||
|
camera: capitalizeFirstLetter(name.replaceAll("_", " ")),
|
||||||
|
}),
|
||||||
color: "text-danger",
|
color: "text-danger",
|
||||||
relevantLink: "logs",
|
relevantLink: "logs",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export interface FrigateStats {
|
|||||||
detectors: { [detectorKey: string]: DetectorStats };
|
detectors: { [detectorKey: string]: DetectorStats };
|
||||||
embeddings?: EmbeddingsStats;
|
embeddings?: EmbeddingsStats;
|
||||||
gpu_usages?: { [gpuKey: string]: GpuStats };
|
gpu_usages?: { [gpuKey: string]: GpuStats };
|
||||||
|
npu_usages?: { [npuKey: string]: NpuStats };
|
||||||
processes: { [processKey: string]: ExtraProcessStats };
|
processes: { [processKey: string]: ExtraProcessStats };
|
||||||
service: ServiceStats;
|
service: ServiceStats;
|
||||||
detection_fps: number;
|
detection_fps: number;
|
||||||
@ -54,6 +55,11 @@ export type GpuStats = {
|
|||||||
pstate?: string;
|
pstate?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NpuStats = {
|
||||||
|
npu: number;
|
||||||
|
mem: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type GpuInfo = "vainfo" | "nvinfo";
|
export type GpuInfo = "vainfo" | "nvinfo";
|
||||||
|
|
||||||
export type ServiceStats = {
|
export type ServiceStats = {
|
||||||
|
|||||||
@ -5,11 +5,16 @@ export function getLifecycleItemDescription(
|
|||||||
lifecycleItem: ObjectLifecycleSequence,
|
lifecycleItem: ObjectLifecycleSequence,
|
||||||
) {
|
) {
|
||||||
// can't use useTranslation here
|
// can't use useTranslation here
|
||||||
const label = (
|
const label = t(
|
||||||
(Array.isArray(lifecycleItem.data.sub_label)
|
(
|
||||||
? lifecycleItem.data.sub_label[0]
|
(Array.isArray(lifecycleItem.data.sub_label)
|
||||||
: lifecycleItem.data.sub_label) || lifecycleItem.data.label
|
? lifecycleItem.data.sub_label[0]
|
||||||
).replaceAll("_", " ");
|
: lifecycleItem.data.sub_label) || lifecycleItem.data.label
|
||||||
|
)
|
||||||
|
.replace(" ", "_")
|
||||||
|
.toLowerCase(),
|
||||||
|
{ ns: "objects" },
|
||||||
|
);
|
||||||
|
|
||||||
switch (lifecycleItem.class_type) {
|
switch (lifecycleItem.class_type) {
|
||||||
case "visible":
|
case "visible":
|
||||||
|
|||||||
@ -336,7 +336,9 @@ export default function ClassificationSettingsView({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-20">
|
<SelectTrigger className="w-20">
|
||||||
{classificationSettings.search.model_size}
|
{t(
|
||||||
|
`classification.semanticSearch.modelSize.${classificationSettings.search.model_size}.title`,
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
@ -466,7 +468,9 @@ export default function ClassificationSettingsView({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-20">
|
<SelectTrigger className="w-20">
|
||||||
{classificationSettings.face.model_size}
|
{t(
|
||||||
|
`classification.faceRecognition.modelSize.${classificationSettings.face.model_size}.title`,
|
||||||
|
)}
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
|
|||||||
@ -366,7 +366,7 @@ function ObjectList({ cameraConfig, objects }: ObjectListProps) {
|
|||||||
{getIconForLabel(obj.label, "size-5 text-white")}
|
{getIconForLabel(obj.label, "size-5 text-white")}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 text-lg">
|
<div className="ml-3 text-lg">
|
||||||
{capitalizeFirstLetter(obj.label.replaceAll("_", " "))}
|
{t(obj.label, { ns: "objects" })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-8/12 flex-row items-center justify-end">
|
<div className="flex w-8/12 flex-row items-center justify-end">
|
||||||
|
|||||||
@ -77,7 +77,10 @@ export default function EnrichmentMetrics({
|
|||||||
const key = rawKey.replaceAll("_", " ");
|
const key = rawKey.replaceAll("_", " ");
|
||||||
|
|
||||||
if (!(key in series)) {
|
if (!(key in series)) {
|
||||||
series[key] = { name: t("features.embeddings." + rawKey), data: [] };
|
series[key] = {
|
||||||
|
name: t("enrichments.embeddings." + rawKey),
|
||||||
|
data: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
series[key].data.push({ x: statsIdx + 1, y: stat });
|
series[key].data.push({ x: statsIdx + 1, y: stat });
|
||||||
@ -90,7 +93,7 @@ export default function EnrichmentMetrics({
|
|||||||
<>
|
<>
|
||||||
<div className="scrollbar-container mt-4 flex size-full flex-col overflow-y-auto">
|
<div className="scrollbar-container mt-4 flex size-full flex-col overflow-y-auto">
|
||||||
<div className="text-sm font-medium text-muted-foreground">
|
<div className="text-sm font-medium text-muted-foreground">
|
||||||
{t("features.title")}
|
{t("enrichments.title")}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export default function GeneralMetrics({
|
|||||||
const { data: initialStats } = useSWR<FrigateStats[]>(
|
const { data: initialStats } = useSWR<FrigateStats[]>(
|
||||||
[
|
[
|
||||||
"stats/history",
|
"stats/history",
|
||||||
{ keys: "cpu_usages,detectors,gpu_usages,processes,service" },
|
{ keys: "cpu_usages,detectors,gpu_usages,npu_usages,processes,service" },
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@ -369,8 +369,57 @@ export default function GeneralMetrics({
|
|||||||
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
|
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
|
||||||
}, [statsHistory]);
|
}, [statsHistory]);
|
||||||
|
|
||||||
|
// npu stats
|
||||||
|
|
||||||
|
const npuSeries = useMemo(() => {
|
||||||
|
if (!statsHistory) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const series: {
|
||||||
|
[key: string]: { name: string; data: { x: number; y: number }[] };
|
||||||
|
} = {};
|
||||||
|
let hasValidNpu = false;
|
||||||
|
|
||||||
|
statsHistory.forEach((stats, statsIdx) => {
|
||||||
|
if (!stats) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.entries(stats.npu_usages || []).forEach(([key, stats]) => {
|
||||||
|
if (!(key in series)) {
|
||||||
|
series[key] = { name: key, data: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.npu) {
|
||||||
|
hasValidNpu = true;
|
||||||
|
series[key].data.push({ x: statsIdx + 1, y: stats.npu });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasValidNpu) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(series).length > 0 ? Object.values(series) : [];
|
||||||
|
}, [statsHistory]);
|
||||||
|
|
||||||
// other processes stats
|
// other processes stats
|
||||||
|
|
||||||
|
const hardwareType = useMemo(() => {
|
||||||
|
const hasGpu = gpuSeries.length > 0;
|
||||||
|
const hasNpu = npuSeries.length > 0;
|
||||||
|
|
||||||
|
if (hasGpu && !hasNpu) {
|
||||||
|
return "GPUs";
|
||||||
|
} else if (!hasGpu && hasNpu) {
|
||||||
|
return "NPUs";
|
||||||
|
} else {
|
||||||
|
return "GPUs / NPUs";
|
||||||
|
}
|
||||||
|
}, [gpuSeries, npuSeries]);
|
||||||
|
|
||||||
const otherProcessCpuSeries = useMemo(() => {
|
const otherProcessCpuSeries = useMemo(() => {
|
||||||
if (!statsHistory) {
|
if (!statsHistory) {
|
||||||
return [];
|
return [];
|
||||||
@ -533,11 +582,13 @@ export default function GeneralMetrics({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(statsHistory.length == 0 || statsHistory[0].gpu_usages) && (
|
{(statsHistory.length == 0 ||
|
||||||
|
statsHistory[0].gpu_usages ||
|
||||||
|
statsHistory[0].npu_usages) && (
|
||||||
<>
|
<>
|
||||||
<div className="mt-4 flex items-center justify-between">
|
<div className="mt-4 flex items-center justify-between">
|
||||||
<div className="text-sm font-medium text-muted-foreground">
|
<div className="text-sm font-medium text-muted-foreground">
|
||||||
GPUs
|
{hardwareType}
|
||||||
</div>
|
</div>
|
||||||
{canGetGpuInfo && (
|
{canGetGpuInfo && (
|
||||||
<Button
|
<Button
|
||||||
@ -556,97 +607,127 @@ export default function GeneralMetrics({
|
|||||||
gpuEncSeries?.length && "md:grid-cols-4",
|
gpuEncSeries?.length && "md:grid-cols-4",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{statsHistory.length != 0 ? (
|
{statsHistory[0]?.gpu_usages && (
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
<>
|
||||||
<div className="mb-5">
|
{statsHistory.length != 0 ? (
|
||||||
{t("general.hardwareInfo.gpuUsage")}
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
</div>
|
<div className="mb-5">
|
||||||
{gpuSeries.map((series) => (
|
{t("general.hardwareInfo.gpuUsage")}
|
||||||
<ThresholdBarGraph
|
</div>
|
||||||
key={series.name}
|
{gpuSeries.map((series) => (
|
||||||
graphId={`${series.name}-gpu`}
|
<ThresholdBarGraph
|
||||||
name={series.name}
|
key={series.name}
|
||||||
unit="%"
|
graphId={`${series.name}-gpu`}
|
||||||
threshold={GPUUsageThreshold}
|
name={series.name}
|
||||||
updateTimes={updateTimes}
|
unit="%"
|
||||||
data={[series]}
|
threshold={GPUUsageThreshold}
|
||||||
/>
|
updateTimes={updateTimes}
|
||||||
))}
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<>
|
||||||
|
{gpuMemSeries && (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.gpuMemory")}
|
||||||
|
</div>
|
||||||
|
{gpuMemSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-mem`}
|
||||||
|
unit="%"
|
||||||
|
name={series.name}
|
||||||
|
threshold={GPUMemThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<>
|
||||||
|
{gpuEncSeries && gpuEncSeries?.length != 0 && (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.gpuEncoder")}
|
||||||
|
</div>
|
||||||
|
{gpuEncSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-enc`}
|
||||||
|
unit="%"
|
||||||
|
name={series.name}
|
||||||
|
threshold={GPUMemThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<>
|
||||||
|
{gpuDecSeries && gpuDecSeries?.length != 0 && (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.gpuDecoder")}
|
||||||
|
</div>
|
||||||
|
{gpuDecSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-dec`}
|
||||||
|
unit="%"
|
||||||
|
name={series.name}
|
||||||
|
threshold={GPUMemThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{statsHistory[0]?.npu_usages && (
|
||||||
|
<div
|
||||||
|
className={cn("mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2")}
|
||||||
|
>
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.npuUsage")}
|
||||||
|
</div>
|
||||||
|
{npuSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-npu`}
|
||||||
|
name={series.name}
|
||||||
|
unit="%"
|
||||||
|
threshold={GPUUsageThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
|
||||||
{statsHistory.length != 0 ? (
|
|
||||||
<>
|
|
||||||
{gpuMemSeries && (
|
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
|
||||||
<div className="mb-5">
|
|
||||||
{t("general.hardwareInfo.gpuMemory")}
|
|
||||||
</div>
|
|
||||||
{gpuMemSeries.map((series) => (
|
|
||||||
<ThresholdBarGraph
|
|
||||||
key={series.name}
|
|
||||||
graphId={`${series.name}-mem`}
|
|
||||||
unit="%"
|
|
||||||
name={series.name}
|
|
||||||
threshold={GPUMemThreshold}
|
|
||||||
updateTimes={updateTimes}
|
|
||||||
data={[series]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
|
||||||
{statsHistory.length != 0 ? (
|
|
||||||
<>
|
|
||||||
{gpuEncSeries && gpuEncSeries?.length != 0 && (
|
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
|
||||||
<div className="mb-5">
|
|
||||||
{t("general.hardwareInfo.gpuEncoder")}
|
|
||||||
</div>
|
|
||||||
{gpuEncSeries.map((series) => (
|
|
||||||
<ThresholdBarGraph
|
|
||||||
key={series.name}
|
|
||||||
graphId={`${series.name}-enc`}
|
|
||||||
unit="%"
|
|
||||||
name={series.name}
|
|
||||||
threshold={GPUMemThreshold}
|
|
||||||
updateTimes={updateTimes}
|
|
||||||
data={[series]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
|
||||||
{statsHistory.length != 0 ? (
|
|
||||||
<>
|
|
||||||
{gpuDecSeries && gpuDecSeries?.length != 0 && (
|
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
|
||||||
<div className="mb-5">
|
|
||||||
{t("general.hardwareInfo.gpuDecoder")}
|
|
||||||
</div>
|
|
||||||
{gpuDecSeries.map((series) => (
|
|
||||||
<ThresholdBarGraph
|
|
||||||
key={series.name}
|
|
||||||
graphId={`${series.name}-dec`}
|
|
||||||
unit="%"
|
|
||||||
name={series.name}
|
|
||||||
threshold={GPUMemThreshold}
|
|
||||||
updateTimes={updateTimes}
|
|
||||||
data={[series]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user