import gradio as gr import requests import base64 import cv2 import numpy as np from typing import List, Union # ========================= # CONFIG # ========================= API_BASE = "https://lifedebugger-face-recognition-sss-beta.hf.space/api/v1" # ========================= # UTILITIES # ========================= def img_np_to_base64(img: np.ndarray) -> str: """Convert numpy image to base64 JPG""" if img is None: return None success, buffer = cv2.imencode(".jpg", img) if not success: return None return base64.b64encode(buffer).decode("utf-8") def load_gallery_item(item: Union[str, dict]) -> np.ndarray: """ Gradio Gallery item handler (v4 compatible) item can be: - str (file path) - dict with key 'name' """ if isinstance(item, dict) and "name" in item: path = item["name"] elif isinstance(item, str): path = item else: return None return cv2.imread(path) # ========================= # VERIFY API # ========================= def call_verify_api(img: np.ndarray, employee_id: str): if img is None: return "❌ No image provided", None b64 = img_np_to_base64(img) if b64 is None: return "❌ Failed to encode image", None payload = { "employee_id": employee_id, "image": b64, } try: r = requests.post( f"{API_BASE}/face/verify-image", json=payload, timeout=30, ) r.raise_for_status() data = r.json() except Exception as e: return f"❌ API error: {e}", None if not data.get("detections"): return "❌ No face detected", None det = data["detections"][0] # Draw bounding box vis = img.copy() x1, y1, x2, y2 = map(int, det["bbox"]) cv2.rectangle(vis, (x1, y1), (x2, y2), (0, 255, 0), 2) text = ( f"Authorized : {det['authorized']}\n" f"Match User : {det.get('match_user')}\n" f"Confidence : {det['confidence']}\n" f"Det Score : {det['det_score']}" ) return text, vis # ========================= # ENROLL API # ========================= def call_enroll_api(images: List[Union[str, dict]], employee_id: str): if not images: return "❌ No images selected" b64_images = [] for item in images: img = load_gallery_item(item) if img is None: continue b64 = img_np_to_base64(img) if b64: b64_images.append(b64) if not b64_images: return "❌ Failed to load images from gallery" payload = { "employee_id": employee_id, "images": b64_images, } try: r = requests.post( f"{API_BASE}/face/enroll", json=payload, timeout=60, ) r.raise_for_status() data = r.json() except Exception as e: return f"❌ API error: {e}" return ( "✅ ENROLL SUCCESS\n" f"Employee ID : {data.get('employee_id')}\n" f"Added : {data.get('added')}\n" f"Total Files : {data.get('total', '-')}" ) # ========================= # GRADIO UI # ========================= with gr.Blocks(title="Face Recognition Test (Uniface)") as demo: gr.Markdown(""" # 🧠 Face Recognition Frontend (TEST) **Backend:** Uniface (RetinaFace + ArcFace) **API:** lifedebugger-face-recognition-sss-beta """) with gr.Tabs(): # ================= VERIFY TAB ================= with gr.Tab("🔍 Verify Face"): with gr.Row(): with gr.Column(): verify_employee_id = gr.Textbox( label="Employee ID", value="1", ) verify_image = gr.Image( label="Input Image (Upload / Webcam)", sources=["upload", "webcam"], type="numpy", ) verify_btn = gr.Button("Verify", variant="primary") with gr.Column(): verify_result = gr.Textbox( label="Result", lines=6, ) verify_vis = gr.Image( label="Visualization", type="numpy", ) verify_btn.click( fn=call_verify_api, inputs=[verify_image, verify_employee_id], outputs=[verify_result, verify_vis], ) # ================= ENROLL TAB ================= with gr.Tab("➕ Enroll Face"): with gr.Row(): with gr.Column(): enroll_employee_id = gr.Textbox( label="Employee ID", value="1", ) enroll_images = gr.Gallery( label="Upload Face Images (Multiple)", columns=3, height=250, ) enroll_btn = gr.Button("Enroll", variant="primary") with gr.Column(): enroll_result = gr.Textbox( label="Enroll Status", lines=6, ) enroll_btn.click( fn=call_enroll_api, inputs=[enroll_images, enroll_employee_id], outputs=enroll_result, ) # ========================= # RUN # ========================= if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860)