mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-26 13:47:03 +02:00
Face recognize api (#17233)
* Add api to run face recognition on image * Rework save attempts option * Cleanup mobile object pane buttons * Adjust api signature * Remove param * Cleanup
This commit is contained in:
parent
7f966df5a4
commit
e33fa96599
@ -201,6 +201,22 @@ async def register_face(request: Request, name: str, file: UploadFile):
|
||||
)
|
||||
|
||||
|
||||
@router.post("/faces/recognize")
|
||||
async def recognize_face(request: Request, file: UploadFile):
|
||||
if not request.app.frigate_config.face_recognition.enabled:
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={"message": "Face recognition is not enabled.", "success": False},
|
||||
)
|
||||
|
||||
context: EmbeddingsContext = request.app.embeddings
|
||||
result = context.recognize_face(await file.read())
|
||||
return JSONResponse(
|
||||
status_code=200 if result.get("success", True) else 400,
|
||||
content=result,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/faces/{name}/delete", dependencies=[Depends(require_role(["admin"]))])
|
||||
def deregister_faces(request: Request, name: str, body: dict = None):
|
||||
if not request.app.frigate_config.face_recognition.enabled:
|
||||
|
@ -13,6 +13,7 @@ class EmbeddingsRequestEnum(Enum):
|
||||
embed_description = "embed_description"
|
||||
embed_thumbnail = "embed_thumbnail"
|
||||
generate_search = "generate_search"
|
||||
recognize_face = "recognize_face"
|
||||
register_face = "register_face"
|
||||
reprocess_face = "reprocess_face"
|
||||
reprocess_plate = "reprocess_plate"
|
||||
|
@ -70,8 +70,8 @@ class FaceRecognitionConfig(FrigateBaseModel):
|
||||
min_area: int = Field(
|
||||
default=500, title="Min area of face box to consider running face recognition."
|
||||
)
|
||||
save_attempts: bool = Field(
|
||||
default=True, title="Save images of face detections for training."
|
||||
save_attempts: int = Field(
|
||||
default=100, ge=0, title="Number of face attempts to save in the train tab."
|
||||
)
|
||||
blur_confidence_filter: bool = Field(
|
||||
default=True, title="Apply blur quality filter to face confidence."
|
||||
|
@ -28,7 +28,6 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MAX_DETECTION_HEIGHT = 1080
|
||||
MAX_FACE_ATTEMPTS = 100
|
||||
MIN_MATCHING_FACES = 2
|
||||
|
||||
|
||||
@ -407,6 +406,28 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
||||
def handle_request(self, topic, request_data) -> dict[str, any] | None:
|
||||
if topic == EmbeddingsRequestEnum.clear_face_classifier.value:
|
||||
self.__clear_classifier()
|
||||
elif topic == EmbeddingsRequestEnum.recognize_face.value:
|
||||
img = cv2.imdecode(
|
||||
np.frombuffer(base64.b64decode(request_data["image"]), dtype=np.uint8),
|
||||
cv2.IMREAD_COLOR,
|
||||
)
|
||||
|
||||
# detect faces with lower confidence since we expect the face
|
||||
# to be visible in uploaded images
|
||||
face_box = self.__detect_face(img, 0.5)
|
||||
|
||||
if not face_box:
|
||||
return {"message": "No face was detected.", "success": False}
|
||||
|
||||
face = img[face_box[1] : face_box[3], face_box[0] : face_box[2]]
|
||||
res = self.__classify_face(face)
|
||||
|
||||
if not res:
|
||||
return {"success": False, "message": "No face was recognized."}
|
||||
|
||||
sub_label, score = res
|
||||
|
||||
return {"success": True, "score": score, "face_name": sub_label}
|
||||
elif topic == EmbeddingsRequestEnum.register_face.value:
|
||||
rand_id = "".join(
|
||||
random.choices(string.ascii_lowercase + string.digits, k=6)
|
||||
@ -490,7 +511,7 @@ class FaceRealTimeProcessor(RealTimeProcessorApi):
|
||||
)
|
||||
|
||||
# delete oldest face image if maximum is reached
|
||||
if len(files) > MAX_FACE_ATTEMPTS:
|
||||
if len(files) > self.config.face_recognition.save_attempts:
|
||||
os.unlink(os.path.join(folder, files[-1]))
|
||||
|
||||
def expire_object(self, object_id: str):
|
||||
|
@ -197,6 +197,14 @@ class EmbeddingsContext:
|
||||
},
|
||||
)
|
||||
|
||||
def recognize_face(self, image_data: bytes) -> dict[str, any]:
|
||||
return self.requestor.send_data(
|
||||
EmbeddingsRequestEnum.recognize_face.value,
|
||||
{
|
||||
"image": base64.b64encode(image_data).decode("ASCII"),
|
||||
},
|
||||
)
|
||||
|
||||
def get_face_ids(self, name: str) -> list[str]:
|
||||
sql_query = f"""
|
||||
SELECT
|
||||
|
@ -717,7 +717,9 @@ function ObjectDetailsTab({
|
||||
draggable={false}
|
||||
src={`${apiHost}api/events/${search.id}/thumbnail.webp`}
|
||||
/>
|
||||
<div className="flex w-full flex-row gap-2">
|
||||
<div
|
||||
className={cn("flex w-full flex-row gap-2", isMobile && "flex-col")}
|
||||
>
|
||||
{config?.semantic_search.enabled &&
|
||||
search.data.type == "object" && (
|
||||
<Button
|
||||
|
Loading…
Reference in New Issue
Block a user