如何使用MediaPipe Pose构建一个俯卧撑计数器

描述

  由于最近虚拟现实和增强现实的增长和普及,人体姿态估计技术变得越来越流行。在我们之前的教程中,我们使用了 Media pipe Hand Tracking 模块来使用手势控制 Raspberry Pi 媒体播放器。在这个项目中,我们将使用MediaPipe Pose 估计包来构建一个俯卧撑。我们将使用姿势估计包来获取肩膀和肘部的坐标。肩部坐标将与肘部坐标进行比较。因此,如果肩膀与肘部处于同一水平或低于肘部,则将被视为俯卧撑。我们还将扬声器连接到 Raspberry Pi 以告知俯卧撑的计数。

  俯卧撑计数所需的组件

  树莓派 4

  Pi 相机模块

  扬声器

  在这里,我们只需要安装了 OpenCV 和 MediaPipe 的 Raspberry Pi 4 和 Pi 摄像头模块。 OpenCV 用于 数字图像处理,而 MediaPipe 用于 Pose 估计。数字图像处理最常见的应用是 物体检测、 人脸识别和 人数统计。

  什么是媒体管道?

  MediaPipe是一个框架,用于构建跨平台(即 Android、iOS、Web、边缘设备)多模式(即视频、音频或任何时间序列数据)应用机器学习管道,包括快速 ML 推理、经典计算机视觉和媒体处理(例如视频解码)。MediaPipe 发布了各种预构建的 Python 和其他语言包,例如:

  物体检测

  人脸检测

  手部追踪

  姿势估计

  多手追踪

  头发分割

  MediaPipe Python 包可用于 Linux、macOS 和 Windows 的 PyPI。使用以下命令在 Raspberry Pi 4 上安装 MediaPipe:

 

须藤 pip3 安装 mediapipe-rpi4

 

如果您没有 Pi 4,则可以使用以下命令将其安装在 Raspberry Pi 3 上:

 

须藤 pip3 安装 mediapipe-rpi3

 

安装 OpenCV

在安装 OpenCV 和其他依赖项之前,Raspberry Pi 需要完全更新。使用以下命令将 Raspberry Pi 更新到其最新版本:

 

sudo apt-get 更新

 

然后使用以下命令安装在 Raspberry Pi 上安装 OpenCV 所需的依赖项。

 

sudo apt-get install libhdf5-dev -y 
sudo apt-get install libhdf5-serial-dev –y 
sudo apt-get install libatlas-base-dev –y 
sudo apt-get install libjasper-dev -y 
sudo apt-get install libqtgui4 –y 
sudo apt-get install libqt4-test –y

 

之后,使用以下命令在您的 Raspberry Pi 上安装 OpenCV。

 

pip3 安装 opencv-contrib-python==4.1.0.25

 

为上推计数器编程 Raspberry Pi

文档末尾给出了使用 OpenCV 上推计数器的完整代码。在这里,我们将解释代码的重要部分以便更好地解释。

通过导入 OpenCV 和 MediaPipe 包来启动代码。如前所述,MediaPipe 是 Pose 估计的核心包,而 OpenCV 用于图像处理。

 

导入简历2
将媒体管道导入为 mp

 

在接下来的几行中,我们创建了两个新变量。第一个是mp_drawing,它将用于从 MediaPipe Python 包中获取所有绘图实用程序,第二个是mp_pose,用于导入姿势估计模型。

 

mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

 

之后,定义一个名为findPosition() 的函数。顾名思义,它用于查找所有身体标志的 X、Y 坐标,如肩膀、眼睛等。所有标志的坐标将存储在名为lmList[]的变量中。

 

def findPosition(图像,绘制=真):
  lmList = []
  如果结果.pose_landmarks:
      mp_drawing.draw_landmarks(
         图像,results.pose_landmarks,mp_pose.POSE_CONNECTIONS)
      对于 id, lm in enumerate(results.pose_landmarks.landmark):
          h, w, c = image.shape
          cx, cy = int(lm.x * w), int(lm.y * h)
          lmList.append([id, cx, cy])
  返回 lmList

 

然后从 Raspberry Pi 摄像头开始视频流。您甚至可以使用预先录制的视频,而不是使用实时流。为此,将“0”替换为视频路径。

 

上限 = cv2.VideoCapture(0)
#cap = cv2.VideoCapture(“我的视频.mp4”)

 

然后在下一行,为 mediapipe feed 设置一个新实例,以访问我们之前导入的姿势估计模型。我们还传递了两个关键字参数,即最小检测置信度和最小跟踪置信度。接下来,我们将读取视频帧并将它们存储在 image 变量中。

以 mp_pose.Pose(min_detection_confidence=0.7, min_tracking_confidence=0.7) 作为姿势:

 

  而 cap.isOpened():
     成功,图像 = cap.read()
     图像 = cv2.resize(图像, (1280, 720))

 

我们从视频源获得的图像最初是 BGR 格式。S,在这一行中,我们将首先将图像水平翻转以供稍后的自拍视图显示,然后将BGR图像转换为RGB。

 

图像 = cv2.cvtColor(cv2.flip(图像, 1), cv2.COLOR_BGR2RGB)

 

之后,我们将通过姿势估计模型传递图像以进行检测并将结果存储在一个名为“结果”的变量中。然后我们将 BGR 图像转换为 RGB。

 

结果=姿势.过程(图像)
图像 = cv2.cvtColor(图像,cv2.COLOR_BGR2RGB)

 

现在我们得到了检测结果,我们将调用findPosition() 函数在图像上绘制这些检测,并使用我们之前导入的绘图实用程序连接所有检测。

 

mp_drawing.draw_landmarks(图像,results.pose_landmarks,mp_pose.POSE_CONNECTIONS)

 

之后,我们会将所有检测的 id 和坐标存储在一个名为 lmList 的变量中。

 

lmList = findPosition(图像,绘制=真)

 

然后在接下来的几行中,我们将首先获取两个肩膀的坐标,然后在上面画圆。如下图所示,左右肩的 id 分别为 12 和 11。

计数器

  如果 len(lmList) != 0:

 

      cv2.circle(图像, (lmList[12][1], lmList[12][2]), 20, (0, 0, 255), cv2.FILLED)
      cv2.circle(图像, (lmList[11][1], lmList[11][2]), 20, (0, 0, 255), cv2.FILLED)
      cv2.circle(图像, (lmList[12][1], lmList[12][2]), 20, (0, 0, 255), cv2.FILLED)
      cv2.circle(图像, (lmList[11][1], lmList[11][2]), 20, (0, 0, 255), cv2.FILLED)

 

现在,当我们找到肩膀的坐标时,我们将把这些坐标与两个肘部的坐标进行比较。所以,如果肩膀与肘部在同一水平或在肘部以下,我们将阶段设置为“向下”,当肩膀在肘部以上时,阶段将变为向上,这将被视为俯卧撑.

 

      如果(lmList[12][2] 和 lmList[11][2] >= lmList[14][2] 和 lmList[13][2]):
        cv2.circle(图像, (lmList[12][1], lmList[12][2]), 20, (0, 255, 0), cv2.FILLED)
        cv2.circle(图像, (lmList[11][1], lmList[11][2]), 20, (0, 255, 0), cv2.FILLED)
        阶段=“下降”
      if (lmList[12][2] and lmList[11][2] <= lmList[14][2] and lmList[13][2]) and stage == "down":
        阶段=“向上”
        计数器 += 1
        打印(计数器)

 

现在,随着俯卧撑的计数,我们将使用cv2.putText() 在视频流上打印俯卧撑计数。

 

text = "{}:{}".format("俯卧撑", counter)
    cv2.putText(图像, 文本, (10, 40), cv2.FONT_HERSHEY_SIMPLEX,
                1, (255, 0, 0), 2)
    cv2.imshow('MediaPipe 姿势', image)

 

最后,我们将视频流保存在我们的项目文件夹中。

 

    如果创建是无:
      fourcc = cv2.VideoWriter_fourcc(*'XVID')
      create = cv2.VideoWriter(opname,fourcc, 30, (image.shape[1], image.shape[0]), True)
    create.write(图像)

 

  测试上推计数器脚本

  现在 Push Up 计数器脚本已准备就绪,让我们继续测试它。有两种测试方法。您可以使用预先录制的视频,也可以使用 Pi 相机进行实时视频流。在这里,我们将使用 Pi 摄像头模块,因此,将 Raspberry Pi 摄像头模块与 Pi 连接,如下所示:

计数器

  现在,检查 Pi 相机是否正常工作。查看相机后,启动 python 脚本,您会发现弹出一个窗口,其中包含您的视频源。最初做俯卧撑时,肩膀上的点将是红色:

计数器

  当肩膀低于肘部时,点将从红色变为绿色,俯卧撑计数将增加一。

计数器

  这就是您可以使用 MediaPipe 计算俯卧撑的方法。您还可以使用此库来计算仰卧起坐或任何其他锻炼。下面给出了完整的工作视频和代码。

  代码

    import cv2

  import mediapipe as mp

  import os

  mp_drawing = mp.solutions.drawing_utils

  mp_pose = mp.solutions.pose

  counter = 0

  stage = None

  create = None

  opname = “output.avi”

  def findPosition(image, draw=True):

  lmList = []

  if results.pose_landmarks:

  mp_drawing.draw_landmarks(

  image, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)

  for id, lm in enumerate(results.pose_landmarks.landmark):

  h, w, c = image.shape

  cx, cy = int( lm.x * w), int(lm.y * h)

  lmList.append([id, cx, cy])

  #cv2.circle(image, (cx, cy), 5, (255, 0, 0), cv2.填充)

  返回 lmList

  cap = cv2.VideoCapture(0)

  与 mp_pose.Pose(

  min_detection_confidence=0.7,

  min_tracking_confidence=0.7) 作为姿势:

  而 cap.isOpened():

  成功,image = cap.read()

  image = cv2.resize(image, (640,480))

  if not success:

  print(“Ignoring empty camera frame.”)

  # 如果加载视频,使用 ‘break’ 而不是 ‘continue’。

  continue

  # 水平翻转图像以供稍后的自拍视图显示,并将

  # BGR 图像转换为 RGB。

  image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)

  # 为了提高性能,可选择将图像标记为不可写入

  # 通过引用传递。

  results = pose.process(image)

  # 在图像上绘制姿态标注。

  image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

  lmList = findPosition(image, draw=True)

  if len(lmList) != 0:

  cv2.circle(image, (lmList[12][1], lmList[12] [2]), 20, (0, 0, 255), cv2.FILLED)

  cv2.circle(image, (lmList[11][1], lmList[11][2]), 20, (0, 0, 255), cv2.FILLED)

  cv2.circle(image, (lmList[12][1], lmList[12][2]), 20, (0, 0, 255), cv2.FILLED)

  cv2.circle(image , (lmList[11][1], lmList[11][2]), 20, (0, 0, 255), cv2.FILLED)

  if (lmList[12][2] 和 lmList[11][2] 》= lmList[14][2] 和 lmList[13][2]):

  cv2.circle(image, (lmList[12][1], lmList[12][2]), 20, (0, 255, 0), cv2.FILLED)

  cv2.circle(image, (lmList[11][ 1], lmList[11][2]), 20, (0, 255, 0), cv2.FILLED)

  stage = “down”

  if (lmList[12][2] and lmList[11][2] 《= lmList[14][2] 和 lmList[13][2]) 和 stage == “down”:

  stage = “up”

  counter += 1

  counter2 = str(int(counter))

  print(counter)

  os.system( “echo ‘” + counter2 + “’ | festival --tts”)

  text = “{}:{}”.format(“Push Ups”, counter)

  cv2.putText(image, text, (10, 40), cv2 .FONT_HERSHEY_SIMPLEX,

  1, (255, 0, 0), 2)

  cv2.imshow(‘MediaPipe Pose’, image)

  如果 create 为 None:

  fourcc = cv2.VideoWriter_fourcc(*‘XVID’)

  create = cv2.VideoWriter(opname,fourcc, 30, (image.shape[1], image.shape[0]), True)

  create.write(image)

  key = cv2 .waitKey(1) & 0xFF

  # 如果 `q` 键被按下,则退出循环

  if key == ord(“q”):

  break

  # 做一些清理

  cv2.destroyAllWindows()

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分