mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 21:44:13 +03:00
Still create all classes
We stil need to create all classes even if the user didn't assign images to them.
This commit is contained in:
parent
397d4e5b49
commit
5919b56ffb
@ -870,6 +870,46 @@ def categorize_classification_image(request: Request, name: str, body: dict = No
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/classification/{name}/dataset/{category}/create",
|
||||||
|
response_model=GenericResponse,
|
||||||
|
dependencies=[Depends(require_role(["admin"]))],
|
||||||
|
summary="Create an empty classification category folder",
|
||||||
|
description="""Creates an empty folder for a classification category.
|
||||||
|
This is used to create folders for categories that don't have images yet.
|
||||||
|
Returns a success message or an error if the name is invalid.""",
|
||||||
|
)
|
||||||
|
def create_classification_category(request: Request, name: str, category: str):
|
||||||
|
config: FrigateConfig = request.app.frigate_config
|
||||||
|
|
||||||
|
if name not in config.classification.custom:
|
||||||
|
return JSONResponse(
|
||||||
|
content=(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"message": f"{name} is not a known classification model.",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
status_code=404,
|
||||||
|
)
|
||||||
|
|
||||||
|
category_folder = os.path.join(
|
||||||
|
CLIPS_DIR, sanitize_filename(name), "dataset", sanitize_filename(category)
|
||||||
|
)
|
||||||
|
|
||||||
|
os.makedirs(category_folder, exist_ok=True)
|
||||||
|
|
||||||
|
return JSONResponse(
|
||||||
|
content=(
|
||||||
|
{
|
||||||
|
"success": True,
|
||||||
|
"message": f"Successfully created category folder: {category}",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
status_code=200,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/classification/{name}/train/delete",
|
"/classification/{name}/train/delete",
|
||||||
response_model=GenericResponse,
|
response_model=GenericResponse,
|
||||||
|
|||||||
@ -166,6 +166,7 @@
|
|||||||
"noImages": "No sample images generated",
|
"noImages": "No sample images generated",
|
||||||
"classifying": "Classifying & Training...",
|
"classifying": "Classifying & Training...",
|
||||||
"trainingStarted": "Training started successfully",
|
"trainingStarted": "Training started successfully",
|
||||||
|
"modelCreated": "Model created successfully. Use the Recent Classifications view to add images for missing states, then train the model.",
|
||||||
"errors": {
|
"errors": {
|
||||||
"noCameras": "No cameras configured",
|
"noCameras": "No cameras configured",
|
||||||
"noObjectLabel": "No object label selected",
|
"noObjectLabel": "No object label selected",
|
||||||
|
|||||||
@ -141,15 +141,49 @@ export default function Step3ChooseExamples({
|
|||||||
);
|
);
|
||||||
await Promise.all(categorizePromises);
|
await Promise.all(categorizePromises);
|
||||||
|
|
||||||
// Step 3: Kick off training
|
// Step 2.5: Create empty folders for classes that don't have any images
|
||||||
|
// This ensures all classes are available in the dataset view later
|
||||||
|
const classesWithImages = new Set(
|
||||||
|
Object.values(classifications).filter((c) => c && c !== "none"),
|
||||||
|
);
|
||||||
|
const emptyFolderPromises = step1Data.classes
|
||||||
|
.filter((className) => !classesWithImages.has(className))
|
||||||
|
.map((className) =>
|
||||||
|
axios.post(
|
||||||
|
`/classification/${step1Data.modelName}/dataset/${className}/create`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await Promise.all(emptyFolderPromises);
|
||||||
|
|
||||||
|
// Step 3: Determine if we should train
|
||||||
|
// For state models, we need ALL states to have examples
|
||||||
|
// For object models, we need at least 2 classes with images
|
||||||
|
const allStatesHaveExamplesForTraining =
|
||||||
|
step1Data.modelType !== "state" ||
|
||||||
|
step1Data.classes.every((className) =>
|
||||||
|
classesWithImages.has(className),
|
||||||
|
);
|
||||||
|
const shouldTrain =
|
||||||
|
allStatesHaveExamplesForTraining && classesWithImages.size >= 2;
|
||||||
|
|
||||||
|
// Step 4: Kick off training only if we have enough classes with images
|
||||||
|
if (shouldTrain) {
|
||||||
await axios.post(`/classification/${step1Data.modelName}/train`);
|
await axios.post(`/classification/${step1Data.modelName}/train`);
|
||||||
|
|
||||||
toast.success(t("wizard.step3.trainingStarted"), {
|
toast.success(t("wizard.step3.trainingStarted"), {
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
});
|
});
|
||||||
setIsTraining(true);
|
setIsTraining(true);
|
||||||
|
} else {
|
||||||
|
// Don't train - not all states have examples
|
||||||
|
toast.success(t("wizard.step3.modelCreated"), {
|
||||||
|
closeButton: true,
|
||||||
|
});
|
||||||
|
setIsTraining(false);
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[step1Data, step2Data, t],
|
[step1Data, step2Data, t, onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleContinueClassification = useCallback(async () => {
|
const handleContinueClassification = useCallback(async () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user