DEV Community

Moji
Moji

Posted on

Ai detec มือ เพื่อใช้ในการเล่น game

Dino Game เป็นเกมที่เล่นใน Google Chrome เมื่อไม่มีอินเทอร์เน็ต โดยควบคุมไดโนเสาร์ให้กระโดดเพื่อหลีกเลี่ยงอุปสรรค คะแนนเพิ่มขึ้นเรื่อยๆ โดยเกมจะเริ่มช้าและเร่งขึ้นตามเวลา ซึ่งเป็นเกมที่นิยมในเว็บ
โดยใช้ Computer vision ในการเล่นโดยใช้ Mediapipe และ OpenCV เพื่อติดตามมือผู้เล่น เลือกจุดสำคัญบนมือแต่ละข้อ เพื่อกดปุ่ม Spacebar ในเกม แทนการกดปุ่มด้วยมือ ทำให้เล่นเกมสะดวกสบายและสนุกมากขึ้น
มาดูขั้นตอนการรันโค้ดเลยค้าบ

import cv2
import pyautogui
import mediapipe as mp
import time
Enter fullscreen mode Exit fullscreen mode
  1. เป็นการ import opencv ใช้สำหรับการประมวลผลภาพและวิดีโอใน Python
  2. เป็นไลบรารีที่ใช้สำหรับควบคุมคีย์บอร์ดและเมาส์ผ่าน Python
  3. เป็นไลบรารีที่ใช้สำหรับการประมวลผลภาพ
  4. เป็นไลบรารีสำหรับการจัดการเวลาใน Python
cap = cv2.VideoCapture(0)

mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=1,
                       min_detection_confidence=0.5, min_tracking_confidence=0.5)

mp_drawing = mp.solutions.drawing_utils
Enter fullscreen mode Exit fullscreen mode
  1. เปิดการใช้งานกล้องหรือวิดีโอจากอุปกรณ์ที่เปิดตัวเป็นกล้อง โดยเลข 0 หมายถึงการใช้กล้องของคอมพิวเตอร์
  2. เป็นการกำหนดตัวแปร mp_hand
  3. hands โดยกำหนดเป็นพารามิเตอร์ เป็น static_image_mode=False คือ การกำหนดให้โมเดลมือทำงานในโหมดวิดีโอแทนโหมดภาพเคลื่อนไหว max_num_hands=1 คือ การกำหนดให้ตรวจจับมือได้มือเพียงหนึ่งมือเท่านั้น min_detection_confidence=0.5 คือ การกำหนดค่าคงที่ขั้นต่ำในการตรวจจับมือ min_tracking_confidence=0.5 คือ การกำหนดค่าคงที่ขั้นต่ำในการติดตามการเคลื่อนไหวของมือ
  4. mp_drawing = mp.solutions.drawing_utils เป็นการเพิ่มความสามารถในการแสดงผล โดยการวาดเส้นเชื่อมจุดต่างๆของมือบนภาพ
while True:
    ret, frame = cap.read()
    if not ret:
        break

    image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    results = hands.process(image_rgb)
Enter fullscreen mode Exit fullscreen mode
  1. สร้างลูป while True เพื่ออ่านภาพจากกล้อง โดยใช้คำสั่ง cap.read และเก็บภาพที่ได้ไว้ในตัวแปร frame และตรวจสอบในการอ่านภาพว่าสำเร็จไหม ด้วยตัวแปร ret ถ้าอ่านไม่ได้ ให้หยุดลูปนั้น ด้วยคำสั่ง break
  2. ทำการแปลงสีของภาพจาก BGR ให้เป็น RGB ด้วยคำสั่ง cv2.cvtColor()
  3. เรียกใช้ตัวแปร hands ที่กำหนดไว้ มาทำการประมวลผลภาพของมือที่ได้รับจากกล้อง โดยใช้ภาพที่แปลงสีแล้ว ด้วยคำสั่ง hands.process(image_rgb) ซึ่งผลการตรวจจับมือจะเก็บมาไว้ในตัวแปร results
if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            thumb_tip = hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP]
            index_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]
            middle_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_TIP]
            ring_finger_tip = hand_landmarks.landmark[mp_hands.HandLandmark.RING_FINGER_TIP]
            pinky_tip = hand_landmarks.landmark[mp_hands.HandLandmark.PINKY_TIP]

            index_finger_mcp = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_MCP]
Enter fullscreen mode Exit fullscreen mode
  1. เป็นการตรวจสอบว่ามือที่ตรวจจับได้ในภาพหรือไม่ หากมีการตรวจจับได้ จะทำการลูปผ่านพิกัดของมือทั้งหมด ด้วยคำสั่ง for hand_landmarks in results.multi_hand_landmarks
  2. ทำการวาดเส้นเชื่อมต่อระหว่างจุดบนมือด้วยคำสั่ง mp_drawing.draw_landmarks() โดยแสดงเส้นเชื่อม ตามข้อมูลการเชื่อมที่ได้จากคำสั่ง mp_hands.HAND_CONNECTIONS
  3. เก็บพิกัดของจุดที่สำคัญบนมือ เช่น ปลายนิ้วมือต่างๆ ในตัวแปรต่างๆ thumb_tip , index_finger_tip , middle_finger_tip , ring_finger_tip , pinky_tip , index_finger_mcp
  4. เป็นการเข้าถึงพิกัดจุดที่สำคัญบนมือ ด้วยการใช้ mp_hands.HandLandmark.INDEX_FINGER_MCP ซึ่ง จะเป็นผลลัพธ์ของการตรวจจับมือที่ได้จาก Mediapipe ในภาพ และกำหนดค่าไว้ในตัวแปร index_finger_mcp
cv2.putText(frame, f"Thumb tip y: {round(thumb_tip.y, 2)}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
            cv2.putText(frame, f"Index tip y: {round(index_finger_tip.y, 2)}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
            cv2.putText(frame, f"Middle tip y: {round(middle_finger_tip.y, 2)}", (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
            cv2.putText(frame, f"Ring tip y: {round(ring_finger_tip.y, 2)}", (10, 120), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
            cv2.putText(frame, f"Pinky tip y: {round(pinky_tip.y, 2)}", (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

Enter fullscreen mode Exit fullscreen mode
  1. cv2.putText() เป็นการแสดงข้อความบนภาพวิดีโอที่มาจากกล้อง และแสดงค่าพิกัด y ของปลายนิ้วทั้ง 5 ซึ่งมี Thumb tip y : พร้อมกับค่าพิกัด y ของปลายนิ้วโป้ง และ แสดงบนภาพที่ตำแหน่ง (10,30) Index tip y : พร้อมกับค่าพิกัด y ของปลายนิ้วชี้ และ แสดงบนภาพที่ตำแหน่ง (10,60) Middle tip y : พร้อมกับค่าพิกัด y ของปลายนิ้วกลาง และ แสดงบนภาพที่ตำแหน่ง (10,90) Ring tip y : พร้อมกับค่าพิกัด y ของปลายนิ้วนาง และ แสดงบนภาพที่ตำแหน่ง (10,120) Pinky tip y : พร้อมกับค่าพิกัด y ของปลายนิ้วก้อย และ แสดงบนภาพที่ตำแหน่ง (10,150)
  2. cv2.FONT_HERSHEY_SIMPLEX แต่ละข้อความจะใช้ฟอนต์ และขนาดข้อความ 1 และสีข้อความเป็นสีเขียว (0,255,0) และความหนาของตัวอักษรเป็น 2
 is_hand_closed = (
                index_finger_tip.y > thumb_tip.y and
                middle_finger_tip.y > thumb_tip.y and
                ring_finger_tip.y > thumb_tip.y and
                pinky_tip.y > thumb_tip.y
            )
            is_hand_jump = (
                index_finger_tip.y < index_finger_mcp.y and
                middle_finger_tip.y < index_finger_mcp.y and
                ring_finger_tip.y > thumb_tip.y and
                pinky_tip.y > thumb_tip.y
            )

Enter fullscreen mode Exit fullscreen mode
  1. กำหนดเงื่อนไขสำหรับการตรวจสถานะของมือ โดย กำหนดขึ้นมา 2 ตัวแปร is_hand_closed และ is_hand_jump ที่จะเก็บค่า (true,false)ตามเงื่อนไข
  2. is_hand_closed เป็นการตรวจสอบว่ามือปิดหรือไม่โดยการดูจากตำแหน่ง y ของปลายนิ้วทั้ง 5 นิ้ว ว่า ตำแหน่งนิ้วก้อยอยู่ต่ำกว่าต่ำแหน่งนิ้วโป้งหรือไม่
  3. ถ้าปลายนิ้วทั้ง 4 อยู่ต่ำกว่าปลายนิ้วโป้ง แสดงว่ามือกำลังจะปิด และเก็บค่าเป็นค่า true
  4. is_hand_jump เป็นการตรวจสอบว่าปลายนิ้วชี้และนิ้วกลางอยู่ต่ำกว่าจุด MCP ของนิ้วชี้หรือไม่
if is_hand_closed:
                pass
            if is_hand_jump:
                pyautogui.press('space')
                time.sleep(0.1)


    cv2.imshow('CV', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
Enter fullscreen mode Exit fullscreen mode
  1. คำสั่ง if is_hand_closed ตรวจสอบว่า มือปิดหรือไม่ ถ้าใช่ จะเป็น pass
  2. คำสั่ง if is_hand_jump ตรวจสอบว่ามือกำลังมีการเคลื่อนไหวหรือกระโดด ถ้าใช่ จะเป็นการใช้ pyautogui.press('space') เพื่อจำลองการกดปุ่ม space บนแป้นพิมพ์ การหน่วงเวลา 0.1 วินาทีหลังการกดปุ่มเพื่อป้องกันการกดซ้ำที่รวดเร็วเกินไป
  3. cv2.imshow('CV', frame) เป็นการแสดงผลภาพวิดีโอที่ได้จากกล้อง
  4. คำสั่ง if cv2.waitKey(1) & 0xFF == ord('q'): เป็นการใช้สำหรับการออกจาก loop และปิดหน้าต่างโปรแกรมเมื่อผู้ใช้กดปุ่ม 'q'
  5. คำสั่ง cap.release() และ cv2.destroyAllWindows() เป็นการใช้สำหรับปิดการจับภาพวิดีโอและปิดหน้าต่าง GUI ทั้งหมดตามลำดับ

สรุปผล
Dino Game การเล่นจะมีความท้าทายเพิ่มมากขึ้นตามความเร็วที่เพิ่มขึ้น Computer vision ในการเล่นเกมโดยใช้ Mediapipe และ OpenCV เพื่อทำให้ผู้เล่นสามารถควบคุมไดโนเสาร์ด้วยการขยับมือหรือนิ้วแทนการกดปุ่ม Spacebar ในคีย์บอร์ด การนำไปต่อยอดในการเพิ่มความสะดวกสบายและความสนุกในการเล่นเกมที่มีความหลากหลายมากขึ้นจากวิธีการเล่นแบบเดิมๆ ด้วยการใช้ Computer vision เพื่อแทนการกดปุ่ม Spacebar ในกระโดดของเกม Dino Game

ตัวอย่างการ detec มือ
Image description

Reference

Top comments (0)