mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-09-10 17:51:45 +02:00
UI improvements (#17684)
* Add sub label to explore chip * Add bird classification to settings * Remove score * Cleanup
This commit is contained in:
parent
4abf945b71
commit
ea98785097
@ -83,6 +83,10 @@
|
|||||||
},
|
},
|
||||||
"classification": {
|
"classification": {
|
||||||
"title": "Classification Settings",
|
"title": "Classification Settings",
|
||||||
|
"birdClassification": {
|
||||||
|
"title": "Bird Classification",
|
||||||
|
"desc": "Bird classification identifies known birds using a quantized Tensorflow model. When a known bird is recognized, its common name will be added as a sub_label. This information is included in the UI, filters, as well as in notifications."
|
||||||
|
},
|
||||||
"semanticSearch": {
|
"semanticSearch": {
|
||||||
"title": "Semantic Search",
|
"title": "Semantic Search",
|
||||||
"desc": "Semantic Search in Frigate allows you to find tracked objects within your review items using either the image itself, a user-defined text description, or an automatically generated one.",
|
"desc": "Semantic Search in Frigate allows you to find tracked objects within your review items using either the image itself, a user-defined text description, or an automatically generated one.",
|
||||||
|
@ -69,6 +69,30 @@ export default function SearchThumbnail({
|
|||||||
return `${searchResult.label}-verified`;
|
return `${searchResult.label}-verified`;
|
||||||
}, [config, hasRecognizedPlate, searchResult]);
|
}, [config, hasRecognizedPlate, searchResult]);
|
||||||
|
|
||||||
|
const objectDetail = useMemo(() => {
|
||||||
|
if (!config) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!searchResult.sub_label) {
|
||||||
|
if (hasRecognizedPlate) {
|
||||||
|
return `(${searchResult.data.recognized_license_plate})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
config.model.attributes_map[searchResult.label]?.includes(
|
||||||
|
searchResult.sub_label,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `(${searchResult.sub_label})`;
|
||||||
|
}, [config, hasRecognizedPlate, searchResult]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="relative size-full cursor-pointer"
|
className="relative size-full cursor-pointer"
|
||||||
@ -107,7 +131,7 @@ export default function SearchThumbnail({
|
|||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<div className="mx-3 pb-1 text-sm text-white">
|
<div className="mx-3 pb-1 text-sm text-white">
|
||||||
<Chip
|
<Chip
|
||||||
className={`z-0 flex items-center justify-between gap-1 space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs`}
|
className={`z-0 flex items-center justify-between gap-1 space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize`}
|
||||||
onClick={() => onClick(searchResult, false, true)}
|
onClick={() => onClick(searchResult, false, true)}
|
||||||
>
|
>
|
||||||
{getIconForLabel(objectLabel, "size-3 text-white")}
|
{getIconForLabel(objectLabel, "size-3 text-white")}
|
||||||
@ -116,7 +140,7 @@ export default function SearchThumbnail({
|
|||||||
searchResult.data.top_score ??
|
searchResult.data.top_score ??
|
||||||
searchResult.top_score) * 100,
|
searchResult.top_score) * 100,
|
||||||
)}
|
)}
|
||||||
%
|
% {objectDetail}
|
||||||
</Chip>
|
</Chip>
|
||||||
</div>
|
</div>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
@ -298,6 +298,13 @@ export interface FrigateConfig {
|
|||||||
[cameraName: string]: CameraConfig;
|
[cameraName: string]: CameraConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
classification: {
|
||||||
|
bird: {
|
||||||
|
enabled: boolean;
|
||||||
|
threshold: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
database: {
|
database: {
|
||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
|
@ -45,6 +45,9 @@ type ClassificationSettings = {
|
|||||||
lpr: {
|
lpr: {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
};
|
};
|
||||||
|
bird: {
|
||||||
|
enabled?: boolean;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type ClassificationSettingsViewProps = {
|
type ClassificationSettingsViewProps = {
|
||||||
@ -67,6 +70,7 @@ export default function ClassificationSettingsView({
|
|||||||
search: { enabled: undefined, model_size: undefined },
|
search: { enabled: undefined, model_size: undefined },
|
||||||
face: { enabled: undefined, model_size: undefined },
|
face: { enabled: undefined, model_size: undefined },
|
||||||
lpr: { enabled: undefined },
|
lpr: { enabled: undefined },
|
||||||
|
bird: { enabled: undefined },
|
||||||
});
|
});
|
||||||
|
|
||||||
const [origSearchSettings, setOrigSearchSettings] =
|
const [origSearchSettings, setOrigSearchSettings] =
|
||||||
@ -74,6 +78,7 @@ export default function ClassificationSettingsView({
|
|||||||
search: { enabled: undefined, model_size: undefined },
|
search: { enabled: undefined, model_size: undefined },
|
||||||
face: { enabled: undefined, model_size: undefined },
|
face: { enabled: undefined, model_size: undefined },
|
||||||
lpr: { enabled: undefined },
|
lpr: { enabled: undefined },
|
||||||
|
bird: { enabled: undefined },
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -89,6 +94,9 @@ export default function ClassificationSettingsView({
|
|||||||
model_size: config.face_recognition.model_size,
|
model_size: config.face_recognition.model_size,
|
||||||
},
|
},
|
||||||
lpr: { enabled: config.lpr.enabled },
|
lpr: { enabled: config.lpr.enabled },
|
||||||
|
bird: {
|
||||||
|
enabled: config.classification.bird.enabled,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +110,7 @@ export default function ClassificationSettingsView({
|
|||||||
model_size: config.face_recognition.model_size,
|
model_size: config.face_recognition.model_size,
|
||||||
},
|
},
|
||||||
lpr: { enabled: config.lpr.enabled },
|
lpr: { enabled: config.lpr.enabled },
|
||||||
|
bird: { enabled: config.classification.bird.enabled },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// we know that these deps are correct
|
// we know that these deps are correct
|
||||||
@ -115,6 +124,7 @@ export default function ClassificationSettingsView({
|
|||||||
search: { ...prevConfig.search, ...newConfig.search },
|
search: { ...prevConfig.search, ...newConfig.search },
|
||||||
face: { ...prevConfig.face, ...newConfig.face },
|
face: { ...prevConfig.face, ...newConfig.face },
|
||||||
lpr: { ...prevConfig.lpr, ...newConfig.lpr },
|
lpr: { ...prevConfig.lpr, ...newConfig.lpr },
|
||||||
|
bird: { ...prevConfig.bird, ...newConfig.bird },
|
||||||
}));
|
}));
|
||||||
setUnsavedChanges(true);
|
setUnsavedChanges(true);
|
||||||
setChangedValue(true);
|
setChangedValue(true);
|
||||||
@ -125,7 +135,7 @@ export default function ClassificationSettingsView({
|
|||||||
|
|
||||||
axios
|
axios
|
||||||
.put(
|
.put(
|
||||||
`config/set?semantic_search.enabled=${classificationSettings.search.enabled ? "True" : "False"}&semantic_search.model_size=${classificationSettings.search.model_size}&face_recognition.enabled=${classificationSettings.face.enabled ? "True" : "False"}&face_recognition.model_size=${classificationSettings.face.model_size}&lpr.enabled=${classificationSettings.lpr.enabled ? "True" : "False"}`,
|
`config/set?semantic_search.enabled=${classificationSettings.search.enabled ? "True" : "False"}&semantic_search.model_size=${classificationSettings.search.model_size}&face_recognition.enabled=${classificationSettings.face.enabled ? "True" : "False"}&face_recognition.model_size=${classificationSettings.face.model_size}&lpr.enabled=${classificationSettings.lpr.enabled ? "True" : "False"}&classification.bird.enabled=${classificationSettings.bird.enabled ? "True" : "False"}`,
|
||||||
{ requires_restart: 0 },
|
{ requires_restart: 0 },
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@ -526,6 +536,50 @@ export default function ClassificationSettingsView({
|
|||||||
|
|
||||||
<Separator className="my-2 flex bg-secondary" />
|
<Separator className="my-2 flex bg-secondary" />
|
||||||
|
|
||||||
|
<Heading as="h4" className="my-2">
|
||||||
|
{t("classification.birdClassification.title")}
|
||||||
|
</Heading>
|
||||||
|
<div className="max-w-6xl">
|
||||||
|
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-primary-variant">
|
||||||
|
<p>{t("classification.birdClassification.desc")}</p>
|
||||||
|
|
||||||
|
<div className="flex items-center text-primary">
|
||||||
|
<Link
|
||||||
|
to="https://docs.frigate.video/configuration/license_plate_recognition"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline"
|
||||||
|
>
|
||||||
|
{t("classification.semanticSearch.readTheDocumentation")}
|
||||||
|
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex w-full max-w-lg flex-col space-y-6">
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<Switch
|
||||||
|
id="enabled"
|
||||||
|
className="mr-3"
|
||||||
|
disabled={classificationSettings.bird.enabled === undefined}
|
||||||
|
checked={classificationSettings.bird.enabled === true}
|
||||||
|
onCheckedChange={(isChecked) => {
|
||||||
|
handleClassificationConfigChange({
|
||||||
|
bird: { enabled: isChecked },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="enabled">
|
||||||
|
{t("button.enabled", { ns: "common" })}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator className="my-2 flex bg-secondary" />
|
||||||
|
|
||||||
<div className="flex w-full flex-row items-center gap-2 pt-2 md:w-[25%]">
|
<div className="flex w-full flex-row items-center gap-2 pt-2 md:w-[25%]">
|
||||||
<Button
|
<Button
|
||||||
className="flex flex-1"
|
className="flex flex-1"
|
||||||
|
Loading…
Reference in New Issue
Block a user