File size: 2,044 Bytes
319af51 4bb52ae 319af51 e5a51e1 319af51 4bb52ae |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# 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
})
|