dschandra commited on
Commit
4da8df7
·
verified ·
1 Parent(s): ae1975b

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +130 -0
  2. requirements.txt +14 -0
app.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+ from pathlib import Path
4
+ import numpy as np
5
+ import cv2
6
+ from utils.video_utils import extract_frames_from_video, save_frames_to_video
7
+ from detection.detector import Detector
8
+ from tracking.ball_tracker import BasicBallTracker
9
+ from trajectory.fit_trajectory import TrajectoryFitter
10
+ from rules.lbw_engine import LBWEngine
11
+ from visualization.overlay_generator import OverlayGenerator
12
+
13
+ # Initialize components
14
+ detector = Detector()
15
+ tracker = BasicBallTracker()
16
+ traj_fitter = TrajectoryFitter()
17
+ umpire = LBWEngine()
18
+ visual = OverlayGenerator(config=None)
19
+
20
+ def review_video(video_file):
21
+ tmp_path = Path(video_file) # video_file is already a path string
22
+ if not tmp_path.is_file():
23
+ return "Video file not found", None
24
+
25
+ try:
26
+ frames = extract_frames_from_video(str(tmp_path))
27
+ except Exception as e:
28
+ return f"Error extracting frames: {str(e)}", None
29
+
30
+ # Ball detection and tracking
31
+ for idx, frm in enumerate(frames):
32
+ dets = detector.infer(frm)
33
+ tracker.update(dets, idx)
34
+
35
+ track_pts = [(int(x), int(y)) for _, x, y in tracker.get_track()]
36
+ if len(track_pts) < 5:
37
+ return "Insufficient ball points detected", None
38
+
39
+ # Fit trajectory and project the ball path dynamically
40
+ traj_fitter.fit(track_pts)
41
+ xs = np.linspace(track_pts[0][0], track_pts[-1][0], 100)
42
+ ys = traj_fitter.project(xs)
43
+
44
+ if len(xs) < 2 or len(ys) < 2:
45
+ return "Trajectory fitting failed", None
46
+
47
+ curve_pts = list(zip(xs.astype(int), ys.astype(int)))
48
+
49
+ # Dynamic pitch zone calculation (based on first detected point)
50
+ pitch_zone = determine_pitch_zone(track_pts[0])
51
+
52
+ # Dynamic impact zone calculation (based on trajectory and impact)
53
+ impact_zone = determine_impact_zone(track_pts)
54
+
55
+ # Check if the ball hits the stumps
56
+ hits_stumps = check_stumps_impact(curve_pts)
57
+
58
+ # Decision logic: determine if it’s OUT or NOT OUT dynamically
59
+ verdict, reason = umpire.decide({
60
+ "pitch_zone": pitch_zone,
61
+ "impact_zone": impact_zone,
62
+ "hits_stumps": hits_stumps,
63
+ "shot_offered": False, # This can be improved with player pose detection
64
+ })
65
+
66
+ # Annotate the frames with the trajectory and decision dynamically
67
+ annotated_frames = []
68
+ for frm in frames:
69
+ annotated_frame = visual.draw(frm.copy(), curve_pts, verdict)
70
+ annotated_frames.append(annotated_frame)
71
+
72
+ # Save the annotated video to a temporary file
73
+ out_file = tempfile.NamedTemporaryFile(suffix="_drs.mp4", delete=False)
74
+ save_frames_to_video(annotated_frames, out_file.name)
75
+ out_file.close() # Ensure the temporary file is closed and accessible
76
+
77
+ return verdict + ": " + reason, out_file.name
78
+
79
+ # Helper Functions
80
+ def determine_pitch_zone(first_point):
81
+ """
82
+ Determine if the ball is in-line with the stumps or outside off/leg.
83
+ Dynamic based on the ball's first detected position.
84
+ """
85
+ x, y = first_point # first_point is (x, y)
86
+ # Example logic: check if the ball is in-line or outside
87
+ if x < 300: # In-line with stumps (This is just an example threshold)
88
+ return "inline"
89
+ elif 300 <= x <= 500: # Outside off stump
90
+ return "outside_off"
91
+ else:
92
+ return "outside_leg"
93
+
94
+ def determine_impact_zone(track_points):
95
+ """
96
+ Determine the impact zone: in-line or outside leg.
97
+ Based on the trajectory of the ball and its impact.
98
+ """
99
+ # Check if the ball impacts the batsman's leg (dynamic)
100
+ impact_point = track_points[-1] # Last point could be an approximation of impact
101
+ x, y = impact_point
102
+ if 200 <= x <= 400: # Assuming this range as an in-line range for example
103
+ return "in_line"
104
+ else:
105
+ return "outside_leg"
106
+
107
+ def check_stumps_impact(trajectory_points):
108
+ """
109
+ Predict if the ball would hit the stumps based on its trajectory.
110
+ """
111
+ last_point = trajectory_points[-1]
112
+ # Example logic: Check if the final projected point is in-line with the stumps
113
+ x, y = last_point
114
+ if 200 <= x <= 400 and y <= 0: # Ball hitting the stumps (example range)
115
+ return True
116
+ else:
117
+ return False
118
+
119
+
120
+ # Gradio interface setup
121
+ gui = gr.Interface(
122
+ fn=review_video,
123
+ inputs=gr.Video(label="Upload LBW Appeal Video"),
124
+ outputs=[gr.Textbox(label="Verdict"), gr.Video(label="Annotated Output")],
125
+ title="LBW DRS AI Review",
126
+ description="Proof‑of‑concept: detects ball, projects trajectory, and renders decision.",
127
+ )
128
+
129
+ if __name__ == "__main__":
130
+ gui.launch()
requirements.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ opencv-python>=4.9.0.80
2
+ numpy>=1.25.0
3
+ scikit-learn>=1.5.0
4
+ ultralytics>=8.2.0
5
+ pyyaml>=6.0.1
6
+ python-box>=7.1.1
7
+ gradio>=4.29.0
8
+ fastapi>=0.111.0
9
+ uvicorn[standard]>=0.29.0
10
+ pydantic>=2.7.1
11
+ pytest>=8.2.1
12
+ filterpy>=1.4.5
13
+ norfair>=2.2.0
14
+ tqdm>=4.66.4