《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》论文笔记

本文主要是介绍《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》论文笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》论文笔记


原论文: Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields
源码:ZheC/Realtime_Multi-Person_Pose_Estimation
   CMU-Perceptual-Computing-Lab/openpose

文章亮点

  • 多人实时人体检测
  • PCM(Part Confidence Maps)+PAF(Part Affinity Fields)
  • Bipartite Matching

人体姿态估计

人体姿态估计的挑战:1.每张图片包含不同数量的人,并且每个人会有不同的姿态;2.图片中肢体遮挡、接触等使得姿态估计比较困难;3.估计的时间随着图片中人数的增长而增加使得实时监测很有挑战。

人体姿态估计两种主流的方式是:top-down approach和bottom-up approach。top-down是在检测到图片中的人之后,对人的姿态进行估计。bottom-up是指先检测关节点再判断关节点属于哪个人。

该论文提出的模型是一种bottom-up的估计方法。通过Convolutional pose machines的方式得到关节点的heatmap来判断关节点(body part),通过PAF的CNN结构(也是采用Convolutional pose machines的网络结构)得到2D的向量集合来编码肢体(limb)的位置和方向。此时判断关节点属于哪个人可以转化为经典的二分图解决方法。通过这种方式解决了之前bottom-up在判断关节点属于哪个人时的NP困难问题,实现了实时估计,具体流程见图1。
figure1
图1 (a)的图片输入模型,生成(b)的PCM热量图和(c)的PAF向量图,通过二分图匹配来确定多人下关节点肢体匹配问题,最终输出得到(e)。

网络结构

输入一张 wh 的图片(已通过CNN提取特征),通过前向传播预测关节点位置的2D置信图S(PCM)和这些关节点亲和度向量集L(PAF)。集合 S=(S1,S2,...,SJ) 指有 J 个关节点对应的位置置信图,S的维度为wh,集合 L=(L1,L2,...LC) 指有 C 个关节点亲和度的向量图,L的维度为 wh2 ,网络结构见图2。
figure2
图2 双层多阶段的CNN架构。每个阶段的第一层预测置信图 St ,第二层预测PAFs Lt 。每个阶段输出之后,输入F和该阶段的两层输出堆叠起来作为下一阶段的输入。

St=ρt(F)t=1
Lt=ϕt(F)t=1
St=ρt(F,St1,Lt1)t2
Lt=ϕt(F,St1,Lt1)t2

ρt , ϕt 为阶段t下CNN计算。
损失函数的计算如下:
ftS=j=1JpW(p)||Stj(p)Sj||22
ftL=j=1JpW(p)||Ltj(p)Lj||22

Sj j 关节点的置信图的groundtruth,Lj是PAF向量的groundtruth, W 是一个二元掩码,当p像素没有被标注时, W(p)=0 (由于某些数据集并没有标注所有的人)。最终的损失函数为:
f=t=1T(ftS+ftL)
每个阶段对损失函数都有贡献可以防止梯度消失的产生。

论文外阅读源码时一些模型细节的补充:
  • F为输入图像经过10层VGG-19后通过两层 3×3 的卷积层(Convolutional pose machines结构),这两层卷积层不改变图像的大小只降低通道数,从512通道降低为128通道。
  • PCM的输出通道为19,PAF的输出通道为38,即 J=19,C=19 ,stage中均不改变图像的大小。
  • stage为6

数据集PCM label处理

计算公式如下:

Sj,k(p)=exp(||pxj,k||22σ2)
Sj,k(p) 为第 k 个人,第j个body part的置信度, xj,k 为第 k 个人,第j个body part的位置groundtruth。即,PCM满足高斯分布,峰值为body part的位置groundtruth。当某个像素点存在多个人的body part置信度时,取最大值而非平均值。这是因为平均值会磨平峰值,而选择最大值会使得像素点靠近峰值时依旧很准确,如图3所示。
figure3
图3

数据集PAF label处理

PAF是每个肢体(limb)的2D向量场:对于位于limb上的像素点,该点的2D向量表示该limb连接两个body part的亲和力大小和方向。
对于单个limb,假设 xj1,k , xj2,k 为第 k 个人两个body part j1, j2 的位置groundtruth。如果 p 在limb上,则Lc,k(p)=v,v=(xj2,kxj1,k)/||(xj2,kxj1,k)||2;如果 p 不在limb上,Lc,k(p)为0向量。
按照如下范围判断像素 p 是否位于limb上:

0v(pxj1,k)||(xj2,kxj1,k)||2()

0v(pxj1,k)σl)
σl 为设置的阈值。
当一个图像上有多人时,取平均值:
Lc(p)=1nc(p)kLc,k(p)
nc(p) p 像素处非零向量的数目。

预测结果的算法

假设只有两个预测的候选位置点dj1, dj2 ,定义这两个点关联的置信度 E 计算公式如下:

E=u=1u=0Lc(p(u))dj1dj2||dj1dj2||2
其中
p(u)=(1u)dj1+udj2
实际测试中,往往是选择两点间等间隔分布的像素点进行求和计算来取代积分计算。
多人估计时,首先通过非极大值抑制算法选择出body part的候选位置,源码如下:

import scipy
print heatmap_avg.shape#plt.imshow(heatmap_avg[:,:,2])
from scipy.ndimage.filters import gaussian_filter
all_peaks = []
peak_counter = 0for part in range(19-1):x_list = []y_list = []map_ori = heatmap_avg[:,:,part]map = gaussian_filter(map_ori, sigma=3)map_left = np.zeros(map.shape)map_left[1:,:] = map[:-1,:]map_right = np.zeros(map.shape)map_right[:-1,:] = map[1:,:]map_up = np.zeros(map.shape)map_up[:,1:] = map[:,:-1]map_down = np.zeros(map.shape)map_down[:,:-1] = map[:,1:]peaks_binary = np.logical_and.reduce((map>=map_left, map>=map_right, map>=map_up, map>=map_down,map > param['thre1']))peaks = zip(np.nonzero(peaks_binary)[1], np.nonzero(peaks_binary)[0]) # note reversepeaks_with_score = [x + (map_ori[x[1],x[0]],) for x in peaks]id = range(peak_counter, peak_counter + len(peaks))peaks_with_score_and_id = [peaks_with_score[i] + (id[i],) for i in range(len(id))]all_peaks.append(peaks_with_score_and_id)peak_counter += len(peaks)

此时一个body part会存在多个候选位置 DJ={dmj:forj(1...J),m(1...Nj)} , Nj 为body party j 候选位置。关于候选位置的关联问题可以转化为二分图问题:
对于第c个limb

maxEc=maxmDj1nDj2Emnzmnj1j2

s.t.

mDj1,nDj2zmnj1j21
mDj2,nDj1zmnj1j21

zmnj1j2{0,1} 表示候选位置 dmj1 dmj2 是否连接, Emn 为候选位置连接的置信度。
文章中最终选择匈牙利算法来解决此二分图问题,源码如下:

# find connection in the specified sequence, center 29 is in the position 15
limbSeq = [[2,3], [2,6], [3,4], [4,5], [6,7], [7,8], [2,9], [9,10], \[10,11], [2,12], [12,13], [13,14], [2,1], [1,15], [15,17], \[1,16], [16,18], [3,17], [6,18]]
# the middle joints heatmap correpondence
mapIdx = [[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]]connection_all = []
special_k = []
mid_num = 10for k in range(len(mapIdx)):score_mid = paf_avg[:,:,[x-19 for x in mapIdx[k]]]candA = all_peaks[limbSeq[k][0]-1]candB = all_peaks[limbSeq[k][1]-1]nA = len(candA)nB = len(candB)indexA, indexB = limbSeq[k]if(nA != 0 and nB != 0):connection_candidate = []for i in range(nA):for j in range(nB):vec = np.subtract(candB[j][:2], candA[i][:2])norm = math.sqrt(vec[0]*vec[0] + vec[1]*vec[1])vec = np.divide(vec, norm)startend = zip(np.linspace(candA[i][0], candB[j][0], num=mid_num), \np.linspace(candA[i][1], candB[j][1], num=mid_num))vec_x = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])),\0] for I in range(len(startend))])vec_y = np.array([score_mid[int(round(startend[I][1])), int(round(startend[I][0])),\1] for I in range(len(startend))])score_midpts = np.multiply(vec_x, vec[0]) + np.multiply(vec_y, vec[1])score_with_dist_prior = sum(score_midpts)/len(score_midpts) + min(0.5*oriImg.shape[0]/norm-1, 0)criterion1 = len(np.nonzero(score_midpts > param['thre2'])[0]) > 0.8 *len(score_midpts)criterion2 = score_with_dist_prior > 0if criterion1 and criterion2:connection_candidate.append([i, j, score_with_dist_prior,score_with_dist_prior+candA[i][2]+candB[j][2]])connection_candidate = sorted(connection_candidate, key=lambda x: x[2], reverse=True)connection = np.zeros((0,5))for c in range(len(connection_candidate)):i,j,s = connection_candidate[c][0:3]if(i not in connection[:,3] and j not in connection[:,4]):connection = np.vstack([connection, [candA[i][3], candB[j][3], s, i, j]])if(len(connection) >= min(nA, nB)):breakconnection_all.append(connection)else:special_k.append(k)connection_all.append([])
# last number in each row is the total parts number of that person
# the second last number in each row is the score of the overall configuration
subset = -1 * np.ones((0, 20))
candidate = np.array([item for sublist in all_peaks for item in sublist])for k in range(len(mapIdx)):if k not in special_k:partAs = connection_all[k][:,0]partBs = connection_all[k][:,1]indexA, indexB = np.array(limbSeq[k]) - 1for i in range(len(connection_all[k])): #= 1:size(temp,1)found = 0subset_idx = [-1, -1]for j in range(len(subset)): #1:size(subset,1):if subset[j][indexA] == partAs[i] or subset[j][indexB] == partBs[i]:subset_idx[found] = jfound += 1if found == 1:j = subset_idx[0]if(subset[j][indexB] != partBs[i]):subset[j][indexB] = partBs[i]subset[j][-1] += 1subset[j][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2]elif found == 2: # if found 2 and disjoint, merge themj1, j2 = subset_idxprint "found = 2"membership = ((subset[j1]>=0).astype(int) + (subset[j2]>=0).astype(int))[:-2]if len(np.nonzero(membership == 2)[0]) == 0: #mergesubset[j1][:-2] += (subset[j2][:-2] + 1)subset[j1][-2:] += subset[j2][-2:]subset[j1][-2] += connection_all[k][i][2]subset = np.delete(subset, j2, 0)else: # as like found == 1subset[j1][indexB] = partBs[i]subset[j1][-1] += 1subset[j1][-2] += candidate[partBs[i].astype(int), 2] + connection_all[k][i][2]# if find no partA in the subset, create a new subsetelif not found and k < 17:row = -1 * np.ones(20)row[indexA] = partAs[i]row[indexB] = partBs[i]row[-1] = 2row[-2] = sum(candidate[connection_all[k][i,:2].astype(int), 2]) +connection_all[k][i][2]subset = np.vstack([subset, row])

模型在MPII上的表现

table1
表1 分别为在测试子集和完整测试集上不同模型结果的对比

figure4
图4 在不同的PCKh阈值下mAP的变化曲线。
PCKh-0.5阈值下,使用PAFs,其mAP比one-midpoint的高2.9%,比two-midpoints的方法高2.3%。这是因为PAFs同时利用了位置和方向这两个信息,在人体有交叉的图像中表现更好。通过对图像未标记的的部分进行掩码,提高了2.3%mAP,因为它避免了训练时对正确的预测进行损失惩罚。PAFs算法可以得到与使用GT连接相似的mAP结果(分别为79.4%和81.6%)

模型在COCO上的表现

figure5
图5 在COCO数据集上AP成绩和运行时间
图5(d)数据来源为:原始的图片为 1080×1920 ,resize成 368×654 ,GPU型号为NVIDIA GeForce GTX-1080 GPU。最终结果可以发现top-down方法运行时间会随着图片人数的增多显著提高,而使用bottom-up其运行时间相对很缓慢。

相关阅读

人体姿态估计综述by桃木子

这篇关于《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》论文笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/616818

相关文章

AI hospital 论文Idea

一、Benchmarking Large Language Models on Communicative Medical Coaching: A Dataset and a Novel System论文地址含代码 大多数现有模型和工具主要迎合以患者为中心的服务。这项工作深入探讨了LLMs在提高医疗专业人员的沟通能力。目标是构建一个模拟实践环境,人类医生(即医学学习者)可以在其中与患者代理进行医学

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

2014 Multi-University Training Contest 8小记

1002 计算几何 最大的速度才可能拥有无限的面积。 最大的速度的点 求凸包, 凸包上的点( 注意不是端点 ) 才拥有无限的面积 注意 :  凸包上如果有重点则不满足。 另外最大的速度为0也不行的。 int cmp(double x){if(fabs(x) < 1e-8) return 0 ;if(x > 0) return 1 ;return -1 ;}struct poin

2014 Multi-University Training Contest 7小记

1003   数学 , 先暴力再解方程。 在b进制下是个2 , 3 位数的 大概是10000进制以上 。这部分解方程 2-10000 直接暴力 typedef long long LL ;LL n ;int ok(int b){LL m = n ;int c ;while(m){c = m % b ;if(c == 3 || c == 4 || c == 5 ||

2014 Multi-University Training Contest 6小记

1003  贪心 对于111...10....000 这样的序列,  a 为1的个数,b为0的个数,易得当 x= a / (a + b) 时 f最小。 讲串分成若干段  1..10..0   ,  1..10..0 ,  要满足x非递减 。  对于 xi > xi+1  这样的合并 即可。 const int maxn = 100008 ;struct Node{int

论文翻译:arxiv-2024 Benchmark Data Contamination of Large Language Models: A Survey

Benchmark Data Contamination of Large Language Models: A Survey https://arxiv.org/abs/2406.04244 大规模语言模型的基准数据污染:一项综述 文章目录 大规模语言模型的基准数据污染:一项综述摘要1 引言 摘要 大规模语言模型(LLMs),如GPT-4、Claude-3和Gemini的快

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个