Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| import cv2 | |
| import plotly.graph_objects as go | |
| from sklearn.metrics.pairwise import cosine_distances | |
| # ========================= | |
| # FACE SETUP | |
| # ========================= | |
| face_cascade = cv2.CascadeClassifier( | |
| cv2.data.haarcascades + "haarcascade_frontalface_default.xml" | |
| ) | |
| def extract_face(image): | |
| if image is None: | |
| return None, None | |
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
| faces = face_cascade.detectMultiScale(gray, 1.3, 5) | |
| if len(faces) == 0: | |
| return image, None | |
| x, y, w, h = faces[0] | |
| cv2.rectangle(image, (x,y), (x+w,y+h), (0,255,0), 2) | |
| face = cv2.resize(gray[y:y+h, x:x+w], (64,64)) | |
| return image, face.flatten() | |
| def embed(face): | |
| vec = face @ np.random.randn(face.shape[0], 128) | |
| return vec / np.linalg.norm(vec) | |
| # ========================= | |
| # STEP FUNCTIONS | |
| # ========================= | |
| def step_detect(img): | |
| img, _ = extract_face(img) | |
| return img | |
| def step_enroll(img): | |
| img, face = extract_face(img) | |
| if face is None: | |
| return img, None, "β No face detected" | |
| emb = embed(face) | |
| return img, emb, "β Face enrolled" | |
| def step_verify(img, stored): | |
| img, face = extract_face(img) | |
| if face is None or stored is None: | |
| return img, "β Missing data", None | |
| live = embed(face) | |
| dist = cosine_distances([stored], [live])[0][0] | |
| status = "π UNLOCKED" if dist < 0.35 else "π DENIED" | |
| fig = go.Figure(go.Indicator( | |
| mode="gauge+number", | |
| value=dist, | |
| title={"text": status}, | |
| gauge={"axis": {"range": [0, 1]}} | |
| )) | |
| return img, f"Distance: {dist:.3f}", fig | |
| # ========================= | |
| # UI | |
| # ========================= | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| page = gr.State(0) | |
| stored_embedding = gr.State() | |
| gr.Markdown("# π Face Unlock β How Mobile Face ID Works") | |
| with gr.Row(): | |
| back = gr.Button("β¬ Back") | |
| nextb = gr.Button("Next β‘") | |
| # ---------- PAGE 0 ---------- | |
| page0 = gr.Column(visible=True) | |
| with page0: | |
| gr.Markdown("## πΈ Face Detection") | |
| cam0 = gr.Image(sources=["webcam"], type="numpy") | |
| out0 = gr.Image() | |
| gr.Button("Detect Face").click(step_detect, cam0, out0) | |
| # ---------- PAGE 1 ---------- | |
| page1 = gr.Column(visible=False) | |
| with page1: | |
| gr.Markdown("## π§ Face Enrollment") | |
| cam1 = gr.Image(sources=["webcam"], type="numpy") | |
| out1 = gr.Image() | |
| msg1 = gr.Markdown() | |
| gr.Button("Enroll Face").click( | |
| step_enroll, | |
| cam1, | |
| [out1, stored_embedding, msg1] | |
| ) | |
| # ---------- PAGE 2 ---------- | |
| page2 = gr.Column(visible=False) | |
| with page2: | |
| gr.Markdown("## π Face Verification") | |
| cam2 = gr.Image(sources=["webcam"], type="numpy") | |
| out2 = gr.Image() | |
| msg2 = gr.Markdown() | |
| gauge = gr.Plot() | |
| gr.Button("Verify").click( | |
| step_verify, | |
| [cam2, stored_embedding], | |
| [out2, msg2, gauge] | |
| ) | |
| # ========================= | |
| # NAVIGATION | |
| # ========================= | |
| def navigate(p, step): | |
| p = min(2, max(0, p + step)) | |
| return ( | |
| p, | |
| gr.update(visible=p == 0), | |
| gr.update(visible=p == 1), | |
| gr.update(visible=p == 2) | |
| ) | |
| back.click(navigate, [page, gr.State(-1)], [page, page0, page1, page2]) | |
| nextb.click(navigate, [page, gr.State(1)], [page, page0, page1, page2]) | |
| demo.launch() |