David Driscoll
commited on
Commit
·
0b4ab6d
1
Parent(s):
4f14988
Update
Browse files
app.py
CHANGED
@@ -4,6 +4,9 @@ import numpy as np
|
|
4 |
from PIL import Image
|
5 |
import mediapipe as mp
|
6 |
from fer import FER # Facial emotion recognition
|
|
|
|
|
|
|
7 |
|
8 |
# -----------------------------
|
9 |
# Configuration
|
@@ -11,6 +14,17 @@ from fer import FER # Facial emotion recognition
|
|
11 |
SKIP_RATE = 1 # For image processing, always run the analysis
|
12 |
DESIRED_SIZE = (640, 480)
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
# -----------------------------
|
15 |
# Global caches for overlay info and frame counters
|
16 |
# -----------------------------
|
@@ -21,17 +35,20 @@ faces_cache = {"boxes": None, "text": "Initializing...", "counter": 0}
|
|
21 |
# -----------------------------
|
22 |
# Initialize Models and Helpers
|
23 |
# -----------------------------
|
24 |
-
# MediaPipe Pose
|
25 |
mp_pose = mp.solutions.pose
|
26 |
pose = mp_pose.Pose()
|
27 |
mp_drawing = mp.solutions.drawing_utils
|
28 |
|
29 |
-
mp_face_detection = mp.solutions.face_detection
|
30 |
-
face_detection = mp_face_detection.FaceDetection(min_detection_confidence=0.5)
|
31 |
-
|
32 |
# Initialize the FER emotion detector (using the FER package)
|
33 |
emotion_detector = FER(mtcnn=True)
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
# -----------------------------
|
36 |
# Overlay Drawing Functions
|
37 |
# -----------------------------
|
@@ -86,21 +103,18 @@ def compute_emotion_overlay(image):
|
|
86 |
return text
|
87 |
|
88 |
def compute_faces_overlay(image):
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
|
|
95 |
boxes = []
|
96 |
-
if
|
97 |
-
for
|
98 |
-
|
99 |
-
|
100 |
-
y = int(bbox.ymin * small_h)
|
101 |
-
box_w = int(bbox.width * small_w)
|
102 |
-
box_h = int(bbox.height * small_h)
|
103 |
-
boxes.append((x, y, x + box_w, y + box_h))
|
104 |
text = f"Detected {len(boxes)} face(s)"
|
105 |
else:
|
106 |
text = "No faces detected"
|
@@ -157,8 +171,11 @@ def compute_facemesh_overlay(image):
|
|
157 |
|
158 |
def analyze_facemesh(image):
|
159 |
annotated_image, mask_image, text = compute_facemesh_overlay(image)
|
160 |
-
return (
|
161 |
-
|
|
|
|
|
|
|
162 |
|
163 |
# -----------------------------
|
164 |
# Main Analysis Functions for Single Image
|
@@ -203,11 +220,13 @@ def analyze_faces_current(image):
|
|
203 |
# -----------------------------
|
204 |
custom_css = """
|
205 |
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
|
|
|
206 |
body {
|
207 |
background-color: #121212;
|
208 |
font-family: 'Orbitron', sans-serif;
|
209 |
-
color: #00ff00;
|
210 |
}
|
|
|
211 |
.gradio-container {
|
212 |
background: linear-gradient(135deg, #2d2d2d, #1a1a1a);
|
213 |
border: 2px solid #00ff00;
|
@@ -217,10 +236,13 @@ body {
|
|
217 |
max-width: 1200px;
|
218 |
margin: auto;
|
219 |
}
|
220 |
-
|
|
|
|
|
221 |
color: #00ff00 !important;
|
222 |
text-shadow: 0 0 10px #00ff00;
|
223 |
}
|
|
|
224 |
input, button, .output {
|
225 |
border: 1px solid #00ff00;
|
226 |
box-shadow: 0 0 8px #00ff00;
|
@@ -236,8 +258,9 @@ posture_interface = gr.Interface(
|
|
236 |
fn=analyze_posture_current,
|
237 |
inputs=gr.Image(label="Upload an Image for Posture Analysis"),
|
238 |
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Posture Analysis")],
|
239 |
-
title="Posture",
|
240 |
-
description="Detects
|
|
|
241 |
live=False
|
242 |
)
|
243 |
|
@@ -245,8 +268,9 @@ emotion_interface = gr.Interface(
|
|
245 |
fn=analyze_emotion_current,
|
246 |
inputs=gr.Image(label="Upload an Image for Emotion Analysis"),
|
247 |
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Emotion Analysis")],
|
248 |
-
title="Emotion",
|
249 |
-
description="Detects facial emotions using FER
|
|
|
250 |
live=False
|
251 |
)
|
252 |
|
@@ -254,8 +278,9 @@ faces_interface = gr.Interface(
|
|
254 |
fn=analyze_faces_current,
|
255 |
inputs=gr.Image(label="Upload an Image for Face Detection"),
|
256 |
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Face Detection")],
|
257 |
-
title="Faces",
|
258 |
-
description="Detects faces using
|
|
|
259 |
live=False
|
260 |
)
|
261 |
|
@@ -267,8 +292,9 @@ facemesh_interface = gr.Interface(
|
|
267 |
gr.Image(type="numpy", label="Mask Output"),
|
268 |
gr.HTML(label="Facemesh Analysis")
|
269 |
],
|
270 |
-
title="Facemesh",
|
271 |
-
description="Detects facial landmarks using MediaPipe Face Mesh
|
|
|
272 |
live=False
|
273 |
)
|
274 |
|
@@ -294,6 +320,10 @@ demo = gr.Blocks(css=custom_css)
|
|
294 |
with demo:
|
295 |
gr.Markdown("<h1 class='gradio-title'>Multi-Analysis Image App</h1>")
|
296 |
gr.Markdown("<p class='gradio-description'>Upload an image to run high-tech analysis for posture, emotions, faces, and facemesh landmarks.</p>")
|
|
|
|
|
|
|
|
|
297 |
tabbed_interface.render()
|
298 |
|
299 |
if __name__ == "__main__":
|
|
|
4 |
from PIL import Image
|
5 |
import mediapipe as mp
|
6 |
from fer import FER # Facial emotion recognition
|
7 |
+
from ultralytics import YOLO # YOLOv8 for face detection
|
8 |
+
from huggingface_hub import hf_hub_download
|
9 |
+
from supervision import Detections
|
10 |
|
11 |
# -----------------------------
|
12 |
# Configuration
|
|
|
14 |
SKIP_RATE = 1 # For image processing, always run the analysis
|
15 |
DESIRED_SIZE = (640, 480)
|
16 |
|
17 |
+
# -----------------------------
|
18 |
+
# Sample Images (Preset Suggested Photos)
|
19 |
+
# -----------------------------
|
20 |
+
SAMPLE_IMAGES = [
|
21 |
+
"https://upload.wikimedia.org/wikipedia/commons/7/76/Daniel_Diermeier_2020_%28cropped%29.jpg",
|
22 |
+
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Gilbert_Stuart_Williamstown_Portrait_of_George_Washington.jpg/1200px-Gilbert_Stuart_Williamstown_Portrait_of_George_Washington.jpg",
|
23 |
+
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.jpg/800px-President_Barack_Obama.jpg",
|
24 |
+
"https://images.wsj.net/im-98527587?width=1280&size=1",
|
25 |
+
"https://media.npr.org/assets/img/2023/11/28/dr.buolamwiniheadshot_c-naima-green-1-_custom-05cd4ce4570c688d00cc558d16c76745abd07539.png"
|
26 |
+
]
|
27 |
+
|
28 |
# -----------------------------
|
29 |
# Global caches for overlay info and frame counters
|
30 |
# -----------------------------
|
|
|
35 |
# -----------------------------
|
36 |
# Initialize Models and Helpers
|
37 |
# -----------------------------
|
38 |
+
# MediaPipe Pose and Drawing
|
39 |
mp_pose = mp.solutions.pose
|
40 |
pose = mp_pose.Pose()
|
41 |
mp_drawing = mp.solutions.drawing_utils
|
42 |
|
|
|
|
|
|
|
43 |
# Initialize the FER emotion detector (using the FER package)
|
44 |
emotion_detector = FER(mtcnn=True)
|
45 |
|
46 |
+
# -----------------------------
|
47 |
+
# Download YOLOv8 face detection model from Hugging Face
|
48 |
+
# -----------------------------
|
49 |
+
model_path = hf_hub_download(repo_id="arnabdhar/YOLOv8-Face-Detection", filename="model.pt")
|
50 |
+
yolo_face_model = YOLO(model_path)
|
51 |
+
|
52 |
# -----------------------------
|
53 |
# Overlay Drawing Functions
|
54 |
# -----------------------------
|
|
|
103 |
return text
|
104 |
|
105 |
def compute_faces_overlay(image):
|
106 |
+
"""
|
107 |
+
Uses the YOLOv8 face detection model from Hugging Face.
|
108 |
+
Processes the input image and returns bounding boxes using Supervision Detections.
|
109 |
+
"""
|
110 |
+
pil_image = image if isinstance(image, Image.Image) else Image.fromarray(image)
|
111 |
+
output = yolo_face_model(pil_image)
|
112 |
+
results = Detections.from_ultralytics(output[0])
|
113 |
boxes = []
|
114 |
+
if results.xyxy.shape[0] > 0:
|
115 |
+
for box in results.xyxy:
|
116 |
+
x1, y1, x2, y2 = map(int, box)
|
117 |
+
boxes.append((x1, y1, x2, y2))
|
|
|
|
|
|
|
|
|
118 |
text = f"Detected {len(boxes)} face(s)"
|
119 |
else:
|
120 |
text = "No faces detected"
|
|
|
171 |
|
172 |
def analyze_facemesh(image):
|
173 |
annotated_image, mask_image, text = compute_facemesh_overlay(image)
|
174 |
+
return (
|
175 |
+
annotated_image,
|
176 |
+
mask_image,
|
177 |
+
f"<div style='color: #00ff00 !important;'>Facemesh Analysis: {text}</div>"
|
178 |
+
)
|
179 |
|
180 |
# -----------------------------
|
181 |
# Main Analysis Functions for Single Image
|
|
|
220 |
# -----------------------------
|
221 |
custom_css = """
|
222 |
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
|
223 |
+
|
224 |
body {
|
225 |
background-color: #121212;
|
226 |
font-family: 'Orbitron', sans-serif;
|
227 |
+
color: #00ff00 !important;
|
228 |
}
|
229 |
+
|
230 |
.gradio-container {
|
231 |
background: linear-gradient(135deg, #2d2d2d, #1a1a1a);
|
232 |
border: 2px solid #00ff00;
|
|
|
236 |
max-width: 1200px;
|
237 |
margin: auto;
|
238 |
}
|
239 |
+
|
240 |
+
.gradio-title, .gradio-description, .tab-item, .tab-item *,
|
241 |
+
label, .label, .wrap .label, .wrap .input, .wrap .output, .wrap .description {
|
242 |
color: #00ff00 !important;
|
243 |
text-shadow: 0 0 10px #00ff00;
|
244 |
}
|
245 |
+
|
246 |
input, button, .output {
|
247 |
border: 1px solid #00ff00;
|
248 |
box-shadow: 0 0 8px #00ff00;
|
|
|
258 |
fn=analyze_posture_current,
|
259 |
inputs=gr.Image(label="Upload an Image for Posture Analysis"),
|
260 |
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Posture Analysis")],
|
261 |
+
title="<div style='color:#00ff00;'>Posture",
|
262 |
+
description="<div style='color:#00ff00;'>Detects posture using MediaPipe with connector lines.</div>",
|
263 |
+
examples=SAMPLE_IMAGES, # clickable examples at bottom
|
264 |
live=False
|
265 |
)
|
266 |
|
|
|
268 |
fn=analyze_emotion_current,
|
269 |
inputs=gr.Image(label="Upload an Image for Emotion Analysis"),
|
270 |
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Emotion Analysis")],
|
271 |
+
title="<div style='color:#00ff00;'>Emotion",
|
272 |
+
description="<div style='color:#00ff00;'>Detects facial emotions using FER.</div>",
|
273 |
+
examples=SAMPLE_IMAGES,
|
274 |
live=False
|
275 |
)
|
276 |
|
|
|
278 |
fn=analyze_faces_current,
|
279 |
inputs=gr.Image(label="Upload an Image for Face Detection"),
|
280 |
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Face Detection")],
|
281 |
+
title="<div style='color:#00ff00;'>Faces",
|
282 |
+
description="<div style='color:#00ff00;'>Detects faces using fine-tuned YOLOv8 model.</div>",
|
283 |
+
examples=SAMPLE_IMAGES,
|
284 |
live=False
|
285 |
)
|
286 |
|
|
|
292 |
gr.Image(type="numpy", label="Mask Output"),
|
293 |
gr.HTML(label="Facemesh Analysis")
|
294 |
],
|
295 |
+
title="<div style='color:#00ff00;'>Facemesh",
|
296 |
+
description="<div style='color:#00ff00;'>Detects facial landmarks using MediaPipe Face Mesh.</div>",
|
297 |
+
examples=SAMPLE_IMAGES,
|
298 |
live=False
|
299 |
)
|
300 |
|
|
|
320 |
with demo:
|
321 |
gr.Markdown("<h1 class='gradio-title'>Multi-Analysis Image App</h1>")
|
322 |
gr.Markdown("<p class='gradio-description'>Upload an image to run high-tech analysis for posture, emotions, faces, and facemesh landmarks.</p>")
|
323 |
+
|
324 |
+
# We removed the top-row sample images and now rely on
|
325 |
+
# the built-in Gradio examples at the bottom of each tab.
|
326 |
+
|
327 |
tabbed_interface.render()
|
328 |
|
329 |
if __name__ == "__main__":
|