Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -18,6 +18,7 @@ from matplotlib.ticker import MaxNLocator
|
|
| 18 |
import gradio as gr
|
| 19 |
import tempfile
|
| 20 |
import shutil
|
|
|
|
| 21 |
|
| 22 |
# Initialize models and other global variables
|
| 23 |
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
@@ -76,48 +77,79 @@ def alignFace(img):
|
|
| 76 |
new_img = cv2.warpAffine(img_raw, rotation_matrix, (width, height))
|
| 77 |
return new_img
|
| 78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
def extract_and_align_faces_from_video(video_path, aligned_faces_folder, desired_fps):
|
| 80 |
-
video
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
embeddings_by_frame = {}
|
| 90 |
emotions_by_frame = {}
|
| 91 |
|
| 92 |
-
for
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
-
video.release()
|
| 121 |
return embeddings_by_frame, emotions_by_frame, desired_fps, original_fps
|
| 122 |
|
| 123 |
def cluster_embeddings(embeddings):
|
|
@@ -264,7 +296,7 @@ def plot_emotion(df, emotion):
|
|
| 264 |
ax.xaxis.set_major_locator(MaxNLocator(nbins=100))
|
| 265 |
ticks = ax.get_xticks()
|
| 266 |
ax.set_xticklabels([df['Timecode'].iloc[int(tick)] if tick >= 0 and tick < len(df) else '' for tick in ticks], rotation=90, ha='right')
|
| 267 |
-
|
| 268 |
return fig
|
| 269 |
|
| 270 |
def process_video(video_path, num_anomalies, num_components, desired_fps, batch_size, progress=gr.Progress()):
|
|
@@ -275,7 +307,10 @@ def process_video(video_path, num_anomalies, num_components, desired_fps, batch_
|
|
| 275 |
os.makedirs(organized_faces_folder, exist_ok=True)
|
| 276 |
|
| 277 |
progress(0.1, "Extracting and aligning faces")
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
if not embeddings_by_frame:
|
| 281 |
return "No faces were extracted from the video.", None, None, None, None
|
|
@@ -292,11 +327,17 @@ def process_video(video_path, num_anomalies, num_components, desired_fps, batch_
|
|
| 292 |
|
| 293 |
progress(0.6, "Performing anomaly detection")
|
| 294 |
feature_columns = [col for col in df.columns if col not in ['Frame', 'Timecode', 'Time (Minutes)', 'Embedding_Index']]
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
progress(0.8, "Generating plots")
|
| 298 |
-
|
| 299 |
-
|
|
|
|
|
|
|
|
|
|
| 300 |
|
| 301 |
progress(0.9, "Preparing results")
|
| 302 |
results = f"Top {num_anomalies} anomalies (All Features):\n"
|
|
@@ -327,4 +368,5 @@ iface = gr.Interface(
|
|
| 327 |
description="Upload a video to detect anomalies in facial expressions and emotions. Adjust parameters as needed."
|
| 328 |
)
|
| 329 |
|
| 330 |
-
|
|
|
|
|
|
| 18 |
import gradio as gr
|
| 19 |
import tempfile
|
| 20 |
import shutil
|
| 21 |
+
import subprocess
|
| 22 |
|
| 23 |
# Initialize models and other global variables
|
| 24 |
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
|
|
| 77 |
new_img = cv2.warpAffine(img_raw, rotation_matrix, (width, height))
|
| 78 |
return new_img
|
| 79 |
|
| 80 |
+
def extract_frames(video_path, output_folder, fps):
|
| 81 |
+
os.makedirs(output_folder, exist_ok=True)
|
| 82 |
+
command = [
|
| 83 |
+
'ffmpeg',
|
| 84 |
+
'-i', video_path,
|
| 85 |
+
'-vf', f'fps={fps}',
|
| 86 |
+
f'{output_folder}/frame_%04d.jpg'
|
| 87 |
+
]
|
| 88 |
+
try:
|
| 89 |
+
subprocess.run(command, check=True, capture_output=True, text=True)
|
| 90 |
+
except subprocess.CalledProcessError as e:
|
| 91 |
+
print(f"Error extracting frames: {e}")
|
| 92 |
+
print(f"FFmpeg output: {e.output}")
|
| 93 |
+
raise
|
| 94 |
+
|
| 95 |
def extract_and_align_faces_from_video(video_path, aligned_faces_folder, desired_fps):
|
| 96 |
+
print(f"Processing video: {video_path}")
|
| 97 |
+
|
| 98 |
+
# Extract frames using FFmpeg
|
| 99 |
+
frames_folder = os.path.join(os.path.dirname(aligned_faces_folder), 'extracted_frames')
|
| 100 |
+
extract_frames(video_path, frames_folder, desired_fps)
|
| 101 |
+
|
| 102 |
+
# Get video info
|
| 103 |
+
ffprobe_command = [
|
| 104 |
+
'ffprobe',
|
| 105 |
+
'-v', 'error',
|
| 106 |
+
'-select_streams', 'v:0',
|
| 107 |
+
'-count_packets',
|
| 108 |
+
'-show_entries', 'stream=nb_read_packets,r_frame_rate',
|
| 109 |
+
'-of', 'csv=p=0',
|
| 110 |
+
video_path
|
| 111 |
+
]
|
| 112 |
+
ffprobe_output = subprocess.check_output(ffprobe_command, universal_newlines=True).strip().split(',')
|
| 113 |
+
frame_count = int(ffprobe_output[0])
|
| 114 |
+
original_fps = eval(ffprobe_output[1])
|
| 115 |
+
|
| 116 |
+
print(f"Total frames: {frame_count}, Original FPS: {original_fps}, Desired FPS: {desired_fps}")
|
| 117 |
+
|
| 118 |
embeddings_by_frame = {}
|
| 119 |
emotions_by_frame = {}
|
| 120 |
|
| 121 |
+
for frame_file in sorted(os.listdir(frames_folder)):
|
| 122 |
+
if frame_file.endswith('.jpg'):
|
| 123 |
+
frame_num = int(frame_file.split('_')[1].split('.')[0])
|
| 124 |
+
frame_path = os.path.join(frames_folder, frame_file)
|
| 125 |
+
frame = cv2.imread(frame_path)
|
| 126 |
+
|
| 127 |
+
if frame is None:
|
| 128 |
+
print(f"Skipping frame {frame_num}: Could not read frame")
|
| 129 |
+
continue
|
| 130 |
+
|
| 131 |
+
try:
|
| 132 |
+
boxes, probs = mtcnn.detect(frame)
|
| 133 |
+
if boxes is not None and len(boxes) > 0:
|
| 134 |
+
box = boxes[0]
|
| 135 |
+
if probs[0] >= 0.99:
|
| 136 |
+
x1, y1, x2, y2 = [int(b) for b in box]
|
| 137 |
+
face = frame[y1:y2, x1:x2]
|
| 138 |
+
if face.size == 0:
|
| 139 |
+
print(f"Skipping frame {frame_num}: Detected face region is empty")
|
| 140 |
+
continue
|
| 141 |
+
aligned_face = alignFace(face)
|
| 142 |
+
if aligned_face is not None:
|
| 143 |
+
aligned_face_resized = cv2.resize(aligned_face, (160, 160))
|
| 144 |
+
output_path = os.path.join(aligned_faces_folder, f"frame_{frame_num}_face.jpg")
|
| 145 |
+
cv2.imwrite(output_path, aligned_face_resized)
|
| 146 |
+
embedding, emotion = get_face_embedding_and_emotion(aligned_face_resized)
|
| 147 |
+
embeddings_by_frame[frame_num] = embedding
|
| 148 |
+
emotions_by_frame[frame_num] = emotion
|
| 149 |
+
except Exception as e:
|
| 150 |
+
print(f"Error processing frame {frame_num}: {str(e)}")
|
| 151 |
+
continue
|
| 152 |
|
|
|
|
| 153 |
return embeddings_by_frame, emotions_by_frame, desired_fps, original_fps
|
| 154 |
|
| 155 |
def cluster_embeddings(embeddings):
|
|
|
|
| 296 |
ax.xaxis.set_major_locator(MaxNLocator(nbins=100))
|
| 297 |
ticks = ax.get_xticks()
|
| 298 |
ax.set_xticklabels([df['Timecode'].iloc[int(tick)] if tick >= 0 and tick < len(df) else '' for tick in ticks], rotation=90, ha='right')
|
| 299 |
+
plt.tight_layout()
|
| 300 |
return fig
|
| 301 |
|
| 302 |
def process_video(video_path, num_anomalies, num_components, desired_fps, batch_size, progress=gr.Progress()):
|
|
|
|
| 307 |
os.makedirs(organized_faces_folder, exist_ok=True)
|
| 308 |
|
| 309 |
progress(0.1, "Extracting and aligning faces")
|
| 310 |
+
try:
|
| 311 |
+
embeddings_by_frame, emotions_by_frame, _, original_fps = extract_and_align_faces_from_video(video_path, aligned_faces_folder, desired_fps)
|
| 312 |
+
except Exception as e:
|
| 313 |
+
return f"Error extracting faces: {str(e)}", None, None, None, None
|
| 314 |
|
| 315 |
if not embeddings_by_frame:
|
| 316 |
return "No faces were extracted from the video.", None, None, None, None
|
|
|
|
| 327 |
|
| 328 |
progress(0.6, "Performing anomaly detection")
|
| 329 |
feature_columns = [col for col in df.columns if col not in ['Frame', 'Timecode', 'Time (Minutes)', 'Embedding_Index']]
|
| 330 |
+
try:
|
| 331 |
+
anomalies_all, anomaly_scores_all, top_indices_all, _ = lstm_anomaly_detection(df[feature_columns].values, feature_columns, num_anomalies=num_anomalies, batch_size=batch_size)
|
| 332 |
+
except Exception as e:
|
| 333 |
+
return f"Error in anomaly detection: {str(e)}", None, None, None, None
|
| 334 |
|
| 335 |
progress(0.8, "Generating plots")
|
| 336 |
+
try:
|
| 337 |
+
anomaly_plot = plot_anomaly_scores(df, anomaly_scores_all, top_indices_all, "All Features")
|
| 338 |
+
emotion_plots = [plot_emotion(df, emotion) for emotion in ['fear', 'sad', 'angry']]
|
| 339 |
+
except Exception as e:
|
| 340 |
+
return f"Error generating plots: {str(e)}", None, None, None, None
|
| 341 |
|
| 342 |
progress(0.9, "Preparing results")
|
| 343 |
results = f"Top {num_anomalies} anomalies (All Features):\n"
|
|
|
|
| 368 |
description="Upload a video to detect anomalies in facial expressions and emotions. Adjust parameters as needed."
|
| 369 |
)
|
| 370 |
|
| 371 |
+
if __name__ == "__main__":
|
| 372 |
+
iface.launch()
|