본문 바로가기
공부공부/2023 쌓여가는 나의 지식~

Mediapipe를 이용한 손 인식

by Lee_story_.. 2023. 11. 1.
728x90

저번 글을 통해 Mediapipe를 이용해서 얼굴을 인식하여 모자이크 처리를 해 보았습니다. 

 

 

Mediapipe를 이용한 얼굴 모자이크(Blur)처리

Mediapipe란 구글에서 개발한 오픈소스 플랫폼으로 각종 딥러닝을 통한 인식들을 지원합니다. 주로 사람의 손, 얼굴, 각 관절의 Keypoint들을 추적하여 인식하는 기능을 가지고 있습니다. https://github.

ljhyunstory.tistory.com

 

 

이번에는 Mediapipe를 이용하여 손을 인식하여 보겠습니다.  

 

 

 

설치는 

 

아래처럼 라이브러리를 설치해주면 끝!

pip install mediapipe

 

 

 

이번엔 손 인식에 대한 코드를 작성해 보겠습니다. 

 

기본적으로 아래처럼 라이브러리를 통해 손 인식 모듈을 불러와 줍시다. 

import cv2
import mediapipe as mp

mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands

 

다음은 모듈을 설정해 줍시다. 

hands = mp_hands.Hands(
    static_image_mode=True,
    max_num_hands=10,
    min_detection_confidence=0.01)

여기에는 많은 옵션들이 있는데 아래와 같습니다.

 

 

static_image_mode  (정적이미지 모드)

false로 설정된 경우 솔루션은 입력 이미지를 비디오 스트림으로 처리합니다. 첫 번째 입력 이미지에서 손을 감지하고 탐지가 성공하면 손 랜드마크의 위치를 추가로 파악합니다. 후속 영상에서 max_num_hands만큼의 손이 감지되고 해당 손 랜드마크가 기준이 되면 손을 추적할 수 없을 때까지 새로 기준을 잡지 않고 랜드마크만 추적한다. 따라서 대기 시간이 단축되고 비디오 프레임 처리에 이상적입니다. true로 설정하면 모든 입력 이미지에서 손 감지가 실행되므로 이미지 별로 관련 없는 이미지 일괄 처리에 이상적입니다. 기본값은 false입니다.

 

( >> 만약 처리하려는 이미지가 비디오 프레임단위의 이미지들이라면! 설정하는 부분인것 같습니다.)

 

max_num_hands  (손 최대 갯수)

탐지할 수 있는 최대 손 수입니다. 기본값은 2입니다.

 

model_complexity  (모델복잡성)

핸드 랜드마크 모델의 복잡성은 0 또는 1입니다. 랜드마크 정확도와 추론 지연 시간은 모델 복잡성과 함께 증가합니다. 기본값은 1입니다.

(사람인식의 경우 모델 복잡성이 2까지 지원되지만 손은 0과 1까지만 지원합니다.)

 

min_detection_confidence  (최소탐지 신뢰도)

탐지가 성공한 것으로 간주되는 손 감지 모델의 최소 신뢰 값([0.0, 1.0]). 기본값은 0.5입니다.

 

 

 

min_trackin_confidence  (최소_추척_신뢰도)

손 랜드마크 추적 모델의 최소 신뢰 값([0.0, 1.0])이 성공적으로 추적된 것으로 간주됩니다 만약 그렇지 않다면 다음 입력 영상에서 기준 손 탐지가 자동으로 호출됩니다. 이 솔루션을 높은 값으로 설정하면 지연 시간이 길어지는 대신 솔루션의 정확성을 높일 수 있습니다. static_image_mode가 True라면 무시됩니다. 여기서 손 감지는 모든 이미지에 대해 실행됩니다. 기본값은 0.5입니다.

 

(이 옵션이 가장 난해 한 것 같은데,

static_image_mode와 관련있는 것으로 보아

만약 입력되는 이미지들이 비디오 프레임 이미지들일 경우, 추적하여 인식해주는 정도로 min_detection_confidence와 동일한 역할을 하는것 같습니다.)

 

 

 

이렇게 설정을 끝내고 나면 아래처럼 이미지를 넣어 실행해주면 끝!

file=['testHands.jpg']  ## 파일 추가 가능~


for idx, file in enumerate(IMAGE_FILES):
        
        image = cv2.flip(cv2.imread(file), 1)
        results = hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

        # 손으로 프린트하고 이미지에 손 랜드마크를 그립니다.
        print('Handedness:', results.multi_handedness)
        if not results.multi_hand_landmarks:
            continue
        image_height, image_width, _ = image.shape
        annotated_image = image.copy()

        for hand_landmarks in results.multi_hand_landmarks:
            mp_drawing.draw_landmarks(
                annotated_image,
                hand_landmarks,
                mp_hands.HAND_CONNECTIONS,
                mp_drawing_styles.get_default_hand_landmarks_style(),
                mp_drawing_styles.get_default_hand_connections_style())
        
        cv2.imwrite(
            'annotated_image' + str(idx) + '.png', cv2.flip(annotated_image, 1))

 

아래처럼 손 관절 부분이 그려지며 인식되는것을 볼 수 있습니다!

 

 

 

사각박스로만 표시하고 싶다면

 

 

 

함수를 하나추가해서 

def get_bbox_coordinates(handLadmark, image_shape):

    all_x, all_y = [], [] 
    for hnd in mp_hands.HandLandmark:
        all_x.append(int(handLadmark.landmark[hnd].x * image_shape[1])) 
        all_y.append(int(handLadmark.landmark[hnd].y * image_shape[0])) 

    return min(all_x), min(all_y), max(all_x), max(all_y)

 

아래 부분을 주석처리하고

# mp_drawing.draw_landmarks(
#     annotated_image,
#     hand_landmarks,
#     mp_hands.HAND_CONNECTIONS,
#     mp_drawing_styles.get_default_hand_landmarks_style(),
#     mp_drawing_styles.get_default_hand_connections_style())

 

 

그자리에 이렇게 추가하면 cv2를 이용해서 그려줄 수 있습니다.

bboxC=get_bbox_coordinates(hand_landmarks,image.shape)  ## 손 따라 잡기
cv2.rectangle(annotated_image, (bboxC[0], bboxC[1]), (bboxC[2], bboxC[3]), (255, 0, 255), 2)

 

 

 

 

끝!

 

대부분의 손을 Mediapipe로 인식할 수 있지만 테스트해보니 너무가깝거나, 멀리 있는 손의 경우 인식하지 못했습니다. 

정확하지 않지만 30~60cm 사이에 있는 손의 경우는 대부분 인식하는것 같습니다.

 

손이 인식되지 못하는 경우는 이미지 내의 범위를 지정하여 잘라서 그부분을 따로 인식하는 분할 인식 방법으로 해결 할 수 있을것 같습니다.

 

 

<ALL>

 

import cv2
import mediapipe as mp

mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands


def get_bbox_coordinates(handLadmark, image_shape):## 박스

    all_x, all_y = [], [] 
    for hnd in mp_hands.HandLandmark:
        all_x.append(int(handLadmark.landmark[hnd].x * image_shape[1])) 
        all_y.append(int(handLadmark.landmark[hnd].y * image_shape[0])) 

    return min(all_x), min(all_y), max(all_x), max(all_y)



def opHand(IMAGE_FILES):
    for idx, file in enumerate(IMAGE_FILES):

        image = cv2.flip(cv2.imread(file), 1)
        results = hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))

        # 손으로 프린트하고 이미지에 손 랜드마크를 그립니다.
        print('Handedness:', results.multi_handedness)
        if not results.multi_hand_landmarks:
            continue
        image_height, image_width, _ = image.shape
        annotated_image = image.copy()

        for hand_landmarks in results.multi_hand_landmarks:
            # mp_drawing.draw_landmarks(
            #     annotated_image,
            #     hand_landmarks,
            #     mp_hands.HAND_CONNECTIONS,
            #     mp_drawing_styles.get_default_hand_landmarks_style(),
            #     mp_drawing_styles.get_default_hand_connections_style())
        

            bboxC=get_bbox_coordinates(hand_landmarks,image.shape)  ## 손 따라 잡기
            cv2.rectangle(annotated_image, (bboxC[0], bboxC[1]), (bboxC[2], bboxC[3]), (255, 0, 255), 2)
        
        cv2.imwrite(
            'annotated_image' + str(idx) + '.png', cv2.flip(annotated_image, 1)) 
            
            
            
            
file=['testHands.jpg']

opHand(file)

 

 

 

 

<미디어파이프 git>

https://github.com/google/mediapipe/blob/master/docs/solutions/hands.md

 

댓글