mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-09 16:47:37 +03:00
Fix classification/face batch actions and snapshot handling
This commit is contained in:
parent
d175c9758e
commit
ae6d6ce2e5
@ -970,6 +970,17 @@ def categorize_classification_image(request: Request, name: str, body: dict = No
|
||||
|
||||
snapshot = get_event_snapshot(event)
|
||||
|
||||
if snapshot is None:
|
||||
return JSONResponse(
|
||||
content=(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"Failed to read snapshot for event {event_id}.",
|
||||
}
|
||||
),
|
||||
status_code=500,
|
||||
)
|
||||
|
||||
# Get object bounding box for the first detection
|
||||
if not event.data.get("attributes") or len(event.data["attributes"]) == 0:
|
||||
return JSONResponse(
|
||||
@ -987,19 +998,7 @@ def categorize_classification_image(request: Request, name: str, body: dict = No
|
||||
|
||||
try:
|
||||
# Extract the crop from the snapshot
|
||||
detect_config: DetectConfig = config.cameras[event.camera].detect
|
||||
frame = cv2.imread(snapshot)
|
||||
|
||||
if frame is None:
|
||||
return JSONResponse(
|
||||
content=(
|
||||
{
|
||||
"success": False,
|
||||
"message": f"Failed to read snapshot for event {event_id}.",
|
||||
}
|
||||
),
|
||||
status_code=500,
|
||||
)
|
||||
frame = snapshot
|
||||
|
||||
height, width = frame.shape[:2]
|
||||
|
||||
|
||||
@ -410,73 +410,53 @@ export default function FaceLibrary() {
|
||||
<FaceSelectionDialog
|
||||
faceNames={faces}
|
||||
onTrainAttempt={(name) => {
|
||||
// Batch train all selected faces
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
const totalCount = selectedFaces.length;
|
||||
|
||||
selectedFaces.forEach((filename, index) => {
|
||||
const requests = selectedFaces.map((filename) =>
|
||||
axios
|
||||
.post(`/faces/train/${name}/classify`, {
|
||||
training_file: filename,
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.status == 200) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
.then(() => true)
|
||||
.catch(() => false),
|
||||
);
|
||||
|
||||
// Show final toast after all requests complete
|
||||
if (index === totalCount - 1) {
|
||||
if (successCount === totalCount) {
|
||||
toast.success(
|
||||
t("toast.success.batchTrainedFaces", {
|
||||
count: successCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
} else if (successCount > 0) {
|
||||
toast.warning(
|
||||
t("toast.warning.partialBatchTrained", {
|
||||
success: successCount,
|
||||
total: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toast.error(
|
||||
t("toast.error.batchTrainFailed", {
|
||||
count: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
}
|
||||
setSelectedFaces([]);
|
||||
refreshFaces();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
failCount++;
|
||||
if (index === totalCount - 1) {
|
||||
toast.error(
|
||||
t("toast.error.batchTrainFailed", {
|
||||
count: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
setSelectedFaces([]);
|
||||
refreshFaces();
|
||||
}
|
||||
});
|
||||
Promise.allSettled(requests).then((results) => {
|
||||
const successCount = results.filter(
|
||||
(result) => result.status === "fulfilled" && result.value,
|
||||
).length;
|
||||
const totalCount = results.length;
|
||||
|
||||
if (successCount === totalCount) {
|
||||
toast.success(
|
||||
t("toast.success.batchTrainedFaces", {
|
||||
count: successCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
} else if (successCount > 0) {
|
||||
toast.warning(
|
||||
t("toast.warning.partialBatchTrained", {
|
||||
success: successCount,
|
||||
total: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toast.error(
|
||||
t("toast.error.batchTrainFailed", {
|
||||
count: totalCount,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setSelectedFaces([]);
|
||||
refreshFaces();
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@ -460,79 +460,62 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
||||
</div>
|
||||
{pageToggle === "train" && (
|
||||
<ClassificationSelectionDialog
|
||||
classes={classes}
|
||||
classes={Object.keys(dataset || {})}
|
||||
modelName={model.name}
|
||||
image={selectedImages[0]}
|
||||
onRefresh={refreshAll}
|
||||
onCategorize={(category) => {
|
||||
// Batch categorize all selected images
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
const totalCount = selectedImages.length;
|
||||
|
||||
selectedImages.forEach((filename, index) => {
|
||||
const requests = selectedImages.map((filename) =>
|
||||
axios
|
||||
.post(`/classification/${model.name}/dataset/categorize`, {
|
||||
category,
|
||||
training_file: filename,
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.status == 200) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
.post(
|
||||
`/classification/${model.name}/dataset/categorize`,
|
||||
{
|
||||
category,
|
||||
training_file: filename,
|
||||
},
|
||||
)
|
||||
.then(() => true)
|
||||
.catch(() => false),
|
||||
);
|
||||
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
Promise.allSettled(requests).then((results) => {
|
||||
const successCount = results.filter(
|
||||
(result) => result.status === "fulfilled" && result.value,
|
||||
).length;
|
||||
const totalCount = results.length;
|
||||
|
||||
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();
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user