Day04 手勢辨識 (以數字代表手勢) Hand Posture Recognition


🟡 先看看程式的效果

今天的程式是利用 OpenCV 和 MediaPipe 來進行即時的手部偵測, 並判斷每根手指是否伸出, 然後將手指的狀態以二進制及十進制的形式顯示在畫面左上角, 這個程式能將各種手勢轉化為數字, 能大大地降低辨識手勢的難度。

Today’s program uses OpenCV and MediaPipe for real-time hand detection and determines whether each finger is extended. It then displays the state of the fingers in both binary and decimal forms. This program can convert various gestures into numbers, significantly reducing the difficulty of gesture recognition.


🟡 學習目標

認識及使用 OpenCV
OpenCV,全名是 Open Source Computer Vision Library,是一個開源的計算機視覺和機器學習軟體庫。OpenCV 提供了數以千計的演算法,這些演算法可以用來進行影像處理、物體識別、運動追蹤、人臉識別等多種計算機視覺任務。

認識及使用 MediaPipe
MediaPipe 是由 Google 提供的一個開源框架,用於構建多模態機器學習管道。它特別適合處理和分析視覺數據,如視頻和圖像。MediaPipe 提供了許多現成的解決方案和工具,能夠幫助開發者輕鬆實現手勢識別、面部偵測、姿態估計等功能。

二進制及十進制的應用
這個例子能將手勢轉化為 0-31 之間的十進制數字, 在設備之間傳送指令將會更快更簡單!


🟡 程式碼

Hand Posture Recognition 手勢辨識 (以數字代表手勢).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 Hands 模組
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=1, min_detection_confidence=0.7)
# static_image_mode:是否靜態模式,若為 True 則每張圖像都會獨立處理
# max_num_hands:最多手部偵測數量
# min_detection_confidence:手部檢測的最低可信度
mp_drawing = mp.solutions.drawing_utils  # 繪製工具

# 儲存手部關鍵點的 x 和 y 座標
x = [0 for _ in range(20)]
y = [0 for _ in range(20)]


# 計算兩個關鍵點之間的距離
def dist(a, b):
    return math.sqrt((x[a] - x[b]) ** 2 + (y[a] - y[b]) ** 2)


# 檢測手指的狀態並返回二進制表示
def detect(hand_landmarks):
    # 更新手部關鍵點的 x 和 y 座標
    for i in range(0, 20):
        x[i] = hand_landmarks.landmark[i].x
        y[i] = hand_landmarks.landmark[i].y

    # 初始化手指狀態計數
    finger1 = finger2 = finger3 = finger4 = finger5 = 0

    # 檢測各手指是否伸出
    if abs(dist(0, 1) + dist(1, 2) + dist(2, 3) + dist(3, 4) - dist(0, 4)) < 0.025:
        finger1 = 1 # 大拇指

    if abs(dist(5, 6) + dist(6, 7) + dist(7, 8) - dist(5, 8)) < 0.02:
        finger2 = 2 # 食指

    if abs(dist(0, 9) + dist(9, 10) + dist(10, 11) + dist(11, 12) - dist(0, 12)) < 0.02:
        finger3 = 4 # 中指

    if abs(dist(0, 13) + dist(13, 14) + dist(14, 15) + dist(15, 16) - dist(0, 16)) < 0.02:
        finger4 = 8 # 無名指

    if abs(dist(0, 17) + dist(17, 18) + dist(18, 19) - dist(0, 19)) < 0.02:
        finger5 = 16 # 小指

    # 在畫面上顯示手指狀態
    cv2.putText(frame, f"{finger1} {finger2} {finger3} {finger4} {finger5}", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255))
    cv2.putText(frame, str(finger1 + finger2 + finger3 + finger4 + finger5), (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255))

    # 返回手指狀態的二進制表示
    return format(finger1 + finger2 + finger3 + finger4 + finger5, "05b")


# 開啟攝影機
cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, frame = cap.read() # 讀取攝影機畫面
    if not ret:
        print("無法讀取攝影機畫面")
        break

    # 轉換 BGR 圖像到 RGB
    rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # 偵測手部
    result = hands.process(rgb_frame)

    # 繪製手部關鍵點和連線
    if result.multi_hand_landmarks:
        for hand_landmarks in result.multi_hand_landmarks:
            finger = detect(hand_landmarks) # 將手指狀態以二進制方式表示
            mp_drawing.draw_landmarks(
                frame,
                hand_landmarks,
                mp_hands.HAND_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)  # 連線樣式
            )

    # 顯示圖像
    cv2.imshow('Hand Landmarks Detection', frame)

    # 按下 ESC 鍵退出
    if cv2.waitKey(1) & 0xFF == 27:
        break

# 釋放攝影機並關閉所有視窗
cap.release()
cv2.destroyAllWindows()

🟡 小小挑戰一下

大家可以嘗試進行一些修改或改良喔! 例如:
✌️同時顯示兩隻手的手勢數字。

😁 明天見!

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