MediaPipe Hand Landmarker를 사용하면 이미지나 동영상에서 손의 21개 주요 지점을 정밀하게 감지할 수 있습니다
MediaPipe Hand Landmarker로 손 랜드마크 감지 : Python
목차1. MediaPipe 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) |
다음은 위의 사전 학습된 모델을 기반으로 한 전체 파이프라인의 태스크 벤치마크입니다. 지연 시간 결과는 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개의 주요 관절 포인트를 놓치지 않고 비교적 정확하게 예측하여, 제스처 인식이나 포즈 추정과 같은 응용에 매우 유용할 것으로 기대됩니다.
관련 글 링크
7.MediaPipe 손 동작 인식 (Gesture Recognition)
7.MediaPipe 손 동작 인식 (Gesture Recognition)
MediaPipe Gesture Recognizer는 손의 제스처를 실시간으로 인식하여 다양한 동작을 제어할 수 있는 강력한 비전 태스크입니다. MediaPipe 손 동작 인식 (Gesture Recognition) 목차 1. Gesture Recognizer란 ? 2. 작동 방
quadcube.tistory.com
'2.인공지능 > MediaPipe' 카테고리의 다른 글
9. MediaPipe 얼굴 감지(Face Detection)기능과 Python 예제 (1) | 2025.07.08 |
---|---|
7.MediaPipe 손 동작 인식 (Gesture Recognition) (0) | 2025.05.16 |
6.MediaPipe Interactive Image Segmenter 사용 : 코드 예시 (0) | 2025.05.13 |
5.MediaPipe Hair Segmenter로 머리카락 염색 효과 테스트 (0) | 2025.05.08 |
4.MediaPipe Image Segmentation 사용하기 : 이미지 분할 (0) | 2025.04.28 |