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()
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()
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()
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ẹ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