File size: 7,041 Bytes
9e9b090
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# --- Hugging Face fixes (add these 4 lines at the very top) ---
import os
if os.path.exists("/usr/bin/apt"):
    import subprocess, sys
    subprocess.check_call([sys.executable, "-m", "pip", "install", "insightface==0.7.3", "faiss-gpu", "deep-sort-realtime", "ultralytics", "onnxruntime-gpu", "--no-cache-dir"])
# ---------------------------------------------------------------
# SECUREFACE ID - FINAL UNIFIED APP
# Privacy by default + Accurate Recognition + Persistent Tracking
# Combines your two perfect apps into one

import os
import cv2
import numpy as np
import gradio as gr
from ultralytics import YOLO
import insightface
from insightface.app import FaceAnalysis
import faiss
from deep_sort_realtime.deepsort_tracker import DeepSort
from pathlib import Path

# ==================== 1. MODELS & DATABASE ====================
detector = YOLO("yolov8n-face.pt")
recognizer = FaceAnalysis(name='buffalo_l', providers=['CUDAExecutionProvider'])
recognizer.prepare(ctx_id=0, det_size=(640,640))

# FAISS index for known faces
KNOWN_EMBS_PATH = "known_embeddings.npy"
KNOWN_NAMES_PATH = "known_names.npy"

index = None
known_names = []

if os.path.exists(KNOWN_EMBS_PATH):
    embeddings = np.load(KNOWN_EMBS_PATH)
    known_names = np.load(KNOWN_NAMES_PATH, allow_pickle=True).tolist()
    dim = embeddings.shape[1]
    index = faiss.IndexHNSWFlat(dim, 32)
    index.hnsw.efSearch = 16
    index.add(embeddings.astype('float32'))
    print(f"Loaded {len(known_names)} known people")

# Tracker for persistent IDs
tracker = DeepSort(max_age=30, n_init=3, max_cosine_distance=0.4, nn_budget=None, embedder_gpu=True)
unknown_counter = 0
track_to_label = {}  # track_id → "Alice" or "Unknown_003"

# ==================== 2. CORE PROCESSING FUNCTION ====================
def process_frame(frame: np.ndarray, blur_type: str = "gaussian", intensity: float = 30, expand: float = 1.2, show_labels: bool = True):
    global unknown_counter, track_to_label

    img = frame.copy()
    h, w = img.shape[:2]

    # Detect faces
    results = detector(img, conf=0.4)[0]
    detections = []
    crops = []

    for box in results.boxes:
        x1, y1, x2, y2 = map(int, box.xyxy[0])
        # Expand bbox
        expand_w = int((x2 - x1) * (expand - 1) / 2)
        expand_h = int((y2 - y1) * (expand - 1) / 2)
        x1 = max(0, x1 - expand_w)
        y1 = max(0, y1 - expand_h)
        x2 = min(w, x2 + expand_w)
        y2 = min(h, y2 + expand_h)

        crop = img[y1:y2, x1:x2]
        if crop.size == 0: continue
        detections.append(([x1, y1, x2-x1, y2-y1], box.conf[0].item(), 'face'))
        crops.append((crop, (x1, y1, x2, y2)))

    # Track
    tracks = tracker.update_tracks(detections, frame=img)

    for track, (crop, (x1, y1, x2, y2)) in zip(tracks, crops):
        if not track.is_confirmed(): continue
        track_id = track.track_id

        # Recognize only when needed
        if track_id not in track_to_label or track.time_since_update % 15 == 0:
            faces = recognizer.get(crop, max_num=1)
            name = "Unknown"
            if faces and index is not None:
                emb = faces[0].normed_embedding.reshape(1, -1).astype('float32')
                D, I = index.search(emb, k=1)
                if D[0][0] < 0.45:
                    name = known_names[I[0][0]]

            if name == "Unknown":
                if track_id not in track_to_label:
                    unknown_counter += 1
                    track_to_label[track_id] = f"Unknown_{unknown_counter:03d}"
                name = track_to_label[track_id]
            else:
                track_to_label[track_id] = name

        label = track_to_label.get(track_id, "Unknown")

        # ALWAYS BLUR
        face_region = img[y1:y2, x1:x2]
        if blur_type == "gaussian":
            k = int(min(x2-x1, y2-y1) * (intensity / 100)) | 1
            blurred = cv2.GaussianBlur(face_region, (k, k), 0)
        elif blur_type == "pixelate":
            small = cv2.resize(face_region, (20, 20), interpolation=cv2.INTER_LINEAR)
            blurred = cv2.resize(small, (x2-x1, y2-y1), interpolation=cv2.INTER_NEAREST)
        else:  # solid
            blurred = np.zeros_like(face_region)
            blurred[:] = (0, 0, 0)

        img[y1:y2, x1:x2] = blurred

        # Optional: show label
        if show_labels:
            color = (0, 255, 0) if "Unknown" not in label else (0, 255, 255)
            cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
            cv2.putText(img, label, (x1, y1-10), cv2.FONT_HERSHEY_DUPLEX, 0.9, color, 2)

    return img

# ==================== 3. ENROLLMENT FUNCTION ====================
def enroll_person(name: str, face_image: np.ndarray):
    global index, known_names
    if face_image is None:
        return "Please upload a clear face photo"

    faces = recognizer.get(face_image, max_num=1)
    if not faces:
        return "No face detected! Please try a clearer photo."

    emb = faces[0].normed_embedding

    # Save to disk
    embs = np.load(KNOWN_EMBS_PATH) if os.path.exists(KNOWN_EMBS_PATH) else np.empty((0, 512))
    names = np.load(KNOWN_NAMES_PATH, allow_pickle=True).tolist() if os.path.exists(KNOWN_NAMES_PATH) else []

    embs = np.vstack([embs, emb])
    names.append(name)

    np.save(KNOWN_EMBS_PATH, embs)
    np.save(KNOWN_NAMES_PATH, np.array(names))

    # Rebuild index
    dim = 512
    index = faiss.IndexHNSWFlat(dim, 32)
    index.add(embs.astype('float32'))
    known_names = names

    return f"Successfully enrolled: {name}"

# ==================== 4. GRADIO UI ====================
with gr.Blocks(title="SecureFace ID – Privacy-First Recognition", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# SecureFace ID")
    gr.Markdown("**Every face is always blurred • Only authorized people are identified • Persistent tracking**")

    with gr.Tab("Live Privacy Mode"):
        with gr.Row():
            inp = gr.Image(sources=["webcam", "upload"], streaming=True, height=600)
            out = gr.Image(height=600)
        with gr.Row():
            blur_type = gr.Radio(["gaussian", "pixelate", "solid"], value="gaussian", label="Blur Style")
            intensity = gr.Slider(10, 100, 40, label="Blur Intensity")
            expand = gr.Slider(1.0, 2.0, 1.3, label="Blur Area Size")
            show_labels = gr.Checkbox(True, label="Show Names / Unknown IDs")
        inp.stream(process_frame, [inp, blur_type, intensity, expand, show_labels], out)

    with gr.Tab("Enroll New Person"):
        gr.Markdown("### Add someone to the database")
        name_in = gr.Textbox(label="Full Name or ID", placeholder="Alice Smith")
        img_in = gr.Image(label="Clear face photo", sources=["upload", "webcam"])
        btn = gr.Button("Enroll Person", variant="primary")
        status = gr.Markdown()
        btn.click(enroll_person, [name_in, img_in], status)

    with gr.Tab("Database"):
        gr.Markdown(f"**{len(known_names)} people in database:**")
        for name in known_names:
            gr.Markdown(f"• {name}")

demo.launch()