Hyebin‘s blog
Published 2021. 12. 17. 02:57
[CV] OpenPose Other/Computer vision

영상으로부터 사람의 포즈, 관절정보를 추정하는 알고리즘

 

# MPI
protoFile = "pose_deploy_linevec_faster_4_stages.prototxt" #신경망의 구조
weightsFile = "pose_iter_160000.caffemodel" #실제 학습 weight
net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile)

Caffe라는 신경망 프레임워크를 사용!

readNetFromCaffe : 카페로부터 학습되어 있는 weight를 읽어서 출력해주는 함수

 

frame = cv2.imread("single.jpeg") #이미지를 읽는다.
frameCopy = np.copy(frame) #복사본을 만든다.
frameWidth = frame.shape[1] 
frameHeight = frame.shape[0]
threshold = 0.1
inpBlob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (368, 368),
                          (0, 0, 0), swapRB=False, crop=False)

net.setInput(inpBlob)

blobFromImage를 통해서 4차원 형태로 만든다.

blobFromImage(영상, 0~1사이로 정규화, 영상 크기, 평균 픽셀,내부적으로 bgr 으로 swap은 안함, crop안함

여기서 고민 해볼 것은 원본 이미지는 직사각형 영상인데 직사각형 영상을 정사각형 영상으로 줄인다면 찌그러질 수 있지만 다시 되돌릴 것이어서 문제 없음

Net에 blob 형태의 데이터 넣어주는 함수

 

%%time
output = net.forward()

yolo와 다르게 출력층이 하나밖에 없다.

Net을 실행 시켜줌(순방향)

 

print(output.shape) #(1, 44, 46, 46)
H = output.shape[2]
W = output.shape[3]

신경 망의 출력층은 (1, 44, 46, 46)으로

첫번째 44개는 네트워크를 통과시키개 되면 44개의 확률 맵이 나오는데 이 확률 맵의 크기는 46*46이다.

이미지의 확률값은 전체 영상에서 어떤 오브젝트가 있는 확률인데, 입력한 영상의 크기와 상관없이 확률맵은 46*46로 줄여서 계산한다.

 

print(output[0, 0, :, :]) 
plt.imshow(output[0, 0, :, :])
plt.imshow(output[0, 15, :, :]) 
 #15 개의 점을 찾고자한다.

0번째 확률값을 출력해보면 정수리 부분의 확률값이 나온다. (머리일 확률이 높음)

1번째의 확률값은 목부분의 확률값이 나온다.

15번째의 맵은 0~14번까지 누적시켜놓은 맵이다.

확률값을 해석해서 각각의 위치값을 추정해야한다.

우리는 각 점에서의 최대값을 갖는 한 점을 찾아야한다.

 

# Empty list to store the detected keypoints

nPoints = 15 #검출할 포인트 15개

points = []
for i in range(nPoints): #0~14까지
    # confidence map of corresponding body's part.
    probMap = output[0, i, :, :]

    # Find global maxima of the probMap.
    minVal, prob, minLoc, point = cv2.minMaxLoc(probMap) 
#최소 혹은 최댓값을 갖는곳의 위치를 리턴
#이때 point는 0~45로 정규화 되어있기 때문에 실제로 원래 이미지에서 위치를 확인하려면frameWidth 와  frameHeight를 곱함
    
    # Scale the point to fit on the original image     
    x = frameWidth * ((point[0]) / W)
    y = frameHeight * ((point[1]) / H)

    if prob > threshold :
        cv2.circle(frameCopy, (int(x), int(y)), 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED)
        cv2.putText(frameCopy, "{}".format(i), (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, lineType=cv2.LINE_AA)
        cv2.circle(frame, (int(x), int(y)), 8, (0, 0, 255), thickness=-1, lineType=cv2.FILLED)

        # Add the point to the list if the probability is greater than the threshold
        points.append((int(x), int(y))) #검출된 값들을 누적
    else :
        points.append(None)

plt.figure(figsize=[10,10])
plt.imshow(cv2.cvtColor(frameCopy, cv2.COLOR_BGR2RGB))

원래 이미지의 좌표값을 구하기 위하기

point는 0~45로 정규화 되어 있기 때문에 원래 이미지에서 위치를 확인하려면

(point[0]) / W)로 해주면 0~1사이가 된다. 그리고 폭과 높이를 곱해주면 원래 영상의 scale이 나오게 된다.

점으로 부터 스켈레톤을 그리는 방법

 

POSE_PAIRS = [[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 pair in POSE_PAIRS:
    partA = pair[0]
    partB = pair[1]

    if points[partA] and points[partB]:
        cv2.line(frame, points[partA], points[partB], (0, 255, 255), 3)

plt.figure(figsize=[10,10])
plt.imshow(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

관절의 연결을 맵핑 시켜주고

line함수로 직선을 그림

'Other > Computer vision' 카테고리의 다른 글

[CV] 객체 검출 Darknet  (0) 2021.12.17
[CV] ImageNet을 이용한 인식  (0) 2021.12.17
[CV] CNN 활용한 영상 인식  (0) 2021.12.17
[CV] 사람의 시각을 닮은 신경망 CNN  (0) 2021.12.17
[CV] 전이 학습  (0) 2021.12.17
profile

Hyebin‘s blog

@hyebin Lee

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그