Johnnyyyyy56 commited on
Commit
e968d95
·
verified ·
1 Parent(s): e9b69ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -54
app.py CHANGED
@@ -9,15 +9,23 @@ import time
9
  import numpy as np
10
  import logging
11
  from glob import glob
 
 
 
12
 
13
  # Set up logging
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger(__name__)
16
 
17
- # Emotion mapping with emojis
18
- EMOJI_MAP = {
19
- "angry": "😠", "disgust": "🤢", "fear": "😨",
20
- "happy": "😄", "sad": "😢", "surprise": "😲", "neutral": "😐"
 
 
 
 
 
21
  }
22
 
23
  # Global variable for processing throttle
@@ -27,18 +35,24 @@ last_process_time = 0
27
  BACKENDS = ['mtcnn', 'opencv', 'ssd', 'dlib']
28
 
29
  # Directory to save faces
30
- SAVE_DIR = "/tmp/faces"
31
- os.makedirs(SAVE_DIR, exist_ok=True)
32
 
33
- def clean_old_files(directory, max_files=20):
 
 
 
 
34
  """Keep only the most recent files to avoid storage issues"""
35
- files = glob(os.path.join(directory, "detected_face_*.jpg"))
36
- files.sort(key=os.path.getmtime)
37
- while len(files) > max_files:
38
- try:
39
- os.remove(files.pop(0))
40
- except Exception as e:
41
- logger.warning(f"Could not remove old file: {e}")
 
 
42
 
43
  def enhance_image(frame):
44
  """Enhance image contrast to improve face detection in poor lighting."""
@@ -55,6 +69,24 @@ def enhance_image(frame):
55
  logger.warning(f"Image enhancement failed: {e}")
56
  return frame
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  def predict_emotion(input_data):
59
  global last_process_time
60
 
@@ -128,29 +160,23 @@ def predict_emotion(input_data):
128
  y2 = min(frame.shape[0], y + h + padding)
129
  face_region = frame[y1:y2, x1:x2]
130
 
131
- # Save face to file if valid region
132
  if face_region.size > 0:
133
- timestamp = int(time.time())
134
- face_filename = f"detected_face_{timestamp}_{i}.jpg"
135
- save_path = os.path.join(SAVE_DIR, face_filename)
136
- try:
137
- cv2.imwrite(save_path, cv2.cvtColor(face_region, cv2.COLOR_RGB2BGR))
138
- logger.info(f"Saved face to: {save_path}")
139
- except Exception as e:
140
- logger.error(f"Failed to save face: {e}")
141
 
142
  # Draw bounding box
143
  cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
144
 
145
  # Put emotion text on image
146
- cv2.putText(frame, f"{emotion} {EMOJI_MAP.get(emotion, '')}",
 
147
  (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
148
 
149
  # Add to text output
150
- emotion_text += f"{emotion.title()} {EMOJI_MAP.get(emotion, '')}: {confidence:.1f}%\n"
151
 
152
  # Clean up old files
153
- clean_old_files(SAVE_DIR)
154
 
155
  if not emotion_text:
156
  emotion_text = "No faces detected. Ensure the face is clearly visible."
@@ -165,19 +191,57 @@ def predict_emotion(input_data):
165
  return None, f"Error: {str(e)}. Check image format or library installation."
166
 
167
  def get_saved_faces():
168
- """Get list of saved face images"""
169
  face_files = []
170
- for f in os.listdir(SAVE_DIR):
171
- if f.startswith("detected_face_") and f.endswith(".jpg"):
172
- face_files.append(os.path.join(SAVE_DIR, f))
 
 
173
  return face_files
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  # Create Gradio interface
176
- with gr.Blocks(title="Emotion Detection with Face Saving") as demo:
177
  gr.Markdown("""
178
  # Real-Time Emotion Detection
179
- **Use webcam or upload an image to detect emotions**
180
- *Detected faces are saved temporarily and can be downloaded below*
181
  """)
182
 
183
  with gr.Tabs():
@@ -193,46 +257,103 @@ with gr.Blocks(title="Emotion Detection with Face Saving") as demo:
193
  upload_text = gr.Textbox(label="Emotion Analysis")
194
  uploader.change(predict_emotion, uploader, [upload_output, upload_text])
195
 
196
- with gr.TabItem("💾 Saved Faces"):
197
- gr.Markdown("### Recently Detected Faces")
 
198
  with gr.Row():
199
- face_gallery = gr.Gallery(label="Saved Faces", columns=4)
200
- download_files = gr.File(label="Download Files", file_count="multiple")
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
  refresh_btn = gr.Button("🔄 Refresh")
203
  clear_btn = gr.Button("🗑️ Clear All Saved Faces")
204
 
205
- def update_face_display():
206
- faces = get_saved_faces()
207
- return faces, faces
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
  refresh_btn.click(
210
- update_face_display,
211
- outputs=[face_gallery, download_files]
 
212
  )
213
 
214
  def clear_faces():
215
- for f in get_saved_faces():
216
- try:
217
- os.remove(f)
218
- except:
219
- pass
220
- return [], []
 
 
 
221
 
222
  clear_btn.click(
223
  clear_faces,
224
- outputs=[face_gallery, download_files]
225
  )
226
 
227
  gr.Button("Clear All").click(
228
- lambda: [None, None, "", None, None, "", [], []],
229
- outputs=[webcam, webcam_output, webcam_text, uploader, upload_output, upload_text, face_gallery, download_files]
 
230
  )
231
 
232
- # Initialize the face display
233
  demo.load(
234
- update_face_display,
235
- outputs=[face_gallery, download_files]
236
  )
237
 
238
  if __name__ == "__main__":
 
9
  import numpy as np
10
  import logging
11
  from glob import glob
12
+ import zipfile
13
+ import io
14
+ import shutil
15
 
16
  # Set up logging
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger(__name__)
19
 
20
+ # Emotion mapping with emojis and corresponding folder names
21
+ EMOTION_MAP = {
22
+ "angry": {"emoji": "😠", "folder": "angry"},
23
+ "disgust": {"emoji": "🤢", "folder": "disgust"},
24
+ "fear": {"emoji": "😨", "folder": "fear"},
25
+ "happy": {"emoji": "😄", "folder": "happy"},
26
+ "sad": {"emoji": "😢", "folder": "sad"},
27
+ "surprise": {"emoji": "😲", "folder": "surprise"},
28
+ "neutral": {"emoji": "😐", "folder": "neutral"}
29
  }
30
 
31
  # Global variable for processing throttle
 
35
  BACKENDS = ['mtcnn', 'opencv', 'ssd', 'dlib']
36
 
37
  # Directory to save faces
38
+ BASE_SAVE_DIR = "/tmp/emotion_dataset"
39
+ os.makedirs(BASE_SAVE_DIR, exist_ok=True)
40
 
41
+ # Create emotion subdirectories
42
+ for emotion in EMOTION_MAP.values():
43
+ os.makedirs(os.path.join(BASE_SAVE_DIR, emotion["folder"]), exist_ok=True)
44
+
45
+ def clean_old_files(directory, max_files_per_emotion=50):
46
  """Keep only the most recent files to avoid storage issues"""
47
+ for emotion in EMOTION_MAP.values():
48
+ emotion_dir = os.path.join(directory, emotion["folder"])
49
+ files = glob(os.path.join(emotion_dir, "*.jpg"))
50
+ files.sort(key=os.path.getmtime)
51
+ while len(files) > max_files_per_emotion:
52
+ try:
53
+ os.remove(files.pop(0))
54
+ except Exception as e:
55
+ logger.warning(f"Could not remove old file: {e}")
56
 
57
  def enhance_image(frame):
58
  """Enhance image contrast to improve face detection in poor lighting."""
 
69
  logger.warning(f"Image enhancement failed: {e}")
70
  return frame
71
 
72
+ def save_face_image(face_region, emotion, confidence):
73
+ """Save face image to appropriate emotion folder with metadata in filename"""
74
+ try:
75
+ emotion_data = EMOTION_MAP.get(emotion, EMOTION_MAP["neutral"])
76
+ folder = emotion_data["folder"]
77
+ timestamp = int(time.time())
78
+
79
+ # Create filename with emotion, confidence, and timestamp
80
+ filename = f"{emotion}_{confidence:.1f}%_{timestamp}.jpg"
81
+ save_path = os.path.join(BASE_SAVE_DIR, folder, filename)
82
+
83
+ cv2.imwrite(save_path, cv2.cvtColor(face_region, cv2.COLOR_RGB2BGR))
84
+ logger.info(f"Saved {emotion} face to: {save_path}")
85
+ return save_path
86
+ except Exception as e:
87
+ logger.error(f"Failed to save face: {e}")
88
+ return None
89
+
90
  def predict_emotion(input_data):
91
  global last_process_time
92
 
 
160
  y2 = min(frame.shape[0], y + h + padding)
161
  face_region = frame[y1:y2, x1:x2]
162
 
163
+ # Save face to appropriate emotion folder if valid region
164
  if face_region.size > 0:
165
+ save_face_image(face_region, emotion, confidence)
 
 
 
 
 
 
 
166
 
167
  # Draw bounding box
168
  cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
169
 
170
  # Put emotion text on image
171
+ emoji = EMOTION_MAP.get(emotion, {}).get("emoji", "")
172
+ cv2.putText(frame, f"{emotion} {emoji}",
173
  (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
174
 
175
  # Add to text output
176
+ emotion_text += f"{emotion.title()} {emoji}: {confidence:.1f}%\n"
177
 
178
  # Clean up old files
179
+ clean_old_files(BASE_SAVE_DIR)
180
 
181
  if not emotion_text:
182
  emotion_text = "No faces detected. Ensure the face is clearly visible."
 
191
  return None, f"Error: {str(e)}. Check image format or library installation."
192
 
193
  def get_saved_faces():
194
+ """Get list of saved face images from all emotion folders"""
195
  face_files = []
196
+ for emotion in EMOTION_MAP.values():
197
+ emotion_dir = os.path.join(BASE_SAVE_DIR, emotion["folder"])
198
+ for f in os.listdir(emotion_dir):
199
+ if f.endswith(".jpg"):
200
+ face_files.append(os.path.join(emotion_dir, f))
201
  return face_files
202
 
203
+ def create_zip_file():
204
+ """Create a zip file containing all saved faces organized by emotion"""
205
+ face_files = get_saved_faces()
206
+ if not face_files:
207
+ return None
208
+
209
+ zip_buffer = io.BytesIO()
210
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
211
+ for face_file in face_files:
212
+ # Maintain directory structure in zip
213
+ relative_path = os.path.relpath(face_file, BASE_SAVE_DIR)
214
+ zip_file.write(face_file, relative_path)
215
+
216
+ zip_buffer.seek(0)
217
+ return zip_buffer
218
+
219
+ def create_emotion_zip(emotion):
220
+ """Create a zip file for a specific emotion"""
221
+ emotion_data = EMOTION_MAP.get(emotion)
222
+ if not emotion_data:
223
+ return None
224
+
225
+ emotion_dir = os.path.join(BASE_SAVE_DIR, emotion_data["folder"])
226
+ face_files = [os.path.join(emotion_dir, f) for f in os.listdir(emotion_dir) if f.endswith(".jpg")]
227
+
228
+ if not face_files:
229
+ return None
230
+
231
+ zip_buffer = io.BytesIO()
232
+ with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
233
+ for face_file in face_files:
234
+ zip_file.write(face_file, os.path.basename(face_file))
235
+
236
+ zip_buffer.seek(0)
237
+ return zip_buffer
238
+
239
  # Create Gradio interface
240
+ with gr.Blocks(title="Emotion Detection with Dataset Creation") as demo:
241
  gr.Markdown("""
242
  # Real-Time Emotion Detection
243
+ **Use webcam or upload an image to detect emotions and build a training dataset**
244
+ *Detected faces are automatically classified by emotion and saved in organized folders*
245
  """)
246
 
247
  with gr.Tabs():
 
257
  upload_text = gr.Textbox(label="Emotion Analysis")
258
  uploader.change(predict_emotion, uploader, [upload_output, upload_text])
259
 
260
+ with gr.TabItem("💾 Emotion Dataset"):
261
+ gr.Markdown("### Classified Faces by Emotion")
262
+
263
  with gr.Row():
264
+ with gr.Column():
265
+ emotion_selector = gr.Dropdown(
266
+ choices=list(EMOTION_MAP.keys()),
267
+ label="Select Emotion to View/Download",
268
+ value="happy"
269
+ )
270
+ emotion_gallery = gr.Gallery(label="Selected Emotion Faces")
271
+ emotion_zip = gr.File(label="Download Emotion as ZIP", visible=False)
272
+ download_emotion_btn = gr.Button("📦 Download This Emotion")
273
+
274
+ with gr.Column():
275
+ all_gallery = gr.Gallery(label="All Detected Faces")
276
+ all_zip = gr.File(label="Download All as ZIP", visible=False)
277
+ download_all_btn = gr.Button("📦 Download Entire Dataset")
278
 
279
  refresh_btn = gr.Button("🔄 Refresh")
280
  clear_btn = gr.Button("🗑️ Clear All Saved Faces")
281
 
282
+ def update_emotion_display(emotion):
283
+ emotion_data = EMOTION_MAP.get(emotion, {})
284
+ if not emotion_data:
285
+ return [], gr.File(visible=False)
286
+
287
+ emotion_dir = os.path.join(BASE_SAVE_DIR, emotion_data["folder"])
288
+ face_files = [os.path.join(emotion_dir, f) for f in os.listdir(emotion_dir) if f.endswith(".jpg")]
289
+ return face_files, gr.File(visible=False)
290
+
291
+ def update_all_display():
292
+ return get_saved_faces(), gr.File(visible=False)
293
+
294
+ def download_selected_emotion(emotion):
295
+ zip_file = create_emotion_zip(emotion)
296
+ if zip_file:
297
+ return gr.File(value=zip_file, visible=True,
298
+ label=f"Download {emotion.capitalize()} Faces")
299
+ return gr.File(visible=False)
300
+
301
+ def download_full_dataset():
302
+ zip_file = create_zip_file()
303
+ if zip_file:
304
+ return gr.File(value=zip_file, visible=True,
305
+ label="Download Entire Dataset")
306
+ return gr.File(visible=False)
307
+
308
+ emotion_selector.change(
309
+ update_emotion_display,
310
+ inputs=[emotion_selector],
311
+ outputs=[emotion_gallery, emotion_zip]
312
+ )
313
+
314
+ download_emotion_btn.click(
315
+ download_selected_emotion,
316
+ inputs=[emotion_selector],
317
+ outputs=[emotion_zip]
318
+ )
319
+
320
+ download_all_btn.click(
321
+ download_full_dataset,
322
+ outputs=[all_zip]
323
+ )
324
 
325
  refresh_btn.click(
326
+ lambda: [update_emotion_display(emotion_selector.value),
327
+ update_all_display()],
328
+ outputs=[emotion_gallery, emotion_zip, all_gallery, all_zip]
329
  )
330
 
331
  def clear_faces():
332
+ for emotion in EMOTION_MAP.values():
333
+ emotion_dir = os.path.join(BASE_SAVE_DIR, emotion["folder"])
334
+ for f in os.listdir(emotion_dir):
335
+ if f.endswith(".jpg"):
336
+ try:
337
+ os.remove(os.path.join(emotion_dir, f))
338
+ except:
339
+ pass
340
+ return [], gr.File(visible=False), [], gr.File(visible=False)
341
 
342
  clear_btn.click(
343
  clear_faces,
344
+ outputs=[emotion_gallery, emotion_zip, all_gallery, all_zip]
345
  )
346
 
347
  gr.Button("Clear All").click(
348
+ lambda: [None, None, "", None, None, "", [], gr.File(visible=False), [], gr.File(visible=False)],
349
+ outputs=[webcam, webcam_output, webcam_text, uploader, upload_output, upload_text,
350
+ emotion_gallery, emotion_zip, all_gallery, all_zip]
351
  )
352
 
353
+ # Initialize the display
354
  demo.load(
355
+ lambda: [update_emotion_display(emotion_selector.value), update_all_display()],
356
+ outputs=[emotion_gallery, emotion_zip, all_gallery, all_zip]
357
  )
358
 
359
  if __name__ == "__main__":