notrey commited on
Commit
9d078aa
·
1 Parent(s): 15f0220
Files changed (1) hide show
  1. app.py +29 -55
app.py CHANGED
@@ -53,13 +53,11 @@ selected_model = st.sidebar.selectbox(
53
  list(model_options.keys())
54
  )
55
 
56
- # Input method selection with addition of video upload and simulation
57
  input_method = st.sidebar.radio(
58
  "Choose Input Method",
59
  ["Real-time Webcam", "Upload an Image", "Capture Image"]
60
  )
61
 
62
- # Confidence threshold
63
  confidence_threshold = st.sidebar.slider(
64
  "Confidence Threshold",
65
  min_value=0.0,
@@ -68,11 +66,8 @@ confidence_threshold = st.sidebar.slider(
68
  step=0.05
69
  )
70
 
71
- # Face detection toggle
72
  use_face_detection = st.sidebar.checkbox("Enable Face Detection", value=True)
73
 
74
-
75
- # History length for real-time tracking
76
  if input_method in ["Real-time Webcam", "Upload Video", "Simulation Mode"]:
77
  history_length = st.sidebar.slider(
78
  "Emotion History Length (seconds)",
@@ -89,10 +84,9 @@ face_detector = load_face_detector()
89
  # --- Utility Functions ---
90
  def detect_faces(image):
91
  """Detect faces in an image using OpenCV."""
92
- # Convert PIL Image to OpenCV format
93
  if isinstance(image, Image.Image):
94
  opencv_image = np.array(image)
95
- opencv_image = opencv_image[:, :, ::-1].copy() # Convert RGB to BGR
96
  else:
97
  opencv_image = image
98
 
@@ -136,7 +130,6 @@ def draw_faces_with_emotions(image, faces, emotions):
136
  """Draw rectangles around faces and label with emotions."""
137
  img = image.copy()
138
 
139
- # Define colors for different emotions (BGR format)
140
  emotion_colors = {
141
  "happy": (0, 255, 0), # Green
142
  "sad": (255, 0, 0), # Blue
@@ -147,15 +140,14 @@ def draw_faces_with_emotions(image, faces, emotions):
147
  "disgust": (0, 128, 128) # Brown
148
  }
149
 
150
- # Default color for unknown emotions
151
- default_color = (255, 255, 255) # White
152
 
153
  for (x, y, w, h), emotion in zip(faces, emotions):
154
- # Get color based on emotion (lowercase and remove any prefix)
155
  emotion_key = emotion["label"].lower().split("_")[-1]
156
  color = emotion_colors.get(emotion_key, default_color)
157
 
158
- # Draw rectangle around face
159
  cv2.rectangle(img, (x, y), (x+w, y+h), color, 2)
160
 
161
  # Add emotion label and confidence
@@ -190,7 +182,7 @@ def generate_simulated_face(frame_num, canvas_size=(640, 480)):
190
  eye_size = max(5, face_radius // 8)
191
 
192
  # Blink occasionally
193
- if frame_num % 50 > 45: # Blink every 50 frames for 5 frames
194
  cv2.ellipse(canvas, (left_eye_x, eye_y), (eye_size, 1), 0, 0, 360, (30, 30, 30), -1)
195
  cv2.ellipse(canvas, (right_eye_x, eye_y), (eye_size, 1), 0, 0, 360, (30, 30, 30), -1)
196
  else:
@@ -199,12 +191,12 @@ def generate_simulated_face(frame_num, canvas_size=(640, 480)):
199
  cv2.circle(canvas, (left_eye_x, eye_y), eye_size-2, (70, 70, 70), -1)
200
  cv2.circle(canvas, (right_eye_x, eye_y), eye_size-2, (70, 70, 70), -1)
201
 
202
- # Draw mouth - change shape based on frame number to simulate different emotions
203
  mouth_y = face_y + int(face_radius * 0.3)
204
  mouth_width = int(face_radius * 0.6)
205
  mouth_height = int(face_radius * 0.2)
206
 
207
- # Cycle through different emotions based on frame number
208
  emotion_cycle = (frame_num // 100) % 4
209
 
210
  if emotion_cycle == 0: # Happy
@@ -223,7 +215,7 @@ def generate_simulated_face(frame_num, canvas_size=(640, 480)):
223
  cv2.line(canvas, (face_x - mouth_width//2, mouth_y),
224
  (face_x + mouth_width//2, mouth_y), (50, 50, 50), 2)
225
 
226
- # Add some text showing what emotion is being simulated
227
  emotions = ["Happy", "Sad", "Surprised", "Neutral"]
228
  cv2.putText(canvas, f"Simulating: {emotions[emotion_cycle]}",
229
  (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (50, 50, 50), 2)
@@ -234,12 +226,12 @@ def generate_simulated_face(frame_num, canvas_size=(640, 480)):
234
 
235
  def process_video_feed(feed_source, is_simulation=False):
236
  """Process video feed (webcam, video file, or simulation)."""
237
- # Create placeholders
238
  video_placeholder = st.empty()
239
  metrics_placeholder = st.empty()
240
  chart_placeholder = st.empty()
241
 
242
- # Initialize session state for tracking emotions over time
243
  if 'emotion_history' not in st.session_state:
244
  st.session_state.emotion_history = {}
245
  st.session_state.last_update_time = time.time()
@@ -270,12 +262,12 @@ def process_video_feed(feed_source, is_simulation=False):
270
 
271
  # Create deques for tracking emotions
272
  emotion_deques = {}
273
- timestamp_deque = deque(maxlen=30*history_length) # Store timestamps for X seconds at 30fps
274
 
275
  while st.session_state.get('running', False):
276
  # Get frame
277
  if is_simulation:
278
- # Generate a simulated frame
279
  frame = generate_simulated_face(st.session_state.simulation_frame)
280
  st.session_state.simulation_frame += 1
281
  ret = True
@@ -287,18 +279,18 @@ def process_video_feed(feed_source, is_simulation=False):
287
  if is_simulation:
288
  st.error("Simulation error")
289
  elif input_method == "Upload Video":
290
- # For video files, loop back to the beginning
291
  cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
292
  continue
293
  else:
294
  st.error("Failed to capture frame from video source")
295
  break
296
 
297
- # For webcam, flip horizontally for a more natural view
298
  if input_method == "Real-time Webcam" and not is_simulation:
299
  frame = cv2.flip(frame, 1)
300
 
301
- # Increment frame count for FPS calculation
302
  st.session_state.frame_count += 1
303
 
304
  # Detect faces
@@ -349,7 +341,7 @@ def process_video_feed(feed_source, is_simulation=False):
349
  2
350
  )
351
 
352
- # Update emotion history
353
  current_time = time.time()
354
  timestamp_deque.append(current_time)
355
 
@@ -362,34 +354,33 @@ def process_video_feed(feed_source, is_simulation=False):
362
  "time": current_time
363
  })
364
 
365
- # Calculate FPS
366
  current_time = time.time()
367
  time_diff = current_time - st.session_state.last_update_time
368
- if time_diff >= 1.0: # Update every second
369
  fps = st.session_state.frame_count / time_diff
370
  st.session_state.last_update_time = current_time
371
  st.session_state.frame_count = 0
372
 
373
- # Update metrics
374
  with metrics_placeholder.container():
375
  cols = st.columns(3)
376
  cols[0].metric("FPS", f"{fps:.1f}")
377
  cols[1].metric("Faces Detected", len(faces) if use_face_detection else "N/A")
378
 
379
- # Display the frame
380
  video_placeholder.image(frame, channels="BGR", use_column_width=True)
381
 
382
- # Update emotion history chart periodically
383
- if len(timestamp_deque) > 0 and time_diff >= 0.5: # Update chart every 0.5 seconds
384
  with chart_placeholder.container():
385
- # Create tabs for each face
386
  if len(emotion_deques) > 0:
387
  tabs = st.tabs(list(emotion_deques.keys()))
388
 
389
  for i, (face_id, emotion_data) in enumerate(emotion_deques.items()):
390
  with tabs[i]:
391
  if len(emotion_data) > 0:
392
- # Count occurrences of each emotion
393
  emotion_counts = {}
394
  for entry in emotion_data:
395
  emotion = entry["emotion"]
@@ -397,7 +388,7 @@ def process_video_feed(feed_source, is_simulation=False):
397
  emotion_counts[emotion] = 0
398
  emotion_counts[emotion] += 1
399
 
400
- # Create pie chart for emotion distribution
401
  fig = go.Figure(data=[go.Pie(
402
  labels=list(emotion_counts.keys()),
403
  values=list(emotion_counts.values()),
@@ -406,8 +397,8 @@ def process_video_feed(feed_source, is_simulation=False):
406
  fig.update_layout(title=f"Emotion Distribution - {face_id}")
407
  st.plotly_chart(fig, use_container_width=True)
408
 
409
- # Create line chart for emotion confidence over time
410
- emotions = list(emotion_data)[-20:] # Get the last 20 entries
411
  times = [(e["time"] - emotions[0]["time"]) for e in emotions]
412
  confidences = [e["confidence"] for e in emotions]
413
  emotion_labels = [e["emotion"] for e in emotions]
@@ -432,11 +423,11 @@ def process_video_feed(feed_source, is_simulation=False):
432
  else:
433
  st.info("No emotion data available yet.")
434
 
435
- # Control processing speed for videos and simulation
436
  if input_method in ["Upload Video", "Simulation Mode"]:
437
- time.sleep(0.03 / processing_speed) # Adjust delay based on processing_speed
 
438
 
439
- # Release resources when done
440
  if not is_simulation and cap.isOpened():
441
  cap.release()
442
 
@@ -444,7 +435,6 @@ def process_video_feed(feed_source, is_simulation=False):
444
  st.error(f"Error during processing: {str(e)}")
445
  st.session_state.running = False
446
  else:
447
- # Display a placeholder image when not running
448
  placeholder_img = np.zeros((300, 500, 3), dtype=np.uint8)
449
  cv2.putText(
450
  placeholder_img,
@@ -479,7 +469,6 @@ def process_static_image(image):
479
  with col2:
480
  st.image(result_image, caption="Detected Emotions", channels="BGR", use_column_width=True)
481
 
482
- # Display predictions
483
  st.subheader("Detected Emotions:")
484
  for i, (emotion, face) in enumerate(zip(emotions, faces)):
485
  if emotion["score"] >= confidence_threshold:
@@ -505,7 +494,6 @@ def process_static_image(image):
505
  else:
506
  st.warning("No faces detected in the image. Try another image or disable face detection.")
507
  else:
508
- # Process the whole image
509
  prediction = predict_emotion(image)
510
  st.subheader("Prediction:")
511
  st.write(f"**Emotion:** {prediction['label']}")
@@ -516,7 +504,6 @@ if input_method == "Upload an Image":
516
  uploaded_file = st.file_uploader("Choose an image file", type=["jpg", "jpeg", "png"])
517
 
518
  if uploaded_file is not None:
519
- # Load and display image
520
  image = Image.open(uploaded_file).convert("RGB")
521
  process_static_image(image)
522
 
@@ -531,17 +518,13 @@ elif input_method == "Upload Video":
531
  uploaded_video = st.file_uploader("Upload a video file", type=["mp4", "avi", "mov", "mkv"])
532
 
533
  if uploaded_video is not None:
534
- # Save the uploaded video to a temporary file
535
  tfile = tempfile.NamedTemporaryFile(delete=False)
536
  tfile.write(uploaded_video.read())
537
 
538
- # Open the video file
539
  cap = cv2.VideoCapture(tfile.name)
540
 
541
- # Process the video
542
  process_video_feed(cap)
543
 
544
- # Clean up the temporary file
545
  os.unlink(tfile.name)
546
 
547
  elif input_method == "Simulation Mode":
@@ -556,15 +539,6 @@ elif input_method == "Real-time Webcam":
556
  st.error("Could not open webcam. Please try the Simulation Mode instead.")
557
  st.info("If you're using Streamlit in a browser, make sure you've granted camera permissions.")
558
 
559
- # Show troubleshooting tips
560
- with st.expander("Webcam Troubleshooting Tips"):
561
- st.markdown("""
562
- 1. **Check Browser Permissions**: Make sure your browser has permission to access your camera.
563
- 2. **Close Other Applications**: Other applications might be using your webcam.
564
- 3. **Refresh the Page**: Sometimes simply refreshing can resolve the issue.
565
- 4. **Try a Different Browser**: Some browsers handle webcam access better than others.
566
- 5. **Use Simulation Mode**: If you cannot get the webcam working, use the Simulation Mode.
567
- """)
568
  else:
569
  # Webcam available, process it
570
  process_video_feed(cap)
 
53
  list(model_options.keys())
54
  )
55
 
 
56
  input_method = st.sidebar.radio(
57
  "Choose Input Method",
58
  ["Real-time Webcam", "Upload an Image", "Capture Image"]
59
  )
60
 
 
61
  confidence_threshold = st.sidebar.slider(
62
  "Confidence Threshold",
63
  min_value=0.0,
 
66
  step=0.05
67
  )
68
 
 
69
  use_face_detection = st.sidebar.checkbox("Enable Face Detection", value=True)
70
 
 
 
71
  if input_method in ["Real-time Webcam", "Upload Video", "Simulation Mode"]:
72
  history_length = st.sidebar.slider(
73
  "Emotion History Length (seconds)",
 
84
  # --- Utility Functions ---
85
  def detect_faces(image):
86
  """Detect faces in an image using OpenCV."""
 
87
  if isinstance(image, Image.Image):
88
  opencv_image = np.array(image)
89
+ opencv_image = opencv_image[:, :, ::-1].copy()
90
  else:
91
  opencv_image = image
92
 
 
130
  """Draw rectangles around faces and label with emotions."""
131
  img = image.copy()
132
 
 
133
  emotion_colors = {
134
  "happy": (0, 255, 0), # Green
135
  "sad": (255, 0, 0), # Blue
 
140
  "disgust": (0, 128, 128) # Brown
141
  }
142
 
143
+ default_color = (255, 255, 255)
 
144
 
145
  for (x, y, w, h), emotion in zip(faces, emotions):
146
+
147
  emotion_key = emotion["label"].lower().split("_")[-1]
148
  color = emotion_colors.get(emotion_key, default_color)
149
 
150
+
151
  cv2.rectangle(img, (x, y), (x+w, y+h), color, 2)
152
 
153
  # Add emotion label and confidence
 
182
  eye_size = max(5, face_radius // 8)
183
 
184
  # Blink occasionally
185
+ if frame_num % 50 > 45:
186
  cv2.ellipse(canvas, (left_eye_x, eye_y), (eye_size, 1), 0, 0, 360, (30, 30, 30), -1)
187
  cv2.ellipse(canvas, (right_eye_x, eye_y), (eye_size, 1), 0, 0, 360, (30, 30, 30), -1)
188
  else:
 
191
  cv2.circle(canvas, (left_eye_x, eye_y), eye_size-2, (70, 70, 70), -1)
192
  cv2.circle(canvas, (right_eye_x, eye_y), eye_size-2, (70, 70, 70), -1)
193
 
194
+
195
  mouth_y = face_y + int(face_radius * 0.3)
196
  mouth_width = int(face_radius * 0.6)
197
  mouth_height = int(face_radius * 0.2)
198
 
199
+
200
  emotion_cycle = (frame_num // 100) % 4
201
 
202
  if emotion_cycle == 0: # Happy
 
215
  cv2.line(canvas, (face_x - mouth_width//2, mouth_y),
216
  (face_x + mouth_width//2, mouth_y), (50, 50, 50), 2)
217
 
218
+
219
  emotions = ["Happy", "Sad", "Surprised", "Neutral"]
220
  cv2.putText(canvas, f"Simulating: {emotions[emotion_cycle]}",
221
  (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (50, 50, 50), 2)
 
226
 
227
  def process_video_feed(feed_source, is_simulation=False):
228
  """Process video feed (webcam, video file, or simulation)."""
229
+
230
  video_placeholder = st.empty()
231
  metrics_placeholder = st.empty()
232
  chart_placeholder = st.empty()
233
 
234
+
235
  if 'emotion_history' not in st.session_state:
236
  st.session_state.emotion_history = {}
237
  st.session_state.last_update_time = time.time()
 
262
 
263
  # Create deques for tracking emotions
264
  emotion_deques = {}
265
+ timestamp_deque = deque(maxlen=30*history_length)
266
 
267
  while st.session_state.get('running', False):
268
  # Get frame
269
  if is_simulation:
270
+
271
  frame = generate_simulated_face(st.session_state.simulation_frame)
272
  st.session_state.simulation_frame += 1
273
  ret = True
 
279
  if is_simulation:
280
  st.error("Simulation error")
281
  elif input_method == "Upload Video":
282
+
283
  cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
284
  continue
285
  else:
286
  st.error("Failed to capture frame from video source")
287
  break
288
 
289
+
290
  if input_method == "Real-time Webcam" and not is_simulation:
291
  frame = cv2.flip(frame, 1)
292
 
293
+
294
  st.session_state.frame_count += 1
295
 
296
  # Detect faces
 
341
  2
342
  )
343
 
344
+
345
  current_time = time.time()
346
  timestamp_deque.append(current_time)
347
 
 
354
  "time": current_time
355
  })
356
 
357
+
358
  current_time = time.time()
359
  time_diff = current_time - st.session_state.last_update_time
360
+ if time_diff >= 1.0:
361
  fps = st.session_state.frame_count / time_diff
362
  st.session_state.last_update_time = current_time
363
  st.session_state.frame_count = 0
364
 
365
+
366
  with metrics_placeholder.container():
367
  cols = st.columns(3)
368
  cols[0].metric("FPS", f"{fps:.1f}")
369
  cols[1].metric("Faces Detected", len(faces) if use_face_detection else "N/A")
370
 
 
371
  video_placeholder.image(frame, channels="BGR", use_column_width=True)
372
 
373
+
374
+ if len(timestamp_deque) > 0 and time_diff >= 0.5:
375
  with chart_placeholder.container():
376
+
377
  if len(emotion_deques) > 0:
378
  tabs = st.tabs(list(emotion_deques.keys()))
379
 
380
  for i, (face_id, emotion_data) in enumerate(emotion_deques.items()):
381
  with tabs[i]:
382
  if len(emotion_data) > 0:
383
+
384
  emotion_counts = {}
385
  for entry in emotion_data:
386
  emotion = entry["emotion"]
 
388
  emotion_counts[emotion] = 0
389
  emotion_counts[emotion] += 1
390
 
391
+
392
  fig = go.Figure(data=[go.Pie(
393
  labels=list(emotion_counts.keys()),
394
  values=list(emotion_counts.values()),
 
397
  fig.update_layout(title=f"Emotion Distribution - {face_id}")
398
  st.plotly_chart(fig, use_container_width=True)
399
 
400
+
401
+ emotions = list(emotion_data)[-20:]
402
  times = [(e["time"] - emotions[0]["time"]) for e in emotions]
403
  confidences = [e["confidence"] for e in emotions]
404
  emotion_labels = [e["emotion"] for e in emotions]
 
423
  else:
424
  st.info("No emotion data available yet.")
425
 
426
+
427
  if input_method in ["Upload Video", "Simulation Mode"]:
428
+ time.sleep(0.03 / processing_speed)
429
+
430
 
 
431
  if not is_simulation and cap.isOpened():
432
  cap.release()
433
 
 
435
  st.error(f"Error during processing: {str(e)}")
436
  st.session_state.running = False
437
  else:
 
438
  placeholder_img = np.zeros((300, 500, 3), dtype=np.uint8)
439
  cv2.putText(
440
  placeholder_img,
 
469
  with col2:
470
  st.image(result_image, caption="Detected Emotions", channels="BGR", use_column_width=True)
471
 
 
472
  st.subheader("Detected Emotions:")
473
  for i, (emotion, face) in enumerate(zip(emotions, faces)):
474
  if emotion["score"] >= confidence_threshold:
 
494
  else:
495
  st.warning("No faces detected in the image. Try another image or disable face detection.")
496
  else:
 
497
  prediction = predict_emotion(image)
498
  st.subheader("Prediction:")
499
  st.write(f"**Emotion:** {prediction['label']}")
 
504
  uploaded_file = st.file_uploader("Choose an image file", type=["jpg", "jpeg", "png"])
505
 
506
  if uploaded_file is not None:
 
507
  image = Image.open(uploaded_file).convert("RGB")
508
  process_static_image(image)
509
 
 
518
  uploaded_video = st.file_uploader("Upload a video file", type=["mp4", "avi", "mov", "mkv"])
519
 
520
  if uploaded_video is not None:
 
521
  tfile = tempfile.NamedTemporaryFile(delete=False)
522
  tfile.write(uploaded_video.read())
523
 
 
524
  cap = cv2.VideoCapture(tfile.name)
525
 
 
526
  process_video_feed(cap)
527
 
 
528
  os.unlink(tfile.name)
529
 
530
  elif input_method == "Simulation Mode":
 
539
  st.error("Could not open webcam. Please try the Simulation Mode instead.")
540
  st.info("If you're using Streamlit in a browser, make sure you've granted camera permissions.")
541
 
 
 
 
 
 
 
 
 
 
542
  else:
543
  # Webcam available, process it
544
  process_video_feed(cap)