File size: 6,609 Bytes
c6abe34 | 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 | """
A utility module providing functions for drawing shapes on video frames.
This module includes functions to draw triangles and ellipses on frames, which can be used
to represent various annotations such as player positions or ball locations in sports analysis.
"""
import cv2
import numpy as np
import sys
sys.path.append('../')
from utils import get_center_of_bbox, get_bbox_width, get_foot_position
def draw_traingle(frame, bbox, color):
"""
Draws a sharp, premium triangle on the frame.
"""
y = int(bbox[1])
x, _ = get_center_of_bbox(bbox)
triangle_points = np.array([
[x, y],
[x - 12, y - 24],
[x + 12, y - 24],
])
# Outer glow / shadow
cv2.drawContours(frame, [triangle_points + [0, 2]], 0, (0, 0, 0), cv2.FILLED)
# Main triangle
cv2.drawContours(frame, [triangle_points], 0, color, cv2.FILLED)
cv2.drawContours(frame, [triangle_points], 0, (255, 255, 255), 1, cv2.LINE_AA)
return frame
def draw_ellipse(frame, bbox, color, track_id=None):
"""
Draws a premium ellipse with a subtle glow and a professional track ID box.
"""
y2 = int(bbox[3])
x_center, _ = get_center_of_bbox(bbox)
width = get_bbox_width(bbox)
axes = (int(width), int(0.35 * width))
# Draw outer glow (semi-transparent)
overlay = frame.copy()
cv2.ellipse(
overlay,
center=(x_center, y2),
axes=(axes[0] + 4, axes[1] + 2),
angle=0.0,
startAngle=-45,
endAngle=235,
color=(0, 0, 0),
thickness=4,
lineType=cv2.LINE_AA
)
cv2.addWeighted(overlay, 0.4, frame, 0.6, 0, frame)
# Main highlight ellipse
cv2.ellipse(
frame,
center=(x_center, y2),
axes=axes,
angle=0.0,
startAngle=-45,
endAngle=235,
color=color,
thickness=2,
lineType=cv2.LINE_AA
)
if track_id is not None:
# Premium ID Box
rectangle_width = 44
rectangle_height = 22
x1_rect = int(x_center - rectangle_width // 2)
y1_rect = int(y2 + 10)
# Shadow for box
cv2.rectangle(frame, (x1_rect + 2, y1_rect + 2), (x1_rect + rectangle_width + 2, y1_rect + rectangle_height + 2), (0,0,0), -1)
# Main box
cv2.rectangle(frame, (x1_rect, y1_rect), (x1_rect + rectangle_width, y1_rect + rectangle_height), color, -1)
# White thin border
cv2.rectangle(frame, (x1_rect, y1_rect), (x1_rect + rectangle_width, y1_rect + rectangle_height), (255,255,255), 1, cv2.LINE_AA)
text = str(track_id)
(tw, th), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_DUPLEX, 0.5, 1)
tx = x1_rect + (rectangle_width - tw) // 2
ty = y1_rect + (rectangle_height + th) // 2
cv2.putText(frame, text, (tx, ty), cv2.FONT_HERSHEY_DUPLEX, 0.5, (0,0,0), 1, cv2.LINE_AA)
return frame
def draw_rounded_rect(frame, rect, color, thickness=1, radius=10):
"""
Draws a rounded rectangle on the given frame using anti-aliased lines.
Supports filled rounded rectangle if thickness < 0.
"""
x, y, w, h = map(int, rect)
if thickness < 0:
cv2.rectangle(frame, (x + radius, y), (x + w - radius, y + h), color, -1)
cv2.rectangle(frame, (x, y + radius), (x + w, y + h - radius), color, -1)
cv2.circle(frame, (x + radius, y + radius), radius, color, -1)
cv2.circle(frame, (x + w - radius, y + radius), radius, color, -1)
cv2.circle(frame, (x + radius, y + h - radius), radius, color, -1)
cv2.circle(frame, (x + w - radius, y + h - radius), radius, color, -1)
return frame
# Top-left corner
cv2.ellipse(frame, (x + radius, y + radius), (radius, radius), 180, 0, 90, color, thickness, cv2.LINE_AA)
# Top-right corner
cv2.ellipse(frame, (x + w - radius, y + radius), (radius, radius), 270, 0, 90, color, thickness, cv2.LINE_AA)
# Bottom-right corner
cv2.ellipse(frame, (x + w - radius, y + h - radius), (radius, radius), 0, 0, 90, color, thickness, cv2.LINE_AA)
# Bottom-left corner
cv2.ellipse(frame, (x + radius, y + h - radius), (radius, radius), 90, 0, 90, color, thickness, cv2.LINE_AA)
# Lines
cv2.line(frame, (x + radius, y), (x + w - radius, y), color, thickness, cv2.LINE_AA)
cv2.line(frame, (x, y + radius), (x, y + h - radius), color, thickness, cv2.LINE_AA)
cv2.line(frame, (x + w, y + radius), (x + w, y + h - radius), color, thickness, cv2.LINE_AA)
cv2.line(frame, (x + radius, y + h), (x + w - radius, y + h), color, thickness, cv2.LINE_AA)
return frame
def draw_glass_panel(frame, rect, alpha=0.7, color=(15, 12, 10), radius=15):
"""
Draws a premium glassmorphic panel.
"""
x, y, w, h = map(int, rect)
overlay = frame.copy()
# Fill background
cv2.rectangle(overlay, (x + radius, y), (x + w - radius, y + h), color, -1)
cv2.rectangle(overlay, (x, y + radius), (x + w, y + h - radius), color, -1)
cv2.circle(overlay, (x + radius, y + radius), radius, color, -1)
cv2.circle(overlay, (x + w - radius, y + radius), radius, color, -1)
cv2.circle(overlay, (x + radius, y + h - radius), radius, color, -1)
cv2.circle(overlay, (x + w - radius, y + h - radius), radius, color, -1)
# Apply alpha blending
frame_new = cv2.addWeighted(overlay, alpha, frame, 1 - alpha, 0)
# Mask area outside panel to preserve original
mask = np.zeros(frame.shape[:2], dtype=np.uint8)
cv2.rectangle(mask, (x + radius, y), (x + w - radius, y + h), 255, -1)
cv2.rectangle(mask, (x, y + radius), (x + w, y + h - radius), 255, -1)
cv2.circle(mask, (x + radius, y + radius), radius, 255, -1)
cv2.circle(mask, (x + w - radius, y + radius), radius, 255, -1)
cv2.circle(mask, (x + radius, y + h - radius), radius, 255, -1)
cv2.circle(mask, (x + w - radius, y + h - radius), radius, 255, -1)
mask_bool = mask > 0
frame[mask_bool] = frame_new[mask_bool]
# Add thin silver border
draw_rounded_rect(frame, (x, y, w, h), (200, 200, 200), thickness=1, radius=radius)
return frame
def draw_text_with_shadow(frame, text, pos, font_scale=0.6, color=(255, 255, 255), thickness=1):
"""
Draws text with a small black shadow for readability.
"""
# Shadow
cv2.putText(frame, text, (pos[0]+1, pos[1]+1), cv2.FONT_HERSHEY_DUPLEX, font_scale, (0, 0, 0), thickness+1, cv2.LINE_AA)
# Main text
cv2.putText(frame, text, pos, cv2.FONT_HERSHEY_DUPLEX, font_scale, color, thickness, cv2.LINE_AA)
return frame |