统计冲刺跑次数脚本
你想统计两段视频/两个运动员的冲刺跑次数对比,下面给你一个可运行的 Python 脚本,用 OpenCV 分析视频中人物的移动速度来判定冲刺。

基于视频运动速度(通用)
import cv2
import numpy as np
from collections import deque
def count_sprints(video_path, speed_threshold=15, min_duration=5,
roi=None, scale=0.5, show=False):
"""
统计视频中冲刺跑次数。
参数:
video_path : 视频文件路径
speed_threshold: 触发冲刺的像素位移阈值(每帧)
min_duration : 连续超过阈值的最小帧数才算一次冲刺
roi : (x, y, w, h) 感兴趣区域,None 表示全图
scale : 缩放比例,加速处理
show : 是否显示检测过程
返回:
冲刺次数
"""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise IOError(f"无法打开视频: {video_path}")
# 背景减除器
bg = cv2.createBackgroundSubtractorMOG2(history=200, varThreshold=25, detectShadows=False)
prev_center = None
over_count = 0 # 连续超阈值帧数
sprint_count = 0
speeds = []
while True:
ret, frame = cap.read()
if not ret:
break
if scale != 1.0:
frame = cv2.resize(frame, None, fx=scale, fy=scale)
if roi:
x, y, w, h = [int(v * scale) for v in roi]
frame = frame[y:y+h, x:x+w]
fg = bg.apply(frame)
fg = cv2.morphologyEx(fg, cv2.MORPH_OPEN, np.ones((5,5), np.uint8))
fg = cv2.dilate(fg, np.ones((7,7), np.uint8), iterations=2)
contours, _ = cv2.findContours(fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
prev_center = None
continue
# 取最大轮廓作为人物
c = max(contours, key=cv2.contourArea)
if cv2.contourArea(c) < 300:
continue
M = cv2.moments(c)
if M["m00"] == 0:
continue
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
if prev_center is not None:
speed = np.hypot(cx - prev_center[0], cy - prev_center[1])
speeds.append(speed)
if speed > speed_threshold:
over_count += 1
if over_count == min_duration: # 达标瞬间记 1 次
sprint_count += 1
else:
over_count = 0
prev_center = (cx, cy)
if show:
cv2.circle(frame, (cx, cy), 8, (0,0,255), -1)
cv2.putText(frame, f"Sprints: {sprint_count}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
cv2.imshow("Sprint Detection", frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
if show:
cv2.destroyAllWindows()
avg_speed = float(np.mean(speeds)) if speeds else 0
return sprint_count, avg_speed
if __name__ == "__main__":
# 对比两个视频
videos = {
"运动员A": "video_A.mp4",
"运动员B": "video_B.mp4",
}
results = {}
for name, path in videos.items():
n, avg = count_sprints(path, speed_threshold=15, min_duration=5, scale=0.5)
results[name] = n
print(f"{name}: 冲刺 {n} 次 (平均速度 {avg:.1f} px/帧)")
winner = max(results, key=results.get)
print(f"\n🏆 冲刺次数更多:{winner} ({results[winner]} 次)")
使用:
pip install opencv-python numpy python sprint_count.py
带坐标数据(如果你有 GPS / 追踪数据)
如果已有逐帧坐标,更精准:
import pandas as pd
import numpy as np
def count_sprints_from_tracking(df, speed_threshold=7.0, min_duration_s=1.0, fps=30):
"""
df 需要列: x, y (米) —— 可选: t
冲刺定义: 速度 > threshold 且持续 >= min_duration_s 秒
"""
dx = df["x"].diff()
dy = df["y"].diff()
speed = np.hypot(dx, dy) * fps # m/s
is_sprint = speed > speed_threshold
sprint_count = 0
streak = 0
min_frames = int(min_duration_s * fps)
for s in is_sprint:
if s:
streak += 1
else:
if streak >= min_frames:
sprint_count += 1
streak = 0
if streak >= min_frames:
sprint_count += 1
return sprint_count, speed.mean(), speed.max()
关键参数调优建议
| 参数 | 说明 | 建议 |
|---|---|---|
speed_threshold |
判定为冲刺的每帧位移 | 先跑一次打印 speeds 分布,取 85~90 分位数 |
min_duration |
持续帧数(防误报) | 30fps 视频建议 5~15 帧(0.2~0.5s) |
scale |
缩小处理加速 | 5 一般够用 |
roi |
只关注跑道区域 | 排除观众/教练干扰 |
更专业的替代方案
- YOLOv8 + ByteTrack 做人员跟踪,再对
track_id的轨迹算速度(多人场景必备) - Sprint 判定运动学惯例:速度 > 7 m/s(≈25 km/h)且持续 > 1 s 视为一次冲刺(可参考 FIFA / Catapult 标准)
如果需要多目标(同时追踪多名运动员) 或 接入 YOLO 追踪 的版本,告诉我你的视频场景(单/多人、是否固定机位),我可以直接给你完整代码。