귀퉁이 서재

컴퓨터 비전 - 12. 자세 추정(Pose Estimation)이란? 본문

컴퓨터 비전

컴퓨터 비전 - 12. 자세 추정(Pose Estimation)이란?

Baek Kyun Shin 2023. 6. 2. 22:46

자세 추정이란?

자세 추정(Pose Estimation)이란 이미지나 영상에 포함된 여러 인물을 탐지(detection)해서 인체 각 부위의 위치를 식별하고 부위를 연결하는 선을 구하는 기술입니다. 게임, 헬스케어, AR, 스포츠 등에 쓰이죠. 특히나 스포츠 분야에서 다양하게 활용될 수 있을 것 같습니다.

출처-https://samim.io/p/2020-11-24-how-artificial-intelligence-is-changing-the-fitness-in/
출처-https://www.youtube.com/watch?v=zACPSbUennk&t=9s
출처-https://viso.ai/deep-learning/pose-estimation-ultimate-overview/
출처-https://viso.ai/deep-learning/pose-estimation-ultimate-overview/

인체에서 중요한 지점(key point)의 위치를 찾는 방식으로 동작합니다. 머리, 어깨, 팔꿈치, 무릎, 발목 등을 말이죠. 이러한 주요 지점을 선으로 연결하면 전체 자세를 추정할 수 있습니다. 

자세 추정의 형태는 크게 세 가지입니다. Skeleton-based 모델, Contour-based 모델, Volume-based 모델이죠. Skeleton-based 모델은 주요 부위를 중심으로 찾기 때문에 신체 위치를 찾기에 유용합니다. 다만 전체적인 모양, 부피는 알기 어렵습니다. Contour-based 모델은 신체의 전체 모양을 알 수 있다는 장점이 있습니다. Volume-based 모델은 3D 자세 추정에 사용하는 모델입니다. 이 글에서는 가장 일반적으로 쓰는 Skeleton-based 모델을 바탕으로 설명합니다.

전통적인 자세 추정 알고리즘이 있습니다. 그런데 전통 자세 추정 알고리즘보다는 신경망을 적용한 알고리즘이 훨씬 성능이 좋습니다. 그러니 여기에서도 딥러닝 자세 추정 모델에 관해 설명하겠습니다. 

딥러닝은 한 사람의 자세를 추정하는 작업은 꽤 잘합니다. 그런데 한 이미지에 여러 사람이 있으면 얘기가 달라집니다. 여러 사람이 다양한 자세를 하고 있거나 사람이 서로 겹쳐 있으면 작업이 제법 복잡해지죠. 계산 시간도 오래 걸리고요. 이 문제를 해결하려고 연구자들은 두 가지 방식을 제시했습니다. Top-down 방식과 Bottom-up 방식이죠.

  • Top-down 방식 : 먼저 사람을 탐지(detect)한 뒤, 탐지 결과를 바탕으로 인체 주요 부위를 찾아 자세 추정을 하는 방식
  • Bottom-up 방식 : 전체 이미지에서 인체 주요 부위들을 먼저 찾은 뒤, 각 부위를 연결해 자세를 추정하는 방식

대표적인 딥러닝 기반 자세 추정 모델로는 OpenPose, High-Resolution Net(HRNet), AlphaPose(RMPE), DeepCut, Deep Pose 등이 있습니다.

아울러, 자세 추정은 사람뿐만 아니라 동물에도 적용할 수 있습니다.

출처-https://viso.ai/deep-learning/pose-estimation-ultimate-overview/

자세 추정 실습

코드 링크 : https://github.com/BaekKyunShin/Computer-Vision-Basic/blob/main/Project6-Pose_Estimation/Pose_Estimation.ipynb

사람 이미지를 바탕으로 자세 추정을 하는 아주 간단한 실습을 해보겠습니다.

아래 코드는 구글 Colab을 바탕으로 설명합니다.

1. 이미지 불러온 뒤, 전처리

자세 추정할 사람 이미지를 불러온 뒤 전처리를 해보겠습니다. 먼저 OpenCV 라이브러리를 활용해 이미지를 불러오죠.

from google.colab import drive
drive.mount('/content/drive')
import cv2
import matplotlib.pyplot as plt
from google.colab.patches import cv2_imshow
import numpy as np
image = cv2.imread('/content/drive/MyDrive/colab/Computer-Vision-Course/Data/Images/standing_person.png')

cv2_imshow(image)

image.shape

(602, 360, 3)

height = image.shape[0]
width = image.shape[1]

# https://docs.opencv.org/3.4/d6/d0f/group__dnn.html#ga29f34df9376379a603acd8df581ac8d7
image_blob = cv2.dnn.blobFromImage(image=image, 
                                   scalefactor=1/255, 
                                   size=(width, height))

OpenCV의 blobFromImage() 메서드는 딥러닝 작업을 위해 이미지를 전처리하는 데 사용됩니다. 이 메서드는 이미지를 blob으로 변환합니다. blob은 딥러닝 모델에서 사용할 수 있는 형식의 이미지 데이터 다차원 배열을 말합니다. blobFromImage는 입력 이미지에 다음 작업을 수행합니다.

  1. 이미지 크기를 조정합니다. (size 파라미터)
  2. 이미지의 각 채널에서 평균 값을 뺍니다. 이 작업은 데이터의 중심을 맞추고 모델 정확도를 높입니다. (mean 파라미터)
  3. 이미지 배율을 조정합니다. (scalefactor 파라미터)
  4. 채널 순서를 바꿉니다. OpenCV의 기본 BGR 형식에서 딥러닝에서 일반적으로 사용하는 RGB 형식으로 바꾸기 위함입니다. (swapRB 파라미터)

결과로 만든 blob을 모델 훈련에 사용할 수 있습니다.

type(image_blob), image_blob.shape

(numpy.ndarray, (1, 3, 602, 360))

형상 맨 앞에 1이 추가됐네요. 1은 배치 크기를 일컫습니다. 딥러닝 모델로 이미지를 다루려면 배치 크기가 포함되어 있어야 합니다.

2. 사전 훈련된 신경망 모델 불러오기

다음으로 사전 훈련된 신경망을 불러오겠습니다.

readNetFromCaffe()로 신경망을 불러올 수 있습니다. readNetFromCaffe()는 두 가지 인수를 받습니다. 첫 번째는 신경망 아키텍처에 관한 설명 파일인 prototxt이고, 두 번째는 사전 훈련 모델인 caffemodel 파일입니다.

prototxt_path = '/content/drive/MyDrive/colab/Computer-Vision-Course/Data/Weights/pose_deploy_linevec_faster_4_stages.prototxt'
caffemodel_path = '/content/drive/MyDrive/colab/Computer-Vision-Course/Data/Weights/pose_iter_160000.caffemodel'

network = cv2.dnn.readNetFromCaffe(prototxt_path, caffemodel_path)

network.getLayerNames()를 실행하면 신경망 계층의 이름을 쭉 출력합니다. len(network.getLayerNames())를 실행하면 계층이 총 127개 있다는 점을 알 수 있죠.

3. 신체의 특징이 되는 지점(body points) 예측

이제 신경망 안에 이미지를 전달한 뒤, body points를 예측해보겠습니다. 먼저 setInput() 메서드의 인수로 '앞서 만든 전처리된 이미지인 image_blob'을 전달합니다. 이어서 forward() 메서드를 실행하면 예측을 수행합니다.

network.setInput(image_blob)

output = network.forward()

output의 형상을 출력해볼까요?

output.shape

 (1, 44, 76, 45)

출력값은 네 가지 원소가 담긴 배열이네요. 각 원소의 의미는 아래와 같습니다.

  • 1 : 배치 크기
  • 44 : 클래스 개수 (여기서는 각 body points의 범주 개수)
  • 76 : 피처맵의 높이
  • 45 : 피처맵의 너비

피처맵의 높이와 너비를 각각 변수로 저장해두겠습니다. body points를 시각화할 때 사용하기 위해서입니다.

feature_map_height = output.shape[2]
feature_map_width = output.shape[3]

이제 각 body points를 이미지 위에 표시해보겠습니다. 여기서 사용할 body points는 총 15개입니다. 원래는 아래 그림처럼 총 16개인데, 배경은 제외할 거라서 총 15개입니다.

num_body_points = 15  # number of body points except for background in MPII model 
body_points = []
confidence_threshold = 0.1

for body_point_idx in range(num_body_points):
    confidence_map = output[0, body_point_idx, :, :]
    _, max_confidence, _, max_position = cv2.minMaxLoc(confidence_map)

    # scaling x, y position
    x = int((image.shape[1] * max_position[0]) / feature_map_width)
    y = int((image.shape[0] * max_position[1]) / feature_map_height)

    if max_confidence > confidence_threshold:
        cv2.circle(image, (x, y), 5, (0, 0, 255), thickness=-1)
        cv2.putText(image, f'{body_point_idx}', (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0))
        body_points.append((x, y))
    else:
        body_points.append(None)

중간에 cv2.minMaxLoc() 메서드가 있습니다. 세 가지 값을 반환하는데, min_confidence, max_confidence, min_position, max_postiion입니다. 우리는 신뢰도가 가장 높은 body points를 가져올 것이므로 max_confidence와 max_position만 변수로 받았습니다. 

이미지에 body points를 표시했으니, 이미지를 출력해보죠.

plt.figure(figsize=(14,10))
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB));

각 body points를 연결하면 보기에 더 좋겠습니다. 예를 들어, 머리와 목을 연결하고, 무릎과 발목을 연결하는 등으로 말이죠. 다음 코드로 간단하게 구현할 수 있습니다.

point_connections = [[0,1], [1,2], [2,3], [3,4], [1,5], [5,6], [6,7],[1,14],
                     [14,8], [8,9], [9,10], [14,11], [11,12], [12,13]]
                     
for connection in point_connections:
    connection_start = connection[0]
    connection_end = connection[1]
    if body_points[connection_start] and body_points[connection_end]:
        cv2.line(image, body_points[connection_start], body_points[connection_end], (255, 0, 0))

plt.figure(figsize=(14,10))
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB));

지금까지 자세추정의 간단한 이론과 코드를 알아봤습니다.

참고 자료

Elisha Odemakinde - "Human Pose Estimation with Deep Learining - Ultimate Overview in 2023"

오가와 유타로 - "만들면서 배우는 파이토치 딥러닝"

SuperMemi - "Human Pose Estimation이란? (2022)"

Nilesh Baria - "A Comprehensive Guide to Human Pose Estimation"

Comments