---
# System prepended metadata

title: 和電腦玩猜拳

---

# 用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 沒辦法被程式抓到......
![螢幕擷取畫面 2024-12-06 004502](https://hackmd.io/_uploads/SyFEYUJNJg.png)
但最終在我~~日以繼夜不斷努力~~的思考和找資料，我還是將程式生出來了


# 接下來是程式碼的部分
## 導入的 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