| import os
|
| import pickle
|
| import tempfile
|
| import numpy as np
|
| from numpy.linalg import norm
|
|
|
| from fastapi import FastAPI, File, UploadFile
|
| from deepface import DeepFace
|
|
|
|
|
|
|
|
|
| BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
| PKL_PATH = os.path.join(BASE_DIR, "student_embeddings.pkl")
|
|
|
| MODEL_NAME = "Facenet512"
|
| THRESHOLD = 0.58
|
|
|
|
|
|
|
|
|
| app = FastAPI(
|
| title="Face Recognition API",
|
| description="DeepFace FaceNet512 based Student Recognition API",
|
| version="1.0"
|
| )
|
|
|
|
|
|
|
|
|
| def cosine_similarity(a, b):
|
| return float(np.dot(a, b) / (norm(a) * norm(b)))
|
|
|
|
|
| def l2_normalize(x):
|
| return x / norm(x)
|
|
|
|
|
|
|
|
|
|
|
| if not os.path.exists(PKL_PATH):
|
| raise FileNotFoundError(f"❌ student_embeddings.pkl not found at {PKL_PATH}")
|
|
|
| with open(PKL_PATH, "rb") as f:
|
| db = pickle.load(f)
|
|
|
| print(f"✅ Loaded {len(db)} student embeddings")
|
|
|
|
|
|
|
|
|
| @app.get("/")
|
| def health():
|
| return {
|
| "status": "running",
|
| "model": MODEL_NAME,
|
| "students_loaded": len(db)
|
| }
|
|
|
|
|
|
|
|
|
| @app.post("/recognize")
|
| async def recognize_face(file: UploadFile = File(...)):
|
| img_path = None
|
|
|
| try:
|
|
|
| with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as tmp:
|
| tmp.write(await file.read())
|
| img_path = tmp.name
|
|
|
|
|
| result = DeepFace.represent(
|
| img_path=img_path,
|
| model_name=MODEL_NAME,
|
| enforce_detection=True
|
| )
|
|
|
| test_emb = np.array(result[0]["embedding"])
|
| test_emb = l2_normalize(test_emb)
|
|
|
| best_match = None
|
| best_score = -1.0
|
|
|
|
|
| for name, db_emb in db.items():
|
| score = cosine_similarity(db_emb, test_emb)
|
| if score > best_score:
|
| best_score = score
|
| best_match = name
|
|
|
| decision = "MATCH" if best_score >= THRESHOLD else "NO MATCH"
|
|
|
| return {
|
| "student": best_match,
|
| "similarity": round(best_score, 4),
|
| "cosine_distance": round(1 - best_score, 4),
|
| "decision": decision
|
| }
|
|
|
| except Exception as e:
|
| return {"error": str(e)}
|
|
|
| finally:
|
|
|
| if img_path and os.path.exists(img_path):
|
| os.remove(img_path)
|
|
|