【深度学习实战—9】:基于MediaPipe的坐姿检测

✨博客主页:王乐予🎈
✨年轻人要:Living for the moment(活在当下)!💪
🏆推荐专栏:【图像处理】【千锤百炼Python】【深度学习】【排序算法】

目录

  • 😺一、MediaPipe概述
  • 😺二、MediaPipe姿态特征点检测
    • 🐶2.1 概述
    • 🐶2.2 度量函数
  • 😺三、代码实现
    • 🐶3.1 utils.py
    • 🐶3.2 main.py
  • 😺四、参考

😺一、MediaPipe概述

MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。

MediaPipe目前支持的解决方案(Solution)及支持的平台如下图所示:
在这里插入图片描述

😺二、MediaPipe姿态特征点检测

🐶2.1 概述

通过 MediaPipe Pose Marker,可以检测图片或视频中人体的特征点。使用此任务识别关键的身体位置,分析姿势并对动作进行分类。该任务会在图片坐标和三维世界坐标中输出身体姿势地标。

姿势特征点使用一系列模型来预测姿势特征点。第一个模型检测图片帧中是否存在人体,第二个模型则在身体上定位地标。

姿势特征点模型会跟踪 33 个身体特征点位置,表示以下身体部位的大致位置:
请添加图片描述
点位信息如下:

0 - nose
1 - left eye (inner)
2 - left eye
3 - left eye (outer)
4 - right eye (inner)
5 - right eye
6 - right eye (outer)
7 - left ear
8 - right ear
9 - mouth (left)
10 - mouth (right)
11 - left shoulder
12 - right shoulder
13 - left elbow
14 - right elbow
15 - left wrist
16 - right wrist
17 - left pinky
18 - right pinky
19 - left index
20 - right index
21 - left thumb
22 - right thumb
23 - left hip
24 - right hip
25 - left knee
26 - right knee
27 - left ankle
28 - right ankle
29 - left heel
30 - right heel
31 - left foot index
32 - right foot index

🐶2.2 度量函数

坐姿检测将使用不同关键点的向量夹角做判定,向量内角图如下:
在这里插入图片描述
内角计算与向量的起止顺序有关,在上图中,假定选择kpt1和kpt2为人体的两个关键点,kpt3为向量起始点即kpt1的垂直方向任意位置的点,则夹角为:
θ = arccos ⁡ ( P 12 → × P 13 → ∣ P 12 → ∣ ∣ P 13 → ∣ ) \theta =\arccos (\frac{\overrightarrow{P_{12} } \times \overrightarrow{P_{13} } }{\left | \overrightarrow{P_{12} } \right | \left | \overrightarrow{P_{13} } \right | } ) θ=arccos( P12 P13 P12 ×P13 )

不妨设kpt3的y3坐标为0,则带入坐标值有:
θ = arccos ⁡ ( y 1 2 − y 1 × y 2 y 1 ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 ) \theta =\arccos (\frac{y_{1}^{2} - y_{1}\times y_{2} }{y_{1}\sqrt{(x_{2}-x_{1})^{2}+(y_{2}-y_{1})^{2} } } ) θ=arccos(y1(x2x1)2+(y2y1)2 y12y1×y2)

根据上图可知 θ \theta θ为锐角,如果向量方向为由kpt2指向kpt1,则需要在kpt2的垂直方向标记点kpt3,此时 θ \theta θ为钝角。

😺三、代码实现

  • utils.py:包含度量函数的定义与姿态检测函数
  • main.py:主函数,获取需要的关键点数据,绘图

🐶3.1 utils.py

import math as m


# 度量函数
def findAngle(x1, y1, x2, y2):
    theta = m.acos((y2 - y1) * (-y1) / (m.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) * y1))
    degree = int(180/m.pi)*theta
    return degree

"""
歪头监控:计算 左耳(7点)和 右耳(8点)的夹角
低头监控:计算 左嘴角(9点)和 左肩膀(11点)的夹角
侧脸监控:计算 右眼内(4点)和 左耳(7点)的距离,计算 左眼内(1点)和 右耳(8点)的距离
高低肩监控:计算 左肩膀(11点)和 右肩膀(12点)的夹角            *****有的人左肩和右肩一个高一个低*****
撑桌监控:如果 左嘴角(9点)或者 右嘴角(10点)的 y 坐标 大于 左肩膀(11点)或 右肩膀(12点)的 y 坐标,视为撑桌
仰头监控:计算 鼻子(0点)和 左耳(7点)的夹角
趴桌监控:如果 左肩膀(11点)和 右肩膀(12点)的 归一化y坐标 之和大于0.75,判定为趴桌
"""
def all_detection(nose_x, nose_y,                               # 鼻子(0点)的 x 坐标 和 y 坐标
                  left_eye_inner_x, left_eye_inner_y,           # 左眼内(1点)的 x 坐标 和 y 坐标
                  right_eye_inner_x, right_eye_inner_y,         # 右眼内(4点)的 x 坐标 和 y 坐标
                  left_ear_x, left_ear_y,                       # 左耳(7点)的 x 坐标 和 y 坐标
                  right_ear_x, right_ear_y,                     # 右耳(8点)的 x 坐标 和 y 坐标
                  left_mouth_x, left_mouth_y,                   # 左嘴角(9点)的 x 坐标 和 y 坐标
                  right_mouth_x, right_mouth_y,                 # 右嘴角(10点)的 x 坐标 和 y 坐标
                  left_shoulder_x, left_shoulder_y,             # 左肩膀(11点)的 x 坐标 和 y 坐标
                  right_shoulder_x, right_shoulder_y,           # 右肩膀(12点)的 x 坐标 和 y 坐标
                  left_shoulder_x_norm, left_shoulder_y_norm,   # 归一化后的左肩膀(11点)的 x 坐标 和 y 坐标
                  right_shoulder_x_norm, right_shoulder_y_norm  # 归一化后的右肩膀(12点)的 x 坐标 和 y 坐标
                  ):
    waitou_inclination = findAngle(left_ear_x, left_ear_y, right_ear_x, right_ear_y)
    ditou_inclination = findAngle(left_mouth_x, left_mouth_y, left_shoulder_x, left_shoulder_y)
    gaodijian_inclination = findAngle(left_shoulder_x, left_shoulder_y, right_shoulder_x, right_shoulder_y)
    yangtou_inclination = findAngle(nose_x, nose_y, left_ear_x, left_ear_y)
    if waitou_inclination < 80:
        tmp = '左歪头'
    elif waitou_inclination > 100:
        tmp = '右歪头'
    elif (left_shoulder_y_norm + right_shoulder_y_norm) > 1.5:
        tmp = '趴桌'
    elif ditou_inclination < 115:
        tmp = '低头'
    elif left_ear_x < right_eye_inner_x:
        tmp = '左侧脸'
    elif right_ear_x > left_eye_inner_x:
        tmp = '右侧脸'
    elif gaodijian_inclination > 100:
        tmp = '高低肩'
    elif gaodijian_inclination < 80:
        tmp = '高低肩'
    elif (left_mouth_y or right_mouth_y) > (left_shoulder_y or right_shoulder_y):
        tmp = '撑桌'
    elif yangtou_inclination > 90:
        tmp = '仰头'
    else:
        tmp = '正脸'
    return tmp

🐶3.2 main.py

import cv2
import mediapipe as mp
from utils import *


mp_pose = mp.solutions.pose
pose = mp_pose.Pose(model_complexity=1, min_detection_confidence=0.5, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    h, w = frame.shape[:2]
    image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    keypoints = pose.process(image)

    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    lm = keypoints.pose_landmarks
    lmPose = mp_pose.PoseLandmark

    # 歪头监控
    left_ear_x = int(lm.landmark[lmPose.LEFT_EAR].x * w)    # 左耳(7点)x 坐标
    left_ear_y = int(lm.landmark[lmPose.LEFT_EAR].y * h)    # 左耳(7点)y 坐标
    right_ear_x = int(lm.landmark[lmPose.RIGHT_EAR].x * w)  # 右耳(8点)x 坐标
    right_ear_y = int(lm.landmark[lmPose.RIGHT_EAR].y * h)  # 右耳(8点)y 坐标

    # 低头监控
    left_mouth_x = int(lm.landmark[lmPose.MOUTH_LEFT].x * w)    # 左嘴角(9点)x 坐标
    left_mouth_y = int(lm.landmark[lmPose.MOUTH_LEFT].y * h)    # 左嘴角(9点)y 坐标
    left_shoulder_x = int(lm.landmark[lmPose.LEFT_SHOULDER].x * w)    # 左肩膀(11点)x 坐标
    left_shoulder_y = int(lm.landmark[lmPose.LEFT_SHOULDER].y * h)    # 左肩膀(11点)y 坐标

    # 侧脸监控
    left_eye_inner_x = int(lm.landmark[lmPose.LEFT_EYE_INNER].x * w)    # 左眼内(1点)x 坐标
    left_eye_inner_y = int(lm.landmark[lmPose.LEFT_EYE_INNER].y * h)    # 左眼内(1点)y 坐标
    right_eye_inner_x = int(lm.landmark[lmPose.RIGHT_EYE_INNER].x * w)  # 右眼内(4点)x 坐标
    right_eye_inner_y = int(lm.landmark[lmPose.RIGHT_EYE_INNER].y * h)  # 右眼内(4点)y 坐标

    # 高低肩监控
    right_shoulder_x = int(lm.landmark[lmPose.RIGHT_SHOULDER].x * w)  # 右肩膀(12点)x 坐标
    right_shoulder_y = int(lm.landmark[lmPose.RIGHT_SHOULDER].y * h)  # 右肩膀(12点)y 坐标

    # 撑桌监控
    right_mouth_x = int(lm.landmark[lmPose.MOUTH_RIGHT].x * w)  # 左嘴角(10点)x 坐标
    right_mouth_y = int(lm.landmark[lmPose.MOUTH_RIGHT].y * h)  # 左嘴角(10点)y 坐标

    # 仰头监控
    nose_x = int(lm.landmark[lmPose.NOSE].x * w)    # 鼻子(0点)x 坐标
    nose_y = int(lm.landmark[lmPose.NOSE].y * h)    # 鼻子(0点)y 坐标

    # 趴桌监控
    left_shoulder_x_norm = lm.landmark[lmPose.LEFT_SHOULDER].x  # 左肩膀(11点)x 坐标-归一化
    left_shoulder_y_norm = lm.landmark[lmPose.LEFT_SHOULDER].y  # 左肩膀(11点)y 坐标-归一化
    right_shoulder_x_norm = lm.landmark[lmPose.RIGHT_SHOULDER].x  # 右肩膀(12点)x 坐标-归一化
    right_shoulder_y_norm = lm.landmark[lmPose.RIGHT_SHOULDER].y  # 右肩膀(12点)y 坐标-归一化

    results = all_detection(nose_x, nose_y,
                  left_eye_inner_x, left_eye_inner_y,
                  right_eye_inner_x, right_eye_inner_y,
                  left_ear_x, left_ear_y,
                  right_ear_x, right_ear_y,
                  left_mouth_x, left_mouth_y,
                  right_mouth_x, right_mouth_y,
                  left_shoulder_x, left_shoulder_y,
                  right_shoulder_x, right_shoulder_y,
                  left_shoulder_x_norm, left_shoulder_y_norm,
                  right_shoulder_x_norm, right_shoulder_y_norm)
    print(results)


    mp_drawing.draw_landmarks(image, keypoints.pose_landmarks, mp_pose.POSE_CONNECTIONS)
    cv2.imshow("Image", image)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

需要注意的是utils.py中的判定指标不是固定的,根据摄像头的位置动态调整才能达到满意的效果。

😺四、参考

Google:pose_landmarker
LearnOpencv:building-a-body-posture-analysis-system-using-mediapipe

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/671136.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端Vue自定义滚动卡片组件设计与实现

摘要 随着技术的日新月异&#xff0c;前端开发的复杂度不断提升。传统的整块应用开发方式在面对小的改动或功能增加时&#xff0c;常常需要修改大量的整体逻辑&#xff0c;造成开发效率低下和维护成本高昂。为了应对这一挑战&#xff0c;组件化开发应运而生。本文将以Vue框架下…

Day46 动态规划part06

完全背包问题 完全背包和01背包问题唯一不同的地方就是&#xff0c;每种物品有无限件。先遍历物品还是先遍历背包以及遍历顺序 根据递推公式可知&#xff1a;每一个dp需要根据上方和左方的数据推出&#xff0c;只要保证数据左上方数据是递推出来的这种两个for循环的顺序就是可…

使用Gradio构建大模型应用:Building Generative AI Applications with Gradio

Building Generative AI Applications with Gradio 本文是学习 https://www.deeplearning.ai/short-courses/building-generative-ai-applications-with-gradio/ 这门课的学习笔记。 What you’ll learn in this course Join our new short course, Building Generative AI A…

C++与Java数据结构设计的区别(持续更新中)

链表 c代码示例 #include <list> int main() {std::list<int> list;list.push_front(1);list.push_back(2);std::list<int>::iterator it list.begin();while (it ! list.end()){std::cout << *it << std::endl;}return 0; } java代码示例 …

C#WPF数字大屏项目实战06--报警信息

1、ItemsControl 简介 ItemsControl 是用来表示一些条目集合的控件&#xff0c;所以它叫条目控件&#xff0c;它的成员是一些其它控件的集合&#xff0c;其继承关系如下&#xff1a; 其常用的派生控件为&#xff1a;ListBox、ListView、ComboBox&#xff0c;为ItemsCo…

Spring Boot 官方不再支持 Spring Boot 的 2.x 版本!新idea如何创建java8项目

idea现在只能创建最少jdk17 使用 IDEA 内置的 Spring Initializr 创建 Spring Boot 新项目时&#xff0c;没有 Java 8 的选项了&#xff0c;只剩下了 > 17 的版本 是因为 Spring Boot 官方不再支持 Spring Boot 的 2.x 版本了&#xff0c;之后全力维护 3.x&#xff1b;而 …

Mac专用投屏工具:AirServer 7 for Mac 激活版下载

AirServer 7 是一款在 Windows 和 macOS 平台上运行的强大的屏幕镜像和屏幕录制软件。它能够将 iOS 设备、Mac 以及其他 AirPlay、Google Cast 和 Miracast 兼容设备的屏幕镜像到电脑上&#xff0c;并支持高质量的录制功能。总的来说&#xff0c;AirServer 7 是一款功能全面的屏…

配置 HTTP 代理 (HTTP proxy)

配置 HTTP 代理 [HTTP proxy] 1. Proxies2. curl2.1. Environment2.2. Proxy protocol prefixes 3. Use an HTTP proxy (使用 HTTP 代理)3.1. Using the examples (使用示例)3.1.1. Linux or macOS3.1.2. Windows Command Prompt 3.2. Authenticating to a proxy (向代理进行身…

Java开发分析工具:JProfiler 14 for Mac/win 激活版下载

JProfiler是一款功能强大的Java应用程序性能分析工具&#xff0c;适用于Java开发人员和企业用户&#xff0c;可帮助他们识别和解决Java应用程序中的性能问题&#xff0c;提高应用程序的性能和稳定性。使用JProfiler&#xff0c;开发人员可以实时查看Java应用程序的性能数据&…

如何实现对亚马逊网站产品搜索结果网页中自动上下页的翻页

需要在亚马逊网站中通过关键词搜索产品时&#xff0c;实现对网页自动进行点击上下页链接翻页&#xff0c;以便进行数据提取。经观察发现&#xff0c;包含上下页翻页链接的HTML内容及代码如下所示&#xff1a; <a href"/s?kiphonecharger&amp;crid1EE3FWFFFIWJEOF&…

mediasoup基础概览

提示&#xff1a;本文为之前mediasoup基础介绍的优化 mediasoup基础概览 架构&#xff1a;2.特性&#xff1a;优点缺点 3.mediasoup常见类介绍js部分c 4.mediasoup类图5.业务类图 Mediasoup 是一个构建在现代 Web 技术之上的实时通信&#xff08;RTC&#xff09;解决方案&#…

Mac硬件设备系统环境的升级/更新 macOS

Mac硬件设备上进行系统环境的升级/更新macOS 1.大版本(升级)判断(比如&#xff1a;我买的这台电脑设备最高支持Monterey) 点击进入对应的大版本描述说明页查看相关的兼容性描述&#xff0c;根据描述确定当前的电脑设备最高可采用哪个大版本系统(Sonoma/Ventura/Monterey/Big Su…

vue中使用WebSocket心跳机制与Linux中的心跳机制

WebSocket心跳机制 一、WebSocket简介 WebSocket是HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术&#xff0c;属于应用层协议。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。 二、WebSocket事件与方法 …

为什么总是卡在验证真人这里无法通过验证?

最近总是在浏览某些网站的时候卡在这个“确认你是真人”的验证页面&#xff0c;无法通过真人验证&#xff0c;这是怎么回事儿&#xff1f;如何解决呢&#xff1f; 首先&#xff0c;出现这个“确认您是真人”的验证一般都是这个网站使用了 CloudFlare 的安全防护 WAF 规则才会出…

【每日刷题】Day54

【每日刷题】Day54 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 575. 分糖果 - 力扣&#xff08;LeetCode&#xff09; 2. 147. 对链表进行插入排序 - 力扣&#xf…

Ubuntu 24.04 LTS 安装Docker

1 更新软件包索引&#xff1a; sudo apt-get update 2 安装必要的软件包&#xff0c;以允许apt通过HTTPS使用仓库&#xff1a; sudo apt-get install apt-transport-https ca-certificates curl software-properties-common 3 添加Docker的官方GPG密钥&#xff1a; curl -fs…

【WEEK14】 【DAY5】Swagger Part 3【English Version】

2024.5.31 Friday Following up on【WEEK14】 【DAY4】Swagger Part 2【English Version】 Contents 16.6. Configure API Groups16.6.1. Modify SwaggerConfig.java16.6.2. Restart 16.7. Entity Configuration16.7.1. Create a new pojo folder16.7.2. Modify HelloControlle…

RDD与Java实战:学生列表,先按性别降序,再按年龄降序排列

文章目录 Scala RDD 实现Java 实现实战总结 在本实战任务中&#xff0c;我们的目标是对学生列表进行排序&#xff0c;排序规则是先按性别降序排列&#xff0c;再按年龄降序排列。我们提供了两种实现方式&#xff1a;使用Scala的RDD&#xff08;弹性分布式数据集&#xff09;和…

牛客网刷题 | BC109 正斜线形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

今日好料推荐(这就是会计)

今日好料推荐&#xff08;这就是会计&#xff09; 参考资料在文末获取&#xff0c;关注我&#xff0c;获取优质资源。 这就是会计&#xff1a;资本市场的会计逻辑 是一本由中国会计专家编写的书籍&#xff0c;旨在深入探讨会计在资本市场中的核心作用及其运作逻辑。作为一本…