基于Mediapipe的跳绳计数识别:从姿态估计到智能计数

本文介绍了一个基于Mediapipe的人体姿态估计应用,通过追踪关键点来实现跳绳计数。通过记录特定关节的上下运动变化,来判断并计算跳绳次数。提供了代码资源和运行依赖说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 引言

跳绳是一项高效的有氧运动,但手动计数可能不准确。本文利用Google开源的Mediapipe姿态识别功能,实现精准的跳绳计数,简化了传统的计数方式。

示例

实现原理

(1) 人体姿态估计:
Mediapipe提供33个人体关键点坐标,通过识别人体的姿态变化,追踪跳跃动作。
在这里插入图片描述

(2) 人体中心变化:
记录人体下半身关键点(23, 24)和上半身关键点(11, 12)的位置变化,判断跳绳过程中人体中心的最高和最低点。
在这里插入图片描述

功能
  • 实时检测人体姿态,并提取髋关节和肩膀的位置。
  • 计算并可视化中心Y轴,监测身体上下波动。
  • 基于波动幅度计数跳绳次数。
  • 实时绘制跳绳计数和中心点位置。
技术栈
  • Python
  • OpenCV
  • MediaPipe
  • Matplotlib
  • NumPy
环境准备

确保你已经安装了以下库:

pip install opencv-python mediapipe matplotlib numpy
代码运行
  1. 将待处理视频文件命名为 demo.mp4,并放在代码文件夹内。

  2. 运行代码:

    python rope_skipping_counter.py
    
  3. 视频处理完成后,输出结果会保存为 demo_output.mp4

参数设置

可以根据需要调整以下参数:

  • buffer_time: 缓冲区时间,默认为 50。
  • dy_ratio: 移动幅度阈值,默认为 0.3。
  • up_ratio: 上升阈值,默认为 0.55。
  • down_ratio: 下降阈值,默认为 0.35。
  • flag_lowflag_high: 控制翻转标志的阈值。
代码实现
import cv2
import matplotlib.pyplot as plt
import mediapipe as mp
import numpy as np


class BufferList:
    def __init__(self, buffer_time, default_value=0):
        self.buffer = [default_value] * buffer_time

    def push(self, value):
        self.buffer.pop(0)
        self.buffer.append(value)

    def max(self):
        return max(self.buffer)

    def min(self):
        return min(filter(lambda x: x is not None, self.buffer), default=0)

    def smooth_update(self, old_value, new_value, alpha=0.5):
        return alpha * new_value + (1 - alpha) * old_value


def extract_landmarks(results, landmarks_indices, image_width, image_height):
    """提取指定关节的坐标"""
    return [
        (lm.x * image_width, lm.y * image_height)
        for i, lm in enumerate(results.pose_landmarks.landmark)
        if i in landmarks_indices
    ]


def calculate_center_y(hip_points, shoulder_points):
    """计算中心Y轴及肩-臀垂直距离"""
    cy_hip = int(np.mean([point[1] for point in hip_points]))
    cy_shoulder = int(np.mean([point[1] for point in shoulder_points]))
    return cy_hip, cy_hip - cy_shoulder


def update_counters(cy, cy_shoulder_hip, cy_max, cy_min, flip_flag, thresholds):
    """更新波动计数逻辑"""
    dy = cy_max - cy_min
    if dy > thresholds["dy_ratio"] * cy_shoulder_hip:
        if (
            cy > cy_max - thresholds["up_ratio"] * dy
            and flip_flag == thresholds["flag_low"]
        ):
            flip_flag = thresholds["flag_high"]
        elif (
            cy < cy_min + thresholds["down_ratio"] * dy
            and flip_flag == thresholds["flag_high"]
        ):
            flip_flag = thresholds["flag_low"]
    return flip_flag


def draw_visualizations(image, cx, cy, count, image_width, image_height):
    """绘制中心点、计数等信息"""
    cv2.circle(image, (cx, cy), 5, (0, 0, 255), -1)
    cv2.putText(
        image,
        "centroid",
        (cx - 25, cy - 25),
        cv2.FONT_HERSHEY_SIMPLEX,
        0.5,
        (0, 0, 255),
        1,
    )
    cv2.putText(
        image,
        f"count = {count}",
        (int(image_width * 0.5), int(image_height * 0.4)),
        cv2.FONT_HERSHEY_SIMPLEX,
        0.5,
        (0, 255, 0),
        1,
    )


def plot_buffers(buffers, ax):
    """实时绘制缓冲区数据"""
    ax.clear()
    for label, buf in buffers.items():
        ax.plot(buf.buffer, label=label)
    ax.legend(loc="upper right")
    plt.pause(0.1)


# 主代码
file_name = "input.mp4"

mp_pose = mp.solutions.pose
hip_landmarks = [23, 24]
shoulder_landmarks = [11, 12]

# 阈值设置
thresholds = {
    "buffer_time": 50,  # 缓冲区时间
    "dy_ratio": 0.3,  # 移动幅度阈值
    "up_ratio": 0.55,  # 上升阈值
    "down_ratio": 0.35,  # 下降阈值
    "flag_low": 150,  # 翻转标志低点
    "flag_high": 250,  # 翻转标志高点
}

buffers = {
    "center_y": BufferList(thresholds["buffer_time"]),
    "center_y_up": BufferList(thresholds["buffer_time"]),
    "center_y_down": BufferList(thresholds["buffer_time"]),
    "center_y_flip": BufferList(thresholds["buffer_time"]),
    "center_y_pref_flip": BufferList(thresholds["buffer_time"]),
}

cy_max, cy_min = 100, 100
flip_flag = thresholds["flag_high"]
count = 0

cap = cv2.VideoCapture(file_name)
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
fps = cap.get(cv2.CAP_PROP_FPS)
out = cv2.VideoWriter(
    file_name.replace(".mp4", "_output.mp4"),
    fourcc,
    fps,
    (int(cap.get(3)), int(cap.get(4))),
)

plt.ion()
fig, ax = plt.subplots()

with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) as pose:
    while cap.isOpened():
        success, image = cap.read()
        if not success:
            break

        image_height, image_width, _ = image.shape
        image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        results = pose.process(image_rgb)

        if results.pose_landmarks:
            hip_points = extract_landmarks(
                results, hip_landmarks, image_width, image_height
            )
            shoulder_points = extract_landmarks(
                results, shoulder_landmarks, image_width, image_height
            )
            cx = int(np.mean([point[0] for point in hip_points]))
            cy, cy_shoulder_hip = calculate_center_y(hip_points, shoulder_points)
        else:
            cx, cy, cy_shoulder_hip = 0, 0, 0

        buffers["center_y"].push(cy)
        cy_max = buffers["center_y"].smooth_update(cy_max, buffers["center_y"].max())
        buffers["center_y_up"].push(cy_max)
        cy_min = buffers["center_y"].smooth_update(cy_min, buffers["center_y"].min())
        buffers["center_y_down"].push(cy_min)

        prev_flip_flag = flip_flag
        flip_flag = update_counters(
            cy, cy_shoulder_hip, cy_max, cy_min, flip_flag, thresholds
        )
        buffers["center_y_flip"].push(flip_flag)
        buffers["center_y_pref_flip"].push(prev_flip_flag)

        if prev_flip_flag < flip_flag:
            count += 1

        draw_visualizations(image, cx, cy, count, image_width, image_height)
        plot_buffers(buffers, ax)

        cv2.imshow("MediaPipe Pose", image)
        out.write(image)

        if cv2.waitKey(5) & 0xFF == 27:
            break

cap.release()
out.release()
cv2.destroyAllWindows()

4. 致谢
5. github

https://github.com/chenwr727/RopeSkippingCounter.git

<think>好的,我现在要帮用户解决用MediaPipe实现跳绳动作检测与计数的问题。首先,我需要理解用户的需求。他们已经有了参考引用,提到数据采集、标注,以及使用MediaPipe进行运动识别计数。但MediaPipe官方可能没有现成的解决方案,所以需要自己构建流程。 首先,我应该回忆一下MediaPipe的功能。MediaPipe提供了很多预训练模型,比如姿势估计、手部检测、面部特征等。跳绳动作主要涉及全身动作,尤其是腿部、手臂和身体的协调运动。因此,使用姿势估计模型可能是关键。 接下来,步骤分解。用户需要检测跳绳动作并进行计数,可能需要检测跳跃的周期性运动。首先,使用MediaPipe的姿势检测模块来获取人体的关键点坐标,比如臀部、膝盖、脚踝的位置变化。然后,分析这些关键点的运动轨迹,判断是否完成了一次跳跃。 具体来说,可能需要以下步骤: 1. 使用MediaPipe Pose模块提取关键点。 2. 跟踪关键点的垂直位置变化,特别是臀部或脚踝的Y坐标。 3. 通过设定阈值或检测峰值来确定跳跃的起始和结束。 4. 统计跳跃次数,并避免重复计数。 然后,代码实现部分需要导入必要的库,初始化姿势检测模块。在视频流中处理每一帧,获取姿势关键点。然后分析这些点的位置变化。例如,当脚踝的Y坐标低于某个阈值时,认为是一次跳跃的开始,当回到原位时计数增加。 但是需要注意的问题包括:不同人的跳跃幅度不同,摄像头角度可能影响坐标值,光照条件等。可能需要动态调整阈值,或者使用相对位置变化而非绝对坐标。例如,计算脚踝相对于臀部的位移,这样可以减少摄像头角度的影响。 另外,计数逻辑需要处理连续的跳跃动作,避免同一跳跃多次触发计数。可能需要设置一个状态机,比如只有在脚踝上升超过阈值后才允许下一次计数。或者检测波峰波谷的变化周期,计算跳跃次数。 用户提供的引用中提到需要手动标注起始和结束点,可能可以结合MediaPipe的输出自动标注。例如,当检测到一次跳跃时,记录时间点作为起始和结束。 最后,可能需要优化性能,确保实时处理,尤其是在视频流中。MediaPipe本身效率较高,但需要合理处理每一帧的数据,避免延迟。 总结步骤:初始化MediaPipe Pose,处理视频帧,提取关键点,分析垂直运动,设定计数逻辑,测试并调整参数。可能还需要处理误检,比如加入时间窗口或滤波算法平滑数据。</think>### MediaPipe跳绳检测与计数实现步骤 #### 一、环境搭建与数据准备 1. 安装MediaPipe库:`pip install mediapipe` 2. 采集包含跳绳动作的视频数据(正面全身视角效果最佳) 3. 使用MediaPipe Pose模型获取33个人体关键点坐标[^2] #### 二、核心检测逻辑 ```python import mediapipe as mp import cv2 mp_pose = mp.solutions.pose pose = mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.5) # 初始化状态参数 jump_count = 0 y_positions = [] is_airborne = False ``` #### 三、关键点分析算法 1. 选择检测点:推荐使用脚踝关键点(第28、29号点) $$ \text{脚踝垂直位移量} = y_{t} - y_{t-1} $$ 2. 跳跃判定条件: - 当脚踝点持续上升超过阈值(建议0.05-0.1倍画面高度) - 随后出现快速下降阶段 ```python def detect_jump(y_current): global jump_count, y_positions, is_airborne # 保持最近10帧的坐标数据 y_positions.append(y_current) if len(y_positions) > 10: y_positions.pop(0) # 计算移动平均 avg = sum(y_positions)/len(y_positions) # 跳跃检测逻辑 if not is_airborne and (y_current < avg - 0.07): # 下蹲阈值 is_airborne = True elif is_airborne and (y_current > avg + 0.05): # 腾空阈值 jump_count += 1 is_airborne = False return jump_count ``` #### 四、完整处理流程 ```python cap = cv2.VideoCapture('jump_rope.mp4') while cap.isOpened(): success, image = cap.read() if not success: break # 姿势检测 results = pose.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) if results.pose_landmarks: # 获取左脚踝坐标(landmark 28) ankle_y = results.pose_landmarks.landmark[28].y current_count = detect_jump(ankle_y) # 在画面显示计数 cv2.putText(image, f"Count: {current_count}", (10,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2) cv2.imshow('Jump Rope Counter', image) if cv2.waitKey(5) & 0xFF == 27: break cap.release() ``` #### 五、优化建议 1. 增加多关键点验证(如同时检测手腕和脚踝) 2. 引入时间窗口过滤误触发 3. 添加姿态完整性检查(如身体直立度检测) 4. 使用卡尔曼滤波器平滑运动轨迹[^1]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值