mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 13:34:13 +03:00
Update to support correct data format
This commit is contained in:
parent
dc19dd5495
commit
e5a2e9637b
@ -28,6 +28,7 @@ import {
|
|||||||
CustomClassificationModelConfig,
|
CustomClassificationModelConfig,
|
||||||
FrigateConfig,
|
FrigateConfig,
|
||||||
} from "@/types/frigateConfig";
|
} from "@/types/frigateConfig";
|
||||||
|
import { ClassificationDatasetResponse } from "@/types/classification";
|
||||||
import { getTranslatedLabel } from "@/utils/i18n";
|
import { getTranslatedLabel } from "@/utils/i18n";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -140,16 +141,19 @@ export default function ClassificationModelEditDialog({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Fetch dataset to get current classes for state models
|
// Fetch dataset to get current classes for state models
|
||||||
const { data: dataset } = useSWR<{
|
const { data: dataset } = useSWR<ClassificationDatasetResponse>(
|
||||||
[id: string]: string[];
|
isStateModel ? `classification/${model.name}/dataset` : null,
|
||||||
}>(isStateModel ? `classification/${model.name}/dataset` : null, {
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Update form with classes from dataset when loaded
|
// Update form with classes from dataset when loaded
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isStateModel && dataset) {
|
if (isStateModel && dataset?.categories) {
|
||||||
const classes = Object.keys(dataset).filter((key) => key !== "none");
|
const classes = Object.keys(dataset.categories).filter(
|
||||||
|
(key) => key !== "none",
|
||||||
|
);
|
||||||
if (classes.length > 0) {
|
if (classes.length > 0) {
|
||||||
(form as ReturnType<typeof useForm<StateFormData>>).setValue(
|
(form as ReturnType<typeof useForm<StateFormData>>).setValue(
|
||||||
"classes",
|
"classes",
|
||||||
|
|||||||
@ -20,3 +20,17 @@ export type ClassificationThreshold = {
|
|||||||
recognition: number;
|
recognition: number;
|
||||||
unknown: number;
|
unknown: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ClassificationDatasetResponse = {
|
||||||
|
categories: {
|
||||||
|
[id: string]: string[];
|
||||||
|
};
|
||||||
|
training_metadata: {
|
||||||
|
has_trained: boolean;
|
||||||
|
last_training_date: string | null;
|
||||||
|
last_training_image_count: number;
|
||||||
|
current_image_count: number;
|
||||||
|
new_images_count: number;
|
||||||
|
dataset_changed: boolean;
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
CustomClassificationModelConfig,
|
CustomClassificationModelConfig,
|
||||||
FrigateConfig,
|
FrigateConfig,
|
||||||
} from "@/types/frigateConfig";
|
} from "@/types/frigateConfig";
|
||||||
|
import { ClassificationDatasetResponse } from "@/types/classification";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaFolderPlus } from "react-icons/fa";
|
import { FaFolderPlus } from "react-icons/fa";
|
||||||
@ -209,9 +210,10 @@ type ModelCardProps = {
|
|||||||
function ModelCard({ config, onClick, onUpdate, onDelete }: ModelCardProps) {
|
function ModelCard({ config, onClick, onUpdate, onDelete }: ModelCardProps) {
|
||||||
const { t } = useTranslation(["views/classificationModel"]);
|
const { t } = useTranslation(["views/classificationModel"]);
|
||||||
|
|
||||||
const { data: dataset } = useSWR<{
|
const { data: dataset } = useSWR<ClassificationDatasetResponse>(
|
||||||
[id: string]: string[];
|
`classification/${config.name}/dataset`,
|
||||||
}>(`classification/${config.name}/dataset`, { revalidateOnFocus: false });
|
{ revalidateOnFocus: false },
|
||||||
|
);
|
||||||
|
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
||||||
@ -260,20 +262,25 @@ function ModelCard({ config, onClick, onUpdate, onDelete }: ModelCardProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const coverImage = useMemo(() => {
|
const coverImage = useMemo(() => {
|
||||||
if (!dataset) {
|
if (!dataset || !dataset.categories) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(dataset).filter((key) => key != "none");
|
const keys = Object.keys(dataset.categories).filter((key) => key != "none");
|
||||||
const selectedKey = keys[0];
|
if (keys.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (!dataset[selectedKey]) {
|
const selectedKey = keys[0];
|
||||||
|
const images = dataset.categories[selectedKey];
|
||||||
|
|
||||||
|
if (!images || images.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: selectedKey,
|
name: selectedKey,
|
||||||
img: dataset[selectedKey][0],
|
img: images[0],
|
||||||
};
|
};
|
||||||
}, [dataset]);
|
}, [dataset]);
|
||||||
|
|
||||||
@ -317,11 +324,19 @@ function ModelCard({ config, onClick, onUpdate, onDelete }: ModelCardProps) {
|
|||||||
)}
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
|
{coverImage ? (
|
||||||
|
<>
|
||||||
<img
|
<img
|
||||||
className="size-full"
|
className="size-full"
|
||||||
src={`${baseUrl}clips/${config.name}/dataset/${coverImage?.name}/${coverImage?.img}`}
|
src={`${baseUrl}clips/${config.name}/dataset/${coverImage.name}/${coverImage.img}`}
|
||||||
/>
|
/>
|
||||||
<ImageShadowOverlay lowerClassName="h-[30%] z-0" />
|
<ImageShadowOverlay lowerClassName="h-[30%] z-0" />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="flex size-full items-center justify-center bg-background_alt">
|
||||||
|
<MdModelTraining className="size-16 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="absolute bottom-2 left-3 text-lg text-white smart-capitalize">
|
<div className="absolute bottom-2 left-3 text-lg text-white smart-capitalize">
|
||||||
{config.name}
|
{config.name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -59,7 +59,11 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { IoMdArrowRoundBack } from "react-icons/io";
|
import { IoMdArrowRoundBack } from "react-icons/io";
|
||||||
import TrainFilterDialog from "@/components/overlay/dialog/TrainFilterDialog";
|
import TrainFilterDialog from "@/components/overlay/dialog/TrainFilterDialog";
|
||||||
import useApiFilter from "@/hooks/use-api-filter";
|
import useApiFilter from "@/hooks/use-api-filter";
|
||||||
import { ClassificationItemData, TrainFilter } from "@/types/classification";
|
import {
|
||||||
|
ClassificationDatasetResponse,
|
||||||
|
ClassificationItemData,
|
||||||
|
TrainFilter,
|
||||||
|
} from "@/types/classification";
|
||||||
import {
|
import {
|
||||||
ClassificationCard,
|
ClassificationCard,
|
||||||
GroupedClassificationCard,
|
GroupedClassificationCard,
|
||||||
@ -118,17 +122,10 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
|||||||
const { data: trainImages, mutate: refreshTrain } = useSWR<string[]>(
|
const { data: trainImages, mutate: refreshTrain } = useSWR<string[]>(
|
||||||
`classification/${model.name}/train`,
|
`classification/${model.name}/train`,
|
||||||
);
|
);
|
||||||
const { data: datasetResponse, mutate: refreshDataset } = useSWR<{
|
const { data: datasetResponse, mutate: refreshDataset } =
|
||||||
categories: { [id: string]: string[] };
|
useSWR<ClassificationDatasetResponse>(
|
||||||
training_metadata: {
|
`classification/${model.name}/dataset`,
|
||||||
has_trained: boolean;
|
);
|
||||||
last_training_date: string | null;
|
|
||||||
last_training_image_count: number;
|
|
||||||
current_image_count: number;
|
|
||||||
new_images_count: number;
|
|
||||||
dataset_changed: boolean;
|
|
||||||
} | null;
|
|
||||||
}>(`classification/${model.name}/dataset`);
|
|
||||||
|
|
||||||
const dataset = datasetResponse?.categories || {};
|
const dataset = datasetResponse?.categories || {};
|
||||||
const trainingMetadata = datasetResponse?.training_metadata;
|
const trainingMetadata = datasetResponse?.training_metadata;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user