由于最近虛擬現(xiàn)實(shí)和增強(qiáng)現(xiàn)實(shí)的增長(zhǎng)和普及,人體姿態(tài)估計(jì)技術(shù)變得越來(lái)越流行。在我們之前的教程中,我們使用了 Media pipe Hand Tracking 模塊來(lái)使用手勢(shì)控制 Raspberry Pi 媒體播放器。在這個(gè)項(xiàng)目中,我們將使用MediaPipe Pose 估計(jì)包來(lái)構(gòu)建一個(gè)俯臥撐。我們將使用姿勢(shì)估計(jì)包來(lái)獲取肩膀和肘部的坐標(biāo)。肩部坐標(biāo)將與肘部坐標(biāo)進(jìn)行比較。因此,如果肩膀與肘部處于同一水平或低于肘部,則將被視為俯臥撐。我們還將揚(yáng)聲器連接到 Raspberry Pi 以告知俯臥撐的計(jì)數(shù)。
俯臥撐計(jì)數(shù)所需的組件
樹(shù)莓派 4
Pi 相機(jī)模塊
揚(yáng)聲器
在這里,我們只需要安裝了 OpenCV 和 MediaPipe 的 Raspberry Pi 4 和 Pi 攝像頭模塊。 OpenCV 用于 數(shù)字圖像處理,而 MediaPipe 用于 Pose 估計(jì)。數(shù)字圖像處理最常見(jiàn)的應(yīng)用是 物體檢測(cè)、 人臉識(shí)別和 人數(shù)統(tǒng)計(jì)。
什么是媒體管道?
MediaPipe是一個(gè)框架,用于構(gòu)建跨平臺(tái)(即 Android、iOS、Web、邊緣設(shè)備)多模式(即視頻、音頻或任何時(shí)間序列數(shù)據(jù))應(yīng)用機(jī)器學(xué)習(xí)管道,包括快速 ML 推理、經(jīng)典計(jì)算機(jī)視覺(jué)和媒體處理(例如視頻解碼)。MediaPipe 發(fā)布了各種預(yù)構(gòu)建的 Python 和其他語(yǔ)言包,例如:
物體檢測(cè)
人臉檢測(cè)
手部追蹤
姿勢(shì)估計(jì)
多手追蹤
頭發(fā)分割
MediaPipePython 包可用于 Linux、macOS 和 Windows 的 PyPI。使用以下命令在 Raspberry Pi 4 上安裝 MediaPipe:
須藤 pip3 安裝 mediapipe-rpi4
如果您沒(méi)有 Pi 4,則可以使用以下命令將其安裝在 Raspberry Pi 3 上:
須藤 pip3 安裝 mediapipe-rpi3
安裝 OpenCV
在安裝 OpenCV 和其他依賴(lài)項(xiàng)之前,Raspberry Pi 需要完全更新。使用以下命令將 Raspberry Pi 更新到其最新版本:
sudo apt-get 更新
然后使用以下命令安裝在 Raspberry Pi 上安裝 OpenCV 所需的依賴(lài)項(xiàng)。
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
為上推計(jì)數(shù)器編程 Raspberry Pi
文檔末尾給出了使用 OpenCV 上推計(jì)數(shù)器的完整代碼。在這里,我們將解釋代碼的重要部分以便更好地解釋。
通過(guò)導(dǎo)入 OpenCV 和 MediaPipe 包來(lái)啟動(dòng)代碼。如前所述,MediaPipe 是 Pose 估計(jì)的核心包,而 OpenCV 用于圖像處理。
導(dǎo)入簡(jiǎn)歷2 將媒體管道導(dǎo)入為 mp
在接下來(lái)的幾行中,我們創(chuàng)建了兩個(gè)新變量。第一個(gè)是mp_drawing,它將用于從 MediaPipe Python 包中獲取所有繪圖實(shí)用程序,第二個(gè)是mp_pose,用于導(dǎo)入姿勢(shì)估計(jì)模型。
mp_drawing = mp.solutions.drawing_utils mp_pose = mp.solutions.pose
之后,定義一個(gè)名為findPosition() 的函數(shù)。顧名思義,它用于查找所有身體標(biāo)志的 X、Y 坐標(biāo),如肩膀、眼睛等。所有標(biāo)志的坐標(biāo)將存儲(chǔ)在名為lmList[]的變量中。
def findPosition(圖像,繪制=真): lmList = [] 如果結(jié)果.pose_landmarks: mp_drawing.draw_landmarks( 圖像,results.pose_landmarks,mp_pose.POSE_CONNECTIONS) 對(duì)于 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 攝像頭開(kāi)始視頻流。您甚至可以使用預(yù)先錄制的視頻,而不是使用實(shí)時(shí)流。為此,將“0”替換為視頻路徑。
上限 = cv2.VideoCapture(0) #cap = cv2.VideoCapture(“我的視頻.mp4”)
然后在下一行,為 mediapipe feed 設(shè)置一個(gè)新實(shí)例,以訪問(wèn)我們之前導(dǎo)入的姿勢(shì)估計(jì)模型。我們還傳遞了兩個(gè)關(guān)鍵字參數(shù),即最小檢測(cè)置信度和最小跟蹤置信度。接下來(lái),我們將讀取視頻幀并將它們存儲(chǔ)在 image 變量中。
以 mp_pose.Pose(min_detection_confidence=0.7, min_tracking_confidence=0.7) 作為姿勢(shì):
而 cap.isOpened(): 成功,圖像 = cap.read() 圖像 = cv2.resize(圖像, (1280, 720))
我們從視頻源獲得的圖像最初是 BGR 格式。S,在這一行中,我們將首先將圖像水平翻轉(zhuǎn)以供稍后的自拍視圖顯示,然后將BGR圖像轉(zhuǎn)換為RGB。
圖像 = cv2.cvtColor(cv2.flip(圖像, 1), cv2.COLOR_BGR2RGB)
之后,我們將通過(guò)姿勢(shì)估計(jì)模型傳遞圖像以進(jìn)行檢測(cè)并將結(jié)果存儲(chǔ)在一個(gè)名為“結(jié)果”的變量中。然后我們將 BGR 圖像轉(zhuǎn)換為 RGB。
結(jié)果=姿勢(shì).過(guò)程(圖像) 圖像 = cv2.cvtColor(圖像,cv2.COLOR_BGR2RGB)
現(xiàn)在我們得到了檢測(cè)結(jié)果,我們將調(diào)用findPosition()函數(shù)在圖像上繪制這些檢測(cè),并使用我們之前導(dǎo)入的繪圖實(shí)用程序連接所有檢測(cè)。
mp_drawing.draw_landmarks(圖像,results.pose_landmarks,mp_pose.POSE_CONNECTIONS)
之后,我們會(huì)將所有檢測(cè)的 id 和坐標(biāo)存儲(chǔ)在一個(gè)名為 lmList 的變量中。
lmList = findPosition(圖像,繪制=真)
然后在接下來(lái)的幾行中,我們將首先獲取兩個(gè)肩膀的坐標(biāo),然后在上面畫(huà)圓。如下圖所示,左右肩的 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)
現(xiàn)在,當(dāng)我們找到肩膀的坐標(biāo)時(shí),我們將把這些坐標(biāo)與兩個(gè)肘部的坐標(biāo)進(jìn)行比較。所以,如果肩膀與肘部在同一水平或在肘部以下,我們將階段設(shè)置為“向下”,當(dāng)肩膀在肘部以上時(shí),階段將變?yōu)橄蛏希@將被視為俯臥撐.
如果(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": 階段=“向上” 計(jì)數(shù)器 += 1 打?。ㄓ?jì)數(shù)器)
現(xiàn)在,隨著俯臥撐的計(jì)數(shù),我們將使用cv2.putText() 在視頻流上打印俯臥撐計(jì)數(shù)。
text = "{}:{}".format("俯臥撐", counter) cv2.putText(圖像, 文本, (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2) cv2.imshow('MediaPipe 姿勢(shì)', image)
最后,我們將視頻流保存在我們的項(xiàng)目文件夾中。
如果創(chuàng)建是無(wú): fourcc = cv2.VideoWriter_fourcc(*'XVID') create = cv2.VideoWriter(opname,fourcc, 30, (image.shape[1], image.shape[0]), True) create.write(圖像)
測(cè)試上推計(jì)數(shù)器腳本
現(xiàn)在 Push Up 計(jì)數(shù)器腳本已準(zhǔn)備就緒,讓我們繼續(xù)測(cè)試它。有兩種測(cè)試方法。您可以使用預(yù)先錄制的視頻,也可以使用 Pi 相機(jī)進(jìn)行實(shí)時(shí)視頻流。在這里,我們將使用 Pi 攝像頭模塊,因此,將 Raspberry Pi 攝像頭模塊與 Pi 連接,如下所示:
現(xiàn)在,檢查 Pi 相機(jī)是否正常工作。查看相機(jī)后,啟動(dòng) python 腳本,您會(huì)發(fā)現(xiàn)彈出一個(gè)窗口,其中包含您的視頻源。最初做俯臥撐時(shí),肩膀上的點(diǎn)將是紅色:
當(dāng)肩膀低于肘部時(shí),點(diǎn)將從紅色變?yōu)榫G色,俯臥撐計(jì)數(shù)將增加一。
這就是您可以使用 MediaPipe 計(jì)算俯臥撐的方法。您還可以使用此庫(kù)來(lái)計(jì)算仰臥起坐或任何其他鍛煉。下面給出了完整的工作視頻和代碼。
代碼
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) 作為姿勢(shì):
而 cap.isOpened():
成功,image = cap.read()
image = cv2.resize(image, (640,480))
if not success:
print(“Ignoring empty camera frame.”)
# 如果加載視頻,使用 ‘break’ 而不是 ‘continue’。
continue
# 水平翻轉(zhuǎn)圖像以供稍后的自拍視圖顯示,并將
# BGR 圖像轉(zhuǎn)換為 RGB。
image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
# 為了提高性能,可選擇將圖像標(biāo)記為不可寫(xiě)入
# 通過(guò)引用傳遞。
results = pose.process(image)
# 在圖像上繪制姿態(tài)標(biāo)注。
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` 鍵被按下,則退出循環(huán)
if key == ord(“q”):
break
# 做一些清理
cv2.destroyAllWindows()
-
揚(yáng)聲器
+關(guān)注
關(guān)注
29文章
1307瀏覽量
63084 -
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2256瀏覽量
94699
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論