opencv人脸识别原理
在之前讲到的人脸测试后,提取出人脸来,并且保存下来,以供训练或识别是用,提取人脸的代码如下:
人脸预处理
现在你已经得到一张人脸,你可以使用那张人脸图片进行人脸识别。然而,假如你尝试这样简单地从一张普通图片直接进行人脸识别的话,你将会至少损失10%的准确率!
在一个人脸识别系统中,应用多种预处理技术对将要识别的图片进行标准化处理是极其重要的。多数人脸识别算法对光照条件十分敏感,所以假如在暗室训练,在明亮的房间就可能不会被识别出来等等。这个问题可归于“luminationdependent”,并且还有其它很多例子,比如脸部也应当在图片的一个十分固定的位置(比如眼睛位置为相同的像素坐标),固定的大小,旋转角度,头发和装饰,表情(笑,怒等),光照方向(向左或向上等),这就是在进行人脸识别前,使用好的图片预处理过滤器十分重要的原因。你还应该做一些其它事情,比如去除脸部周围的多余像素(如用椭圆遮罩,只显示其内部的人脸区域而不是头发或图片背景,因为他们的变化多于脸部区域)。
为简单起见,我展示给你的人脸识别系统是使用灰度图像的特征脸方法。所以我将向你说明怎样简单地把彩色图像转化为灰度图像,并且之后简单地使用直方图均衡化(HistogramEqualization)作为一种自动的标准化脸部图像亮度和对比度的方法。为了得到更好的结果,你可以使用彩色人脸识别(colorfacerecognition,ideallywithcolorhistogramfittinginHSVoranothercolorspaceinsteadofRGB),或者使用更多的预处理,比如边缘增强(edgeenhancement),轮廓检测(contourdetection),手势检测(motiondetection),等等。
PCA原理
现在你已经有了一张经过预处理后的脸部图片,你可以使用特征脸(PCA)进行人脸识别。OpenCV自带了执行PCA操作的”cvEigenDecomposite()”函数,然而你需要一个图片数据库(训练集)告诉机器怎样识别当中的人。
所以你应该收集每个人的一组预处理后的脸部图片用于识别。比如,假如你想要从10人的班级当中识别某个人,你可以为每个人存储20张图片,总共就有200张大小相同(如100×100像素)的经预处理的脸部图片。
特征脸的理论在ServoMagazine的两篇文章(FaceRecognitionwithEigenface)中解释了,但我仍会在这里尝试着向你解释。
我们使用“主元分析”把你的200张训练图片转换成一个代表这些训练图片主要区别的“特征脸”集。首先它将会通过获取每个像素的平均值,生成这些图片的“平均人脸图片”。然后特征脸将会与“平均人脸”比较。第一个特征脸是最主要的脸部区别,第二个特征脸是第二重要的脸部区别,等……直到你有了大约50张代表大多数训练集图片的区别的特征脸。
在上面这些示例图片中你可以看到平均人脸和第一个以及最后一个特征脸。注意到,平均人脸显示的是一个普通人的平滑脸部结构,排在最前的一些特征脸显示了一些主要的脸部特征,而最后的特征脸(比如Eigenface119)主要是图像噪声。你可以在下面看到前32张特征脸。
简单地说,特征脸方法(PrincipalComponentAnalysis)计算出了训练集中图片的主要区别,并且用这些“区别”的组合来代表每幅训练图片。
比如,一张训练图片可能是如下的组成:
(averageFace)+(13.5%ofeigenface0)–(34.3%ofeigenface1)+(4.7%ofeigenface2)+…+(0.0%ofeigenface199)。
一旦计算出来,就可以认为这张训练图片是这200个比率(ratio):
{13.5,-34.3,4.7,…,0.0}。
用特征脸图片分别乘以这些比率,并加上平均人脸图片(averageface),从这200个比率还原这张训练图片是完全可以做到的。但是既然很多排在后面的特征脸是图像噪声或者不会对图片有太大作用,这个比率表可以被降低到只剩下最主要的,比如前30个,不会对图像质量有很大影响。所以现在可以用30个特征脸,平均人脸图片,和一个含有30个比率的表,来代表全部的200张训练图片。
在另一幅图片中识别一个人,可以应用相同的PCA计算,使用相同的200个特征脸来寻找200个代表输入图片的比率。并且仍然可以只保留前30个比率而忽略其余的比率,因为它们是次要的。然后通过搜索这些比率的表,寻找在数据库中已知的20个人,来看谁的前30个比率与输入图片的前30个比率最接近。这就是寻找与输入图片最相似的训练图片的基本方法,总共提供了200张训练图片。
训练图片
创建一个人脸识别数据库,就是训练一个列出图片文件和每个文件代表的人的文本文件,形成一个facedata.xml“文件。
比如,你可以把这些输入一个名为”trainingphoto.txt”的文本文件:
joke1.jpg
joke2.jpg
joke3.jpg
joke4.jpg
lily1.jpg
lily2.jpg
lily3.jpg
lily4.jpg
它告诉这个程序,第一个人的名字叫“joke,而joke有四张预处理后的脸部图像,第二个人的名字叫”lily”,有她的四张图片。这个程序可以使用”loadFaceImgArray()”函数把这些图片加载到一个图片数组中。
为了从这些加载好的图片中创建一个数据库,你可以使用OpenCV的”cvCalcEigenObjects()”和”cvEigenDecomposite()”函数。
获得特征空间的函数:
voidcvCalcEigenObjects(intnObjects,void*input,void*output,intioFlags,intioBufSize,void*userData,CvTermCriteria*calcLimit,IplImage*avg,float*eigVals)
nObjects:目标的数目,即输入训练图片的数目。
input:输入训练的图片。
output:输出特征脸,总共有nEigens
ioFlags、ioBufSize:默认为0
userData:指向回调函数(callbackfunction)必须数据结构体的指针。
calcLimit:终止迭代计算目标特征的条件。根据calcLimit的参数,计算会在前nEigens主要特征目标被提取后结束(这句话有点绕,应该就是提取了前nEigens个特征值,),另一种结束的情况是:目前特征值同最s大特征值的比值降至calcLimit的epsilon值之下。
赋值如下calcLimit=cvTermCriteria(CV_TERMCRIT_ITER,nEigens,1);
它的类型定义如下:
typedefstructCvTermCriteria
{
inttype;intmax_iter;//最大迭代次数
doubleepsilon;//结果精确性
}
avg:训练样本的平均图像
eigVals:以降序排列的特征值的行向量指针。可以为0。
最后将所得数据形成一个facedata.xml“文件保存下来,它可以随时被重新载入来识别经训练过的人。
图像在特征空间的投影:
voidcvEigenDecomposite(IplImage*obj,intnEigObjs,void*eigInput,intioFlags,void*userData,IplImage*avg,float*coeffs);
obj:输入图像,训练或识别图像
nEigObjs:特征空间的eigen数量
eigInput:特征空间中的特征脸
ioFlags、userData:默认为0
avg:特征空间中的平均图像
coeffs:这是唯一一个输出,即人脸在子空间的投影,特征值
识别的过程
1.读取用于测试的图片。
2.平均人脸,特征脸和特征值(比率)使用函数“loadTrainingData()”从人脸识别数据库文件(thefacerecognitiondatabasefil)“facedata.xml”载入。
3.使用OpenCV的函数“cvEigenDecomposite()”,每张输入的图片都被投影到PCA子空间,来观察哪些特征脸的比率最适合于代表这张图片。
4.现在有了特征值(特征脸图片的比率)代表这张输入图片,程序需要查找原始的训练图片,找出拥有最相似比率的图片。这些用数学的方法在“findNearestNeighbor()”函数中执行,采用的是“欧几里得距离(EuclideanDistance)”,但是它只是基本地检查输入图片与每张训练图片的相似性,找到最相似的一张:一张在欧几里得空间上与输入图片距离最近的图片。就像在ServoMagazine的文章上提到的那样,如果使用马氏距离(theMahalanobisspace,需要在代码里定义USE_MAHALANOBIS_DISTANCE),你可以得到更准确的结果。
5.在输入图片与最相似图片之间的距离用于确定可信度(confidence),作为是否识别出某人的指导。1.0的可信度意味着完全相同,0.0或者负的可信度意味着非常不相似。但是需要注意,我在代码中用到的可信度公式只是一个非常基本的可信度测量,不是很可靠,但是我觉得多数人会想要看到一个粗略的可信度值。你可能发现它对你的图片给出错误的值,所以你可以禁用它(比如:把可信度设为恒定的1.0)。
一旦指导哪张训练图片和输入图片最相似,并假定可信度值不是太低(应该至少是0.6或更高),那么它就指出了那个人是谁,换句话说,它识别出了那个人!
人脸识别原理
人脸识别主要分为人脸检测(facedetection)、特征提取(featureextraction)和人脸识别(facerecognition)三个过程。
人脸识别又可以分为两个大类:一类是确认,这是人脸图像与数据库中已存的该人图像比对的过程,回答你是不是你的问题;
另一类是辨认,这是人脸图像与数据库中已存的所有图像匹配的过程,回答你是谁的问题。显然,人脸辨认要比人脸确认困难,因为辨认需要进行海量数据的匹配。常用的分类器有最近邻分类器、支持向量机等。
与指纹应用方式类似,人脸识别技术目前比较成熟的也是考勤机。因为在考勤系统中,用户是主动配合的,可以在特定的环境下获取符合要求的人脸。这就为人脸识别提供了良好的输入源,往往可以得到满意的结果。
但是在一些公共场所安装的视频监控探头,由于光线、角度问题,得到的人脸图像很难比对成功。这也是未来人脸识别技术发展必须要解决的难题之一。
1、人脸识别流程人脸识别是由一系列的几个相关问题组成的:
首先找到一张图片中的所有人脸。对于每一张脸来说,无论光线明暗或面朝别处,它依旧能够识别出是同一个人的脸。能够在每一张脸上找出可用于他人区分的独特之处,比如眼睛多大,脸有多长等等。最后将这张脸的特点与已知所有人脸进行比较,以确定这个人是谁。第一步:找出所有的面孔
很显然在我们在人脸识别的流程中得首先找到图片中的人脸。我们在使用手机或相机拍照时都会有人像模式,它能轻松的检测出人脸的位置,帮助相机快速对焦。
我们得感谢保罗·比奥拉(PaulViola)和迈克尔·琼斯(MichaelJones)在2000年发明了一种能够快速在廉价相机上运行的人脸检测方法,人脸检测在相机上的应用才成为主流。然而现在我们有更可靠的解决方案HOG(HistogramofOrientedGradients)方向梯度直方图,一种能够检测物体轮廓的算法。
首先我们把图片灰度化,因为颜色信息对于人脸检测而言没什么用。
我们分析每个像素以及其周围的像素,根据明暗度画一个箭头,箭头的指向代表了像素逐渐变暗的方向,如果我们重复操作每一个像素,最终像素会被箭头取代。这些箭头被称为梯度(gradients),它们能显示出图像从明亮到黑暗流动的过程。
分析每个像素对我们来说有点不划算,因为它太过细节化了,我们可能会迷失在像素的海洋里,我们应该从更高的角度观察明暗的流动。
为此我们将图像分割成16x16像素的小方块。在每个小方块中,计算出每个主方向有多少个剃度(有多少指向上,指向右上,指向右等)。然后用指向性最强的那个方向箭头来代替原来那个小方块。
最终结果,我们把原始图像转换成一个非常简单的HOG表达形式,它可以很轻松的捕获面部的基本结构。
为了在HOG图像中找到脸部,我们需要做的是,与已知的一些HOG图案中,看起来最相似的部分。这些HOG图案都是重其他面部训练数据中提取出来的。
第二步:脸部的不同姿势
我们已经找出了图片中的人脸,那么如何鉴别面朝不同方向的人脸呢?
对于电脑来说朝向不同的人脸是不同的东西,为此我们得适当的调整扭曲图片中的人脸,使得眼睛和嘴总是与被检测者重叠。
为了达到目的我们将使用一种面部特征点估计(facelandmarkestimation)的算法。其实还有很多算法都可以做到,但我们这次使用的是由瓦希德·卡奇米(VahidKazemi)和约瑟菲娜·沙利文(JosephineSullivan)在2014年发明的方法。
这一算法的基本思路是找到68个人脸上普遍存在的点(称为特征点,landmark)。
下巴轮廓17个点[0-16]左眉毛5个点[17-21]右眉毛5个点[22-26]鼻梁4个点[27-30]鼻尖5个点[31-35]左眼6个点[36-41]右眼6个点[42-47]外嘴唇12个点[48-59]内嘴唇8个点[60-67]有了这68个点,我们就可以轻松的知道眼睛和嘴巴在哪儿了,后续我们将图片进行旋转,缩放和错切,使得眼睛和嘴巴尽可能的靠近中心。
现在人脸基本上对齐了,这使得下一步更加准确。
第三步:给脸部编码
我们还有个核心的问题没有解决,那就是如何区分不同的人脸。
最简单的方法就是把我们第二步中发现的未知人脸与我们已知的人脸作对比。当我们发现未知的面孔与一个以前标注过的面孔看起来相似的时候,就可以认定他们是同一个人。
我们人类能通过眼睛大小,头发颜色等等信息轻松的分辨不同的两张人脸,可是电脑怎么分辨呢?没错,我们得量化它们,测量出他们的不同,那要怎么做呢?
实际上,对于人脸这些信息很容易分辨,可是对于计算机,这些值没什么价值。实际上最准确的方法是让计算机自己找出他要收集的测量值。深度学习比人类更懂得哪些面部测量值比较重要。
所以,解决方案是训练一个深度卷积神经网络,训练让它为脸部生成128个测量值。
每次训练要观察三个不同的脸部图像:
加载一张已知的人的面部训练图像加载同一个人的另一张照片加载另外一个人的照片然后,算法查看它自己为这三个图片生成的测量值。再然后,稍微调整神经网络,以确保第一张和第二张生成的测量值接近,而第二张和第三张生成的测量值略有不同。
我们要不断的调整样本,重复以上步骤百万次,这确实是个巨大的挑战,但是一旦训练完成,它能攻轻松的找出人脸。
庆幸的是OpenFace上面的大神已经做完了这些,并且他们发布了几个训练过可以直接使用的网络,我们可以不用部署复杂的机器学习,开箱即用,感谢开源精神。
这128个测量值是什么鬼?
其实我们不用关心,这对我们也不重要。我们关心的是,当看到同一个人的两张不同照片时,我们的网络需要能得到几乎相同的数值。
第四步:从编码中找出人的名字
最后一步实际上是最简单的一步,我们需要做的是找到数据库中与我们的测试图像的测量值最接近的那个人。
如何做呢,我们利用一些现成的数学公式,计算两个128D数值的欧氏距离。
这样我们得到一个欧式距离值,系统将给它一个认为是同一个人欧氏距离的阀值,即超过这个阀值我们就认定他们是同(失)一(散)个(兄)人(弟)。
人脸识别就这样达成啦,来来我们再回顾下流程:
使用HOG找出图片中所有人脸的位置。计算出人脸的68个特征点并适当的调整人脸位置,对齐人脸。把上一步得到的面部图像放入神经网络,得到128个特征测量值,并保存它们。与我们以前保存过的测量值一并计算欧氏距离,得到欧氏距离值,比较数值大小,即可得到是否同一个人。