blackmamba2408 commited on
Commit
1d12d62
·
verified ·
1 Parent(s): f42bbdc

🔧 FIXED: Minimal bulletproof app avoiding Gradio schema bug

Browse files
Files changed (1) hide show
  1. app.py +123 -243
app.py CHANGED
@@ -3,303 +3,183 @@ import numpy as np
3
  from PIL import Image
4
  import os
5
  import json
6
- from datetime import datetime
7
 
8
- # Try to import InsightFace, fallback gracefully if not available
9
- INSIGHTFACE_AVAILABLE = False
10
  try:
11
  from insightface.app.face_analysis import FaceAnalysis
12
- import onnxruntime as ort
13
  INSIGHTFACE_AVAILABLE = True
14
  print("✓ InsightFace available")
15
  except Exception as e:
 
16
  print(f"InsightFace not available: {e}")
17
- print("Will use demo mode")
18
 
19
- class FaceMatchingSystem:
20
  def __init__(self):
21
- """Initialize the face matching system"""
22
  self.app = None
23
- self.face_database = {}
24
- self.model_status = "Initializing..."
25
- self.setup_models()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- def setup_models(self):
28
- """Setup the face recognition models"""
29
  try:
30
- if INSIGHTFACE_AVAILABLE:
31
- print("Attempting to load InsightFace models...")
32
- try:
33
- self.app = FaceAnalysis(
34
- name='buffalo_l',
35
- providers=['CPUExecutionProvider']
36
- )
37
- self.app.prepare(ctx_id=0, det_thresh=0.5, det_size=(640, 640))
38
- self.model_status = "✓ InsightFace models loaded successfully"
39
- print(self.model_status)
40
- return
41
- except Exception as e:
42
- print(f"Failed to load InsightFace models: {e}")
43
-
44
- # Fallback to demo mode
45
- self.app = MockFaceApp()
46
- self.model_status = "Demo mode active (InsightFace not available)"
47
- print(self.model_status)
48
-
49
- except Exception as e:
50
- print(f"Error in model setup: {e}")
51
- self.app = MockFaceApp()
52
- self.model_status = f"Demo mode (Error: {str(e)})"
53
-
54
- def extract_face_embedding(self, image):
55
- """Extract face embedding from image"""
56
  try:
57
- if image is None:
58
- return None, "No image provided"
59
-
60
- # Convert PIL to numpy array (RGB format)
61
- if isinstance(image, Image.Image):
62
- image_array = np.array(image.convert('RGB'))
63
- else:
64
- image_array = image
65
-
66
- # Use the face analysis app
67
- if hasattr(self.app, 'get'):
68
- faces = self.app.get(image_array)
69
- else:
70
- return np.random.rand(512), "Demo mode: mock embedding generated"
71
-
72
- if len(faces) == 0:
73
- return None, "No face detected in the image"
74
-
75
- # Use the largest face if multiple detected
76
- if len(faces) > 1:
77
- faces = sorted(faces, key=lambda x: (x.bbox[2] - x.bbox[0]) * (x.bbox[3] - x.bbox[1]), reverse=True)
78
-
79
  face = faces[0]
80
- embedding = face.embedding
81
- confidence = getattr(face, 'det_score', 0.95)
82
-
83
- return embedding, f"Face detected (confidence: {confidence:.3f})"
84
-
85
  except Exception as e:
86
- print(f"Error extracting face embedding: {e}")
87
- return None, f"Error processing image: {str(e)}"
88
-
89
- def add_face_to_database(self, image, person_name):
90
- """Add a face to the database"""
91
- if not person_name or not person_name.strip():
92
- return "Please provide a valid person name", ""
93
 
94
- person_name = person_name.strip()
 
95
 
96
- embedding, message = self.extract_face_embedding(image)
97
  if embedding is None:
98
- return f"Failed to add {person_name}: {message}", ""
99
 
100
- # Store embedding in database
101
- self.face_database[person_name] = {
102
- 'embedding': embedding.tolist() if hasattr(embedding, 'tolist') else embedding,
103
- 'added_at': datetime.now().isoformat()
104
- }
105
 
106
  # Save database
107
- self.save_database()
 
 
 
 
108
 
109
- return f"✓ Successfully added {person_name} to database ({message})", self.get_database_info()
110
-
111
- def match_face(self, image, threshold=0.6):
112
- """Match a face against the database"""
113
- if not self.face_database:
114
- return "Database is empty. Please add faces first.", "", 0.0
 
 
115
 
116
- embedding, message = self.extract_face_embedding(image)
117
  if embedding is None:
118
- return f"Face matching failed: {message}", "", 0.0
119
 
120
  best_match = None
121
- best_similarity = 0.0
122
 
123
- for person_name, data in self.face_database.items():
124
- stored_embedding = np.array(data['embedding'])
125
-
126
- # Calculate cosine similarity
127
- similarity = np.dot(embedding, stored_embedding) / (
128
- np.linalg.norm(embedding) * np.linalg.norm(stored_embedding)
129
- )
130
-
131
- if similarity > best_similarity:
132
- best_similarity = similarity
133
- best_match = person_name
134
 
135
- if best_similarity >= threshold:
136
- confidence_percentage = best_similarity * 100
137
- return (
138
- f"✓ Match Found: {best_match}",
139
- f"Confidence: {confidence_percentage:.1f}%",
140
- confidence_percentage
141
- )
142
  else:
143
- return (
144
- "❌ No match found",
145
- f"Best similarity: {best_similarity*100:.1f}% (below threshold {threshold*100:.1f}%)",
146
- best_similarity * 100
147
- )
148
-
149
- def save_database(self):
150
- """Save the face database"""
151
- try:
152
- with open('face_database.json', 'w') as f:
153
- json.dump(self.face_database, f, indent=2)
154
- except Exception as e:
155
- print(f"Failed to save database: {e}")
156
-
157
- def load_database(self):
158
- """Load the face database"""
159
- try:
160
- if os.path.exists('face_database.json'):
161
- with open('face_database.json', 'r') as f:
162
- self.face_database = json.load(f)
163
- print(f"Loaded {len(self.face_database)} faces from database")
164
- except Exception as e:
165
- print(f"Failed to load database: {e}")
166
- self.face_database = {}
167
-
168
- def get_database_info(self):
169
- """Get information about the current database"""
170
- if not self.face_database:
171
  return "Database is empty"
172
-
173
- info = f"Database contains {len(self.face_database)} faces:\\n"
174
- for name, data in self.face_database.items():
175
- added_date = data.get('added_at', 'Unknown')[:10]
176
- info += f"• {name} (added: {added_date})\\n"
177
-
178
- return info
179
-
180
- def clear_database(self):
181
- """Clear the entire database"""
182
- self.face_database = {}
183
- self.save_database()
184
- return "Database cleared successfully", ""
185
 
186
- class MockFaceApp:
187
- """Mock face app for demo purposes when InsightFace is not available"""
188
- def __init__(self):
189
- self.face_counter = 0
190
-
191
- def get(self, image):
192
- if image is None:
193
- return []
194
-
195
- # Create deterministic embedding based on image hash
196
- image_hash = hash(str(np.array(image).mean())) % 1000
197
-
198
- class MockFace:
199
- def __init__(self, image_hash):
200
- np.random.seed(image_hash)
201
- self.embedding = np.random.rand(512)
202
- self.embedding = self.embedding / np.linalg.norm(self.embedding)
203
- self.det_score = 0.85 + (image_hash % 15) / 100
204
- self.bbox = [50, 50, 200, 200]
205
-
206
- return [MockFace(image_hash)]
207
 
208
- # Initialize the system
209
- print("Initializing Face Matching System...")
210
- face_system = FaceMatchingSystem()
211
- face_system.load_database()
212
 
213
- # Create a simple, robust Gradio interface
214
- with gr.Blocks(title="FaceMatch Pro") as demo:
215
-
216
- gr.Markdown("# 🎯 FaceMatch Pro")
217
- gr.Markdown("### Professional Face Recognition System")
218
-
219
- # Status display
220
- status_display = gr.Textbox(
221
- label="System Status",
222
- value=face_system.model_status,
223
- interactive=False
224
- )
225
-
226
- with gr.Tabs():
227
- # Tab 1: Add Face
228
  with gr.Tab("Add Face"):
229
- gr.Markdown("### Add a face to the database")
230
-
231
  with gr.Row():
 
232
  with gr.Column():
233
- add_image = gr.Image(label="Upload Photo", type="pil")
234
- person_name = gr.Textbox(label="Person Name", placeholder="Enter name...")
235
  add_btn = gr.Button("Add to Database", variant="primary")
236
-
237
- with gr.Column():
238
- add_result = gr.Textbox(label="Result", lines=3)
239
- database_info = gr.Textbox(
240
- label="Database Info",
241
- lines=6,
242
- value=face_system.get_database_info()
243
- )
244
 
245
  add_btn.click(
246
- face_system.add_face_to_database,
247
- inputs=[add_image, person_name],
248
- outputs=[add_result, database_info]
249
  )
250
 
251
- # Tab 2: Match Face
252
  with gr.Tab("Match Face"):
253
- gr.Markdown("### Find face matches")
254
-
255
  with gr.Row():
 
256
  with gr.Column():
257
- match_image = gr.Image(label="Upload Photo to Match", type="pil")
258
- threshold = gr.Slider(
259
- minimum=0.3,
260
- maximum=0.9,
261
- value=0.6,
262
- step=0.05,
263
- label="Matching Threshold"
264
- )
265
- match_btn = gr.Button("Find Matches", variant="primary")
266
-
267
- with gr.Column():
268
  match_result = gr.Textbox(label="Match Result", lines=2)
269
- confidence_text = gr.Textbox(label="Confidence Details", lines=2)
270
- confidence_score = gr.Number(label="Confidence Score (%)", precision=1)
271
 
272
  match_btn.click(
273
  face_system.match_face,
274
- inputs=[match_image, threshold],
275
- outputs=[match_result, confidence_text, confidence_score]
276
  )
277
 
278
- # Tab 3: Database Management
279
  with gr.Tab("Database"):
280
- gr.Markdown("### Database Management")
281
-
282
- db_stats = gr.Textbox(
283
- label="Database Contents",
284
- lines=8,
285
- value=face_system.get_database_info()
286
- )
287
-
288
  with gr.Row():
289
- refresh_btn = gr.Button("Refresh Info", variant="secondary")
290
  clear_btn = gr.Button("Clear Database", variant="stop")
 
291
 
292
- clear_result = gr.Textbox(label="Action Result", lines=2)
293
-
294
- refresh_btn.click(
295
- lambda: face_system.get_database_info(),
296
- outputs=[db_stats]
297
- )
298
-
299
- clear_btn.click(
300
- face_system.clear_database,
301
- outputs=[clear_result, db_stats]
302
- )
303
 
304
  if __name__ == "__main__":
305
  demo.launch()
 
3
  from PIL import Image
4
  import os
5
  import json
 
6
 
7
+ # Try to import InsightFace
 
8
  try:
9
  from insightface.app.face_analysis import FaceAnalysis
 
10
  INSIGHTFACE_AVAILABLE = True
11
  print("✓ InsightFace available")
12
  except Exception as e:
13
+ INSIGHTFACE_AVAILABLE = False
14
  print(f"InsightFace not available: {e}")
 
15
 
16
+ class SimpleFaceSystem:
17
  def __init__(self):
 
18
  self.app = None
19
+ self.database = {}
20
+ self.status = "Initializing..."
21
+ self.setup()
22
+
23
+ def setup(self):
24
+ if INSIGHTFACE_AVAILABLE:
25
+ try:
26
+ print("Loading InsightFace models...")
27
+ self.app = FaceAnalysis(name='buffalo_l', providers=['CPUExecutionProvider'])
28
+ self.app.prepare(ctx_id=0, det_thresh=0.5, det_size=(640, 640))
29
+ self.status = "✓ InsightFace loaded"
30
+ print(self.status)
31
+ except Exception as e:
32
+ print(f"Failed to load InsightFace: {e}")
33
+ self.status = f"Demo mode (InsightFace failed: {str(e)[:50]})"
34
+ else:
35
+ self.status = "Demo mode (InsightFace not available)"
36
 
37
+ # Load existing database
 
38
  try:
39
+ if os.path.exists('faces.json'):
40
+ with open('faces.json', 'r') as f:
41
+ self.database = json.load(f)
42
+ print(f"Loaded {len(self.database)} faces from database")
43
+ except:
44
+ self.database = {}
45
+
46
+ def get_embedding(self, image):
47
+ if not self.app or not image:
48
+ # Demo mode - return random but consistent embedding
49
+ if image:
50
+ seed = int(np.array(image).mean() * 1000) % 1000
51
+ np.random.seed(seed)
52
+ emb = np.random.rand(512)
53
+ return emb / np.linalg.norm(emb), "Demo embedding"
54
+ return None, "No image"
55
+
 
 
 
 
 
 
 
 
 
56
  try:
57
+ img_array = np.array(image.convert('RGB'))
58
+ faces = self.app.get(img_array)
59
+ if not faces:
60
+ return None, "No face detected"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  face = faces[0]
62
+ return face.embedding, f"Face detected (confidence: {face.det_score:.2f})"
 
 
 
 
63
  except Exception as e:
64
+ return None, f"Error: {str(e)}"
65
+
66
+ def add_face(self, image, name):
67
+ if not name or not name.strip():
68
+ return "Please enter a name", self.get_db_info()
 
 
69
 
70
+ name = name.strip()
71
+ embedding, msg = self.get_embedding(image)
72
 
 
73
  if embedding is None:
74
+ return f"Failed: {msg}", self.get_db_info()
75
 
76
+ self.database[name] = embedding.tolist()
 
 
 
 
77
 
78
  # Save database
79
+ try:
80
+ with open('faces.json', 'w') as f:
81
+ json.dump(self.database, f)
82
+ except:
83
+ pass
84
 
85
+ return f"✓ Added {name} ({msg})", self.get_db_info()
86
+
87
+ def match_face(self, image):
88
+ if not self.database:
89
+ return "Database is empty", ""
90
+
91
+ if not image:
92
+ return "Please upload an image", ""
93
 
94
+ embedding, msg = self.get_embedding(image)
95
  if embedding is None:
96
+ return f"Failed: {msg}", ""
97
 
98
  best_match = None
99
+ best_score = -1
100
 
101
+ for name, stored_emb in self.database.items():
102
+ stored_emb = np.array(stored_emb)
103
+ score = np.dot(embedding, stored_emb) / (np.linalg.norm(embedding) * np.linalg.norm(stored_emb))
104
+ if score > best_score:
105
+ best_score = score
106
+ best_match = name
 
 
 
 
 
107
 
108
+ if best_score > 0.6:
109
+ return f"✓ Match: {best_match} ({best_score:.2f})", f"Confidence: {best_score*100:.1f}%"
 
 
 
 
 
110
  else:
111
+ return "❌ No match found", f"Best score: {best_score:.2f} (threshold: 0.6)"
112
+
113
+ def get_db_info(self):
114
+ if not self.database:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  return "Database is empty"
116
+ return f"Database has {len(self.database)} faces: {', '.join(list(self.database.keys())[:5])}"
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ def clear_db(self):
119
+ self.database = {}
120
+ try:
121
+ with open('faces.json', 'w') as f:
122
+ json.dump({}, f)
123
+ except:
124
+ pass
125
+ return "Database cleared", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ # Initialize system
128
+ print("Starting FaceMatch system...")
129
+ face_system = SimpleFaceSystem()
 
130
 
131
+ # Create interface
132
+ def create_app():
133
+ with gr.Blocks(title="FaceMatch Pro") as app:
134
+ gr.Markdown("# 🎯 FaceMatch Pro")
135
+ gr.Markdown("### Professional Face Recognition System")
136
+
137
+ # Status
138
+ gr.Textbox(value=face_system.status, label="System Status", interactive=False)
139
+
 
 
 
 
 
 
140
  with gr.Tab("Add Face"):
 
 
141
  with gr.Row():
142
+ add_img = gr.Image(type="pil", label="Upload Photo")
143
  with gr.Column():
144
+ add_name = gr.Textbox(label="Name", placeholder="Enter person's name")
 
145
  add_btn = gr.Button("Add to Database", variant="primary")
146
+ add_result = gr.Textbox(label="Result", lines=2)
147
+ add_info = gr.Textbox(label="Database Info", lines=3, value=face_system.get_db_info())
 
 
 
 
 
 
148
 
149
  add_btn.click(
150
+ face_system.add_face,
151
+ inputs=[add_img, add_name],
152
+ outputs=[add_result, add_info]
153
  )
154
 
 
155
  with gr.Tab("Match Face"):
 
 
156
  with gr.Row():
157
+ match_img = gr.Image(type="pil", label="Upload Photo to Match")
158
  with gr.Column():
159
+ match_btn = gr.Button("Find Match", variant="primary")
 
 
 
 
 
 
 
 
 
 
160
  match_result = gr.Textbox(label="Match Result", lines=2)
161
+ match_conf = gr.Textbox(label="Confidence", lines=2)
 
162
 
163
  match_btn.click(
164
  face_system.match_face,
165
+ inputs=[match_img],
166
+ outputs=[match_result, match_conf]
167
  )
168
 
 
169
  with gr.Tab("Database"):
170
+ db_info = gr.Textbox(label="Database Contents", lines=5, value=face_system.get_db_info())
 
 
 
 
 
 
 
171
  with gr.Row():
172
+ refresh_btn = gr.Button("Refresh")
173
  clear_btn = gr.Button("Clear Database", variant="stop")
174
+ clear_result = gr.Textbox(label="Result", lines=2)
175
 
176
+ refresh_btn.click(lambda: face_system.get_db_info(), outputs=[db_info])
177
+ clear_btn.click(face_system.clear_db, outputs=[clear_result, db_info])
178
+
179
+ return app
180
+
181
+ # Create and launch
182
+ demo = create_app()
 
 
 
 
183
 
184
  if __name__ == "__main__":
185
  demo.launch()