社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python玩人工智能:你能做几个引体向上?

编程玩家俱乐部 • 4 年前 • 316 次点击  
阅读 18

Python玩人工智能:你能做几个引体向上?

Google出了一个开源的、跨平台的、可定制化的机器学习解决方案工具包,给在线流媒体(当然也可以用于普通的视频、图像等)提供了机器学习解决方案。感兴趣的同学可以打开这个网址了解详情:mediapipe.dev/

image-20210422112313073.png

它提供了手势、人体姿势、人脸、物品等识别和追踪功能,并提供了C++、Python、JavaScript等编程语言的工具包以及iOS、Android平台的解决方案,今天我们就来看一下如何使用MediaPipe提供的人体姿势识别功能,使用Python编程完成一个“引体向上检测”的程序。

电脑需要安装Python3,建议安装Python3.8.x的版本。除此之外,还需要安装Opencv-Python、MediaPipe以及numpy几个工具包,可以使用pip进行安装:

pip install mediapipe numpy opencv-python

我的电脑是Python3.8.3,各工具包版本是:

mediapipe==0.8.3.1
numpy==1.20.2
opencv-python==4.5.1.48
复制代码

编写一个poseutil.py模块,这个postutil模块有一个PoseDetector类,提供了检测人体姿势、获取人体姿势数据以及获取人体关节的角度的方法。代码如下,详细解决看代码注释:

import cv2
import mediapipe as mp
import math

class PoseDetector():
    '''
    人体姿势检测类
    '''
    def __init__(self,
                 static_image_mode=False,
                 upper_body_only=False,
                 smooth_landmarks=True,
                 min_detection_confidence=0.5,
                 min_tracking_confidence=0.5):
        '''
        初始化
        :param static_image_mode: 是否是静态图片,默认为否
        :param upper_body_only: 是否是上半身,默认为否
        :param smooth_landmarks: 设置为True减少抖动
        :param min_detection_confidence:人员检测模型的最小置信度值,默认为0.5
        :param min_tracking_confidence:姿势可信标记的最小置信度值,默认为0.5
        '''
        self.static_image_mode = static_image_mode
        self.upper_body_only = upper_body_only
        self.smooth_landmarks = smooth_landmarks
        self.min_detection_confidence = min_detection_confidence
        self.min_tracking_confidence = min_tracking_confidence
        # 创建一个Pose对象用于检测人体姿势
        self.pose = mp.solutions.pose.Pose(self.static_image_mode, self.upper_body_only, self.smooth_landmarks,
                                           self.min_detection_confidence, self.min_tracking_confidence)

    def find_pose(self, img, draw=True):
        '''
        检测姿势方法
        :param img: 一帧图像
        :param draw: 是否画出人体姿势节点和连接图
        :return: 处理过的图像
        '''
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # pose.process(imgRGB) 会识别这帧图片中的人体姿势数据,保存到self.results中
        self.results = self.pose.process(imgRGB)
        if self.results.pose_landmarks:
            if draw:
                mp.solutions.drawing_utils.draw_landmarks(img, self.results.pose_landmarks,
                                                          mp.solutions.pose.POSE_CONNECTIONS)
        return img

    def find_positions(self, img):
        '''
        获取人体姿势数据
        :param img: 一帧图像
        :param draw: 是否画出人体姿势节点和连接图
        :return: 人体姿势数据列表
        '''
        # 人体姿势数据列表,每个成员由3个数字组成:id, x, y
        # id代表人体的某个关节点,x和y代表坐标位置数据
        self.lmslist = []
        if self.results.pose_landmarks:
            for id, lm in enumerate(self.results.pose_landmarks.landmark):
                h, w, c = img.shape
                cx, cy = int(lm.x * w), int(lm.y * h)
                self.lmslist.append([id, cx, cy])

        return self.lmslist

    def find_angle(self, img, p1, p2, p3, draw=True):
        


    
'''
        获取人体姿势中3个点p1-p2-p3的角度
        :param img: 一帧图像
        :param p1: 第1个点
        :param p2: 第2个点
        :param p3: 第3个点
        :param draw: 是否画出3个点的连接图
        :return: 角度
        '''
        x1, y1 = self.lmslist[p1][1], self.lmslist[p1][2]
        x2, y2 = self.lmslist[p2][1], self.lmslist[p2][2]
        x3, y3 = self.lmslist[p3][1], self.lmslist[p3][2]

        # 使用三角函数公式获取3个点p1-p2-p3,以p2为角的角度值,0-180度之间
        angle = int(math.degrees(math.atan2(y1-y2, x1-x2) - math.atan2(y3-y2, x3-x2)))
        if angle < 0:
            angle = angle + 360
        if angle > 180:
            angle = 360 - angle

        if draw:
            cv2.circle(img, (x1, y1), 8, (0, 255, 255), cv2.FILLED)
            cv2.circle(img, (x2, y2), 15, (255, 0, 255), cv2.FILLED)
            cv2.circle(img, (x3, y3), 8, (0, 255, 255), cv2.FILLED)
            cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255, 3))
            cv2.line(img, (x2, y2), (x3, y3), (255, 255, 255, 3))
            #cv2.putText(img, str(angle), (x2-50, y2+50),cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 2)

        return angle
复制代码

编写另一个posetracking.py代码,在这个代码中,调用poseutil.py的PoseDetector类提供的方法,从引体向上的视频中(或者是实时摄像头视频)获取人体左右手肘的的弯曲角度,假设我们以手肘角度从170度(完全放松时)到20度(引体向上拉上去时)认定一个标准的引体向上,这样我们就可以检测标准引体向上完成个数啦。

pose_tracking_full_body_landmarks.png

从上图可知:11、13、15这3个点是左边肩膀、手肘和手腕的节点,而12、14、16是右手的。完整代码如下:

import cv2
import numpy as np
from poseutil import PoseDetector

# opencv打开一个视频
cap = cv2.VideoCapture('mp4/1.mp4')
# 创建一个PoseDetector类的对象
detector = PoseDetector()
# 方向和完成次数的变量
dir = 0
count = 0

while True:
    # 读取视频图片帧
    success, img = cap.read()
    if success:
        # 检测视频图片帧中人体姿势
        img = detector.find_pose(img, draw=True)
        # 获取人体姿势列表数据
        lmslist = detector.find_positions(img)

        # 右手肘的角度
        right_angle = detector.find_angle(img, 12, 14, 16)
        # 以170到20度检测右手肘弯曲的程度
        right_per = np.interp(right_angle, (20, 170), (100, 0))
        # 进度条高度数据
        right_bar = np.interp(right_angle, (20, 170), (200, 400))
        # 使用opencv画进度条和写右手肘弯曲的程度
        cv2.rectangle(img, (1200, 200), (1220, 400), (0, 255, 0), 3)
        cv2.rectangle(img, (1200, int(right_bar)), (1220, 400), (0, 255, 0), cv2.FILLED)
        cv2.putText(img, str(int(right_per)) + '%', (1190, 450), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)

        # 左手肘的角度
        left_angle = detector.find_angle(img, 11, 13, 15)
        left_per = np.interp(left_angle, (20, 170), (100, 0))
        left_bar = np.interp(left_angle, (20, 170), (200, 400))
        cv2.rectangle(img, (500, 200), (520, 400), (0, 255, 0), 3)
        cv2.rectangle(img, (500, int(left_bar)), (520, 400), (0, 255, 0), cv2.FILLED)
        cv2.putText(img, str(int(left_per)) + '%', (490, 450), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)

        # 检测个数,我这里设置的是从20%做到80%,就认为是一个
        if (left_per >= 80 and right_per >= 80):
            if dir == 0:
                count = count + 0.5
                dir = 1
        if (left_per <= 20 and right_per <= 20):
            if dir == 1:
                count = count + 0.5
                dir = 0

        # 在视频上显示完成个数
        cv2.putText(img, str(int(count)), (1000, 100), cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 255, 255), 4)

        cv2.imshow('Image', img)
    else:
        break
    k = cv2.waitKey(1)
    if k == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
复制代码

运行代码,我们会看到2个手肘的角度变化以及2个进度条和完成个数,使用它就可以检测标准引体向上的完成个数啦。

image-20210422115824724.png

image-20210422115931135.png

image-20210422115850468.png

image-20210422115958771.png

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/113823
 
316 次点击