AhmedAdamu commited on
Commit
5c40b77
·
verified ·
1 Parent(s): 158ac93

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -110
app.py CHANGED
@@ -3,6 +3,7 @@ import cv2
3
  import numpy as np
4
  import gradio as gr
5
  import json
 
6
  from ultralytics import YOLO
7
  from insightface.app import FaceAnalysis
8
  from huggingface_hub import hf_hub_download
@@ -17,13 +18,11 @@ class FaceSystem:
17
  def __init__(self):
18
  print("🚀 Initializing AI Models...")
19
 
20
- # 1. Load YOLOv8-Face (Best for detection speed/accuracy)
21
- # We download a specific version trained for faces
22
  model_path = hf_hub_download(repo_id="arnabdhar/YOLOv8-Face-Detection", filename="model.pt")
23
  self.detector = YOLO(model_path)
24
 
25
- # 2. Load InsightFace (Best for recognition accuracy)
26
- # 'buffalo_l' is the large model (higher accuracy). Use 'buffalo_s' if you need more speed.
27
  self.recognizer = FaceAnalysis(name='buffalo_l', providers=['CPUExecutionProvider'])
28
  self.recognizer.prepare(ctx_id=0, det_size=(640, 640))
29
 
@@ -34,7 +33,6 @@ class FaceSystem:
34
  print("✅ System Ready.")
35
 
36
  def load_db(self):
37
- """Load names and vector embeddings from disk."""
38
  if os.path.exists(DB_FILE) and os.path.exists(EMBEDDINGS_FILE):
39
  with open(DB_FILE, 'r') as f:
40
  self.known_names = json.load(f)
@@ -44,56 +42,41 @@ class FaceSystem:
44
  print("📂 Database empty. Starting fresh.")
45
 
46
  def save_db(self):
47
- """Save current memory to disk."""
48
  with open(DB_FILE, 'w') as f:
49
  json.dump(self.known_names, f)
50
  np.save(EMBEDDINGS_FILE, self.known_embeddings)
51
 
52
  def enroll_user(self, name, image):
53
- """Analyzes an image and adds the person to the database."""
54
  if image is None or name.strip() == "":
55
  return "⚠️ Error: Missing name or photo."
56
 
57
- # InsightFace expects BGR format (OpenCV standard)
58
  img_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
59
-
60
- # Detect face for enrollment
61
  faces = self.recognizer.get(img_bgr)
62
 
63
  if len(faces) == 0:
64
- return "⚠️ Error: No face detected. Please use a clear, front-facing photo."
65
 
66
- # Get the largest face (in case there are others in background)
67
- # We sort by area (width * height)
68
  face = sorted(faces, key=lambda x: (x.bbox[2]-x.bbox[0]) * (x.bbox[3]-x.bbox[1]))[-1]
69
-
70
- # Extract embedding (512-dimensional vector)
71
  embedding = face.normed_embedding.reshape(1, -1)
72
 
73
- # Add to memory
74
  if self.known_embeddings.shape[0] == 0:
75
  self.known_embeddings = embedding
76
  else:
77
  self.known_embeddings = np.vstack([self.known_embeddings, embedding])
78
 
79
  self.known_names.append(name)
80
-
81
- # Save to disk
82
  self.save_db()
83
-
84
  return f"✅ Success: '{name}' added to database."
85
 
86
  def recognize_and_process(self, frame, blur_intensity=20, threshold=0.5):
87
- """
88
- The Core Loop: Detect -> Identify -> Anonymize
89
- """
90
  if frame is None: return None
91
 
92
  img_vis = frame.copy()
93
  h, w = img_vis.shape[:2]
94
 
95
- # 1. DETECT (YOLOv8)
96
- # conf=0.5 reduces false positives
97
  results = self.detector(img_vis, conf=0.5, verbose=False)
98
 
99
  for result in results:
@@ -101,120 +84,125 @@ class FaceSystem:
101
  for box in boxes:
102
  x1, y1, x2, y2 = map(int, box.xyxy[0])
103
 
104
- # Add margin to the face crop for better recognition
105
  margin = 0
106
- cx1 = max(0, x1 - margin)
107
- cy1 = max(0, y1 - margin)
108
- cx2 = min(w, x2 + margin)
109
- cy2 = min(h, y2 + margin)
110
-
111
  face_crop = img_vis[cy1:cy2, cx1:cx2]
112
 
113
- # 2. IDENTIFY (InsightFace + Vector Math)
114
  name = "Unknown"
115
- color = (200, 0, 0) # Red for unknown
116
 
117
  if self.known_embeddings.shape[0] > 0 and face_crop.size > 0:
118
- # Convert crop to BGR for InsightFace
119
  face_crop_bgr = cv2.cvtColor(face_crop, cv2.COLOR_RGB2BGR)
120
-
121
- # Extract embedding
122
  analysis = self.recognizer.get(face_crop_bgr)
123
-
124
  if len(analysis) > 0:
125
- # Get embedding of the main face in the crop
126
  target_emb = analysis[0].normed_embedding
127
-
128
- # Calculate Cosine Similarity against ALL known faces at once
129
- # (Dot product of normalized vectors = Cosine Similarity)
130
  similarities = np.dot(self.known_embeddings, target_emb)
131
-
132
- # Find best match
133
  best_idx = np.argmax(similarities)
134
- best_score = similarities[best_idx]
135
-
136
- if best_score > threshold:
137
  name = self.known_names[best_idx]
138
- color = (0, 255, 0) # Green for known
139
- # Optional: Show confidence score
140
- # name += f" ({int(best_score*100)}%)"
141
 
142
- # 3. PRIVACY (Pixelation)
143
- # We extract the ROI again (strictly inside the box)
144
  roi = img_vis[y1:y2, x1:x2]
145
  if roi.size > 0:
146
- # Pixelation logic: Downscale -> Upscale
147
- # Map intensity (10-100) to a factor. Lower factor = bigger blocks.
148
- # Intensity 10 = Block size 20px
149
- # Intensity 100 = Block size 3px (barely blurred)
150
  block_size = max(3, int(30 - (blur_intensity / 4)))
151
-
152
  h_roi, w_roi = roi.shape[:2]
153
-
154
- # Downscale
155
  small = cv2.resize(roi, (max(1, w_roi // block_size), max(1, h_roi // block_size)), interpolation=cv2.INTER_LINEAR)
156
- # Upscale (Nearest Neighbor creates the pixel effect)
157
  pixelated = cv2.resize(small, (w_roi, h_roi), interpolation=cv2.INTER_NEAREST)
158
-
159
- # Apply back to image
160
  img_vis[y1:y2, x1:x2] = pixelated
161
 
162
- # 4. OVERLAY (ID Label)
163
- # We draw the label ON TOP of the blurred face
164
-
165
- # Box
166
  cv2.rectangle(img_vis, (x1, y1), (x2, y2), color, 2)
167
-
168
- # Text Background
169
- label_size, baseline = cv2.getTextSize(name, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)
170
  cv2.rectangle(img_vis, (x1, y1 - label_size[1] - 10), (x1 + label_size[0] + 10, y1), color, -1)
171
-
172
- # Text
173
  cv2.putText(img_vis, name, (x1 + 5, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
174
 
175
  return img_vis
176
 
177
- # Initialize System
178
  system = FaceSystem()
179
 
180
  # ==========================================
181
- # GRADIO INTERFACE
182
  # ==========================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  # ==========================================
184
- # GRADIO INTERFACE
185
  # ==========================================
186
- # We removed the 'theme=' argument to prevent version errors
187
  with gr.Blocks() as demo:
188
 
189
- gr.Markdown(
190
- """
191
- # 👁️ SecureVision Pro
192
- **Enterprise-Grade Identity Protection & Recognition System**
193
- """
194
- )
195
 
196
  with gr.Tabs():
197
- # TAB 1: MONITOR
198
- with gr.Tab("📹 Live Monitor"):
199
- with gr.Row():
200
- with gr.Column(scale=2):
201
- input_feed = gr.Image(sources=["webcam"], streaming=True, label="Camera Feed")
202
- with gr.Column(scale=2):
203
- output_feed = gr.Image(label="Processed Stream (Privacy + ID)")
204
 
205
- with gr.Accordion("⚙️ System Settings", open=True):
206
- blur_slider = gr.Slider(1, 100, value=50, label="Privacy Level (Pixelation Strength)")
207
- conf_slider = gr.Slider(0.1, 0.9, value=0.5, label="Recognition Strictness (Threshold)")
208
-
209
- # Connect the stream
210
- input_feed.stream(
211
- fn=system.recognize_and_process,
212
- inputs=[input_feed, blur_slider, conf_slider],
213
- outputs=output_feed
214
- )
215
-
216
- # TAB 2: DATABASE
217
- with gr.Tab("👤 Database Management"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  with gr.Row():
219
  with gr.Column():
220
  gr.Markdown("### Enroll New Personnel")
@@ -225,7 +213,6 @@ with gr.Blocks() as demo:
225
 
226
  with gr.Column():
227
  gr.Markdown("### Database Status")
228
- # A function to list current users
229
  def get_user_list():
230
  if not system.known_names: return "No users enrolled."
231
  return "\n".join([f"• {n}" for n in system.known_names])
@@ -233,16 +220,9 @@ with gr.Blocks() as demo:
233
  user_list = gr.Markdown(get_user_list)
234
  refresh_btn = gr.Button("Refresh List")
235
 
236
- # Enroll Logic
237
- add_btn.click(
238
- fn=system.enroll_user,
239
- inputs=[new_name, new_photo],
240
- outputs=status_msg
241
- )
242
- # Refresh Logic
243
- refresh_btn.click(fn=get_user_list, outputs=user_list)
244
- # Auto-refresh list on enroll
245
- add_btn.click(fn=get_user_list, outputs=user_list)
246
 
247
  if __name__ == "__main__":
248
  demo.launch()
 
3
  import numpy as np
4
  import gradio as gr
5
  import json
6
+ import tempfile
7
  from ultralytics import YOLO
8
  from insightface.app import FaceAnalysis
9
  from huggingface_hub import hf_hub_download
 
18
  def __init__(self):
19
  print("🚀 Initializing AI Models...")
20
 
21
+ # 1. Load YOLOv8-Face
 
22
  model_path = hf_hub_download(repo_id="arnabdhar/YOLOv8-Face-Detection", filename="model.pt")
23
  self.detector = YOLO(model_path)
24
 
25
+ # 2. Load InsightFace
 
26
  self.recognizer = FaceAnalysis(name='buffalo_l', providers=['CPUExecutionProvider'])
27
  self.recognizer.prepare(ctx_id=0, det_size=(640, 640))
28
 
 
33
  print("✅ System Ready.")
34
 
35
  def load_db(self):
 
36
  if os.path.exists(DB_FILE) and os.path.exists(EMBEDDINGS_FILE):
37
  with open(DB_FILE, 'r') as f:
38
  self.known_names = json.load(f)
 
42
  print("📂 Database empty. Starting fresh.")
43
 
44
  def save_db(self):
 
45
  with open(DB_FILE, 'w') as f:
46
  json.dump(self.known_names, f)
47
  np.save(EMBEDDINGS_FILE, self.known_embeddings)
48
 
49
  def enroll_user(self, name, image):
 
50
  if image is None or name.strip() == "":
51
  return "⚠️ Error: Missing name or photo."
52
 
 
53
  img_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
 
 
54
  faces = self.recognizer.get(img_bgr)
55
 
56
  if len(faces) == 0:
57
+ return "⚠️ Error: No face detected."
58
 
59
+ # Get largest face
 
60
  face = sorted(faces, key=lambda x: (x.bbox[2]-x.bbox[0]) * (x.bbox[3]-x.bbox[1]))[-1]
 
 
61
  embedding = face.normed_embedding.reshape(1, -1)
62
 
 
63
  if self.known_embeddings.shape[0] == 0:
64
  self.known_embeddings = embedding
65
  else:
66
  self.known_embeddings = np.vstack([self.known_embeddings, embedding])
67
 
68
  self.known_names.append(name)
 
 
69
  self.save_db()
 
70
  return f"✅ Success: '{name}' added to database."
71
 
72
  def recognize_and_process(self, frame, blur_intensity=20, threshold=0.5):
73
+ """Core processing logic for a single frame"""
 
 
74
  if frame is None: return None
75
 
76
  img_vis = frame.copy()
77
  h, w = img_vis.shape[:2]
78
 
79
+ # Detect
 
80
  results = self.detector(img_vis, conf=0.5, verbose=False)
81
 
82
  for result in results:
 
84
  for box in boxes:
85
  x1, y1, x2, y2 = map(int, box.xyxy[0])
86
 
87
+ # Add context margin
88
  margin = 0
89
+ cx1 = max(0, x1 - margin); cy1 = max(0, y1 - margin)
90
+ cx2 = min(w, x2 + margin); cy2 = min(h, y2 + margin)
 
 
 
91
  face_crop = img_vis[cy1:cy2, cx1:cx2]
92
 
93
+ # Identify
94
  name = "Unknown"
95
+ color = (200, 0, 0) # Red
96
 
97
  if self.known_embeddings.shape[0] > 0 and face_crop.size > 0:
 
98
  face_crop_bgr = cv2.cvtColor(face_crop, cv2.COLOR_RGB2BGR)
 
 
99
  analysis = self.recognizer.get(face_crop_bgr)
 
100
  if len(analysis) > 0:
 
101
  target_emb = analysis[0].normed_embedding
 
 
 
102
  similarities = np.dot(self.known_embeddings, target_emb)
 
 
103
  best_idx = np.argmax(similarities)
104
+ if similarities[best_idx] > threshold:
 
 
105
  name = self.known_names[best_idx]
106
+ color = (0, 255, 0) # Green
 
 
107
 
108
+ # Privacy Blur (Pixelate)
 
109
  roi = img_vis[y1:y2, x1:x2]
110
  if roi.size > 0:
 
 
 
 
111
  block_size = max(3, int(30 - (blur_intensity / 4)))
 
112
  h_roi, w_roi = roi.shape[:2]
 
 
113
  small = cv2.resize(roi, (max(1, w_roi // block_size), max(1, h_roi // block_size)), interpolation=cv2.INTER_LINEAR)
 
114
  pixelated = cv2.resize(small, (w_roi, h_roi), interpolation=cv2.INTER_NEAREST)
 
 
115
  img_vis[y1:y2, x1:x2] = pixelated
116
 
117
+ # Overlay
 
 
 
118
  cv2.rectangle(img_vis, (x1, y1), (x2, y2), color, 2)
119
+ label_size, _ = cv2.getTextSize(name, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)
 
 
120
  cv2.rectangle(img_vis, (x1, y1 - label_size[1] - 10), (x1 + label_size[0] + 10, y1), color, -1)
 
 
121
  cv2.putText(img_vis, name, (x1 + 5, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
122
 
123
  return img_vis
124
 
125
+ # Initialize
126
  system = FaceSystem()
127
 
128
  # ==========================================
129
+ # VIDEO PROCESSING HELPER
130
  # ==========================================
131
+ def process_video_file(video_path, blur_intensity, threshold):
132
+ """Reads a video file, processes every frame, saves it, and returns path."""
133
+ if video_path is None: return None
134
+
135
+ cap = cv2.VideoCapture(video_path)
136
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
137
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
138
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
139
+
140
+ # Create temp output file
141
+ temp_out = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
142
+ output_path = temp_out.name
143
+
144
+ # Setup writer (mp4v is usually safe for CPU)
145
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
146
+ writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
147
+
148
+ while cap.isOpened():
149
+ ret, frame = cap.read()
150
+ if not ret: break
151
+
152
+ # Process frame using our existing core function
153
+ # We convert BGR (OpenCV) to RGB (needed for our function) then back to BGR
154
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
155
+ processed_rgb = system.recognize_and_process(frame_rgb, blur_intensity, threshold)
156
+ processed_bgr = cv2.cvtColor(processed_rgb, cv2.COLOR_RGB2BGR)
157
+
158
+ writer.write(processed_bgr)
159
+
160
+ cap.release()
161
+ writer.release()
162
+ return output_path
163
+
164
  # ==========================================
165
+ # GRADIO INTERFACE (Fixed UI)
166
  # ==========================================
 
167
  with gr.Blocks() as demo:
168
 
169
+ gr.Markdown("# 👁️ SecureVision Pro")
 
 
 
 
 
170
 
171
  with gr.Tabs():
172
+ # --- SURVEILLANCE TAB ---
173
+ with gr.Tab("📹 Surveillance"):
 
 
 
 
 
174
 
175
+ # Global Settings for this tab
176
+ with gr.Accordion("⚙️ Settings", open=False):
177
+ blur_slider = gr.Slider(1, 100, value=50, label="Privacy Level")
178
+ conf_slider = gr.Slider(0.1, 0.9, value=0.5, label="Recognition Threshold")
179
+
180
+ with gr.Tabs():
181
+ # Sub-Tab 1: Live Webcam
182
+ with gr.TabItem("Live Webcam"):
183
+ with gr.Row():
184
+ web_in = gr.Image(sources=["webcam"], streaming=True, label="Live Feed")
185
+ web_out = gr.Image(label="Live Output")
186
+ web_in.stream(system.recognize_and_process, [web_in, blur_slider, conf_slider], web_out)
187
+
188
+ # Sub-Tab 2: Upload Image
189
+ with gr.TabItem("Upload Image"):
190
+ with gr.Row():
191
+ img_in = gr.Image(sources=["upload", "clipboard"], label="Upload Image")
192
+ img_out = gr.Image(label="Processed Image")
193
+ img_btn = gr.Button("Analyze Image", variant="primary")
194
+ img_btn.click(system.recognize_and_process, [img_in, blur_slider, conf_slider], img_out)
195
+
196
+ # Sub-Tab 3: Upload Video
197
+ with gr.TabItem("Upload Video"):
198
+ with gr.Row():
199
+ vid_in = gr.Video(label="Upload Video")
200
+ vid_out = gr.Video(label="Processed Output")
201
+ vid_btn = gr.Button("Process Video", variant="primary")
202
+ vid_btn.click(process_video_file, [vid_in, blur_slider, conf_slider], vid_out)
203
+
204
+ # --- DATABASE TAB ---
205
+ with gr.Tab("👤 Database"):
206
  with gr.Row():
207
  with gr.Column():
208
  gr.Markdown("### Enroll New Personnel")
 
213
 
214
  with gr.Column():
215
  gr.Markdown("### Database Status")
 
216
  def get_user_list():
217
  if not system.known_names: return "No users enrolled."
218
  return "\n".join([f"• {n}" for n in system.known_names])
 
220
  user_list = gr.Markdown(get_user_list)
221
  refresh_btn = gr.Button("Refresh List")
222
 
223
+ add_btn.click(system.enroll_user, [new_name, new_photo], status_msg)
224
+ refresh_btn.click(get_user_list, outputs=user_list)
225
+ add_btn.click(get_user_list, outputs=user_list)
 
 
 
 
 
 
 
226
 
227
  if __name__ == "__main__":
228
  demo.launch()