Deep Learning Based Multiple Human Pose Estimation Using OpenCV

Lời mở đầu

Lưu ý: Để sử dụng được các mô hình trong bài viết này, bạn phải sử dụng phiên bản opencv > 3.4.1.

Ở bài viết trước, chúng ta đã tìm hiểu cách thức rút trích khung xương sử dụng DNN và đã áp dụng thành công trên ảnh có chứa 1 đối tượng người. Trong bài viết này, chúng ta sẽ thực hiện áp dụng mô hình cho bài toán có nhiều người trong cùng 1 bức ảnh.

Sử dụng pretrain model trong bài toán multiple Pose Estimation

Trong bài viết này, chúng ta tiếp tục sử dụng mô hình MPI để dò tìm các điểm đặc trưng của con người và rút ra mô hình khung xương. Kết quả trả về của thuật toán gồm 15 đặc trưng như bên dưới.

1Head  0, Neck  1, Right Shoulder  2, Right Elbow  3, Right Wrist  4,
2Left Shoulder  5, Left Elbow  6, Left Wrist  7, Right Hip  8,
3Right Knee  9, Right Ankle  10, Left Hip  11, Left Knee  12,
4Left Ankle  13, Chest  14, Background  15

Áp dụng mô hình với ảnh của nhóm T-ARA.

 1import cv2
 2
 3nPoints = 15
 4POSE_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] ]
 5
 6protoFile = "pose/mpi/pose_deploy_linevec.prototxt"
 7weightsFile = "pose/mpi/pose_iter_160000.caffemodel"
 8
 9net = cv2.dnn.readNetFromCaffe(protoFile, weightsFile)
10
11frame = cv2.imread("tara1.jpg")
12
13inWidth = 368
14inHeight = 368
15
16# Prepare the frame to be fed to the network
17inpBlob = cv2.dnn.blobFromImage(frame, 1.0 / 255, (inWidth, inHeight), (0, 0, 0), swapRB=False, crop=False)
18
19# Set the prepared object as the input blob of the network
20net.setInput(inpBlob)
21
22output = net.forward()

Thử show lên vị trí vùng cổ trong hình.

 1
 2i = 0
 3probMap = output[0, i, :, :]
 4probMap = cv2.resize(probMap, (frameWidth, frameHeight))
 5
 6import matplotlib.pyplot as plt
 7
 8plt.imshow(cv2.cvtColor(frameCopy, cv2.COLOR_BGR2RGB))
 9plt.imshow(probMap, alpha=0.5)
10plt.show()

Hình với điểm đặc trưng vùng đầu

Thử show lên hình điểm đặc trưng vùng cổ

1i = 1
2probMap = output[0, i, :, :]
3probMap = cv2.resize(probMap, (frameWidth, frameHeight))
4
5import matplotlib.pyplot as plt
6
7plt.imshow(cv2.cvtColor(frameCopy, cv2.COLOR_BGR2RGB))
8plt.imshow(probMap, alpha=0.5)
9plt.show()

Hình với điểm đặc trưng vùng cổ

Bằng một số phép biến đổi quen thuộc có sẵn trong opencv, chúng ta hoàn toàn có thể lấy được toạ độ của các điểm keypoint một cách dễ dàng.

 1
 2# Find the Keypoints using Non Maximum Suppression on the Confidence Map
 3def getKeypoints(probMap, threshold=0.1):
 4
 5    mapSmooth = cv2.GaussianBlur(probMap,(3,3),0,0)
 6
 7    mapMask = np.uint8(mapSmooth>threshold)
 8    keypoints = []
 9
10    #find the blobs
11    _, contours, _ = cv2.findContours(mapMask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
12
13    #for each blob find the maxima
14    for cnt in contours:
15        blobMask = np.zeros(mapMask.shape)
16        blobMask = cv2.fillConvexPoly(blobMask, cnt, 1)
17        maskedProbMap = mapSmooth * blobMask
18        _, maxVal, _, maxLoc = cv2.minMaxLoc(maskedProbMap)
19        keypoints.append(maxLoc + (probMap[maxLoc[1], maxLoc[0]],))
20
21    return keypoints
22
23
24detected_keypoints = []
25keypoints_list = np.zeros((0,3))
26keypoint_id = 0
27threshold = 0.1
28for i in range(nPoints):
29    probMap = output[0, i, :, :]
30    probMap = cv2.resize(probMap, (frameWidth, frameHeight))
31
32    keypoints = getKeypoints(probMap, threshold)
33    keypoints_with_id = []
34    for j in range(len(keypoints)):
35        keypoints_with_id.append(keypoints[j] + (keypoint_id,))
36        keypoints_list = np.vstack([keypoints_list, keypoints[j]])
37        keypoint_id += 1
38
39    detected_keypoints.append(keypoints_with_id)
40
41
42
43frameClone = cv2.cvtColor(frameCopy,cv2.COLOR_BGR2RGB)
44for i in range(nPoints):
45    for j in range(len(detected_keypoints[i])):
46        cv2.circle(frameClone, detected_keypoints[i][j][0:2], 3, [0,0,255], -1, cv2.LINE_AA)
47
48plt.imshow(frameClone)
49plt.show()

Hình toàn bộ điểm đặc trưng

Cuối cùng, chúng ta sẽ nối các điểm đặc trưng của các nhân vật thông qua thuật toán Part Affinity Heatmaps. Thuật toán này được đề xuất bởi nhóm tác giả Zhe Cao, Tomas Simon,Shih-En Wei, Yaser Sheikh thuộc phòng thí nghiệm The Robotics Institute trường đại học Carnegie Mellon. Các bạn có nhu cầu có thể tìm hiểu ở https://arxiv.org/pdf/1611.08050.pdf.

  1
  2mapIdx = [[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]]
  3
  4
  5
  6colors = [ [0,100,255], [0,100,255], [0,255,255], [0,100,255], [0,255,255], [0,100,255],
  7         [0,255,0], [255,200,100], [255,0,255], [0,255,0], [255,200,100], [255,0,255],
  8         [0,0,255], [255,0,0], [200,200,0], [255,0,0], [200,200,0], [0,0,0]]
  9# Find valid connections between the different joints of a all persons present
 10def getValidPairs(output):
 11    valid_pairs = []
 12    invalid_pairs = []
 13    n_interp_samples = 10
 14    paf_score_th = 0.1
 15    conf_th = 0.5
 16    # loop for every POSE_PAIR
 17    for k in range(len(mapIdx)):
 18        # A->B constitute a limb
 19        pafA = output[0, mapIdx[k][0], :, :]
 20        pafB = output[0, mapIdx[k][1], :, :]
 21        pafA = cv2.resize(pafA, (frameWidth, frameHeight))
 22        pafB = cv2.resize(pafB, (frameWidth, frameHeight))
 23
 24
 25        # Find the keypoints for the first and second limb
 26        candA = detected_keypoints[POSE_PAIRS[k][0]]
 27        candB = detected_keypoints[POSE_PAIRS[k][1]]
 28        nA = len(candA)
 29        nB = len(candB)
 30
 31        # fig=plt.figure(figsize=(8, 8))
 32
 33        # interp_coord = list(zip(np.linspace(candA[0][0], candB[0][0], num=n_interp_samples),
 34        #                                     np.linspace(candA[0][1], candB[0][1], num=n_interp_samples)))
 35
 36        # frameClone1 = frameClone.copy()
 37        # fig.add_subplot(1, 2, 1)
 38
 39        # for xx in interp_coord:
 40        #     cv2.circle(frameClone1,(int(xx[0]),int(xx[1])), 3, [0,0,255], -1, cv2.LINE_AA)
 41
 42
 43        # plt.imshow(cv2.cvtColor(frameClone1, cv2.COLOR_BGR2RGB))
 44        # plt.imshow(pafA, alpha=0.5)
 45
 46        # frameClone1 = frameClone.copy()
 47        # fig.add_subplot(1, 2, 2)
 48
 49
 50
 51
 52        # for xx in interp_coord:
 53        #     cv2.circle(frameClone1,(int(xx[0]),int(xx[1])), 3, [0,0,255], -1, cv2.LINE_AA)
 54
 55        # plt.imshow(cv2.cvtColor(frameClone1, cv2.COLOR_BGR2RGB))
 56        # plt.imshow(pafB, alpha=0.5)
 57        # plt.show()
 58
 59
 60
 61
 62
 63        # If keypoints for the joint-pair is detected
 64        # check every joint in candA with every joint in candB
 65        # Calculate the distance vector between the two joints
 66        # Find the PAF values at a set of interpolated points between the joints
 67        # Use the above formula to compute a score to mark the connection valid
 68
 69        if( nA != 0 and nB != 0):
 70            valid_pair = np.zeros((0,3))
 71            for i in range(nA):
 72                max_j=-1
 73                maxScore = -1
 74                found = 0
 75                for j in range(nB):
 76                    # Find d_ij
 77                    d_ij = np.subtract(candB[j][:2], candA[i][:2])
 78                    norm = np.linalg.norm(d_ij)
 79                    if norm:
 80                        d_ij = d_ij / norm
 81                    else:
 82                        continue
 83                    # Find p(u)
 84                    interp_coord = list(zip(np.linspace(candA[i][0], candB[j][0], num=n_interp_samples),
 85                                            np.linspace(candA[i][1], candB[j][1], num=n_interp_samples)))
 86                    # Find L(p(u))
 87                    paf_interp = []
 88                    for k in range(len(interp_coord)):
 89                        paf_interp.append([pafA[int(round(interp_coord[k][1])), int(round(interp_coord[k][0]))],
 90                                           pafB[int(round(interp_coord[k][1])), int(round(interp_coord[k][0]))] ])
 91                    # Find E
 92                    paf_scores = np.dot(paf_interp, d_ij)
 93                    avg_paf_score = sum(paf_scores)/len(paf_scores)
 94
 95                    # Check if the connection is valid
 96                    # If the fraction of interpolated vectors aligned with PAF is higher then threshold -> Valid Pair
 97                    if ( len(np.where(paf_scores > paf_score_th)[0]) / n_interp_samples ) > conf_th :
 98                        if avg_paf_score > maxScore:
 99                            max_j = j
100                            maxScore = avg_paf_score
101                            found = 1
102                # Append the connection to the list
103                if found:
104                    valid_pair = np.append(valid_pair, [[candA[i][3], candB[max_j][3], maxScore]], axis=0)
105
106            # Append the detected connections to the global list
107            valid_pairs.append(valid_pair)
108
109            pprint(valid_pair)
110        else: # If no keypoints are detected
111            print("No Connection : k = {}".format(k))
112            invalid_pairs.append(k)
113            valid_pairs.append([])
114    pprint(valid_pairs)
115    return valid_pairs, invalid_pairs
116
117# This function creates a list of keypoints belonging to each person
118# For each detected valid pair, it assigns the joint(s) to a person
119# It finds the person and index at which the joint should be added. This can be done since we have an id for each joint
120def getPersonwiseKeypoints(valid_pairs, invalid_pairs):
121    # the last number in each row is the overall score
122    personwiseKeypoints = -1 * np.ones((0, 19))
123
124    for k in range(len(mapIdx)):
125        if k not in invalid_pairs:
126            partAs = valid_pairs[k][:,0]
127            partBs = valid_pairs[k][:,1]
128            indexA, indexB = np.array(POSE_PAIRS[k])
129
130            for i in range(len(valid_pairs[k])):
131                found = 0
132                person_idx = -1
133                for j in range(len(personwiseKeypoints)):
134                    if personwiseKeypoints[j][indexA] == partAs[i]:
135                        person_idx = j
136                        found = 1
137                        break
138
139                if found:
140                    personwiseKeypoints[person_idx][indexB] = partBs[i]
141                    personwiseKeypoints[person_idx][-1] += keypoints_list[partBs[i].astype(int), 2] + valid_pairs[k][i][2]
142
143                # if find no partA in the subset, create a new subset
144                elif not found and k < 17:
145                    row = -1 * np.ones(19)
146                    row[indexA] = partAs[i]
147                    row[indexB] = partBs[i]
148                    # add the keypoint_scores for the two keypoints and the paf_score
149                    row[-1] = sum(keypoints_list[valid_pairs[k][i,:2].astype(int), 2]) + valid_pairs[k][i][2]
150                    personwiseKeypoints = np.vstack([personwiseKeypoints, row])
151    return personwiseKeypoints
152
153valid_pairs, invalid_pairs = getValidPairs(output)
154
155personwiseKeypoints = getPersonwiseKeypoints(valid_pairs, invalid_pairs)
156
157
158for i in range(nPoints-1):
159    for n in range(len(personwiseKeypoints)):
160
161        index = personwiseKeypoints[n][np.array(POSE_PAIRS[i])]
162        if -1 in index:
163            continue
164        B = np.int32(keypoints_list[index.astype(int), 0])
165        A = np.int32(keypoints_list[index.astype(int), 1])
166        cv2.line(frameClone, (B[0], A[0]), (B[1], A[1]), colors[i], 3, cv2.LINE_AA)
167
168
169
170plt.imshow(frameClone)
171    # plt.imshow(mapMask, alpha=0.5)
172plt.show()

Hình kết quả cuối cùng

Hẹn gặp lại các bạn ở những bài viết tiếp theo.

Bài viết này được viết dựa vào nguồn https://www.learnopencv.com/multi-person-pose-estimation-in-opencv-using-openpose/ của tác giả VIKAS GUPTA. Tôi sử dụng tập model và hình ảnh khác với bài viết nguyên gốc của tác giả.

Comments