当前位置: 首页 > article >正文

【mediapipe】实现卷腹运动识别(视频或摄像头)并计数

介绍

本人机器学习小白,通过语言大模型进行搜索,只用两小时不到就完成了该功能的初步效果,非常惊讶!比以往百度搜索的效率更高,结果也更准确。

思路

1.先通过mediapipe识别出人体关键节点
2.找到运动中会变化的节点,例如:我想实现卷腹计数,变化很大的节点就是膝盖
3.找到开始与结束时,关键节点的值,用来判断是否完成了一次动作

代码

from time import sleep

import cv2
import mediapipe as mp

# 识别摄像头
def readCamara():
    # 初始化MediaPipe姿态检测对象
    mp_pose = mp.solutions.pose
    pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.5, min_tracking_confidence=0.5)

    # 打开摄像头获取视频流
    cap = cv2.VideoCapture(1)

    # 获取视频的帧率、宽度、高度等信息,用于设置输出视频参数
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # 设置输出视频的编码格式(这里使用XVID编码,常用的还有MJPG等,根据系统支持情况选择)
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    # 创建VideoWriter对象,用于保存输出视频,指定输出文件名、编码格式、帧率、视频尺寸
    out = cv2.VideoWriter('output_video.avi', fourcc, fps, (width, height))


    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        # 将图像转换为RGB格式(MediaPipe要求)
        image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # 进行人体姿态检测
        results = pose.process(image_rgb)

        if results.pose_landmarks:
            # 遍历所有的人体姿态地标点
            for idx, landmark in enumerate(results.pose_landmarks.landmark):
                # 获取地标点的坐标(这里坐标是归一化的,范围0-1,后续可根据图像宽高转换为实际像素坐标)
                x = int(landmark.x * frame.shape[1])
                y = int(landmark.y * frame.shape[0])
                # 在画面上绘制地标点(用小圆圈表示)
                #cv2.circle(frame, (x, y), 5, (0, 255, 0), -1)
                # 在地标点旁边显示对应的编号
                #cv2.putText(frame, str(idx), (x + 10, y + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

        # 显示视频帧
        cv2.imshow('Frame', frame)
        # 将处理后的帧写入输出视频文件
        out.write(frame)
        if cv2.waitKey(1) & 0xFF == 27:  # 按ESC键退出
            break

    cap.release()
    cv2.destroyAllWindows()
    pose.close()

# 识别视频
def readVedio():

    # 用于记录初始膝盖位置纵坐标(这里简单以膝盖点24为例,也可综合考虑25等情况)
    initial_knee_y = 150
    # 卷腹计数
    complete_count = 0
    # 定义膝盖上升距离阈值(可根据实际情况调整),当膝盖上升超过此阈值认为可能完成一次卷腹
    distance_threshold = 150

    isCheck = False

    count = 0
    # 初始化MediaPipe姿态检测对象
    mp_pose = mp.solutions.pose
    pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.5, min_tracking_confidence=0.5)

    # 打开摄像头获取视频流
    cap = cv2.VideoCapture("./output_video.avi")


    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        count += 1

		# 这里是为了控制视频在指定的帧数范围才进行识别,提高调试的效率
        if count < 230 or count > 1000:
            continue
        print(count)

        # 将图像转换为RGB格式(MediaPipe要求)
        image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # 进行人体姿态检测
        results = pose.process(image_rgb)

        if results.pose_landmarks:
            # 遍历所有的人体姿态地标点
            for idx, landmark in enumerate(results.pose_landmarks.landmark):
                # 获取地标点的坐标(这里坐标是归一化的,范围0-1,后续可根据图像宽高转换为实际像素坐标)
                x = int(landmark.x * frame.shape[1])
                y = int(landmark.y * frame.shape[0])
                if idx == 26 or idx == 25:
                    if isCheck == False:
                        if x <= 150:
                            isCheck = True
                    if isCheck == True:
                        if x - initial_knee_y > distance_threshold:
                            isCheck = False
                            complete_count += 1
                    print(idx,x,y)
                # 在画面上绘制地标点(用小圆圈表示)
                cv2.circle(frame, (x, y), 5, (0, 255, 0), -1)
                # 在地标点旁边显示对应的编号
                cv2.putText(frame, str(idx), (x + 10, y + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)

            mp.solutions.drawing_utils.draw_landmarks(frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)

        # 在画面上显示计数结果
        cv2.putText(frame, f"Count: {complete_count}", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        # 显示视频帧
        cv2.imshow('Frame', frame)
        if cv2.waitKey(1) & 0xFF == 27:  # 按ESC键退出
            break
        #sleep(0.3)


    cap.release()
    cv2.destroyAllWindows()
    pose.close()


if __name__ == '__main__':
    readVedio()

效果

记录最低点的值
记录膝盖最高点的值

后续优化

目前是根据视频中膝盖最低点与最高点的差值来计算出是否完成一次运动,还存在一些弊端:
1.对视频的识别效果很好,但是通用性不强,当人离摄像头的远近不同时,其点位的值会同步发生变化
2.可以采用其他方式进行判断:
1.高地差的绝对值判断改为百分比判断
2.辅助其他节点进行判断,例如:膝盖的点(25,26)的x值大于等于臀部的点(23,24)的时候,记为完成一次运动

后记

根据该思路,可实现其他很多类的运动检测,如:俯卧撑,深蹲,等等,可通过计算百分比的值,播放不同的音乐,来让运动过程更有趣,就像 switch的健身环大冒险一样!包括也搜了一些市面上成熟的产品,例如:魔镜(一个卖大几千),看着是同样的效果


http://www.kler.cn/a/463680.html

相关文章:

  • 深入理解 Android 中的 ApplicationInfo
  • 非docker方式部署openwebui过程记录
  • Swift Combine 学习(五):Backpressure和 Scheduler
  • B3842 [GESP202306 三级] 春游
  • 【74HC192减法24/20/72进制】2022-5-17
  • Linux Red Hat 7.9 Server安装GitLab
  • html 音频和视频组件
  • Kubernetes Gateway API-3-TLS配置
  • CES Asia 2025:助力新型城市基础设施建设,展现智慧城市科技魅力
  • Modbus知识详解
  • 单片机--51- RAM
  • @colyseus/loadtest 插件详解
  • 代码随想录算法训练营第十七天-二叉树-654.最大二叉树
  • STM32-笔记19-串口打印功能
  • arm rk3588 升级glibc2.31到2.33
  • AIGC与未来的通用人工智能(AGI):从生成内容到智能革命
  • 华为云Welink数据怎么连接到小满CRM?
  • gesp(C++一级)(12)洛谷:B3953:[GESP202403 一级] 找因数
  • 电脑与手机
  • GPT分区 使用parted标准分区划分,以及相邻分区扩容
  • 苍穹外卖04——Redis初入门 在店铺打烊or营业状态管理功能中的使用
  • 条款35:考虑虚函数以外的其它选择(Consider alternatives to virtual functions)
  • 元宇宙金融新纪元:CZ协议全球启航
  • ctrip 小试牛刀记录
  • 分布式系统架构6:链路追踪
  • 基于SpringBoot的题库管理系统的设计与实现(源码+SQL+LW+部署讲解)