python案例统计克鲁伊夫转身做了几次?

wen python案例 3

统计克鲁伊夫转身次数 - Python案例

背景说明

克鲁伊夫转身(Cruyff Turn) 是足球中经典的过人动作:球员假装要传球或射门,然后用脚内侧把球从支撑腿后方扣过,摆脱防守球员,这个动作由荷兰传奇球星约翰·克鲁伊夫命名。

python案例统计克鲁伊夫转身做了几次?

要用Python统计某场比赛/视频中克鲁伊夫转身的次数,通常需要结合计算机视觉 + 姿态识别手动标注,下面给出一个完整的实现案例。


基于姿态关键点检测(推荐)

思路:用 MediaPipe 提取球员下肢关键点,检测"脚从支撑腿后方穿过"的轨迹特征。

import cv2
import mediapipe as mp
import numpy as np
from collections import deque
mp_pose = mp.solutions.pose
# 关键点索引
LEFT_HIP, RIGHT_HIP = 23, 24
LEFT_KNEE, RIGHT_KNEE = 25, 26
LEFT_ANKLE, RIGHT_ANKLE = 27, 28
def is_cruyff_turn(landmarks, history, side="left"):
    """
    判断是否发生克鲁伊夫转身
    side: 用哪只脚做转身(左脚则球从左脚后方绕过)
    """
    if side == "left":
        hip, knee, ankle = LEFT_HIP, LEFT_KNEE, LEFT_ANKLE
        opp_hip = RIGHT_HIP
    else:
        hip, knee, ankle = RIGHT_HIP, RIGHT_KNEE, RIGHT_ANKLE
        opp_hip = LEFT_HIP
    # 需要历史帧判断轨迹
    if len(history) < 10:
        return False
    # 当前踝关节相对于髋关节的横向位置
    cur_ankle_x = landmarks[ankle].x
    cur_hip_x = landmarks[hip].x
    opp_hip_x = landmarks[opp_hip].x
    # → 关键特征1:踝关节从自己髋部一侧,绕到对侧髋部之后
    crossed = (
        (side == "left" and cur_ankle_x > opp_hip_x) or
        (side == "right" and cur_ankle_x < opp_hip_x)
    )
    # → 关键特征2:膝关节抬起(模拟踢球动作)
    knee_raised = landmarks[knee].y < landmarks[hip].y
    # → 关键特征3:最近几帧踝关节有明显横向位移
    past_ankle_x = history[0]
    lateral_move = abs(cur_ankle_x - past_ankle_x) > 0.15
    return crossed and knee_raised and lateral_move
def count_cruyff_turns(video_path):
    cap = cv2.VideoCapture(video_path)
    history = deque(maxlen=10)
    count = 0
    cooldown = 0  # 防止同一动作重复计数
    with mp_pose.Pose(min_detection_confidence=0.5,
                      min_tracking_confidence=0.5) as pose:
        while cap.isOpened():
            ret, frame = cap.read()
            if not ret:
                break
            rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            result = pose.process(rgb)
            if result.pose_landmarks:
                lm = result.pose_landmarks.landmark
                history.append(lm[LEFT_ANKLE].x)
                if cooldown == 0:
                    # 检测左右脚任一方向
                    if is_cruyff_turn(lm, history, "left") or \
                       is_cruyff_turn(lm, history, "right"):
                        count += 1
                        cooldown = 30  # 30帧冷却
                        print(f"⚽ 第 {count} 次克鲁伊夫转身 @ frame={int(cap.get(cv2.CAP_PROP_POS_FRAMES))}")
            if cooldown > 0:
                cooldown -= 1
    cap.release()
    return count
if __name__ == "__main__":
    total = count_cruyff_turns("match_clip.mp4")
    print(f"\n✅ 克鲁伊夫转身总计:{total} 次")

基于事件标注表(更实用)

实际工程中,往往手动/半自动打标签后统计,更可靠:

import pandas as pd
# 假设人工或AI标注了事件流
events = [
    {"frame": 120, "player": "Messi",  "action": "Cruyff Turn"},
    {"frame": 350, "player": "Messi",  "action": "Pass"},
    {"frame": 512, "player": "Messi",  "action": "Cruyff Turn"},
    {"frame": 780, "player": "Neymar", "action": "Cruyff Turn"},
    {"frame": 950, "player": "Messi",  "action": "Shot"},
]
df = pd.DataFrame(events)
# 1. 全场总计
total = (df["action"] == "Cruyff Turn").sum()
print(f"全场克鲁伊夫转身次数:{total}")
# 2. 按球员统计
per_player = df[df["action"] == "Cruyff Turn"] \
               .groupby("player").size() \
               .sort_values(ascending=False)
print("\n各球员克鲁伊夫转身次数:")
print(per_player)

输出示例:

全场克鲁伊夫转身次数:3
各球员克鲁伊夫转身次数:
player
Messi     2
Neymar    1
dtype: int64

比赛时间段统计

想统计"每15分钟"或"上下半场"的分布:

# 假设视频 30fps,比赛 90 分钟
fps = 30
df["minute"] = df["frame"] / fps / 60
# 按15分钟分段
bins = [0, 15, 30, 45, 60, 75, 90]
labels = ["0-15", "15-30", "30-45", "45-60", "60-75", "75-90"]
df["period"] = pd.cut(df["minute"], bins=bins, labels=labels)
cruyff = df[df["action"] == "Cruyff Turn"]
print(cruyff.groupby("period").size().reindex(labels, fill_value=0))

⚠️ 注意事项

问题 说明
单目视觉误判 遮挡、镜头切换会导致姿态丢失
左右脚区分 真实克鲁伊夫转身常用惯用脚,可加约束
冷却时间 必须加,否则一次动作会被连续计数多帧
准确率 纯姿态方案准确率约 60-75%,建议结合球轨迹跟踪(YOLO + 足球检测)
最佳实践 工业级方案:球员检测(YOLO) + 球检测 + 姿态(MediaPipe/HRNet) + 事件分类器(LSTM)

回答原问题

"克鲁伊夫转身做了几次?"

如果只是针对某段具体视频/某场比赛,请把视频给我,或告诉我:

  1. 视频文件路径
  2. 关注哪位球员
  3. 是否需要区分左右脚

我可以基于上面代码帮你实际跑一遍并返回准确次数。📊

需要我把方案一改造成可运行的最小demo(用公开测试视频)吗?

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