单人目标检测

前言

近期闲来无事,于是将一些复现的实验做一些总结,本篇是系列第一篇,希望自己能坚持做自己不想做但却是正确的事,加油!
本篇文章主要介绍如何让电脑认识自己,具体实现代码与运行步骤请参考Github.


准备工作

1)首先安装环境。本实验只在Ubuntu14以上64位版本实现过(原本win10的可以装双系统),也可自行在windows系统下自行调试;

2)安装cudn+cudnn,本人配置:cudn8.0+cudnn5.1,csdn上教程很多,可参考Ubuntu 16.04安装CUDA8.0 + cuDNN5.1 + OpenCV3.1.0

3)安装Tensorflow,如果是独显,且支持CUDA,请安装GPU版本,否则选择CPU版本。

4)安装opencv2,请安装openCV3.0以上版本(该版本支持python3和python2),参考如下链接:

https://blog.csdn.net/weixin_42287851/article/details/80419646

5)安装keras、sklearn、PIP等,它们的安装说明网上有的是,也简单,这里就不多说了;

提示:当运行程序过程中如果提示某个模块无法导入,根据提示安装该模块。

获取并显示USB摄像头实时视频

从实时视频流中识别出人脸区域,从原理上看,其依然属于机器学习的领域之一,本质上与谷歌利用深度学习识别出猫没有什么区别。程序通过大量的人脸图片数据进行训练,利用数学算法建立建立可靠的人脸特征模型,如此即可识别出人脸。幸运的是,这些工作OpenCV已经帮我们做了,我们只需调用对应的API函数即可。
视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头,使用人脸识别分类器haarcascade_frontalface_alt2.xml,识别出人脸后要画的边框的颜色,RGB格式,读取一帧数据,将当前帧转换成灰度图像,检测到人脸时单独框出每一张人脸并显示。
对同一个画面有可能出现多张人脸,因此,我们需要用一个for循环将所有检测到的人脸都读取出来,然后逐个用矩形框框出来,这就是接下来的for语句的作用。Opencv会给出每张人脸在图像中的起始坐标(左上角,x、y)以及长、宽(h、w),我们据此就可以截取出人脸。其中,cv2.rectangle()完成画框的工作,在这里我有意识的外扩了10个像素以框出比人脸稍大一点的区域。cv2.rectangle()函数的最后两个参数一个用于指定矩形边框的颜色,一个用于指定矩形边框线条的粗细程度。

训练数据

在前面代码的基础上增加脸部图像存储功能。程序能够指定要截取的人脸数量,由cv2.imwrite()函数完成实际的保存,到达指定数量程序会自动退出。同时,在图像上提供了信息输出功能,以便我们能随时知道已经截取了多少张人脸,当然前提是你在一定距离之外还能看清楚屏幕。整个流程还是比较简单的,不多说了。我们需要利用这个程序准备至少1000张自己的人脸图片,将其单独放到一个文件夹下。我将它们放到了与程序同路径的”data/me“文件夹下。然后我们还需要截取至少另外一个人的图片以便训练程序分类使用,以提高模型准确度。我截取的我同学的,将其存储到了data/other文件夹下,同样也是1000张。注意一定要确保每个文件夹下的所有图片都是同一个人的(对于我来说data/me是我的,data/other全部是同学的),接下来的训练程序将以文件夹作为标签数据区分个人。
前面已经说过,OpenCV对人脸的识别也不是100%准确,因此,我们截取的人脸图像中会有些不合格的,比如误把灯笼当人脸存下来了或者人脸图像很模糊。在我截取的1000张人脸中大约有几十张这样的,要想确保模型可靠,必须要把这样的图片去掉。这个活只能手动了,没办法。幸运的是,数据量不大,不会耽误太多时间的。

利用keras库训练人脸识别模型

前面我们已经准备好了2000张脸部图像,但没有进行标注,并且还需要将数据加载到内存,以方便输入给CNN。因此,第一步工作就是加载并标注数据到内存。按照指定图像大小调整尺寸:获取图像尺寸、对于长宽不相等的图片,找到最长的一边、计算短边需要增加多上像素宽度使其与长边等长、给图像增加边界,是图片长、宽等长,cv2.BORDER_CONSTANT指定边界颜色由value指定、调整图像大小并返回。从指定路径读取训练数据,将输入的所有图片转成四维数组,尺寸为(图片数量IMAGE_SIZE*IMAGE_SIZE*3),图片为64 64像素,一个像素3个颜色值(RGB),标注数据,’me’文件夹下都是我的脸部图像,全部指定为0,另外一个文件夹下是同学的,全部指定为1。
上面给出的代码标注主函数就是load_dataset(),它将图片数据进行标注并以多维数组的形式加载到内存中。我实际用于训练的脸部数据共1000张,我去掉了一些模糊的或者表情基本一致的头像,留下了清晰、脸部表情有些区别的,我跟同学各留了500张,所以训练数据变成了1000。上述代码注释很清楚,不多讲,唯一一个理解起来稍微有点难度的就是resize_image()函数。这个函数其实就做了一件事情,判断图片是不是四边等长,也就是图片是不是正方形。如果不是,则短的那两边增加两条黑色的边框,使图像变成正方形,这样再调用cv2.resize()函数就可以实现等比例缩放了。因为我们指定缩放的比例就是64 x 64,只有缩放之前图像为正方形才能确保图像不失真。
这些工作做完之后,我们就可以开始构建训练代码了。先把需要的库文件添加到代码中,数据加载的工作已经完成,我们只需调用这个接口即可。关于训练集的使用,我们需要拿出一部分用于训练网络,建立识别模型;另一部分用于验证模型。同时我们还有一些其它的比如数据归一化等预处理的工作要做,因此,我们把这些工作封装成一个dataset类来完成。我们构建了一个Dataset类,用于数据加载及预处理。其中,init()为类的初始化函数,load()则完成实际的数据加载及预处理工作。加载前面已经说过很多了,就不多说了。关于预处理,我们做了几项工作:

1)按照交叉验证的原则将数据集划分成三部分:训练集、验证集、测试集;

2)按照keras库运行的后端系统要求改变图像数据的维度顺序;

3)将数据标签进行one-hot编码,使其向量化

4)归一化图像数据
关于第一项工作,先简单说说什么是交叉验证?交叉验证属于机器学习中常用的精度测试方法,它的目的是提升模型的可靠和稳定性。我们会拿出大部分数据用于模型训练,小部分数据用于对训练后的模型验证,验证结果会与验证集真实值(即标签值)比较并计算出差平方和,此项工作重复进行,直至所有验证结果与真实值相同,交叉验证结束,模型交付使用。在这里我们导入了sklearn库的交叉验证模块,利用函数train_test_split()来划分训练集和验证集,具体语句如下:

train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size = 0.2,                                                                                  random_state = random.randint(0, 100))

train_test_split()会根据test_size参数按比例划分数据集(不要被test_size的外表所迷惑,它只是用来指定数据集划分比例的,本质上与测试无关,划分完了你爱咋用就咋用),在这里我们划分出了30%的数据用于验证,70%用于训练模型。参数random_state用于指定一个随机数种子,从全部数据中随机选取数据建立训练集和验证集,所以你将会看到每次训练的结果都会稍有不同。当然,为了省事,测试集也调用了这个函数

_, test_images, _, test_labels = train_test_split(images, labels, test_size = 0.5,                                             random_state = random.randint(0, 100))

在这里,测试集我选择的比例为0.5,所以前面的“, test_images, , test_labels”语句你调个顺序也成,即“test_images, , test_labels, ”,但是如果你改成其它数值,就必须严格按照代码给出的顺序才能得到你想要的结果。train_test_split()函数会按照训练集特征数据(这里就是图像数据)、测试集特征数据、训练集标签、测试集标签的顺序返回各数据集。所以,看你的选择了。
关于第二项工作,我们前面不止一次说过keras建立在tensorflow或theano基础上,换句话说,keras的后端系统可以是tensorflow也可以是theano。后端系统决定了图像数据输入CNN网络时的维度顺序,tensorflow的维度顺序为行数(rows)、列数(cols)、通道数(颜色通道,channels);theano则是通道数、行数、列数。所以,我们通过调用image_dim_ordering()函数来确定后端系统的类型(‘th’代表theano,’tf’代表tensorflow),然后我们再通过numpy提供的reshape()函数重新调整数组维度。
关于第三项工作,对标签集进行one-hot编码的原因是我们的训练模型采用categorical_crossentropy作为损失函数(多分类问题的常用函数,后面会详解),这个函数要求标签集必须采用one-hot编码形式。所以,我们对训练集、验证集和测试集标签均做了编码转换。那么什么是one-hot编码呢?one-hot有的翻译成独热,有的翻译成一位有效,个人感觉一位有效更直白一些。因为one-hot编码采用状态寄存器的组织方式对状态进行编码,每个状态值对应一个寄存器位,且任意时刻,只有一位有效。对于我们的程序来说,我们类别状态只有两种(nb_classes = 2):0和1,0代表我,1代表闺女。one-hot编码会提供两个寄存器位保存这两个状态,如果标签值为0,则编码后值为[1 0],代表第一位有效;如果为1,则编码后值为[0 1],代表第2为有效。换句话说,one-hot编码将数值变成了位置信息,使其向量化,这样更方便CNN操作。
关于第四项工作,数据集先浮点后归一化的目的是提升网络收敛速度,减少训练时间,同时适应值域在(0,1)之间的激活函数,增大区分度。其实归一化有一个特别重要的原因是确保特征值权重一致。举个例子,我们使用mse这样的均方误差函数时,大的特征数值比如(5000-1000)2与小的特征值(3-1)2相加再求平均得到的误差值,显然大值对误差值的影响最大,但大部分情况下,特征值的权重应该是一样的,只是因为单位不同才导致数值相差甚大。因此,我们提前对特征数据做归一化处理,以解决此类问题。关于归一化的详细介绍有兴趣的请参考如下链接:

http://www.thinksaas.cn/topics/0/491/491257.html

数据准备工作到此完成,接下来就要进入整个系列最关键的一个节点——建立我们自己的卷积神经网络模型,激动吧;)?与数据集加载及预处理模块一样,我们依然将模型构建成一个类来使用,新建的这个模型类添加在Dataset类的下面。

#建立模型
def build_model(self, dataset, nb_classes = 2):
    #构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
    self.model = Sequential() 

    #以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
    self.model.add(Convolution2D(32, 3, 3, border_mode='same', 
                                 input_shape = dataset.input_shape))    #1 2维卷积层
    self.model.add(Activation('relu'))                                  #2 激活函数层

    self.model.add(Convolution2D(32, 3, 3))                             #3 2维卷积层                             
    self.model.add(Activation('relu'))                                  #4 激活函数层

    self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #5 池化层
    self.model.add(Dropout(0.25))                                       #6 Dropout层

    self.model.add(Convolution2D(64, 3, 3, border_mode='same'))         #7  2维卷积层
    self.model.add(Activation('relu'))                                  #8  激活函数层

    self.model.add(Convolution2D(64, 3, 3))                             #9  2维卷积层
    self.model.add(Activation('relu'))                                  #10 激活函数层

    self.model.add(MaxPooling2D(pool_size=(2, 2)))                      #11 池化层
    self.model.add(Dropout(0.25))                                       #12 Dropout层

    self.model.add(Flatten())                                           #13 Flatten层
    self.model.add(Dense(512))                                          #14 Dense层,又被称作全连接层
    self.model.add(Activation('relu'))                                  #15 激活函数层   
    self.model.add(Dropout(0.5))                                        #16 Dropout层
    self.model.add(Dense(nb_classes))                                   #17 Dense层
    self.model.add(Activation('softmax'))                               #18 分类层,输出最终结果

    #输出模型概况
    self.model.summary() ```

模型介绍

我们通过调用self.model.summary()函数将网络模型基本结构信息展示在我们面前,包括层类型、维度、参数个数、层连接等信息,一目了然,简洁、清晰。通过上图我们可以看出,这个网络模型共18层,包括4个卷积层、5个激活函数层、2个池化层(pooling layer)、3个Dropout层、2个全连接层、1个Flatten层、1个分类层,训练参数为6,489,634个。
卷积层(convolution layer):这里重点讲讲Convolution2D()函数。根据keras官方文档描述,2D代表这是一个2维卷积,其功能为对2维输入进行滑窗卷积计算。我们的脸部图像尺寸为64*64,拥有长、宽两维,所以在这里我们使用2维卷积函数计算卷积。所谓的滑窗计算,其实就是利用卷积核逐个像素、顺序进行计算,整个操作过程就像一个滑动的窗口逐个滑过所有像素,最终生成一副尺寸相同但已经过卷积处理的图像,均值卷积核的实际效果就是将图像变模糊了。显然,卷积核覆盖图像边界像素时,会有部分区域越界,越界的部分我们以0填充,如上图。对于此种情况,还有一种处理方法,就是丢掉边界像素,从覆盖区域不越界的像素开始计算。在我们建立的模型中,卷积层采用哪种方式处理图像边界,卷积核尺寸有多大等参数都可以通过Convolution2D()函数来指定:
第一个卷积层包含32个卷积核,每个卷积核大小为3x3,border_mode值为“same”意味着我们采用保留边界特征的方式滑窗,而值“valid”则指定丢掉边界像素。根据keras开发文档的说明,当我们将卷积层作为网络的第一层时,我们还应指定input_shape参数,显式地告知输入数据的形状,对我们的程序来说,input_shape的值为(64,64,3),来自Dataset类,代表64x64的彩色RGB图像。
激活函数层:它的作用前面已经说了,这里讲一下代码中采用的relu(Rectified Linear Units,修正线性单元)函数,它的数学形式如下:

                      ƒ(x) = max(0, x)

这个函数非常简单,其输出一目了然,小于0的输入,输出全部为0,大于0的则输入与输出相等。该函数的优点是收敛速度快,除了它,keras库还支持其它几种激活函数,如下:
1.softplus
2.softsign
3.tanh
4.sigmoid
5.hard_sigmoid
6.linear
它们的函数式、优缺点度娘会告诉你,不多说。对于不同的需求,我们可以选择不同的激活函数,这也是模型训练可调整的一部分,运用之妙,存乎一心,请自忖之。另外再交代一句,其实激活函数层按照我们前文所讲,其属于人工神经元的一部分,所以我们亦可以在构造层对象时通过传递activation参数设置

池化层(pooling layer):池化层存在的目的是缩小输入的特征图,简化网络计算复杂度;同时进行特征压缩,突出主要特征。我们通过调用MaxPooling2D()函数建立了池化层,这个函数采用了最大值池化法,这个方法选取覆盖区域的最大值作为区域主要特征组成新的缩小后的特征图.
显然,池化层与卷积层覆盖区域的方法不同,前者按照池化尺寸逐块覆盖特征图,卷积层则是逐个像素滑动覆盖。对于我们输入的64x64的脸部特征图来说,经过2x2池化后,图像变为32x32大小。

Dropout层:随机断开一定百分比的输入神经元链接,以防止过拟合。那么什么是过拟合呢?一句话解释就是训练数据预测准确率很高,测试数据预测准确率很低,用图形表示就是拟合曲线较尖,不平滑。导致这种现象的原因是模型的参数很多,但训练样本太少,导致模型拟合过度。为了解决这个问题,Dropout层将有意识的随机减少模型参数,让模型变得简单,而越简单的模型越不容易产生过拟合。代码中Dropout()函数只有一个输入参数——指定抛弃比率,范围为0~1之间的浮点数,其实就是百分比。这个参数亦是一个可调参数,我们可以根据训练结果调整它以达到更好的模型成熟度。

Flatten层:截止到Flatten层之前,在网络中流动的数据还是多维的(对于我们的程序就是2维的),经过多次的卷积、池化、Dropout之后,到了这里就可以进入全连接层做最后的处理了。全连接层要求输入的数据必须是一维的,因此,我们必须把输入数据“压扁”成一维后才能进入全连接层,Flatten层的作用即在于此。该层的作用如此纯粹,因此反映到代码上我们看到它不需要任何输入参数。

全连接层(dense layer):全连接层的作用就是用于分类或回归,对于我们来说就是分类。keras将全连接层定义为Dense层,其含义就是这里的神经元连接非常“稠密”。我们通过Dense()函数定义全连接层。这个函数的一个必填参数就是神经元个数,其实就是指定该层有多少个输出。在我们的代码中,第一个全连接层(#14 Dense层)指定了512个神经元,也就是保留了512个特征输出到下一层。这个参数可以根据实际训练情况进行调整,依然是没有可参考的调整标准,自调之。

分类层:全连接层最终的目的就是完成我们的分类要求:0或者1,模型构建代码的最后两行完成此项工作:

第17层我们按照实际的分类要求指定神经元个数,对我们来说就是2,18层我们通过softmax函数完成最终分类。

测试

添加测试代码之前,我们需要对训练代码中几个关键函数交代一下。首先是优化器函数,优化器用于训练模型,它的作用就是调整训练参数(权重和偏置值)使其最优,确保e值最小(参见系列4——CNN入门)。keras提供了很多优化器,我们在这里采用的SGD就是其中一种,它就是机器学习领域最著名的随机梯度下降法。函数第一个参数lr用于指定学习效率(lr,Learning Rate,参见系列4),其值为大于0的浮点数。decay指定每次更新后学习效率的衰减值,这个值一定很小(1e-6,0.000 001),否则速率会衰减很快。momentum指定动量值。SGD方法有一个明显的缺点就是,它的下降方向完全依赖当前的训练样本(batch),因此其优化十分不稳定。为了解决这个问题,大牛们引进了动量(momentum),用它来模拟物体运动时的惯性,让优化器在一定程度上保留之前的优化方向,同时利用当前样本微调最终的优化方向,这样即能增加稳定性,提高学习速度,又在一定程度上避免了陷入局部最优陷阱。参数momentum即用于指定在多大程度上保留原有方向,其值为0~1之间的浮点数。一般来说,选择一个在0.5~0.9之间的数即可。代码中SGD函数的最后一个参数nesterov则用于指定是否采用nesterov动量方法,nesterov momentum是对传统动量法的一个改进方法,其效率更高,关于它的详细介绍可参考如下链接:

http://www.360doc.com/content/16/1010/08/36492363_597225745.shtml
对于compile()函数,其作用就是编译模型以完成实际的配置工作,为接下来的模型训练做好准备。换句话说,compile之后模型就可以开始训练了。这个函数有一个很重要的参数:loss,它用于指定一个损失函数。所谓损失函数,通俗地说,它是统计学中衡量损失和错误程度的函数,显然,其值越小,模型就越好。如果你仔细阅读了系列4——CNN入门,那么,你肯定能猜到这个函数其实就是我们的优化对象。代码中loss的值为“categorical_crossentropy”,常用于多分类问题,其与激活函数softmax配对使用(我们的类别只有两种,也可采用‘binary_crossentropy’二值分类函数,该函数与sigmoid配对使用,注意如果采用它就不需要one-hot编码)。参数metrics用于指定模型评价指标,参数值”accuracy“表示用准确率来评价(keras官方文档目前没有查到第2种评价指标,有知道的请告知)。
接着就是数据提升,我们可以选择不提升,也就是采用原始训练集和验证集,这时我们直接调用model.fit()函数即可开始模型训练。该函数shuffle参数用于指定是否随机打乱数据集。一般来说选择数据提升要比不提升好,这样可以让我们利用有限数量的图片获得无限数量的训练图片。因为我们一旦选择数据提升,ImageDataGenerator()函数返回的生成器会在模型训练时无限生成训练数据,直至所有训练轮次(epoch)结束(对我们的代码来说就是840 x 10,生成了8400张图片)。model.fit_generator()函数使用生成器开始模型训练。
在这里需要重点交代一下batch_size和nb_epoch两个参数。nb_epoch指定模型需要训练多少轮次,使用训练集全部样本训练一次为一个训练轮次。根据模型成熟度,我们可以适当调整该值以增加或减少训练次数。batch_size则是一个影响模型训练结果的重要参数。我们知道,一个训练轮次要经过多次迭代训练才能让模型逐渐趋向本轮最优,这是因为理论上每次迭代训练结束后,模型都应该朝着梯度下降的方向前进一步,直至全部样本训练完毕,模型梯度到达本轮最小点。之所以说理论上,是因为决定梯度方向的是训练样本,每次迭代训练选取的样本——其决定的下降方向能否很好的代表样本全体,直接决定了模型能否到达正确的极值点。对于小的训练集,我们完全可以采用全数据集的方式进行训练,因为,全数据集确定的方向肯定能代表正确方向。但这样做对大的训练集就很不现实,因为内存有限,无法一次载入全部数据。于是,批梯度下降法(Mini-batches Learning)应运而生。我们一次选取适当数量的训练样本(视内存大小,可多可少),逐批次迭代,直至本轮全部样本训练完毕。参数batch_size的作用即在于此,其指定每次迭代训练样本的数量。该值的选取非常讲究,不能盲目的增大或减小,因为batch_size太大或太小都会让模型训练效率变慢。显然,batch_size肯定存在一个局部最优值,这需要我们慢慢调试,调试时可从一个小值开始,慢慢加大,直至到达一个合理值(建议编码实现该参数调优)。
现在模型训练的工作已经完成,接下来我们就要考虑模型使用的问题了。要想使用模型,我们必须能够把模型保存下来,因此,我们继续为Model类添加两个函数:

 MODEL_PATH = './me.face.model.h5'
 def save_model(self, file_path = MODEL_PATH):
     self.model.save(file_path)
 def load_model(self, file_path = MODEL_PATH):
     self.model = load_model(file_path)

一个函数用于保存模型,一个函数用于加载模型。keras库利用了压缩效率更高的HDF5保存模型,所以我们用“.h5”作为文件后缀。上述代码添加完毕后,我们接着在文件尾部添加测试代码,把模型训练好并把模型保存下来:

if __name__ == '__main__':
    dataset = Dataset('./data/')    
    dataset.load()

    model = Model()
    model.build_model(dataset)
    model.train(dataset)
    model.save_model(file_path = './model/me.face.model.h5')

从实时视频流识别出自己

首先是为Model类增加一个预测函数,这个函数是提供给外部模块使用的,外部模块用它来预测哪个是“我”,哪个不是“我”。代码很简单,注释也很详细,就不多解释了。
face_predict_use_keras.py代码功能:先加载模型、框住人脸的矩形边框颜色
、捕获指定摄像头的实时视频流、循环检测人脸,通过分类器识别人脸区域,截取脸部图像提交给模型识别,如果是本人就文字提示是谁。
最终的程序能够从USB拍摄的实时视频流中找出哪一个是我。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单人人体姿态估计是计算机视觉领域中的一个重要问题,它旨在从RGB图像或视频中推断出人体的关节点位置和姿态信息。姿态估计在许多应用领域中都具有重要的应用价值,如机器人控制、人体动作分析、运动捕捉等。在过去的几十年中,研究者们提出了许多方法来解决这个问题,其中基于回归和基于检测的方法是两个主要的研究方向。 基于回归的方法是一种直接从图像中回归出关键点位置的方法。这种方法通常将问题转化为回归一个输出向量,其元素表示关键点的坐标值。最初的基于回归的方法是使用手工设计的特征来进行回归,如SIFT、HOG等。然而,这些方法的性能受到了限制,因为它们无法充分捕捉到人体姿态的复杂性和多样性。近年来,随着深度学习技术的发展,基于回归的方法得到了广泛的应用。这些方法通常使用卷积神经网络(CNN)来提取特征,然后通过回归模块来预测关键点的位置。例如,Hourglass网络是一种常用的基于回归的方法,它使用多个Hourglass模块来逐步预测关键点位置。这种方法在关键点定位方面的精度很高,但是在复杂场景下,如人体遮挡、变形、姿势多样性等情况下,它的性能会下降。 基于检测的方法是另一种常用的单人人体姿态估计方法。这种方法先使用目标检测器来检测人体,然后再使用姿态估计算法来对检测到的人体进行姿态估计。这种方法通常具有很好的鲁棒性,因为它可以处理遮挡、姿态多样性等问题。然而,基于检测的方法的主要挑战在于如何设计有效的人体检测器。传统的人体检测器通常是基于手工设计的特征或基于滑动窗口的方法,这些方法的性能受到了一些限制。近年来,随着深度学习技术的发展,基于检测的方法也得到了很大的发展。这些方法通常使用卷积神经网络来进行人体检测,如Faster R-CNN、YOLO等。一旦人体被检测出来,就可以使用基于回归的方法来进行姿态估计。例如,OpenPose是一种基于检测的方法,它使用卷积神经网络来进行人体检测,然后通过回归模块来预测关键点的位置。 综上所述,基于回归和基于检测的方法是两种常用的单人人体姿态估计方法。基于回归的方法通常具有很高的精度,但是在复杂场景下的表现会受到限制。基于检测的方法通常具有很好的鲁棒性,但是需要设计有效的人体检测器。未来,基于回归和基于检测的方法都有着很大的发展空间,特别是在多人姿态估计和视频姿态估计等方面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值