mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-10 00:57:38 +03:00
Updated understanding: events support multiple classification models
Co-authored-by: Teagan42 <2989925+Teagan42@users.noreply.github.com>
This commit is contained in:
parent
8f8e9badd3
commit
220f3e1e7e
@ -35,6 +35,7 @@ type ClassificationSelectionDialogProps = {
|
|||||||
modelName: string;
|
modelName: string;
|
||||||
image: string;
|
image: string;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
|
onCategorize?: (category: string) => void; // Optional custom categorize handler
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
export default function ClassificationSelectionDialog({
|
export default function ClassificationSelectionDialog({
|
||||||
@ -43,12 +44,20 @@ export default function ClassificationSelectionDialog({
|
|||||||
modelName,
|
modelName,
|
||||||
image,
|
image,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
|
onCategorize,
|
||||||
children,
|
children,
|
||||||
}: ClassificationSelectionDialogProps) {
|
}: ClassificationSelectionDialogProps) {
|
||||||
const { t } = useTranslation(["views/classificationModel"]);
|
const { t } = useTranslation(["views/classificationModel"]);
|
||||||
|
|
||||||
const onCategorizeImage = useCallback(
|
const onCategorizeImage = useCallback(
|
||||||
(category: string) => {
|
(category: string) => {
|
||||||
|
// If custom categorize handler is provided, use it instead
|
||||||
|
if (onCategorize) {
|
||||||
|
onCategorize(category);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default behavior: categorize single image
|
||||||
axios
|
axios
|
||||||
.post(`/classification/${modelName}/dataset/categorize`, {
|
.post(`/classification/${modelName}/dataset/categorize`, {
|
||||||
category,
|
category,
|
||||||
@ -72,7 +81,7 @@ export default function ClassificationSelectionDialog({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[modelName, image, onRefresh, t],
|
[modelName, image, onRefresh, onCategorize, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isChildButton = useMemo(
|
const isChildButton = useMemo(
|
||||||
|
|||||||
@ -458,6 +458,89 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{pageToggle === "train" && (
|
||||||
|
<ClassificationSelectionDialog
|
||||||
|
classes={classes}
|
||||||
|
modelName={model.name}
|
||||||
|
image={selectedImages[0]}
|
||||||
|
onRefresh={(category: string) => {
|
||||||
|
// Batch categorize all selected images
|
||||||
|
let successCount = 0;
|
||||||
|
let failCount = 0;
|
||||||
|
const totalCount = selectedImages.length;
|
||||||
|
|
||||||
|
selectedImages.forEach((filename, index) => {
|
||||||
|
axios
|
||||||
|
.post(`/classification/${model.name}/dataset/categorize`, {
|
||||||
|
category,
|
||||||
|
training_file: filename,
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.status == 200) {
|
||||||
|
successCount++;
|
||||||
|
} else {
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show final toast after all requests complete
|
||||||
|
if (index === totalCount - 1) {
|
||||||
|
if (successCount === totalCount) {
|
||||||
|
toast.success(
|
||||||
|
t("toast.success.batchCategorized", {
|
||||||
|
count: successCount,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
position: "top-center",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (successCount > 0) {
|
||||||
|
toast.warning(
|
||||||
|
t("toast.warning.partialBatchCategorized", {
|
||||||
|
success: successCount,
|
||||||
|
total: totalCount,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
position: "top-center",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toast.error(
|
||||||
|
t("toast.error.batchCategorizeFailed", {
|
||||||
|
count: totalCount,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
position: "top-center",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setSelectedImages([]);
|
||||||
|
refreshAll();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
failCount++;
|
||||||
|
if (index === totalCount - 1) {
|
||||||
|
toast.error(
|
||||||
|
t("toast.error.batchCategorizeFailed", {
|
||||||
|
count: totalCount,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
position: "top-center",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setSelectedImages([]);
|
||||||
|
refreshAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button className="flex gap-2">
|
||||||
|
<TbCategoryPlus className="size-7 rounded-md p-1 text-secondary-foreground" />
|
||||||
|
{isDesktop && t("button.categorizeImages")}
|
||||||
|
</Button>
|
||||||
|
</ClassificationSelectionDialog>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
className="flex gap-2"
|
className="flex gap-2"
|
||||||
onClick={() => setDeleteDialogOpen(selectedImages)}
|
onClick={() => setDeleteDialogOpen(selectedImages)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user