MediaPipe Gesture Recognizer는 손의 제스처를 실시간으로 인식하여 다양한 동작을 제어할 수 있는 강력한 비전 태스크입니다.
MediaPipe 손 동작 인식 (Gesture Recognition)
목차
1. Gesture Recognizer란?
MediaPipe Gesture Recognizer는 손 제스처를 인식하는 머신러닝 태스크입니다.
손의 위치와 형태를 실시간으로 추적하여 사전 정의된 제스처 또는 사용자 정의 제스처를 분류하고,
이를 기반으로 UI 동작, 명령 실행 등 다양한 기능과 연동할 수 있습니다.
예시: 사용자가 "엄지척" 제스처를 하면, 시스템은 이를 Thumb_Up 제스처로 인식하고 관련 기능을 실행합니다.
2. 작동 방식 - 입력과 출력
- 입력 이미지 처리 - 처리에는 이미지 회전, 크기 조절, 정규화, 색상 공간 변환이 포함됩니다.
- 점수 기준점: 예측 점수를 기준으로 결과를 필터링합니다.
- 라벨 허용 목록 및 차단 목록: 모델에서 인식하는 동작 카테고리를 지정합니다.
Task 입력 | Task 출력 |
동작 감지기는 다음 데이터 유형 중 하나의 입력을 허용합니다. - 정지 이미지 - 디코딩된 동영상 프레임 - 라이브 동영상 피드 |
동작 감지기는 다음과 같은 결과를 출력합니다. - 손 동작 카테고리 - 감지된 손의 손잡이 - 이미지 좌표에서 감지된 손의 랜드마크 - 감지된 손의 랜드마크(세계 좌표) |
◆ GestureRecognizerResult 포맷 예시
🖐 Hand #1
──────────────────────────────────────────────
🔹 Gesture
- category_name: "None"
- score : 0.8128
- index : -1
🔹 Handedness
- category_name: "Left"
- score : 0.9974
- index : 1
🔹 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
🔹 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
항목 | 설명 |
gestures | 인식된 제스처 목록 (일반적으로 score가 가장 높은 top-1 사용) |
handedness | 해당 손이 Left/Right 중 어떤 것으로 분류되었는지, 추 |
hand_landmarks | 0~1 사이 정규화된 좌표 (이미지 기준) |
hand_world_landmarks | 실제 세계 단위의 3D 좌표 (카메라 캘리브레이션 기준), m 단위로 추정됨 |
◆ Normalized vs. World Landmarks 상세 비교
구분 | Normalized Landmarks | World Landmarks |
좌표계 | 이미지 비율 기반 2D 좌표계(x, y ∈ [0, 1]) | 3차원 실세계 공간 좌표계(단위: meter) |
x/y/z 의미 | - x, y: 이미지 너비/높이 기준 정규화 위치- z: 손 깊이 (카메라 기준 상대값, 보통 -이면 카메라 가까움) | - x, y, z: 카메라 기준 실세계 위치 (왼쪽/위쪽/앞뒤 방향 포함) |
z축 기준 | 이미지 기반 상대 거리 추정 (픽셀 기준 추정) | 실제 거리로 추정된 깊이값(3D 공간 상 절대 거리 기반) |
정밀도 | 이미지 왜곡, 포즈에 따라 왜곡 가능 (시각화 중심) | 학습된 모델에 따라 깊이까지 추정하므로 상대적으로 더 정밀함 |
시점 의존성 | 이미지 크기와 위치에 의존 | 시점과 해상도에 독립적 (실제 위치 추정이 목적이므로) |
좌표 활용성 | - 이미지에 시각화 (draw_landmarks)- 바운딩박스, 손 위치 분류 등 | - 손 간 거리 계산- 손의 3D 위치 추정- 물체 잡기, AR 제스처 인식 |
대표 사용처 | - 제스처 분류- 이미지상 손 위치 판단- 웹 기반 인터페이스 | - 3D 공간 추적- 거리 기반 판단 (예: 두 손 사이 거리)- AR/VR 제어 |
제공 구조체 | NormalizedLandmark 객체 리스트 (landmarks) | Landmark 객체 리스트 (world_landmarks) |
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 객체 | 없음 |
MediaPipe Gesture Recognizer는 canned_gestures_classifier와 custom_gestures_classifier를 동시에 함께 사용할 수 있도록 설계되어 있습니다.
MediaPipe Gesture Recognizer는 손의 움직임을 분석하여 특정 제스처를 분류하는 기능을 제공합니다.
이때 사용하는 분류 모델은 기본적으로 8가지 제스처를 인식할 수 있도록 사전 학습되어 있습니다.
MediaPipe는 이 기본 제스처 분류기의 동작을 좀 더 유연하게 제어할 수 있도록 여러 가지 설정 옵션을 제공합니다.
옵션 | 설명 | 값 범위 | 기본 값 |
display_names_locale | 라벨 표시 언어 코드 ("en", "ko" 등) | 문자열 | "en" |
max_results | 반환할 최대 분류 결과 수. 음수일 경우 모든 결과 반환 | 정수 | -1 |
score_threshold | 이 값보다 낮은 신뢰도 결과는 제외됨 | 0.0 ~ 1.0 | 0.0 |
category_allowlist | 허용할 제스처 라벨 목록. 이 리스트에 없는 결과는 제외됨 (denylist와 함께 사용 불가) |
문자열 리스트 | 빈 리스트 |
category_denylist | 제외할 제스처 라벨 목록. 이 리스트에 있는 결과는 제외됨 (allowlist와 함께 사용 불가) |
문자열 리스트 | 빈 리스트 |
◆ custom_gestures_classifier_options
MediaPipe Gesture Recognizer는 기본적으로 8가지 손 제스처를 인식할 수 있도록 설정되어 있지만, 직접 정의한 새로운 제스처를 인식하고 싶을 때는 사용자 정의 제스처 분류기(Custom Gesture Classifier)를 사용할 수 있습니다.
옵션 | 설명 | 값 범위 | 기본 값 |
display_names_locale | 표시할 라벨의 언어 코드 | 문자열 | "en" |
max_results | 최대 결과 수 | 정수 | -1 |
score_threshold | 신뢰도 기준 | 0.0 ~ 1.0 | 0.0 |
category_allowlist | 허용할 사용자 제스처 라벨 리스트 | 문자열 리스트 | 빈 리스트 |
category_denylist | 제외할 사용자 제스처 라벨 리스트 | 문자열 리스트 | 빈 리스트 |
4. HandGestureClassifier 구조 분석
모델 이름 입력 셰이프 양자화 유형 모델 카드 링크 버전
모델 | 입력 세이프 | 양자화 |
HandGestureClassifier | 192×192, 224×224 (지원) | float16 (부동 소수점 16비트) |
MediaPipe Gesture Recognizer의 핵심은 손의 구조를 정확히 감지하고, 그 구조를 기반으로 손 제스처를 인식하는 두 개의 주요 컴포넌트로 이루어져 있습니다:
- 손 관절을 감지하는 손 랜드마크 모델
- 손의 모양을 분류하는 제스처 분류기 모델
이 두 모델은 연속적으로 작동하며, 사용자가 손을 움직이기만 해도 실시간으로 동작을 감지하고 해석할 수 있게 합니다.
◆ 손 랜드마크 모델 (Hand Landmark Model)
손 랜드마크 모델 번들은 감지된 손 영역 내에서 21개의 손가락 관절 좌표의 주요 지점 위치를 감지합니다. 이 모델은 약 30, 000개의 실제 이미지와 여러 배경에 적용된 여러 개의 렌더링된 합성 손 모델을 사용하여 학습되었습니다.
손 랜드마커 모델 번들에는 손바닥 감지 모델과 손 랜드마커 감지 모델이 포함되어 있습니다.
손바닥 감지 모델은 전체 입력 이미지에서 손의 영역을 찾고, 손 랜드마크 감지 모델은 손바닥 감지 모델에 의해 정의된 잘린 손 이미지에서 랜드마크를 찾습니다.
단계 | 설명 |
Palm Detection | 전체 이미지에서 손바닥의 위치(ROI)를 찾아냅니다. 정확도는 높지만 계산량이 많기 때문에 반복 실행은 비효율적입니다. |
Hand Landmark Detection | palm detection으로 잘라낸 손 영역에서 21개 손 관절 위치를 정밀하게 추출합니다. |
◆ 제스처 분류 모델 (Gesture Classifier)
손 랜드마크(관절 좌표)를 기반으로 손의 전체 모양을 분석하고, 이를 특정 제스처로 분류합니다.
단계 | 설명 |
Gesture Embedding Model | 관절 위치를 압축된 벡터 형태의 특징(feature vector)로 변환 |
Gesture Classification Model | 이 벡터를 입력으로 받아, 가장 가능성 높은 제스처를 예측 |
ID | 라벨 이름 | 설명 |
0 | None | 인식된 제스처 없음 또는 불확실함 |
1 | Closed_Fist | 주먹 쥔 상태 |
2 | Open_Palm | 손바닥 펼친 상태 |
3 | Pointing_Up | 손가락 하나를 위로 가리킴 |
4 | Thumb_Down | 엄지를 아래로 |
5 | Thumb_Up | 엄지를 위로 (👍) |
6 | Victory | 브이(V) 제스처 (✌️) |
7 | ILoveYou | 수어(SL)의 사랑해요 제스처 |
◆ 성능
Pixel 6 기준으로 측정된 평균 처리 시간은 다음과 같습니다:
실행 환경 | 평균 지연 시작 |
CPU | 16.76ms |
GPU | 20.87ms |
5. 사용자 정의 제스쳐
MediaPipe Gesture Recognizer는 기본적으로 8가지 손동작(Thumb_Up, Victory 등)을 인식할 수 있도록 사전 학습된 제스처 분류기를 제공합니다.
하지만 실제 응용에서는 다음과 같은 추가적인 제스처가 필요할 수 있습니다:
- “OK 사인”
- “Call Me” 손동작
- 특정 수어(수화) 제스처
- 산업별 특화 제스처 (예: 수신호, 원격 제어 동작 등)
1. 학습 데이터 준비
- 여러 각도와 손 모양으로 촬영한 이미지 또는 랜드마크 데이터를 수집
- 클래스(예: OK, Stop, Call_Me) 별로 구분된 라벨 구성
- 가능하면 직접 손 랜드마크 좌표를 추출하여 CSV로 저장하는 방식이 효과적임 (이미지보다는 랜드마크 기반 분류가 일반적)
2. Model Maker로 학습
- TensorFlow Lite의 Model Maker 라이브러리를 사용
- 손 랜드마크 → 특징 벡터 → 분류기를 학습하는 간단한 구조
# 예시 코드 (pseudo)
model = model_maker.train(
data=gesture_landmark_data,
labels=["OK", "Call_Me", "Stop"]
)
3. .task 파일로 내보내기
- MediaPipe Task API가 인식 가능한 .task 형식으로 모델 번들 생성
# 내보낸 모델을 task 파일로 변환
model_maker.export_to_task("custom_gesture.task")
4. GestureRecognizer에 적용 방법
from mediapipe.tasks.python.vision import GestureRecognizer
from mediapipe.tasks import base
options = GestureRecognizer.GestureRecognizerOptions(
base_options=base.BaseOptions(model_asset_path='custom_gesture.task'),
running_mode=vision.RunningMode.LIVE_STREAM,
result_callback=your_result_callback_fn
)
recognizer = GestureRecognizer.create_from_options(options)
5. 주의 사항
항목 | 설명 |
기본 제스처와 동시 사용 가능? | 가능 (단, .task 파일에 두 분류기 모두 포함되어야 함) |
학습 시 기존 제스처가 사라지나? | 사용자 정의 모델만 포함된 .task를 쓸 경우, 기본 제스처는 인식되지 않음 |
권장 방식 | 기존 모델을 기반으로 사용자 정의 분류기를 추가 구성하여 병합하는 방식 |
최소 데이터셋 | 제스처당 수백 개 이상의 랜드마크 샘플이 있을수록 일반화 성능 향상 |
입력 형식 | 손 이미지가 아닌 21개 손 랜드마크 좌표(x, y, z) 기반 학습이 일반적 |
6. 제스쳐 인식 테스트 코드
이 예제는 MediaPipe Tasks API를 이용해 실시간 손 제스처를 인식하고, OpenCV를 통해 손 랜드마크 및 인식된 제스처 결과를 시각화하는 구현한 코드예시 입니다.
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/gesture_recognizer.task')
# 모델 파일이 존재하지 않으면 예외를 발생시킵니다.
if not os.path.exists(model_path):
raise FileNotFoundError(f"Model file not found at path: {model_path}")
# MediaPipe BaseOptions 객체를 생성합니다. (CPU 사용)
# NotImplementedError: GPU Delegate is not yet supported for Windows
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 = []
# GestureRecognizer 옵션을 설정합니다.
options = vision.GestureRecognizerOptions(
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=gesture_callback, # 결과 콜백 함수
)
2. Cam 연결 및 실시간 제스쳐 인식
- OpenCV를 통해 기본 웹캠을 활성화하고 프레임을 읽습니다.
- MediaPipe의 GestureRecognizer 객체로부터 recognize_async() 메서드를 호출하여 비동기 인식 수행.
- 프레임마다 처리된 결과가 있을 경우 시각화된 프레임을 frame_queue에서 꺼내 cv2.imshow()로 화면에 출력합니다.
# 웹캠에서 비디오 캡처를 시작합니다.
cap = cv2.VideoCapture(0)
if not cap.isOpened():
raise RuntimeError("Could not open video device")
last_processed_time = 0 # 마지막 처리 시점 (ms 단위)
# MediaPipe Gesture Recognizer 객체를 생성하고, 실시간으로 프레임을 처리합니다.
try:
with vision.GestureRecognizer.create_from_options(options) as recognizer:
while cap.isOpened():
ret, frame = cap.read() # 프레임 캡처
if not ret:
print("Failed to capture image")
break
# 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))
# 실시간 제스처 인식 (비동기)
recognizer.recognize_async(image, timestamp_ms=elapsed_time_ms)
last_processed_time = 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('Gesture', frame)
# 'q' 키를 누르면 루프 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
# 자원 해제 및 모든 OpenCV 창 닫기
cap.release()
cv2.destroyAllWindows()
3. 랜트마크 및 제스쳐 인식 결과 그리기
- MediaPipe가 추론 결과를 비동기적으로 전달하면 호출되는 콜백 함수입니다.
# GestureRecognizerResult 타입을 직접 import하여 타입 힌트에 사용합니다.
from mediapipe.tasks.python.vision.gesture_recognizer import GestureRecognizerResult
def gesture_callback(result: GestureRecognizerResult, output_image: mp.Image, timestamp_ms: int):
# MediaPipe 이미지 객체를 numpy 배열로 변환 (RGB)
frame = output_image.numpy_view().copy()
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)
# 인식된 제스처 라벨 및 신뢰도, 손 좌우 정보 표시
if result.gestures and result.gestures[i]:
gesture_label = result.gestures[i][0].category_name
confidence = result.gestures[i][0].score
hand_label = result.handedness[i][0].category_name
text = f"{hand_label}: {gesture_label} ({confidence:.2f})"
x0, y0 = int(hand_landmarks[0].x * width), int(hand_landmarks[0].y * height)
cv2.putText(frame, text, (x0, y0 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
# 시각화된 프레임을 큐에 추가
frame_queue.append(frame)
7. 제스쳐 테스트 결과
MediaPipe Gesture Recognizer는 손바닥이 카메라를 정면으로 향하고 손가락이 위쪽을 향할 때 가장 안정적으로 제스처를 인식했습니다.
반면, 손가락이 측면 또는 아래쪽을 향하거나 손등이 카메라를 향할 경우에는 손 감지가 불안정해지고 제스처 분류가 실패하는 경우가 자주 발생했습니다
관련 글 링크
https://ai.google.dev/edge/mediapipe/solutions/vision/gesture_recognizer?hl=ko
동작 인식 작업 가이드 | Google AI Edge | Google AI for Developers
LiteRT 소개: 온디바이스 AI를 위한 Google의 고성능 런타임(이전 명칭: TensorFlow Lite)입니다. 이 페이지는 Cloud Translation API를 통해 번역되었습니다. 의견 보내기 동작 인식 작업 가이드 MediaPipe 동작 인
ai.google.dev
'2.인공지능 > MediaPipe' 카테고리의 다른 글
9. MediaPipe 얼굴 감지(Face Detection)기능과 Python 예제 (1) | 2025.07.08 |
---|---|
8.MediaPipe Hand Landmarker로 손 랜드마크 감지 : Python (0) | 2025.06.13 |
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 |