🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
🖍foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟👋
文章目录
动作识别是计算机视觉的关键部分,涉及识别人的手、腿、头部和身体位置,以检测特定动作并将其分类为众所周知的类别。困难在于视觉输入的变化(例如身体凌乱或被衣服覆盖),类似的动作但不同的类别,例如喝水或使用手持手机交谈,以及获得有代表性的训练数据。
本章详细概述了我们可用于人体姿态估计和动作识别的关键方法。动作识别结合了姿态估计方法和基于加速度的活动识别,以及基于视频和三维点云的动作识别。该理论将通过使用 TensorFlow 2.0 对其实现的解释来补充。
本章分为四个部分。前三个讨论了我们可以用于人体姿态估计的三种不同方法,而第四个则是关于动作识别:
人体姿态估计是深度神经网络取得显著成功的另一个领域,近年来发展迅速。在最后几章中,我们了解到深度神经网络使用线性(卷积)和非线性(ReLU)操作的组合来预测一组给定输入图像的输出。在姿态估计的情况下,当给定一组输入图像时,深度神经网络会预测关节位置。图像中的标记数据集由一个边界框组成,该边界框确定图像中的N个人和K每人关节。随着姿势的变化,关节的方向也会发生变化,因此通过查看关节的相对位置来表征不同的位置。在接下来的部分中,我们将描述我们可以使用的不同姿势估计方法。
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找到这篇论文。
本文的主要发现如下:
2019 年,OpenPose 的作者和其他一些人(Gines Hidalgo、Yaadhav Raaj、Haroon Idrees、Donglai Xiang、Hanbyul Joo、Tomas Simon1 和 Yaser Sheikh)在一篇名为Single -网络全身姿势估计。您可以在https://arxiv.org/abs/1909.13423找到这篇论文。
主要特点如下:
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由 Alejandro Newell、Kaiyu Yang 和 Jia Deng 在他们的论文Stacked Hourglass Networks for Human Pose Estimation中于 2016 年开发。该模型的详细信息可以在https://arxiv.org/abs/1603.06937找到。
该模型的架构如下图所示:
该模型的主要特点如下:
沙漏模型在 MPII 人体姿势数据集中的所有关节上实现了最先进的结果,但这是以资源密集型网络带宽使用为代价的。这是由于每层的通道数较多导致训练困难。FastPose蒸馏( FPD ) 在 CVPR 2019 中由 Feng Zhang、Xiatian Zhu 和 Mao Ye 在他们题为Fast Human Pose Estimation的论文中介绍。与沙漏模型相比,FPD 可实现更快且更具成本效益的模型推理,同时达到相同的模型性能。主要特点如下:
这里,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找到
在下一节中,我们将描述模型的架构并详细解释它。
沙漏模型的编码块如下图所示:
让我们花点时间来理解前面的图表,因为我们将在下一节中对其进行编码:
让我们看一下不同的代码块。
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)
沙漏网络已经被描述过了。在本节中,我们将解释训练网络背后的代码。
训练沙漏网络的代码如下:
- 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. 如前所述,代码具有以下组件。
以下代码描述了前端模块:
- 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)。接下来的几行贯穿批标准化、瓶颈和最大池化层。让我们定义瓶颈块。
左半块代码如下:
- 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)
上述代码执行两个特定操作:
接下来,以下代码迭代 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函数将两者相加。
右块代码如下:
- 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)
头块代码如下:
- 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)
沙漏网络在包含 5,000 张图像(4,000 张用于训练和 1,000 张用于测试)的 FLIC 人体姿势数据集和具有 40,000 张图像(28,000 用于训练和 12,000 用于测试)的 MPII 人体姿势数据集上进行训练。
在大约 20,000 次训练迭代中,所有关节的平均准确率达到约 70%,最大准确率约为 80%。
到目前为止,我们已经讨论了 OpenPose 和姿势估计的堆叠沙漏方法。在下一节中,我们将讨论 PoseNet。
TensorFlow 发布了 PoseNet 模型,该模型用于使用浏览器检测人体姿势。它可以用于单个姿势和多个姿势。
PoseNet 基于谷歌的两篇论文。一种使用自上而下的方法,而另一种使用自下而上的方法。
第一篇论文的标题是在野外准确的多人姿势估计,由 George Papandreou、Tyler Zhu、Nori Kanazawa、Alexander Toshev、Jonathan Tompson、Chris Bregler 和 Kevin Murphy 撰写。你可以在https://arxiv.org/abs/1701.01779找到这篇论文。
这是一个两阶段的自上而下的方法:
第二篇论文的标题是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 模型的详细信息可以在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 的边界框:
请注意边界框的大小和位置如何随不同方向变化。关键点存储在向量中。生成的关键点之间的角度可用于预测动作。上图由三个不同的动作组成——侧向运动、向上和向下。这些动作的关键点角度不重叠,因此预测将是可靠的。
到目前为止,我们已经学会了如何使用给定的关键点进行训练以生成人体姿势。手势识别的过程类似。按照以下步骤对手部运动执行手势识别:
到目前为止,我们已经学会了如何开发用于训练的二维神经网络。我们开发的网络也可以用于生产。
加速度计测量加速度的x、y和z分量,如下图所示:
加速度计的这一特性使其能够放置在可穿戴设备中,例如通过腕带安装在人手腕上的手机、智能手表,甚至鞋子中,以测量加速度的 XYZ 分量。在本节中,我们将学习如何使用神经网络分析加速度计数据以识别人类活动。我们将使用 TensorFlow 开发机器学习模型。这是本书中唯一讨论如何在没有图像的情况下使用原始数据以及如何将其传递给神经网络以开发模型并从中得出推论的部分。
人类活动识别涉及基于加速度计数据对不同类型的活动进行分类。这里的挑战是关联不同类型的身体运动产生的加速度计数据,并根据不同的身体运动和活动区分相似的加速度计轨迹。例如,当安装到人的腰部时,左手运动和右手运动可能会产生相似的加速度计数据。这减轻了加速度计数据应与姿势估计或视频图像数据相结合的事实。在本节中,我们将讨论可用于人类活动识别的两种不同工具。
该方法包括以下步骤:
有关遵循这些步骤的代码示例,请参阅以下 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 函数替换。下图显示了模型在三种不同场景下的迭代:
前面的数据显示,每次迭代大约需要 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动作识别意味着体积表示的三维动作作为时间的函数工作。将其视为对动作进行音量跟踪。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 表示跟踪人类,并在杂乱和拥挤的环境中识别他们的行为。论文的概念如下:
在本章中,我们学习并实现了三种不同的姿态估计方法——OpenPose、堆叠沙漏和 PostNet。我们学习了如何使用 OpenCV 和 TensorFlow 预测人类关键点。然后,我们了解了堆叠沙漏法的详细理论和TensorFlow实现。我们向您展示了如何在浏览器中评估人体姿势并使用网络摄像头实时估计关键点。然后将人体姿势估计与动作识别模型相关联,以演示如何使用两者来提高准确性。基于加速的代码展示了如何使用 TensorFlow 2.0 来加载数据、训练模型和预测动作。
在下一章中,我们将学习如何实现 R-CNN,并将其与 ResNet、Inception 和 SSD 等其他 CNN 模型相结合,以提高目标检测的预测、准确性和速度。