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
    })