手勢識別是計算機視覺領域中的重要方向,通過對攝像機采集的手部相關的圖像序列進行分析處理,進而識別其中的手勢,手勢被識別后用戶就可以通過手勢來控制設備或者與設備交互。完整的手勢識別一般有手的檢測和姿態估計、手部跟蹤和手勢識別等。
一、手掌檢測
import cv2
import mediapipe as mp# 初始化 MediaPipe 手部模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()# 初始化視頻流
cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()if not ret:break# 轉換為 RGBrgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 獲取手部關鍵點results = hands.process(rgb_frame)# 如果檢測到手部if results.multi_hand_landmarks:for landmarks in results.multi_hand_landmarks:# 繪制手部關鍵點mp.solutions.drawing_utils.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)# 顯示圖像cv2.imshow('Hand Detection', frame)# 按 'q' 鍵退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 釋放資源
cap.release()
cv2.destroyAllWindows()
這段代碼是一個簡單的手部檢測應用程序,使用了 OpenCV 和 MediaPipe 庫來實時檢測和繪制手部關鍵點。下面是對代碼的詳細解釋:
1. 導入必要的庫
import cv2
import mediapipe as mp
cv2
是 OpenCV 的 Python 接口庫,用于圖像處理和計算機視覺任務。mediapipe
是 Google 提供的一個跨平臺框架,用于實現高效的計算機視覺任務,這里用它來做手部關鍵點檢測。
2. 初始化 MediaPipe 手部模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_hands
是 MediaPipe 提供的手部檢測模塊。通過mp.solutions.hands
訪問。hands = mp_hands.Hands()
創建了一個Hands
對象,用于進行手部檢測。它會自動處理圖像中的手部檢測、手部關鍵點識別和跟蹤。
3. 初始化視頻流
cap = cv2.VideoCapture(0)
cv2.VideoCapture(0)
打開了計算機的默認攝像頭(0
表示第一個攝像頭)。返回的cap
對象用于讀取視頻流。
4. 開始處理視頻流
while True:ret, frame = cap.read()if not ret:break
cap.read()
從攝像頭讀取一幀圖像并返回兩個值:ret
和frame
。ret
是布爾值,表示圖像是否成功讀取,frame
是當前幀的圖像。- 如果
ret
為False
,則說明讀取圖像失敗,程序退出循環。
5. 轉換圖像為 RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
- OpenCV 使用 BGR(藍綠紅)色彩空間,而 MediaPipe 要求輸入圖像為 RGB(紅綠藍)色彩空間。因此,我們用
cv2.cvtColor
將圖像從 BGR 轉換為 RGB。
6. 處理圖像以檢測手部關鍵點
results = hands.process(rgb_frame)
hands.process(rgb_frame)
將 RGB 圖像傳遞給 MediaPipe 的Hands
模型進行處理。results
包含了檢測到的手部信息,包括手部關鍵點的位置。如果圖像中沒有手部,results.multi_hand_landmarks
會是空的。
7. 繪制手部關鍵點
if results.multi_hand_landmarks:for landmarks in results.multi_hand_landmarks:mp.solutions.drawing_utils.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
results.multi_hand_landmarks
是一個包含所有檢測到的手的關鍵點位置的列表。如果檢測到手部,它會返回一個非空列表。landmarks
是每個手部的關鍵點集合,每個關鍵點是一個(x, y, z)
坐標。mp.solutions.drawing_utils.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
將每個手的關鍵點繪制到frame
圖像上,并且連接關鍵點,形成手的骨架結構。mp_hands.HAND_CONNECTIONS
用于連接不同的關鍵點。
8. 顯示處理后的圖像
cv2.imshow('Hand Detection', frame)
cv2.imshow
顯示處理后的圖像(frame
)。窗口的標題是 ‘Hand Detection’。
9. 按鍵退出
if cv2.waitKey(1) & 0xFF == ord('q'):break
cv2.waitKey(1)
等待鍵盤輸入,1
表示等待 1 毫秒。返回值是按下鍵的 ASCII 碼。- 如果按下
q
鍵,ord('q')
的值與cv2.waitKey(1)
返回值相等,程序就會退出循環,停止視頻流。
10. 釋放資源并關閉窗口
cap.release()
cv2.destroyAllWindows()
cap.release()
釋放攝像頭資源。cv2.destroyAllWindows()
關閉所有 OpenCV 顯示的窗口。
二、手關鍵點估計
要進行手關鍵點估計,可以使用 MediaPipe 庫來進行高效的手部檢測,結合 OpenCV 來進行圖像處理和顯示。這里將展示一個基于 MediaPipe 手部模型的手關鍵點估計的完整代碼。
MediaPipe 提供了一個預訓練的模型來檢測手部的 21 個關鍵點,包括手指關節和手掌部位。我們將使用 MediaPipe 提供的 Hands
模塊來檢測手部關鍵點。
import cv2
import mediapipe as mp# 初始化 MediaPipe 手部檢測模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils# 打開攝像頭
cap = cv2.VideoCapture(0)while True:# 讀取每一幀圖像ret, frame = cap.read()if not ret:print("無法獲取視頻幀")break# 將圖像轉換為 RGB 格式,MediaPipe 需要 RGB 輸入rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 獲取手部關鍵點檢測結果results = hands.process(rgb_frame)# 如果檢測到手部if results.multi_hand_landmarks:for landmarks in results.multi_hand_landmarks:# 繪制手部關鍵點和連接線mp_drawing.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)# 獲取并顯示每個關鍵點的坐標for idx, landmark in enumerate(landmarks.landmark):h, w, c = frame.shapecx, cy = int(landmark.x * w), int(landmark.y * h)cv2.putText(frame, str(idx), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1) # 繪制紅色的關鍵點# 顯示圖像cv2.imshow('Hand Keypoint Estimation', frame)# 按 'q' 鍵退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 釋放攝像頭資源并關閉窗口
cap.release()
cv2.destroyAllWindows()
1.代碼解釋
-
初始化 MediaPipe 手部模型:
- 使用
mp.solutions.hands.Hands()
創建手部檢測模型,min_detection_confidence=0.7
和min_tracking_confidence=0.5
表示最低的檢測和追蹤置信度閾值,適用于動態環境中。
- 使用
-
打開攝像頭:
- 使用
cv2.VideoCapture(0)
打開計算機的默認攝像頭,并獲取實時視頻流。
- 使用
-
圖像轉換:
- OpenCV 默認使用 BGR 色彩空間,但 MediaPipe 需要 RGB 色彩空間,因此使用
cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
將圖像轉換為 RGB 格式。
- OpenCV 默認使用 BGR 色彩空間,但 MediaPipe 需要 RGB 色彩空間,因此使用
-
處理圖像并獲取手部關鍵點:
hands.process(rgb_frame)
用于處理圖像并檢測手部關鍵點。results
對象包含手部信息,如果檢測到手部,results.multi_hand_landmarks
將返回手部的 21 個關鍵點的位置。
-
繪制關鍵點和連接線:
mp_drawing.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)
繪制手的 21 個關鍵點以及它們之間的連接線(即手指的骨架)。landmarks.landmark
包含了每個關鍵點的(x, y, z)
坐標,其中x
和y
是圖像中的像素坐標,z
是深度信息(在 3D 空間中的位置,但對于 2D 圖像可忽略)。
-
顯示關鍵點坐標:
- 對于每個關鍵點,通過
cv2.putText
在圖像上顯示關鍵點的索引。cv2.circle
用來在圖像上繪制關鍵點位置。
- 對于每個關鍵點,通過
-
顯示視頻流:
- 使用
cv2.imshow()
來顯示處理過的每一幀圖像,展示檢測到的手部關鍵點。
- 使用
-
退出條件:
- 按下
q
鍵退出程序并釋放攝像頭資源。
- 按下
2.手部關鍵點索引
MediaPipe 的 Hands
模型檢測手部的 21 個關鍵點,索引如下(從 0 到 20):
- 0: 手腕
- 1: 大拇指根部
- 2: 大拇指第一關節
- 3: 大拇指第二關節
- 4: 大拇指尖端
- 5: 食指根部
- 6: 食指第一關節
- 7: 食指第二關節
- 8: 食指尖端
- 9: 中指根部
- 10: 中指第一關節
- 11: 中指第二關節
- 12: 中指尖端
- 13: 無名指根部
- 14: 無名指第一關節
- 15: 無名指第二關節
- 16: 無名指尖端
- 17: 小指根部
- 18: 小指第一關節
- 19: 小指第二關節
- 20: 小指尖端
三、實例
YOLO(You Only Look Once)是一個非常強大的目標檢測算法,特別適用于實時應用。它將目標檢測任務轉化為一個回歸問題,能夠快速且準確地檢測圖像中的目標物體。雖然 YOLO 本身并沒有直接針對手部關鍵點估計的功能,但可以用它來檢測手部區域,然后結合其他模型(比如 MediaPipe 或深度學習的關鍵點檢測模型)來進行手關鍵點估計。
- YOLO 檢測手部:首先用 YOLO 模型檢測圖像中的手部位置,YOLO 會輸出手部的邊界框。
- 手部關鍵點估計:然后,我們可以通過一個手部關鍵點估計模型(如 MediaPipe 或自定義深度學習模型)來進一步估計手部的 21 個關鍵點。
import cv2
import torch
import mediapipe as mp# 初始化 MediaPipe 手部檢測模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(min_detection_confidence=0.7, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils# 加載 YOLOv5 模型(選擇合適的模型大小)
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # 'yolov5s' 是一個小型的 YOLOv5 模型# 打開攝像頭
cap = cv2.VideoCapture(0)while True:# 讀取每一幀圖像ret, frame = cap.read()if not ret:print("無法獲取視頻幀")break# 使用 YOLOv5 檢測圖像中的物體(包括手部)results = model(frame) # 檢測結果,包括邊界框、標簽、置信度等# 提取 YOLOv5 檢測到的手部(通常手部被標記為 0 或其他標簽,依賴于訓練數據)# 獲取所有檢測到的物體的邊界框hands_detections = results.xyxy[0] # 獲取檢測結果,xyxy 形式 [x_min, y_min, x_max, y_max, confidence, class]for detection in hands_detections:if detection[5] == 0: # 類別 0 代表手部(具體根據訓練數據集而定)x_min, y_min, x_max, y_max = map(int, detection[:4])# 在圖像上繪制手部邊界框cv2.rectangle(frame, (x_min, y_min), (x_max, y_max), (255, 0, 0), 2)# 提取手部區域并進行手部關鍵點估計hand_region = frame[y_min:y_max, x_min:x_max]rgb_hand_region = cv2.cvtColor(hand_region, cv2.COLOR_BGR2RGB)# 使用 MediaPipe 估計手部關鍵點hand_results = hands.process(rgb_hand_region)# 如果檢測到手部關鍵點if hand_results.multi_hand_landmarks:for landmarks in hand_results.multi_hand_landmarks:# 繪制手部關鍵點和連接線mp_drawing.draw_landmarks(frame, landmarks, mp_hands.HAND_CONNECTIONS)for idx, landmark in enumerate(landmarks.landmark):h, w, c = frame.shapecx, cy = int(landmark.x * w), int(landmark.y * h)cv2.putText(frame, str(idx), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)cv2.circle(frame, (cx, cy), 5, (0, 0, 255), -1) # 繪制紅色的關鍵點# 顯示圖像cv2.imshow('Hand Detection and Keypoint Estimation', frame)# 按 'q' 鍵退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 釋放攝像頭資源并關閉窗口
cap.release()
cv2.destroyAllWindows()
1.代碼解析
- 加載 YOLOv5 模型:
torch.hub.load('ultralytics/yolov5', 'yolov5s')
載入 YOLOv5 模型,yolov5s
是一個小型的 YOLOv5 模型,適合實時應用。可以根據需求選擇yolov5m
或yolov5l
(更大的模型,精度更高但速度較慢)。
- 讀取攝像頭視頻流:
- 使用
cv2.VideoCapture(0)
打開攝像頭并讀取每一幀圖像。
- 使用
- YOLOv5 物體檢測:
- 使用
model(frame)
進行圖像檢測,返回一個results
對象,其中包含檢測到的邊界框、類別標簽、置信度等信息。
- 使用
- 提取手部區域:
- 如果 YOLO 檢測到手部(通過檢測類別標號),提取手部的區域并將其轉化為 RGB 格式(因為 MediaPipe 需要 RGB 輸入)。
- MediaPipe 手部關鍵點估計:
- 使用
hands.process(rgb_hand_region)
進行手部關鍵點估計,返回手部的關鍵點信息。 - 使用
mp_drawing.draw_landmarks()
繪制手部的 21 個關鍵點和連接線。
- 使用
- 結果顯示:
- 將檢測到的手部邊界框、關鍵點及連接線顯示在視頻流上。
- 退出條件:
- 按下
q
鍵退出程序。
- 按下