🤖 AI/ML

포즈 추정

Pose Estimation

이미지에서 인체 관절 위치 추정. AR, 피트니스 앱.

📖 상세 설명

포즈 추정(Pose Estimation)은 이미지나 영상에서 인체의 관절(keypoint) 위치를 감지하고 연결하여 자세를 파악하는 컴퓨터 비전 기술입니다. 일반적으로 17-25개의 관절점(코, 눈, 어깨, 팔꿈치, 손목, 엉덩이, 무릎, 발목 등)을 추정하며, 이를 연결하여 스켈레톤(skeleton)을 구성합니다. 2D 포즈 추정은 이미지 평면에서의 좌표를, 3D 포즈 추정은 깊이 정보를 포함한 공간 좌표를 예측합니다.

포즈 추정 연구는 2014년 DeepPose를 시작으로 딥러닝 기반 접근이 주류가 되었습니다. 2017년 OpenPose의 등장으로 실시간 다중 인물 포즈 추정이 가능해졌고, 이후 HRNet, AlphaPose 등이 정확도를 높였습니다. 2024-2025년에는 RTMPose, YOLOv8-Pose 같은 경량 모델이 모바일과 Edge 디바이스에서 실시간 처리를 가능하게 했으며, ViTPose 같은 Transformer 기반 모델이 최고 정확도를 달성하고 있습니다.

포즈 추정 방식은 크게 두 가지로 나뉩니다. Top-down 방식은 먼저 사람을 감지(Detection)한 후 각 사람의 바운딩 박스 내에서 포즈를 추정합니다. 정확도가 높지만 인물 수에 비례하여 연산량이 증가합니다. Bottom-up 방식은 먼저 모든 관절점을 감지한 후 같은 사람의 관절끼리 연결합니다. 인물 수와 무관하게 연산량이 일정하여 실시간 처리에 유리합니다. OpenPose는 Bottom-up, HRNet은 Top-down 방식의 대표적 모델입니다.

실무에서 포즈 추정은 스포츠 분석(자세 교정, 동작 분석), 피트니스 앱(운동 카운트, 폼 체크), AR/VR(아바타 동작 매핑), 의료(재활 모니터링, 보행 분석), 보안(이상 행동 감지), 게임(동작 인식 컨트롤러) 등 다양하게 활용됩니다. 2025년 현재 스마트폰에서도 MediaPipe, TensorFlow Lite 기반으로 30FPS 이상의 실시간 포즈 추정이 가능하며, Apple Vision Pro 같은 XR 기기에서 핵심 입력 방식으로 사용됩니다.

💻 코드 예제

MediaPipe를 사용한 실시간 포즈 추정 예제입니다.

# MediaPipe 포즈 추정
# pip install mediapipe opencv-python

import cv2
import mediapipe as mp

# MediaPipe 포즈 솔루션 초기화
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles

# 포즈 모델 설정
pose = mp_pose.Pose(
    static_image_mode=False,       # 비디오 모드
    model_complexity=1,             # 0: Lite, 1: Full, 2: Heavy
    enable_segmentation=False,      # 배경 분할
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
)

# 웹캠 캡처
cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # BGR to RGB 변환
    image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # 포즈 추정 수행
    results = pose.process(image_rgb)

    # 결과 시각화
    if results.pose_landmarks:
        # 스켈레톤 그리기
        mp_drawing.draw_landmarks(
            frame,
            results.pose_landmarks,
            mp_pose.POSE_CONNECTIONS,
            landmark_drawing_spec=mp_drawing_styles.get_default_pose_landmarks_style()
        )

        # 특정 관절점 좌표 추출 (예: 오른쪽 어깨)
        right_shoulder = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER]
        h, w, _ = frame.shape
        x, y = int(right_shoulder.x * w), int(right_shoulder.y * h)
        cv2.putText(frame, f"Shoulder: ({x}, {y})", (10, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)

    cv2.imshow('MediaPipe Pose', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
pose.close()

YOLOv8-Pose를 사용한 다중 인물 포즈 추정 예제입니다.

# YOLOv8 포즈 추정
# pip install ultralytics opencv-python

from ultralytics import YOLO
import cv2

# YOLOv8 포즈 모델 로드
model = YOLO('yolov8n-pose.pt')  # nano 버전 (빠름)
# model = YOLO('yolov8m-pose.pt')  # medium 버전 (균형)
# model = YOLO('yolov8x-pose.pt')  # xlarge 버전 (정확)

# 이미지에서 포즈 추정
image_path = 'people.jpg'
results = model(image_path)

for result in results:
    # 키포인트 정보
    keypoints = result.keypoints

    if keypoints is not None:
        # 각 감지된 사람의 포즈
        for person_idx, person_kpts in enumerate(keypoints.data):
            print(f"\n=== 사람 {person_idx + 1} ===")

            # COCO 17 keypoints
            keypoint_names = [
                'nose', 'left_eye', 'right_eye', 'left_ear', 'right_ear',
                'left_shoulder', 'right_shoulder', 'left_elbow', 'right_elbow',
                'left_wrist', 'right_wrist', 'left_hip', 'right_hip',
                'left_knee', 'right_knee', 'left_ankle', 'right_ankle'
            ]

            for kpt_idx, kpt in enumerate(person_kpts):
                x, y, conf = kpt[0].item(), kpt[1].item(), kpt[2].item()
                if conf > 0.5:  # 신뢰도 임계값
                    print(f"  {keypoint_names[kpt_idx]}: ({x:.1f}, {y:.1f}) conf: {conf:.2f}")

    # 결과 이미지 저장
    result.save(filename='pose_result.jpg')

# 비디오에서 실시간 포즈 추정
cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # 추론
    results = model(frame, verbose=False)

    # 결과 시각화
    annotated_frame = results[0].plot()

    cv2.imshow('YOLOv8 Pose', annotated_frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

📊 성능 & 비용

2025년 1월 기준 주요 포즈 추정 모델 비교입니다.

모델 COCO AP 속도 (FPS) 파라미터 용도
MediaPipe Pose ~68% ~50 (CPU) 3M 모바일/웹
YOLOv8n-Pose 50.4% ~150 3.3M Edge/실시간
YOLOv8x-Pose 69.2% ~30 69M 서버/정밀
RTMPose-L 72.3% ~45 27M 균형
ViTPose-H 79.1% ~10 632M 최고 정확도

* FPS는 NVIDIA RTX 3090 기준 참고치

플랫폼별 추천 모델:

플랫폼 추천 모델 예상 FPS 비고
스마트폰 (iOS/Android) MediaPipe Pose 30-50 ML Kit 통합
웹 브라우저 TensorFlow.js PoseNet 15-30 WebGL 가속
Edge (Jetson) YOLOv8n-Pose + TensorRT 50-80 INT8 양자화
서버 (GPU) RTMPose / ViTPose 30-100 배치 처리

🗣️ 실무에서 이렇게 말하세요

💬 회의에서
"피트니스 앱에서 스쿼트 자세 분석을 위해 MediaPipe를 적용했습니다. 무릎, 엉덩이, 어깨의 각도를 실시간 계산하여 올바른 폼인지 피드백을 주고, 100회 이상 스쿼트를 추적해 운동 카운트 기능도 구현했습니다."
💬 면접에서
"Top-down과 Bottom-up 방식의 차이를 설명하면, Top-down은 먼저 사람을 찾고 각각에서 포즈를 추정하여 정확하지만 인원수에 비례해 느려집니다. Bottom-up은 모든 관절을 먼저 찾고 연결해서 인원수와 무관하게 빠르지만, 밀집된 상황에서 연결 오류가 생길 수 있습니다."
💬 기술 토론에서
"3D 포즈 추정은 2D 포즈에서 lifting하는 방식과 직접 3D 예측하는 방식이 있어요. 단안 카메라에서는 깊이 모호성 때문에 lifting 방식이 일반적이고, Apple Vision Pro 같은 깊이 센서가 있으면 직접 3D 추정이 더 정확합니다."

⚠️ 흔한 실수 & 주의사항

가려짐(Occlusion) 처리 미흡

물체나 다른 사람에 의해 관절이 가려지면 예측이 불안정해집니다. 단순히 낮은 신뢰도 관절을 무시하지 말고, 시간적 연속성이나 신체 구조 제약을 활용하여 추정하세요.

실시간 처리 요구사항 과소평가

높은 정확도 모델을 모바일에 배포하면 프레임이 떨어집니다. 타겟 디바이스의 연산 능력을 먼저 확인하고, 정확도와 속도의 트레이드오프를 고려하여 모델을 선택하세요.

올바른 접근법

용도에 맞는 모델을 선택하세요. 모바일은 MediaPipe, Edge는 YOLOv8-nano + TensorRT, 서버는 RTMPose를 권장합니다. 신뢰도 임계값을 조정하고, 시간적 스무딩으로 떨림을 줄이세요.

🔗 관련 용어

📚 더 배우기