# 人工智慧導論專題製作 ## *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)