读了Openpose的论文,可以清楚,Openpose的GT由两部分组成
-
关键点的标注,采用高斯Map,博主在OpenPose(一):根据关键点生成置信图(Confidence Map)做过解释
-
PAF(Part Affinity Fields),部件关联场,这是一个新的名称,由Openpose首次提出(是不是首次我也不知道,姑且认为是首次吧)。这么高大上的名字,一下子就懵了,博主这里简单暴力理解为,就是对肢体进行标注(肢体分割),目的是通过肢体找关键点直接的连接。
一、什么是PAF(Part Affinity Fields)?
paf就是对肢体进行标注,是身体每个肢体的2D向量,同时保持了肢体区域之间的位置信息和方向信息。
以论文中的公式来解答上图
-
x j 1 , k x_{j1,k} xj1,k 和 x j 2 , k x_{j2,k} xj2,k 表示个体 k k k 的肢体 c c c 的部位 j 1 和 j 2 j1和j2 j1和j2 的肢体坐标。如果一个点 p p p 落在肢体上,则 L c , k ∗ ( p ) L^∗_{c,k}(p) Lc,k∗(p) 的值是一个从 j 1 j1 j1指向 j 2 j2 j2 的单位向量;对于其它点,向量的值为0。
-
训练的时候,生成GT时,PAF在点 p p p 的GT值为
其中, v = ( x j 2 , k − x j 1 , k ) / ∥ x j 2 , k − x j 1 , k ∥ v=(x_{j2,k}−x_{j1,k})/∥x_{j2,k}−x_{j1,k}∥ v=(xj2,k−xj1,k)/∥xj2,k−xj1,k∥ 是肢体的单位向量。
点 p p p 的范围如下:
其中,肢体宽度 σ l σ_l σl 是像素级上的距离,肢体长度 l c , k = ∥ x j 2 , k − x j 1 , k ∥ 2 l_{c,k}=∥x_{j2,k}−x_{j1,k}∥_2 lc,k=∥xj2,k−xj1,k∥2,并且 v ⊥ v_⊥ v⊥是正交于 v v v 的向量。
上面公式中的点乘即
A
⋅
B
A ·B
A⋅B,为
B
B
B 向量在
A
A
A 向量方向上的投影距离,
v
⊥
v_⊥
v⊥和
v
v
v则是两个单位垂直向量。
以上就是为了得出肢体方向上的宽高,形成一个范围,这样只要任意一点落在这个肢体区域内,那就都赋值为
L
c
,
k
∗
(
p
)
L^∗_{c,k}(p)
Lc,k∗(p)即单位向量,如果没有落在这个区域内,则赋值为0;
-
一个肢体,对应两个map,其中一个Map是单位向量中的 x x x,一个Map是单位向量中的 y y y。
-
因为可能出现多个肢体重合,还需要取重合肢体的平均值
二、PAF的生成代码
以一个肢体为例,不考虑重合肢体取平均值
- 第一步、定义点和图像
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
* * *** * * * *
* * * ** * *
**** * ** * *
* * * ** * *
* * ** * * ****
@File :Openpose/3-showPafForJoint
@Date :2020/12/7 下午2:07
@Require : matplotlib, numpy
@Author :hjxu2016, https://blog.csdn.net/hjxu2016/
@Funtion :OpenPose中的 paf
"""
import numpy as np
import matplotlib.pyplot as plt
paf_sigma = 8 # 肢体宽度
shape = (425, 640, 3) # 图像大小
joint_from = np.array([378, 118]) # 肢体的两个点
joint_to = np.array([393, 214])
plt.xlim((0,shape[1]))
plt.ylim((0,shape[0]))
plt.scatter([joint_from[0], joint_to[0]], [joint_from[1], joint_to[1]], color='b')
plt.gca().invert_yaxis() # 将plt的原点由坐下设置为左上
第二步、得到躯干平行方向区域
相当于遍历图上的每一个点,从这个点到joint_from的向量与unit_vector点乘
两个向量点乘相当于取一个向量在另一个向量方向上的投影
如果点乘大于0,那就可以判断这个点在不在这个躯干的方向上了,
(0 <= horizontal_inner_product) & (horizontal_inner_product <= joint_distance)
这个限制条件是保证在与躯干水平的方向上,找出所有落在躯干范围内的点
然而还要判断这个点离躯干的距离有多远
joint_distance = np.linalg.norm(joint_to - joint_from)
unit_vector = (joint_to - joint_from) / joint_distance
rad = np.pi / 2
rot_matrix = np.array([[np.cos(rad), np.sin(rad)], [-np.sin(rad), np.cos(rad)]])
# print("垂直分量 = ", np.dot(rot_matrix,(joint_to - joint_from) ))
vertical_unit_vector = np.dot(rot_matrix, unit_vector) # 垂直分量
print("vertical_unit_vector = ", vertical_unit_vector)
grid_x = np.tile(np.arange(shape[1]), (shape[0], 1))
grid_y = np.tile(np.arange(shape[0]), (shape[1], 1)).transpose() # grid_x, grid_y用来遍历图上的每一个点
horizontal_inner_product = unit_vector[0] * (grid_x - joint_from[0]) + unit_vector[1] * (grid_y - joint_from[1])
horizontal_paf_flag = (0 <= horizontal_inner_product) & (horizontal_inner_product <= joint_distance)
plt.imshow(horizontal_paf_flag)
第三步、得出肢体区域
要判断这个点离躯干的距离有多远,只要拿与起始点的向量点乘垂直分量就可以了,
所以这里的限制条件是paf_width, 不然一个手臂就无限粗了
vertical_paf_flag = np.abs(vertical_inner_product) <= paf_width
这个限制条件是保证在与躯干垂直的方向上,找出所有落在躯干范围内的点(这个躯干范围看来是手工定义的)
vectemp = vertical_unit_vector # 垂直分量
# 求图像上点到线之间的距离 = 图像上的向量与垂直向量的点乘
vertical_inner_product = vectemp[0] * (grid_x - joint_from[0]) + vectemp[1] * (grid_y - joint_from[1])
vertical_paf_flag = np.abs(vertical_inner_product) <= paf_sigma # paf_width : 8
paf_flag = horizontal_paf_flag & vertical_paf_flag # 合并两个限制条件
plt.imshow(paf_flag)
第四步、给肢体区域赋值为单位向量的大小,得到一个肢体的paf
# constant_paf.shape : (2, 368, 368), 上面这一步就是把2维的unit_vector broadcast到所有paf_flag为true的点上去
# constant_paf里面有368*368个点,每个点上有两个值,代表一个矢量
# constant_paf里的这些矢量只会取两种值,要么是(0,0),要么是unit_vector的值
print(unit_vector)
np.broadcast_to(unit_vector , shape[:-1] + (2,))
print(np.broadcast_to(unit_vector , shape[:-1] + (2,)).shape)
paf_flag = paf_flag + 0
constant_paf = np.stack((paf_flag, paf_flag)) * np.broadcast_to(unit_vector, shape[:-1] + (2,)).transpose(2, 0, 1)
# plt.show(constant_paf[0])
print(constant_paf.shape)
print(constant_paf[0].shape)
print(constant_paf[0][119, 379]) # 得到单位向量X=0.1543768802736096
print(constant_paf[1][119, 379]) # 得到单位向量y=0.9880120337511015
把这个肢体显示在原图上
import cv2
bgr_image = cv2.imread("./000000000785.jpg")
rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
heatmap = constant_paf[0]
heatmap = cv2.normalize(heatmap, heatmap, 0, 255, cv2.NORM_MINMAX)
heatmap = np.uint8(heatmap)
jetmap = cv2.applyColorMap(255-heatmap, cv2.COLORMAP_PINK)
alpha = 0.5
out = cv2.addWeighted(rgb_image, alpha, jetmap, 1 - alpha, 0, jetmap)
plt.imshow(out)
三、预测部分
四、多人解析PAFS
参考资料整理在