From cd972fd36a8e24acb12e9d7b865ddfc4adafa0c7 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 18 Mar 2025 16:05:15 -0600 Subject: [PATCH] Add api to run face recognition on image --- frigate/api/classification.py | 16 ++++++++++++++++ frigate/comms/embeddings_updater.py | 1 + frigate/data_processing/real_time/face.py | 22 ++++++++++++++++++++++ frigate/embeddings/__init__.py | 9 +++++++++ 4 files changed, 48 insertions(+) diff --git a/frigate/api/classification.py b/frigate/api/classification.py index df804f34a..6d2da59d9 100644 --- a/frigate/api/classification.py +++ b/frigate/api/classification.py @@ -198,6 +198,22 @@ async def register_face(request: Request, name: str, file: UploadFile): ) +@router.post("/faces/{name}/register", dependencies=[Depends(require_role(["admin"]))]) +async def recognize_face(request: Request, name: str, 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(name, 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: diff --git a/frigate/comms/embeddings_updater.py b/frigate/comms/embeddings_updater.py index 61c2331cf..fc35c4665 100644 --- a/frigate/comms/embeddings_updater.py +++ b/frigate/comms/embeddings_updater.py @@ -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" diff --git a/frigate/data_processing/real_time/face.py b/frigate/data_processing/real_time/face.py index acb891449..fa0c824e2 100644 --- a/frigate/data_processing/real_time/face.py +++ b/frigate/data_processing/real_time/face.py @@ -407,6 +407,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) diff --git a/frigate/embeddings/__init__.py b/frigate/embeddings/__init__.py index 0a0d7200a..be666c974 100644 --- a/frigate/embeddings/__init__.py +++ b/frigate/embeddings/__init__.py @@ -197,6 +197,15 @@ class EmbeddingsContext: }, ) + def recognize_face(self, face_name: str, image_data: bytes) -> dict[str, any]: + return self.requestor.send_data( + EmbeddingsRequestEnum.recognize_face.value, + { + "face_name": face_name, + "image": base64.b64encode(image_data).decode("ASCII"), + }, + ) + def get_face_ids(self, name: str) -> list[str]: sql_query = f""" SELECT