Sign2Voice / signspeak /debug_video.py
lilblueyes's picture
Add sliding-window ASL phrase prototype
7d969c6
Raw
History Blame Contribute Delete
4.11 kB
from __future__ import annotations
import tempfile
import time
from pathlib import Path
from typing import Any
from .asl.mediapipe_utils import draw_holistic_landmarks
def create_debug_overlay_video(video_path: str | Path, result: dict[str, Any]) -> str:
cv2 = _load_cv2()
mp, holistic = _load_holistic()
path = Path(video_path)
cap = cv2.VideoCapture(str(path))
if not cap.isOpened():
raise ValueError(f"Could not open video for debug overlay: {path}")
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) or 640)
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) or 480)
fps = float(cap.get(cv2.CAP_PROP_FPS) or 24.0)
if fps <= 0:
fps = 24.0
output_path = Path(tempfile.gettempdir()) / f"signspeak_debug_{int(time.time() * 1000)}.mp4"
writer = cv2.VideoWriter(
str(output_path),
cv2.VideoWriter_fourcc(*"mp4v"),
fps,
(width, height),
)
if not writer.isOpened():
cap.release()
raise RuntimeError(f"Could not create debug overlay video: {output_path}")
asl = result.get("asl", {})
emotion = result.get("emotion", {})
intent = result.get("intent_input", {})
glosses = intent.get("detected_glosses") or asl.get("gloss_sequence") or []
gloss_text = " ".join(str(gloss) for gloss in glosses) if glosses else "NO ASL WORDS DETECTED"
emotion_text = str(emotion.get("dominant_emotion", "unknown")).upper()
top_prediction = asl.get("top_prediction") or "none"
confidence = float(asl.get("confidence", 0.0) or 0.0)
threshold = float(asl.get("confidence_threshold", 0.0) or 0.0)
windows_used = int(asl.get("windows_used", 0) or 0)
status_text = (
f"ASL {asl.get('status', 'unknown')} | top {top_prediction} "
f"{confidence:.2f}/{threshold:.2f} | windows {windows_used} | EMOTION {emotion.get('status', 'unknown')}"
)
try:
while True:
ok, frame = cap.read()
if not ok or frame is None:
break
if mp is not None and holistic is not None:
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = holistic.process(frame_rgb)
draw_holistic_landmarks(mp, frame, results)
_draw_overlay(cv2, frame, gloss_text, emotion_text, status_text)
writer.write(frame)
finally:
cap.release()
if holistic is not None:
holistic.close()
writer.release()
return str(output_path)
def _draw_overlay(cv2, frame, gloss_text: str, emotion_text: str, status_text: str) -> None:
height, width = frame.shape[:2]
pad = 14
panel_height = 112
cv2.rectangle(frame, (0, 0), (width, panel_height), (8, 11, 16), -1)
cv2.rectangle(frame, (0, panel_height - 2), (width, panel_height), (45, 212, 191), -1)
_put_text(cv2, frame, "DETECTED ASL", (pad, 28), 0.52, (203, 213, 225), 1)
_put_text(cv2, frame, gloss_text, (pad, 64), 0.86, (248, 250, 252), 2)
_put_text(cv2, frame, f"EMOTION: {emotion_text}", (pad, 96), 0.58, (245, 158, 11), 2)
status_size = cv2.getTextSize(status_text, cv2.FONT_HERSHEY_SIMPLEX, 0.46, 1)[0]
x = max(pad, width - status_size[0] - pad)
_put_text(cv2, frame, status_text, (x, height - 18), 0.46, (203, 213, 225), 1)
def _put_text(cv2, frame, text: str, origin: tuple[int, int], scale: float, color: tuple[int, int, int], thickness: int) -> None:
cv2.putText(
frame,
text[:90],
origin,
cv2.FONT_HERSHEY_SIMPLEX,
scale,
color,
thickness,
cv2.LINE_AA,
)
def _load_cv2():
try:
import cv2
return cv2
except Exception as exc:
raise RuntimeError("OpenCV is required for debug overlay video generation.") from exc
def _load_holistic():
try:
import mediapipe as mp
return (
mp,
mp.solutions.holistic.Holistic(
min_detection_confidence=0.5,
min_tracking_confidence=0.5,
),
)
except Exception:
return None, None