前言
我們針對 Google Mediapipe 已經發表過多篇教學文章,這套裝置端的機器學習工具目前已支援視覺(分類、偵測與分割)、文字與聲音(分類)。本文要使用手部 api 做一個簡易的點球遊戲,畫面上會隨機出現不同顏色的球,點擊三次之後球消失,並撥放對應音效。並且可透過 USB 傳輸線來控制 Arduino 的 LED 亮起。

目前CAVEUE已發表的 Mediapipe 專題包含:
阿吉老師也很榮幸受邀到今年的 DevFest Taipei 2023 分享使用 Mediapipe 結合邊緣運算的小小心得,投影片如下,歡迎看看喔
本文
本專題分成兩端:Python 與 Arduino,依序說明如下:
python程式碼
您需要先安裝 mediapipe 與 pypserial 函式庫,請在終端機中輸入以下指令來安裝:
注意:如果您沒有 Arduino,只要把以下程式碼中的 ser 相關都註解掉就可以執行了,例如:
ser = serial.Serial(COM_PORT, BAUD_RATES)
[pastacode lang=”bash” manual=”” message=”pip install mediapipe” highlight=”” provider=”manual”/]
完整Python端程式碼如下,註解都在程式碼中,相當好理解。您可以參考 #164 開始的計算食指指尖位置與圓心之間距離 d,如果 d < r (圓半徑),代表碰到球了。
每次觸碰球就會修改球的透明度 ( alpha -= 0.33),並在碰觸到三次之後播放音效並發送指定給 Arduino ( ser.write(str(light).encode()) )
[pastacode lang=”python” manual=”import%20serial%0Aimport%20argparse%0Aimport%20cv2%0Aimport%20time%0Aimport%20numpy%20as%20np%0Aimport%20math%0Aimport%20mediapipe%20as%20mp%0Aimport%20random%0Aimport%20pygame%0A%0A%23%23%23%23%23%23%23%23%23%23%20%E6%89%8B%E9%83%A8%E8%BF%BD%E8%B9%A4%E5%81%B5%E6%B8%AC%20%23%23%23%23%23%23%23%23%23%23%23%23%23%0Aclass%20handDetector()%3A%0A%20%20%20%20def%20__init__(self%2C%20mode%3DFalse%2C%20maxHands%3D2%2C%20detectionCon%3D0.5%2C%20trackCon%3D0.5)%3A%0A%20%20%20%20%20%20%20%20self.mode%20%3D%20mode%0A%20%20%20%20%20%20%20%20self.maxHands%20%3D%20maxHands%0A%20%20%20%20%20%20%20%20self.detectionCon%20%3D%20detectionCon%0A%20%20%20%20%20%20%20%20self.trackCon%20%3D%20trackCon%0A%0A%20%20%20%20%20%20%20%20self.mpHands%20%3D%20mp.solutions.hands%0A%20%20%20%20%20%20%20%20%23%20self.hands%20%3D%20self.mpHands.Hands(self.mode%2C%20self.maxHands%2C%0A%20%20%20%20%20%20%20%20%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.detectionCon%2C%20self.trackCon)%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20self.hands%20%3D%20self.mpHands.Hands(self.mode%2C%20self.maxHands%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20min_detection_confidence%3Dself.detectionCon%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20min_tracking_confidence%3Dself.trackCon)%0A%0A%20%20%20%20%20%20%20%20self.mpDraw%20%3D%20mp.solutions.drawing_utils%0A%20%20%20%20def%20findHands(self%2C%20img%2C%20draw%3DTrue)%3A%0A%20%20%20%20%20%20%20%20imgRGB%20%3D%20cv2.cvtColor(img%2C%20cv2.COLOR_BGR2RGB)%0A%20%20%20%20%20%20%20%20self.results%20%3D%20self.hands.process(imgRGB)%0A%20%20%20%20%20%20%20%20%23%20print(results.multi_hand_landmarks)%0A%0A%20%20%20%20%20%20%20%20if%20self.results.multi_hand_landmarks%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20handLms%20in%20self.results.multi_hand_landmarks%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20draw%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.mpDraw.draw_landmarks(img%2C%20handLms%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.mpHands.HAND_CONNECTIONS)%0A%20%20%20%20%20%20%20%20return%20img%0A%0A%20%20%20%20def%20findPosition(self%2C%20img%2C%20handNo%3D0%2C%20draw%3DTrue)%3A%0A%0A%20%20%20%20%20%20%20%20lmList%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20if%20self.results.multi_hand_landmarks%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20myHand%20%3D%20self.results.multi_hand_landmarks%5BhandNo%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20id%2C%20lm%20in%20enumerate(myHand.landmark)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20print(id%2C%20lm)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20h%2C%20w%2C%20c%20%3D%20img.shape%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cx%2C%20cy%20%3D%20int(lm.x%20*%20w)%2C%20int(lm.y%20*%20h)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20print(id%2C%20cx%2C%20cy)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lmList.append(%20%5Bid%2C%20cx%2C%20cy%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20draw%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv2.circle(img%2C%20(cx%2C%20cy)%2C%2015%2C%20(255%2C%200%2C%20255)%2C%20cv2.FILLED)%0A%20%20%20%20%20%20%20%20return%20lmList%0A%20%20%20%20%0Adef%20play_background_music()%3A%0A%20%20%20%20pygame.mixer.music.load(%22bgm.mp3%22)%0A%20%20%20%20pygame.mixer.music.play(-1)%0A%0Adef%20play_good_sound()%3A%0A%20%20%20%20good_sound%20%3D%20pygame.mixer.Sound(%22good.mp3%22)%0A%20%20%20%20good_sound.play()%0A%0Adef%20play_bad_sound()%3A%0A%20%20%20%20bad_sound%20%3D%20pygame.mixer.Sound(%22bad.mp3%22)%0A%20%20%20%20bad_sound.play()%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0Adef%20main()%3A%0A%0A%20%20%20%20pygame.init()%0A%0A%20%20%20%20%23%20%E8%A8%AD%E5%AE%9A%E9%9F%B3%E9%87%8F%EF%BC%880.0%20%E5%88%B0%201.0%20%E4%B9%8B%E9%96%93%EF%BC%89%0A%20%20%20%20pygame.mixer.music.set_volume(0.8)%0A%20%20%20%20%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%E5%90%84%E5%8F%83%E6%95%B8%E8%A8%AD%E5%AE%9A%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%20%20%20%20pTime%20%20%3D%200%0A%20%20%20%20minPwm%20%3D%200%0A%20%20%20%20maxPwm%20%3D%20255%0A%20%20%20%20briArd%20%3D%200%0A%20%20%20%20briBar%20%3D%20400%0A%20%20%20%20briPer%20%3D%200%0A%20%20%20%20%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%E6%8C%87%E5%AE%9AWEBCAM%E5%92%8CArduino%20Serial%20Port%E7%B7%A8%E8%99%9F%E7%9A%84%E6%8C%87%E4%BB%A4%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%20%20%20%20%0A%20%20%20%20parser%20%3D%20argparse.ArgumentParser(%0A%20%20%20%20%20%20formatter_class%3Dargparse.ArgumentDefaultsHelpFormatter)%0A%20%20%20%20parser.add_argument(%0A%20%20%20%20′–video’%2C%20help%3D’Video%20number’%2C%20required%3DFalse%2C%20type%3Dint%2C%20default%3D0)%0A%20%20%20%20parser.add_argument(%0A%20%20%20%20%20%20′–com’%2C%20help%3D’Number%20of%20UART%20prot.’%2C%20required%3DFalse)%0A%20%20%20%20args%20%3D%20parser.parse_args()%0A%20%20%20%20%0A%20%20%20%20COM_PORT%20%3D%20’COM’%2Bstr(args.com)%0A%20%20%20%20BAUD_RATES%20%3D%209600%0A%20%20%20%20ser%20%3D%20serial.Serial(COM_PORT%2C%20BAUD_RATES)%0A%20%20%20%20%0A%20%20%20%20args%20%3D%20parser.parse_args()%0A%20%20%20%20%0A%20%20%20%20%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20WEBCAM%E7%9B%B8%E9%97%9C%E5%8F%83%E6%95%B8%E5%AE%9A%E7%BE%A9%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%20%20%20%20height%20%3D%201080%3B%20width%3D1920%0A%20%20%20%20wCam%2C%20hCam%20%3D%20height%2C%20width%20%20%20%0A%20%20%20%20cap%20%3D%20cv2.VideoCapture(args.video)%20%23%20%E6%94%9D%E5%BD%B1%E6%A9%9F%E7%B7%A8%E8%99%9F%E9%A0%90%E8%A8%AD%E7%82%BA0%EF%BC%8C%E4%B9%9F%E5%8F%AF%E4%BB%A5%E8%BC%B8%E5%85%A5%E5%85%B6%E4%BB%96%E7%B7%A8%E8%99%9F!%0A%20%20%20%20cap.set(3%2C%20wCam)%0A%20%20%20%20cap.set(4%2C%20hCam)%0A%20%20%20%20detector%20%3D%20handDetector(detectionCon%3D0.7)%0A%0A%20%20%20%20count%3D0%0A%20%20%20%20score%3D0%0A%20%20%20%20hp%3D3%0A%0A%20%20%20%20circle_radius%20%3D%2050%0A%20%20%20%20circle_y%2C%20circle_x%3D50%2Crandom.randint(circle_radius%2C%20(width%20-%20circle_radius)*0.6)%0A%20%20%20%20circle_color%20%3D%20(random.randint(0%2C%20255)%2C%20random.randint(0%2C%20255)%2C%20random.randint(0%2C255))%0A%20%20%20%20finger_radius%20%3D%2020%0A%20%20%20%20finger_touching%3DFalse%20%20%20%20%0A%20%20%20%20alpha%20%3D%200.99%20%23%20%E8%A8%AD%E5%AE%9A%E9%80%8F%E6%98%8E%E5%BA%A6%EF%BC%8C%E9%80%99%E8%A3%A1%E8%A8%AD%E5%AE%9A%E7%82%BA50%25%0A%20%20%20%20play_background_music()%0A%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20while%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20success%2C%20img%20%3D%20cap.read()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20img%20%3D%20cv2.flip(img%2C%201)%20%20%23%20%E5%8A%A0%E5%85%A5%E9%80%99%E8%A1%8C%E9%80%B2%E8%A1%8C%E5%B7%A6%E5%8F%B3%E7%BF%BB%E8%BD%89%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20img%20%3D%20detector.findHands(img)%0A%20%20%20%20%20%20%20%20%20%20%20%20mask%20%3D%20np.zeros(img.shape%2C%20dtype%3Dnp.uint8)%0A%20%20%20%20%20%20%20%20%20%20%20%20lmList%20%3D%20detector.findPosition(img%2C%20draw%3DFalse)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23print(lmList)%0A%20%20%20%20%20%20%20%20%20%20%20%20light%3D0%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E7%95%AB%E5%9C%93%E9%BB%9E%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20circle_center%20%3D%20(circle_x%2Ccircle_y)%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.circle(mask%2C%20circle_center%2C%20circle_radius%2C%20circle_color%2C%20-1)%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(mask%2C%20f’%7Bcount%7D’%2C%20circle_center%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%203%2C%20(255%2C255%2C255)%2C3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E5%9C%93%E9%BB%9E%E8%87%AA%E7%94%B1%E8%90%BD%E9%AB%94%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20circle_y%2B%3D3%20%237%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E6%8E%89%E5%88%B0%E6%9C%80%E4%B8%8B%E9%9D%A2%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20circle_y%3E%3D(height%20-%20circle_radius)*0.7%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20circle_y%2C%20circle_x%3D50%2Crandom.randint(circle_radius%2C%20(width%20-%20circle_radius)*0.6)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20circle_color%20%3D%20(random.randint(100%2C%20200)%2C%20random.randint(100%2C%20200)%2C%20random.randint(100%2C200))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%3D0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alpha%3D0.99%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20hp-%3D1%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20hp%3E0%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20play_bad_sound()%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E9%A1%AF%E7%A4%BA%E6%96%87%E5%AD%97%0A%20%20%20%20%20%20%20%20%20%20%20%20%23cv2.putText(img%2Cf’circle_position%7Bcircle_center%7D’%2C(40%2C150)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(255%2C%20255%2C%200)%2C%203)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23cv2.putText(img%2C%20f’alpha%20%7Balpha%7D’%2C%20(40%2C%20200)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(255%2C%20255%2C%200)%2C%203)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23cv2.putText(img%2C%20f’count%20%7Bcount%7D’%2C%20(40%2C%20100)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(255%2C%20255%2C%200)%2C3)%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(img%2C%20f’Score%3A%20%7Bscore%7D’%2C%20(40%2C%20100)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(0%2C%20153%2C%20255)%2C3)%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(img%2C%20f’HP%3A%20%7Bhp%7D’%2C%20(40%2C%20150)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(0%2C%200%2C%20255)%2C3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23cv2.putText(img%2C%20f’color%3A%20%7Bcircle_color%7D’%2C%20(40%2C%20200)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(0%2C%200%2C%20255)%2C3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20len(lmList)%20!%3D%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%2Cy%3DlmList%5B8%5D%5B1%5D%2ClmList%5B8%5D%5B2%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20finger_position%3D(x%2Cy)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv2.circle(img%2Cfinger_position%2Cfinger_radius%2C255%2C-1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23cv2.putText(img%2Cf’finger_position%7Bfinger_position%7D’%2C(40%2C300)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(255%2C%20255%2C%200)%2C%203)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E8%A8%88%E7%AE%97%E9%A3%9F%E6%8C%87%E4%BD%8D%E7%BD%AE%E5%92%8C%E5%9C%93%E9%BB%9E%E4%BD%8D%E7%BD%AE%E7%9A%84%E8%B7%9D%E9%9B%A2%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20distance%20%3D%20math.sqrt((finger_position%5B0%5D%20-%20circle_center%5B0%5D)**2%20%2B%20(finger_position%5B1%5D%20-%20circle_center%5B1%5D)**2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23cv2.putText(img%2C%20f’distance%20%7Bdistance%7D’%2C%20(40%2C%20250)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(255%2C%20255%2C%200)%2C3)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%E5%88%A4%E6%96%B7%E6%98%AF%E5%90%A6%E7%A2%B0%E8%A7%B8%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20distance%20%3C%20circle_radius%20%2B%20finger_radius%20and%20not%20finger_touching%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alpha%20-%3D%200.33%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20finger_touching%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20elif%20distance%20%3E%3D%20(circle_radius%20%2B%20finger_radius)*2%20and%20finger_touching%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20finger_touching%20%3D%20False%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E7%A2%B0%E4%B8%89%E6%AC%A1%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20count%3D%3D3%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20circle_color%20%3D%20(random.randint(0%2C%20255)%2C%20random.randint(0%2C%20255)%2C%20random.randint(0%2C255))%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23circle_y%2C%20circle_x%3Drandom.randint(circle_radius%2C%20(height%20-%20circle_radius)*0.7)%2Crandom.randint(circle_radius%2C%20(width%20-%20circle_radius)*0.6)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20circle_y%2C%20circle_x%3D50%2Crandom.randint(circle_radius%2C%20(width%20-%20circle_radius)*0.6)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20circle_center%20%3D%20(circle_x%2Ccircle_y)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23time.sleep(0.5)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%3D0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alpha%3D0.99%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20score%2B%3D1%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20light%3D1%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20play_good_sound()%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E9%80%81%E5%87%BA%E6%95%B8%E5%80%BC%E7%B5%A6Arduino%0A%20%20%20%20%20%20%20%20%20%20%20%20ser.write(str(light).encode())%0A%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E8%A8%88%E7%AE%97%E6%AF%8F%E7%A7%92%E8%B7%91%E5%B9%BE%E5%BC%B5%0A%20%20%20%20%20%20%20%20%20%20%20%20cTime%20%3D%20time.time()%0A%20%20%20%20%20%20%20%20%20%20%20%20fps%20%3D%201%20%2F%20(cTime%20-%20pTime)%0A%20%20%20%20%20%20%20%20%20%20%20%20pTime%20%3D%20cTime%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(img%2C%20f’FPS%3A%20%7Bint(fps)%7D’%2C%20(40%2C%2050)%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(255%2C%200%2C%200)%2C%203)%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20hp%3C%3D0%20%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20np.zeros(img.shape%2C%20dtype%3Dnp.uint8)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(result%2C%20f’GAME%20OVER’%2C%20(150%2C%20int(height*0.6%2F2))%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%205%2C%20(0%2C%200%2C%20255)%2C%203)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(result%2C%20f’Score%3A%20%7Bscore%7D’%2C%20(540%2C%20int(height*0.6-200))%2C%20cv2.FONT_HERSHEY_COMPLEX%2C%201%2C%20(0%2C%20153%2C%20255)%2C%203)%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pygame.mixer.music.stop()%20%20%23%20%E7%A8%8B%E5%BC%8F%E7%B5%90%E6%9D%9F%E6%99%82%E5%81%9C%E6%AD%A2%E8%83%8C%E6%99%AF%E9%9F%B3%E6%A8%82%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BD%BF%E7%94%A8%20addWeighted%20%E5%87%BD%E6%95%B8%E6%B7%B7%E5%90%88%E5%9C%96%E5%83%8F%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20cv2.addWeighted(img%2C%201%2C%20mask%2C%20alpha%2C%200)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E9%A1%AF%E7%A4%BA%E7%95%AB%E9%9D%A2%20%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.imshow(%22HandDetector%22%2C%20result)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E6%8C%89q%E5%81%9C%E6%AD%A2%E7%A8%8B%E5%BC%8F%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20cv2.waitKey(10)%20%26%200xFF%20%3D%3D%20ord(‘q’)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20except%20KeyboardInterrupt%3A%0A%20%20%20%20%20%20%20%20ser.close()%0A%20%20%20%20%20%20%20%20cap.release()%0A%20%20%20%20%20%20%20%20cv2.destroyAllWindows()%0A%20%20%20%20%0Aif%20__name__%20%3D%3D%20’__main__’%20%3A%0A%20%20%20%20main()” message=”mediapipe hands api and send data to Arduino” highlight=”” provider=”manual”/]
Arduino 端程式
Arduino 端程式就更簡單了,等候來自另一端的指令,進行 ASCII code 處理之後,用於控制 LED 亮暗。在此保留之後的彈性所以使用 analogWrite(LEDPin, 255); 語法,效果等同於 digitalWrite(LEDPin, HIGH);
[pastacode lang=”cpp” manual=”%2F%2FArduno%20digital%20%E8%85%B3%E4%BD%8D%0Aint%20LEDPin%20%3D%207%3B%0AString%20number%20%3D%20%22%22%3B%0Aint%20i%20%3D%200%3B%0Aint%20pwm_val%3B%20%2F%2F%20%E6%94%B9%E7%82%BA%E6%95%B4%E6%95%B8%E9%A1%9E%E5%9E%8B%0A%0Avoid%20setup()%0A%7B%0A%20%20%2F%2F%E5%90%84%E5%8D%94%E5%AE%9A%E9%80%9A%E8%A8%8A%E5%88%9D%E5%A7%8B%E5%8C%96%0A%20%20Serial.begin(9600)%3B%0A%20%20pinMode(LEDPin%2C%20OUTPUT)%3B%0A%7D%0A%0Avoid%20loop()%0A%7B%0A%20%20%2F%2F%E5%9F%B7%E8%A1%8Ccommand%E5%89%AF%E5%87%BD%E5%BC%8F%0A%20%20command()%3B%0A%7D%0A%0Along%20command()%0A%7B%0A%20%20while%20(Serial.available())%0A%20%20%7B%0A%20%20%20%20if%20(i%20%3D%3D%200)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20number%20%3D%20%22%22%3B%0A%20%20%20%20%7D%0A%20%20%20%20%2F%2F%20%E6%89%A3%E9%99%A4ASCII%E7%A2%BC%E5%80%BC%0A%20%20%20%20number%20%2B%3D%20Serial.read()%20-%2048%3B%0A%20%20%20%20i%2B%2B%3B%0A%20%20%7D%0A%20%20%2F%2F%20%E5%AD%97%E4%B8%B2%E8%BD%89%E6%8F%9B%E6%88%90%E6%95%B4%E6%95%B8%E5%80%BC%0A%20%20pwm_val%20%3D%20number.toInt()%3B%0A%20%20i%20%3D%200%3B%0A%0A%20%20Serial.println(pwm_val)%3B%0A%0A%20%20%2F%2FPWM%E6%8E%A7%E5%88%B6LED%0A%20%20if%20(pwm_val%3D%3D1)%0A%20%20%7B%0A%20%20analogWrite(LEDPin%2C%20255)%3B%0A%20%20delay(1000)%3B%0A%20%20analogWrite(LEDPin%2C%200)%3B%0A%20%20%7D%0A%7D” message=”” highlight=”” provider=”manual”/]
執行
執行以下指令即可看到畫面,伸出您的手指來點點球吧!可在畫面左上角看到 FPS、Score 與 HP 三個遊戲參數。

[pastacode lang=”bash” manual=”python%20mp_finger.py” message=”” highlight=”” provider=”manual”/]
如果要搭配 Arduino,則請先燒錄好 Arduino sketch 之後並修改以下指令的 COM 號:
[pastacode lang=”bash” manual=”python%20mp_finger.py%20–com%203″ message=”” highlight=”” provider=”manual”/]
Post Views: 243