50  손동작 인식 (Hand Gesture Recognition)

카메라 영상을 통해 손동작을 인식하고, 인식된 동작에 따라 특정 작업을 수행하는 소프트웨어를 개발해 봅시다. 예를 들어 손을 흔들면 인사를 하거나, 손가락 숫자에 따라 마우스 클릭이나 볼륨 조절 등의 명령을 내릴 수 있습니다.

이 프로젝트는 컴퓨터 비전과 머신러닝의 실제 적용 사례를 이해하는 데 아주 좋습니다. MediaPipe나 OpenCV와 같은 현대적인 라이브러리를 활용하여 손의 관절 포인트(Landmarks)를 추출하고, 이를 기반으로 동작을 분류하는 시스템을 설계해 보세요.

50.1 주요 개발 포인트

  • 컴퓨터 비전 라이브러리 활용: MediaPipe, OpenCV 등을 사용하여 손의 21개 랜드마크를 실시간으로 탐지합니다.
  • 손동작 분류 (Gesture Classification): 손가락의 펴진 상태나 위치 관계를 분석하여 ‘주먹’, ‘가위’, ‘보’ 등의 동작을 식별합니다.
  • 동작-명령 매핑: 인식된 손동작을 특정 키보드 입력이나 마우스 이벤트로 변환합니다.
  • 실시간 비디오 분석: 저지연(Low-latency) 처리를 통해 사용자의 움직임에 즉각 반응하는 시스템을 구축합니다.
  • 사용자 정의 제스처 학습: 사용자가 직접 동작을 가르치고 저장할 수 있는 기능을 추가합니다.

50.2 Python 구현 예시 (MediaPipe 활용 손동작 탐지 시뮬레이션)

# 실제 실행을 위해서는 mediapipe와 opencv-python이 필요합니다.
import cv2
import mediapipe as mp

class GestureRecognizer:
    """
    영상 프레임에서 손동작을 감지하고 분류합니다.
    """
    def __init__(self):
        # MediaPipe Hands 모델 초기화
        self.mp_hands = mp.solutions.hands
        self.hands = self.mp_hands.Hands(
            static_image_mode=False,
            max_num_hands=1,
            min_detection_confidence=0.5,
            min_tracking_confidence=0.5
        )
        print("손동작 인식 엔진 초기화 완료.")

    def process_frame(self, frame):
        """
        영상 프레임을 분석하여 손가락 개수를 세거나 제스처를 판별합니다.
        """
        if frame is None:
            return "인식 불가"

        # MediaPipe는 RGB 이미지를 사용하므로 BGR에서 RGB로 변환
        image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = self.hands.process(image_rgb)

        finger_count = 0
        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                # 손가락 끝(Tip) 번호: 엄지(4), 검지(8), 중지(12), 약지(16), 소지(20)
                # 손가락 마디(PIP/IP) 번호: 엄지(3), 검지(6), 중지(10), 약지(14), 소지(18)
                tips = [8, 12, 16, 20]
                pips = [6, 10, 14, 18]

                # 검지~소지: 끝 마디가 아래 마디보다 위에 있는지 확인 (Y값이 작을수록 위쪽)
                for tip, pip in zip(tips, pips):
                    if hand_landmarks.landmark[tip].y < hand_landmarks.landmark[pip].y:
                        finger_count += 1

                # 엄지: 간단하게 Y 좌표로 비교 (더 정확하게는 X 좌표나 각도 활용 가능)
                if hand_landmarks.landmark[4].y < hand_landmarks.landmark[3].y:
                    finger_count += 1
        
        # 손가락 개수에 따른 제스처 판별
        if finger_count == 2:
            gesture = "가위"
        elif finger_count == 5:
            gesture = "보"
        elif finger_count == 0:
            gesture = "주먹"
        else:
            gesture = f"손가락 {finger_count}개"
        
        print(f"인식된 제스처: {gesture} (손가락 {finger_count}개)")
        return gesture

    def execute_command(self, gesture):
        """
        제스처에 따라 시스템 명령을 실행합니다.
        """
        if gesture == "보":
            print("명령: 화면 스크롤 다운 실행!")
        elif gesture == "주먹":
            print("명령: 프로그램 종료 시퀀스 가동!")

if __name__ == "__main__":
    recognizer = GestureRecognizer()
    
    # 3번의 프레임 처리 시뮬레이션
    for _ in range(3):
        res = recognizer.process_frame(None)
        recognizer.execute_command(res)