SentAI / app.py
Solar-Prince's picture
Upload 3 files
7fd00f2 verified
import time
from typing import Optional, Tuple
import cv2
import gradio as gr
import numpy as np
FACE_CASCADE = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
CUSTOM_CSS = """
:root { --radius-xl: 22px; }
.gradio-container {
max-width: 1500px !important;
margin: auto !important;
background: radial-gradient(circle at 15% 15%, rgba(38, 166, 191, .22), transparent 30%),
linear-gradient(135deg, #101827 0%, #273142 48%, #10232b 100%) !important;
color: #eef6ff !important;
}
#component-0, .contain, .block, .panel {
border-radius: var(--radius-xl) !important;
}
.prose h1, h1 {
font-size: clamp(2.2rem, 5vw, 4.4rem) !important;
text-align: center !important;
letter-spacing: -0.04em !important;
}
.prose p, .prose li { color: #c9d6e8 !important; }
label, .label-wrap span { font-weight: 800 !important; }
button {
border-radius: 999px !important;
font-weight: 800 !important;
}
.image-container, .wrap, .block {
overflow: hidden !important;
}
#status_box textarea, #status_box .prose {
font-size: 1.05rem !important;
}
@media (max-width: 760px) {
.gradio-container { padding: 8px !important; }
h1 { font-size: 2.35rem !important; }
.image-container img, .image-container video { max-height: 62vh !important; object-fit: contain !important; }
}
"""
HEADER = """
# FaceSense Live
Real-time face boxes for desktop and mobile webcam testing.
Click **Record** on the camera panel to start live analysis. On phones, allow camera permission; your browser may offer front/rear camera selection.
"""
def draw_corner_box(img: np.ndarray, x: int, y: int, w: int, h: int) -> None:
color = (0, 238, 255)
shadow = (6, 18, 28)
thickness = 4
line = max(18, int(min(w, h) * 0.22))
# shadow first for readability
cv2.rectangle(img, (x, y), (x + w, y + h), shadow, 7)
# corner style box
cv2.line(img, (x, y), (x + line, y), color, thickness)
cv2.line(img, (x, y), (x, y + line), color, thickness)
cv2.line(img, (x + w, y), (x + w - line, y), color, thickness)
cv2.line(img, (x + w, y), (x + w, y + line), color, thickness)
cv2.line(img, (x, y + h), (x + line, y + h), color, thickness)
cv2.line(img, (x, y + h), (x, y + h - line), color, thickness)
cv2.line(img, (x + w, y + h), (x + w - line, y + h), color, thickness)
cv2.line(img, (x + w, y + h), (x + w, y + h - line), color, thickness)
label = "Face detected"
cv2.rectangle(img, (x, max(0, y - 38)), (x + 185, y), (0, 145, 175), -1)
cv2.putText(img, label, (x + 10, max(22, y - 12)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
def process_frame(frame: Optional[np.ndarray]) -> Tuple[Optional[np.ndarray], str]:
start = time.perf_counter()
if frame is None:
return None, "### Waiting for camera\nClick **Record** on the webcam panel to start live analysis."
output = frame.copy()
if output.ndim == 2:
output = cv2.cvtColor(output, cv2.COLOR_GRAY2RGB)
if output.shape[-1] == 4:
output = cv2.cvtColor(output, cv2.COLOR_RGBA2RGB)
gray = cv2.cvtColor(output, cv2.COLOR_RGB2GRAY)
gray = cv2.equalizeHist(gray)
faces = FACE_CASCADE.detectMultiScale(
gray,
scaleFactor=1.08,
minNeighbors=5,
minSize=(55, 55),
flags=cv2.CASCADE_SCALE_IMAGE,
)
for (x, y, w, h) in faces:
draw_corner_box(output, int(x), int(y), int(w), int(h))
elapsed_ms = (time.perf_counter() - start) * 1000
fps = 1000 / elapsed_ms if elapsed_ms > 0 else 0
if len(faces) == 0:
status = (
"### Live status\n"
"No frontal face detected yet. Move your face toward the center, improve lighting, and keep the camera steady.\n\n"
f"**Processing:** {elapsed_ms:.1f} ms \n"
f"**Approx FPS:** {fps:.1f}"
)
else:
status = (
"### Live status\n"
f"**Faces detected:** {len(faces)} \n"
"**Phase 1:** bounding boxes only \n"
"**Next phase:** facial expression + apparent age range + optional presentation estimate \n\n"
f"**Processing:** {elapsed_ms:.1f} ms \n"
f"**Approx FPS:** {fps:.1f}"
)
return output, status
demo = gr.Interface(
fn=process_frame,
inputs=gr.Image(
label="Camera input",
sources=["webcam"],
type="numpy",
streaming=True,
mirror_webcam=True,
height=520,
),
outputs=[
gr.Image(label="Annotated output", type="numpy", height=520),
gr.Markdown(label="Status"),
],
title="FaceSense Live",
description=HEADER,
live=True,
css=CUSTOM_CSS,
allow_flagging="never",
api_name="predict",
)
if __name__ == "__main__":
demo.queue(default_concurrency_limit=4).launch()