File size: 4,293 Bytes
297f570
 
0cb6105
 
 
297f570
153fc95
93146ee
153fc95
 
0cb6105
297f570
 
153fc95
6776f03
93146ee
 
 
153fc95
 
297f570
 
 
 
0cb6105
 
 
297f570
0cb6105
297f570
0cb6105
 
 
 
 
 
93146ee
153fc95
93146ee
 
153fc95
93146ee
153fc95
93146ee
153fc95
 
 
 
93146ee
153fc95
93146ee
 
 
153fc95
 
 
93146ee
 
6776f03
 
93146ee
153fc95
 
6776f03
0cb6105
 
 
 
93146ee
0cb6105
 
 
 
 
 
153fc95
90f98f4
 
 
 
0cb6105
93146ee
 
 
 
153fc95
93146ee
 
 
 
 
0cb6105
 
 
 
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
# 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()