Spaces:
Sleeping
Sleeping
import cv2 | |
import numpy as np | |
import gradio as gr | |
import tempfile | |
from pathlib import Path | |
from cvzone.ColorModule import ColorFinder | |
from batsman import batsman_detect | |
from ball_detect import ball_detect | |
# --------------------------------------------------- | |
# Static detection parameters (tune these if needed) | |
# --------------------------------------------------- | |
mycolorFinder = ColorFinder(False) | |
# HSV range for ball – tune for your footage | |
hsvVals = { | |
"hmin": 10, | |
"smin": 44, | |
"vmin": 192, | |
"hmax": 125, | |
"smax": 114, | |
"vmax": 255, | |
} | |
# RGB range & Canny thresholds for batsman – replace with tuned values | |
tuned_rgb_lower = np.array([112, 0, 181]) | |
tuned_rgb_upper = np.array([255, 255, 255]) | |
tuned_canny_threshold1 = 100 | |
tuned_canny_threshold2 = 200 | |
# --------------------------------------------------- | |
# Helper to classify each frame event | |
# --------------------------------------------------- | |
def ball_pitch_pad(x, x_prev, prev_x_diff, y, y_prev, prev_y_diff, batLeg): | |
"""Return 'Pad', 'Pitch' or 'Motion' based on ball & batsman coords.""" | |
if x_prev == 0 and y_prev == 0: | |
return "Motion", 0, 0 | |
if abs(x - x_prev) > 3 * abs(prev_x_diff) and abs(prev_x_diff) > 0: | |
if y < batLeg: | |
return "Pad", x - x_prev, y - y_prev | |
if y - y_prev < 0 and prev_y_diff > 0: | |
if y < batLeg: | |
return "Pad", x - x_prev, y - y_prev | |
else: | |
return "Pitch", x - x_prev, y - y_prev | |
return "Motion", x - x_prev, y - y_prev | |
# --------------------------------------------------- | |
# Main analysis routine wrapped for Gradio | |
# --------------------------------------------------- | |
def detect_lbw(video): | |
"""Run the LBW detector on an uploaded video, return annotated clip + verdict.""" | |
# Accept both dict (gradio 4.x) or str | |
video_path = video if isinstance(video, (str, Path)) else video.get("name") | |
cap = cv2.VideoCapture(str(video_path)) | |
if not cap.isOpened(): | |
raise ValueError("Unable to open uploaded video.") | |
fps = cap.get(cv2.CAP_PROP_FPS) or 25 | |
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
# Prepare temporary output file for annotated video | |
tmpfile = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") | |
out_path = tmpfile.name | |
fourcc = cv2.VideoWriter_fourcc(*"mp4v") | |
writer = cv2.VideoWriter(out_path, fourcc, fps, (width, height)) | |
# Tracking variables | |
x = y = batLeg = 0 | |
x_prev = y_prev = 0 | |
prev_x_diff = prev_y_diff = 0 | |
lbw_detected = False | |
while True: | |
x_prev, y_prev = x, y | |
success, img = cap.read() | |
if not success: | |
break | |
overlay = img.copy() | |
# 1️⃣ Ball detection | |
_, x, y = ball_detect(img, mycolorFinder, hsvVals) | |
if x and y: | |
cv2.circle(overlay, (x, y), 8, (255, 0, 0), -1) | |
# 2️⃣ Batsman detection | |
batsmanContours = batsman_detect( | |
img, | |
tuned_rgb_lower, | |
tuned_rgb_upper, | |
tuned_canny_threshold1, | |
tuned_canny_threshold2, | |
) | |
# Compute batsman's leg (lowest y among contours above the ball) | |
current_batLeg = float("inf") | |
for cnt in batsmanContours: | |
if cv2.contourArea(cnt) > 5000 and y != 0 and min(cnt[:, :, 1]) < y: | |
leg_candidate = max(cnt[:, :, 1]) | |
current_batLeg = min(current_batLeg, leg_candidate) | |
cv2.drawContours(overlay, cnt, -1, (0, 255, 0), 3) | |
batLeg = current_batLeg if current_batLeg != float("inf") else batLeg | |
# 3️⃣ Classify the motion event for this frame | |
motion_type, prev_x_diff, prev_y_diff = ball_pitch_pad( | |
x, x_prev, prev_x_diff, y, y_prev, prev_y_diff, batLeg | |
) | |
if motion_type == "Pad": | |
lbw_detected = True | |
cv2.putText( | |
overlay, | |
"PAD CONTACT", | |
(50, 80), | |
cv2.FONT_HERSHEY_SIMPLEX, | |
1.6, | |
(0, 0, 255), | |
4, | |
cv2.LINE_AA, | |
) | |
elif motion_type == "Pitch": | |
cv2.putText( | |
overlay, | |
"Bounced", | |
(50, 80), | |
cv2.FONT_HERSHEY_SIMPLEX, | |
1.6, | |
(0, 255, 255), | |
4, | |
cv2.LINE_AA, | |
) | |
writer.write(overlay) | |
cap.release() | |
writer.release() | |
verdict = "✅ Potential LBW Detected!" if lbw_detected else "❌ No LBW Detected." | |
return out_path, verdict | |
# --------------------------------------------------- | |
# Gradio interface | |
# --------------------------------------------------- | |
demo = gr.Interface( | |
fn=detect_lbw, | |
inputs=gr.Video(label="Upload cricket clip (side-on view)"), | |
outputs=[ | |
gr.Video(label="Annotated Review"), | |
gr.Textbox(label="Decision"), | |
], | |
title="Automated LBW Detector", | |
description=( | |
"Upload a short video of the delivery. The system analyses the ball & batsman " | |
"interaction frame-by-frame, overlays detections, and flags potential LBW instances." | |
), | |
) | |
if __name__ == "__main__": | |
demo.launch() |