귀퉁이 서재
컴퓨터 비전 - 12. 자세 추정(Pose Estimation)이란? 본문
자세 추정이란?
자세 추정(Pose Estimation)이란 이미지나 영상에 포함된 여러 인물을 탐지(detection)해서 인체 각 부위의 위치를 식별하고 부위를 연결하는 선을 구하는 기술입니다. 게임, 헬스케어, AR, 스포츠 등에 쓰이죠. 특히나 스포츠 분야에서 다양하게 활용될 수 있을 것 같습니다.
인체에서 중요한 지점(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 등이 있습니다.
아울러, 자세 추정은 사람뿐만 아니라 동물에도 적용할 수 있습니다.
자세 추정 실습
사람 이미지를 바탕으로 자세 추정을 하는 아주 간단한 실습을 해보겠습니다.
아래 코드는 구글 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는 입력 이미지에 다음 작업을 수행합니다.
- 이미지 크기를 조정합니다. (size 파라미터)
- 이미지의 각 채널에서 평균 값을 뺍니다. 이 작업은 데이터의 중심을 맞추고 모델 정확도를 높입니다. (mean 파라미터)
- 이미지 배율을 조정합니다. (scalefactor 파라미터)
- 채널 순서를 바꿉니다. 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"
'컴퓨터 비전' 카테고리의 다른 글
컴퓨터 비전 - 14. 스타일 전이(Style Transfer) (4) | 2023.06.06 |
---|---|
컴퓨터 비전 - 13. 딥드림(Deep Dream)이란? (0) | 2023.06.05 |
컴퓨터 비전 - 11. YOLO v1, v2, v3 개요와 실습 (0) | 2023.05.06 |
컴퓨터 비전 - 10. R-CNN vs. SPP-net vs. Fast R-CNN vs. Faster R-CNN 개요 (4) | 2023.03.15 |
컴퓨터 비전 - 9. 객체 탐지(Object Detection) 개요 (4) | 2023.03.14 |