工程实现 || 基于opencv使用openpose完成人体姿态估计

2021.2.23更新: 没想到自己闲下来完成的测试代码,看的人挺多的。平时工作也忙,看到评论和私信有时没及时法就忘了。我已经把工程上传到我的资源,需要的自行获取,不再邮箱发送工程。

openpose的简单介绍

该篇内容是在对openpose的内容已经了解清楚的情况下,记录如何使用opencv中的dnn进行openpose的人体姿势的估计。 openpose的详细内容,已经在之前的openpose论文翻译中介绍。

在下载模型权重文件后,预测过程主要分一下几步:

  • (1) 读取模型、预测的图片,进行神经网络的预测,获取预测结果
  • (2) 关键点的检测
  • (3) 利用PAFs,找到有效的关键点对
  • (4) 将点对组合成正确的人体骨骼图

在这里插入图片描述 // {0, "Nose"}, // {1, "Neck"}, // {2, "RShoulder"}, // {3, "RElbow"}, // {4, "RWrist"}, // {5, "LShoulder"}, // {6, "LElbow"}, // {7, "LWrist"}, // {8, "MidHip"}, // {9, "RHip"}, // {10, "RKnee"}, // {11, "RAnkle"}, // {12, "LHip"}, // {13, "LKnee"}, // {14, "LAnkle"}, // {15, "REye"}, // {16, "LEye"}, // {17, "REar"}, // {18, "LEar"}, // {19, "LBigToe"}, // {20, "LSmallToe"}, // {21, "LHeel"}, // {22, "RBigToe"}, // {23, "RSmallToe"}, // {24, "RHeel"}, // {25, "Background"}

在这里插入图片描述 // {0, "Nose"},// {1, "Neck"},// {2, "RShoulder"},// {3, "RElbow"},// {4, "RWrist"},// {5, "LShoulder"},// {6, “LElbow”},// {7, "LWrist"},// {8, "RHip"},// {9, "RKnee"},// {10, "RAnkle"},// {11, "LHip"},// {12, "LKnee"},// {13, "LAnkle"},// {14, "REye"},// {15, "LEye"},// {16, "REar"},// {17, "LEar"}

2 config.py

config.py 中保存着18个关节点和25个关节点的相关信息


prototxt_25 = "./models/body_25/pose_deploy.prototxt"
caffemodel_25 = "./models/body_25/pose_iter_584000.caffemodel"

point_names_25 = ['Nose', 'Neck', 'RShoulder',
                  'RElbow', 'RWrist', 'LShoulder',
                  'LElbow', 'LWrist', 'MidHip',
                  'RHip', 'RKnee', 'RAnkle',
                  'LHip', 'LKnee', 'LAnkle',
                  'REye', 'LEye', 'REar',
                  'LEar', 'LBigToe', 'LSmallToe',
                  'LHeel', 'RBigToe', 'RSmallToe' ,
                  'RHeel']

point_pairs_25 = [[1, 8], [1, 2], [1, 5], [2, 3], [3, 4], [5, 6],
                     [6, 7], [8, 9], [9, 10], [10, 11], [8, 12], [12, 13],
                     [13, 14], [1, 0], [0, 15], [15, 17], [0, 16], [16, 18],
                     [2, 17], [5, 18], [14, 19], [19, 20], [14, 21], [11, 22],
                     [22, 23], [11, 24]]


map_idx_25  = [[26, 27], [40, 41], [48, 49], [42, 43], [44, 45], [50, 51],
                 [52, 53], [32, 33], [28, 29], [30, 31], [34, 35], [36, 37],
                 [38, 39], [56, 57], [58, 59], [62, 63], [60, 61], [64, 65],
                 [46, 47], [54, 55], [66, 67], [68, 69], [70, 71], [72, 73],
                 [74, 75], [76, 77]]

colors_25  = [[255, 0, 0], [255, 85, 0], [255, 170, 0],
                 [255, 255, 0], [170, 255, 0], [85, 255, 0],
                 [0, 255, 0], [0, 255, 85], [0, 255, 170],
                 [0, 255, 255], [0, 170, 255], [0, 85, 255],
                 [0, 0, 255], [85, 0, 255], [170, 0, 255],
                 [255, 0, 255], [255, 0, 170], [255, 0, 85],
                 [255, 170, 85], [255, 170, 170], [255, 170, 255],
                 [255, 85, 85], [255, 85, 170], [255, 85, 255],
                 [170, 170, 170]]

prototxt_18 = "./models/coco/pose_deploy_linevec.prototxt"
caffemodel_18 = "./models/coco/pose_iter_440000.caffemodel"

point_names_18 = ['Nose', 'Neck',
                           'R-Sho', 'R-Elb', 'R-Wr',
                           'L-Sho', 'L-Elb', 'L-Wr',
                           'R-Hip', 'R-Knee', 'R-Ank',
                           'L-Hip', 'L-Knee', 'L-Ank',
                           'R-Eye', 'L-Eye', 'R-Ear', 'L-Ear']

point_pairs_18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7],
                           [1, 8], [8, 9], [9, 10], [1, 11], [11, 12], [12, 13],
                           [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],
                           [2, 17], [5, 16]]

map_idx_18 = [[31, 32], [39, 40], [33, 34], [35, 36], [41, 42], [43, 44],
                       [19, 20], [21, 22], [23, 24], [25, 26], [27, 28], [29, 30],
                       [47, 48], [49, 50], [53, 54], [51, 52], [55, 56],
                       [37, 38], [45, 46]]

colors_18 = [[0, 100, 255], [0, 100, 255], [0, 255, 255],
                      [0, 100, 255], [0, 255, 255], [0, 100, 255],
                      [0, 255, 0], [255, 200, 100], [255, 0, 255],
                      [0, 255, 0], [255, 200, 100], [255, 0, 255],
                      [0, 0, 255], [255, 0, 0], [200, 200, 0],
                      [255, 0, 0], [200, 200, 0], [0, 0, 0]]

3 predict.py

3.1 代码概述

import cv2
import time
import numpy as np
import matplotlib.pyplot as plt
from config import *

class general_mulitpose_model(object):

   def __init__(self):
       """ 初始化"""
   def get_model(self):
       """ 加载openpose的模型""" 
   def getKeypoints(self):
       """ 获取关键点"""
   def getValidPairs(self):
       """ 获取有效点对"""  
   def getPersonwiseKeypoints(self):
       """ 连接有效点对,获取完整的人体骨骼图"""
   def predict(self):
       """ 整体的预测流程"""
   def vis_pose(self):
       """ 预测结果可视化"""
 

3.2 初始化、模型加载

3.2.1 def __init__()

   def __init__(self, keypoint_num):
   
       self.point_names = point_names_25 if keypoint_num==25 else point_names_18
       self.point_pairs = point_pairs_25 if keypoint_num==25 else point_pairs_25
       self.map_idx = map_idx_25 if keypoint_num==25 else map_idx_25
       self.colors = colors_25 if keypoint_num==25 else colors_25
       self.num_points = 25 if keypoint_num==25 else 18

       self.prototxt = prototxt_25 if keypoint_num==25 else prototxt_18
       self.caffemodel = caffemodel_25 if keypoint_num==25 else caffemodel_18
       self.pose_net = self.get_model()

3.2.2def get_model()

   def get_model(self):
       coco_net = cv2.dnn.readNetFromCaffe(self.prototxt, self.caffemodel)
       # coco_net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
       # coco_net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
       return coco_net

注释掉的两句代码,是用于调用GPU进行预测。使用GPU预测,就要注意opencv的环境的配置,这里不做讲述


3.3 前向计算 def predict()

3.3.1 【step1】读取图片并处理,进行CNN预测,获取预测结果

   def predict(self, imgfile):
       img_cv2 = cv2.imread(imgfile)
       img_width, img_height = img_cv2.shape[1], img_cv2.shape[0]

       net_height = 368
       net_width = int((net_height / img_height) * img_width)

       start = time.time()
       #将读取的图片转化成神经网络在dnn的api中所需形式
       in_blob = cv2.dnn.blobFromImage(
           img_cv2,  1.0 / 255, (net_width, net_height), (0, 0, 0),  swapRB=False, crop=False)
       # 进行神经网络的前向计算,并获取预测结果output
       self.pose_net.setInput(in_blob)
       output = self.pose_net.forward()
       print("[INFO]Time Taken in Forward pass: {}".format(time.time() - start))

我们可以可视化一下预测的关节点的信息。当i=0时,我们可视化的是第0个关节点鼻子。具体操作为:首先把输出的大小调整到与输入一样,然后在图像上进行Alpha混合proMap。

       """========== for testing ========"""
       i = 0
       probMap = output[0, i, :, :]
       probMap = cv2.resize(probMap, (img_width, img_height))
       plt.imshow(cv2.cvtColor(img_cv2, cv2.COLOR_BGR2RGB))
       plt.imshow(probMap, alpha=0.6)
       plt.show()

在这里插入图片描述


3.3.2 【step 2】解析预测结果,获取预测的关节点

我们针对每个关键点,对相应的置信图设置一个阈值(这里采用0.1)生成二值图

  mapSmooth = cv2.GaussianBlur(probMap, (3, 3), 0, 0)
  mapMask = np.uint8(mapSmooth > threshold)
  plt.imshow(mapMask) 
  plt.show()

probMap为预测结果数关键点特征图中的每一通道的热量图。
mapMask为probMap的mask,其中存在这当前关键点的多个blob。第0个通道的mapMask(也就是鼻子的关键点的热量图的mask)可视化为
在这里插入图片描述
寻找关键点的确切位置,我们需要找到当前关键点中的所有blob的极大值。具体步骤如下:

  • 使用阈值,找到当前关节点的所有的blob的mask
  • 迭代每个blob,利用掩码获取每个blob的像素值,然后求取最大位置的坐标和数值。

上面的过程实现的函数为def getKeypoints()
   def getKeypoints(self, probMap, threshold=0.1):
       mapSmooth = cv2.GaussianBlur(probMap, (3, 3), 0, 0)
       mapMask = np.uint8(mapSmooth > threshold)
       
       keypoints = []
       # find the blobs
       contours, hierarchy = cv2.findContours(mapMask, cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
       # len(contours)=2,因为图中存在2个鼻子,contours列表中元素为每一个blob的所有坐标值
       
       # for each blob find the maxima
       for cnt in contours:
           blobMask = np.zeros(mapMask.shape)
           blobMask = cv2.fillConvexPoly(blobMask, cnt, 1)  # 将blobMask中的cnt坐标位置填充1
           maskedProbMap = mapSmooth * blobMask # 获取当前blob位置上的像素值
           _, maxVal, _, maxLoc = cv2.minMaxLoc(maskedProbMap) # 获取maskedProbMap中的最大最小值、以及相应的坐标信息
           keypoints.append(maxLoc + (probMap[maxLoc[1], maxLoc[0]],))

       return keypoints

这里第0个热量图,获取的keyspoints为

  • [(400, 198, 0.8132073), (148, 180, 0.784292)]
getKeypoints()def predict()中被调用的方式如下
## def predict() 中
       detected_keypoints = []  
       keypoints_list = np.zeros((0, 3))
       keypoint_id = 0
       threshold = 0.1
       for part in range(self.num_points):
           probMap = output[0, part, :, :]
           probMap = cv2.resize(probMap, (img_cv2.shape[1], img_cv2.shape[0]))

           keypoints = self.getKeypoints(probMap, threshold)
           print("Keypoints - {} : {}".format(self.point_names[part], keypoints))
           keypoints_with_id = [] # 记录的是当前置信图中获取到的关键点,并且给同类关键点增加了索引号
           for i in range(len(keypoints)):
               keypoints_with_id.append(keypoints[i] + (keypoint_id,))
               keypoints_list = np.vstack([keypoints_list, keypoints[i]])
               keypoint_id += 1

           detected_keypoints.append(keypoints_with_id)

这里需要注意:一个人总共有的关节点个数,这里称之为关节点序号。将图片中所有人关节点进行一个编号,这里称之为编号

  • detected_keypoints:子元素为,某一种关节点的所有检测位置的信息
  • keypoints_list:子元素为,所有被检测的关键点的相关信息

后面会使用到的数据为 detected_keypoints、keypoints_list。之所以分成两种形式记录检测点的信息,是为了更好代码编写和管理

   for i in range(len(detected_keypoints)):
       print(detected_keypoints[i])

运行完该函数打印detected_keypoints如下,其中每一行为一张热量图中获取到对应关键的所有可能位置。[(坐标x, 坐标y, 置信度, 编号),…]

[(400, 198, 0.8132073, 0), (148, 180, 0.784292, 1)]
[(383, 245, 0.8672323, 2), (178, 229, 0.77064836, 3)]
[(321, 244, 0.713973, 4), (118, 230, 0.6964537, 5)]
[(305, 340, 0.83444226, 6), (117, 310, 0.73989004, 7)]
[(321, 418, 0.7464948, 8), (86, 261, 0.77350193, 9)]
[(431, 246, 0.7677428, 10), (226, 228, 0.6995288, 11)]
[(415, 340, 0.74716604, 12), (242, 324, 0.71403414, 13)]
[(415, 419, 0.62556756, 14), (195, 387, 0.7372464, 15), (416, 261, 0.46505377, 16)]
[(178, 387, 0.5826589, 17), (351, 372, 0.6017035, 18)]
[(133, 388, 0.62657654, 19), (306, 372, 0.56834227, 20)]
[(117, 500, 0.67122287, 21), (273, 482, 0.6239272, 22)]
[(101, 626, 0.74809325, 23), (272, 595, 0.6482342, 24)]
[(211, 387, 0.6102824, 25), (383, 373, 0.59796363, 26)]
[(383, 483, 0.6995035, 27), (210, 483, 0.5740754, 28)]
[(225, 625, 0.6215888, 29), (399, 626, 0.7091607, 30)]
[(399, 182, 0.89564496, 31), (132, 167, 0.83662105, 32)]
[(429, 197, 0.8141045, 33), (148, 165, 0.84335065, 34)]
[(117, 182, 0.7798492, 35), (384, 167, 0.7490212, 36)]
[(431, 213, 0.8018406, 37), (164, 150, 0.8991325, 38)]
[(383, 674, 0.74414533, 39), (210, 674, 0.62340456, 40)]
[(399, 673, 0.7276634, 41), (225, 673, 0.5719849, 42)]
[(399, 640, 0.53600895, 43), (226, 626, 0.6315871, 44)]
[(102, 688, 0.6469026, 45), (273, 641, 0.55285895, 46)]
[(87, 673, 0.62596595, 47), (258, 641, 0.5646609, 48)]
[(101, 640, 0.44578055, 49), (273, 609, 0.5640535, 50)]

   for i in range(len(keypoints_list)):
       print(keypoints_list[i])

运行完该函数打印detected_keypoints如下,其中每一行为一张热量图中获取到对应关键的所有可能位置。(坐标x, 坐标y, 置信度),是按照关键点的在keypoints_list的索引号,即该关键点的编号

[400. 198. 0.81320733]
[148. 180. 0.78429198]
[383. 245. 0.86723232]
[178. 229. 0.77064836]
[321. 244. 0.71397299]
[118. 230. 0.69645369]
[305. 340. 0.83444226]
[117. 310. 0.73989004]
[321. 418. 0.74649483]
[ 86. 261. 0.77350193]
[431. 246. 0.76774281]
[226. 228. 0.69952881]
[415. 340. 0.74716604]
[242. 324. 0.71403414]
[415. 419. 0.62556756]
[195. 387. 0.73724639]
[416. 261. 0.46505377]
[178. 387. 0.58265889]
[351. 372. 0.60170352]
[133. 388. 0.62657654]
[306. 372. 0.56834227]
[117. 500. 0.67122287]
[273. 482. 0.62392718]
[101. 626. 0.74809325]
[272. 595. 0.64823419]
[211. 387. 0.61028242]
[383. 373. 0.59796363]
[383. 483. 0.69950348]
[210. 483. 0.5740754]
[2.25000000e+02 6.25000000e+02 6.21588826e-01]
[399. 626. 0.70916069]
[399. 182. 0.89564496]
[132. 167. 0.83662105]
[429. 197. 0.8141045]
[148. 165. 0.84335065]
[117. 182. 0.77984917]
[384. 167. 0.74902117]
[431. 213. 0.8018406]
[164. 150. 0.89913249]
[383. 674. 0.74414533]
[2.10000000e+02 6.74000000e+02 6.23404562e-01]
[399. 673. 0.7276634]
[2.25000000e+02 6.73000000e+02 5.71984887e-01]
[3.99000000e+02 6.40000000e+02 5.36008954e-01]
[226. 626. 0.63158709]
[1.02000000e+02 6.88000000e+02 6.46902621e-01]
[2.73000000e+02 6.41000000e+02 5.52858949e-01]
[8.70000000e+01 6.73000000e+02 6.25965953e-01]
[2.58000000e+02 6.41000000e+02 5.64660907e-01]
[1.01000000e+02 6.40000000e+02 4.45780545e-01]
[2.73000000e+02 6.09000000e+02 5.64053476e-01]


3.3.3 【step3】获取有效连接点对

1 计算算法回顾

E = ∫ u = 0 u = 1 L c ( p ( u ) ) ⋅ d j 2 − d j 1 ∣ ∣ d j 2 − d j 1 ∣ ∣ 2 d u E=\int_{u=0}^{u=1}L_{c}(p(u)) \cdot \frac{d_{j2}-d_{j1}}{||d_{j2}-d_{j1}||_2}du E=u=0u=1Lc(p(u))dj2dj12dj2dj1du 这里 p ( u ) p(u) p(u) 是肢体两端关键点 d j 1 , d j 2 d_{j1},d_{j2} dj1,dj2 的之间的点集(两个关键点之间的插值点)。 p ( u ) = ( 1 − u ) d j 1 + u d j 2 p(u)=(1-u)d_{j1}+ud_{j2} p(u)=(1u)dj1+udj2实际中,我们通过取样u的等间距值,求和对应L值来计算积分。

  • L c ( p ( u ) ) L_c(p(u)) Lc(p(u)) 是在点p位置上的PAF数值;
  • d j 2 − d j 1 ∣ ∣ d j 2 − d j 1 ∣ ∣ 2 \frac{d_{j2}-d_{j1}}{||d_{j2}-d_{j1}||_2} dj2dj12dj2dj1 是两个关键点之间的单位向量
  • p ( u ) p(u) p(u) 是两个关键点之间的插值
  • E E E 是通过计算PAF L 和向量 d i j d_{ij} dij之间的点积得到的。

2 计算流程为:

  • step1:获取每个肢体的的两个PAFs,已经肢体两端的关键点
  • step2:迭代两个关键点的点集,两两计算方向向量 d i j = d j 2 − d j 1 ∣ ∣ d j 2 − d j 1 ∣ ∣ 2 d_{ij} = \frac{d_{j2}-d_{j1}}{||d_{j2}-d_{j1}||_2} dij=dj2dj12dj2dj1
  • step3:在连线两点的线段上创建一个10个插值点的数组 p ( u ) p(u) p(u)
  • step4:并对这些点的PAF和单位向量进行点乘运算 e = L c ( p ( u ) ) ⋅ d i j e = L_{c}(p(u)) \cdot d_{ij} e=Lc(p(u))dij
  • step5:如果这些点中有70%满足标准,则把这一对当成有效

3 进行代码分析:

  • getValidPairs()def predict()中被调用的方式如下,返回的为有效的连接点对和无效的连接点对
       valid_pairs, invalid_pairs =  self.getValidPairs(output, detected_keypoints,img_width,img_height)
  • 具体定义如下
    (其中output为神经网络的输出,detect_keypoints为上面讲解的检测出的关键点,img_width为图片的宽,img_height为图片的高)
   def getValidPairs(self, output, detected_keypoints, img_width, img_height):
       valid_pairs = []
       invalid_pairs = []
       n_interp_samples = 10
       paf_score_th = 0.1
       conf_th = 0.7

step1:获取每个肢体的的两个PAFs,已经肢体两端的关键点

       """迭代每一个肢体的PAFs,进行处理"""
       for k in range(len(self.map_idx)):
           # A->B constitute a limb
           pafA = output[0, self.map_idx[k][0], :, :]
           pafB = output[0, self.map_idx[k][1], :, :]
           pafA = cv2.resize(pafA, (img_width, img_height))
           pafB = cv2.resize(pafB, (img_width, img_height))
           
           # 肢体的两端的关节点 的所有可能位置
           candA = detected_keypoints[self.point_pairs[k][0]]
           candB = detected_keypoints[self.point_pairs[k][1]]

这里获取到了当前肢体的PAFs,可视化后

           # plt.imshow(cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB))
           # plt.imshow(pafA, alpha=0.4)
           # plt.show()

在这里插入图片描述

step2:迭代两个关键点的点集,两两计算方向向量 d i j = d j 2 − d j 1 ∣ ∣ d j 2 − d j 1 ∣ ∣ 2 d_{ij} = \frac{d_{j2}-d_{j1}}{||d_{j2}-d_{j1}||_2} dij=dj2dj12dj2dj1

           nA = len(candA)
           nB = len(candB)

           if (nA != 0 and nB != 0):
               valid_pair = np.zeros((0, 3))
               for i in range(nA):
                   max_j = -1
                   maxScore = -1
                   found = 0
                   for j in range(nB):
                       # 获取关节点A中的第i个,与关节点B中的第j个,计算两个点之间的单位向量
                       d_ij = np.subtract(candB[j][:2], candA[i][:2])
                       norm = np.linalg.norm(d_ij)
                       if norm:
                           d_ij = d_ij / norm
                       else:
                           continue

step3: 在连线两点的线段上创建一个10个插值点的数组 p ( u ) p(u) p(u)

                       # Find p(u)
                       interp_coord = list(
                           zip(np.linspace(candA[i][0], candB[j][0], num=n_interp_samples),
                               np.linspace(candA[i][1], candB[j][1], num=n_interp_samples)))
                       # Find L(p(u))
                       paf_interp = []
                       for k in range(len(interp_coord)):
                           paf_interp.append([pafA[int(round(interp_coord[k][1])), int(round(interp_coord[k][0]))],
                                              pafB[int(round(interp_coord[k][1])), int(round(interp_coord[k][0]))]])

step4:并对这些点的PAF和单位向量进行点乘运算 e = L c ( p ( u ) ) ⋅ d i j e = L_{c}(p(u)) \cdot d_{ij} e=Lc(p(u))dij

                       paf_scores = np.dot(paf_interp, d_ij)
                       avg_paf_score = sum(paf_scores) / len(paf_scores)

step5:如果这些点中有70%满足标准,则把这一对当成有效

                       if (len(np.where(paf_scores > paf_score_th)[
                                   0]) / n_interp_samples) > conf_th:
                           if avg_paf_score > maxScore:
                               max_j = j
                               maxScore = avg_paf_score
                               found = 1

                   # Append the connection to the list
                   if found:
                       valid_pair = np.append(valid_pair,
                                              [[candA[i][3], candB[max_j][3], maxScore]], axis=0)

               # Append the detected connections to the global list
               valid_pairs.append(valid_pair)
           else:  # If no keypoints are detected
               print("No Connection : k = {}".format(k))
               invalid_pairs.append(k)
               valid_pairs.append([])

       return valid_pairs, invalid_pairs

运行完该函数
【invalid_pairs】 中存放的为图片中没有找到的肢体的序号
【valid_pairs】 存放得到数据如下 (有效点对中点A的 编号, 有效点对中点B的 编号,连接的置信度)

  for i in range(len(valid_pairs)):
       print(valid_pairs[i],"===")

[[ 2. 18. 0.90824686]
[ 3. 17. 0.91445097]] ===
[[2. 4. 0.94886258]
[3. 5. 0.79143293]] ===
[[ 2. 10. 0.92424058]
[ 3. 11. 0.92916565]] ===
[[4. 6. 0.8333285 ]
[5. 7. 0.87003183]] ===
[[6. 8. 0.90968755]
[7. 9. 0.87161859]] ===
[[10. 12. 0.76567687]
[11. 13. 0.90720761]] ===
[[12. 16. 0.51112171]
[13. 15. 0.94733449]] ===
[[17. 19. 0.64863247]
[18. 20. 0.649182 ]] ===
[[19. 21. 0.70886038]
[20. 22. 0.7336272 ]] ===
[[21. 23. 0.84220238]
[22. 24. 0.92282994]] ===
[[17. 25. 0.64012247]
[18. 26. 0.59312024]] ===
[[25. 28. 0.91612404]
[26. 27. 0.91176482]] ===
[[27. 30. 0.92497094]
[28. 29. 0.89947163]] ===
[[2. 0. 0.73280045]
[3. 1. 0.95625335]] ===
[[ 0. 31. 0.89538064]
[ 1. 32. 0.79193573]] ===
[[31. 36. 0.78094221]
[32. 35. 0.59810466]] ===
[[ 0. 33. 0.91020581]
[ 1. 34. 0.95672418]] ===
[[33. 37. 0.75157858]
[34. 38. 0.8036731 ]] ===
[[ 4. 36. 0.57039805]
[ 5. 35. 0.74548511]] ===
[[10. 37. 0.8626399 ]
[11. 38. 0.89889973]] ===
[[29. 40. 0.81810908]
[30. 39. 0.77516393]] ===
[[39. 41. 0.75241477]
[40. 42. 0.59358489]] ===
[[29. 44. 0.20911491]
[30. 43. 0.21324079]] ===
[[23. 45. 0.70849594]
[24. 46. 0.8847764 ]] ===
[[45. 47. 0.6372949 ]
[46. 48. 0.64648099]] ===
[[23. 49. 0.21661166]
[24. 50. 0.32713855]] ===


3.3.3 【step 3】连接有效点对,获取完整的人体骨骼图

完成有效关键点对的获取,然后就可以将有效点对组合起来,得到完整且独立的人体骨架。

  • getPersonwiseKeypoints()def predict()中被调用的方式如下,
       personwiseKeypoints = self.getPersonwiseKeypoints(valid_pairs, invalid_pairs,  keypoints_list)
  • 具体定义为:
   def getPersonwiseKeypoints(self, valid_pairs, invalid_pairs, keypoints_list):

【step 1】 针对每类肢体,遍历其有效点对(当肢体存在有效的连接点对时),点对至少存在一组。代码中用partAs、partBs表示。

       # 创建空列表,用来存放每个人的关键点
       personwiseKeypoints = -1 * np.ones((0, self.num_points+1))
       # 遍历每类肢体,并且该肢体存在有效的连接点对
       for k in range(len(self.map_idx)):
           if k not in invalid_pairs:
               partAs = valid_pairs[k][:, 0]  #获取了当前肢体两端,的关节点的序号A的所有位置的编号
               partBs = valid_pairs[k][:, 1] #获取了当前肢体两端,的关节点的序号B的所有位置的编号
               indexA, indexB = np.array(self.point_pairs[k])  #获取了当前肢体,两端所对应的两个关节点的序号

               # 遍历当前肢体的 所有有效的连接点对
               for i in range(len(valid_pairs[k])):
                   found = 0
                   person_idx = -1

【step 2】 针对该类肢体当前有效点对中的点partA,判断其是否在在已有的 personwiseKeypoints 中的第 person_idx人 的关键点中

                   # 判断肢体起始端点partAs,,设置 found、person_idx 的信息
                   for j in range(len(personwiseKeypoints)):
                       if personwiseKeypoints[j][indexA] == partAs[i]:
                           person_idx = j
                           found = 1
                           break

【step 3】 如果是,记录点partA对应的点partB,以及计算相应的置信度

                   if found:
                       personwiseKeypoints[person_idx][indexB] = partBs[i]
                       personwiseKeypoints[person_idx][-1] += keypoints_list[
                                                                  partBs[i].astype(int), 2] + \
                                                              valid_pairs[k][i][2]

【step 4】 如果不是,重新建立一个列表,记录该类肢体当前有效点对中的点partA、partB,以及计算相应的置信度。然后将该列表添加到personwiseKeypoints中。

                   # if find no partA in the subset, create a new subset
                   elif not found and k < self.num_points-1:
                       row = -1 * np.ones(self.num_points+1)
                       row[indexA] = partAs[i]
                       row[indexB] = partBs[i]
                       # add the keypoint_scores for the two keypoints and the paf_score
                       row[-1] = sum(keypoints_list[valid_pairs[k][i, :2].astype(int), 2]) + \
                                 valid_pairs[k][i][2]
                       personwiseKeypoints = np.vstack([personwiseKeypoints, row])

       return personwiseKeypoints


3.4 预测结果画图

    def vis_pose(self, img_file, personwiseKeypoints, keypoints_list):
       img_cv2 = cv2.imread(img_file)
       for i in range(self.num_points-1):
           for n in range(len(personwiseKeypoints)):
               index = personwiseKeypoints[n][np.array(self.point_pairs[i])]
               if -1 in index:
                   continue
               B = np.int32(keypoints_list[index.astype(int), 0])
               A = np.int32(keypoints_list[index.astype(int), 1])
               cv2.line(img_cv2, (B[0], A[0]), (B[1], A[1]), self.colors[i], 3, cv2.LINE_AA)

       plt.figure()
       plt.imshow(img_cv2[:, :, ::-1])
       plt.title("Results")
       plt.axis("off")
       plt.show()

在这里插入图片描述

评价

可以明显的看到,上面的预测结果某些关节点并不准确,比如右边人物的左脚踝,就发生了明显的错误。
为了对比实验,使用openpose官网发布的exe进行测试,使用相同的网络模型(即官网提供的模型),效果要优于该篇博客讲解的基于opencv的openpose的预测结果,大家可以自行测试。
分析原因,排除了网络模型的因素,剩预测时数值精度以及后处理的影响了。具体细节可以阅读官网提供的代码进行算法细节比较。实际使用,看自己项目对速度和精度的要求。

  • 6
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 52
    评论
OpenCV OpenPose人体姿态识别算法是一种基于深度学习技术的人体姿态估计算法,可以通过分析像或视频中的人体关键点来推断人体的姿势。该算法的实现借助了OpenCV库和Caffe深度学习框架。 该算法的代码实现主要涉及以下几个步骤: 1. 首先,导入必要的库和模型。通过调用OpenCV和Caffe提供的接口,加载OpenPose预训练的深度学习模型。 2. 然后,读取像或视频数据。可以通过OpenCV提供的函数读取本地文件,或者使用摄像头实时获取像。 3. 对于每一帧的像,将其输入到OpenPose模型中进行预测。通过调用预训练的模型,可以得到对应关键点的坐标。 4. 接下来,可以根据关键点坐标进行姿态分析和识别。通过分析关键点之间的关系,可以推断出人体的姿势,例如手势、站立、走路等。 5. 最后,将识别结果可视化或输出到文件中。可以使用OpenCV提供的绘函数,将关键点和姿势形化展示,或者将结果保存到本地文件。 需要注意的是,该算法的实现可能需要一些深度学习相关的知识和经验。此外,算法的性能和准确率也会受到输入数据质量和模型训练的影响。 总之,OpenCV OpenPose人体姿态识别算法代码实现像或视频中人体关键点的识别和姿态推断功能,通过深度学习模型和OpenCV库的协同工作,可以实现较准确的人体姿态分析与识别。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 52
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值