[캐글스터디] 가위바위보 기계 만들기

뚜비·2022년 1월 21일
0

캐글스터디

목록 보기
3/5
post-thumbnail


✌가위✊바위🖐보 기계 만들기

가운데 손가락 모자이크 알고리즘 만들기 코드 자체가 가위바위보 기계 만들기 코드에서 응용한 것이기 때문에 먼저 가위바위보 기계 만들기 영상을 보고 왔다.

✅ 우리가 사용할 라이브러리

  • openCV(cv2) : 이미지 처리 라이브러리, 웹캠 사용을 위해 가져옴!
  • mediapipe : 구글에서 만든 크로스 플랫폼 프레임워크, 추론 모델과 미디어 처리 기능을 포함한 ML 솔루션 혹은 파이프라인들을 제공한다.(pip install mediapipe로 설치하면 됨)
    MediaPipe에 대한 친절한 설명
    영어로 된 친절한 설명
  • numpy : 행렬 연산 라이브러리


STEP 1. 빵형님 깃허브에서 코드 가져오기

손가락
해당 링크로 들어가서 single.py, dual.py 코드를 가져온다.



STEP 2. 간단한 원리 이해하기

(출처: [빵형의 개발도상국](https://youtu.be/udeQhZHx-00))
  1. MediaPipe로 손가락을 인식
  2. 손가락 마디 각도 계산
    : 즉 마디마다 포인트가 존재하는데 모든 포인트 간의 각도를 각각 계산
  3. 각도를 이용하여 제스쳐 인식(가위, 바위, 보 구분)
  4. 승자 결정


STEP 3. 코딩하기

먼저 한 손으로 가위바위보를 인식하는 코드이다. -> single.py

import cv2 # 웹캠 제어 및 ML 사용 
import mediapipe as mp # 손 인식을 할 것
import numpy as np

max_num_hands = 1 # 손은 최대 1개만 인식
gesture = { # **11가지나 되는 제스처 라벨, 각 라벨의 제스처 데이터는 이미 수집됨 (제스처 데이터 == 손가락 관절의 각도, 각각의 라벨)**
    0:'fist', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five',
    6:'six', 7:'rock', 8:'spiderman', 9:'yeah', 10:'ok',
}
rps_gesture = {0:'rock', 5:'paper', 9:'scissors'} # 우리가 사용할 제스처 라벨만 가져옴 

# MediaPipe hands model
mp_hands = mp.solutions.hands # 웹캠 영상에서 손가락 마디와 포인트를 그릴 수 있게 도와주는 유틸리티1
mp_drawing = mp.solutions.drawing_utils # 웹캠 영상에서 손가락 마디와 포인트를 그릴 수 있게 도와주는 유틸리티2

 # 손가락 detection 모듈을 초기화
hands = mp_hands.Hands(  
    max_num_hands=max_num_hands, # 최대 몇 개의 손을 인식? 
    min_detection_confidence=0.5, # 0.5로 해두는 게 좋다!  
    min_tracking_confidence=0.5)  

# 제스처 인식 모델 
file = np.genfromtxt('data/gesture_train.csv', delimiter=',') # **각 제스처들의 라벨과 각도가 저장되어 있음, 정확도를 높이고 싶으면 데이터를 추가해보자!** 
angle = file[:,:-1].astype(np.float32) # 각도
label = file[:, -1].astype(np.float32) # 라벨
knn = cv2.ml.KNearest_create() # knn(k-최근접 알고리즘)으로   
knn.train(angle, cv2.ml.ROW_SAMPLE, label) # 학습! 

cap = cv2.VideoCapture(0) 

while cap.isOpened(): # 웹캠에서 한 프레임씩 이미지를 읽어옴
    ret, img = cap.read()
    if not ret:
        continue

    img = cv2.flip(img, 1)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    result = hands.process(img)

    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)

    # 각도를 인식하고 제스처를 인식하는 부분 
    if result.multi_hand_landmarks is not None: # 만약 손을 인식하면 
        for res in result.multi_hand_landmarks: 
            joint = np.zeros((21, 3)) # joint == 랜드마크에서 빨간 점, joint는 21개가 있고 x,y,z 좌표니까 21,3
            for j, lm in enumerate(res.landmark):
                joint[j] = [lm.x, lm.y, lm.z] # 각 joint마다 x,y,z 좌표 저장

            # Compute angles between joints joint마다 각도 계산 
            # **공식문서 들어가보면 각 joint 번호의 인덱스가 나옴**
            v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19],:] # Parent joint
            v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],:] # Child joint
            v = v2 - v1 # [20,3]관절벡터 
            # Normalize v
            v = v / np.linalg.norm(v, axis=1)[:, np.newaxis] # 벡터 정규화(크기 1 벡터) = v / 벡터의 크기

            # Get angle using arcos of dot product **내적 후 arcos으로 각도를 구해줌** 
            angle = np.arccos(np.einsum('nt,nt->n',
                v[[0,1,2,4,5,6,8,9,10,12,13,14,16,17,18],:], 
                v[[1,2,3,5,6,7,9,10,11,13,14,15,17,18,19],:])) # [15,]

            angle = np.degrees(angle) # Convert radian to degree

            # Inference gesture 학습시킨 제스처 모델에 참조를 한다. 
            data = np.array([angle], dtype=np.float32)
            ret, results, neighbours, dist = knn.findNearest(data, 3) # k가 3일 때 값을 구한다! 
            idx = int(results[0][0]) # 인덱스를 저장! 

            # Draw gesture result
            if idx in rps_gesture.keys(): # 만약 인덱스가 가위바위보 중에 있다면 가위바위보 글씨 표시
                cv2.putText(img, text=rps_gesture[idx].upper(), org=(int(res.landmark[0].x * img.shape[1]), int(res.landmark[0].y * img.shape[0] + 20)), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2)

            # Other gestures 모든 제스처를 표시한다면 
            # cv2.putText(img, text=gesture[idx].upper(), org=(int(res.landmark[0].x * img.shape[1]), int(res.landmark[0].y * img.shape[0] + 20)), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2)

            mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS) # 손에 랜드마크를 그려줌 

    cv2.imshow('Game', img)
    if cv2.waitKey(1) == ord('q'):
        break

  • 11가지나 되는 제스처 라벨, 각 라벨의 제스처 데이터는 이미 수집됨 (제스처 데이터 == 손가락 관절의 각도, 각각의 라벨)
이런 제스처에는 다음 텍스트가 출력되도록 모아져 있다

  • gesture_train.csv 파일에 각 제스처들의 라벨과 각도가 저장되어 있음, 정확도를 높이고 싶으면 데이터를 추가해보자!

    각각의 제스처들을 모아놓음! 손가락 각도들과 마지막은 제스처 라벨이 적혀 있음.
    학습데이터가 110개라 정확도를 높이고 싶으면 더 추가해보자!

  • 공식문서 들어가보면 각 joint 번호의 인덱스가 나옴
    즉 joint[인덱스]한 것을 서로 다른 인덱스에 대해서 빼주면 각 관절에 대한 벡터들을 구한 것이다.

  • 내적 후 arcos으로 각도를 구해줌
    내적 : np.einsum (아인슈타인 표기법 으로 내적을 구함)
    각도 구하기 : arccos(내적값) = 각도(라디안)
    을 통해서 15개의 각을 구해줌



두 번째로 두 손이 들어왔을 때 코드는 위와 거의 동일하기 때문에 깃허브의 dual.py를 참고하길 바란다!
코드클릭!



❗ 결과 ❗

다음과 같은 결과가 나온다!

이제 뻐큐 모자이크를 만들러 가보자!!

profile
SW Engineer 꿈나무 / 자의식이 있는 컴퓨터

0개의 댓글