計算機視覺——基于MediaPipe實現人體姿態估計與不良動作檢測

概述

正確的身體姿勢是個人整體健康的關鍵。然而,保持正確的身體姿勢可能會很困難,因為我們常常會忘記。本博客文章將逐步指導您構建一個解決方案。最近,我們使用 MediaPipe POSE 進行身體姿勢檢測,效果非常好!

一、使用 MediaPipe Pose 進行身體姿勢檢測

MediaPipe Pose 是一種高保真度的身體姿勢追蹤解決方案,可以從 RGB 幀中渲染 33 個 3D 關鍵點和一個背景分割掩碼(注意是 RGB 圖像幀)。它利用了 BlazePose拓撲結構,這是 COCO、BlazeFace和 BlazePalm拓撲結構的超集。

1. 應用目標 —— MediaPipe 身體追蹤

我們的目標是從完美的側視圖中檢測一個人,并測量頸部和軀干相對于某個參考軸的傾斜角度。通過監控當人彎腰低于某個特定閾值角度時的傾斜角度來實現。

其他功能包括測量特定姿勢的時間和相機對齊。我們必須確保相機能夠正確地拍攝到側面視角,因此我們需要對齊功能。

2. 身體姿勢檢測與分析應用工作流程

3. 準備工作

OpenCV 和 MediaPipe 是我們需要的主要包。使用代碼文件夾中提供的 requirements.txt 文件來安裝依賴項。

pip install -r requirements.txt

您需要具備 OpenCV Python 的基礎知識才能理解代碼。如果您是 OpenCV 的新手,這里有一些專門為初學者準備的 OpenCV 教程。

二、代碼實現

1. 導入庫

import cv2
import time
import math as m
import MediaPipe as mp

2. 計算偏移距離的函數

設置要求人處于正確的側視圖中。findDistance 函數幫助我們確定兩點之間的偏移距離。它可以是臀部點、眼睛或肩膀。

這些點被選中是因為它們總是或多或少關于人體中心軸對稱。通過這種方式,我們可以在腳本中加入相機對齊功能。

def findDistance(x1, y1, x2, y2):dist = m.sqrt((x2 - x1)**2 + (y2 - y1)**2)return dist

3. 計算身體姿勢傾斜角度的函數

角度是判斷姿勢的主要決定性因素。我們使用頸部線軀干線與 y 軸之間的夾角。頸部線連接肩膀和眼睛,這里我們以肩膀為支點。

同樣,軀干線連接臀部和肩膀,其中臀部被視為支點。

圖:頸部傾斜角度測量

以頸部線為例,我們有以下點:

  • P1(x1, y1):肩膀
  • P2(x2, y2):眼睛
  • P3(x3, y3):通過 P1 的垂直軸上的任意一點

顯然,對于 P3,x 坐標與 P1 相同。由于 y3 對所有 y 都有效,為了簡單起見,我們取 y3 = 0。

我們采用向量方法來求三個點之間的夾角。向量 P****12P****13 之間的夾角由以下公式給出:

# 計算角度。
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) * thetareturn degree

4. 發送不良姿勢警報的函數

當檢測到不良姿勢時,使用此函數發送警報。我們將其保留為空,供您自行發揮創意并根據需要進行自定義。例如,您可以連接一個 Telegram Bot 來發送警報,這非常簡單。鏈接在參考資料部分[6]。或者您可以更進一步,創建一個安卓應用。

def sendWarning(x):pass

5. 初始化

在這里初始化常量和方法。這些應該通過內聯注釋一目了然。

# 初始化幀計數器。
good_frames = 0
bad_frames = 0# 字體類型。
font = cv2.FONT_HERSHEY_SIMPLEX# 顏色。
blue = (255, 127, 0)
red = (50, 50, 255)
green = (127, 255, 0)
dark_blue = (127, 20, 0)
light_green = (127, 233, 100)
yellow = (0, 255, 255)
pink = (255, 0, 255)# 初始化 MediaPipe 姿勢類。
mp_pose = mp.solutions.pose
pose = mp_pose.Pose()

6.身體姿勢檢測

6.1 創建視頻捕獲和視頻寫入對象

為了演示,我們使用預先錄制的視頻樣本。在實際應用中,您需要將網絡攝像頭定位以捕捉您的側視圖。在以下代碼片段中,創建了視頻捕獲和視頻寫入對象。

正如您所看到的,我們正在獲取視頻元數據以創建視頻捕獲對象。如果您想以 mp4 格式寫入,請將編碼器改為 *’mp4v’。有關視頻寫入器和處理編碼器的更直觀指南,請查看有關 [OpenCV 視頻寫入器](https://learnopencv.com/
reading-and-writing-videos-using-opencv/)的文章。

# 對于網絡攝像頭輸入,將文件名替換為 0。
file_name = 'input.mp4'
cap = cv2.VideoCapture(file_name)# 元數據。
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_size = (width, height)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')# 視頻寫入器。
video_output = cv2.VideoWriter('output.mp4', fourcc, fps, frame_size)

6.2 身體姿勢檢測主循環

Pose() 解決方案的可配置 API 不需要太多調整。默認值足以檢測姿勢關鍵點。然而,如果我們要生成分割掩碼,則必須將 ENABLE_SEGMENTATION 標志設置為 True。以下是 MediaPipe 姿勢解決方案中的一些可配置 API。

  • STATIC_IMAGE_MODE:這是一個布爾值。如果設置為 True,則會對每個輸入圖像運行人物檢測。對于視頻來說,這不是必要的,因為在視頻中,檢測運行一次后會跟隨關鍵點跟蹤。默認值為 False

  • MODEL_COMPLEXITY:默認值為 1。它可以是 0、1 或 2。如果選擇更高的復雜性,則推理時間會增加。

  • ENABLE_SEGMENTATION:如果設置為 True,則解決方案會生成一個分割掩碼以及姿勢關鍵點。默認值為 False

  • MIN_DETECTION_CONFIDENCE:范圍為 [0.0 – 1.0]。顧名思義,這是檢測被認為有效的最低置信度值。默認值為 0.5。

  • MIN_TRACKING_CONFIDENCE:范圍為 [0.0 – 1.0]。這是關鍵點被認為被跟蹤的最低置信度值。默認值為 0.5。

通常,默認值表現良好。因此,我們沒有在 mp_pose.Pose() 中傳遞任何參數。以下部分涉及處理 RGB 幀,以便我們稍后從中提取姿勢關鍵點。最后,我們將圖像轉換回 OpenCV 友好的 BGR 顏色空間。

# 捕獲幀。
success, image = cap.read()
if not success:print("Null.Frames")break
# 獲取 fps。
fps = cap.get(cv2.CAP_PROP_FPS)
# 獲取幀的高度和寬度。
h, w = image.shape[:2]# 將 BGR 圖像轉換為 RGB。
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 處理圖像。
keypoints = pose.process(image)# 將圖像轉換回 BGR。
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

6.3 獲取身體姿勢關鍵點坐標

解決方案輸出對象的 pose_landmarks 屬性提供了關鍵點的歸一化 x 和 y 坐標。因此,要獲取實際值,我們需要分別將輸出乘以圖像的 寬度高度

關鍵點的 LEFT_SHOULDER‘RIGHT_SHOULDER’ 等是 PoseLandmark 類的屬性。要獲取歸一化坐標,我們使用以下語法。

norm_coordinate = pose.process(image).pose_landmark.landmark[MediaPipe.solutions.pose.PoseLandmark.<SPECIFIC_LANDMARK>].coordinate

為了簡化表示,我們使用以下簡寫方法。

# 使用 lm 和 lmPose 作為以下方法的簡寫。
lm = keypoints.pose_landmarks
lmPose = mp_pose.PoseLandmark
# 左肩膀。
l_shldr_x = int(lm.landmark[lmPose.LEFT_SHOULDER].x * w)
l_shldr_y = int(lm.landmark[lmPose.LEFT_SHOULDER].y * h)# 右肩膀。
r_shldr_x = int(lm.landmark[lmPose.RIGHT_SHOULDER].x * w)
r_shldr_y = int(lm.landmark[lmPose.RIGHT_SHOULDER].y * h)# 左耳。
l_ear_x = int(lm.landmark[lmPose.LEFT_EAR].x * w)
l_ear_y = int(lm.landmark[lmPose.LEFT_EAR].y * h)# 左臀部。
l_hip_x = int(lm.landmark[lmPose.LEFT_HIP].x * w)
l_hip_y = int(lm.landmark[lmPose.LEFT_HIP].y * h)

6.4 對齊相機

這是為了確保相機能夠正確地拍攝到人的側視圖。我們測量左肩點和右肩點之間的水平距離。在正確對齊的情況下,左點和右點應該幾乎重合。

請注意,偏移距離閾值是基于對具有與視頻樣本完全相同尺寸的數據集進行分析的結果得出的。如果您嘗試使用更高分辨率的樣本,這個值將會改變。它不需要非常精確,您可以根據自己的直覺設置一個閾值。

實際上,距離方法根本不是確定對齊的正確方式。它應該是基于角度的。

我們為了簡化而使用距離方法。

# 計算左肩點和右肩點之間的距離。
offset = findDistance(l_shldr_x, l_shldr_y, r_shldr_x, r_shldr_y)# 協助對齊相機以拍攝人的側視圖。
# 偏移閾值 30 是基于對 100 個樣本進行分析的結果得出的。
if offset < 100:cv2.putText(image, str(int(offset)) + ' Aligned', (w - 150, 30), font, 0.9, green, 2)
else:cv2.putText(image, str(int(offset)) + ' Not Aligned', (w - 150, 30), font, 0.9, red, 2)

6.5 計算身體姿勢傾斜角度并繪制關鍵點

使用預先定義的 findAngle 函數獲得傾斜角度。如下所示繪制關鍵點及其連接線。

# 計算角度。
neck_inclination = findAngle(l_shldr_x, l_shldr_y, l_ear_x, l_ear_y)
torso_inclination = findAngle(l_hip_x, l_hip_y, l_shldr_x, l_shldr_y)# 繪制關鍵點。
cv2.circle(image, (l_shldr_x, l_shldr_y), 7, yellow, -1)
cv2.circle(image, (l_ear_x, l_ear_y), 7, yellow, -1)# 為了顯示的優雅,我們取 x1 的 y 坐標上方 100px 處。
# 雖然我們在計算 P1、P2、P3 之間的角度時取 y = 0。
cv2.circle(image, (l_shldr_x, l_shldr_y - 100), 7, yellow, -1)
cv2.circle(image, (r_shldr_x, r_shldr_y), 7, pink, -1)
cv2.circle(image, (l_hip_x, l_hip_y), 7, yellow, -1)# 同樣地,這里我們取 x1 的 y 坐標上方 100px。注意
# 您可以取任意值作為 y,不一定是 100 或 200 像素。
cv2.circle(image, (l_hip_x, l_hip_y - 100), 7, yellow, -1)# 添加文本,姿勢和角度傾斜。
# 文本字符串用于顯示。
angle_text_string = 'Neck : ' + str(int(neck_inclination)) + '  Torso : ' + str(int(torso_inclination))

6.6 身體姿勢檢測條件語句

根據姿勢是良好還是不良,顯示結果。再次強調,閾值角度是基于直覺的。您可以根據需要設置閾值。每次檢測時,分別增加良好姿勢和不良姿勢的幀計數器。

通過將幀數除以fps可以計算出特定姿勢的時間。有關 fps 測量方法,請查看我們之前的博客文章。

# 判斷是良好姿勢還是不良姿勢。
# 閾值角度是基于直覺設置的。
if neck_inclination < 40 and torso_inclination < 10:bad
_frames = 0good_frames += 1cv2.putText(image, angle_text_string, (10, 30), font, 0.9, light_green, 2)cv2.putText(image, str(int(neck_inclination)), (l_shldr_x + 10, l_shldr_y), font, 0.9, light_green, 2)cv2.putText(image, str(int(torso_inclination)), (l_hip_x + 10, l_hip_y), font, 0.9, light_green, 2)# 連接關鍵點。cv2.line(image, (l_shldr_x, l_shldr_y), (l_ear_x, l_ear_y), green, 4)cv2.line(image, (l_shldr_x, l_shldr_y), (l_shldr_x, l_shldr_y - 100), green, 4)cv2.line(image, (l_hip_x, l_hip_y), (l_shldr_x, l_shldr_y), green, 4)cv2.line(image, (l_hip_x, l_hip_y), (l_hip_x, l_hip_y - 100), green, 4)else:good_frames = 0bad_frames += 1cv2.putText(image, angle_text_string, (10, 30), font, 0.9, red, 2)cv2.putText(image, str(int(neck_inclination)), (l_shldr_x + 10, l_shldr_y), font, 0.9, red, 2)cv2.putText(image, str(int(torso_inclination)), (l_hip_x + 10, l_hip_y), font, 0.9, red, 2)# 連接關鍵點。cv2.line(image, (l_shldr_x, l_shldr_y), (l_ear_x, l_ear_y), red, 4)cv2.line(image, (l_shldr_x, l_shldr_y), (l_shldr_x, l_shldr_y - 100), red, 4)cv2.line(image, (l_hip_x, l_hip_y), (l_shldr_x, l_shldr_y), red, 4)cv2.line(image, (l_hip_x, l_hip_y), (l_hip_x, l_hip_y - 100), red, 4)# 計算保持特定姿勢的時間。
good_time = (1 / fps) * good_frames
bad_time = (1 / fps) * bad_frames# 姿勢時間。
if good_time > 0:time_string_good = 'Good Posture Time : ' + str(round(good_time, 1)) + 's'cv2.putText(image, time_string_good, (10, h - 20), font, 0.9, green, 2)
else:time_string_bad = 'Bad Posture Time : ' + str(round(bad_time, 1)) + 's'cv2.putText(image, time_string_bad, (10, h - 20), font, 0.9, red, 2)# 如果保持不良姿勢超過 3 分鐘(180 秒),發送警報。
if bad_time > 180:sendWarning()

結論

以上就是使用 MediaPipe Pose 構建姿勢校正應用的全部內容。在本篇博文中,我們討論了如何實現 MediaPipe Pose 以檢測人體姿勢。您了解了如何獲取姿勢關鍵點、可配置 API、輸出等。希望本篇博文幫助您了解了 MediaPipe 姿勢的基礎知識,并為您的下一個項目帶來一些新的想法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/79284.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/79284.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/79284.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LSTM結合LightGBM高緯時序預測

1. LSTM 時間序列預測 LSTM 是 RNN&#xff08;Recurrent Neural Network&#xff09;的一種變體&#xff0c;它解決了普通 RNN 訓練時的梯度消失和梯度爆炸問題&#xff0c;適用于長期依賴的時間序列建模。 LSTM 結構 LSTM 由 輸入門&#xff08;Input Gate&#xff09;、遺…

六、adb通過Wifi連接

背景 收集是榮耀X40,數據線原裝全新的&#xff0c;USB連上之后&#xff0c;老是斷&#xff0c;電腦一直叮咚叮咚的響個不停&#xff0c;試試WIFI 連接是否穩定&#xff0c;需要手機和電腦用相同的WIFI. 連接 1.通過 USB 連接手機和電腦(打開USB調試等這些都略過) adb device…

如何理解前端開發中的“換皮“

"換皮"在前端開發中是一個常見的術語&#xff0c;通常指的是在不改變網站或應用核心功能和結構的情況下&#xff0c;只改變其外觀和視覺表現。以下是關于前端"換皮"的詳細理解&#xff1a; 基本概念 定義&#xff1a;換皮(Skinning)是指保持應用程序功能不…

從 Vue 到 React:深入理解 useState 的異步更新

目錄 從 Vue 到 React&#xff1a;深入理解 useState 的異步更新與函數式寫法1. Vue 的響應式回顧&#xff1a;每次賦值立即生效2. React 的狀態更新是異步且批量的原因解析 3. 函數式更新&#xff1a;唯一的正確寫法4. 對比 Vue vs React 狀態更新5. React useState 的核心源碼…

使用Redis實現分布式限流

一、限流場景與算法選擇 1.1 為什么需要分布式限流 在高并發系統中&#xff0c;API接口的突發流量可能導致服務雪崩。傳統的單機限流方案在分布式環境下存在局限&#xff0c;需要借助Redis等中間件實現集群級流量控制。 1.2 令牌桶算法優勢 允許突發流量&#xff1a;穩定速…

快速搭建WordPress網站的主題

WP快主題(wpkuai.com )是一款由知名WordPress專業團隊打造的專業化WordPress主題&#xff0c;旨在讓用戶使用該wordpress主題快速搭建網站。 WP快主題專注于快速搭建WordPress網站的主題解決方案。其主題設計注重簡潔性與高效性&#xff0c;旨在幫助用戶快速完成網站的搭建和部…

STM32江科大----------PID算法

聲明&#xff1a;本人跟隨b站江科大學習&#xff0c;本文章是觀看完視頻后的一些個人總結和經驗分享&#xff0c;也同時為了方便日后的復習&#xff0c;如果有錯誤請各位大佬指出&#xff0c;如果對你有幫助可以點個贊小小鼓勵一下&#xff0c;本文章建議配合原視頻使用?? 如…

將JSON格式的SQL查詢轉換為完整SQL語句的實戰解析

一、背景與需求 在現代數據處理中,JSON格式因其靈活性和可讀性,常被用于定義SQL查詢的結構。然而,直接編寫JSON格式的SQL指令后,如何將其轉換為可執行的SQL語句是開發者常遇到的挑戰。本文將通過一個Python函數和多個實際案例,解析如何將JSON結構轉換為完整的SQL語句,并…

java CountDownLatch用法簡介

CountDownLatch倒計數鎖存器 CountDownLatch&#xff1a;用于協同控制一個或多個線程等待在其他線程中執行的一組操作完成&#xff0c;然后再繼續執行 CountDownLatch用法 構造方法&#xff1a;CountDownLatch(int count)&#xff0c;count指定等待的條件數&#xff08;任務…

Leetcode - 雙周賽135

目錄 一、3512. 使數組和能被 K 整除的最少操作次數二、3513. 不同 XOR 三元組的數目 I三、3514. 不同 XOR 三元組的數目 II四、3515. 帶權樹中的最短路徑 一、3512. 使數組和能被 K 整除的最少操作次數 題目鏈接 本題實際上求的就是數組 nums 和的余數&#xff0c;代碼如下&…

【后端】【python】利用反射器----動態設置裝飾器

&#x1f4d8; Python 裝飾器進階指南 一、裝飾器本質 ? 本質概念 Python 裝飾器的本質是 函數嵌套 返回函數&#xff0c;它是對已有函數的增強&#xff0c;不修改原函數代碼&#xff0c;使用語法糖 decorator 實現包裹效果。 def my_decorator(func):def wrapper(*args, …

Nodejs Express框架

參考&#xff1a;Node.js Express 框架 | 菜鳥教程 第一個 Express 框架實例 接下來我們使用 Express 框架來輸出 "Hello World"。 以下實例中我們引入了 express 模塊&#xff0c;并在客戶端發起請求后&#xff0c;響應 "Hello World" 字符串。 創建 e…

Docker Swarm 集群

Docker Swarm 集群 本文檔介紹了 Docker Swarm 集群的基本概念、工作原理以及相關命令使用示例&#xff0c;包括如何在服務調度中使用自定義標簽。本文檔適用于需要管理和擴展 Docker 容器化應用程序的生產環境場景。 1. 什么是 Docker Swarm Docker Swarm 是用于管理 Docker…

充電寶項目中的MQTT(輕量高效的物聯網通信協議)

文章目錄 補充&#xff1a;HTTP協議MQTT協議MQTT的核心特性MQTT vs HTTP&#xff1a;關鍵對比 EMQX項目集成EMQX集成配置客戶端和回調方法具體接口和方法處理處理類 補充&#xff1a;HTTP協議 HTTP是一種應用層協議&#xff0c;使用TCP作為傳輸層協議&#xff0c;默認端口是80…

【iOS】UIPageViewController學習

UIPageViewController學習 前言創建一個UIPageViewController最簡單的使用 UIPageViewController的方法說明&#xff1a;效果展示 UIPageViewController的協議方法 前言 筆者最近在寫項目時想實現一個翻書效果&#xff0c;上網學習到了UIPageViewController今天寫本篇博客總結…

Linux搭建環境:從零開始掌握基礎操作(四)

? ? 您好&#xff0c;我是程序員小羊&#xff01; 前言 軟件測試第一步就是搭建測試環境&#xff0c;如何搭建好測試環境&#xff0c;需要具備兩項的基礎知識&#xff1a; 1、Linux 命令: 軟件測試第一個任務, 一般都需要進行環境搭建, 一部分&#xff0c;環境搭建內容是在服…

一天一個java知識點----Tomcat與Servlet

認識BS架構 靜態資源&#xff1a;服務器上存儲的不會改變的數據&#xff0c;通常不會根據用戶的請求而變化。比如&#xff1a;HTML、CSS、JS、圖片、視頻等(負責頁面展示) 動態資源&#xff1a;服務器端根據用戶請求和其他數據動態生成的&#xff0c;內容可能會在每次請求時都…

YOLOV8 OBB 海思3516訓練流程

YOLOV8 OBB 海思3516訓練流程 目錄 1、 下載帶GPU版本的torch(可選) 1 2、 安裝 ultralytics 2 3、 下載pycharm 社區版 2 4、安裝pycharm 3 5、新建pycharm 工程 3 6、 添加conda 環境 4 7、 訓練代碼 5 9、配置Ymal 文件 6 10、修改網絡結構 9 11、運行train.py 開始訓練模…

【深度學習】花書第18章——配分函數

直面配分函數 許多概率模型&#xff08;通常是無向圖模型&#xff09;由一個未歸一化的概率分布 p ~ ( x , θ ) \tilde p(\mathbf x,\theta) p~?(x,θ)定義。我們必須通過除以配分函數 Z ( θ ) Z(\pmb{ \theta}) Z(θ)來歸一化 p ~ \tilde p p~?。以獲得一個有效的概率分…

工作記錄1

日常總結、靈感記錄、學習要點。持續記錄 學海無涯,再好的記性也比不過爛筆頭,記錄一下學習日常、靈感、要點。 前言:最近看見一個博文,很有感觸,是某個大佬自己運營的網站,分享了他的各種經驗文章和自身的一些筆記。本人還沒有他這么屌,所以還是先在CSDN上小試牛刀吧…