# FastAPI face verification API (CPU) import io, json, numpy as np, os from typing import Optional from fastapi import FastAPI, UploadFile, File, Form from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from PIL import Image import insightface from insightface.app import FaceAnalysis # --- Load artifacts --- LABELS = json.load(open("models/labels.json")) GALLERY = np.load("models/gallery_mean.npy").astype("float32") THRESH = json.load(open("models/threshold.json")).get("cosine_threshold", 0.35) # --- Embedder (CPU) --- FA = FaceAnalysis(name="buffalo_l", providers=["CPUExecutionProvider"]) FA.prepare(ctx_id=-1, det_size=(640,640)) def embed_pil(img: Image.Image) -> Optional[np.ndarray]: arr = np.array(img.convert("RGB")) faces = FA.get(arr) if not faces: return None f = max(faces, key=lambda z:(z.bbox[2]-z.bbox[0])*(z.bbox[3]-z.bbox[1])) return f.normed_embedding.astype("float32") # already L2-normalized def cosine_sim(a: np.ndarray, B: np.ndarray) -> np.ndarray: return (B @ a) / (np.linalg.norm(a)*np.linalg.norm(B,axis=1) + 1e-12) app = FastAPI(title="FaceVerify API") app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) @app.post("/verify") async def verify(name: str = Form(...), image: UploadFile = File(...)): try: img = Image.open(io.BytesIO(await image.read())).convert("RGB") except Exception as e: return JSONResponse({"status":"error","reason":f"bad_image:{e}"}, 400) emb = embed_pil(img) if emb is None: return JSONResponse({"status":"denied","reason":"no_face_detected"}, 200) sims = cosine_sim(emb, GALLERY) # higher is better i = int(np.argmax(sims)) score = float(sims[i]) matched = LABELS[i] if 0 <= i < len(LABELS) else None ok = score >= THRESH return JSONResponse({ "status": "accepted" if ok else "denied", "score": score, "threshold": THRESH, "claimed_name": name, "matched_name": matched })