2.인공지능/MediaPipe

8.MediaPipe Hand Landmarker로 손 랜드마크 감지 : Python

쿼드큐브 2025. 6. 13. 14:38
728x90
반응형

MediaPipe Hand Landmarker를 사용하면 이미지나 동영상에서 손의 21개 주요 지점을 정밀하게 감지할 수 있습니다

 

MediaPipe Hand Landmarker로 손 랜드마크 감지 : Python

 

목차

1. MediaPipe Hand Landmarker란?

2. Hand Landmarker 동작방식

3. 설정 옵션

4. Hand Landmarker 테스트 코드 : Python

5. Hand Landmarker 테스트 결과

관련 글 링크

 

 

1. MediaPipe Hand Landmarker란?

MediaPipe Hand Landmarker는 이미지 또는 실시간 영상 스트림에서 손의 위치를 식별하고 21개의 손가락 관절 포인트(랜드마크)를 추출하는 머신러닝 기반 솔루션입니다.
손의 방향(왼손/오른손)까지 식별할 수 있어, 제스처 인식, 증강현실, 터치 인터페이스 구현에 유용하게 활용됩니다

 

2. Hand Landmarker 동작방식

Hand Landmarker는 두 개의 모델로 구성된 모델 번들을 사용합니다:

  • 손바닥 감지 모델 (Palm Detection): 전체 이미지에서 손을 찾습니다.
  • 손 랜드마크 감지 모델 (Landmark Model): 감지된 손 부위 내에서 21개 지점을 정밀하게 예측합니다.

손바닥 감지 모델은 연산량이 크므로, 실시간 모드에서는 첫 감지 이후에는 경량 추적 알고리즘을 통해 다음 프레임에서 손을 추적합니다. 손이 사라졌다고 판단되면 손바닥 감지를 다시 수행합니다.

손이 프레임에서 사라졌다가 다시 나타날 때도 같은 손으로 인식할 수 있나요?
A. 기본적으로는 아닙니다. 하지만 좌우 손잡이 판단 및 위치 기반 클러스터링을 이용해 임의의 손을 추적하도록 보완할 수 있습니다.

 

◆ 모델

손 랜드마크 모델 번들은 감지된 손 영역 내에서 21개의 손가락 관절 좌표의 주요 지점 위치를 감지합니다.

이 모델은 약 30, 000개의 실제 이미지와 여러 배경에 적용된 여러 개의 렌더링된 합성 손 모델을 사용하여 학습되었습니다.

모델이름 Input Shape 양자화 유형
HandLandmarker (전체) 192 x 192, 224 x 224 부동 소수점 수 16 (Float16)

MediaPipe Hand Landmarker
출처:https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker?hl=ko

 

다음은 위의 사전 학습된 모델을 기반으로 한 전체 파이프라인의 태스크 벤치마크입니다. 지연 시간 결과는 CPU / GPU를 사용하는 Pixel 6의 평균 지연 시간입니다.

기기 CPU GPU
Pixel 6 17.12ms 12.27ms

 

◆ 입력과 출력

  • 입력 이미지 처리 - 처리에는 이미지 회전, 크기 조절, 정규화, 색상 공간 변환이 포함됩니다.
  • 점수 기준점: 예측 점수를 기준으로 결과를 필터링합니다.
입력 출력
- 정지 이미지
- 디코딩된 동영상 프레임
- 라이브 동영상 피드
- 감지된 손의 손잡이 (왼손/오른손)
- 이미지 좌표에서 감지된 손의 랜드마크
- 세계 좌표에서 감지된 손의 랜드마크

 

◆ GestureRecognizerResult 포맷 예시

🖐 Hand #1
──────────────────────────────────────────────
1. 손잡이 (왼손/오른손) 식별
🔹 Handedness
  - category_name: "Left"
  - score        : 0.9974
  - index        : 1

2. 2D 이미지 좌표 기반 손 랜드마크
🔹 Landmarks (Normalized - image space)
  - Total points: 21
  - Sample:
    - [0] x=0.6816, y=1.1222, z=+0.0000
    - [1] x=0.7000, y=1.0495, z=-0.0068
    - [2] x=0.6860, y=0.9710, z=-0.0172
    - ...
    - [20] x=0.4673, y=1.0535, z=-0.0371

3. 3D 세계 좌표 기반 손 랜드마크 (정규화된 위치)
🔹 Landmarks (World - 3D space)
  - Total points: 21
  - Sample:
    - [0] x=+0.0261, y=+0.0451, z=+0.0416
    - [1] x=+0.0260, y=+0.0208, z=+0.0350
    - [2] x=+0.0218, y=+0.0061, z=+0.0358
    - ...
    - [20] x=-0.0366, y=+0.0138, z=+0.0400
항목 설명
handedness 해당 손이 Left/Right 중 어떤 것으로 분류되었는지, 추
hand_landmarks 0~1 사이 정규화된 좌표 (이미지 기준)
hand_world_landmarks 실제 세계 단위의 3D 좌표 (카메라 캘리브레이션 기준), m 단위로 추정됨

 

3. 설정 옵션

MediaPipe는 세부 조정을 위한 다양한 옵션을 제공합니다:

옵션 설명 값 범위 기본 값
running_mode Gesture Recognizer의 실행 모드를 설정합니다.
- IMAGE: 단일 이미지 입력 모드
- VIDEO: 디코딩된 동영상 프레임 입력 모드
- LIVE_STREAM: 실시간 카메라 입력 모드
  (이 경우 result_callback 필수)
{"IMAGE", "VIDEO", "LIVE_STREAM"} "IMAGE"
num_hands 동시에 감지 가능한 손의 최대 수 정수 ≥ 1 1
min_hand_detection_confidence 손바닥 감지 성공으로 간주하기 위한 최소 신뢰도
(Palm Detection 단계에서 사용)
0.0 ~ 1.0 0.5
min_hand_presence_confidence 손 랜드마크 모델에서 손이 존재한다고 판단하기 위한 최소 신뢰도.
이 값보다 낮으면 palm detection을 다시 실행함
0.0 ~ 1.0 0.5
min_tracking_confidence 현재 프레임과 이전 프레임 간 손 추적이 성공으로 간주될 최소 신뢰도 (IoU 기반 추적) 0.0 ~ 1.0 0.5
result_callback LIVE_STREAM 모드에서 분류 결과를 비동기 방식으로 수신하는 리스너 함수 설정 ResultListener 객체 없음

 

4. Hand Landmarker 테스트 코드 : Python

1. 모델 로드 및 옵션 설정

  • MediaPipe에서 제공하는 .task 모델 파일 경로를 지정하고 로드합니다.
  • GestureRecognizerOptions 객체를 통해 최대 감지 손 수, 신뢰도 임계값, 스트리밍 모드, 콜백 함수 등을 지정합니다.
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
import os
import cv2  # OpenCV: 이미지/비디오 처리 라이브러리
import numpy as np  # NumPy: 수치 계산 라이브러리

# 현재 파일의 디렉토리 경로를 기준으로 모델 파일 경로를 설정합니다.
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
model_path = os.path.join(BASE_DIR, './models/hand_landmarker.task')

# 모델 파일이 존재하지 않으면 예외를 발생시킵니다.
if not os.path.exists(model_path):
    raise FileNotFoundError(f"Model file not found at path: {model_path}")

# MediaPipe 모델 옵션을 설정합니다. (CPU 사용)
base_options = python.BaseOptions(model_asset_path=model_path, 
                                  delegate=python.BaseOptions.Delegate.CPU)

# MediaPipe에서 정의한 21개 손 관절의 연결 구조입니다.
HAND_CONNECTIONS = [
    (0, 1), (1, 2), (2, 3), (3, 4),       # 엄지
    (0, 5), (5, 6), (6, 7), (7, 8),       # 검지
    (5, 9), (9, 10), (10, 11), (11, 12),  # 중지
    (9, 13), (13, 14), (14, 15), (15, 16),# 약지
    (13, 17), (17, 18), (18, 19), (19, 20),# 새끼
    (0, 17)                               # 손목 → 새끼 기저 연결
]

# 콜백에서 처리된 결과 이미지를 임시로 저장하는 큐입니다.
frame_queue = []

# HandLandmarker의 옵션을 설정합니다.
options = vision.HandLandmarkerOptions(
    base_options=base_options,  # 모델 및 디바이스 옵션
    running_mode=vision.RunningMode.LIVE_STREAM,  # 실시간 스트림 모드
    num_hands=1,  # 최대 감지 손 개수
    min_hand_detection_confidence=0.5,  # 손 감지 신뢰도 임계값
    min_hand_presence_confidence=0.5,  # 손 존재 신뢰도 임계값
    min_tracking_confidence=0.5,  # 손 추적 신뢰도 임계값
    result_callback=detect_callback,  # 결과 콜백 함수
)

 

2. Cam 연결 및 실시간 손 랜드마크 인식

  • OpenCV를 통해 기본 웹캠을 활성화하고 프레임을 읽습니다.
  • MediaPipe의 HandLandmarker 객체에서 detect_async()를 호출하여 손 랜드마크를 비동기 방식으로 감지합니다.
  • 프레임마다 처리된 결과가 있을 경우 frame_queue에서 시각화된 프레임을 꺼내 cv2.imshow()로 화면에 출력합니다.
# 웹캠(기본 장치)에서 비디오 캡처를 시작합니다.
cap = cv2.VideoCapture(0)
if not cap.isOpened():
    raise RuntimeError("Could not open video device")

try:
    # HandLandmarker를 생성하고, 웹캠 프레임을 반복적으로 처리합니다.
    with vision.HandLandmarker.create_from_options(options) as detector:
        while cap.isOpened():
            ret, frame = cap.read()  # 프레임 캡처
            if not ret:
                print("Failed to capture image")
                break

            # BGR(OpenCV) 이미지를 RGB로 변환
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            # OpenCV 이미지를 MediaPipe 이미지 객체로 변환
            image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb)

            # 현재 프레임의 타임스탬프(ms) 추출
            elapsed_time_ms = int(cap.get(cv2.CAP_PROP_POS_MSEC))

            # 비동기 손 감지 실행 (콜백에서 결과 처리)
            detector.detect_async(image, timestamp_ms=elapsed_time_ms)

            # 큐에 처리된 프레임이 있으면 시각화하여 출력
            if frame_queue:
                visualized_frame = frame_queue[0]
                bgr_frame = cv2.cvtColor(visualized_frame, cv2.COLOR_RGB2BGR)
                cv2.imshow('LandMarks', bgr_frame)
                frame_queue.clear()
            # 처리 결과가 없으면 원본 프레임 출력
            cv2.imshow('Frame', frame)

            # 'q' 키를 누르면 루프 종료
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
finally:
    # 자원 해제: 비디오 캡처와 모든 OpenCV 윈도우 종료
    cap.release()
    cv2.destroyAllWindows()

 

3. 손 랜드마크 그리기

  • MediaPipe가 추론 결과를 비동기적으로 전달하면 호출되는 콜백 함수입니다.
from mediapipe.tasks.python.vision.hand_landmarker import HandLandmarkerResult

def detect_callback(result: HandLandmarkerResult, output_image: mp.Image, timestamp_ms: int):
    """
    손 랜드마커의 결과를 받아 랜드마크와 연결선을 이미지에 그려주는 콜백 함수입니다.
    Args:
        result: HandLandmarkerResult 객체 (손 랜드마크 정보 포함)
        output_image: MediaPipe 이미지 객체 (RGB)
        timestamp_ms: 타임스탬프 (ms)
    """
    frame = output_image.numpy_view().copy()  # RGB 이미지 복사
    height, width, _ = frame.shape

    # 손이 감지되지 않은 경우, 원본 이미지를 큐에 추가하고 종료
    if not result.hand_landmarks:
        frame_queue.append(frame)
        return

    # 감지된 각 손에 대해 랜드마크와 연결선을 그림
    for i, hand_landmarks in enumerate(result.hand_landmarks):
        # 각 랜드마크(관절)에 원을 그림
        for j, landmark in enumerate(hand_landmarks):
            x = int(landmark.x * width)
            y = int(landmark.y * height)
            cv2.circle(frame, (x, y), 5, (0, 255, 0), -1)  # 초록색 원

        # 관절 연결선을 그림
        for start_idx, end_idx in HAND_CONNECTIONS:
            start = hand_landmarks[start_idx]
            end = hand_landmarks[end_idx]
            x0, y0 = int(start.x * width), int(start.y * height)
            x1, y1 = int(end.x * width), int(end.y * height)
            cv2.line(frame, (x0, y0), (x1, y1), (255, 255, 255), 2)  # 흰색 선

    # 처리된 이미지를 큐에 추가
    frame_queue.append(frame)

 

 

5. Hand Landmarker 테스트 결과

MediaPipe Hand Landmarker를 테스트한 결과, 손을 펼친 형태뿐만 아니라 주먹을 쥔 모양, 정면에서 보이는 주먹, 손등이 보이는 뒷면 주먹 등 다양한 손 모양에 대해서도 랜드마크를 안정적으로 감지하는 것을 확인할 수 있었습니다.

 

특히 손가락이 겹쳐지거나 일부가 가려지는 경우에도 21개의 주요 관절 포인트를 놓치지 않고 비교적 정확하게 예측하여, 제스처 인식이나 포즈 추정과 같은 응용에 매우 유용할 것으로 기대됩니다.

MediaPipe Hand Landmarker

 

 

t2_hand_land_marker.py
0.01MB


 

 

 

관련 글 링크

7.MediaPipe 손 동작 인식 (Gesture Recognition)

 

7.MediaPipe 손 동작 인식 (Gesture Recognition)

MediaPipe Gesture Recognizer는 손의 제스처를 실시간으로 인식하여 다양한 동작을 제어할 수 있는 강력한 비전 태스크입니다. MediaPipe 손 동작 인식 (Gesture Recognition) 목차 1. Gesture Recognizer란 ? 2. 작동 방

quadcube.tistory.com

 

728x90
반응형