2022年南港瓶蓋工廠所舉行的Maker Faire 中,在公司的攤位上,最受到矚目與歡迎的,就是Google MediaPipe 的深蹲偵測互動遊戲了,達到一定的深蹲次數不僅買徽章可以折價,還可以免費獲得摩艾石像3D列印一個,吸引了很多大小朋友,一起來看看當時熱鬧的畫面吧!
原理及功能說明
本次的深蹲互動專題,以這篇[累累累] Google Mediapipe 深蹲偵測,結合 Arduino 首次接觸就上手所改編的,一樣著重於偵測身體大腿及小腿的角度小於一定的數值,將深蹲次數顯示出來外,並取消了和Arduino的連線。而MediaPipe已經將人體簡化成由點和線所構成的人體線條骨架的樣貌,如下圖所示。
由上圖所示,就是偵測左腿點23(left_hip)到點25(left _knee)的直線距離,和點25(left _knee)到點27(left _ankle)的直線距離,兩條直線所產生的夾角小於120;右腿則是點24(right_hip)到點26(right _knee)的直線距離,和點28(right _knee)到點27(right _ankle)的直線距離,兩條直線所產生的夾角小於120。
也就是說,當左右腳膝蓋彎曲角度小於120度時,字體顏色就會變綠色。只要螢幕上顯示的字體「Left Angle」、「Right Angle」、「Pose」皆為綠色時,次數會加1。
再來是遊戲互動體驗,筆者使用了一顆超大的Enter鍵,當程式開始執行時,畫面會出現指示「Press Enter To Start」,Enter鍵按下去後就可以開始深蹲遊戲,此時就會開始計算深蹲次數。再按一次Enter鍵,次數會歸零,同時又會回到「Press Enter To Start」的畫面。而筆者在每次計算深蹲次數後,都會加上音效,以確保動作完成,如以下所示。
再來是場地架設,以當時在Maker Faire南港瓶蓋工廠場地為例,筆者使用的是羅技C270的攝影鏡頭,鏡頭距離地面高度需2公尺,距離人的身體約3公尺,才能偵測得到全身骨架畫面,由下圖所示。
程式撰寫
由於程式過長,故說明重要片段,如下列程式所示。
以下程式為影像訊號來源。第1行為攝影機訊號;第2行可撥放影片。
cam = cv2.VideoCapture(3)
#cam=cv2.VideoCapture("POSE1.mp4")
以下程式為計算身體比率函式。
def get_body_ratio(landmarks):
r_body = abs(landmarks[mppose.PoseLandmark["RIGHT_SHOULDER"].value].y
- landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].y)
l_body = abs(landmarks[mppose.PoseLandmark["LEFT_SHOULDER"].value].y
- landmarks[mppose.PoseLandmark["LEFT_HIP"].value].y)
avg_body = (r_body + l_body) / 2
r_leg = abs(landmarks[mppose.PoseLandmark["RIGHT_HIP"].value].y
- landmarks[mppose.PoseLandmark["RIGHT_ANKLE"].value].y)
l_leg = abs(landmarks[mppose.PoseLandmark["LEFT_HIP"].value].y
- landmarks[mppose.PoseLandmark["LEFT_ANKLE"].value].y)
if r_leg > l_leg:
return r_leg / avg_body
else:
return l_leg / avg_body
以下程式為計算左右腳的角度函式。
def get_knee_angle(landmarks):
r_hip = get_landmark(landmarks, "RIGHT_HIP")
l_hip = get_landmark(landmarks, "LEFT_HIP")
r_knee = get_landmark(landmarks, "RIGHT_KNEE")
l_knee = get_landmark(landmarks, "LEFT_KNEE")
r_ankle = get_landmark(landmarks, "RIGHT_ANKLE")
l_ankle = get_landmark(landmarks, "LEFT_ANKLE")
r_angle = calc_angles(r_hip, r_knee, r_ankle)
l_angle = calc_angles(l_hip, l_knee, l_ankle)
#print(r_hip)
m_hip = (r_hip + l_hip)
m_hip = [x / 2 for x in m_hip]
m_knee = (r_knee + l_knee)
m_knee = [x / 2 for x in m_knee]
m_ankle = (r_ankle + l_ankle)
m_ankle = [x / 2 for x in m_ankle]
mid_angle = calc_angles(m_hip, m_knee, m_ankle)
return [int(r_angle),int(l_angle) ,int(mid_angle)]
以下程式為計算按下Enter鍵的次數,可觸發遊戲開始。
if (read_dir_key == 13):
sport['count'] = 0
run_flag += 1
if (run_flag == 2):
run_flag = 0
以下程式為左腳膝蓋彎曲角度之顏色顯示。小於120度綠色,介於120~130度黃色,大於130度紅色。
而檔案POSE_entercount_sound.py中的第226~248行則為顯示右腳膝蓋彎曲角度之顏色顯示。
if knee_angles[0] < 120:
cv2.putText(preview, "Left Angle: {:d}".format(knee_angles[0]), (400, 360)
, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (82, 169, 255), 4, cv2.LINE_AA)
cv2.putText(preview, "Left Angle: {:d}".format(knee_angles[0]), (400, 360)
, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 1, cv2.LINE_AA)
elif knee_angles[0] < 130:
cv2.putText(preview, "Left Angle: {:d}".format(knee_angles[0]), (400, 360)
, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (82, 169, 255), 4, cv2.LINE_AA)
cv2.putText(preview, "Left Angle: {:d}".format(knee_angles[0]), (400, 360)
, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 1, cv2.LINE_AA)
else:
cv2.putText(preview, "Left Angle: {:d}".format(knee_angles[0]), (400, 360)
, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (82, 169, 255), 4, cv2.LINE_AA)
cv2.putText(preview, "Left Angle: {:d}".format(knee_angles[0]), (400, 360)
, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 1, cv2.LINE_AA)
決定深蹲的次數,如果次數已加1,則會撥放音效。
if status:
if avg_angle > 160:
status = False
pass_time = time.time() - start_time
start_time = 0
if 3000 > pass_time > 0.5:
sport['count'] = sport['count'] + 1
while (pygame.mixer.music.get_busy()!=1):
pygame.mixer.music.load('coin05.mp3')
pygame.mixer.music.play()
sport['calories'] = sport['calories'] + int(0.66 * pass_time)
logger(sport['count'], sport['calories'])
tmp = f"a{sport['count']}\n"
#ser.write(str.encode(tmp))
tmp = f"b{sport['calories']}\n"
#ser.write(str.encode(tmp))
#print(pass_time)
else:
if avg_angle < 120 and body_ratio < 1.2:
start_time = time.time()
status = True
在螢幕上顯示”Press Enter To Start”,以開始深蹲遊戲
cv2.putText(preview, "Press Enter To Start", (10, 80)
, cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 0), 5, cv2.LINE_AA)
cv2.putText(preview, "Press Enter To Start", (10, 80)
, cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 2, cv2.LINE_AA)
以上程式傳送門-連結
下載解壓縮後,電腦中需依本文安裝Python的執行環境(操作至圖 21)。
執行程式
cd pose_detection
python POSE_entercount_sound.py
影片展示
以下影片為腦波弱老闆在南港瓶蓋工廠所示範的深蹲畫面,一起來看看吧!
〈Python程式打造Google MediaPipe 深蹲偵測互動遊戲〉這篇文章最早發佈於《CAVEDU教育團隊技術部落格》。