blackmamba2408 commited on
Commit
8d7356f
Β·
verified Β·
1 Parent(s): cdce318

πŸš€ Fixed app - removed CV2, simplified UI for HF Spaces

Browse files
Files changed (1) hide show
  1. app.py +305 -0
app.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ 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()