实用脚本如何评估门将扑救反应速度?

wen 实用脚本 3

本文目录导读:

实用脚本如何评估门将扑救反应速度?

  1. 先明确:什么叫“反应速度”
  2. 可测量的关键指标
  3. 测量方法
  4. Python 脚本示例(基于视频逐帧 + 姿态估计)
  5. 脚本使用要点
  6. 如果想更专业

评估门将扑救反应速度,可以从实验室测量实战视频分析两个层面来做,下面按“指标定义 → 测量方法 → 脚本实现”展开,最后给一个可直接运行的 Python 脚本示例。


先明确:什么叫“反应速度”

门将扑救反应速度通常拆成三段:

阶段 含义 典型耗时
感知 看到球、判断方向 100–200 ms
决策 选择扑救方向/方式 50–150 ms
动作 蹬地、腾空、手到球 300–600 ms

总反应时间 = 感知 + 决策 + 动作,真正能通过视频估的是“从球出脚到门将开始移动”这一段,即反应启动时间


可测量的关键指标

  1. 反应启动时间:球离脚 → 门将重心开始移动
  2. 手到球时间:门将启动 → 手触及球
  3. 覆盖距离/速度:门将移动距离 ÷ 用时
  4. 方向判断正确率:扑救方向与球实际方向是否一致
  5. 有效扑救率:扑到球的比例

测量方法

方法 A:高速摄像 + 标记点(最准)

  • 240 fps 以上摄像机,侧向+正向双机
  • 球和门将髋、手、脚贴标记点
  • 逐帧标注,算时间差

方法 B:普通视频 + 逐帧分析(实用)

  • 60 fps 视频,每帧 16.7 ms
  • 人工或用姿态估计模型标出:
    • 球离脚帧
    • 门将髋部首次移动帧
    • 手触球帧
  • 时间 = 帧差 ÷ 帧率

方法 C:可穿戴 IMU

  • 门将腰部/手腕戴 IMU
  • 检测加速度突变作为启动时刻
  • 与球的雷达/高速摄像同步

Python 脚本示例(基于视频逐帧 + 姿态估计)

下面这个脚本用 mediapipe 做姿态估计,用 opencv 读视频,自动估算门将“启动反应时间”。

"""
goalkeeper_reaction.py
评估门将扑救反应速度(简化版)
依赖:
    pip install opencv-python mediapipe numpy
"""
import cv2
import mediapipe as mp
import numpy as np
# ---------- 参数 ----------
VIDEO_PATH = "gk_save.mp4"
FPS = 60                     # 视频帧率
BALL_RELEASE_FRAME = None    # 球离脚帧(可手动指定,或用球检测自动找)
HIP_MOVE_THRESHOLD = 0.02    # 髋部归一化位移阈值,判断"启动"
HAND_BALL_DIST_THRESHOLD = 0.08  # 手与球归一化距离阈值,判断"触球"
mp_pose = mp.solutions.pose
mp_hands = mp.solutions.hands
def detect_ball(frame):
    """简单用颜色/轮廓找球,实际项目建议用 YOLO,返回归一化坐标或 None。"""
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    # 白色足球示例范围,需按实际调整
    mask = cv2.inRange(hsv, (0, 0, 200), (180, 40, 255))
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not contours:
        return None
    c = max(contours, key=cv2.contourArea)
    if cv2.contourArea(c) < 50:
        return None
    M = cv2.moments(c)
    cx = M["m10"] / M["m00"]
    cy = M["m01"] / M["m00"]
    h, w = frame.shape[:2]
    return (cx / w, cy / h)
def main():
    cap = cv2.VideoCapture(VIDEO_PATH)
    pose = mp_pose.Pose(min_detection_confidence=0.5)
    hands = mp_hands.Hands(min_detection_confidence=0.5)
    hip_positions = []   # (frame_idx, x, y)
    hand_positions = []  # (frame_idx, x, y)
    ball_positions = []  # (frame_idx, x, y)
    frame_idx = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # 姿态
        res_pose = pose.process(rgb)
        if res_pose.pose_landmarks:
            lm = res_pose.pose_landmarks.landmark
            hip_x = (lm[23].x + lm[24].x) / 2
            hip_y = (lm[23].y + lm[24].y) / 2
            hip_positions.append((frame_idx, hip_x, hip_y))
        # 手
        res_hands = hands.process(rgb)
        if res_hands.multi_hand_landmarks:
            for hlm in res_hands.multi_hand_landmarks:
                # 取掌心近似
                xs = [p.x for p in hlm.landmark]
                ys = [p.y for p in hlm.landmark]
                hand_positions.append((frame_idx, np.mean(xs), np.mean(ys)))
        # 球
        ball = detect_ball(frame)
        if ball:
            ball_positions.append((frame_idx, ball[0], ball[1]))
        frame_idx += 1
    cap.release()
    # ---------- 分析 ----------
    if not hip_positions:
        print("未检测到门将姿态")
        return
    # 1. 找门将启动帧:髋部首次超过阈值位移
    start_frame = None
    x0, y0 = hip_positions[0][1], hip_positions[0][2]
    for f, x, y in hip_positions:
        if np.hypot(x - x0, y - y0) > HIP_MOVE_THRESHOLD:
            start_frame = f
            break
    # 2. 球离脚帧:这里简化为球首次出现在画面下半部(可替换为脚触球检测)
    release_frame = BALL_RELEASE_FRAME
    if release_frame is None and ball_positions:
        release_frame = ball_positions[0][0]
    # 3. 手触球帧
    touch_frame = None
    ball_dict = {f: (x, y) for f, x, y in ball_positions}
    for f, hx, hy in hand_positions:
        if f in ball_dict:
            bx, by = ball_dict[f]
            if np.hypot(hx - bx, hy - by) < HAND_BALL_DIST_THRESHOLD:
                touch_frame = f
                break
    # ---------- 输出 ----------
    print("=" * 40)
    print(f"球离脚帧:   {release_frame}")
    print(f"门将启动帧: {start_frame}")
    print(f"手触球帧:   {touch_frame}")
    if release_frame is not None and start_frame is not None:
        react_ms = (start_frame - release_frame) / FPS * 1000
        print(f"✅ 反应启动时间: {react_ms:.1f} ms")
    if start_frame is not None and touch_frame is not None:
        action_ms = (touch_frame - start_frame) / FPS * 1000
        print(f"✅ 动作执行时间: {action_ms:.1f} ms")
    if release_frame is not None and touch_frame is not None:
        total_ms = (touch_frame - release_frame) / FPS * 1000
        print(f"✅ 总扑救时间:   {total_ms:.1f} ms")
    print("=" * 40)
if __name__ == "__main__":
    main()

脚本使用要点

  1. 帧率越高越准:60 fps 每帧 16.7 ms,240 fps 每帧 4.2 ms,业余拍摄至少 60 fps。
  2. 球检测建议升级:示例用颜色阈值,实战建议用 YOLOv8 训练足球检测。
  3. 同步问题:多机位需用闪光/拍手做同步标记。
  4. 阈值需标定HIP_MOVE_THRESHOLDHAND_BALL_DIST_THRESHOLD 要按机位和画幅调整。
  5. 手动校验:自动结果最好人工逐帧回看一遍,尤其是启动帧。

如果想更专业

  • OpenPose / MMPose 替代 MediaPipe,精度更高
  • 雷达测速仪 测球速,反推理论反应时间
  • IMU + 视频同步,启动时刻精度可到 ±5 ms
  • 建立数据库:记录每次扑救的 反应时间 / 球速 / 方向 / 结果,做统计分析

如果你告诉我你手头有什么设备(手机视频?高速摄像?IMU?),我可以帮你把脚本改成对应方案,比如纯手机 60fps 视频的极简版,或者带球速反推的版本。

抱歉,评论功能暂时关闭!