# 用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