오늘은 openpose 라이브러리를 이용해서 관절 포인트들을 검출해보겠습니다. 한 장의 이미지를 이미 훈련된 pose estimation 모델에 넣어줘서 key point(관절 포인트)들이 검출된 결과를 얻어볼 것입니다.
우선 CMU-Perceptual-Computing-Lab의 깃헙에서 openpose를 다운로드 받습니다. github.com/CMU-Perceptual-Computing-Lab/openpose
별의 갯수와 fork의 숫자를 보니 정말 많은 사람들이 사용하는 라이브러리네요.
openpose 라이브러리 다운로드가 완료되었다면, 압축을 푼 후에 models 디렉토리로 이동한 후에 getModels.bat을 실행합니다. 더블클릭하면 됩니다. 그러면 다음과 같은 창이 뜨면서 이런 저런 모델들을 다운로드하기 시작합니다. 꽤 오래 걸리니 다른 급한 일 처리하고 계셔도 됩니다.
다운로드가 완료되면 저절로 창이 닫힙니다.
그러면 여러 훈련된 모델들 중에 저는 MPII Human Pose Dataset에서 훈련된 관절 검출 모델을 사용해보겠습니다. MPII 데이터셋은 관절 부위가 라벨링된 25000장의 이미지를 제공합니다.
models/pose/mpi/ 디렉토리 안에 있는 pose_deploy_linevecpose_deploy_linevec_faster_4_stages.prototxt와 pose_iter_160000.caffemodel를 복사해서 파이썬 스크립트 파일이 있는 위치에 붙여넣어줍니다. 만약 좀 더 무겁지만 정확한 모델을 사용하고 싶다면, pose_deloy_linevec_faster_4_stages.prototxt 대신에 pose_deploy_linevec.prototxt를 사용하시면 됩니다.
이전에 물론 파이참이나 아나콘다 등의 IDE를 이용해서 개발환경 세팅을 해줘야합니다. 그리고 opencv 라이브러리도 설치해줘야 합니다.
pip install opencv-python
그리고 테스트에 사용할 이미지를 하나 준비해줍니다. 저는 다음과 같은 이미지를 테스트 이미지로 준비했습니다.
test.JPG
이제 파이썬 스크립트 파일에 다음과 같은 코드를 복사해서 붙여넣어줍니다. 아래 코드는 m.blog.naver.com/rhrkdfus/221531159811 블로그(코칸리's 코오딩)에서 제공한 코드를 제 상황에 맞게 살짝 수정한 것입니다.
test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
import cv2
# 관절 번호: 머리는 0, 목은 1 등등
BODY_PARTS = {"Head": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,
"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,
"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "Chest": 14,
"Background": 15}
# 관절들을 선으로 이을 때 쌍이 되는 것들
POSE_PAIRS = [["Head", "Neck"], ["Neck", "RShoulder"], ["RShoulder", "RElbow"],
["RElbow", "RWrist"], ["Neck", "LShoulder"], ["LShoulder", "LElbow"],
["LElbow", "LWrist"], ["Neck", "Chest"], ["Chest", "RHip"], ["RHip", "RKnee"],
["RKnee", "RAnkle"], ["Chest", "LHip"], ["LHip", "LKnee"], ["LKnee", "LAnkle"]]
# 훈련된 network 세팅
protoFile = "pose_deploy_linevec_faster_4_stages.prototxt"
weightsFile = "pose_iter_160000.caffemodel"
net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile)
# 테스트 이미지 읽기
image = cv2.imread("test.JPG")
# 테스트 이미지에서 height, width, color 정보 파악
imageHeight, imageWidth, imageColor = image.shape
# 테스트 이미지를 network에 넣기 위해 전처리
inpBlob = cv2.dnn.blobFromImage(image, 1.0 / 255, (imageWidth, imageHeight), (0, 0, 0), swapRB=False, crop=False)
# 테스트 이미지를 network에 넣어줌
net.setInput(inpBlob)
# 결과 받아오기
output = net.forward()
H = output.shape[2]
W = output.shape[3]
# 검출된 관절 포인트를 테스트 이미지에 그려주기
points = []
for i in range(0, 15):
# 해당 관절 신뢰도 얻기
probMap = output[0, i, :, :]
# global 최대값 찾기
minVal, prob, minLoc, point = cv2.minMaxLoc(probMap)
# 원래 이미지에 맞게 점 위치 변경
x = (imageWidth * point[0]) / W
y = (imageHeight * point[1]) / H
# 키포인트 검출한 결과가 0.1보다 크면(검출한곳이 위 BODY_PARTS랑 맞는 부위면) points에 추가, 검출했는데 부위가 없으면 None으로
if prob > 0.1:
cv2.circle(image, (int(x), int(y)), 3, (0, 255, 255), thickness=-1,
lineType=cv2.FILLED) # circle(그릴곳, 원의 중심, 반지름, 색)
cv2.putText(image, "{}".format(i), (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1,
lineType=cv2.LINE_AA)
points.append((int(x), int(y)))
else:
points.append(None)
cv2.imshow("Output-Keypoints", image)
cv2.waitKey(0)
# 관절들을 선으로 연결해주기
for pair in POSE_PAIRS:
partA = pair[0] # Head
partA = BODY_PARTS[partA] # 0
partB = pair[1] # Neck
partB = BODY_PARTS[partB] # 1
# print(partA," 와 ", partB, " 연결\n")
if points[partA] and points[partB]:
cv2.line(image, points[partA], points[partB], (255, 0, 0), 2)
cv2.imshow("Output-Keypoints-with-Lines", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
|
cs |
이제 위 파일을 실행시켜보겠습니다.
python test.py
실행 결과 두 개의 이미지가 순차적으로 화면에 뜹니다.
보시다시피 상반신의 관절들은 가슴 포인트(14번)를 제외하고 잘 검출되었지만, 하반신 쪽 포인트들(8-13번)은 약간 엉망으로 검출되었습니다. 관절 포인트들을 연결해놓은 이미지를 보니 목이 긴 괴물처럼 되었죠. ㅋㅋ
오늘은 일단 관절 포인트 검출을 해봤다는 것에 의의를 두고 글을 마치도록 하겠습니다.
참고자료
[1] m.blog.naver.com/rhrkdfus/221531159811, 코칸리's 코오딩, "[OpenPose] Python OpenPose 시작하기"
'Dev > python' 카테고리의 다른 글
[python] pytesseract 사용중 print 안 되는 애러 해결법 (2) | 2021.04.14 |
---|---|
[python] cv2 이미지를 pillow 이미지로 변경하기, Image.fromarray() (2) | 2021.04.13 |
[python] 리스트 안의 숫자 요소들을 문자로 변경하려면, map() 함수 사용 (4) | 2021.04.12 |
[anaconda+python] spyder 5.0.0 버전 열자마자 나오는 에러 메시지(You have missing dependencies! # Mandatory: spyder_kernels)에 관하여 (0) | 2021.04.10 |
[python] 파일의 확장자를 알려주는 함수, os.path.splitext() (0) | 2021.04.02 |
[flask] 구름 IDE의 항상 켜두기 기능을 이용해서 flask 웹 서버 구동하기 (0) | 2021.03.29 |
[flask] 색칠 공부 도안 만들어주는 사이트 제작 (18) | 2021.03.24 |
[python] 넘파이 배열에서 어떤 값의 위치를 알고 싶다면, np.where 함수 (0) | 2021.03.15 |