🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
🖍foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟👋
文章目录
动作识别是计算机视觉的关键部分,涉及识别人的手、腿、头部和身体位置,以检测特定动作并将其分类为众所周知的类别。困难在于视觉输入的变化(例如身体凌乱或被衣服覆盖),类似的动作但不同的类别,例如喝水或使用手持手机交谈,以及获得有代表性的训练数据。
本章详细概述了我们可用于人体姿态估计和动作识别的关键方法。动作识别结合了姿态估计方法和基于加速度的活动识别,以及基于视频和三维点云的动作识别。该理论将通过使用 TensorFlow 2.0 对其实现的解释来补充。
本章分为四个部分。前三个讨论了我们可以用于人体姿态估计的三种不同方法,而第四个则是关于动作识别:
- 人体姿态估计——OpenPose
- 人体姿态估计——stacked hourglass model
- 人体姿态估计——PoseNet
- 使用各种方法的动作识别
人体姿态估计——OpenPose
人体姿态估计是深度神经网络取得显著成功的另一个领域,近年来发展迅速。在最后几章中,我们了解到深度神经网络使用线性(卷积)和非线性(ReLU)操作的组合来预测一组给定输入图像的输出。在姿态估计的情况下,当给定一组输入图像时,深度神经网络会预测关节位置。图像中的标记数据集由一个边界框组成,该边界框确定图像中的N个人和K每人关节。随着姿势的变化,关节的方向也会发生变化,因此通过查看关节的相对位置来表征不同的位置。在接下来的部分中,我们将描述我们可以使用的不同姿势估计方法。
OpenPose 背后的理论
OpenPose 是第一个开源实时二维姿态估计系统,用于图像或视频中的多人。它主要由卡内基梅隆大学( CMU ) 的学生和教职员工开发。论文的标题是OpenPose: Realtime Multi-Person 2D Pose Estimation Using Part Affinity Fields,作者是Zhe Cao、Gines Hidalgo、Tomas Simon、Shih-En-Wei 和 Yaser Sheikh。你可以在https://arxiv.org/abs/1812.08008找到这篇论文。
本文的主要发现如下:
- 网络的输入由 VGG-19 模型的前 10 层组成,用于生成一组特征图F。OpenPose 网络的架构如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/d402c7ff102e782467ad7f6b44b74530.png)
- OpenPose 网络以特征图为输入,由 CNN 的两个阶段组成:第一阶段预测具有 T p次迭代的Part Affinity Fields ( PAF ) ,而第二阶段预测具有 T c次迭代的置信度图. 由于两个关键指标,2018 年提出的 OpenPose 模型比 2017 年提出的早期模型普遍改进:
- 通过计算 PAF 和置信度图将计算时间减少一半。这不同于同时计算两者并将 7 x 7 卷积替换为 3 x 3 卷积。
- 改进的 PAF 估计(2018 年论文)通过增加神经网络的深度提高了常规 PAF(2017 年)的置信度图,从而提高了准确性。
- 在下一阶段,将前一阶段的预测与原始图像特征F连接起来,为图像中的所有人生成二维关键点预测。在估计预测、地面实况特征图和 PAF 之间的每个阶段结束时应用损失函数。这个过程会重复几次迭代,从而产生最新的特征图和 PAF 检测。
- 使用了基于特征向量的自下而上的方法,无论图像中有多少人,都能获得很高的准确率。
- 置信度图是特定特征(身体部位)可以位于任何给定像素中的概率的二维表示。另一方面,特征图表示 CNN 给定层中给定滤波器的输出图。
- 网络架构由几个 1 x 1 和 3 x 3 内核组成。每个 3 x 3 内核的输出是串联的。
- OpenPose 是第一个使用三个独立的 CNN 模块检测 135 个关键点的实时多人系统:(a) 身体和足部检测,(b) 手部检测,以及 (c) 面部检测。
- 在目标检测讨论(第 5 章,神经网络架构和模型,以及第 7 章,使用 YOLO 的目标检测)中,我们发现与单次检测相比,Faster R-CNN 等区域提议方法的精度更高但速度更低SSD 或 YOLO 等方法。类似地,对于人体姿态估计,与自下而上的方法相比,自上而下的方法实现了更高的准确度但速度更低。自上而下的方法分别提供图像中由人组成的每个边界框。自下而上的方法馈送由人的几个边界框组成的整个图像,从而导致人的分辨率更小。
2019 年,OpenPose 的作者和其他一些人(Gines Hidalgo、Yaadhav Raaj、Haroon Idrees、Donglai Xiang、Hanbyul Joo、Tomas Simon1 和 Yaser Sheikh)在一篇名为Single -网络全身姿势估计。您可以在https://arxiv.org/abs/1909.13423找到这篇论文。
主要特点如下:
- 该网络不需要重复进行手部和面部姿势检测,因此与 OpenPose 相比更快。多任务学习( MTL ) 用于从四个不同的任务中训练单个全身估计模型:身体、面部、手部和足部检测。
- 通过连接面部、手和脚的置信度图来增强关键点置信度图。使用这种方法,所有的关键点都定义在同一个模型架构下。
- 增加了网络架构输入分辨率和卷积层数以提高整体精度。
- 对于包含一个人的图像,检测时间输出比 OpenPose 快约 10%。
了解 OpenPose 代码
CMU 使用 OpenPose 模型,而 OpenCV 在其新的深度神经网络( DNN ) 框架中集成了预训练的 OpenPose 模型。整个代码块可以从以下 GitHub 页面下载。该模型使用 TensorFlow 示例而不是 OpenPose 作者最初使用的 Caffe 模型,可以在GitHub - quanhua92/human-pose-estimation-opencv: Perform Human Pose Estimation in OpenCV Using OpenPose MobileNet找到。
可以使用以下命令在终端中执行 OpenCV 的 OpenPose 代码:
python openpose.py --input image.jpg
要开始使用 PC 的网络摄像头,只需在终端中输入以下内容:
python openpose.py
下图显示了足球运动员图像的 OpenPose 实现:
该算法容易受到背景图像的影响,如下面的棒球运动员图像所示:
去除背景后,算法的预测效果非常好。
让我们回顾一下代码的主要功能。我们将定义关键点,然后构建预测模型:
1.模型输入 18 个身体部位和姿势对,如下所示:
BODY_PARTS = { "Nose": 0, "Neck": 1, "RShoulder": 2, "RElbow": 3, "RWrist": 4,"LShoulder": 5, "LElbow": 6, "LWrist": 7, "RHip": 8, "RKnee": 9,"RAnkle": 10, "LHip": 11, "LKnee": 12, "LAnkle": 13, "REye": 14,"LEye": 15, "REar": 16, "LEar": 17, "Background": 18 }
POSE_PAIRS = [ ["Neck", "RShoulder"], ["Neck", "LShoulder"], ["RShoulder", "RElbow"],["RElbow", "RWrist"], ["LShoulder", "LElbow"], ["LElbow", "LWrist"],["Neck", "RHip"], ["RHip", "RKnee"], ["RKnee", "RAnkle"], ["Neck", "LHip"],["LHip", "LKnee"], ["LKnee", "LAnkle"], ["Neck", "Nose"], ["Nose", "REye"],["REye", "REar"], ["Nose", "LEye"], ["LEye", "LEar"] ]
2.接下来,使用以下代码通过 TensorFlow 实现 OpenPose:
net = cv.dnn.readNetFromTensorflow("graph_opt.pb")
3.接下来,使用以下方法对图像进行预处理(执行减法和缩放)cv.dnn.blobFromImage:
net.setInput(cv.dnn.blobFromImage(frame, 1.0, (inWidth, inHeight), (127.5, 127.5, 127.5), swapRB=True,crop=False))
4.接下来,我们使用out = net.forward()并获得 MobileNetV1 输出的前 19 个元素来预测模型的输出:
out = out[:, :19, :, :]
5.以下代码计算热图,使用 OpenCV 的minMaxLoc函数找到点值,如果其置信度高于阈值,则添加一个点。热图是用颜色表示的数据图:
for i in range(len(BODY_PARTS)):
# 切片相应身体部位的热图。
heatMap = out[0, i, :, :]
# 最初,我们试图找到所有的局部最大值。为了简化示例
# 我们只需找到一个全局示例。
然而,这种方式只能同时检测到一个姿势。
_, conf, _, point = cv.minMaxLoc(heatMap)
x = (frameWidth * point[0]) / out.shape[3]
y = (frameHeight * point[1]) / out.shape[2]
# 添加如果它的置信度高于阈值,则为一个点。
points.append((int(x), int(y)) if conf > args.thr else None)
6.以下代码块显示了原始图像中使用cv.line和的关键点:cv.ellipse
for pair in POSE_PAIRS:
partFrom = pair[0]
partTo = pair[1]
assert(partFrom in BODY_PARTS)
assert(partTo in BODY_PARTS)
idFrom = BODY_PARTS[partFrom]
idTo = BODY_PARTS[partTo]
if points[idFrom] and points[idTo]:
cv.line(frame, points[idFrom], points[idTo], (0, 255, 0), 3)
cv.ellipse(frame, points[idFrom], (3, 3), 0, 0, 360, (0, 0, 255),cv.FILLED)
cv.ellipse(frame, points[idTo], (3, 3), 0, 0, 360, (0, 0, 255),cv.FILLED)
到目前为止,我们已经使用 OpenPose 使用自下而上的方法确定多个身体姿势。在下一节中,我们将使用堆叠沙漏法,它同时使用自上而下和自下而上的方法。
人体姿态估计——stacked hourglass model
stacked hourglass model由 Alejandro Newell、Kaiyu Yang 和 Jia Deng 在他们的论文Stacked Hourglass Networks for Human Pose Estimation中于 2016 年开发。该模型的详细信息可以在https://arxiv.org/abs/1603.06937找到。
该模型的架构如下图所示:
该模型的主要特点如下:
- 通过将多个沙漏堆叠在一起,在所有尺度上重复对特征进行自下而上和自上而下的处理。这种方法能够验证整个图像的初始估计和特征。
- 该网络使用多个卷积和一个最大池化层,这会导致最终分辨率较低,然后再进行上采样以恢复分辨率。
- 在每个最大池化步骤中,额外的卷积层会与主网络并行添加。
- 输出产生的热图表示每个像素中关节存在的概率。
- 该架构广泛使用残差模型。每个残差都有三层:
- 1 x 1 Conv2D 用于从 256 到 128 个通道的降维
- 128 个通道的 3 x 3 Conv2D
- 1 x 1 Conv2D 用于从 128 到 256 个通道的维度增加
- 该架构从步长为 2 的 7 x 7 卷积开始,将输入图像从 256 x 256 提升到 64 x 64,从而可以有效地使用 GPU 内存。
- 使用步长为 2 的 2 x 2 的最大池化来对图像进行下采样。在执行每个最大池之前和之后,添加残差块,然后在上采样到原始大小后添加回主块。
- 最终的标头块由两个 1 x 1 Conv2D 组成。
- 最好的结果是八个沙漏模块堆叠在一起;每个沙漏模块在每个分辨率下都有一个残差模块。
- 该模型需要大约 40,000 次迭代才能获得 70% 以上的准确度。
- 训练过程需要大约 5,000 个图像(4,000 个用于训练,1,000 个用于测试)和40,000 个用于 MPII 的注释样本(28,000 个用于训练,12,000 个用于测试)。电影中标记的帧(FLIC,可从FLIC dataset获得)和 MPII(http://human-pose.mpi-inf.mpg.de)是两个姿势估计数据库。FLIC 由 5,003 个图像(3,987 个用于训练,1,016 个用于测试)组成,这些图像取自电影,而 MPII 由40,000 个带注释的样本(28,000 个用于训练,12,000 个用于测试)组成。
- 该网络在 Torch 7 上进行训练,学习率为 2.5e-4。在 12 GB NVIDIA Titan X GPU 上训练大约需要 3 天时间。
了解hourglass model
沙漏模型在 MPII 人体姿势数据集中的所有关节上实现了最先进的结果,但这是以资源密集型网络带宽使用为代价的。这是由于每层的通道数较多导致训练困难。FastPose蒸馏( FPD ) 在 CVPR 2019 中由 Feng Zhang、Xiatian Zhu 和 Mao Ye 在他们题为Fast Human Pose Estimation的论文中介绍。与沙漏模型相比,FPD 可实现更快且更具成本效益的模型推理,同时达到相同的模型性能。主要特点如下:
- 四个沙漏(而不是八个)足以预测 95% 的模型准确度。
- 从 256 个通道减少到 128 个通道只会导致准确度下降 1%。
- 首先,在大沙漏模型(也称为教师姿势模型)上进行训练。然后,在教师姿势模型的帮助下训练目标学生模型(四个沙漏,128 个通道)。定义蒸馏损失函数以从教师模型中提取知识并将其传递给学生模型。
- 位姿蒸馏损失函数如下:
这里,K是关节的总数,L pd是 FPD 的预测关节置信度图,m ks 是学生模型预测的第k个关节的置信度图,m kt 是第k个关节的置信度图由教师模型预测。
- 整体损失函数如下:
这里,L fpd是整体 FPD 损失函数,L gt是地面实况标注的置信度图,M是权重函数。
- 使用前面描述的损失函数分别训练教师模型和目标模型。
该模型的 Keras 实现可以在https://github.com/yuanyuanli85/Stacked_Hourglass_Network_Keras找到
在下一节中,我们将描述模型的架构并详细解释它。
编写hourglass model
沙漏模型的编码块如下图所示:
让我们花点时间来理解前面的图表,因为我们将在下一节中对其进行编码:
- 前端模块将图像作为输入和通道数(每层的第三个维度,前两个维度是宽度和高度)。
- 图像通过前面模块的不同层,最终模块连接到左半块。
- 左半块有四个瓶颈卷积块——f1、f2、f4 和 f8——每一个的分辨率分别为 1、1/2、1/4 和 1/8。如果你看一下架构图,这应该很清楚,就在堆叠的沙漏感应下。
- 每个块的最后一层——f1、f2、f4 和f8——创建相应的特征图,即 lf1、lf2、lf4 和 lf8。
- 特征图——lf1、lf2、lf4 和 lf8——连接到右半块。该模块的输出是 rf1。
- 底层也从左半块连接到 lf8 特征图。
- 头块连接到 rf1。总共有两个头块。每个都使用 1 x 1 卷积。
让我们看一下不同的代码块。
argparse 块
Python 命令行参数(通过终端输入)允许程序通过命令获取有关神经网络操作的不同指令parser.add_argument。这可以从argparse函数包中导入。
下图显示了 16 个不同的类:
用于上图的代码块如下:
0 - r ankle, 1 - r knee, 2 - r hip, 3 - l hip, 4 - l knee, 5 - l ankle, 6 - pelvis, 7 - thorax, 8 - upper neck, 9 - head top, 10 - r wrist, 11 - r elbow, 12 - r shoulder, 13 - l shoulder, 14 - l elbow, 15 - l wrist
以下代码导入argparse模块、TensorFlow 和 HourglassNet 模型。它有两种类型的用户可选模型:128 个用于微型网络的通道和 256 个用于大型网络的通道:
import argparse
import os
import tensorflow as tf
from keras import backend as k
from hourglass import HourglassNet
parser.add_argument("--resume", default=False, type=bool, help="resume training or not")
parser.add_argument("--resume_model", help="start point to retrain")
parser.add_argument("--resume_model_json", help="model json")
parser.add_argument("--init_epoch", type=int, help="epoch to resume")
parser.add_argument("--tiny", default=False, type=bool, help="tiny network for speed, inres=[192x128], channel=128")
args = parser.parse_args()
if args.tiny:
xnet = HourglassNet(num_classes=16, num_stacks=args.num_stack, num_channels=128, inres=(192, 192),outres=(48, 48))
else:
xnet = HourglassNet(num_classes=16, num_stacks=args.num_stack, num_channels=256, inres=(256, 256),outres=(64, 64))
if args.resume:
xnet.resume_train(batch_size=args.batch_size, model_json=args.resume_model_json,model_weights=args.resume_model,init_epoch=args.init_epoch, epochs=args.epochs)
else:
xnet.build_model(mobile=args.mobile, show=True)
xnet.train(epochs=args.epochs, model_path=args.model_path, batch_size=args.batch_size)
训练hourglass network
沙漏网络已经被描述过了。在本节中,我们将解释训练网络背后的代码。
训练沙漏网络的代码如下:
def build_model(self, mobile=False, show=False):
if mobile:
self.model = create_hourglass_network(self.num_classes, self.num_stacks, self.num_channels, self.inres, self.outres, bottleneck_mobile)
else:
self.model = create_hourglass_network(self.num_classes, self.num_stacks,self.num_channels, self.inres, self.outres, bottleneck_block)
# 显示模型摘要和层名
if show:
self.model.summary(def train(self, batch_size, model_path, epochs):
train_dataset = MPIIDataGen("../../data/mpii/mpii_annotations.json", "../../data/mpii/images"
inres=self.inres, outres=self.outres, is_train=True)
#here MPIIDataGen is a data generator function (not shown here) - it takes in json file and the images to preapre data for training similar to how we use image data generator in Chapter6.
train_gen = train_dataset.generator(batch_size, self.num_stacks, sigma=1, is_shuffle=True,rot_flag=True, scale_flag=True, flip_flag=True)
csvlogger = CSVLogger(os.path.join(model_path, "csv_train_" + str(datetime.datetime.now().strftime('%H:%M')) + ".csv"))
modelfile = os.path.join(model_path, 'weights_{epoch:02d}_{loss:.2f}.hdf5')
checkpoint = EvalCallBack(model_path, self.inres, self.outres)
xcallbacks = [csvlogger, checkpoint]
self.model.fit_generator(generatepochs=epochs, callbacks=xcallbacks)
前面的代码是一个典型示例,说明了如何设置神经网络进行训练。我们在第 6 章“使用迁移学习的视觉搜索”中详细介绍了这一点。主要特点如下:
- create_hourglass_network是主要型号。
- train_dataset使用 MPIIDatagen,这是一个用于输入数据的外部模块。
- train_gen输入train_dataset并增强图像。
- 它包含回调和检查点,以便我们在训练期间了解模型的内部状态。
- model.fit_generator开始训练过程。
创建hourglass network
沙漏模型代码的实际实现将在此处说明。为此的代码称为create_hourglass_network. 如前所述,代码具有以下组件。
Front module
以下代码描述了前端模块:
def create_front_module(input, num_channels, bottleneck):
_x = Conv2D(64, kernel_size=(7, 7), strides=(2, 2), padding='same', activation='relu', name='front_conv_1x1_x1')(input)
_x = BatchNormalization()(_x)
_x = bottleneck(_x, num_channels // 2, 'front_residual_x1')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(_x)
_x = bottleneck(_x, num_channels // 2, 'front_residual_x2')
_x = bottleneck(_x, num_channels, 'front_residual_x3')
return _x
front_features = create_front_module(input, num_channels, bottleneck)
如前所述,这由一个 Conv2D 块组成,该块共有 64 个过滤器,过滤器大小为 7 x 7,步幅为 2。该块的输出为(None, 32, 32,6)。接下来的几行贯穿批标准化、瓶颈和最大池化层。让我们定义瓶颈块。
Left half-block
左半块代码如下:
def create_left_half_blocks(bottom, bottleneck, hglayer, num_channels):
# create left half blocks for hourglass module
# f1, f2, f4 , f8 : 1, 1/2, 1/4 1/8 resolution
hgname = 'hg' + str(hglayer)
f1 = bottleneck(bottom, num_channels, hgname + '_l1')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(f1)
f2 = bottleneck(_x, num_channels, hgname + '_l2')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(f2)
f4 = bottleneck(_x, num_channels, hgname + '_l4')
_x = MaxPool2D(pool_size=(2, 2), strides=(2, 2))(f4)
f8 = bottleneck(_x, num_channels, hgname + '_l8')
return (f1, f2, f4, f8)
上述代码执行两个特定操作:
- (f1, f2, f4, and f8)它定义了分辨率为的滤波器系数1, ½, ¼, and 1/8。
- 对于每个过滤器块,它通过使用过滤器为 2 和步长为 2 的最大池化来生成最终输出。
接下来,以下代码迭代 0 到 2 以创建每个过滤器分辨率的三个过滤器块:
for i in range(2):
head_next_stage, head_to_loss = create_left_half_blocks (front_features, num_classes, num_channels, bottleneck, i)
outputs.append(head_to_loss)
从左到右连接
如果您查看编码沙漏模型部分开头提供的图像,您会注意到左右块由connect_left_to_right块连接。用于将左块连接到右块的代码如下:
def connect_left_to_right(left,right,bottleneck,name,num_channels):
'''
:param left:将左侧特征连接到右侧特征
:param name:图层名称
:return:
'''
_xleft =bottleneck(left,num_channels,name +' _connect')
_xright = UpSampling2D()(right)
add = Add()([_xleft, _xright])
out =bottleneck(add, num_channels, name + '_connect_conv')
return
请注意,每个右块是通过上采样生成的,并添加到左块以生成最终输出。在前面的代码中,_xleft显示了左块,_xright显示了右块,add函数将两者相加。
Right half-block
右块代码如下:
def create_right_half_blocks(leftfeatures, bottleneck, hglayer, num_channels):
lf1, lf2, lf4, lf8 = leftfeatures
rf8 = bottom_layer(lf8, bottleneck, hglayer, num_channels)
rf4 = connect_left_to_right(lf4, rf8, bottleneck, 'hg' + str(hglayer) + '_rf4', num_channels)
rf2 = connect_left_to_right(lf2, rf4, bottleneck, 'hg' + str(hglayer) + '_rf2', num_channels)
rf1 = connect_left_to_right(lf1, rf2, bottleneck, 'hg' + str(hglayer) + '_rf1', num_channels)
return rf1
在前面的代码中,lf8, lf4, lf2, and lf1已经留下了特征。对应的右块特征—— 是通过将左到右瓶颈块应用于每个左特征而生成的rf8, rf4, rf2, and rf1 。以下代码通过对每个左侧范围迭代 0 到 2 来应用此逻辑:
for i in range(2):
head_next_stage, head_to_loss = create_right_half_blocks (front_features, num_classes, num_channels, bottleneck, i)
outputs.append(head_to_loss)
Head block
头块代码如下:
def create_heads(prelayerfeatures, rf1, num_classes, hgid, num_channels):
# 两个头,一个头到下一阶段,一个头到中间特征
head = Conv2D(num_channels, kernel_size=(1, 1), activation='relu', padding ='same', name=str(hgid) + '_conv_1x1_x1')(rf1)
head = BatchNormalization()(head)
# 对于 head 作为中间监督,使用 'linear' 作为激活。
head_parts = Conv2D(num_classes, kernel_size=(1, 1), activation='linear', padding='same',name=str(hgid) + '_conv_1x1_parts')(head)
# 使用线性激活
head = Conv2D(num_channels, kernel_size=(1, 1), activation='linear', padding='same',name=str(hgid) + '_conv_1x1_x2')(head)
head_m = Conv2D(num_channels, kernel_size=(1, 1), activation='linear', padding='same',name=str(hgid) + '_conv_1x1_x3')(head_parts)
head_next_stage = Add()([head, head_m , prelayerfeatures])
return head_next_stage, head_parts
头部有两个主要块,每个块都包含一个 1 x 1 Conv2D 过滤器。它正在使用激活层和填充。作为复习,请参考人体姿态估计-堆叠沙漏模型部分下显示的沙漏架构图,以了解以下组件之间的连接:
- 前往下一阶段
- 前往中间特征
以下逻辑将 head 块应用于从 0 到 2 的每个范围,它们分别对应于 left 和 right 块:
for i in range(2):
head_next_stage, head_to_loss = create_head_blocks (front_features, num_classes, num_channels, bottleneck, i)
outputs.append(head_to_loss)
Hourglass 训练
沙漏网络在包含 5,000 张图像(4,000 张用于训练和 1,000 张用于测试)的 FLIC 人体姿势数据集和具有 40,000 张图像(28,000 用于训练和 12,000 用于测试)的 MPII 人体姿势数据集上进行训练。
在大约 20,000 次训练迭代中,所有关节的平均准确率达到约 70%,最大准确率约为 80%。
到目前为止,我们已经讨论了 OpenPose 和姿势估计的堆叠沙漏方法。在下一节中,我们将讨论 PoseNet。
人体姿态估计——PoseNet
TensorFlow 发布了 PoseNet 模型,该模型用于使用浏览器检测人体姿势。它可以用于单个姿势和多个姿势。
PoseNet 基于谷歌的两篇论文。一种使用自上而下的方法,而另一种使用自下而上的方法。
自上而下的方法
第一篇论文的标题是在野外准确的多人姿势估计,由 George Papandreou、Tyler Zhu、Nori Kanazawa、Alexander Toshev、Jonathan Tompson、Chris Bregler 和 Kevin Murphy 撰写。你可以在https://arxiv.org/abs/1701.01779找到这篇论文。
这是一个两阶段的自上而下的方法:
- 边界框坐标 ( x,y,w,h ) 使用带有 ResNet-101 网络主干的 Faster R-CNN 确定。Faster R-CNN 和 ResNet 在第 5 章“神经网络架构和模型”中介绍,但在第 10 章“使用 R-CNN、SSD 和 R-FCN 进行对象检测”中将介绍在实际实现中一起使用它们。这种分类只针对一个类别——人类。所有返回的边界框都经过调整,使其具有固定的纵横比,然后裁剪为 353 x 257 大小。
- 使用 ResNet-101 估计位于每个边界框中的人/人的 17 个关键点,用 3 x 17 输出替换最后一层。已使用组合的分类和回归方法来找到人体每个位置与 17 个关键点位置中的每个位置之间的偏移向量或距离。每 17 个关键点计算距离小于半径的概率,得到 17 个热图。具有 17 个热图的 ResNet-101 模型使用 Sigmoid 激活函数进行训练。
自下而上的方法
第二篇论文的标题是PersonLab: Person Pose Estimation and Instance Segmentation with a Bottom-Up, Part-Based, Geometric Embedding Model,由第一篇论文的许多作者共同撰写;即 George Papandreou、Tyler Zhu、Liang-Chieh Chen、Spyros Gidaris、Jonathan Tompson 和 Kevin Murphy。你可以在https://arxiv.org/abs/1803.08225找到这篇论文。
在这种无框、自下而上的方法中,作者使用卷积神经网络来检测单个关键点及其相对位移,从而将关键点分组为人物姿势实例。此外,设计了一个几何嵌入描述符来确定人物分割。该模型使用 ResNet-101 和 ResNet-152 架构进行训练。
与自上而下的方法一样,定义了一个 32 像素大小的半径,对应于 17 个关键点中的每一个。然后,如果图像中的空间位置在关键点位置的半径范围内,则定义 17 个独立的二元分类任务,热图概率为 1;否则,将其设置为 0。与自上而下的方法一样,图像位置与关键点之间的距离称为短距离偏移向量。因此,有 17 个这样的偏移向量。与自上而下的方法一样,热图和偏移向量使用二维霍夫分数图组合在一起。
在这种方法中,我们将某人与关键点相关联,但是当图像中存在多个人实例时,这不允许我们对每个人的关键点进行分组。为了解决这个问题,开发了 32 个独立的中程二维偏移场来连接关键点对。除此之外,还开发了一个由单个 1 x 1 Conv2D 层组成的简单语义分割模型,该模型执行密集逻辑回归并计算每个图像像素属于至少一个人的概率。语义分割的细节在第 8 章,语义分割和神经风格迁移中进行了描述。
有关预测图像,请参阅以下关于自上而下与自下而上的论文:https ://arxiv.org/abs/1701.01779和https://arxiv.org/abs/1803.08225。两篇论文都包含大量示例图像。
两种方法对关键点的预测大致相同,但自上而下的方法从绘制边界框开始,而自下而上的方法执行语义分割。
PoseNet 实现
到目前为止,我们已经讨论了 PoseNet 自上而下和自下而上方法背后的理论。在本节中,我们将使用 PoseNet 来识别这些动作。如何实现 PoseNet 模型的详细信息可以在tfjs-models/posenet at master · tensorflow/tfjs-models · GitHub找到。查看此链接以了解 PoseNet 的文档。
接下来,我们将进行现场演示。这个使用网络摄像头完成的现场演示可以通过在您的网络浏览器中键入以下链接开始:https ://storage.googleapis.com/tfjs-models/demos/posenet/camera.html 。
尽管自顶向下和自底向上的方法使用 ResNet-101 模型,但 PoseNet 模型使用 MobileNetV1 或 ResNet-50。它们之间的区别如下表所示:
MobileNet V1 | ResNet 50 | |
Stride | 16 | 32 |
Input resolution | Width: 640, height: 480 | Width: 257, height: 200 |
PoseNet 网站说明了如何调整模型参数。可以使用以下屏幕截图中显示的参数窗口调整模型参数:
在前面的截图中,我们可以通过改变输入图像的分辨率来演示这两种模型——这似乎效果最好。
下图比较了八种不同配置的 PoseNet 输出(MobileNetV1 和 ResNet 的 200 和 500 分辨率的上下姿势):
上图说明,平均而言,当人的手悬空时,ResNet 比 MobileNetV1 更准确。当手位向下时,表现或多或少是一样的。此外,与 500 分辨率相比,200 分辨率可以更好地预测关键点。边界框选项可用但未显示。下图显示了用于其他配置的 ResNet 的边界框:
请注意边界框的大小和位置如何随不同方向变化。关键点存储在向量中。生成的关键点之间的角度可用于预测动作。上图由三个不同的动作组成——侧向运动、向上和向下。这些动作的关键点角度不重叠,因此预测将是可靠的。
应用人体姿势进行手势识别
到目前为止,我们已经学会了如何使用给定的关键点进行训练以生成人体姿势。手势识别的过程类似。按照以下步骤对手部运动执行手势识别:
- 收集不同手部位置的图像——上、下、左和右。
- 调整图像大小。
- 此时,您可以使用关键点标记图像。如果您为关键点关节标记图像,则必须用相应的关键点关节表示每个图像。
- 将图像及其对应的标签加载到两个不同的数组中。
- 下一步是执行图像分类,类似于我们在第 6 章中所做的,使用迁移学习进行视觉搜索。
- CNN 模型最多可以包含三个 Conv2D 层、一个最大池化层和一个 ReLU 层。
- 对于关键点估计,我们将使用每个关键帧手势位置之间的距离,而不是分类,并选择距离最小的手势。
到目前为止,我们已经学会了如何开发用于训练的二维神经网络。我们开发的网络也可以用于生产。
使用各种方法的动作识别
加速度计测量加速度的x、y和z分量,如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/f9534a93e363995ca6c2a853da03b30e.png)
加速度计的这一特性使其能够放置在可穿戴设备中,例如通过腕带安装在人手腕上的手机、智能手表,甚至鞋子中,以测量加速度的 XYZ 分量。在本节中,我们将学习如何使用神经网络分析加速度计数据以识别人类活动。我们将使用 TensorFlow 开发机器学习模型。这是本书中唯一讨论如何在没有图像的情况下使用原始数据以及如何将其传递给神经网络以开发模型并从中得出推论的部分。
人类活动识别涉及基于加速度计数据对不同类型的活动进行分类。这里的挑战是关联不同类型的身体运动产生的加速度计数据,并根据不同的身体运动和活动区分相似的加速度计轨迹。例如,当安装到人的腰部时,左手运动和右手运动可能会产生相似的加速度计数据。这减轻了加速度计数据应与姿势估计或视频图像数据相结合的事实。在本节中,我们将讨论可用于人类活动识别的两种不同工具。
基于accelerometer识别动作
该方法包括以下步骤:
- 处理输入加速度计数据:加速度计数据对其所在位置敏感。例如,如果安装在腰部,与安装在手臂上时相比,加速度计的手部动作不会有太大变化。此外,对于不同的职位,需要收集不同的数据,然后进行组合。
- 准备数据以便将其输入到 TensorFlow 中:加载数据tf.data.Dataset以开发简单、高效的数据管道。该tensor_slices命令从输入中提取一段数据。
- 开发 CNN 模型并对其进行训练:一到两个密集层,最后带有 flatten 和 softmax 函数。
- 对照测试数据检查:对照测试数据验证数据。
有关遵循这些步骤的代码示例,请参阅以下 GitHub 页面上的代码:https://github.com/PacktPublishing/Mastering-Computer-Vision-with-TensorFlow-2.0/blob/master/Chapter09/Chapter9_TF_Accelerometer_activity。 ipynb。
在上一个链接中可以找到两个文件:Chapter9_TF_Accelerometer_activity.ipynb和sample.csv. 下载这两个文件并将它们放在同一个文件夹下。
接下来,根据索引文件将数据分为两部分:训练和测试。在这里,我们将评估两个不同的拆分,18 和 28,这意味着在一种情况下,如果索引文件小于 18,则数据属于 train 文件夹;否则,它属于测试文件夹。该模型加载了三个密集(全连接)层,分辨率为 128。最终的 softmax 层被 Sigmoid 函数替换。下图显示了模型在三种不同场景下的迭代:
- 训练与测试在索引 18 处分割的 Softmax
- 用于训练与测试的 Sigmoid 函数在索引 18 处分割
- 训练与测试在索引 28 处分割的 Softmax:
![](https://i-blog.csdnimg.cn/blog_migrate/26f6092d2a31171b3caddc94f45ae716.png)
前面的数据显示,每次迭代大约需要 40 秒,最终准确率在 0.97 左右。以下图表以图形方式说明了这一点:
前面的图表明,对于所研究的三个条件,训练准确度或多或少是相同的。为了进一步分析这一点,让我们看一下下图中显示的置信度图:
混淆矩阵决定了测试数据与预测数据的对比情况。在这里,我们可以看到,在索引 18 处进行训练拆分的 Softmax 函数比其他站立和步行案例提供了更好的结果。正如预期的那样,Softmax 与 Sigmoid 激活函数没有产生任何显着差异。一旦开发了模型,模型的预测函数就可以用于预测真实测试情况的数据。
将基于视频的动作与姿势估计相结合
动作识别可以是二维的,也可以是三维的。二维动作识别方法使用身体的关节信息,以关键点表示。这些关键点用称为特征图的向量表示。另一方面,三维动作识别方法不仅需要特征图,还需要全身骨骼数据。可以使用深度传感器(例如 Microsoft Kinect 或 Intel RealSense)获取此数据。2018 年,Diogo C. Luvizon、David Picard 和 Hedi Tabia 介绍了他们的论文,题为2D/3D Pose Estimation and Action Recognition using Multitask Deep Learning。本文的详细信息可以在https://arxiv.org/abs/1802.09232找到。
在本文中,作者将基于身体关节的高级姿势信息与低级视觉特征(来自对象识别和特征识别)集成在一个多任务框架中。该方法能够进行二维和三维动作识别。使用体积表示将二维姿态图扩展到三维图。
这些技术的结合有助于使动作识别对类似的身体关节运动更加稳健,例如喝水和打电话。
使用 4D 方法的动作识别
4D动作识别意味着体积表示的三维动作作为时间的函数工作。将其视为对动作进行音量跟踪。Quanzeng You 和 Hao Jiang 提出了一种新颖的 4D 方法,名为Action4D: Online Action Recognition in the Crowd and Clutter。本文的详细信息可以在http://openaccess.thecvf.com/content_CVPR_2019/html/You_Action4D_Online_Action_Recognition_in_the_Crowd_and_Clutter_CVPR_2019_paper.html找到。
该方法使用 4D 表示跟踪人类,并在杂乱和拥挤的环境中识别他们的行为。论文的概念如下:
- 使用多个 RGBD 图像为每个场景创建一个 3D 点云。
- 拥挤场景中的检测得到了不使用背景减法的创新跟踪方案的补充,这意味着在拥挤空间中更不容易出错。
- 跟踪过程通过训练三维CNN(具有三维卷积、ReLU 和池化层)来使用人员候选提议,将每个候选卷分类为人与非人。
- 使用一系列三维体积来识别动作,这些体积经过一系列三维卷积和池化层,称为 Action4D。
概括
在本章中,我们学习并实现了三种不同的姿态估计方法——OpenPose、堆叠沙漏和 PostNet。我们学习了如何使用 OpenCV 和 TensorFlow 预测人类关键点。然后,我们了解了堆叠沙漏法的详细理论和TensorFlow实现。我们向您展示了如何在浏览器中评估人体姿势并使用网络摄像头实时估计关键点。然后将人体姿势估计与动作识别模型相关联,以演示如何使用两者来提高准确性。基于加速的代码展示了如何使用 TensorFlow 2.0 来加载数据、训练模型和预测动作。
在下一章中,我们将学习如何实现 R-CNN,并将其与 ResNet、Inception 和 SSD 等其他 CNN 模型相结合,以提高目标检测的预测、准确性和速度。