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()

🟡 小小挑戰一下

大家可以嘗試進行一些修改或改良喔! 例如:
✌️嘗試修改成深蹲運動小遊戲吧。

😁 明天見!

按這裏回到 Python + AI 的 21 天挑戰