# 人工智慧導論專題製作
## *MediaPipe Hand* 電腦猜拳遊戲
##### 報告者:D1173566 資訊一乙 蔡耀東
---
### MediaPipe Hand 簡略介紹
Mediapipe 偵測手掌後,會在手掌與手指上產生 21 個具有 x、y、z 座標的節點,透過包含立體深度的節點,就能在 3D 場景中做出多種不同的應用,下圖標示出每個節點的順序和位置。
![](https://i.imgur.com/XUr3ray.png)
搭配 Mediapipe 手掌偵測的方法,透過攝影鏡頭獲取影像後,即時標記出手掌骨架和動作。
---
### 以下為進度以及部分程式碼
#### 程式碼
```python
import cv2
import mediapipe as mp
import time
cap = cv2.VideoCapture(0)
mpHands = mp.solutions.hands
hands = mpHands.Hands()
mpDraw = mp.solutions.drawing_utils
handSpoStyle = mpDraw.DrawingSpec(color=(0, 255, 20), thickness=5)
handConStyle = mpDraw.DrawingSpec(color=(255, 20, 255), thickness=3)
previousTime = 0
currentTime = 0
while True:
ret, img = cap.read()
if ret:
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
result = hands.process(imgRGB)
# print(result.multi_hand_landmarks)
imgHeight = img.shape[0]
imgWidth = img.shape[1]
if result.multi_hand_landmarks:
for handLms in result.multi_hand_landmarks: # print the landmarks on hand
mpDraw.draw_landmarks(
img, handLms, mpHands.HAND_CONNECTIONS, handSpoStyle, handConStyle)
# print the point's coordinates
for i, lm in enumerate(handLms.landmark):
xPosition = int(lm.x * imgWidth)
yPosition = int(lm.y * imgHeight)
cv2.putText(img, str(i), (xPosition - 25, yPosition + 5),
cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 0.4, (0, 215, 255), 2)
currentTime = time.time()
fps = 1 / (currentTime - previousTime)
previousTime = currentTime
cv2.putText(img, f"FPS : {int(fps)}", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 3)
cv2.imshow('img', img)
if cv2.waitKey(1) == ord('q'):
break
```
---
#### 目前進度
https://youtu.be/d_oCL-c8xg4
## 未來增加項目
* 手勢偵測(剪刀,石頭,布)
* 電腦判斷輸贏
* 提供選項輸入
* 透過猜拳輸出選項、答案
---
## 最終成果
程式碼
```python=
import cv2
import mediapipe as mp
import math
import random
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands
ans1 = input('輸入你的選項:')
print('你的選項是:'+ans1)
ans2 = input('輸入電腦的選項:')
print('電腦的選項是:'+ans2)
print("請比OK手勢開始猜拳")
def vector_2d_angle(v1, v2):
v1_x = v1[0]
v1_y = v1[1]
v2_x = v2[0]
v2_y = v2[1]
try:
angle_ = math.degrees(math.acos(
(v1_x*v2_x+v1_y*v2_y)/(((v1_x**2+v1_y**2)**0.5)*((v2_x**2+v2_y**2)**0.5))))
except:
angle_ = 180
return angle_
def hand_angle(hand_):
angle_list = []
# thumb angle
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[2][0])),
(int(hand_[0][1])-int(hand_[2][1]))),
((int(hand_[3][0]) - int(hand_[4][0])),
(int(hand_[3][1]) - int(hand_[4][1])))
)
angle_list.append(angle_)
# forefinger angle
angle_ = vector_2d_angle(
((int(hand_[0][0])-int(hand_[6][0])),
(int(hand_[0][1]) - int(hand_[6][1]))),
((int(hand_[7][0]) - int(hand_[8][0])),
(int(hand_[7][1]) - int(hand_[8][1])))
)
angle_list.append(angle_)
# middlefinger angle
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[10][0])),
(int(hand_[0][1]) - int(hand_[10][1]))),
((int(hand_[11][0]) - int(hand_[12][0])),
(int(hand_[11][1]) - int(hand_[12][1])))
)
angle_list.append(angle_)
# ringfinger angle
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[14][0])),
(int(hand_[0][1]) - int(hand_[14][1]))),
((int(hand_[15][0]) - int(hand_[16][0])),
(int(hand_[15][1]) - int(hand_[16][1])))
)
angle_list.append(angle_)
# pinky angle
angle_ = vector_2d_angle(
((int(hand_[0][0]) - int(hand_[18][0])),
(int(hand_[0][1]) - int(hand_[18][1]))),
((int(hand_[19][0]) - int(hand_[20][0])),
(int(hand_[19][1]) - int(hand_[20][1])))
)
angle_list.append(angle_)
return angle_list
def hand_pos(finger_angle):
f1 = finger_angle[0] # thumb
f2 = finger_angle[1] # forefinger
f3 = finger_angle[2] # middlefinger
f4 = finger_angle[3] # ringfinger
f5 = finger_angle[4] # picky
# < 50 stretch,>= 50 curl
if f1 >= 50 and f2 >= 50 and f3 >= 50 and f4 >= 50 and f5 >= 50:
return 2
elif f1 >= 50 and f2 < 50 and f3 < 50 and f4 >= 50 and f5 >= 50:
return 1
elif f1 < 50 and f2 < 50 and f3 < 50 and f4 < 50 and f5 < 50:
return 0
elif f1 >= 50 and f2 >= 50 and f3 < 50 and f4 < 50 and f5 < 50: # ok
return 3
elif f1 < 50 and f2 >= 50 and f3 < 50 and f4 < 50 and f5 < 50:
return 3
else:
return 4
def int2hand(interger):
int2hand = ['Paper', 'Scissor', 'Stone', 'ok']
return int2hand[interger]
def game(your):
cp = random.randint(0, 2)
print("電腦出:" + int2hand(cp))
win = [1, 2, 0]
lose = [2, 0, 1]
if (your == win[cp]):
print("You win!")
print("最終選項是"+ans1)
elif (your == lose[cp]):
print("You lose!")
print("最終選項是"+ans2)
else:
print("平手,請在猜一次")
cap = cv2.VideoCapture(0) # read camera
fontFace = cv2.FONT_HERSHEY_SIMPLEX # print text
lineType = cv2.LINE_AA # print text
start = False
# mediapipe tracking hands
with mp_hands.Hands(
model_complexity=0,
min_detection_confidence=0.7,
min_tracking_confidence=0.7) as hands:
if not cap.isOpened():
print("Cannot open camera")
exit()
w, h = 540, 310
while True:
ret, img = cap.read()
img = cv2.resize(img, (w, h))
if not ret:
print("Cannot receive frame")
break
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
results = hands.process(img2)
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
finger_points = []
for i in hand_landmarks.landmark:
xPosition = i.x*w
yPosition = i.y*h
finger_points.append((xPosition, yPosition))
if finger_points:
finger_angle = hand_angle(finger_points)
# print(finger_angle)
your = hand_pos(finger_angle) # return pose
if (your <= 3):
cv2.putText(img, int2hand(your), (30, 120), fontFace, 3,
(255, 255, 255), 10, lineType) # print text
if (your != 4):
if (start == False and your == 3):
start = True
elif (start == True and your != 3):
game(your)
start = False
cv2.imshow('img', img)
if cv2.waitKey(5) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
```
### 程式執行
#### 輸入兩個選項
![](https://i.imgur.com/eW7o55y.png)
#### 鏡頭打開,開始猜拳,得出最終結果![](https://i.imgur.com/xG8V9yf.png)
---
### 最終成果展示
[展示影片](https://youtu.be/-OCqrDLLS-Q)