mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-01 19:17:41 +03:00
Implement viewing of images
This commit is contained in:
parent
d014922a99
commit
3420de88be
@ -435,7 +435,7 @@ def transcribe_audio(request: Request, body: AudioTranscriptionBody):
|
|||||||
def get_classification_dataset(name: str):
|
def get_classification_dataset(name: str):
|
||||||
dataset_dict: dict[str, list[str]] = {}
|
dataset_dict: dict[str, list[str]] = {}
|
||||||
|
|
||||||
dataset_dir = os.path.join(MODEL_CACHE_DIR, f"{sanitize_filename(name)}/dataset")
|
dataset_dir = os.path.join(CLIPS_DIR, sanitize_filename(name), "dataset")
|
||||||
|
|
||||||
if not os.path.exists(dataset_dir):
|
if not os.path.exists(dataset_dir):
|
||||||
return JSONResponse(status_code=200, content={})
|
return JSONResponse(status_code=200, content={})
|
||||||
@ -459,7 +459,7 @@ def get_classification_dataset(name: str):
|
|||||||
|
|
||||||
@router.get("/classification/{name}/train")
|
@router.get("/classification/{name}/train")
|
||||||
def get_classification_images(name: str):
|
def get_classification_images(name: str):
|
||||||
train_dir = os.path.join(CLIPS_DIR, sanitize_filename(name))
|
train_dir = os.path.join(CLIPS_DIR, sanitize_filename(name), "train")
|
||||||
|
|
||||||
if not os.path.exists(train_dir):
|
if not os.path.exists(train_dir):
|
||||||
return JSONResponse(status_code=200, content=[])
|
return JSONResponse(status_code=200, content=[])
|
||||||
@ -492,9 +492,7 @@ async def train_configured_model(
|
|||||||
status_code=404,
|
status_code=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
background_tasks.add_task(
|
background_tasks.add_task(train_classification_model, name)
|
||||||
train_classification_model, os.path.join(MODEL_CACHE_DIR, name)
|
|
||||||
)
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content={"success": True, "message": "Started classification model training."},
|
content={"success": True, "message": "Started classification model training."},
|
||||||
status_code=200,
|
status_code=200,
|
||||||
|
|||||||
@ -38,12 +38,10 @@ export default function ModelSelectionView({
|
|||||||
<div className="flex size-full gap-2 p-2">
|
<div className="flex size-full gap-2 p-2">
|
||||||
{classificationConfigs.map((config) => (
|
{classificationConfigs.map((config) => (
|
||||||
<div
|
<div
|
||||||
|
key={config.name}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-52 cursor-pointer flex-col gap-2 rounded-lg bg-card p-2 outline outline-[3px]",
|
"flex h-52 cursor-pointer flex-col gap-2 rounded-lg bg-card p-2 outline outline-[3px]",
|
||||||
isMobile && "w-full",
|
isMobile && "w-full",
|
||||||
false
|
|
||||||
? "shadow-selected outline-selected"
|
|
||||||
: "outline-transparent duration-500",
|
|
||||||
)}
|
)}
|
||||||
onClick={() => onClick(config)}
|
onClick={() => onClick(config)}
|
||||||
onContextMenu={() => {
|
onContextMenu={() => {
|
||||||
|
|||||||
@ -40,8 +40,12 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
|||||||
|
|
||||||
// dataset
|
// dataset
|
||||||
|
|
||||||
const { data: trainImages } = useSWR(`classification/${model.name}/train`);
|
const { data: trainImages } = useSWR<string[]>(
|
||||||
const { data: dataset } = useSWR(`classification/${model.name}/dataset`);
|
`classification/${model.name}/train`,
|
||||||
|
);
|
||||||
|
const { data: dataset } = useSWR<{ [id: string]: string[] }>(
|
||||||
|
`classification/${model.name}/dataset`,
|
||||||
|
);
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
|
|
||||||
@ -54,8 +58,8 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
|||||||
<div className="flex flex-row justify-between gap-2 align-middle">
|
<div className="flex flex-row justify-between gap-2 align-middle">
|
||||||
<LibrarySelector
|
<LibrarySelector
|
||||||
pageToggle={pageToggle}
|
pageToggle={pageToggle}
|
||||||
dataset={dataset}
|
dataset={dataset || {}}
|
||||||
trainImages={trainImages}
|
trainImages={trainImages || []}
|
||||||
setPageToggle={setPageToggle}
|
setPageToggle={setPageToggle}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
||||||
onRename={() => {}}
|
onRename={() => {}}
|
||||||
@ -65,13 +69,18 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) {
|
|||||||
{pageToggle == "train" ? (
|
{pageToggle == "train" ? (
|
||||||
<TrainGrid
|
<TrainGrid
|
||||||
model={model}
|
model={model}
|
||||||
trainImages={trainImages}
|
trainImages={trainImages || []}
|
||||||
selected={false}
|
selected={false}
|
||||||
onClickImages={() => {}}
|
onClickImages={() => {}}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DatasetGrid />
|
<DatasetGrid
|
||||||
|
modelName={model.name}
|
||||||
|
categoryName={pageToggle}
|
||||||
|
images={dataset?.[pageToggle] || []}
|
||||||
|
onDelete={() => {}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -259,51 +268,65 @@ function LibrarySelector({
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DatasetGridProps = {
|
type DatasetGridProps = {
|
||||||
name: string;
|
modelName: string;
|
||||||
|
categoryName: string;
|
||||||
images: string[];
|
images: string[];
|
||||||
onDelete: (name: string, ids: string[]) => void;
|
onDelete: (modelName: string, categoryName: string, ids: string[]) => void;
|
||||||
};
|
};
|
||||||
function DatasetGrid({ name, images, onDelete }: DatasetGridProps) {
|
function DatasetGrid({
|
||||||
|
modelName,
|
||||||
|
categoryName,
|
||||||
|
images,
|
||||||
|
onDelete,
|
||||||
|
}: DatasetGridProps) {
|
||||||
const { t } = useTranslation(["views/classificationModel"]);
|
const { t } = useTranslation(["views/classificationModel"]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="grid grid-cols-10 gap-2 overflow-y-auto">
|
||||||
className={cn(
|
{images.map((image) => (
|
||||||
"flex cursor-pointer flex-col gap-2 rounded-lg bg-card outline outline-[3px]",
|
<div
|
||||||
)}
|
className={cn(
|
||||||
>
|
"flex h-60 cursor-pointer flex-col gap-2 rounded-lg bg-card outline outline-[3px]",
|
||||||
<div
|
"outline-transparent duration-500",
|
||||||
className={cn(
|
)}
|
||||||
"w-full overflow-hidden p-2 *:text-card-foreground",
|
onClick={() => {
|
||||||
isMobile && "flex justify-center",
|
//e.stopPropagation();
|
||||||
)}
|
//onClickImages([data.raw], e.ctrlKey || e.metaKey);
|
||||||
>
|
}}
|
||||||
<img
|
>
|
||||||
className="h-40 rounded-lg"
|
<div
|
||||||
src={`${baseUrl}clips/faces/${name}/${images}`}
|
className={cn(
|
||||||
/>
|
"w-full overflow-hidden p-2 *:text-card-foreground",
|
||||||
</div>
|
isMobile && "flex justify-center",
|
||||||
<div className="rounded-b-lg bg-card p-3">
|
)}
|
||||||
<div className="flex w-full flex-row items-center justify-between gap-2">
|
>
|
||||||
<div className="flex flex-col items-start text-xs text-primary-variant">
|
<img
|
||||||
<div className="smart-capitalize">{name}</div>
|
className="rounded-lg"
|
||||||
|
src={`${baseUrl}clips/${modelName}/dataset/${categoryName}/${image}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-start justify-end gap-5 md:gap-4">
|
<div className="rounded-b-lg bg-card p-3">
|
||||||
<Tooltip>
|
<div className="flex w-full flex-row items-center justify-between gap-2">
|
||||||
<TooltipTrigger>
|
<div className="flex w-full flex-row items-start justify-end gap-5 md:gap-4">
|
||||||
<LuTrash2
|
<Tooltip>
|
||||||
className="size-5 cursor-pointer text-primary-variant hover:text-primary"
|
<TooltipTrigger>
|
||||||
onClick={(e) => {
|
<LuTrash2
|
||||||
e.stopPropagation();
|
className="size-5 cursor-pointer text-primary-variant hover:text-primary"
|
||||||
onDelete(name, images);
|
onClick={(e) => {
|
||||||
}}
|
e.stopPropagation();
|
||||||
/>
|
onDelete(modelName, categoryName, [image]);
|
||||||
</TooltipTrigger>
|
}}
|
||||||
<TooltipContent>{t("button.deleteFaceAttempts")}</TooltipContent>
|
/>
|
||||||
</Tooltip>
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
{t("button.deleteClassificationAttempts")}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -339,9 +362,10 @@ function TrainGrid({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid size-full grid-cols-10 gap-2 overflow-y-auto">
|
<div className="grid grid-cols-10 gap-2 overflow-y-auto">
|
||||||
{trainData.map((data) => (
|
{trainData?.map((data) => (
|
||||||
<div
|
<div
|
||||||
|
key={data.timestamp}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex cursor-pointer flex-col gap-2 rounded-lg bg-card outline outline-[3px]",
|
"flex cursor-pointer flex-col gap-2 rounded-lg bg-card outline outline-[3px]",
|
||||||
selected
|
selected
|
||||||
@ -361,7 +385,7 @@ function TrainGrid({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="h-48 rounded-lg"
|
className="h-48 rounded-lg"
|
||||||
src={`${baseUrl}clips/${model.name}/${data.raw}`}
|
src={`${baseUrl}clips/${model.name}/train/${data.raw}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-b-lg bg-card p-3">
|
<div className="rounded-b-lg bg-card p-3">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user