mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-06 05:27:44 +03:00
Handle event id saving in API
This commit is contained in:
parent
d27e2ff54f
commit
60bea4debc
@ -6,6 +6,7 @@ import random
|
|||||||
import shutil
|
import shutil
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
import cv2
|
||||||
from fastapi import APIRouter, Depends, Request, UploadFile
|
from fastapi import APIRouter, Depends, Request, UploadFile
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from pathvalidate import sanitize_filename
|
from pathvalidate import sanitize_filename
|
||||||
@ -14,9 +15,11 @@ from playhouse.shortcuts import model_to_dict
|
|||||||
|
|
||||||
from frigate.api.auth import require_role
|
from frigate.api.auth import require_role
|
||||||
from frigate.api.defs.tags import Tags
|
from frigate.api.defs.tags import Tags
|
||||||
|
from frigate.config.camera import DetectConfig
|
||||||
from frigate.const import FACE_DIR
|
from frigate.const import FACE_DIR
|
||||||
from frigate.embeddings import EmbeddingsContext
|
from frigate.embeddings import EmbeddingsContext
|
||||||
from frigate.models import Event
|
from frigate.models import Event
|
||||||
|
from frigate.util.path import get_event_snapshot
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -87,16 +90,27 @@ def train_face(request: Request, name: str, body: dict = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
json: dict[str, any] = body or {}
|
json: dict[str, any] = body or {}
|
||||||
training_file = os.path.join(
|
training_file_name = sanitize_filename(json.get("training_file", ""))
|
||||||
FACE_DIR, f"train/{sanitize_filename(json.get('training_file', ''))}"
|
training_file = os.path.join(FACE_DIR, f"train/{training_file_name}")
|
||||||
)
|
event_id = json.get("event_id")
|
||||||
|
|
||||||
if not training_file or not os.path.isfile(training_file):
|
if not training_file_name and not event_id:
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=(
|
content=(
|
||||||
{
|
{
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": f"Invalid filename or no file exists: {training_file}",
|
"message": "A training file or event_id must be passed.",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
status_code=400,
|
||||||
|
)
|
||||||
|
|
||||||
|
if training_file_name and not os.path.isfile(training_file):
|
||||||
|
return JSONResponse(
|
||||||
|
content=(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"message": f"Invalid filename or no file exists: {training_file_name}",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
status_code=404,
|
status_code=404,
|
||||||
@ -106,7 +120,36 @@ def train_face(request: Request, name: str, body: dict = None):
|
|||||||
rand_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
rand_id = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
||||||
new_name = f"{sanitized_name}-{rand_id}.webp"
|
new_name = f"{sanitized_name}-{rand_id}.webp"
|
||||||
new_file = os.path.join(FACE_DIR, f"{sanitized_name}/{new_name}")
|
new_file = os.path.join(FACE_DIR, f"{sanitized_name}/{new_name}")
|
||||||
|
|
||||||
|
if training_file_name:
|
||||||
shutil.move(training_file, new_file)
|
shutil.move(training_file, new_file)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
event: Event = Event.get(Event.id == event_id)
|
||||||
|
except DoesNotExist:
|
||||||
|
return JSONResponse(
|
||||||
|
content=(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"message": f"Invalid event_id or no event exists: {event_id}",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
status_code=404,
|
||||||
|
)
|
||||||
|
|
||||||
|
snapshot = get_event_snapshot(event)
|
||||||
|
face_box = event.data["attributes"][0]["box"]
|
||||||
|
detect_config: DetectConfig = request.app.frigate_config.cameras[
|
||||||
|
event.camera
|
||||||
|
].detect
|
||||||
|
|
||||||
|
# crop onto the face box minus the bounding box itself
|
||||||
|
x1 = int(face_box[0] * detect_config.width) + 2
|
||||||
|
y1 = int(face_box[1] * detect_config.height) + 2
|
||||||
|
x2 = x1 + int(face_box[2] * detect_config.width) - 4
|
||||||
|
y2 = y1 + int(face_box[3] * detect_config.height) - 4
|
||||||
|
face = snapshot[y1:y2, x1:x2]
|
||||||
|
cv2.imwrite(new_file, face)
|
||||||
|
|
||||||
context: EmbeddingsContext = request.app.embeddings
|
context: EmbeddingsContext = request.app.embeddings
|
||||||
context.clear_face_classifier()
|
context.clear_face_classifier()
|
||||||
@ -115,7 +158,7 @@ def train_face(request: Request, name: str, body: dict = None):
|
|||||||
content=(
|
content=(
|
||||||
{
|
{
|
||||||
"success": True,
|
"success": True,
|
||||||
"message": f"Successfully saved {training_file} as {new_name}.",
|
"message": f"Successfully saved {training_file_name} as {new_name}.",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
status_code=200,
|
status_code=200,
|
||||||
|
|||||||
@ -4,6 +4,9 @@ import base64
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
from numpy import ndarray
|
||||||
|
|
||||||
from frigate.const import CLIPS_DIR, THUMB_DIR
|
from frigate.const import CLIPS_DIR, THUMB_DIR
|
||||||
from frigate.models import Event
|
from frigate.models import Event
|
||||||
|
|
||||||
@ -21,6 +24,11 @@ def get_event_thumbnail_bytes(event: Event) -> bytes | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_event_snapshot(event: Event) -> ndarray:
|
||||||
|
media_name = f"{event.camera}-{event.id}"
|
||||||
|
return cv2.imread(f"{os.path.join(CLIPS_DIR, media_name)}.jpg")
|
||||||
|
|
||||||
|
|
||||||
### Deletion
|
### Deletion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -564,7 +564,7 @@ function ObjectDetailsTab({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return search.data.attributes.find((attr) => attr.label == "face");
|
return search.data.attributes?.find((attr) => attr.label == "face");
|
||||||
}, [config, search]);
|
}, [config, search]);
|
||||||
|
|
||||||
const { data: faceData } = useSWR(hasFace ? "faces" : null);
|
const { data: faceData } = useSWR(hasFace ? "faces" : null);
|
||||||
|
|||||||
@ -50,7 +50,7 @@ export type SearchResult = {
|
|||||||
score: number;
|
score: number;
|
||||||
sub_label_score?: number;
|
sub_label_score?: number;
|
||||||
region: number[];
|
region: number[];
|
||||||
attributes: [{ box: number[]; label: string; score: number }];
|
attributes?: [{ box: number[]; label: string; score: number }];
|
||||||
box: number[];
|
box: number[];
|
||||||
area: number;
|
area: number;
|
||||||
ratio: number;
|
ratio: number;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user