머신러닝_이미지 인식 실습(손 인식해서 마스크 씌우기)

장준휴·2024년 1월 23일
0

딥러닝

목록 보기
3/3

코드

# 캠 연결하기
import cv2
import mediapipe as mp
import numpy as np
from sklearn.neighbors import KNeighborsClassifier

# 스파이더맨 이미지 관련 코드
spider = cv2.imread('./data/spider.jpg')
spider = cv2.resize(spider, (250,250))
mask2gray_spider = cv2.cvtColor(spider, cv2.COLOR_RGB2GRAY)
_, mask_b_spider = cv2.threshold(mask2gray_spider, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_spider = cv2.bitwise_not(mask_b_spider)
img_fg_spider = cv2.bitwise_and(spider,spider,mask = mask_b_inv_spider) 

# 마스크 이미지 관련 코드
mask = cv2.imread('./data/mask.jpg')# 225,225
mask2gray_mask = cv2.cvtColor(mask, cv2.COLOR_RGB2GRAY)
_, mask_b_mask = cv2.threshold(mask2gray_mask, 250, 255, cv2.THRESH_BINARY)
mask_b_inv_mask = cv2.bitwise_not(mask_b_mask)
img_fg_mask = cv2.bitwise_and(mask,mask,mask = mask_b_inv_mask) 

# 아이언맨
iron = cv2.imread('./data/ironman.jpg')# 225,225
mask2gray_iron = cv2.cvtColor(iron, cv2.COLOR_RGB2GRAY)
_, mask_b_iron = cv2.threshold(mask2gray_iron, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_iron = cv2.bitwise_not(mask_b_iron)
img_fg_iron = cv2.bitwise_and(iron,iron,mask = mask_b_inv_iron) 

# 헐크
hulk = cv2.imread('./data/hulk.jpg')# 225,225
mask2gray_hulk = cv2.cvtColor(hulk, cv2.COLOR_RGB2GRAY)
_, mask_b_hulk = cv2.threshold(mask2gray_hulk, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_hulk = cv2.bitwise_not(mask_b_hulk)
img_fg_hulk = cv2.bitwise_and(hulk,hulk,mask = mask_b_inv_hulk) 

# 선글라스 이미지 관련 코드
sun = cv2.imread('./data/sunglass.jpg')# 225,150 > 180,120
sun = cv2.resize(sun,(180,120))
mask2gray_sun = cv2.cvtColor(sun, cv2.COLOR_RGB2GRAY)
_, mask_b_sun = cv2.threshold(mask2gray_sun, 200, 255, cv2.THRESH_BINARY)
mask_b_inv_sun = cv2.bitwise_not(mask_b_sun)
img_fg_sun = cv2.bitwise_and(sun,sun,mask = mask_b_inv_sun) 

# 마스크이미지에서 사용할 영역의 값만 추출
video = cv2.VideoCapture(0)
cnt = 0

fps = 20
fcc = cv2.VideoWriter_fourcc(*'DIVX')
width = int(video.get(3))
height = int(video.get(4))

# 인식 가능한 11가지 동작
gesture = {
    0:'fist', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five',
    6:'six', 7:'rock', 8:'spiderman', 9:'yeah', 10:'ok',
}


# 동작 인식 모델 만들기(knn 모델)
file = np.genfromtxt('./data/gesture_train.csv',delimiter = ',')
X = file[:, :-1].astype(np.float32)
y = file[:, -1].astype(np.float32)
knn = KNeighborsClassifier(n_neighbors = 3)
knn.fit(X,y)

# mediapipe 사용하기
# 손찾기 관련 기능 불러오기
mp_hands = mp.solutions.hands
# 손 그려주는 기능 불러오기
mp_drawing = mp.solutions.drawing_utils
# 손찾기 관련 세부 설정
hands = mp_hands.Hands(
    max_num_hands = 1, # 탐지할 최대 손의 갯수
    min_detection_confidence = 0.5, # 표시할 손의 최소 정확도
    min_tracking_confidence = 0.5 # 표시할 관절의 최소 정확도
)

mp_face = mp.solutions.face_mesh
# 특징점 찾기 세부 기능
face = mp_face.FaceMesh(
    min_detection_confidence = 0.5, # 얼굴 표현할 최소 정확도
    min_tracking_confidence = 0.5 # 특징점 표현할 최소 정확도
)

video = cv2.VideoCapture(0)
while video.isOpened():
    ret, img = video.read()
    img = cv2.flip(img,1)
    # 파이썬이 인식 잘 하도록 BGR > RGB로 변경
    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    # 손 탐지하기
    result = hands.process(img)
    img = cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
    if not ret:
        break
    # 찾은 손 표시하기
    if result.multi_hand_landmarks is not None:
        # print(result.multi_hand_landmarks)  
        # 이미지에 손 표현하기
        for res in result.multi_hand_landmarks:
            joint = np.zeros((21,3)) # 21개 관절, xyz값 저장할 배열 생성
            # enumerate = for문의 순서 표현
            # 관절 값 저장
            for j , lm in enumerate(res.landmark):
                joint[j] = [lm.x, lm.y, lm.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 # 뼈의 값(x,y,z좌표값 > 벡터값)
            # 유클리디안 길이로 변환(피타고라스)
            # 뼈의 값(직선 값)
            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모델에 예측시키기
            # 학습을 위한 타입 변경(2차원 array)
            X_pred = np.array([angle], dtype = np.float32)
            results = knn.predict(X_pred)
            idx = int(results)

            # 인식된 제스쳐 표현하기
            img_x = img.shape[1]
            img_y = img.shape[0]
            hand_x = res.landmark[0].x
            hand_y = res.landmark[0].y
            
            
            # 어떤 동작에 어떤 가면을 출력할지 정하기
            # 0 : 마스크, 7/8 : 스파이더맨, 9 : 선글라스
            if idx == 0:
                cv2.putText(img, text = 'Mask', 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    # 0번이 입의점
                    lip = face_result.multi_face_landmarks[0].landmark[0]
                    x_lip = int(lip.x * img.shape[1])
                    y_lip = int(lip.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_lip -112 :y_lip + 113, x_lip -112 : x_lip + 113] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_mask)
                        bg_fg = cv2.add(img_bg, img_fg_mask)
                        img[y_lip -112 :y_lip + 113, x_lip -112 : x_lip + 113] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass
            
                
            elif idx == 7 or idx == 8:
                cv2.putText(img, text = 'SpiderMan', 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    nose = face_result.multi_face_landmarks[0].landmark[4]
                    x_nose = int(nose.x * img.shape[1])
                    y_nose = int(nose.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_nose -125 :y_nose + 125, x_nose -125 : x_nose + 125] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_spider)
                        bg_fg = cv2.add(img_bg, img_fg_spider)
                        img[y_nose -125 :y_nose + 125, x_nose -125 : x_nose + 125] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass
            elif idx == 9:
                cv2.putText(img, text = 'Sunglass', 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )

            elif idx == 5:
                cv2.putText(img, text = 'Iron Man', 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    nose = face_result.multi_face_landmarks[0].landmark[4]
                    x_nose = int(nose.x * img.shape[1])
                    y_nose = int(nose.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_iron)
                        bg_fg = cv2.add(img_bg, img_fg_iron)
                        img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass
            elif idx == 10:
                cv2.putText(img, text = 'Hulk', 
                       org = ( int(hand_x * img_x) , int(hand_y * img_y)+20 ),
                       fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255, 255, 255), thickness=2
                       )
                face_result = face.process(img)
                if face_result.multi_face_landmarks is not None:
                    nose = face_result.multi_face_landmarks[0].landmark[4]
                    x_nose = int(nose.x * img.shape[1])
                    y_nose = int(nose.y * img.shape[0])
                    # cv2.circle(img, (x_nose,y_nose), 20, (0,0,255), cv2.FILLED)
                    # 프레임마다 실행하는 코드
                    try :
                        roi = img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] # 어디 위치에 표현할건지
                        img_bg = cv2.bitwise_and(roi,roi,mask=mask_b_hulk)
                        bg_fg = cv2.add(img_bg, img_fg_hulk)
                        img[y_nose -112 :y_nose + 113, x_nose -112 : x_nose + 113] = bg_fg # 어디 위치에 표현할건지
                    except : 
                        pass
                      


            
            mp_drawing.draw_landmarks(img, res, mp_hands.HAND_CONNECTIONS)
            
    k = cv2.waitKey(30)
    if k == 49:
        break
    elif k == 50: # 캡쳐
        cv2.imwrite(f'./data/cap_sun{cnt}.png', img, params=[cv2.IMWRITE_PNG_COMPRESSION, 0] )
        cnt += 1

    elif k == 51: # 녹화 시작
        out = cv2.VideoWriter('./data/sun.avi', fcc, fps, (width, height))
        record = True
    elif k == 52: #녹화 종료
        record = False
        out.release() # 동영상 녹화 종료(동영상 종료는 아님)
    cv2.imshow('hand',img)
video.release()
cv2.destroyAllWindows()

결과


profile
나는야 토마토

0개의 댓글