한종서 2024. 7. 31. 11:47

영상인식

import cv2
import mediapipe as mp
import numpy as np
import random
import pygame
import serial  # 라이브러리 불러오기

choice = ['rock', 'scissors', 'paper']
computer_selec = -1

def calculate_angle(a, b, c):
    """a, b, c는 각각 landmark의 좌표. b는 가운데 점."""
    ba = np.array([a.x - b.x, a.y - b.y])
    bc = np.array([c.x - b.x, c.y - b.y])
   
    cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
    angle = np.arccos(cosine_angle)
   
    return np.degrees(angle)

def get_random_choice():
    computer_selec = random.randint(0,2)
    return choice[computer_selec]

def play_game():
    global uart
    # Pygame 초기화 및 MP3 파일 재생
    pygame.mixer.init()
    pygame.mixer.music.load('가위바위보.mp3')
    pygame.mixer.music.play()

    # MP3 파일 재생이 끝날 때까지 대기
    while pygame.mixer.music.get_busy():
        continue
   
    computer_choice = get_random_choice()
    send_data = ''
    while True:
        ret, frame = cap.read()
        if not ret:
            break
       
        frame = cv2.flip(frame, 1)
        image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = hands.process(image)

        user_choice = None

        if results.multi_hand_landmarks:
            folding = ""
            angle_list=[]
            for hand_landmarks in results.multi_hand_landmarks:
                h1 = hand_landmarks.landmark

                mp_drawing.draw_landmarks(
                    frame, hand_landmarks, mp_hands.HAND_CONNECTIONS, circle_style, line_style)

                for mcp, dip, tip in compare:
                    angle = abs(calculate_angle(h1[mcp], h1[dip], h1[tip]))
                    angle_list.append(angle)
                    folding += "01"[angle > 140]  # 각도가 140도보다 크면 손가락을 폈다.
            #print( folding)
            #print( angle_list[0], angle_list[1], angle_list[2], angle_list[3], angle_list[4])
            cv2.putText( frame, folding, (50,50), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 1, (0,0,0), 2)
           
            # 가위바위보 판정
            if folding == "00000" or folding == "10000":
                user_choice = 'rock'
            elif folding == "11111":
                user_choice = 'paper'
            elif folding == "01000" or folding == "01100" or folding == "11000" or folding == "11100":
                user_choice = 'scissors'
            else:
                user_choice = 'none'

            if user_choice:
                cv2.putText(frame, f"User: {user_choice}", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
                cv2.putText(frame, f"Computer: {computer_choice}", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

                # 컴퓨터 선택 이미지 표시
                comp_img = computer_images[computer_choice]
                if comp_img is not None:
                    comp_img = cv2.resize(comp_img, (200, 200))  # 이미지 크기 조정
                    frame[50:250, -250:-50] = comp_img  # 화면에 이미지 표시

                if send_data == '':
                    # 부저 소리 재생
                    if computer_choice == user_choice:
                        send_data = 'd'
                    elif (user_choice == 'rock' and computer_choice == 'scissors') or \
                        (user_choice == 'paper' and computer_choice == 'rock') or \
                        (user_choice == 'scissors' and computer_choice == 'paper'):
                        send_data = 'w'
                    else:
                        send_data = 'l'
                    uart.write(bytes(send_data,'utf8'))
                #win_selec = choice[(computer_selec+1)%3]
                #if send_data == '':
                #    if computer_choice == user_choice:
                #        send_data = 'd'
                #    elif user_choice == win_selec:
                #        send_data = 'l'
                #    else:
                #        send_data = 'w'
                #    uart.write(bytes(send_data,'utf8'))

                cv2.putText(frame, f"send_data: {send_data}", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
        cv2.imshow('Hand Tracking', frame)

        key = cv2.waitKey(1)
        if key == 27:  # 27 == 'ESC'
            break
        elif key == ord('r'):  # 'r' 키를 눌러 게임을 다시 시작
            return True

    return False


# 시리얼 통신을 위한 인스턴스 생성
uart = serial.Serial("COM4",115200)


# Mediapipe 설정
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
hands = mp_hands.Hands(max_num_hands=1)

line_style = mp_drawing.DrawingSpec(
    color=(166, 151, 18),
    thickness=5
)

circle_style = mp_drawing.DrawingSpec(
    color=(166, 115, 31),
    thickness=2,
    circle_radius=5
)

# 비디오 캡처 설정
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1080)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)

# 엄지손가락을 제외한 네 개의 손가락 비교
compare = [(2, 3, 4), (5, 6, 8), (9, 11, 12), (13, 15, 16), (17, 19, 20)]

# 컴퓨터의 선택 이미지 불러오기
computer_images = {
    'rock': cv2.imread('rock.png'),
    'paper': cv2.imread('paper.png'),
    'scissors': cv2.imread('scissors.png')
}

# 값 보내기
uart.write(bytes('s','utf8'))

while True:
    if not play_game():
        break
       
cap.release()
cv2.destroyAllWindows()



 

보드

# UART 모듈 불러오기
from machine import Pin, UART
from neopixel import NeoPixel
from time import sleep
import random
from buzzer import *

# UART 시리얼 통신 인스턴스 생성 및 시작
# tx와 rx가 각각 1번, 3번 핀
uart=UART(2, baudrate=115200, tx=17, rx=16)
uart.init()



NUM_OF_LED=16
np=NeoPixel(Pin(18),NUM_OF_LED)


# 부저 설정
bu = BUZZER(2)

# 각 결과에 따른 멜로디
win_melody = [C4, D4, E4, F4, G4, A4, B4, C5]
lose_melody = [C5, B4, A4, G4, F4, E4, D4, C4]
draw_melody = [G4, G4, G4, G4, G4, G4, G4, G4]




def start():
    bu.play(draw_melody, 100)
    for i in range(16):
        np[i]=(0,0,0)
        np.write()

def win():
    bu.play(win_melody, 100)
    for i in range(16):
        np[(i-2)%16]=(0,0,0)
        np[(i-1)%16]=(0,0,0)
        np[(i)%16]=(random.randint(1, 255),random.randint(1, 255),random.randint(1, 255))
        np[(i+1)%16]=(random.randint(1, 255),random.randint(1, 255),random.randint(1, 255))
        np[(i+2)%16]=(random.randint(1, 255),random.randint(1, 255),random.randint(1, 255))
        np.write()
        sleep(0.05)
       
    for i in range(16):
        np[i]=(0,0,0)
        np.write()
   
   
def lose():
    bu.play(lose_melody, 100)
    for i in range(16):
        np[i]=(255,0,0)
        np.write()

    sleep(1)
    for i in range(16):
        np[i]=(0,0,0)
        np.write()
   
   
def draw():
    bu.play(draw_melody, 100)

       
    for i in range(16):
        np[i]=(0,255,0)
        np.write()

    sleep(1)
    for i in range(16):
        np[i]=(0,0,0)
        np.write()
   
# UART로부터 값을 읽는 함수
def uart_receive():
    # 앞에서 정의한 uart 변수 사용
    global uart
    # uart로부터 받는 신호가 포착되면
    if uart.any():
        # 그 줄을 읽어서
        data = uart.readline()
        data = data.decode('ascii').rstrip()
        # 바이트 값을 ascii코드로 해석
        # rstrip: 줄의 오른쪽 끝의 줄바꿈, 띄어쓰기 제거
        # 읽어낸 값을 내보냄
        print(data)
        return data
    # 포착된 신호가 없다면 None을 내보
    return None


# 계속 반복
while 1:
# uart 통신으로 데이터 받기
# 전화기를 귀에 대고 있어야 들리는 것과 같음
# 지속적으로 값을 받아야함
    data = uart_receive()
    # 그 데이터가 각각 r,g,b값에 해당하면 해당 LED 색 켜기
    if data:
        print(data)
    if data == 's':
        start()
    elif data == 'w':
        win()
    elif data == 'd':
        draw()
    elif data == 'l':
        lose()