openpose pytorch代碼分析
阿新 • • 發佈:2018-05-04
pla BE sea 位向量 heat gic lis wax kernel
github: https://github.com/tensorboy/pytorch_Realtime_Multi-Person_Pose_Estimation
1 # -*- coding: utf-8 -* 2 import os 3 import re 4 import sys 5 import cv2 6 import math 7 import time 8 import scipy 9 import argparse 10 import matplotlib 11 import numpy as np 12 importpylab as plt 13 from joblib import Parallel, delayed 14 import util 15 import torch 16 import torch as T 17 import torch.nn as nn 18 import torch.nn.functional as F 19 from torch.autograd import Variable 20 from collections import OrderedDict 21 from config_reader import config_reader22 from scipy.ndimage.filters import gaussian_filter 23 #parser = argparse.ArgumentParser() 24 #parser.add_argument(‘--t7_file‘, required=True) 25 #parser.add_argument(‘--pth_file‘, required=True) 26 #args = parser.parse_args() 27 28 torch.set_num_threads(torch.get_num_threads()) 29 weight_name = ‘./model/pose_model.pth‘ 30 31 blocks = {} 32 # 從1開始算的limb,圖對應:Pose Output Format 33 # find connection in the specified sequence, center 29 is in the position 15 34 limbSeq = [[2,3], [2,6], [3,4], [4,5], [6,7], [7,8], [2,9], [9,10], 35 [10,11], [2,12], [12,13], [13,14], [2,1], [1,15], [15,17], 36 [1,16], [16,18], [3,17], [6,18]] 37 38 # the middle joints heatmap correpondence 39 mapIdx = [[31,32], [39,40], [33,34], [35,36], [41,42], [43,44], [19,20], [21,22], 40 [23,24], [25,26], [27,28], [29,30], [47,48], [49,50], [53,54], [51,52], 41 [55,56], [37,38], [45,46]] 42 43 # visualize 44 colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], 45 [0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], 46 [170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]] 47 48 # heatmap channel為19 表示關節點的score 49 # PAF channel為38 表示limb的單位向量 50 block0 = [{‘conv1_1‘:[3,64,3,1,1]},{‘conv1_2‘:[64,64,3,1,1]},{‘pool1_stage1‘:[2,2,0]},{‘conv2_1‘:[64,128,3,1,1]},{‘conv2_2‘:[128,128,3,1,1]},{‘pool2_stage1‘:[2,2,0]},{‘conv3_1‘:[128,256,3,1,1]},{‘conv3_2‘:[256,256,3,1,1]},{‘conv3_3‘:[256,256,3,1,1]},{‘conv3_4‘:[256,256,3,1,1]},{‘pool3_stage1‘:[2,2,0]},{‘conv4_1‘:[256,512,3,1,1]},{‘conv4_2‘:[512,512,3,1,1]},{‘conv4_3_CPM‘:[512,256,3,1,1]},{‘conv4_4_CPM‘:[256,128,3,1,1]}] 51 52 blocks[‘block1_1‘] = [{‘conv5_1_CPM_L1‘:[128,128,3,1,1]},{‘conv5_2_CPM_L1‘:[128,128,3,1,1]},{‘conv5_3_CPM_L1‘:[128,128,3,1,1]},{‘conv5_4_CPM_L1‘:[128,512,1,1,0]},{‘conv5_5_CPM_L1‘:[512,38,1,1,0]}] 53 54 blocks[‘block1_2‘] = [{‘conv5_1_CPM_L2‘:[128,128,3,1,1]},{‘conv5_2_CPM_L2‘:[128,128,3,1,1]},{‘conv5_3_CPM_L2‘:[128,128,3,1,1]},{‘conv5_4_CPM_L2‘:[128,512,1,1,0]},{‘conv5_5_CPM_L2‘:[512,19,1,1,0]}] 55 56 for i in range(2,7): 57 blocks[‘block%d_1‘%i] = [{‘Mconv1_stage%d_L1‘%i:[185,128,7,1,3]},{‘Mconv2_stage%d_L1‘%i:[128,128,7,1,3]},{‘Mconv3_stage%d_L1‘%i:[128,128,7,1,3]},{‘Mconv4_stage%d_L1‘%i:[128,128,7,1,3]}, 58 {‘Mconv5_stage%d_L1‘%i:[128,128,7,1,3]},{‘Mconv6_stage%d_L1‘%i:[128,128,1,1,0]},{‘Mconv7_stage%d_L1‘%i:[128,38,1,1,0]}] 59 blocks[‘block%d_2‘%i] = [{‘Mconv1_stage%d_L2‘%i:[185,128,7,1,3]},{‘Mconv2_stage%d_L2‘%i:[128,128,7,1,3]},{‘Mconv3_stage%d_L2‘%i:[128,128,7,1,3]},{‘Mconv4_stage%d_L2‘%i:[128,128,7,1,3]}, 60 {‘Mconv5_stage%d_L2‘%i:[128,128,7,1,3]},{‘Mconv6_stage%d_L2‘%i:[128,128,1,1,0]},{‘Mconv7_stage%d_L2‘%i:[128,19,1,1,0]}] 61 62 def make_layers(cfg_dict): 63 layers = [] 64 for i in range(len(cfg_dict)-1): 65 one_ = cfg_dict[i] 66 for k,v in one_.iteritems(): 67 if ‘pool‘ in k: 68 layers += [nn.MaxPool2d(kernel_size=v[0], stride=v[1], padding=v[2] )] 69 else: 70 conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride = v[3], padding=v[4]) 71 layers += [conv2d, nn.ReLU(inplace=True)] 72 one_ = cfg_dict[-1].keys() 73 k = one_[0] 74 v = cfg_dict[-1][k] 75 conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride = v[3], padding=v[4]) 76 layers += [conv2d] 77 return nn.Sequential(*layers) 78 79 layers = [] 80 for i in range(len(block0)): 81 one_ = block0[i] 82 for k,v in one_.iteritems(): 83 if ‘pool‘ in k: 84 layers += [nn.MaxPool2d(kernel_size=v[0], stride=v[1], padding=v[2] )] 85 else: 86 conv2d = nn.Conv2d(in_channels=v[0], out_channels=v[1], kernel_size=v[2], stride = v[3], padding=v[4]) 87 layers += [conv2d, nn.ReLU(inplace=True)] 88 89 models = {} 90 models[‘block0‘]=nn.Sequential(*layers) 91 92 for k,v in blocks.iteritems(): 93 models[k] = make_layers(v) 94 95 class pose_model(nn.Module): 96 def __init__(self,model_dict,transform_input=False): 97 super(pose_model, self).__init__() 98 self.model0 = model_dict[‘block0‘] 99 self.model1_1 = model_dict[‘block1_1‘] 100 self.model2_1 = model_dict[‘block2_1‘] 101 self.model3_1 = model_dict[‘block3_1‘] 102 self.model4_1 = model_dict[‘block4_1‘] 103 self.model5_1 = model_dict[‘block5_1‘] 104 self.model6_1 = model_dict[‘block6_1‘] 105 106 self.model1_2 = model_dict[‘block1_2‘] 107 self.model2_2 = model_dict[‘block2_2‘] 108 self.model3_2 = model_dict[‘block3_2‘] 109 self.model4_2 = model_dict[‘block4_2‘] 110 self.model5_2 = model_dict[‘block5_2‘] 111 self.model6_2 = model_dict[‘block6_2‘] 112 113 def forward(self, x): 114 out1 = self.model0(x) 115 116 out1_1 = self.model1_1(out1) 117 out1_2 = self.model1_2(out1) 118 out2 = torch.cat([out1_1,out1_2,out1],1) 119 120 out2_1 = self.model2_1(out2) 121 out2_2 = self.model2_2(out2) 122 out3 = torch.cat([out2_1,out2_2,out1],1) 123 124 out3_1 = self.model3_1(out3) 125 out3_2 = self.model3_2(out3) 126 out4 = torch.cat([out3_1,out3_2,out1],1) 127 128 out4_1 = self.model4_1(out4) 129 out4_2 = self.model4_2(out4) 130 out5 = torch.cat([out4_1,out4_2,out1],1) 131 132 out5_1 = self.model5_1(out5) 133 out5_2 = self.model5_2(out5) 134 out6 = torch.cat([out5_1,out5_2,out1],1) 135 136 out6_1 = self.model6_1(out6) 137 out6_2 = self.model6_2(out6) 138 139 return out6_1,out6_2 140 141 142 model = pose_model(models) 143 model.load_state_dict(torch.load(weight_name)) 144 model.cuda() 145 model.float() 146 model.eval() 147 148 param_, model_ = config_reader() 149 150 #torch.nn.functional.pad(img pad, mode=‘constant‘, value=model_[‘padValue‘]) 151 tic = time.time() 152 test_image = ‘./sample_image/ski.jpg‘ 153 #test_image = ‘a.jpg‘ 154 oriImg = cv2.imread(test_image) # B,G,R order 155 imageToTest = Variable(T.transpose(T.transpose(T.unsqueeze(torch.from_numpy(oriImg).float(),0),2,3),1,2),volatile=True).cuda() 156 157 multiplier = [x * model_[‘boxsize‘] / oriImg.shape[0] for x in param_[‘scale_search‘]] # 不同scale輸入 158 159 heatmap_avg = torch.zeros((len(multiplier),19,oriImg.shape[0], oriImg.shape[1])).cuda() 160 paf_avg = torch.zeros((len(multiplier),38,oriImg.shape[0], oriImg.shape[1])).cuda() 161 #print heatmap_avg.size() 162 163 toc =time.time() 164 print ‘time is %.5f‘%(toc-tic) 165 tic = time.time() 166 for m in range(len(multiplier)): 167 scale = multiplier[m] 168 h = int(oriImg.shape[0]*scale) 169 w = int(oriImg.shape[1]*scale) 170 pad_h = 0 if (h%model_[‘stride‘]==0) else model_[‘stride‘] - (h % model_[‘stride‘]) 171 pad_w = 0 if (w%model_[‘stride‘]==0) else model_[‘stride‘] - (w % model_[‘stride‘]) 172 new_h = h+pad_h 173 new_w = w+pad_w 174 175 imageToTest = cv2.resize(oriImg, (0,0), fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC) 176 imageToTest_padded, pad = util.padRightDownCorner(imageToTest, model_[‘stride‘], model_[‘padValue‘]) 177 imageToTest_padded = np.transpose(np.float32(imageToTest_padded[:,:,:,np.newaxis]), (3,2,0,1))/256 - 0.5 178 # (-0.5~0.5) 179 feed = Variable(T.from_numpy(imageToTest_padded)).cuda() 180 output1,output2 = model(feed) 181 print output1.size() 182 print output2.size() 183 heatmap = nn.UpsamplingBilinear2d((oriImg.shape[0], oriImg.shape[1])).cuda()(output2) # 對output上采樣至原圖大小 184 185 paf = nn.UpsamplingBilinear2d((oriImg.shape[0], oriImg.shape[1])).cuda()(output1) # 同理 186 187 heatmap_avg[m] = heatmap[0].data 188 paf_avg[m] = paf[0].data 189 190 191 toc =time.time() 192 print ‘time is %.5f‘%(toc-tic) 193 tic = time.time() 194 # 不同scale的heatmap和PAF取均值 195 heatmap_avg = T.transpose(T.transpose(T.squeeze(T.mean(heatmap_avg, 0)),0,1),1,2).cuda() 196 paf_avg = T.transpose(T.transpose(T.squeeze(T.mean(paf_avg, 0)),0,1),1,2).cuda() 197 heatmap_avg=heatmap_avg.cpu().numpy() 198 paf_avg = paf_avg.cpu().numpy() 199 toc =time.time() 200 print ‘time is %.5f‘%(toc-tic) 201 tic = time.time() 202 203 all_peaks = [] 204 peak_counter = 0 205 206 #maps = 207 # picture array is reversed 208 for part in range(18): # 18個關節點的featuremap 209 map_ori = heatmap_avg[:,:,part] 210 map = gaussian_filter(map_ori, sigma=3) 211 212 map_left = np.zeros(map.shape) 213 map_left[1:,:] = map[:-1,:] 214 map_right = np.zeros(map.shape) 215 map_right[:-1,:] = map[1:,:] 216 map_up = np.zeros(map.shape) 217 map_up[:,1:] = map[:,:-1] 218 map_down = np.zeros(map.shape) 219 map_down[:,:-1] = map[:,1:] 220 221 # 計算是否為局部極值 222 peaks_binary = np.logical_and.reduce((map>=map_left, map>=map_right, map>=map_up, map>=map_down, map > param_[‘thre1‘])) 223 # peaks_binary = T.eq( 224 # peaks = zip(T.nonzero(peaks_binary)[0],T.nonzero(peaks_binary)[0]) 225 226 peaks = zip(np.nonzero(peaks_binary)[1], np.nonzero(peaks_binary)[0]) # note reverse 227 228 peaks_with_score = [x + (map_ori[x[1],x[0]],) for x in peaks] 229 id = range(peak_counter, peak_counter + len(peaks)) 230 peaks_with_score_and_id = [peaks_with_score[i] + (id[i],) for i in range(len(id))] 231 232 all_peaks.append(peaks_with_score_and_id) # 一個關節點featuremap上不同人的peak [[y, x, peak_score, id)],...] 233 peak_counter += len(peaks) 234 235 236 237 238 # 計算線性積分 采樣10個點計算 239 connection_all = [] 240 special_k = [] 241 mid_num = 10 242 243 for k in range(len(mapIdx)): 244 score_mid = paf_avg[:,:,[x-19 for x in mapIdx[k]]] # channel為2的paf_avg,表示PAF向量 245 candA = all_peaks[limbSeq[k][0]-1] #第k個limb中左關節點的候選集合A(不同人的關節點) 246 candB = all_peaks[limbSeq[k][1]-1] #第k個limb中右關節點的候選集合B(不同人的關節點) 247 nA = len(candA) 248 nB = len(candB) 249 # indexA, indexB = limbSeq[k] 250 if(nA != 0 and nB != 0): # 有候選時開始連接 251 connection_candidate = [] 252 for i in range(nA): 253 for j in range(nB): 254 vec = np.subtract(candB[j][:2], candA[i][:2]) 255 norm = math.sqrt(vec[0]*vec[0] + vec[1]*vec[1]) 256 vec = np.divide(vec, norm) # 計算單位向量 257 258 startend = zip(np.linspace(candA[i][0], candB[j][0], num=mid_num), 259 np.linspace(candA[i][1], candB[j][1], num=mid_num)) # 在A[i],B[j]連接線上采樣mid_num個點 260 261 # 計算采樣點的PAF向量 262 vec_x = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])), 0] 263 for I in range(len(startend))]) 264 vec_y = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])), 1] 265 for I in range(len(startend))]) 266 267 # 采樣點的PAF向量與limb的單位向量計算余弦相似度score,內積 268 score_midpts = np.multiply(vec_x, vec[0]) + np.multiply(vec_y, vec[1]) 269 score_with_dist_prior = sum(score_midpts)/len(score_midpts) + min(0.5*oriImg.shape[0]/norm-1, 0) 270 criterion1 = len(np.nonzero(score_midpts > param_[‘thre2‘])[0]) > 0.8 * len(score_midpts) 271 criterion2 = score_with_dist_prior > 0 272 if criterion1 and criterion2: 273 # (i,j,score,score_all) 274 connection_candidate.append([i, j, score_with_dist_prior, score_with_dist_prior+candA[i][2]+candB[j][2]]) 275 276 connection_candidate = sorted(connection_candidate, key=lambda x: x[2], reverse=True) # 按score排序 277 connection = np.zeros((0,5)) 278 for c in range(len(connection_candidate)): 279 i,j,s = connection_candidate[c][0:3] 280 if(i not in connection[:,3] and j not in connection[:,4]): 281 connection = np.vstack([connection, [candA[i][3], candB[j][3], s, i, j]]) # A_id, B_id, score, i, j 282 if(len(connection) >= min(nA, nB)): 283 break 284 285 connection_all.append(connection) # 多個符合當前limb的組合 [[A_id, B_id, score, i, j],...] 286 else: 287 special_k.append(k) 288 connection_all.append([]) 289 290 ‘‘‘ 291 function: 關節點連成每個人的limb 292 subset: last number in each row is the total parts number of that person 293 subset: the second last number in each row is the score of the overall configuration 294 candidate: 候選關節點 295 connection_all: 候選limb 296 297 ‘‘‘ 298 subset = -1 * np.ones((0, 20)) 299 candidate = np.array([item for sublist in all_peaks for item in sublist]) # 一個id的(y,x,score,id)(關節點) 300 301 for k in range(len(mapIdx)): 302 if k not in special_k: 303 partAs = connection_all[k][:,0] # 第k個limb,左端點的候選id集合 304 partBs = connection_all[k][:,1] # 第k個limb,右端點的候選id集合 305 indexA, indexB = np.array(limbSeq[k]) - 1 # 關節點index 306 307 for i in range(len(connection_all[k])): #= 1:size(temp,1) 308 found = 0 309 subset_idx = [-1, -1] 310 for j in range(len(subset)): #1:size(subset,1): 遍歷subset裏每個人,看當前兩個關節點出現過幾次 311 if subset[j][indexA] == partAs[i] or subset[j][indexB] == partBs[i]: 312 subset_idx[found] = j 313 found += 1 314 315 if found == 1: # 在這個人的subset裏連上這個limb 316 j = subset_idx[0] 317 if(subset[j][indexB] != partBs[i]): 318 subset[j][indexB] = partBs[i] 319 subset[j][-1] += 1 320 subset[j][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2] 321 elif(subset[j][indexA] != partAs[i]): 322 subset[j][indexA] = partAs[i] 323 subset[j][-1] += 1 324 subset[j][-2] += candidate[partAs[i].astype(int), 2] + connection_all[k][i][2] 325 326 elif found == 2: # if found 2 and disjoint, merge them 327 j1, j2 = subset_idx 328 print "found = 2" 329 membership = ((subset[j1]>=0).astype(int) + (subset[j2]>=0).astype(int))[:-2] 330 if len(np.nonzero(membership == 2)[0]) == 0: 331 # 如果兩個人的相同關節點沒有在各自的subset中都連成limb,那麽合並兩個subset構成一個人 332 subset[j1][:-2] += (subset[j2][:-2] + 1) 333 subset[j1][-2:] += subset[j2][-2:] 334 subset[j1][-2] += connection_all[k][i][2] 335 subset = np.delete(subset, j2, 0) 336 # To-Do 這裏有問題, 怎麽合並才對? 337 # else: # as like found == 1 338 # subset[j1][indexB] = partBs[i] 339 # subset[j1][-1] += 1 340 # subset[j1][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2] 341 342 # if find no partA in the subset, create a new subset 343 elif not found and k < 17: 344 row = -1 * np.ones(20) 345 row[indexA] = partAs[i] 346 row[indexB] = partBs[i] 347 row[-1] = 2 348 row[-2] = sum(candidate[connection_all[k][i,:2].astype(int), 2]) + connection_all[k][i][2] 349 subset = np.vstack([subset, row]) 350 351 # delete some rows of subset which has few parts occur 352 deleteIdx = []; 353 for i in range(len(subset)): 354 if subset[i][-1] < 4 or subset[i][-2]/subset[i][-1] < 0.4: 355 deleteIdx.append(i) 356 subset = np.delete(subset, deleteIdx, axis=0) 357 358 canvas = cv2.imread(test_image) # B,G,R order 359 for i in range(18): 360 for j in range(len(all_peaks[i])): 361 cv2.circle(canvas, all_peaks[i][j][0:2], 4, colors[i], thickness=-1) 362 363 stickwidth = 4 364 365 for i in range(17): 366 for n in range(len(subset)): 367 index = subset[n][np.array(limbSeq[i])-1] # limb的兩個關節點index 368 if -1 in index: 369 continue 370 cur_canvas = canvas.copy() 371 Y = candidate[index.astype(int), 0] # 兩個index點的縱坐標 372 X = candidate[index.astype(int), 1] # 兩個index點的橫坐標 373 mX = np.mean(X) 374 mY = np.mean(Y) 375 length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5 376 angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1])) 377 polygon = cv2.ellipse2Poly((int(mY),int(mX)), (int(length/2), stickwidth), int(angle), 0, 360, 1) 378 cv2.fillConvexPoly(cur_canvas, polygon, colors[i]) 379 canvas = cv2.addWeighted(canvas, 0.4, cur_canvas, 0.6, 0) 380 381 #Parallel(n_jobs=1)(delayed(handle_one)(i) for i in range(18)) 382 383 toc =time.time() 384 print ‘time is %.5f‘%(toc-tic) 385 cv2.imwrite(‘result.png‘,canvas)
openpose pytorch代碼分析