Spaces:
Runtime error
Runtime error
File size: 5,700 Bytes
c713aa5 de31001 8fcf0b8 936b6dc 8fcf0b8 936b6dc 8fcf0b8 98786f5 de31001 34d5cfd de31001 34d5cfd de31001 34d5cfd 8fcf0b8 34d5cfd 8fcf0b8 de31001 936b6dc 98786f5 8fcf0b8 98786f5 8fcf0b8 de31001 8fcf0b8 de31001 936b6dc de31001 8fcf0b8 de31001 98786f5 de31001 8fcf0b8 936b6dc 8fcf0b8 de31001 936b6dc 8fcf0b8 de31001 8fcf0b8 de31001 8fcf0b8 98786f5 8fcf0b8 98786f5 34d5cfd 868dc0d 98786f5 |
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 |
import cv2
import os
import math
import numpy as np
import tempfile
import matplotlib.pyplot as plt
from ultralytics import YOLO
from pydub import AudioSegment
import ffmpeg
from scipy.optimize import curve_fit
model = YOLO("best.pt")
FPS = 30
def estimate_speed(p1, p2, fps):
dist = math.hypot(p2[0] - p1[0], p2[1] - p1[1])
meters_per_pixel = 0.03
mps = dist * meters_per_pixel * fps
return mps * 3.6
def add_voice_to_video(video_path, verdict):
audio_file = "out.mp3" if verdict == "OUT" else "not_out.mp3"
audio_temp = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
AudioSegment.from_file(audio_file).export(audio_temp, format="mp3")
final_output = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
(
ffmpeg
.input(video_path)
.output(final_output, audio=audio_temp, vcodec='copy', acodec='aac', strict='experimental')
.run(overwrite_output=True)
)
return final_output
def extend_trajectory_with_rotation(points, bounce_idx, final_x=20.12):
x_vals = [pt[0] for pt in points]
y_vals = [pt[1] for pt in points]
if bounce_idx is None or bounce_idx >= len(points) - 2:
return x_vals, y_vals
x_pre = x_vals[:bounce_idx]
y_pre = y_vals[:bounce_idx]
def poly2(x, a, b, c): return a*x**2 + b*x + c
try:
popt, _ = curve_fit(poly2, x_pre, y_pre)
x_post = np.linspace(x_vals[bounce_idx], final_x, 50)
curve_shift = np.linspace(0, 0.05, len(x_post))
y_post = poly2(x_post, *popt) + curve_shift
return x_vals + list(x_post), y_vals + list(y_post)
except:
return x_vals, y_vals
def draw_top_down_trajectory(points, bounce_frame_idx, output_path):
if len(points) < 4:
return None
x_vals = [pt[0] for pt in points]
y_vals = [pt[1] for pt in points]
x_ext, y_ext = extend_trajectory_with_rotation(points, bounce_frame_idx)
plt.figure(figsize=(10, 3))
plt.plot(x_ext, y_ext, 'r-', label='Predicted Trajectory')
plt.scatter(x_vals, y_vals, c='blue', s=10, label='Detected Points')
plt.axvline(x=17.68, color='gray', linestyle='--', label='Stumps (17.68m)')
plt.title("Top-Down Predicted Ball Path")
plt.xlabel("Pitch Length (m)")
plt.ylabel("Lateral Movement (m)")
plt.grid(True)
plt.legend()
image_path = output_path.replace(".mp4", "_trajectory.png")
plt.savefig(image_path)
plt.close()
return image_path
def process_video(video_path):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise Exception("Video open failed")
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS) or FPS
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
temp_out = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False).name
out = cv2.VideoWriter(temp_out, fourcc, fps, (width, height))
max_frames = int(fps * 4)
frame_count = 0
src = np.array([
[width * 0.3, height * 0.4],
[width * 0.7, height * 0.4],
[width * 0.7, height * 0.9],
[width * 0.3, height * 0.9],
], dtype=np.float32)
dst = np.array([
[0, 0],
[20.12, 0],
[20.12, 3.05],
[0, 3.05]
], dtype=np.float32)
H, _ = cv2.findHomography(src, dst)
def project_point(px, py):
pt = np.array([[[px, py]]], dtype=np.float32)
return cv2.perspectiveTransform(pt, H)[0][0]
trajectory, real_trajectory = [], []
bounce_detected = False
bounce_frame_idx = None
prev_center = None
verdict = "NOT OUT"
while frame_count < max_frames:
ret, frame = cap.read()
if not ret:
break
results = model(frame)
for box in results[0].boxes:
if int(box.cls[0]) == 0:
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy().astype(int)
cx, cy = int((x1 + x2) / 2), int((y1 + y2) / 2)
trajectory.append((cx, cy))
x_m, y_m = project_point(cx, cy)
real_trajectory.append((x_m, y_m))
cv2.circle(frame, (cx, cy), 8, (0, 0, 255), -1)
if prev_center and not bounce_detected and cy - prev_center[1] > 15:
bounce_detected = True
bounce_frame_idx = frame_count
cv2.putText(frame, "Bounce!", (cx, cy - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,255), 2)
prev_center = (cx, cy)
break
for i in range(1, len(trajectory)):
x1, y1 = trajectory[i - 1]
x2, y2 = trajectory[i]
cv2.line(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
if len(real_trajectory) >= 2:
x1, y1 = real_trajectory[-2]
x2, y2 = real_trajectory[-1]
speed = estimate_speed((x1, y1), (x2, y2), fps)
cv2.putText(frame, f"Speed: {int(speed)} km/h", (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2)
if bounce_detected and real_trajectory:
x_latest, y_latest = real_trajectory[-1]
if 1.0 < y_latest < 2.1:
verdict = "OUT"
cv2.putText(frame, f"Decision: {verdict}", (30, height - 30), cv2.FONT_HERSHEY_SIMPLEX, 1.2,
(0,255,0) if verdict == "NOT OUT" else (0,0,255), 3)
out.write(frame)
frame_count += 1
cap.release()
out.release()
topdown_image = draw_top_down_trajectory(real_trajectory, bounce_frame_idx, temp_out)
try:
final_video = add_voice_to_video(temp_out, verdict)
except:
final_video = temp_out
return final_video, topdown_image
|