reab5555 commited on
Commit
dec9aa7
·
verified ·
1 Parent(s): 8ebfc5c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -125
app.py CHANGED
@@ -115,6 +115,7 @@ def extract_frames(video_path, output_folder, desired_fps, progress_callback=Non
115
  def process_frames(frames_folder, aligned_faces_folder, frame_count, progress, batch_size):
116
  embeddings_by_frame = {}
117
  emotions_by_frame = {}
 
118
  frame_files = sorted([f for f in os.listdir(frames_folder) if f.endswith('.jpg')])
119
 
120
  for i in range(0, len(frame_files), batch_size):
@@ -144,6 +145,7 @@ def process_frames(frames_folder, aligned_faces_folder, frame_count, progress, b
144
  aligned_face_resized = cv2.resize(aligned_face, (160, 160))
145
  output_path = os.path.join(aligned_faces_folder, f"frame_{frame_num}_face.jpg")
146
  cv2.imwrite(output_path, aligned_face_resized)
 
147
  embedding, emotion = get_face_embedding_and_emotion(aligned_face_resized)
148
  embeddings_by_frame[frame_num] = embedding
149
  emotions_by_frame[frame_num] = emotion
@@ -151,35 +153,33 @@ def process_frames(frames_folder, aligned_faces_folder, frame_count, progress, b
151
  progress((i + len(batch_files)) / frame_count,
152
  f"Processing frames {i + 1} to {min(i + len(batch_files), frame_count)} of {frame_count}")
153
 
154
- return embeddings_by_frame, emotions_by_frame
155
 
 
 
 
 
156
 
157
- def cluster_embeddings(embeddings):
158
- if len(embeddings) < 2:
159
- print("Not enough embeddings for clustering. Assigning all to one cluster.")
160
- return np.zeros(len(embeddings), dtype=int)
161
 
162
- scaler = StandardScaler()
163
- embeddings_scaled = scaler.fit_transform(embeddings)
164
 
165
- # Use DBSCAN for adaptive clustering
166
- dbscan = DBSCAN(eps=0.5, min_samples=5) # Adjust these parameters as needed
167
- clusters = dbscan.fit_predict(embeddings_scaled)
168
 
169
- # If DBSCAN couldn't find meaningful clusters, fall back to KMeans
170
- if len(set(clusters)) == 1:
171
- best_n_clusters = 1
172
- best_score = -1
173
- for n_clusters in range(2, min(5, len(embeddings))):
174
- kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
175
- labels = kmeans.fit_predict(embeddings_scaled)
176
- score = silhouette_score(embeddings_scaled, labels)
177
- if score > best_score:
178
- best_score = score
179
- best_n_clusters = n_clusters
180
 
181
- kmeans = KMeans(n_clusters=best_n_clusters, random_state=42, n_init=10)
182
- clusters = kmeans.fit_predict(embeddings_scaled)
 
 
 
 
 
 
183
 
184
  return clusters
185
 
@@ -310,38 +310,41 @@ def lstm_anomaly_detection(X, feature_columns, num_anomalies=10, epochs=100, bat
310
  anomalies_comp, mse_comp, top_indices_comp,
311
  model)
312
 
313
- from scipy import interpolate
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
 
315
- def plot_with_segments(ax, df_filtered, y_column, color):
316
- segments = []
317
- current_segment = []
318
- for i, (time, score) in enumerate(zip(df_filtered['Seconds'], df_filtered[y_column])):
319
- if i > 0 and time - df_filtered['Seconds'].iloc[i-1] > 1: # Gap of more than 1 second
320
- if current_segment:
321
- segments.append(current_segment)
322
- current_segment = []
323
- current_segment.append((time, score))
324
- if current_segment:
325
- segments.append(current_segment)
326
-
327
- for segment in segments:
328
- times, scores = zip(*segment)
329
- if len(times) > 3:
330
- try:
331
- # Use scipy's interpolate to create a smooth curve
332
- f = interpolate.interp1d(times, scores, kind='cubic')
333
- smooth_times = np.linspace(min(times), max(times), num=200)
334
- smooth_scores = f(smooth_times)
335
- ax.plot(smooth_times, smooth_scores, color=color, linewidth=1.5)
336
- except ValueError:
337
- # Fall back to linear interpolation if cubic fails
338
- f = interpolate.interp1d(times, scores, kind='linear')
339
- smooth_times = np.linspace(min(times), max(times), num=200)
340
- smooth_scores = f(smooth_times)
341
- ax.plot(smooth_times, smooth_scores, color=color, linewidth=1.5)
342
- else:
343
- # For very short segments, just plot the points
344
- ax.plot(times, scores, color=color, linewidth=1.5)
345
 
346
  def plot_anomaly_scores(df, anomaly_scores, top_indices, title):
347
  plt.figure(figsize=(16, 8), dpi=400)
@@ -350,23 +353,21 @@ def plot_anomaly_scores(df, anomaly_scores, top_indices, title):
350
  df['Seconds'] = df['Timecode'].apply(
351
  lambda x: sum(float(t) * 60 ** i for i, t in enumerate(reversed(x.split(':')))))
352
 
353
- # Filter out rows with no data
354
- mask = ~np.isnan(anomaly_scores)
355
- df_filtered = df[mask].copy()
356
- df_filtered['anomaly_scores'] = anomaly_scores[mask]
357
 
358
- if df_filtered.empty:
359
- ax.text(0.5, 0.5, "No data available", ha='center', va='center')
360
- else:
361
- plot_with_segments(ax, df_filtered, 'anomaly_scores', 'blue')
 
 
362
 
363
- # Highlight top anomalies
364
- top_indices_filtered = [i for i in top_indices if i in df_filtered.index]
365
- ax.scatter(df_filtered['Seconds'].iloc[top_indices_filtered],
366
- df_filtered['anomaly_scores'].iloc[top_indices_filtered],
367
- color='red', s=100, zorder=5)
368
 
369
- max_seconds = df['Seconds'].max() # Use the full range for x-axis
370
  ax.set_xlim(0, max_seconds)
371
  num_ticks = 80
372
  ax.set_xticks(np.linspace(0, max_seconds, num_ticks))
@@ -375,35 +376,32 @@ def plot_anomaly_scores(df, anomaly_scores, top_indices, title):
375
 
376
  ax.set_xlabel('Time')
377
  ax.set_ylabel('Anomaly Score')
378
- ax.set_title(f'Anomaly Scores Over Time ({title})')
379
 
380
  ax.grid(True, linestyle='--', alpha=0.7)
381
  plt.tight_layout()
382
  return fig
383
 
384
- def plot_emotion(df, emotion, num_anomalies, color):
 
385
  plt.figure(figsize=(16, 8), dpi=400)
386
  fig, ax = plt.subplots(figsize=(16, 8))
387
 
388
  df['Seconds'] = df['Timecode'].apply(
389
  lambda x: sum(float(t) * 60 ** i for i, t in enumerate(reversed(x.split(':')))))
390
 
391
- # Filter out rows with no data
392
- mask = ~np.isnan(df[emotion])
393
- df_filtered = df[mask]
394
 
395
- if df_filtered.empty:
396
- ax.text(0.5, 0.5, "No data available", ha='center', va='center')
397
- else:
398
- plot_with_segments(ax, df_filtered, emotion, color)
399
 
400
- # Highlight top anomalies
401
- top_indices = np.argsort(df_filtered[emotion].values)[-num_anomalies:][::-1]
402
- ax.scatter(df_filtered['Seconds'].iloc[top_indices],
403
- df_filtered[emotion].iloc[top_indices],
404
- color='red', s=100, zorder=5)
405
 
406
- max_seconds = df['Seconds'].max() # Use the full range for x-axis
407
  ax.set_xlim(0, max_seconds)
408
  num_ticks = 80
409
  ax.set_xticks(np.linspace(0, max_seconds, num_ticks))
@@ -411,30 +409,32 @@ def plot_emotion(df, emotion, num_anomalies, color):
411
  rotation=90, ha='center', va='top')
412
 
413
  ax.set_xlabel('Time')
414
- ax.set_ylabel(f'{emotion.capitalize()} Score')
415
- ax.set_title(f'{emotion.capitalize()} Scores Over Time (Top {num_anomalies} in Red)')
416
 
417
  ax.grid(True, linestyle='--', alpha=0.7)
418
  plt.tight_layout()
419
  return fig
420
 
 
421
  def get_random_face_samples(organized_faces_folder, output_folder):
422
- face_samples = {}
423
  for cluster_folder in os.listdir(organized_faces_folder):
424
  if cluster_folder.startswith("person_"):
425
- cluster_id = int(cluster_folder.split("_")[1])
426
  person_folder = os.path.join(organized_faces_folder, cluster_folder)
427
  face_files = [f for f in os.listdir(person_folder) if f.endswith('.jpg')]
428
  if face_files:
429
  random_face = np.random.choice(face_files)
430
  face_path = os.path.join(person_folder, random_face)
431
- output_path = os.path.join(output_folder, f"face_sample_person_{cluster_id}.jpg")
432
  face_img = cv2.imread(face_path)
433
- small_face = cv2.resize(face_img, (160, 160))
434
- cv2.imwrite(output_path, small_face)
435
- face_samples[cluster_id] = output_path
 
436
  return face_samples
437
 
 
438
  def process_video(video_path, num_anomalies, num_components, desired_fps, batch_size, progress=gr.Progress()):
439
  output_folder = "output"
440
  os.makedirs(output_folder, exist_ok=True)
@@ -459,16 +459,17 @@ def process_video(video_path, num_anomalies, num_components, desired_fps, batch_
459
 
460
  progress(1, "Frame extraction complete")
461
  progress(0.3, "Processing frames")
462
- embeddings_by_frame, emotions_by_frame = process_frames(frames_folder, aligned_faces_folder, frame_count,
463
- progress, batch_size)
 
464
 
465
- if not embeddings_by_frame:
466
  return ("No faces were extracted from the video.",
467
  None, None, None, None, None, None, None, None, None)
468
 
469
- progress(0.6, "Clustering embeddings")
470
- embeddings = list(embeddings_by_frame.values())
471
- clusters = cluster_embeddings(embeddings)
472
  num_clusters = len(set(clusters)) # Get the number of unique clusters
473
 
474
  progress(0.7, "Organizing faces")
@@ -485,11 +486,25 @@ def process_video(video_path, num_anomalies, num_components, desired_fps, batch_
485
  feature_columns = [col for col in df.columns if
486
  col not in ['Frame', 'Timecode', 'Time (Minutes)', 'Embedding_Index']]
487
  X = df[feature_columns].values
488
- print(f"Shape of input data: {X.shape}")
489
- print(f"Feature columns: {feature_columns}")
490
  try:
491
  anomalies_all, anomaly_scores_all, top_indices_all, anomalies_comp, anomaly_scores_comp, top_indices_comp, _ = lstm_anomaly_detection(
492
  X, feature_columns, num_anomalies=num_anomalies, batch_size=batch_size)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  except Exception as e:
494
  print(f"Error details: {str(e)}")
495
  return f"Error in anomaly detection: {str(e)}", None, None, None, None, None, None, None, None, None
@@ -499,38 +514,39 @@ def process_video(video_path, num_anomalies, num_components, desired_fps, batch_
499
  anomaly_plot_all = plot_anomaly_scores(df, anomaly_scores_all, top_indices_all, "All Features")
500
  anomaly_plot_comp = plot_anomaly_scores(df, anomaly_scores_comp, top_indices_comp, "Components Only")
501
  emotion_plots = [
502
- plot_emotion(df, 'fear', num_anomalies, 'purple'),
503
- plot_emotion(df, 'sad', num_anomalies, 'green'),
504
- plot_emotion(df, 'angry', num_anomalies, 'orange'),
505
- plot_emotion(df, 'happy', num_anomalies, 'darkblue'),
506
- plot_emotion(df, 'surprise', num_anomalies, 'gold'),
507
- plot_emotion(df, 'neutral', num_anomalies, 'grey')
508
  ]
509
  except Exception as e:
510
  return f"Error generating plots: {str(e)}", None, None, None, None, None, None, None, None, None
511
 
512
  progress(1.0, "Preparing results")
513
- results = f"Number of persons detected: {num_clusters}\n\n"
 
514
  for cluster_id in range(num_clusters):
515
- results += f"Person {cluster_id + 1}: {len([c for c in clusters if c == cluster_id])} frames\n"
516
  results += f"\nTop {num_anomalies} anomalies (All Features):\n"
517
- results += "\n".join([f"{score:.4f} at {timecode}" for score, timecode in
518
- zip(anomaly_scores_all[top_indices_all], df['Timecode'].iloc[top_indices_all].values)])
 
519
  results += f"\n\nTop {num_anomalies} anomalies (Components Only):\n"
520
- results += "\n".join([f"{score:.4f} at {timecode}" for score, timecode in
521
- zip(anomaly_scores_comp[top_indices_comp], df['Timecode'].iloc[top_indices_comp].values)])
 
522
 
523
  for emotion in ['fear', 'sad', 'angry', 'happy', 'surprise', 'neutral']:
524
- top_indices = np.argsort(df[emotion].values)[-num_anomalies:][::-1]
525
- results += f"\n\nTop {num_anomalies} {emotion.capitalize()} Scores:\n"
526
- results += "\n".join([f"{df[emotion].iloc[i]:.4f} at {df['Timecode'].iloc[i]}" for i in top_indices])
527
 
528
  return (
529
  results,
530
  anomaly_plot_all,
531
  anomaly_plot_comp,
532
  *emotion_plots,
533
- *[face_samples.get(i, None) for i in range(num_clusters)]
534
  )
535
 
536
 
@@ -541,7 +557,7 @@ iface = gr.Interface(
541
  gr.Slider(minimum=1, maximum=20, step=1, value=10, label="Number of Anomalies"),
542
  gr.Slider(minimum=1, maximum=20, step=1, value=10, label="Number of Components"),
543
  gr.Slider(minimum=1, maximum=20, step=1, value=15, label="Desired FPS"),
544
- gr.Slider(minimum=1, maximum=32, step=4, value=8, label="Batch Size")
545
  ],
546
  outputs=[
547
  gr.Textbox(label="Anomaly Detection Results"),
@@ -557,15 +573,15 @@ iface = gr.Interface(
557
  ],
558
  title="Facial Expressions Anomaly Detection",
559
  description="""
560
- This application detects anomalies in facial expressions and emotions from a video input.
561
- It identifies distinct persons in the video and provides a sample face for each.
562
-
563
- Adjust the parameters as needed:
564
- - Number of Anomalies: How many top anomalies or high intensities to highlight
565
- - Number of Components: Complexity of the facial expression model
566
- - Desired FPS: Frames per second to analyze (lower for faster processing)
567
- - Batch Size: Affects processing speed and memory usage
568
- """,
569
  allow_flagging="never"
570
  )
571
 
 
115
  def process_frames(frames_folder, aligned_faces_folder, frame_count, progress, batch_size):
116
  embeddings_by_frame = {}
117
  emotions_by_frame = {}
118
+ aligned_face_paths = []
119
  frame_files = sorted([f for f in os.listdir(frames_folder) if f.endswith('.jpg')])
120
 
121
  for i in range(0, len(frame_files), batch_size):
 
145
  aligned_face_resized = cv2.resize(aligned_face, (160, 160))
146
  output_path = os.path.join(aligned_faces_folder, f"frame_{frame_num}_face.jpg")
147
  cv2.imwrite(output_path, aligned_face_resized)
148
+ aligned_face_paths.append(output_path)
149
  embedding, emotion = get_face_embedding_and_emotion(aligned_face_resized)
150
  embeddings_by_frame[frame_num] = embedding
151
  emotions_by_frame[frame_num] = emotion
 
153
  progress((i + len(batch_files)) / frame_count,
154
  f"Processing frames {i + 1} to {min(i + len(batch_files), frame_count)} of {frame_count}")
155
 
156
+ return embeddings_by_frame, emotions_by_frame, aligned_face_paths
157
 
158
+ def cluster_faces(face_images):
159
+ if len(face_images) < 2:
160
+ print("Not enough faces for clustering. Assigning all to one cluster.")
161
+ return np.zeros(len(face_images), dtype=int)
162
 
163
+ # Resize all images to a consistent size
164
+ resized_faces = [cv2.resize(face, (224, 224)) for face in face_images]
 
 
165
 
166
+ # Convert images to grayscale and flatten
167
+ gray_faces = [cv2.cvtColor(face, cv2.COLOR_BGR2GRAY).flatten() for face in resized_faces]
168
 
169
+ # Stack the flattened images
170
+ X = np.stack(gray_faces)
 
171
 
172
+ # Normalize the pixel values
173
+ X = X / 255.0
 
 
 
 
 
 
 
 
 
174
 
175
+ # Perform DBSCAN clustering
176
+ dbscan = DBSCAN(eps=0.3, min_samples=3, metric='euclidean')
177
+ clusters = dbscan.fit_predict(X)
178
+
179
+ # If DBSCAN assigns all to noise (-1), consider it as one cluster
180
+ if np.all(clusters == -1):
181
+ print("DBSCAN assigned all to noise. Considering as one cluster.")
182
+ return np.zeros(len(face_images), dtype=int)
183
 
184
  return clusters
185
 
 
310
  anomalies_comp, mse_comp, top_indices_comp,
311
  model)
312
 
313
+ def emotion_anomaly_detection(emotion_data, num_anomalies=10, epochs=100, batch_size=64):
314
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
315
+ X = torch.FloatTensor(emotion_data.values.reshape(-1, 1)).to(device)
316
+ X = X.unsqueeze(0) # Add batch dimension
317
+
318
+ model = LSTMAutoencoder(input_size=1).to(device)
319
+ criterion = nn.MSELoss()
320
+ optimizer = optim.Adam(model.parameters())
321
+
322
+ for epoch in range(epochs):
323
+ model.train()
324
+ optimizer.zero_grad()
325
+ output = model(X)
326
+ loss = criterion(output, X)
327
+ loss.backward()
328
+ optimizer.step()
329
+
330
+ model.eval()
331
+ with torch.no_grad():
332
+ reconstructed = model(X).squeeze(0).cpu().numpy()
333
+
334
+ mse = np.mean(np.power(X.squeeze(0).cpu().numpy() - reconstructed, 2), axis=1)
335
+ top_indices = mse.argsort()[-num_anomalies:][::-1]
336
+ anomalies = np.zeros(len(mse), dtype=bool)
337
+ anomalies[top_indices] = True
338
+
339
+ return anomalies, mse, top_indices
340
+
341
+ def normalize_scores(scores):
342
+ min_score = np.min(scores)
343
+ max_score = np.max(scores)
344
+ if max_score == min_score:
345
+ return np.full_like(scores, 100)
346
+ return ((scores - min_score) / (max_score - min_score)) * 100
347
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
 
349
  def plot_anomaly_scores(df, anomaly_scores, top_indices, title):
350
  plt.figure(figsize=(16, 8), dpi=400)
 
353
  df['Seconds'] = df['Timecode'].apply(
354
  lambda x: sum(float(t) * 60 ** i for i, t in enumerate(reversed(x.split(':')))))
355
 
356
+ # Normalize scores
357
+ normalized_scores = normalize_scores(anomaly_scores)
 
 
358
 
359
+ # Omit the first data point
360
+ seconds = df['Seconds'].values[1:]
361
+ scores = normalized_scores[1:]
362
+
363
+ # Create bar plot
364
+ ax.bar(seconds, scores, width=1, color='blue', alpha=0.7)
365
 
366
+ # Highlight top anomalies (excluding the first data point)
367
+ top_indices = [idx for idx in top_indices if idx > 0]
368
+ ax.bar(df['Seconds'].iloc[top_indices], normalized_scores[top_indices], width=1, color='red', alpha=0.7)
 
 
369
 
370
+ max_seconds = df['Seconds'].max()
371
  ax.set_xlim(0, max_seconds)
372
  num_ticks = 80
373
  ax.set_xticks(np.linspace(0, max_seconds, num_ticks))
 
376
 
377
  ax.set_xlabel('Time')
378
  ax.set_ylabel('Anomaly Score')
379
+ ax.set_title(f'Anomaly Scores ({title})')
380
 
381
  ax.grid(True, linestyle='--', alpha=0.7)
382
  plt.tight_layout()
383
  return fig
384
 
385
+
386
+ def plot_emotion(df, emotion, anomaly_scores, top_indices, num_anomalies, color):
387
  plt.figure(figsize=(16, 8), dpi=400)
388
  fig, ax = plt.subplots(figsize=(16, 8))
389
 
390
  df['Seconds'] = df['Timecode'].apply(
391
  lambda x: sum(float(t) * 60 ** i for i, t in enumerate(reversed(x.split(':')))))
392
 
393
+ # Omit the first data point
394
+ seconds = df['Seconds'].values[1:]
395
+ scores = anomaly_scores[1:]
396
 
397
+ # Create bar plot
398
+ ax.bar(seconds, scores, width=1, color=color, alpha=0.7)
 
 
399
 
400
+ # Highlight top anomalies (excluding the first data point)
401
+ top_indices = [idx for idx in top_indices if idx > 0]
402
+ ax.bar(df['Seconds'].iloc[top_indices], anomaly_scores[top_indices], width=1, color='red', alpha=0.7)
 
 
403
 
404
+ max_seconds = df['Seconds'].max()
405
  ax.set_xlim(0, max_seconds)
406
  num_ticks = 80
407
  ax.set_xticks(np.linspace(0, max_seconds, num_ticks))
 
409
  rotation=90, ha='center', va='top')
410
 
411
  ax.set_xlabel('Time')
412
+ ax.set_ylabel(f'{emotion.capitalize()} Anomaly Score')
413
+ ax.set_title(f'{emotion.capitalize()} Anomaly Scores (Top {num_anomalies} in Red)')
414
 
415
  ax.grid(True, linestyle='--', alpha=0.7)
416
  plt.tight_layout()
417
  return fig
418
 
419
+
420
  def get_random_face_samples(organized_faces_folder, output_folder):
421
+ face_samples = []
422
  for cluster_folder in os.listdir(organized_faces_folder):
423
  if cluster_folder.startswith("person_"):
 
424
  person_folder = os.path.join(organized_faces_folder, cluster_folder)
425
  face_files = [f for f in os.listdir(person_folder) if f.endswith('.jpg')]
426
  if face_files:
427
  random_face = np.random.choice(face_files)
428
  face_path = os.path.join(person_folder, random_face)
429
+ output_path = os.path.join(output_folder, f"face_sample_{cluster_folder}.jpg")
430
  face_img = cv2.imread(face_path)
431
+ if face_img is not None:
432
+ small_face = cv2.resize(face_img, (160, 160))
433
+ cv2.imwrite(output_path, small_face)
434
+ face_samples.append(output_path)
435
  return face_samples
436
 
437
+
438
  def process_video(video_path, num_anomalies, num_components, desired_fps, batch_size, progress=gr.Progress()):
439
  output_folder = "output"
440
  os.makedirs(output_folder, exist_ok=True)
 
459
 
460
  progress(1, "Frame extraction complete")
461
  progress(0.3, "Processing frames")
462
+ embeddings_by_frame, emotions_by_frame, aligned_face_paths = process_frames(frames_folder, aligned_faces_folder,
463
+ frame_count,
464
+ progress, batch_size)
465
 
466
+ if not aligned_face_paths:
467
  return ("No faces were extracted from the video.",
468
  None, None, None, None, None, None, None, None, None)
469
 
470
+ progress(0.6, "Clustering faces")
471
+ face_images = [cv2.imread(path) for path in aligned_face_paths]
472
+ clusters = cluster_faces(face_images)
473
  num_clusters = len(set(clusters)) # Get the number of unique clusters
474
 
475
  progress(0.7, "Organizing faces")
 
486
  feature_columns = [col for col in df.columns if
487
  col not in ['Frame', 'Timecode', 'Time (Minutes)', 'Embedding_Index']]
488
  X = df[feature_columns].values
489
+
 
490
  try:
491
  anomalies_all, anomaly_scores_all, top_indices_all, anomalies_comp, anomaly_scores_comp, top_indices_comp, _ = lstm_anomaly_detection(
492
  X, feature_columns, num_anomalies=num_anomalies, batch_size=batch_size)
493
+
494
+ # Normalize anomaly scores
495
+ anomaly_scores_all = normalize_scores(anomaly_scores_all)
496
+ anomaly_scores_comp = normalize_scores(anomaly_scores_comp)
497
+
498
+ # Perform anomaly detection for each emotion
499
+ emotion_anomalies = {}
500
+ for emotion in ['fear', 'sad', 'angry', 'happy', 'surprise', 'neutral']:
501
+ anomalies, scores, indices = emotion_anomaly_detection(df[emotion], num_anomalies=num_anomalies)
502
+ emotion_anomalies[emotion] = {
503
+ 'anomalies': anomalies,
504
+ 'scores': normalize_scores(scores),
505
+ 'indices': indices
506
+ }
507
+
508
  except Exception as e:
509
  print(f"Error details: {str(e)}")
510
  return f"Error in anomaly detection: {str(e)}", None, None, None, None, None, None, None, None, None
 
514
  anomaly_plot_all = plot_anomaly_scores(df, anomaly_scores_all, top_indices_all, "All Features")
515
  anomaly_plot_comp = plot_anomaly_scores(df, anomaly_scores_comp, top_indices_comp, "Components Only")
516
  emotion_plots = [
517
+ plot_emotion(df, emotion, emotion_anomalies[emotion]['scores'], emotion_anomalies[emotion]['indices'],
518
+ num_anomalies, color)
519
+ for emotion, color in zip(['fear', 'sad', 'angry', 'happy', 'surprise', 'neutral'],
520
+ ['purple', 'green', 'orange', 'darkblue', 'gold', 'grey'])
 
 
521
  ]
522
  except Exception as e:
523
  return f"Error generating plots: {str(e)}", None, None, None, None, None, None, None, None, None
524
 
525
  progress(1.0, "Preparing results")
526
+ results = f"Number of persons/clusters detected: {num_clusters}\n\n"
527
+ results += f"Breakdown of persons/clusters:\n"
528
  for cluster_id in range(num_clusters):
529
+ results += f"Person/Cluster {cluster_id + 1}: {len([c for c in clusters if c == cluster_id])} frames\n"
530
  results += f"\nTop {num_anomalies} anomalies (All Features):\n"
531
+ results += "\n".join([f"{score:.2f} at {timecode}" for score, timecode in
532
+ zip(anomaly_scores_all[top_indices_all[1:]],
533
+ df['Timecode'].iloc[top_indices_all[1:]].values)])
534
  results += f"\n\nTop {num_anomalies} anomalies (Components Only):\n"
535
+ results += "\n".join([f"{score:.2f} at {timecode}" for score, timecode in
536
+ zip(anomaly_scores_comp[top_indices_comp[1:]],
537
+ df['Timecode'].iloc[top_indices_comp[1:]].values)])
538
 
539
  for emotion in ['fear', 'sad', 'angry', 'happy', 'surprise', 'neutral']:
540
+ results += f"\n\nTop {num_anomalies} {emotion.capitalize()} Anomalies:\n"
541
+ results += "\n".join([f"{emotion_anomalies[emotion]['scores'][i]:.2f} at {df['Timecode'].iloc[i]}"
542
+ for i in emotion_anomalies[emotion]['indices'] if i > 0])
543
 
544
  return (
545
  results,
546
  anomaly_plot_all,
547
  anomaly_plot_comp,
548
  *emotion_plots,
549
+ face_samples
550
  )
551
 
552
 
 
557
  gr.Slider(minimum=1, maximum=20, step=1, value=10, label="Number of Anomalies"),
558
  gr.Slider(minimum=1, maximum=20, step=1, value=10, label="Number of Components"),
559
  gr.Slider(minimum=1, maximum=20, step=1, value=15, label="Desired FPS"),
560
+ gr.Slider(minimum=1, maximum=32, step=1, value=8, label="Batch Size")
561
  ],
562
  outputs=[
563
  gr.Textbox(label="Anomaly Detection Results"),
 
573
  ],
574
  title="Facial Expressions Anomaly Detection",
575
  description="""
576
+ This application detects anomalies in facial expressions and emotions from a video input.
577
+ It identifies distinct persons in the video and provides a sample face for each.
578
+
579
+ Adjust the parameters as needed:
580
+ - Number of Anomalies: How many top anomalies or high intensities to highlight
581
+ - Number of Components: Complexity of the facial expression model
582
+ - Desired FPS: Frames per second to analyze (lower for faster processing)
583
+ - Batch Size: Affects processing speed and memory usage
584
+ """,
585
  allow_flagging="never"
586
  )
587