上个月用了两周的时间,学习了用于人脸检测的haartraining算法,今天打算做一总结
首先先为和我一样的初学者推荐几篇博客
http://blog.csdn.net/zouxy09/article/details/7922923真的很感谢写这篇文章的博主,讲解深入浅出。本文中的主要逻辑也都是由他的文章启发而来,有部分引用的内容,这里提前声明。
http://blog.sina.com.cn/s/blog_5f853eb10100sdgn.html 这篇文章是AdaBoost算法的代码实现,注释实在是详细!赞
http://www.cnblogs.com/tornadomeet/archive/2012/03/28/2420936.html 这篇文章非常明白的讲解了如何利用opencv提供的可执行文件一步步尝试进行人脸识别
开始进入正题
haartraining方法是基于Haar特征和AdaBoost算法以及级联思想的
那么首先,特征是什么?
假设在人脸检测时我们需要有这么一个子窗口在待检测的图片窗口中不断的移位滑动,子窗口每到一个位置,就会计算出该区域的特征,这个能够按照我们规定的方式计算出某个区域的特征值的子窗口就是特征
那么haar特征又是什么
haar特征是由Viola、Lienhart等人提出的,原理并没有不同,这里不多赘述
大家可以参考我之前总结的人脸识别尝试(二)——Haar特征 总结的还是挺用心的,希望大家多支持
http://blog.csdn.net/u011583927/article/details/44782197
总之,一个Haar特征的数据结构应该包含以下内容:
*haar特征模板类型
*是否有旋转
*矩阵位置及大小
知道了Haar特性是什么,我们来聊聊AdaBoost算法
AdaBoost是一种具有一般性的分类器提升算法,既然是提升,那么一定是从弱分类器到强分类器的一个过程
先说弱分类器是怎样产生的
在这之前,我先引出一个最差的分类器,暂且叫它“糟糕分类器”
对于一个待检测的图片,选出任意一种Haar特征,就可以计算得到一个特征值
如果我们定义一个阈值,那么将计算的特征值与这个阈值进行比较,就能做出一个最低级的决策
我将这整个的过程称为一个“糟糕分类器”(实际上并没有这个称呼,只是我个人这样理解)用下图表示
显然仅仅凭借这样粗糙的方式不可能判别一副图片是否为人脸,因为它的效果确实太糟糕了。我们应用多个这样的糟糕分类器进行级联,如下图所示
一个弱分类器就是一个基本和上图类似的决策树,最基本的弱分类器只包含一个Haar-like特征,也就是它的决策树只有一层,被称为树桩(stump)。注意:这里这样的结构还不能称之为弱分类器,因为我们的阈值目前为止还是未知,也可以理解为随意选取的,这样当然不行。
对于这样一个弱分类器(或者说是二叉决策树)最重要的就是如何决定每个结点判断的输出,要比较输入图片的特征值和弱分类器中阈值。也就是要寻找合适的分类器阈值,使该分类器对所有样本的判读误差最低。寻找最优阈值的过程也就是我们进行样本训练的过程,训练后二叉决策树就是一个弱分类器。
那么如何寻找最优阈值呢?对于某一种特征,我们可以将输入样本(既有正样本又有负样本)分别计算对应的特征值并按升序排序成一个队列Seq。那么我们将阈值取遍队列中每一个特征值,对于每次循环,计算分错样本的加权平均和,并记录。遍历完成后分错样本的加权最小的那个位置对应的特征值就是最优阈值。然后我们要根据这次阈值的选取,将错分的样本适当的增加权重,选取另一个Haar特征,重复上面的过程。直至训练出要求的个数个“糟糕分类器”级联起来组成一个弱分类器。
现在我们已经能够得到弱分类器了。它的数据结构如下:
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;
那么由以上讨论,假设我们由第一组样本已经训练出了一个弱分类器。显然我们可以找出这个分类器错分的样本出来,与一些新样本组成第二组样本,由这组样本训练出第二个弱分类器。重复这个过程,可以得到T个弱分类器。我们组合这T个弱分类器成为一个强分类器CvStageHaarClassifier。我们先这样理解,让这些最优弱分类器根据自己的地位进行表决(判别的正确率决定其地位),决定当前判断的子窗口是否为人脸。这样一个表决过程就代表了强分类器。然后我们可以训练多个强分类器,让他们强强联手。还是构造一颗二叉决策树,树的每个节点是一个强分类器
强分类器的数据结构如下:
/* internal stage classifier */
typedef struct CvStageHaarClassifier
{CV_INT_HAAR_CLASSIFIER_FIELDS()int count;float threshold;CvIntHaarClassifier** classifier;
} CvStageHaarClassifier;
强强联手后就得到的最终的分类器,最终的分类器就如下图一样进行判决得出结论
最终的Haar分类器为CvTreeCascadeClassifier,它代表一颗树,树中的每一个节点的核心功能就是一个强分类器,数据结构如下:
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;/* 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;
到这里,haartraining的主要思想就都说清楚了
最后,我们总结一下上面的内容得出这样的结论:
一组学习样本可以通过训练得到一个弱分类器。
弱分类器就是一颗决策树,决策树的每个结点存储的是一种haar特征和对应的最优阈值
多个弱分类器可以构成一个强分类器
Haar分类器就是一颗结点是强分类器的决策树
毕竟水平有限,希望大家看了这篇文章能够多多指正,也希望小白能从中学到一点东西。
大家一起分享,才能一起变得强大~