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