Mediapipe 手掌侦测 ( 旧 )
这篇教学会使用 MediaPipe 的手掌侦测模型 ( hands ) 侦测双手的手掌,再透过 OpenCV 读取摄影镜头图像进行辨识,在手掌与每只手指标记骨架,最后还会简单设计隔空触碰的小游戏。
MidoaPipe 在 2023 年进行了比较大的改版和更新,虽然旧版的程序“可能”还可以运行,但建议使用新版,参考:Mediapipe 手掌特征点侦测 Hand Landmark Detection ( 2023 年版 )
快速导览:
因为程序使用 Jupyter 搭配 Tensorflow 进行开发,所以请先阅读“使用 Anaconda”和“使用 MediaPipe”,安装对应的软件包,如果不要使用 Juputer,也可参考“使用 Python 虚拟环境”,建立虚拟环境进行实作。
使用 MediaPipe,侦测并绘制手掌骨架
MediaPipe Hands 利用多个模型协同工作,可以侦测手掌模型,返回手掌与每只手指精确的 3D 关键点,MediaPipe Hand 除了可以侦测清晰的手掌形状与动作,更可以判断出被少部分被遮蔽的手指形状和动作,再清晰的画面下,针对手掌判断的精准度可达 95.7%。
Mediapipe 侦测手掌后,会在手掌与手指上产生 21 个具有 x、y、z 座标的节点,透过包含立体深度的节点,就能在 3D 场景中做出多种不同的应用,下图标示出每个节点的顺序和位置 ( 图片来源 )。
如果同时出现两只手,采用交错侦测 ( 短时间内侦测两次,一次侦测一只手 ),最后仍然维持 21 个点的数据,如果只希望侦测一只手,可设定 max_num_hands=1。
下方的程序码延伸“读取并播放影片”文章的范例,搭配 mediapipe 手掌侦测的方法,透过摄影镜头获取图像后,即时标记出手掌骨架和动作。
import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils # mediapipe 繪圖方法
mp_drawing_styles = mp.solutions.drawing_styles # mediapipe 繪圖樣式
mp_hands = mp.solutions.hands # mediapipe 偵測手掌方法
cap = cv2.VideoCapture(0)
# mediapipe 啟用偵測手掌
with mp_hands.Hands(
model_complexity=0,
# max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as hands:
if not cap.isOpened():
print("Cannot open camera")
exit()
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 將 BGR 轉換成 RGB
results = hands.process(img2) # 偵測手掌
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
# 將節點和骨架繪製到影像中
mp_drawing.draw_landmarks(
img,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
mp_drawing_styles.get_default_hand_landmarks_style(),
mp_drawing_styles.get_default_hand_connections_style())
cv2.imshow('oxxostudio', img)
if cv2.waitKey(5) == ord('q'):
break # 按下 q 鍵停止
cap.release()
cv2.destroyAllWindows()
隔空触碰的小游戏
侦测到手掌后,就可取得 21 个节点的座标位置,下方的程序码会在摄影机取得画面时,在画面上绘制一个正方形区域,当食指末端 ( 第 8 个节点 ) 触碰到这个区域 ( 座标落在这个区域内 ),就将正方形区域移动到随机的位置。
参考:rectangle() 画四边形、随机整数、
import cv2
import mediapipe as mp
import random
mp_drawing = mp.solutions.drawing_utils # mediapipe 繪圖方法
mp_drawing_styles = mp.solutions.drawing_styles # mediapipe 繪圖樣式
mp_hands = mp.solutions.hands # mediapipe 偵測手掌方法
cap = cv2.VideoCapture(0)
# mediapipe 啟用偵測手掌
with mp_hands.Hands(
model_complexity=0,
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as hands:
if not cap.isOpened():
print("Cannot open camera")
exit()
run = True # 設定是否更動觸碰區位置
while True:
ret, img = cap.read()
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(540,320)) # 調整畫面尺寸
size = img.shape # 取得攝影機影像尺寸
w = size[1] # 取得畫面寬度
h = size[0] # 取得畫面高度
if run:
run = False # 如果沒有碰到,就一直是 False ( 不會更換位置 )
rx = random.randint(50,w-50) # 隨機 x 座標
ry = random.randint(50,h-100) # 隨機 y 座標
print(rx, ry)
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 將 BGR 轉換成 RGB
results = hands.process(img2) # 偵測手掌
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
x = hand_landmarks.landmark[7].x * w # 取得食指末端 x 座標
y = hand_landmarks.landmark[7].y * h # 取得食指末端 y 座標
print(x,y)
if x>rx and x<(rx+80) and y>ry and y<(ry+80):
run = True
# 將節點和骨架繪製到影像中
mp_drawing.draw_landmarks(
img,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
mp_drawing_styles.get_default_hand_landmarks_style(),
mp_drawing_styles.get_default_hand_connections_style())
cv2.rectangle(img,(rx,ry),(rx+80,ry+80),(0,0,255),5) # 畫出觸碰區
cv2.imshow('oxxostudio', img)
if cv2.waitKey(5) == ord('q'):
break # 按下 q 鍵停止
cap.release()
cv2.destroyAllWindows()
微信扫码关注
抖音扫码关注