import base64 import os import cv2 # type:ignore import numpy as np # type:ignore from deepface import DeepFace # type:ignore from dotenv import load_dotenv # type:ignore from flask import Flask, jsonify, request # type:ignore from flask_cors import CORS # type:ignore from supabase import create_client # type:ignore load_dotenv() SUPABASE_URL = os.getenv("SUPABASE_URL") SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_KEY") FACE_MODEL = "Facenet512" MATCH_THRESHOLD = 22 supabase = create_client(SUPABASE_URL, SUPABASE_SERVICE_KEY) app = Flask(__name__) CORS(app) def base64_to_image(base64_str: str): """Convert base64 image to OpenCV BGR image""" try: img_bytes = base64.b64decode(base64_str.split(",")[1]) np_arr = np.frombuffer(img_bytes, np.uint8) img = cv2.imdecode(np_arr, cv2.IMREAD_COLOR) return img except Exception: raise ValueError("Invalid base64 image") def get_embedding(img_path): result = DeepFace.represent( img_path=img_path, model_name=FACE_MODEL, enforce_detection=True ) return result[0]["embedding"] def extract_single_embedding(img): """Extract exactly ONE face embedding""" result = DeepFace.represent( img_path=img, model_name=FACE_MODEL, enforce_detection=True ) if not result or len(result) != 1: raise ValueError("Exactly one face must be visible") return np.array(result[0]["embedding"], dtype=np.float32) @app.route("/enroll", methods=["POST"]) def enroll_face(): data = request.get_json(force=True) user_id = data.get("userId") image_base64 = data.get("image") if not user_id or not image_base64: return jsonify({"error": "userId and image required"}), 400 try: img = base64_to_image(image_base64) embedding = extract_single_embedding(img) supabase.table("user_profiles").update({ "face_embedding": embedding.tolist() }).eq("id", user_id).execute() return jsonify({ "success": True, "message": "Face enrolled successfully" }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route("/verify-face-by-qr", methods=["POST"]) def verify_face_by_qr(): data = request.get_json(force=True) user_id = data.get("user_id") image_base64 = data.get("image") try: if not user_id or not image_base64: return jsonify({"error": "user_id and image required"}), 400 res = supabase.table("user_profiles") \ .select("face_embedding") \ .eq("id", user_id) \ .execute() if not res.data: return jsonify({"error": "User not found"}), 404 stored_embedding = res.data[0].get("face_embedding") if not stored_embedding: return jsonify({"error": "Face not enrolled"}), 404 stored_embedding = np.array(stored_embedding, dtype=np.float32) live_embedding = np.array(get_embedding(image_base64)) distance = np.linalg.norm(stored_embedding - live_embedding) is_match = distance < MATCH_THRESHOLD return jsonify({ "success": True, "match": bool(is_match), "distance": float(distance) }) except Exception as e: print(e) return jsonify({"error": str(e)}), 500 if __name__ == "__main__": port = int(os.environ.get("PORT", 5000)) app.run(host="0.0.0.0", port=port)