# 用OpenCV跟電腦玩猜拳
## by ZiyanGZiyaNG所做
ㄤㄤ(●'◡'●)
### 起源
~~因為被教育部要求一定要有自主學習的學分,才可以畢業~~
其實感覺很酷,可以跟電腦玩猜拳耶(超適合我這種邊緣人
### 靈感發想
在IG看到有人用openCV做出了可以依手勢判別出數字,然後就靈光乍現既然可以用來判別數字那剪刀、石頭、布應該也不式件大事吧?
### 做死的開端
在開始寫程式之前我只知道openCV就只是個可以開啟相機偵測的mod而已,也只是個剛入坑python的菜逼八。而我需要在一學期只有一學分的課中從零開始全部學會openCV跟把Python提升到一定的水準(自作孽不可活
### 踏入地獄
1. 第一步:寫出一個可輸出相機訊號源的程式
2. 第二步:加入mediapipe來去檢測手勢
3. 第三步:完成mediapipe的手部截點
4. 第四步:用random去隨機生成剪刀、石頭、布
5. 第五步:可以判斷誰輸誰贏
這是我在企劃書上列出的簡易流程圖,但實際上根本根本根本沒有照著走(~~because I am a chill guy~~
### 問題一堆
看到這你一定會想說妳他在吵三小你自己不做的,但實際上每次上課都是寫好寫滿的
至於為什麼寫不完,問就是我寫來好幾個不同的版本(就跑不動咩Ex:break 沒辦法被程式抓到......

但最終在我~~日以繼夜不斷努力~~的思考和找資料,我還是將程式生出來了
# 接下來是程式碼的部分
## 導入的 MOD
```
import cv2
import mediapipe as mp
import random
import time
```
## 初始化 mediapipe MOD
```
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_draw = mp.solutions.drawing_utils
```
## 鏡頭設定
```
cap = cv2.VideoCapture(0)
```
## 定義手勢只有三種
```
gestures = ['石頭', '布', '剪刀']
```
## 定義誰輸誰贏的規則
```
rules = {('石頭', '剪刀'): '你贏了!',('剪刀', '布'): '你贏了!',('布', '石頭'): '你贏了!',('剪刀', '石頭'): '電腦贏了!',('布', '剪刀'): '電腦贏了!',('石頭', '布'): '電腦贏了!',('石頭', '石頭'): '平手',('布', '布'): '平手',('剪刀', '剪刀'): '平手'}
```
## 記分板初始化
```
player_score = 0
computer_score = 0
```
## 定義何謂手指伸起和將輸出訂為相機所照到的
```
def get_hand_gesture(image):
results = hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
if results.multi_hand_landmarks:
for hand_landmarks in results.multi_hand_landmarks:
mp_draw.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)
fingers_up = [(hand_landmarks.landmark[i].y < hand_landmarks.landmark[i - 2].y) for i in [8, 12, 16, 20]]
```
## 全部手指都是伸直的=布
if all(fingers_up):
return 'Paper'
## 只有兩根手指伸起=剪刀
if fingers_up[0] and fingers_up[1] and not fingers_up[2] and not fingers_up[3]:
return '剪刀'
## 全部手指都沒有伸起=石頭
if not any(fingers_up):
return '石頭'
## 如果都不在三種手勢中,輸出不知道
return 'WDYM??'
## 相機設定
```
while True:
ret, frame = cap.read()
if not ret:
break
player_gesture = get_hand_gesture(frame)
if player_gesture != 'WDYM??':
computer_gesture = random.choice(gestures)
result = rules.get((player_gesture, computer_gesture), 'Invalid gestures')
```
## 當玩家贏了,分數+1
if result == '玩家贏了!':
player_score += 1
## 當電腦贏了,分數+1
elif result == '電腦贏了!':
computer_score += 1
## 分數比較
```
for text, y in [(f"玩家: {player_gesture}", 30),
(f"電腦: {computer_gesture}", 70),
(result, 110),
(f"玩家分數: {player_score}", 150),
(f"電腦分數: {computer_score}", 190)]:
if player_score > computer_score:
print("玩家贏得整場比賽!")
elif player == computer_score:
print("平手")
else:
print("電腦贏得整場比賽!")
```
## 輸出影像設定
```
v2.putText(frame, text, (10, y), cv2.FONT_HERSHEY_SIMPLEX, 1,(255, 255, 255), 2)
cv2.imshow('石頭 布 剪刀', frame)
time.sleep(1)
cv2.imshow('石頭 布 剪刀', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
```
## 總結
雖然在最後有趕出成果,但我個人認為成果還是不太完美還是有一些bug。Ex:鏡頭只要拍到手就會卡住、勝負判斷太快(一秒10幾局就結束)。我覺得我的時間安排還是有些問題,照我的規劃應該可以時間充裕的完成啊~
## 預告
下學期,我搞了也是openCV相關的(希望不會搞到沒時間:D