Johnnyyyyy56 commited on
Commit
5258012
·
verified ·
1 Parent(s): 4646367

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +200 -55
app.py CHANGED
@@ -26,22 +26,25 @@ BACKENDS = ['opencv', 'mtcnn', 'ssd', 'dlib']
26
  SAVE_DIR = Path("/tmp/emotion_results")
27
  SAVE_DIR.mkdir(exist_ok=True)
28
 
29
- # Create emotion subdirectories
 
 
30
  for emotion in EMOTION_MAP.keys():
31
- (SAVE_DIR / emotion).mkdir(exist_ok=True)
 
32
 
33
  # Log file setup
34
  LOG_FILE = SAVE_DIR / "emotion_logs.csv"
35
  if not LOG_FILE.exists():
36
  with open(LOG_FILE, 'w', newline='') as f:
37
  writer = csv.writer(f)
38
- writer.writerow(["timestamp", "batch_no", "emotion", "confidence", "image_path"])
39
 
40
- def log_emotion(batch_no, emotion, confidence, image_path):
41
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
42
  with open(LOG_FILE, 'a', newline='') as f:
43
  writer = csv.writer(f)
44
- writer.writerow([timestamp, batch_no, emotion, confidence, image_path])
45
 
46
  def predict_emotion(batch_no: str, image):
47
  if not batch_no.strip():
@@ -80,23 +83,31 @@ def predict_emotion(batch_no: str, image):
80
  confidence = result['emotion'][emotion]
81
  region = result['region']
82
 
83
- # Draw annotations on the image
84
  x, y, w, h = region['x'], region['y'], region['w'], region['h']
85
- cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
86
- cv2.putText(frame, f"{emotion} {EMOTION_MAP[emotion]} {confidence:.1f}%",
87
- (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
88
 
89
- # Save the result in emotion-specific folder
 
90
  timestamp = int(time.time())
91
- emotion_dir = SAVE_DIR / emotion
92
- output_path = str(emotion_dir / f"{batch_no}_{timestamp}.jpg")
93
- cv2.imwrite(output_path, frame)
 
 
 
 
 
 
94
 
95
- # Log the result
96
- log_emotion(batch_no, emotion, confidence, output_path)
 
 
 
 
97
 
98
  # Convert back to PIL format for display
99
- output_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
100
  return output_img, f"Batch {batch_no}: {emotion.title()} ({confidence:.1f}%)", "", gr.Image(visible=True), gr.Textbox(visible=True)
101
 
102
  except Exception as e:
@@ -107,21 +118,66 @@ def toggle_webcam(batch_no):
107
  return gr.Image(visible=True), gr.Image(visible=False), gr.Textbox(visible=False), ""
108
  return gr.Image(visible=False), gr.Image(visible=False), gr.Textbox(visible=False), "Please enter a batch number"
109
 
110
- def create_zip(emotion=None):
111
- zip_path = SAVE_DIR / "emotion_results.zip"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  with zipfile.ZipFile(zip_path, 'w') as zipf:
113
- if emotion and emotion != "All Emotions":
114
- # Zip specific emotion folder
115
- folder = SAVE_DIR / emotion
116
- for file in folder.iterdir():
117
- zipf.write(file, arcname=f"{emotion}/{file.name}")
118
- else:
119
- # Zip all images
120
- for emotion in EMOTION_MAP.keys():
121
- folder = SAVE_DIR / emotion
122
- for file in folder.iterdir():
123
- zipf.write(file, arcname=f"{emotion}/{file.name}")
124
- return str(zip_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  def get_logs():
127
  if LOG_FILE.exists():
@@ -139,12 +195,15 @@ def download_logs():
139
  return str(LOG_FILE)
140
  return None
141
 
142
- def clear_data():
 
143
  # Clear all images
144
  for emotion in EMOTION_MAP.keys():
145
- folder = SAVE_DIR / emotion
146
- for file in folder.glob("*"):
147
- file.unlink()
 
 
148
 
149
  # Clear logs
150
  if LOG_FILE.exists():
@@ -153,7 +212,7 @@ def clear_data():
153
  # Recreate empty log file
154
  with open(LOG_FILE, 'w', newline='') as f:
155
  writer = csv.writer(f)
156
- writer.writerow(["timestamp", "batch_no", "emotion", "confidence", "image_path"])
157
 
158
  return "All data has been cleared", pd.DataFrame(), None
159
 
@@ -161,6 +220,7 @@ def clear_data():
161
  with gr.Blocks(title="Emotion Capture", css="""
162
  .gradio-container { max-width: 800px !important }
163
  .message { color: red; font-weight: bold; }
 
164
  """) as capture_interface:
165
 
166
  gr.Markdown("""
@@ -205,44 +265,117 @@ with gr.Blocks(title="Emotion Capture", css="""
205
 
206
  # Data Management Interface
207
  with gr.Blocks(title="Data Management", css="""
208
- .gradio-container { max-width: 900px !important }
209
  .data-section { border: 1px solid #ccc; padding: 20px; border-radius: 5px; margin-top: 20px; }
 
 
210
  """) as data_interface:
211
 
212
  gr.Markdown("# Data Management Interface")
213
 
214
- with gr.Tab("Download Images"):
215
- with gr.Column(elem_classes="data-section"):
216
- gr.Markdown("## Download Options")
217
  with gr.Row():
218
  emotion_selector = gr.Dropdown(
219
  choices=["All Emotions"] + list(EMOTION_MAP.keys()),
220
- label="Select Emotion Category",
221
- value="All Emotions",
222
- interactive=True
 
 
 
 
223
  )
224
- download_btn = gr.Button("Download Images as ZIP")
225
- download_file = gr.File(label="Download Result")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  download_btn.click(
228
- create_zip,
229
- inputs=emotion_selector,
230
- outputs=download_file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  )
232
 
233
  with gr.Tab("Emotion Logs"):
234
- with gr.Column(elem_classes="data-section"):
235
- gr.Markdown("## Emotion Logs")
236
  with gr.Row():
237
- refresh_btn = gr.Button("Refresh Logs")
238
  download_logs_btn = gr.Button("Download Logs as CSV")
239
- clear_btn = gr.Button("Clear All Data", variant="stop")
240
 
241
  logs_display = gr.Markdown()
242
  logs_csv = gr.File(label="Logs Download")
243
  clear_message = gr.Textbox(label="Status", interactive=False)
244
 
245
- refresh_btn.click(
246
  view_logs,
247
  outputs=logs_display
248
  )
@@ -252,9 +385,12 @@ with gr.Blocks(title="Data Management", css="""
252
  outputs=logs_csv
253
  )
254
 
255
- clear_btn.click(
256
- clear_data,
257
  outputs=[clear_message, logs_display, logs_csv]
 
 
 
258
  )
259
 
260
  # Initial load of logs
@@ -266,7 +402,16 @@ with gr.Blocks(title="Data Management", css="""
266
  # Combine interfaces
267
  demo = gr.TabbedInterface(
268
  [capture_interface, data_interface],
269
- ["Emotion Capture", "Data Management"]
 
 
 
 
 
 
 
 
 
270
  )
271
 
272
  if __name__ == "__main__":
 
26
  SAVE_DIR = Path("/tmp/emotion_results")
27
  SAVE_DIR.mkdir(exist_ok=True)
28
 
29
+ # Create directories
30
+ (SAVE_DIR / "faces").mkdir(exist_ok=True) # For raw face crops
31
+ (SAVE_DIR / "annotated").mkdir(exist_ok=True) # For annotated images
32
  for emotion in EMOTION_MAP.keys():
33
+ (SAVE_DIR / "faces" / emotion).mkdir(exist_ok=True, parents=True)
34
+ (SAVE_DIR / "annotated" / emotion).mkdir(exist_ok=True, parents=True)
35
 
36
  # Log file setup
37
  LOG_FILE = SAVE_DIR / "emotion_logs.csv"
38
  if not LOG_FILE.exists():
39
  with open(LOG_FILE, 'w', newline='') as f:
40
  writer = csv.writer(f)
41
+ writer.writerow(["timestamp", "batch_no", "emotion", "confidence", "face_path", "annotated_path"])
42
 
43
+ def log_emotion(batch_no, emotion, confidence, face_path, annotated_path):
44
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
45
  with open(LOG_FILE, 'a', newline='') as f:
46
  writer = csv.writer(f)
47
+ writer.writerow([timestamp, batch_no, emotion, confidence, str(face_path), str(annotated_path)])
48
 
49
  def predict_emotion(batch_no: str, image):
50
  if not batch_no.strip():
 
83
  confidence = result['emotion'][emotion]
84
  region = result['region']
85
 
86
+ # Extract face coordinates
87
  x, y, w, h = region['x'], region['y'], region['w'], region['h']
 
 
 
88
 
89
+ # 1. Save raw face crop (for training)
90
+ face_crop = frame[y:y+h, x:x+w]
91
  timestamp = int(time.time())
92
+ face_dir = SAVE_DIR / "faces" / emotion
93
+ face_path = face_dir / f"{batch_no}_{timestamp}.jpg"
94
+ cv2.imwrite(str(face_path), face_crop)
95
+
96
+ # 2. Create and save annotated image (for display)
97
+ annotated_frame = frame.copy()
98
+ cv2.rectangle(annotated_frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
99
+ cv2.putText(annotated_frame, f"{emotion} {EMOTION_MAP[emotion]} {confidence:.1f}%",
100
+ (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
101
 
102
+ annotated_dir = SAVE_DIR / "annotated" / emotion
103
+ annotated_path = annotated_dir / f"{batch_no}_{timestamp}.jpg"
104
+ cv2.imwrite(str(annotated_path), annotated_frame)
105
+
106
+ # Log both paths
107
+ log_emotion(batch_no, emotion, confidence, face_path, annotated_path)
108
 
109
  # Convert back to PIL format for display
110
+ output_img = Image.fromarray(cv2.cvtColor(annotated_frame, cv2.COLOR_BGR2RGB))
111
  return output_img, f"Batch {batch_no}: {emotion.title()} ({confidence:.1f}%)", "", gr.Image(visible=True), gr.Textbox(visible=True)
112
 
113
  except Exception as e:
 
118
  return gr.Image(visible=True), gr.Image(visible=False), gr.Textbox(visible=False), ""
119
  return gr.Image(visible=False), gr.Image(visible=False), gr.Textbox(visible=False), "Please enter a batch number"
120
 
121
+ def get_image_gallery(emotion, image_type):
122
+ """Get image gallery for selected emotion and type"""
123
+ if emotion == "All Emotions":
124
+ image_dict = {}
125
+ for emot in EMOTION_MAP.keys():
126
+ folder = SAVE_DIR / image_type / emot
127
+ image_dict[emot] = [str(f) for f in folder.glob("*.jpg") if f.exists()]
128
+ else:
129
+ folder = SAVE_DIR / image_type / emotion
130
+ image_dict = {emotion: [str(f) for f in folder.glob("*.jpg") if f.exists()]}
131
+ return image_dict
132
+
133
+ def create_custom_zip(selected_images):
134
+ """Create zip from selected images"""
135
+ if not selected_images:
136
+ return None
137
+ zip_path = SAVE_DIR / "selected_images.zip"
138
  with zipfile.ZipFile(zip_path, 'w') as zipf:
139
+ for img_path in selected_images:
140
+ if Path(img_path).exists():
141
+ zipf.write(img_path, arcname=Path(img_path).name)
142
+ return str(zip_path) if zip_path.exists() else None
143
+
144
+ def delete_selected_images(selected_images):
145
+ """Delete selected images"""
146
+ if not selected_images:
147
+ return "No images selected"
148
+
149
+ deleted_count = 0
150
+ for img_path in selected_images:
151
+ try:
152
+ Path(img_path).unlink()
153
+ deleted_count += 1
154
+ except Exception as e:
155
+ print(f"Error deleting {img_path}: {e}")
156
+
157
+ # Update logs
158
+ if LOG_FILE.exists():
159
+ df = pd.read_csv(LOG_FILE)
160
+ for img_path in selected_images:
161
+ if "faces" in img_path:
162
+ df = df[df.face_path != img_path]
163
+ else:
164
+ df = df[df.annotated_path != img_path]
165
+ df.to_csv(LOG_FILE, index=False)
166
+
167
+ return f"Deleted {deleted_count} images"
168
+
169
+ def update_gallery(emotion, image_type):
170
+ """Update the image gallery view"""
171
+ image_dict = get_image_gallery(emotion, image_type)
172
+ gallery = []
173
+ for emotion, images in image_dict.items():
174
+ for img_path in images:
175
+ gallery.append((img_path, f"{emotion}: {Path(img_path).name}"))
176
+ return gr.Gallery(value=gallery, label="Image Gallery"), gr.CheckboxGroup(
177
+ choices=[img[0] for img in gallery],
178
+ label="Selected Images",
179
+ value=[]
180
+ )
181
 
182
  def get_logs():
183
  if LOG_FILE.exists():
 
195
  return str(LOG_FILE)
196
  return None
197
 
198
+ def clear_all_data():
199
+ """Clear all images and logs"""
200
  # Clear all images
201
  for emotion in EMOTION_MAP.keys():
202
+ for img_type in ["faces", "annotated"]:
203
+ folder = SAVE_DIR / img_type / emotion
204
+ for file in folder.glob("*"):
205
+ if file.is_file():
206
+ file.unlink()
207
 
208
  # Clear logs
209
  if LOG_FILE.exists():
 
212
  # Recreate empty log file
213
  with open(LOG_FILE, 'w', newline='') as f:
214
  writer = csv.writer(f)
215
+ writer.writerow(["timestamp", "batch_no", "emotion", "confidence", "face_path", "annotated_path"])
216
 
217
  return "All data has been cleared", pd.DataFrame(), None
218
 
 
220
  with gr.Blocks(title="Emotion Capture", css="""
221
  .gradio-container { max-width: 800px !important }
222
  .message { color: red; font-weight: bold; }
223
+ .gallery { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
224
  """) as capture_interface:
225
 
226
  gr.Markdown("""
 
265
 
266
  # Data Management Interface
267
  with gr.Blocks(title="Data Management", css="""
268
+ .gradio-container { max-width: 1200px !important }
269
  .data-section { border: 1px solid #ccc; padding: 20px; border-radius: 5px; margin-top: 20px; }
270
+ .gallery { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
271
+ .action-buttons { margin-top: 20px; }
272
  """) as data_interface:
273
 
274
  gr.Markdown("# Data Management Interface")
275
 
276
+ with gr.Tab("Image Management"):
277
+ with gr.Column():
278
+ gr.Markdown("## Select and Manage Images")
279
  with gr.Row():
280
  emotion_selector = gr.Dropdown(
281
  choices=["All Emotions"] + list(EMOTION_MAP.keys()),
282
+ label="Emotion Category",
283
+ value="All Emotions"
284
+ )
285
+ image_type_selector = gr.Dropdown(
286
+ choices=["faces", "annotated"],
287
+ label="Image Type",
288
+ value="faces"
289
  )
290
+ refresh_btn = gr.Button("Refresh Gallery")
291
+
292
+ gallery = gr.Gallery(
293
+ label="Image Gallery",
294
+ elem_classes="gallery",
295
+ columns=4
296
+ )
297
+ selected_images = gr.CheckboxGroup(
298
+ label="Selected Images",
299
+ interactive=True
300
+ )
301
+
302
+ with gr.Row(variant="panel"):
303
+ with gr.Column():
304
+ gr.Markdown("### Download Options")
305
+ download_btn = gr.Button("Download Selected", variant="primary")
306
+ download_all_btn = gr.Button("Download All in Category")
307
+ download_output = gr.File(label="Download Result")
308
+
309
+ with gr.Column():
310
+ gr.Markdown("### Delete Options")
311
+ delete_btn = gr.Button("Delete Selected", variant="stop")
312
+ delete_all_btn = gr.Button("Delete All in Category", variant="stop")
313
+ delete_output = gr.Textbox(label="Delete Status")
314
 
315
+ # Update gallery when parameters change
316
+ emotion_selector.change(
317
+ update_gallery,
318
+ inputs=[emotion_selector, image_type_selector],
319
+ outputs=[gallery, selected_images]
320
+ )
321
+
322
+ image_type_selector.change(
323
+ update_gallery,
324
+ inputs=[emotion_selector, image_type_selector],
325
+ outputs=[gallery, selected_images]
326
+ )
327
+
328
+ refresh_btn.click(
329
+ update_gallery,
330
+ inputs=[emotion_selector, image_type_selector],
331
+ outputs=[gallery, selected_images]
332
+ )
333
+
334
+ # Download handlers
335
  download_btn.click(
336
+ create_custom_zip,
337
+ inputs=selected_images,
338
+ outputs=download_output
339
+ )
340
+
341
+ download_all_btn.click(
342
+ lambda e, t: create_custom_zip([img[0] for img in get_image_gallery(e, t).items() for img in img[1]]),
343
+ inputs=[emotion_selector, image_type_selector],
344
+ outputs=download_output
345
+ )
346
+
347
+ # Delete handlers
348
+ delete_btn.click(
349
+ delete_selected_images,
350
+ inputs=selected_images,
351
+ outputs=delete_output
352
+ ).then(
353
+ lambda: update_gallery(emotion_selector.value, image_type_selector.value),
354
+ outputs=[gallery, selected_images]
355
+ )
356
+
357
+ delete_all_btn.click(
358
+ lambda e, t: delete_selected_images([img for img_list in get_image_gallery(e, t).values() for img in img_list]),
359
+ inputs=[emotion_selector, image_type_selector],
360
+ outputs=delete_output
361
+ ).then(
362
+ lambda: update_gallery(emotion_selector.value, image_type_selector.value),
363
+ outputs=[gallery, selected_images]
364
  )
365
 
366
  with gr.Tab("Emotion Logs"):
367
+ with gr.Column():
368
+ gr.Markdown("## Emotion Analysis Logs")
369
  with gr.Row():
370
+ refresh_logs_btn = gr.Button("Refresh Logs")
371
  download_logs_btn = gr.Button("Download Logs as CSV")
372
+ clear_all_btn = gr.Button("Clear All Data", variant="stop")
373
 
374
  logs_display = gr.Markdown()
375
  logs_csv = gr.File(label="Logs Download")
376
  clear_message = gr.Textbox(label="Status", interactive=False)
377
 
378
+ refresh_logs_btn.click(
379
  view_logs,
380
  outputs=logs_display
381
  )
 
385
  outputs=logs_csv
386
  )
387
 
388
+ clear_all_btn.click(
389
+ clear_all_data,
390
  outputs=[clear_message, logs_display, logs_csv]
391
+ ).then(
392
+ lambda: update_gallery("All Emotions", "faces"),
393
+ outputs=[gallery, selected_images]
394
  )
395
 
396
  # Initial load of logs
 
402
  # Combine interfaces
403
  demo = gr.TabbedInterface(
404
  [capture_interface, data_interface],
405
+ ["Emotion Capture", "Data Management"],
406
+ css="""
407
+ .tab { padding: 20px; }
408
+ """
409
+ )
410
+
411
+ # Initialize gallery and logs
412
+ demo.load(
413
+ lambda: update_gallery("All Emotions", "faces"),
414
+ outputs=[gallery, selected_images]
415
  )
416
 
417
  if __name__ == "__main__":