OpenCV人脸识别知识

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

参考:
http://www.cnblogs.com/ello/archive/2012/04/28/2475419.html

目前的人脸检测方法主要有两大类:基于知识和基于统计。

Ø 基于知识的方法:主要利用先验知识将人脸看作器官特征的组合,根据眼睛、眉毛、嘴巴、鼻子等器官的特征以及相互之间的几何位置关系来检测人脸。

Ø 基于统计的方法:将人脸看作一个整体的模式——二维像素矩阵,从统计的观点通过大量人脸图像样本构造人脸模式空间,根据相似度量来判断人脸是否存在。

基于知识的人脸检测方法:

模板匹配、人脸特征、形状与边缘、纹理特性、颜色特征

基于统计的人脸检测方法:

主成分分析与特征脸、神经网络方法、支持向量机、隐马尔可夫模型、Adaboost算法

聚类和分类的区别是什么?

Ø 分类:一般对已知物体类别总数的识别方式我们称之为分类,并且训练的数据是有标签的,比如已经明确指定了是人脸还是非人脸,这是一种有监督学习。

Ø 聚类:也存在可以处理类别总数不确定的方法或者训练的数据是没有标签的,这就是聚类,不需要学习阶段中关于物体类别的信息,是一种无监督学习。

其中包括Mahalanobis距离、K均值、朴素贝叶斯分类器、决策树、Boosting、随机森林、Haar分类器、期望最大化、K近邻、神经网络、支持向量机。

我们要探讨的Haar分类器实际上是Boosting算法的一个应用,Haar分类器用到了Boosting算法中的AdaBoost算法,只是把AdaBoost算法训练出的强分类器进行了级联,并且在底层的特征提取中采用了高效率的矩形特征和积分图方法,这里涉及到的几个名词接下来会具体讨论。

Haar分类器 = Haar-like特征 + 积分图方法 + AdaBoost +级联;

Haar分类器算法的要点如下:

① 使用Haar-like特征做检测。

② 使用积分图(Integral Image)对Haar-like特征求值进行加速。

③ 使用AdaBoost算法训练区分人脸和非人脸的强分类器。

④ 使用筛选式级联把强分类器级联到一起,提高准确率。

 /* * CART classifier */ typedef struct CvCARTHaarClassifier {     CV_INT_HAAR_CLASSIFIER_FIELDS()     int count;     int* compidx;     CvTHaarFeature* feature;     CvFastHaarFeature* fastfeature;     float* threshold;     int* left;     int* right;     float* val;} CvCARTHaarClassifier;

CART这个词,它是一种二叉决策树 ,“分类和回归树(CART)”

什么是决策树?

机器学习中,决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映射关系。树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个叶结点则对应从根节点到该叶节点所经历的路径所表示的对象的值。决策树仅有单一输出,若欲有复数输出,可以建立独立的决策树以处理不同输出。从数据产生决策树的机器学习技术叫做决策树学习, 通俗说就是决策树。

决策树包含:分类树,回归树,分类和回归树(CART),CHAID

分类和回归的区别是,分类是当预计结果可能为两种类型(例如男女,输赢等)使用的概念。

回归当局域结果可能为实数(例如房价,患者住院时间等)使用的概念。

决策树用途很广可以分析因素对事件结果的影响,同时也是很常用的分类方法,我举个最简单的决策树例子,假设我们使用三个Haar-like特征f1,f2,f3来判断输入数据是否为人脸,可以建立如下决策树:


 

OpenCV人脸识别知识


可以看出,在分类的应用中,每个非叶子节点都表示一种判断,每个路径代表一种判断的输出,每个叶子节点代表一种类别,并作为最终判断的结果。

一个弱分类器就是一个基本和上图类似的决策树,最基本的弱分类器只包含一个Haar-like特征也就是它的决策树只有一层,被称为树桩(stump)。


具体操作过程如下:

1)对于每个特征 f,计算所有训练样本的特征值,并将其排序。

扫描一遍排好序的特征值,对排好序的表中的每个元素,计算下面四个值:

全部人脸样本的权重的和t1;

全部非人脸样本的权重的和t0;

在此元素之前的人脸样本的权重的和s1;

在此元素之前的非人脸样本的权重的和s0;

2)最终求得每个元素的分类误差OpenCV人脸识别知识

在表中寻找r值最小的元素,则该元素作为最优阈值。有了该阈值,我们的第一个最优弱分类器就诞生了。

在这漫长的煎熬中,我们见证了一个弱分类器孵化成长的过程,并回答了如何得到弱分类器以及二叉决策树是什么。最后的问题是强分类器是如何得到的。

首先看一下强分类器的代码结构:

/* internal stage classifier */typedef struct CvStageHaarClassifier{    CV_INT_HAAR_CLASSIFIER_FIELDS()    int count;    float threshold;    CvIntHaarClassifier** classifier;}CvStageHaarClassifier;
/* internal weak classifier*/typedef struct CvIntHaarClassifier{    CV_INT_HAAR_CLASSIFIER_FIELDS()} CvIntHaarClassifier;

这里要提到的是CvIntHaarClassifier结构: 它就相当于一个接口类,当然是用C语言模拟的面向对象思想,利用CV_INT_HAAR_CLASSIFIER_FIELDS()这个宏让弱分类CvCARTHaarClassifier和强分类器CvStageHaarClassifier继承于CvIntHaarClassifier。


强分类器的诞生需要T轮的迭代,具体操作如下:

1. 给定训练样本集S,共N个样本,其中X和Y分别对应于正样本和负样本; T为训练的最大循环次数;  

2. 初始化样本权重为1/N ,即为训练样本的初始概率分布;  

3. 第一次迭代训练N个样本,得到第一个最优弱分类器,步骤见上

4. 提高上一轮中被误判的样本的权重;

5. 将新的样本和上次本分错的样本放在一起进行新一轮的训练。

6. 循环执行4-5步骤,T轮后得到T个最优弱分类器。

7.组合T个最优弱分类器得到强分类器

相当于让所有弱分类器投票,再对投票结果按照弱分类器的错误率加权求和,将投票加权求和的结果与平均投票结果比较得出最终的结果

至今为止我们好像一直在讲分类器的训练,实际上Haar分类器是有两个体系的,训练的体系,和检测的体系。训练的部分大致都提到了,还剩下最后一部分就是对筛选式级联分类器的训练。我们看到了通过AdaBoost算法辛苦的训练出了强分类器,然而在现实的人脸检测中,只靠一个强分类器还是难以保证检测的正确率,这个时候,需要一个豪华的阵容,训练出多个强分类器将它们强强联手,最终形成正确率很高的级联分类器这就是我们最终的目标Haar分类器。

那么训练级联分类器的目的就是为了检测的时候,更加准确,这涉及到Haar分类器的另一个体系,检测体系,检测体系是以现实中的一幅大图片作为输入,然后对图片中进行多区域,多尺度的检测,所谓多区域,是要对图片划分多块,对每个块进行检测,由于训练的时候用的照片一般都是20*20左右的小图片,所以对于大的人脸,还需要进行多尺度的检测,多尺度检测机制一般有两种策略,一种是不改变搜索窗口的大小,而不断缩放图片,这种方法显然需要对每个缩放后的图片进行区域特征值的运算,效率不高,而另一种方法,是不断初始化搜索窗口size为训练时的图片大小,不断扩大搜索窗口,进行搜索,解决了第一种方法的弱势。在区域放大的过程中会出现同一个人脸被多次检测,这需要进行区域的合并,这里不作探讨。

无论哪一种搜索方法,都会为输入图片输出大量的子窗口图像,这些子窗口图像经过筛选式级联分类器会不断地被每一个节点筛选,抛弃或通过。

/* internal tree cascade classifier node */typedef struct CvTreeCascadeNode{    CvStageHaarClassifier* stage;    struct CvTreeCascadeNode* next;    struct CvTreeCascadeNode* child;    struct CvTreeCascadeNode* parent;    struct CvTreeCascadeNode* next_same_level;    struct CvTreeCascadeNode* child_eval;    int idx;    int leaf;} CvTreeCascadeNode;/* internal tree cascade classifier */typedef struct CvTreeCascadeClassifier{    CV_INT_HAAR_CLASSIFIER_FIELDS()    CvTreeCascadeNode* root;      /* root of the tree */    CvTreeCascadeNode* root_eval; /* root node for the filtering */    int next_idx;} CvTreeCascadeClassifier;

我们熟悉了Haar-like分类器的训练和检测过程,你会看到无论是训练还是检测,每遇到一个图片样本,每遇到一个子窗口图像,我们都面临着如何计算当前子图像特征值的问题,一个Haar-like特征在一个窗口中怎样排列能够更好的体现人脸的特征,这是未知的,所以才要训练而训练之前我们只能通过排列组合穷举所有这样的特征,仅以Viola牛提出的最基本四个特征为例,在一个24×24size的窗口中任意排列至少可以产生数以10万计的特征,对这些特征求值的计算量是非常大的。

而积分图就是只遍历一次图像就可以求出图像中所有区域像素和的快速算法,大大的提高了图像特征值计算的效率

我们来看看它是怎么做到的。

积分图是一种能够描述全局信息的矩阵表示方法。积分图的构造方式是位置(i,j)处的值ii(i,j)是原图像(i,j)左上角方向所有像素的和:

OpenCV人脸识别知识

积分图构建算法:

1)用s(i,j)表示行方向的累加和,初始化s(i,-1)=0;

2)用ii(i,j)表示一个积分图像,初始化ii(-1,i)=0;

3)逐行扫描图像,递归计算每个像素(i,j)行方向的累加和s(i,j)和积分图像ii(i,j)的值

s(i,j)=s(i,j-1)+f(i,j)

ii(i,j)=ii(i-1,j)+s(i,j)

4)扫描图像一遍,当到达图像右下角像素时,积分图像ii就构造好了。

积分图构造好之后,图像中任何矩阵区域的像素累加和都可以通过简单运算得到如图所示。

OpenCV人脸识别知识

设D的四个顶点分别为α、β、γ、δ,则D的像素和可以表示为

Dsum = ii( α )+ii( β)-(ii( γ)+ii( δ ));

而Haar-like特征值无非就是两个矩阵像素和的差,同样可以在常数时间内完成。

搜索窗口的搜索区域是提高效率的关键

OpenCV的人脸检测主要是调用训练好的cascade(Haar分类器)来进行模式匹配。

cvHaarDetectObjects,先将图像灰度化,根据传入参数判断是否进行canny边缘处理(默认不使用),再进行匹配。匹配后收集找出的匹配块,过滤噪声,计算相邻个数如果超过了规定值(传入的min_neighbors)就当成输出结果,否则删去。

匹配循环将匹配分类器放大scale(传入值)倍,同时原图缩小scale倍,进行匹配,直到匹配分类器的大小大于原图,则返回匹配结果。匹配的时候调用cvRunHaarClassifierCascade来进行匹配,将所有结果存入CvSeq* Seq (可动态增长元素序列),将结果传给cvHaarDetectObjects。

cvRunHaarClassifierCascade函数整体是根据传入的图像和cascade来进行匹配。并且可以根据传入的cascade类型不同(树型、stump(不完整的树)或其他的),进行不同的匹配方式。

函数 cvRunHaarClassifierCascade 用于对单幅图片的检测。在函数调用前首先利用 cvSetImagesForHaarClassifierCascade设定积分图和合适的比例系数 (=> 窗口尺寸)。当分析的矩形框全部通过级联分类器每一层的时返回正值(这是一个候选目标),否则返回0或负值。

cvRunHaarClassifierCascade
在给定位置的图像中运行 cascade of boosted classifier
int cvRunHaarClassifierCascade( CvHaarClassifierCascade* cascade,
CvPoint pt, int start_stage=0 );
cascade Haar 级联分类器
pt 待检测区域的左上角坐标。待检测区域大小为原始窗口尺寸乘以当前设定的比例系数。当前窗口尺寸可以通过cvGetHaarClassifierCascadeWindowSize重新得到。
start_stage 级联层的初始下标值(从0开始计数)。函数假定前面所有每层的分类器都已通过。这个特征通过函数cvHaarDetectObjects内部调用,用于更好的处理器高速缓冲存储器。


correction_ratio = weight_scale * (!feature->tilted ? 1 : 0.5);
其中:
weight_scale = 1./(equRect.width*equRect.height);
cascade->inv_window_area = weight_scale;

weight_scale就是面积分之一。我个人理解correction_ratio就是看你的haar-like特征旋转没,
若旋转过则:要乘以1/2。若没有就是原面积。

然后去乘以矩形内的像素和就应该是该haar—like的特征值了。

该算法的主要贡献有三:

1.提出积分图像(integral image),从而可以快速计算Haar-like特征。

2.利用Adaboost学习算法进行特征选择和分类器训练,把弱分类器组合成强分类器。

3.采用分类器级联提高效率。

DSP 算法优化~

线性汇编
当C语言级的优化仍不能满足要求时,应采用汇编级优化。测试中发现,一些程序很短,但在板子上上运行相当耗时,这类程序考虑用线性汇编优化。如:浮点数的四则运算。


循环优化
在程序2中,使用这种方法优化:
1)循环变量尽量不参加运算,直接给出循环变量的值;
2)循环嵌套时,尽可能打开最里面的小循环,才能形成软件流水。


线性汇编
当C语言级的优化仍不能满足要求时,应采用汇编级优化。测试中发现,一些程序很短,但在DM642上运行相当耗时,这类程序考虑用线性汇编优化。如:浮点数的四则运算。

DM6447是定点DSP,浮点数运算占用大量的运行时间,可采用下面的方法优化:
1)在程序中,要尽量把浮点运算转化为定点运算,比如采用定标的方式;
2)编写浮点运算对应的线性汇编程序


二级缓存L2的使用
增大Cache,可以明显提高性能,但是DM642中二级Cache共享片内RAM,因此增大Cache,就减小了实际的片内可用空间,在优化设计时必须综合考虑。在本文系统中,经常
用到两个数据(检测分类器和识别转换矩阵)和两个程序(检测和识别代码) ,为了提高系统速度,程序设计时,把这四部分尽可能多地放到L2中,如程序3所示


数据的EDMA搬移
由于DM642有限的片内RAM,当L2中无法存储数据和程序时,经常把数据存储在片外, EDMA技术把到的数据搬入片内,大大提高了程序的运行速度。

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

OpenCV人脸识别知识