본문 바로가기
Study/Python

[Python] OpenCV - 9 Mediapipe-Hand

by YoungD 2023. 10. 31.
### 동영상과 mediapipe-hand 를 연결하기
import cv2
import mediapipe as mp

# 손을 찾는 기능 불러오기
mp_hands = mp.solutions.hands
# 특징점 그리기 설정
mp_drawing = mp.solutions.drawing_utils
# 손 특징점 찾기 관련 설정
hands = mp_hands.Hands(
    max_num_hands = 2,
    min_detection_confidence = 0.5, # 손 검출 확률(자체판단) 50%이상인 것들만 출력하기
    min_tracking_confidence = 0.5 # 특징점 검출 확률(자체 판단) 50%이상인 것들만 출력하기
)


cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, img = cap.read()
    img = cv2.flip(img, 1)
    if not ret:
        break
    # 이미지에서 원하는 대상(손) 찾기   
    result = hands.process(img)
    # 손을 검출했다면 표현하기(21개의 특징점을 찾음)
    if result.multi_hand_landmarks is not None:
        # 21개 특징점을 하나씩 그려주기
        for res in result.multi_hand_landmarks:
            mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS)
        
    
    cv2.imshow('video',img)
    if cv2.waitKey(33) == 49:
        break
cap.release()
cv2.destroyAllWindows()
  1. 손에서 특징점 21개 찾기 완료
  2. 특징점들을 연결해서 뼈 만들기
  3. 뼈와 뼈 사이의 각도 구하기
    • 동작에 따라 각도의 변화가 많은 15개의 각도 사용
  4. KNN에 예측시켜서 어떤 동작인지 구분하기
In [2]:
### 동영상과 mediapipe-hand 를 연결하기
## 한 손의 동작 인식하기
import cv2
import mediapipe as mp

gesture = {
    0:'fist', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five',
    6:'six', 7:'rock', 8:'spiderman', 9:'yeah', 10:'ok',
}

# gesture_train을 머신러닝 모델에 학습
import numpy as np
file = np.genfromtxt('images/gesture_train.csv', delimiter = ",")
angle = file[:, :-1].astype(np.float32) # 문제데이터
label = file[:, -1].astype(np.float32) # 정답데이터
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(angle, label)

# 손을 찾는 기능 불러오기
mp_hands = mp.solutions.hands
# 특징점 그리기 설정
mp_drawing = mp.solutions.drawing_utils
# 손 특징점 찾기 관련 설정
hands = mp_hands.Hands(
    max_num_hands = 1,
    min_detection_confidence = 0.5, # 손 검출 확률(자체판단) 50%이상인 것들만 출력하기
    min_tracking_confidence = 0.5 # 특징점 검출 확률(자체 판단) 50%이상인 것들만 출력하기
)

cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, img = cap.read()
    img = cv2.flip(img, 1)
    if not ret:
        break
    # 이미지에서 원하는 대상(손) 찾기   
    result = hands.process(img)
    # 손을 검출했다면 표현하기(21개의 특징점을 찾음)
    if result.multi_hand_landmarks is not None:
        # 21개 특징점을 하나씩 그려주기
        for res in result.multi_hand_landmarks:
            # 21개의 특징점 위치 저장할 용도(x,y,z)
            joint = np.zeros((21,3))
            # 21개의 특징점 저장
            # enumerate : 반복문의 순서를 알려줌, 추가적인 변수 하나가 더 필요
            for j, lm in enumerate(res.landmark):
                joint[j] = [lm.x, lm.y, lm.z]

            # 특징점을 연결해서 뼈의 값 구하기(좌표값 기반-x,y,z)
            v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19],:]
            v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],:]
            v = v2 - v1 # [20,3]
            # 뼈의 직선 구하기
            v = v / np.linalg.norm(v, axis=1)[:, np.newaxis]
            # 뼈의 직선을 통해서 각도 구하기(각도의 변화가 큰 15개만 사)
            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],:]))
            # radian 각도를 degree 각도로 변경하기
            angle = np.degrees(angle)

            # 학습된 KNN 모델에 동작 예측시키기
            # 예측시킬 데이터 전처리하기
            data = np.array([angle], dtype=np.float32)
            # 예측하기
            results = knn.predict(data)
            idx = int(results)
            
            # cv2.putText : 이미지 위에 글씨 쓰기
            # 사용할 이미지, 쓸 글씨(영어), 글씨 위치, 폰트, 글씨 크기, 글씨 색깔, 글씨 두께
            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('video',img)
    if cv2.waitKey(33) == 49:
        break
cap.release()
cv2.destroyAllWindows()
In [3]:
rsp_result
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 rsp_result

NameError: name 'rsp_result' is not defined
In [ ]:
### 동영상과 mediapipe-hand 를 연결하기
## 한 손의 동작 인식하기
import cv2
import mediapipe as mp

gesture = {
    0:'fist', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five',
    6:'six', 7:'rock', 8:'spiderman', 9:'yeah', 10:'ok',
}

rsp_gesture = {
    0:'rock', 9:'scissors', 5:'paper'
}

# gesture_train을 머신러닝 모델에 학습
import numpy as np
file = np.genfromtxt('images/gesture_train.csv', delimiter = ",")
angle = file[:, :-1].astype(np.float32) # 문제데이터
label = file[:, -1].astype(np.float32) # 정답데이터
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(angle, label)

# 손을 찾는 기능 불러오기
mp_hands = mp.solutions.hands
# 특징점 그리기 설정
mp_drawing = mp.solutions.drawing_utils
# 손 특징점 찾기 관련 설정
hands = mp_hands.Hands(
    max_num_hands = 2,
    min_detection_confidence = 0.5, # 손 검출 확률(자체판단) 50%이상인 것들만 출력하기
    min_tracking_confidence = 0.5 # 특징점 검출 확률(자체 판단) 50%이상인 것들만 출력하기
)

cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, img = cap.read()
    img = cv2.flip(img, 1)
    if not ret:
        break
    # 이미지에서 원하는 대상(손) 찾기   
    result = hands.process(img)
    # 손을 검출했다면 표현하기(21개의 특징점을 찾음)
    if result.multi_hand_landmarks is not None:
        # 가위바위보 결과 저장할 변
        rsp_result = []
        # 21개 특징점을 하나씩 그려주기
        for res in result.multi_hand_landmarks:
            # 21개의 특징점 위치 저장할 용도(x,y,z)
            joint = np.zeros((21,3))
            # 21개의 특징점 저장
            # enumerate : 반복문의 순서를 알려줌, 추가적인 변수 하나가 더 필요
            for j, lm in enumerate(res.landmark):
                joint[j] = [lm.x, lm.y, lm.z]

            # 특징점을 연결해서 뼈의 값 구하기(좌표값 기반-x,y,z)
            v1 = joint[[0,1,2,3,0,5,6,7,0,9,10,11,0,13,14,15,0,17,18,19],:]
            v2 = joint[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],:]
            v = v2 - v1 # [20,3]
            # 뼈의 직선 구하기
            v = v / np.linalg.norm(v, axis=1)[:, np.newaxis]
            # 뼈의 직선을 통해서 각도 구하기(각도의 변화가 큰 15개만 사)
            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],:]))
            # radian 각도를 degree 각도로 변경하기
            angle = np.degrees(angle)

            # 학습된 KNN 모델에 동작 예측시키기
            # 예측시킬 데이터 전처리하기
            data = np.array([angle], dtype=np.float32)
            # 예측하기
            results = knn.predict(data)
            idx = int(results)

            # cv2.putText : 이미지 위에 글씨 쓰기
            # 사용할 이미지, 쓸 글씨(영어), 글씨 위치, 폰트, 글씨 크기, 글씨 색깔, 글씨 두께

            # 동작이 주먹, 가위, 보 일때만 실행
            if idx in rsp_gesture.keys():
                # 주먹, 가위, 보의 동작만 글씨 쓰기
                org = (int(res.landmark[0].x * img.shape[1]), int(res.landmark[0].y * img.shape[0]))
                cv2.putText(img, text=rsp_gesture[idx].upper(), org=(org[0], org[1] + 20), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2)

                # 주먹, 가위, 보의 동작값 저장하기
                rsp_result.append({
                    'rsp' : rsp_gesture[idx],
                    'org' : org
                })
                
            mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS)

            # 누가 이겼나?
            # 두 개의 결과값이 저장이 되어야 실행
            if len(rsp_result) == 2:
                winner = None  # 누가 이겼는지 저장할 변수
                text = ''  # 어떤 동작이 이겼는지 저장 or 비김

                if rsp_result[0]['rsp'] == 'rock':
                    if rsp_result[1]['rsp'] == 'rock': text = 'Tie'
                    elif rsp_result[1]['rsp'] == 'paper': text = 'Paper Win'; winner = 1
                    elif rsp_result[1]['rsp'] == 'scissors' : text = 'Rock Win'; winner = 0
                elif rsp_result[0]['rsp'] == 'paper':
                    if rsp_result[1]['rsp'] == 'rock': text = 'Paper Win'; winner = 0
                    elif rsp_result[1]['rsp'] == 'paper': text = 'Tie'
                    elif rsp_result[1]['rsp'] == 'scissors' : text = 'Scissors Win'; winner = 1
                elif rsp_result[0]['rsp'] == 'scissors':
                    if rsp_result[1]['rsp'] == 'rock': text = 'Rock Win'; winner = 1
                    elif rsp_result[1]['rsp'] == 'paper': text = 'Scissors Win'; winner = 0
                    elif rsp_result[1]['rsp'] == 'scissors' : text = 'Tie'

                # 이긴 사람 표시하기
                if winner is not None:
                    cv2.putText(img, text='Winner', org=(rsp_result[winner]['org'][0], rsp_result[winner]['org'][1] + 70), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=2, color=(0, 255, 0), thickness=3)
                cv2.putText(img, text=text, org=(int(img.shape[1] / 3), 100), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=2, color=(0, 0, 255), thickness=3)
                
    
    cv2.imshow('video',img)
    if cv2.waitKey(33) == 49:
        break
cap.release()
cv2.destroyAllWindows()