File size: 13,239 Bytes
e5a1544 473b2d5 0b4ab6d e5a1544 5f27df7 ee83343 5f27df7 f3de933 4a53aae 2553966 0b4ab6d b37a8e6 f3de933 e5a1544 f3de933 e5a1544 f3de933 b37a8e6 0b4ab6d f3de933 b37a8e6 473b2d5 107dab2 0b4ab6d f3de933 107dab2 f3de933 b37a8e6 e5a1544 f3de933 b37a8e6 f3de933 b37a8e6 f3de933 107dab2 f3de933 473b2d5 5f27df7 f3de933 107dab2 f3de933 0b4ab6d f3de933 0b4ab6d f3de933 2553966 91863a8 fd8b339 6754f93 91863a8 1a94dbc 91863a8 fd8b339 91863a8 fd8b339 1a94dbc 91863a8 1a94dbc 91863a8 fd8b339 91863a8 fd8b339 4f14988 fd8b339 91863a8 fd8b339 91863a8 1a94dbc 0b4ab6d 91863a8 2553966 f3de933 2553966 f3de933 4f14988 4a53aae f3de933 4f14988 4a53aae f3de933 4f14988 e5a1544 9a89e03 e5a1544 5148899 0b4ab6d 5148899 fd8b339 5148899 0b4ab6d 5148899 0b4ab6d 5148899 fd8b339 4f14988 5148899 565e309 5148899 0b4ab6d 4f14988 5148899 0b4ab6d dfc63b4 4f14988 fd8b339 5148899 73fbe68 5148899 e5a1544 f3de933 e5a1544 f3de933 0b4ab6d f6a647b e5a1544 5148899 f3de933 0b4ab6d f6a647b 5148899 f3de933 0b4ab6d 4a53aae 5148899 91863a8 1a94dbc fd8b339 0b4ab6d 1a94dbc ea6f081 473b2d5 5148899 473b2d5 4f14988 473b2d5 4f14988 473b2d5 5148899 f3de933 5148899 73fbe68 5148899 fd8b339 07c5ca0 0b4ab6d 565e309 5148899 e5a1544 5148899 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
import gradio as gr
import cv2
import numpy as np
from PIL import Image
import mediapipe as mp
from fer import FER # Facial emotion recognition
from ultralytics import YOLO # YOLOv8 for face detection
from huggingface_hub import hf_hub_download
from supervision import Detections
# -----------------------------
# Configurations
# -----------------------------
SKIP_RATE = 1 # For image processing, always run the analysis
DESIRED_SIZE = (640, 480)
# -----------------------------
# Sample Images (Preset Suggested Photos)
# -----------------------------
SAMPLE_IMAGES = [
"https://upload.wikimedia.org/wikipedia/commons/7/76/Daniel_Diermeier_2020_%28cropped%29.jpg",
"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",
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.jpg/800px-President_Barack_Obama.jpg",
"https://images.wsj.net/im-98527587?width=1280&size=1",
"https://media.npr.org/assets/img/2023/11/28/dr.buolamwiniheadshot_c-naima-green-1-_custom-05cd4ce4570c688d00cc558d16c76745abd07539.png"
]
# -----------------------------
# Global caches for overlay info and frame counters
# -----------------------------
posture_cache = {"landmarks": None, "text": "Initializing...", "counter": 0}
emotion_cache = {"text": "Initializing...", "counter": 0}
faces_cache = {"boxes": None, "text": "Initializing...", "counter": 0}
# -----------------------------
# Initialize Models and Helpers
# -----------------------------
# MediaPipe Pose and Drawing
mp_pose = mp.solutions.pose
pose = mp_pose.Pose()
mp_drawing = mp.solutions.drawing_utils
# Initialize the FER emotion detector (using the FER package)
emotion_detector = FER(mtcnn=True)
# -----------------------------
# Download YOLOv8 face detection model from Hugging Face
# -----------------------------
model_path = hf_hub_download(repo_id="arnabdhar/YOLOv8-Face-Detection", filename="model.pt")
yolo_face_model = YOLO(model_path)
# -----------------------------
# Overlay Drawing Functions
# -----------------------------
def draw_posture_overlay(raw_frame, landmarks):
for connection in mp_pose.POSE_CONNECTIONS:
start_idx, end_idx = connection
if start_idx < len(landmarks) and end_idx < len(landmarks):
start_point = landmarks[start_idx]
end_point = landmarks[end_idx]
cv2.line(raw_frame, start_point, end_point, (50, 205, 50), 2)
for (x, y) in landmarks:
cv2.circle(raw_frame, (x, y), 4, (50, 205, 50), -1)
return raw_frame
def draw_boxes_overlay(raw_frame, boxes, color):
for (x1, y1, x2, y2) in boxes:
cv2.rectangle(raw_frame, (x1, y1), (x2, y2), color, 2)
return raw_frame
# -----------------------------
# Heavy (Synchronous) Detection Functions
# -----------------------------
def compute_posture_overlay(image):
frame_bgr = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
h, w, _ = frame_bgr.shape
frame_bgr_small = cv2.resize(frame_bgr, DESIRED_SIZE)
small_h, small_w, _ = frame_bgr_small.shape
frame_rgb_small = cv2.cvtColor(frame_bgr_small, cv2.COLOR_BGR2RGB)
pose_results = pose.process(frame_rgb_small)
if pose_results.pose_landmarks:
landmarks = []
for lm in pose_results.pose_landmarks.landmark:
x = int(lm.x * small_w * (w / small_w))
y = int(lm.y * small_h * (h / small_h))
landmarks.append((x, y))
text = "Posture detected"
else:
landmarks = []
text = "No posture detected"
return landmarks, text
def compute_emotion_overlay(image):
frame_bgr = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
frame_bgr_small = cv2.resize(frame_bgr, DESIRED_SIZE)
frame_rgb_small = cv2.cvtColor(frame_bgr_small, cv2.COLOR_BGR2RGB)
emotions = emotion_detector.detect_emotions(frame_rgb_small)
if emotions:
top_emotion, score = max(emotions[0]["emotions"].items(), key=lambda x: x[1])
text = f"{top_emotion} ({score:.2f})"
else:
text = "No face detected"
return text
def compute_faces_overlay(image):
"""
Uses the YOLOv8 face detection model from Hugging Face.
Processes the input image and returns bounding boxes using Supervision Detections.
"""
pil_image = image if isinstance(image, Image.Image) else Image.fromarray(image)
output = yolo_face_model(pil_image)
results = Detections.from_ultralytics(output[0])
boxes = []
if results.xyxy.shape[0] > 0:
for box in results.xyxy:
x1, y1, x2, y2 = map(int, box)
boxes.append((x1, y1, x2, y2))
text = f"Detected {len(boxes)} face(s)"
else:
text = "No faces detected"
return boxes, text
# -----------------------------
# New Facemesh Functions (with connected red lines and mask output)
# (Only changes made here are to add a slider-controlled confidence)
# -----------------------------
def compute_facemesh_overlay(image, confidence=0.5):
"""
Uses MediaPipe Face Mesh to detect and draw facial landmarks.
Draws green dots for landmarks and connects them with thin red lines.
Returns two images:
- annotated: the original image overlaid with the facemesh
- mask: a black background image with only the facemesh drawn
"""
frame_bgr = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
h, w, _ = frame_bgr.shape
# Create a copy for annotated output and a black mask
annotated = frame_bgr.copy()
mask = np.zeros_like(frame_bgr)
# Initialize Face Mesh in static mode with adjustable confidence
face_mesh = mp.solutions.face_mesh.FaceMesh(
static_image_mode=True, max_num_faces=1, refine_landmarks=True, min_detection_confidence=confidence
)
results = face_mesh.process(cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB))
if results.multi_face_landmarks:
for face_landmarks in results.multi_face_landmarks:
# Convert landmarks to pixel coordinates
landmark_points = []
for lm in face_landmarks.landmark:
x = int(lm.x * w)
y = int(lm.y * h)
landmark_points.append((x, y))
# Draw thin red lines between connected landmarks using the FACEMESH_TESSELATION
for connection in mp.solutions.face_mesh.FACEMESH_TESSELATION:
start_idx, end_idx = connection
if start_idx < len(landmark_points) and end_idx < len(landmark_points):
pt1 = landmark_points[start_idx]
pt2 = landmark_points[end_idx]
cv2.line(annotated, pt1, pt2, (255, 0, 0), 1)
cv2.line(mask, pt1, pt2, (255, 0, 0), 1)
# Draw green dots for each landmark
for pt in landmark_points:
cv2.circle(annotated, pt, 2, (0, 255, 0), -1)
cv2.circle(mask, pt, 2, (0, 255, 0), -1)
text = "Facemesh detected"
else:
text = "No facemesh detected"
face_mesh.close()
return annotated, mask, text
def analyze_facemesh(image, confidence):
annotated_image, mask_image, text = compute_facemesh_overlay(image, confidence)
return (
annotated_image,
mask_image,
f"<div style='color: #00ff00 !important;'>Facemesh Analysis: {text}</div>"
)
# -----------------------------
# Main Analysis Functions for Single Image
# -----------------------------
def analyze_posture_current(image):
global posture_cache
posture_cache["counter"] += 1
current_frame = np.array(image)
if posture_cache["counter"] % SKIP_RATE == 0 or posture_cache["landmarks"] is None:
landmarks, text = compute_posture_overlay(image)
posture_cache["landmarks"] = landmarks
posture_cache["text"] = text
output = current_frame.copy()
if posture_cache["landmarks"]:
output = draw_posture_overlay(output, posture_cache["landmarks"])
return output, f"<div style='color: #00ff00 !important;'>Posture Analysis: {posture_cache['text']}</div>"
def analyze_emotion_current(image):
global emotion_cache
emotion_cache["counter"] += 1
current_frame = np.array(image)
if emotion_cache["counter"] % SKIP_RATE == 0 or emotion_cache["text"] is None:
text = compute_emotion_overlay(image)
emotion_cache["text"] = text
return current_frame, f"<div style='color: #00ff00 !important;'>Emotion Analysis: {emotion_cache['text']}</div>"
def analyze_faces_current(image):
global faces_cache
faces_cache["counter"] += 1
current_frame = np.array(image)
if faces_cache["counter"] % SKIP_RATE == 0 or faces_cache["boxes"] is None:
boxes, text = compute_faces_overlay(image)
faces_cache["boxes"] = boxes
faces_cache["text"] = text
output = current_frame.copy()
if faces_cache["boxes"]:
output = draw_boxes_overlay(output, faces_cache["boxes"], (0, 0, 255))
return output, f"<div style='color: #00ff00 !important;'>Face Detection: {faces_cache['text']}</div>"
# -----------------------------
# Custom CSS (Revamped High-Contrast Neon Theme with Green Glows removed from text)
# -----------------------------
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
body {
background-color: #121212;
font-family: 'Orbitron', sans-serif;
color: #00ff00 !important;
}
.gradio-container {
background: linear-gradient(135deg, #2d2d2d, #1a1a1a);
border: 2px solid #00ff00;
box-shadow: 0 0 15px #00ff00;
border-radius: 10px;
padding: 20px;
max-width: 1200px;
margin: auto;
}
.gradio-title, .gradio-description, .tab-item, .tab-item *,
label, .label, .wrap .label, .wrap .input, .wrap .output, .wrap .description {
color: #00ff00 !important;
}
input, button, .output {
border: 1px solid #00ff00;
box-shadow: 0 0 8px #00ff00;
color: #00ff00;
background-color: #1a1a1a;
}
/* Added higher specificity override for all elements within the gradio container */
.gradio-container * {
color: #00ff00 !important;
}
"""
# -----------------------------
# Create Individual Interfaces for Image Processing
# -----------------------------
posture_interface = gr.Interface(
fn=analyze_posture_current,
inputs=gr.Image(label="Upload an Image for Posture Analysis"),
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Posture Analysis")],
title="<div style='color:#00ff00;'>Posture",
description="<div style='color:#00ff00;'>Detects posture using MediaPipe with connector lines.</div>",
examples=SAMPLE_IMAGES, # clickable examples at bottom
live=False
)
emotion_interface = gr.Interface(
fn=analyze_emotion_current,
inputs=gr.Image(label="Upload an Image for Emotion Analysis"),
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Emotion Analysis")],
title="<div style='color:#00ff00;'>Emotion",
description="<div style='color:#00ff00;'>Detects facial emotions using FER.</div>",
examples=SAMPLE_IMAGES,
live=False
)
faces_interface = gr.Interface(
fn=analyze_faces_current,
inputs=gr.Image(label="Upload an Image for Face Detection"),
outputs=[gr.Image(type="numpy", label="Annotated Output"), gr.HTML(label="Face Detection")],
title="<div style='color:#00ff00;'>Faces",
description="<div style='color:#00ff00;'>Detects faces using fine-tuned YOLOv8 model.</div>",
examples=SAMPLE_IMAGES,
live=False
)
facemesh_interface = gr.Interface(
fn=analyze_facemesh,
inputs=[
gr.Image(label="Upload an Image for Facemesh"),
gr.Slider(0.0, 1.0, value=0.5, label="Detection Confidence", elem_id="confidence_slider")
],
outputs=[
gr.Image(type="numpy", label="Annotated Output"),
gr.Image(type="numpy", label="Mask Output"),
gr.HTML(label="Facemesh Analysis")
],
title="<div style='color:#00ff00;'>Facemesh",
description="""
<div style='color:#00ff00;'>
Detects facial landmarks using MediaPipe Face Mesh.
<button onclick="document.getElementById('confidence_slider').value = 0.5; document.getElementById('confidence_slider').dispatchEvent(new Event('change'))" style="margin-left:10px;">Reset to Default</button>
</div>
""",
examples=[[img, 0.5] for img in SAMPLE_IMAGES],
live=False
)
tabbed_interface = gr.TabbedInterface(
interface_list=[
posture_interface,
emotion_interface,
faces_interface,
facemesh_interface
],
tab_names=[
"Posture",
"Emotion",
"Faces",
"Facemesh"
]
)
# -----------------------------
# Wrap in a Blocks Layout and Launch
# -----------------------------
demo = gr.Blocks(css=custom_css, theme=None)
with demo:
gr.Markdown("<h1 class='gradio-title'>Multi-Analysis Image App</h1>")
gr.Markdown("<p class='gradio-description'>Upload an image to run analysis for posture, emotions, faces, and facemesh landmarks.</p>")
tabbed_interface.render()
if __name__ == "__main__":
demo.launch()
|