beaupreda's picture
Upload sensAI-Generic-Object-Detection with upload_repo.py
13170f7 verified
Raw
History Blame Contribute Delete
3.74 kB
"""Frame overlay and annotation drawing utilities.
Provides functions for compositing visual overlays onto video frames
(banners, timers, centered text). Extracted from ``frame_utils`` to
keep that module focused on frame extraction/loading.
"""
import cv2
import numpy as np
def draw_countdown_banner(frame: np.ndarray, text: str) -> np.ndarray:
"""Draw a warning banner at the top of the frame (doesn't obscure the feed).
Args:
frame: Input frame (any color space).
text: Text to display in the banner.
Returns:
Copy of the frame with the banner composited in.
"""
result = frame.copy()
h, w = result.shape[:2]
banner_h = max(40, h // 12)
overlay = result.copy()
cv2.rectangle(overlay, (0, 0), (w, banner_h), (0, 0, 180), -1)
cv2.addWeighted(overlay, 0.7, result, 0.3, 0, result)
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.7
thickness = 2
text_size = cv2.getTextSize(text, font, font_scale, thickness)[0]
x = (w - text_size[0]) // 2
y = (banner_h + text_size[1]) // 2
cv2.putText(result, text, (x, y), font, font_scale, (255, 255, 255), thickness)
return result
def draw_session_timer(frame: np.ndarray, remaining_seconds: float) -> np.ndarray:
"""Draw a small session timer in the bottom-right corner of the frame.
Shows remaining time as "M:SS". Uses a subtle semi-transparent dark
background pill with white text — informational, not a warning.
Args:
frame: Input frame (any color space).
remaining_seconds: Seconds remaining in the session.
Returns:
Copy of the frame with the timer composited in.
"""
minutes = int(remaining_seconds) // 60
seconds = int(remaining_seconds) % 60
text = f"{minutes}:{seconds:02d}"
result = frame.copy()
h, w = result.shape[:2]
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.5
thickness = 1
text_size = cv2.getTextSize(text, font, font_scale, thickness)[0]
pad_x, pad_y = 8, 6
margin = 10
box_w = text_size[0] + 2 * pad_x
box_h = text_size[1] + 2 * pad_y
x1 = w - box_w - margin
y1 = h - box_h - margin
x2 = x1 + box_w
y2 = y1 + box_h
# Semi-transparent dark background
overlay = result.copy()
cv2.rectangle(overlay, (x1, y1), (x2, y2), (0, 0, 0), -1)
cv2.addWeighted(overlay, 0.5, result, 0.5, 0, result)
text_x = x1 + pad_x
text_y = y2 - pad_y
cv2.putText(result, text, (text_x, text_y), font, font_scale, (255, 255, 255), thickness)
return result
def draw_overlay(frame: np.ndarray, text: str) -> np.ndarray:
"""Draw a semi-transparent overlay with centered text on the frame.
Args:
frame: Input frame (any color space).
text: Text to display in the center of the frame.
Returns:
Copy of the frame with the overlay composited in.
"""
overlay = frame.copy()
h, w = overlay.shape[:2]
cv2.rectangle(overlay, (0, h // 3), (w, 2 * h // 3), (0, 0, 0), -1)
result = cv2.addWeighted(frame, 0.4, overlay, 0.6, 0)
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1.0
thickness = 2
lines = text.split("\n")
line_sizes = [cv2.getTextSize(line, font, font_scale, thickness)[0] for line in lines]
line_height = max(s[1] for s in line_sizes)
line_gap = int(line_height * 0.6)
total_height = len(lines) * line_height + (len(lines) - 1) * line_gap
# Start y so the block is vertically centered
y = h // 2 - total_height // 2 + line_height
for line, (tw, th) in zip(lines, line_sizes):
x = (w - tw) // 2
cv2.putText(result, line, (x, y), font, font_scale, (255, 255, 255), thickness)
y += line_height + line_gap
return result