| import gradio as gr |
| import requests |
| import base64 |
| import cv2 |
| import numpy as np |
| from typing import List, Union |
|
|
| |
| |
| |
| API_BASE = "https://lifedebugger-face-recognition-sss-beta.hf.space/api/v1" |
|
|
| |
| |
| |
| 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) |
|
|
|
|
| |
| |
| |
| 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] |
|
|
| |
| 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 |
|
|
|
|
| |
| |
| |
| 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', '-')}" |
| ) |
|
|
|
|
| |
| |
| |
| 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(): |
|
|
| |
| 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], |
| ) |
|
|
| |
| 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, |
| ) |
|
|
| |
| |
| |
| if __name__ == "__main__": |
| demo.launch(server_name="0.0.0.0", server_port=7860) |
|
|