# app.py import streamlit as st import cv2 import numpy as np import pandas as pd from ultralytics import YOLO import supervision as sv from scipy.spatial import distance as dist import tempfile import matplotlib.pyplot as plt # Sidebar with logo and team information with st.sidebar: st.image("https://cs.christuniversity.in/softex/resources/img/christ_university_Black.png", width=200) st.markdown("

Christ University Kengeri Campus

", unsafe_allow_html=True) st.markdown("
Sports Department
", unsafe_allow_html=True) st.markdown("### Team Members") st.markdown(""" - Harsh Vardhan Lal 2262069 6BTCSAIML B - Harsheet Sandeep Thakur 2262070 6BTCSAIML B - Nandalal C B 2262115 6BTCSAIML B - Athul Nambiar 2262041 6BTCSAIML B """) # Initialize components model = YOLO('yolov8s.pt') tracker = sv.ByteTrack() # Streamlit UI st.title("⚽️ Player Tracking System") uploaded_video = st.file_uploader("Upload match video", type=["mp4", "mov"]) calibration_dist = st.number_input("Field width in meters (for speed calibration):", value=68.0) # Initialize session state if 'player_data' not in st.session_state: st.session_state.player_data = {} # Create a placeholder for the live tracking table live_table_container = st.container() live_table = live_table_container.empty() if uploaded_video: tfile = tempfile.NamedTemporaryFile(delete=False) tfile.write(uploaded_video.read()) cap = cv2.VideoCapture(tfile.name) fps = cap.get(cv2.CAP_PROP_FPS) frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) pixels_per_meter = frame_width / calibration_dist if calibration_dist > 0 else 1.0 st_frame = st.empty() frame_count = 0 # Clear previous player data st.session_state.player_data = {} player_count = 0 # Track the number of distinct players while cap.isOpened(): ret, frame = cap.read() if not ret: break # Detection + tracking with person class filter (class 0) results = model(frame)[0] detections = sv.Detections.from_ultralytics(results) # Filter for person class only (class 0 in COCO dataset) person_mask = np.array([class_id == 0 for class_id in detections.class_id]) detections = detections[person_mask] # Apply tracking - this will assign consistent IDs detections = tracker.update_with_detections(detections) # Extract bounding boxes, track IDs, etc. boxes = detections.xyxy # Get bounding boxes [x1, y1, x2, y2] track_ids = detections.tracker_id # Get track IDs if boxes is not None and len(boxes) > 0: for i, (box, track_id) in enumerate(zip(boxes, track_ids)): # Skip detections without a track_id if track_id is None: continue # Use the track_id directly from ByteTrack player_id = int(track_id) # Calculate center point of bounding box x1, y1, x2, y2 = box centroid = (int((x1 + x2) / 2), int((y1 + y2) / 2)) # Initialize speed to 0 for all cases speed = 0.0 # Initialize new player if not seen before if player_id not in st.session_state.player_data: st.session_state.player_data[player_id] = { 'positions': [centroid], 'timestamps': [frame_count / fps], 'distance': 0.0, 'speeds': [0.0], # Initialize with zero speed 'last_seen': frame_count } player_count += 1 # Increment player counter else: # Calculate movement metrics prev_pos = st.session_state.player_data[player_id]['positions'][-1] time_diff = (frame_count / fps) - st.session_state.player_data[player_id]['timestamps'][-1] # Calculate distance pixel_dist = dist.euclidean(prev_pos, centroid) # Apply motion smoothing to ignore unrealistic movements if pixel_dist < frame_width * 0.1: # Max 10% of screen width per frame speed_px = pixel_dist / time_diff if time_diff > 0 else 0.0 # Convert to meters per second speed = speed_px / pixels_per_meter meter_dist = pixel_dist / pixels_per_meter # Update player data st.session_state.player_data[player_id]['positions'].append(centroid) st.session_state.player_data[player_id]['timestamps'].append(frame_count / fps) st.session_state.player_data[player_id]['distance'] += meter_dist st.session_state.player_data[player_id]['speeds'].append(speed) st.session_state.player_data[player_id]['last_seen'] = frame_count else: # Just update position without adding to distance/speed st.session_state.player_data[player_id]['positions'].append(centroid) st.session_state.player_data[player_id]['timestamps'].append(frame_count / fps) st.session_state.player_data[player_id]['last_seen'] = frame_count # Maintain previous speed speed = st.session_state.player_data[player_id]['speeds'][-1] if st.session_state.player_data[player_id]['speeds'] else 0 # Draw player info on frame label = f"ID:{player_id} Speed:{speed:.1f}m/s" cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, label, (int(x1), int(y1-10)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) # Update live table if st.session_state.player_data: live_data = [] for player_id, data in st.session_state.player_data.items(): # Only show recently seen players if frame_count - data['last_seen'] < 30: current_speed = data['speeds'][-1] if data['speeds'] else 0 live_data.append({ "Player": f"Player {player_id}", "Current Speed (m/s)": round(current_speed, 2), "Distance (m)": round(data['distance'], 2), "Time (s)": round(data['timestamps'][-1], 2) if data['timestamps'] else 0 }) live_df = pd.DataFrame(live_data) if not live_df.empty: live_table.dataframe(live_df, use_container_width=True, hide_index=True) st_frame.image(frame, channels="BGR") frame_count += 1 cap.release() # Display final analytics st.subheader(f"Player Statistics ({player_count} players detected)") # Create player stats table stats_data = [] for player_id, data in st.session_state.player_data.items(): if len(data['speeds']) > 0: avg_speed = np.mean(data['speeds']) max_speed = np.max(data['speeds']) else: avg_speed = 0 max_speed = 0 duration = data['timestamps'][-1] - data['timestamps'][0] if len(data['timestamps']) > 1 else 0 # Only include players with significant tracking data if len(data['positions']) > 10: stats_data.append({ "Player": f"Player {player_id}", "Total Distance (m)": round(data['distance'], 2), "Avg Speed (m/s)": round(avg_speed, 2), "Max Speed (m/s)": round(max_speed, 2), "Duration (s)": round(duration, 2) }) stats_df = pd.DataFrame(stats_data) st.dataframe(stats_df, use_container_width=True, hide_index=True) # Create speed comparison chart if st.session_state.player_data: st.subheader("Player Speed Comparison") fig, ax = plt.subplots(figsize=(10, 6)) for player_id, data in st.session_state.player_data.items(): # Only plot players with significant data if len(data['speeds']) > 10: ax.plot(data['timestamps'], data['speeds'], label=f"Player {player_id}") ax.set_xlabel('Time (seconds)') ax.set_ylabel('Speed (m/s)') ax.set_title('Player Speed Over Time') ax.legend() ax.grid(True) st.pyplot(fig)