AhmedAdamu commited on
Commit
9e9b090
·
verified ·
1 Parent(s): cd6a4d7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -0
app.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --- Hugging Face fixes (add these 4 lines at the very top) ---
2
+ import os
3
+ if os.path.exists("/usr/bin/apt"):
4
+ import subprocess, sys
5
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "insightface==0.7.3", "faiss-gpu", "deep-sort-realtime", "ultralytics", "onnxruntime-gpu", "--no-cache-dir"])
6
+ # ---------------------------------------------------------------
7
+ # SECUREFACE ID - FINAL UNIFIED APP
8
+ # Privacy by default + Accurate Recognition + Persistent Tracking
9
+ # Combines your two perfect apps into one
10
+
11
+ import os
12
+ import cv2
13
+ import numpy as np
14
+ import gradio as gr
15
+ from ultralytics import YOLO
16
+ import insightface
17
+ from insightface.app import FaceAnalysis
18
+ import faiss
19
+ from deep_sort_realtime.deepsort_tracker import DeepSort
20
+ from pathlib import Path
21
+
22
+ # ==================== 1. MODELS & DATABASE ====================
23
+ detector = YOLO("yolov8n-face.pt")
24
+ recognizer = FaceAnalysis(name='buffalo_l', providers=['CUDAExecutionProvider'])
25
+ recognizer.prepare(ctx_id=0, det_size=(640,640))
26
+
27
+ # FAISS index for known faces
28
+ KNOWN_EMBS_PATH = "known_embeddings.npy"
29
+ KNOWN_NAMES_PATH = "known_names.npy"
30
+
31
+ index = None
32
+ known_names = []
33
+
34
+ if os.path.exists(KNOWN_EMBS_PATH):
35
+ embeddings = np.load(KNOWN_EMBS_PATH)
36
+ known_names = np.load(KNOWN_NAMES_PATH, allow_pickle=True).tolist()
37
+ dim = embeddings.shape[1]
38
+ index = faiss.IndexHNSWFlat(dim, 32)
39
+ index.hnsw.efSearch = 16
40
+ index.add(embeddings.astype('float32'))
41
+ print(f"Loaded {len(known_names)} known people")
42
+
43
+ # Tracker for persistent IDs
44
+ tracker = DeepSort(max_age=30, n_init=3, max_cosine_distance=0.4, nn_budget=None, embedder_gpu=True)
45
+ unknown_counter = 0
46
+ track_to_label = {} # track_id → "Alice" or "Unknown_003"
47
+
48
+ # ==================== 2. CORE PROCESSING FUNCTION ====================
49
+ def process_frame(frame: np.ndarray, blur_type: str = "gaussian", intensity: float = 30, expand: float = 1.2, show_labels: bool = True):
50
+ global unknown_counter, track_to_label
51
+
52
+ img = frame.copy()
53
+ h, w = img.shape[:2]
54
+
55
+ # Detect faces
56
+ results = detector(img, conf=0.4)[0]
57
+ detections = []
58
+ crops = []
59
+
60
+ for box in results.boxes:
61
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
62
+ # Expand bbox
63
+ expand_w = int((x2 - x1) * (expand - 1) / 2)
64
+ expand_h = int((y2 - y1) * (expand - 1) / 2)
65
+ x1 = max(0, x1 - expand_w)
66
+ y1 = max(0, y1 - expand_h)
67
+ x2 = min(w, x2 + expand_w)
68
+ y2 = min(h, y2 + expand_h)
69
+
70
+ crop = img[y1:y2, x1:x2]
71
+ if crop.size == 0: continue
72
+ detections.append(([x1, y1, x2-x1, y2-y1], box.conf[0].item(), 'face'))
73
+ crops.append((crop, (x1, y1, x2, y2)))
74
+
75
+ # Track
76
+ tracks = tracker.update_tracks(detections, frame=img)
77
+
78
+ for track, (crop, (x1, y1, x2, y2)) in zip(tracks, crops):
79
+ if not track.is_confirmed(): continue
80
+ track_id = track.track_id
81
+
82
+ # Recognize only when needed
83
+ if track_id not in track_to_label or track.time_since_update % 15 == 0:
84
+ faces = recognizer.get(crop, max_num=1)
85
+ name = "Unknown"
86
+ if faces and index is not None:
87
+ emb = faces[0].normed_embedding.reshape(1, -1).astype('float32')
88
+ D, I = index.search(emb, k=1)
89
+ if D[0][0] < 0.45:
90
+ name = known_names[I[0][0]]
91
+
92
+ if name == "Unknown":
93
+ if track_id not in track_to_label:
94
+ unknown_counter += 1
95
+ track_to_label[track_id] = f"Unknown_{unknown_counter:03d}"
96
+ name = track_to_label[track_id]
97
+ else:
98
+ track_to_label[track_id] = name
99
+
100
+ label = track_to_label.get(track_id, "Unknown")
101
+
102
+ # ALWAYS BLUR
103
+ face_region = img[y1:y2, x1:x2]
104
+ if blur_type == "gaussian":
105
+ k = int(min(x2-x1, y2-y1) * (intensity / 100)) | 1
106
+ blurred = cv2.GaussianBlur(face_region, (k, k), 0)
107
+ elif blur_type == "pixelate":
108
+ small = cv2.resize(face_region, (20, 20), interpolation=cv2.INTER_LINEAR)
109
+ blurred = cv2.resize(small, (x2-x1, y2-y1), interpolation=cv2.INTER_NEAREST)
110
+ else: # solid
111
+ blurred = np.zeros_like(face_region)
112
+ blurred[:] = (0, 0, 0)
113
+
114
+ img[y1:y2, x1:x2] = blurred
115
+
116
+ # Optional: show label
117
+ if show_labels:
118
+ color = (0, 255, 0) if "Unknown" not in label else (0, 255, 255)
119
+ cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
120
+ cv2.putText(img, label, (x1, y1-10), cv2.FONT_HERSHEY_DUPLEX, 0.9, color, 2)
121
+
122
+ return img
123
+
124
+ # ==================== 3. ENROLLMENT FUNCTION ====================
125
+ def enroll_person(name: str, face_image: np.ndarray):
126
+ global index, known_names
127
+ if face_image is None:
128
+ return "Please upload a clear face photo"
129
+
130
+ faces = recognizer.get(face_image, max_num=1)
131
+ if not faces:
132
+ return "No face detected! Please try a clearer photo."
133
+
134
+ emb = faces[0].normed_embedding
135
+
136
+ # Save to disk
137
+ embs = np.load(KNOWN_EMBS_PATH) if os.path.exists(KNOWN_EMBS_PATH) else np.empty((0, 512))
138
+ names = np.load(KNOWN_NAMES_PATH, allow_pickle=True).tolist() if os.path.exists(KNOWN_NAMES_PATH) else []
139
+
140
+ embs = np.vstack([embs, emb])
141
+ names.append(name)
142
+
143
+ np.save(KNOWN_EMBS_PATH, embs)
144
+ np.save(KNOWN_NAMES_PATH, np.array(names))
145
+
146
+ # Rebuild index
147
+ dim = 512
148
+ index = faiss.IndexHNSWFlat(dim, 32)
149
+ index.add(embs.astype('float32'))
150
+ known_names = names
151
+
152
+ return f"Successfully enrolled: {name}"
153
+
154
+ # ==================== 4. GRADIO UI ====================
155
+ with gr.Blocks(title="SecureFace ID – Privacy-First Recognition", theme=gr.themes.Soft()) as demo:
156
+ gr.Markdown("# SecureFace ID")
157
+ gr.Markdown("**Every face is always blurred • Only authorized people are identified • Persistent tracking**")
158
+
159
+ with gr.Tab("Live Privacy Mode"):
160
+ with gr.Row():
161
+ inp = gr.Image(sources=["webcam", "upload"], streaming=True, height=600)
162
+ out = gr.Image(height=600)
163
+ with gr.Row():
164
+ blur_type = gr.Radio(["gaussian", "pixelate", "solid"], value="gaussian", label="Blur Style")
165
+ intensity = gr.Slider(10, 100, 40, label="Blur Intensity")
166
+ expand = gr.Slider(1.0, 2.0, 1.3, label="Blur Area Size")
167
+ show_labels = gr.Checkbox(True, label="Show Names / Unknown IDs")
168
+ inp.stream(process_frame, [inp, blur_type, intensity, expand, show_labels], out)
169
+
170
+ with gr.Tab("Enroll New Person"):
171
+ gr.Markdown("### Add someone to the database")
172
+ name_in = gr.Textbox(label="Full Name or ID", placeholder="Alice Smith")
173
+ img_in = gr.Image(label="Clear face photo", sources=["upload", "webcam"])
174
+ btn = gr.Button("Enroll Person", variant="primary")
175
+ status = gr.Markdown()
176
+ btn.click(enroll_person, [name_in, img_in], status)
177
+
178
+ with gr.Tab("Database"):
179
+ gr.Markdown(f"**{len(known_names)} people in database:**")
180
+ for name in known_names:
181
+ gr.Markdown(f"• {name}")
182
+
183
+ demo.launch()