dschandra's picture
Update utils.py
153fc95 verified
# utils.py
import cv2
import numpy as np
def analyze_frame_sequence(frames):
# Simulate realistic trajectory with umpire's call zone
h, w = frames[0].shape[:2]
trajectory_curve = [(200 + i * 10, 300 - i * 8 + i ** 2 // 5) for i in range(6)] # parabolic downward curve
last = trajectory_curve[-1]
return {
"pitch": "in line",
"impact": "in line",
"trajectory": "umpires call", # simulate marginal zone
"shot_offered": True,
"pitch_point": trajectory_curve[0],
"impact_point": trajectory_curve[2],
"trajectory_curve": trajectory_curve,
"stump_zone": [(last[0] - 20, last[1] - 40), (last[0] + 20, last[1] + 40)],
"hit_point": last
}
def make_decision(analysis):
if analysis['pitch'] == 'outside leg':
return "NOT OUT", "Pitched outside leg stump."
if analysis['impact'] == 'outside off' and analysis['shot_offered']:
return "NOT OUT", "Impact outside off with shot offered."
if analysis['trajectory'] == 'missing':
return "NOT OUT", "Ball missing stumps."
if analysis['trajectory'] == 'umpires call':
return "UMPIRE’S CALL", "Marginal trajectory impact."
return "OUT", "Impact in line and ball hitting stumps."
def overlay_text(frame, text, pos, size=1.0, color=(255,255,255)):
cv2.putText(frame, text, pos, cv2.FONT_HERSHEY_SIMPLEX, size, color, 2, cv2.LINE_AA)
def overlay_annotations_dynamic(frame, analysis, t):
h, w = frame.shape[:2]
if t < len(analysis['trajectory_curve']):
ball_pos = analysis['trajectory_curve'][t]
cv2.circle(frame, ball_pos, 8, (0, 255, 0) if analysis['trajectory'] == 'hitting' else (0, 140, 255), -1)
# Draw dashed arc
for i in range(1, min(t + 1, len(analysis['trajectory_curve']))):
p1 = analysis['trajectory_curve'][i - 1]
p2 = analysis['trajectory_curve'][i]
if i % 2 == 0:
cv2.line(frame, p1, p2, (0, 255, 0) if analysis['trajectory'] == 'hitting' else (0, 140, 255), 2)
# Umpire's Call Zone
x1, y1 = analysis['stump_zone'][0]
x2, y2 = analysis['stump_zone'][1]
overlay = frame.copy()
color = (0, 255, 0) if analysis['trajectory'] == 'hitting' else (0, 140, 255) # amber for umpire's call
cv2.rectangle(overlay, (x1, y1), (x2, y2), color, -1)
frame[:] = cv2.addWeighted(overlay, 0.25, frame, 0.75, 0)
cv2.rectangle(frame, (x1, y1), (x2, y2), (180, 180, 180), 2)
shot_icon = "✓" if analysis['shot_offered'] else "✗"
overlay_text(frame, f"Shot Offered: {shot_icon}", (30, 170), 0.8, (0,255,0) if analysis['shot_offered'] else (0,0,255))
overlay_text(frame, f"Trajectory: {analysis['trajectory'].capitalize()}", (30, 130), 0.8,
(0, 255, 0) if analysis['trajectory'] == 'hitting' else (0, 140, 255))
overlay_text(frame, "ICC LBW Review • Third-Umpire Analysis", (30, h - 30), 0.6, (180,180,180))
def render_annotated_clip(frames, analysis, decision, reason, output_path):
h, w = frames[0].shape[:2]
out = cv2.VideoWriter(output_path, cv2.VideoWriter_fourcc(*'mp4v'), 20.0, (w, h))
# Verdict card (1s)
verdict_card = np.zeros_like(frames[0])
overlay_text(verdict_card, f"FINAL DECISION: {decision}", (50, 200), 2.0, (255,255,255))
overlay_text(verdict_card, reason, (50, 250), 0.9, (200,200,200))
for _ in range(20):
out.write(verdict_card)
# Dynamic replay with animated ball tracking and umpire's call zone
for t, f in enumerate(frames):
annotated = f.copy()
overlay_annotations_dynamic(annotated, analysis, t)
out.write(annotated)
# Slow-motion: emphasize final 3 points
for pt in analysis['trajectory_curve'][-3:]:
for _ in range(2):
f = frames[-1].copy()
cv2.circle(f, pt, 10, (0,255,0) if analysis['trajectory']=="hitting" else (0,140,255), -1)
cv2.rectangle(f, analysis['stump_zone'][0], analysis['stump_zone'][1], (180,180,180), 2)
overlay_text(f, "Trajectory End", (pt[0] + 10, pt[1] - 10), 0.8, (255,255,255))
overlay_text(f, "ICC LBW Review • Third-Umpire Analysis", (30, h - 30), 0.6, (180,180,180))
out.write(f)
for _ in range(10):
out.write(verdict_card)
out.release()