Day14 運動遊戲
🟡 先看看程式的效果
今天的程式是利用 OpenCV 和 MediaPipe 來計算某種運動的重複次數, 例如這個是手臂彎曲運動的比賽, 只要設定使用不同的 Landmarks 便可以修改成不同的運動小遊戲。
Today’s program uses OpenCV and MediaPipe to count repetitions of a certain exercise, such as an arm curl competition. By setting different landmarks, it can be modified into different exercise mini-games.
🟡 學習目標
標記指定個關鍵點(可能是手肘)
計算關鍵點之間的角度(可能是上臂、前臂和手的角度)
根據角度繪製進度條
根據角度變化計算運動次數
🟡 程式碼
請先下載 “運動遊戲.py”
請按此下載
'''
Python + AI in 21 days
https://jasonworkshop.com/python
Designed by Jason Workshop
[請勿修改以上內容]
---
預備工作:
首先,請確保已安裝 opencv-python, mediapipe 模組
如不確定可直接在 Windows 的 cmd prompt 執行以下指定
pip install opencv-python mediapipe
'''
import cv2
import mediapipe as mp
import math
# 初始化 MediaPipe Pose 模組
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(static_image_mode=False, model_complexity=1, enable_segmentation=False, min_detection_confidence=0.5) # min_detection_confidence 為最低可信度
mp_drawing = mp.solutions.drawing_utils
dir = 0
count = 0
def calculate_angle(point1, point2, point3):
# 計算兩個向量的座標差值
vector1 = [point2[0] - point1[0], point2[1] - point1[1]]
vector2 = [point3[0] - point2[0], point3[1] - point2[1]]
# 計算兩個向量的長度
length1 = math.sqrt(vector1[0] ** 2 + vector1[1] ** 2)
length2 = math.sqrt(vector2[0] ** 2 + vector2[1] ** 2)
# 計算兩個向量的內積
dot_product = vector1[0] * vector2[0] + vector1[1] * vector2[1]
# 計算夾角的弧度值
angle_radians = math.acos(dot_product / (length1 * length2))
# 將弧度轉換為角度
angle_degrees = math.degrees(angle_radians)
return angle_degrees
# 開啟攝影機
cap = cv2.VideoCapture(0)
# 設定螢幕解析度
screen_width, screen_height = 960, 540
# 創建一個視窗
cv2.namedWindow('JCCSSYL Sports', cv2.WINDOW_NORMAL)
# 設定視窗屬性為全屏
#cv2.setWindowProperty('JCCSSYL Sports', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
# 調整視窗大小以符合解析度
cv2.resizeWindow('JCCSSYL Sports', screen_width, screen_height)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 轉換 BGR 圖像到 RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 偵測身體姿勢
result = pose.process(rgb_frame)
if result.pose_landmarks:
#"""
# 繪畫身體關鍵點和連線
mp_drawing.draw_landmarks(
image=frame,
landmark_list=result.pose_landmarks,
connections=mp_pose.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2, circle_radius=2),
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 255, 255), thickness=1, circle_radius=2))
# 在指定關鍵點位置繪製紅色圓形
landmark_index = 13 # 指定關鍵點編號
landmark = result.pose_landmarks.landmark[landmark_index]
landmark_x = int(landmark.x * frame.shape[1])
landmark_y = int(landmark.y * frame.shape[0])
cv2.circle(frame, (landmark_x, landmark_y), 10, (0, 0, 255), -1)
#"""
# 計算11、13和15的夾角角度
point1 = [result.pose_landmarks.landmark[11].x, result.pose_landmarks.landmark[11].y]
point2 = [result.pose_landmarks.landmark[13].x, result.pose_landmarks.landmark[13].y]
point3 = [result.pose_landmarks.landmark[15].x, result.pose_landmarks.landmark[15].y]
angle = 180 - calculate_angle(point1, point2, point3)
#print(angle)
# 顯示進度條
bar_width = int((180 - angle) / 180 * 400)
color = int(angle / 180 * 255)
cv2.rectangle(frame, (150, 30), (150 + bar_width, 65), (0, color, 255), -1)
# 計算及顯示分數
if angle >= 120:
if dir == 0: # 放鬆
count = count + 0.5
dir = 1
if angle <= 60:
if dir == 1: # 收縮
count = count + 0.5
dir = 0
cv2.putText(frame, str(int(count)), (40, 70), cv2.FONT_HERSHEY_SIMPLEX, 2, (255, 255, 255), 5)
# 顯示圖像
cv2.imshow('JCCSSYL Sports', frame)
if cv2.waitKey(1) & 0xFF == ord("r"): # 按下 R 重設分數
count = 0
if cv2.waitKey(1) & 0xFF == 27: # 按下 ESC 鍵退出
break
# 釋放攝影機並關閉所有視窗
cap.release()
cv2.destroyAllWindows()
🟡 小小挑戰一下
大家可以嘗試進行一些修改或改良喔! 例如:
✌️嘗試修改成深蹲運動小遊戲吧。
😁 明天見!