http://blog.sina.com.cn/s/blog_4e6680090100d2se.html
对人脸检测的研究最初可以追溯到 20 世纪 70 年代,早期的研究主要致力于模板匹配、子空间方法,变形模板匹配等。近期人脸检测的研究主要集中在基于数据驱动的学习方法,如统计模型方法,神经网络学习方法,统计知识理论和支持向量机方法,基于马尔可夫随机域的方法,以及基于肤色的人脸检测。目前在实际中应用的人脸检测方法多为基于 Adaboost 学习算法的方法。
Viola人脸检测方法是一种基于积分图、 级联检测器和AdaBoost 算法的方法,方法框架可以分为以下三大部分:
第一部分,使用Harr-like特征表示人脸,使用“ 积分图”实现特征数值的快速计算;
第二部分, 使用Adaboost算法挑选出一些最能代表人脸的矩形特征( 弱分类器),按照加权投票的方式将弱分类器构造为一个强分类器;
第三部分, 将训练得到的若干强分类器串联组成一个级联结构的层叠分类器,级联结构能有效地提高分类器的检测速度。
Adaboost 算法是一种用来分类的方法,它的基本原理就是“三个臭皮匠,顶个诸葛亮”。它把一些比较弱的分类方法合在一起,组合出新的很强的分类方法。
例如下图中

需要用一些线段把红色的球与深蓝色的球分开,然而如果仅仅画一条线的话,是分不开的。
使用Adaboost算法来进行划分的话,先画出一条错误率最小的线段如图 1 ,但是左下脚的深蓝色球被错误划分到红色区域,因此加重被错误球的权重,再下一次划分时,将更加考虑那些权重大的球,如 c 所示,最终得到了一个准确的划分,如下图所示。

人脸检测的目的就是从图片中找出所有包含人脸的子窗口,将人脸的子窗口与非人脸的子窗口分开。
大致步骤如下:
(1)在一个 20*20 的图片提取一些简单的特征(称为Harr特征),如下图所示。

它的计算方法就是将白色区域内的像素和减去黑色区域,因此在人脸与非人脸图片的相同位置上,值的大小是不一样的,这些特征可以用来区分人脸和分人脸。

(2)目前的方法是使用数千张切割好的人脸图片,和上万张背景图片作为训练样本。训练图片一般归一化到 20*20 的大小。在这样大小的图片中,可供使用的 haar 特征数在 1 万个左右,然后通过机器学习算法 —adaboost 算法挑选数千个有效的 haar 特征来组成人脸检测器。

(3)学习算法训练出一个人脸检测器后,便可以在各个场合使用了。使用时,将图像按比例依次缩放,然后在缩放后的图片的 20*20 的子窗口依次判别是人脸还是非人脸。

人脸检测的流程
人脸检测在实际中主要用于人脸识别的预处理,即在图像中准确标定出人脸的位置和大小。
目前人脸检测技术在门禁系统、智能监控系统中已得到了很好的应用。另外,目前的笔记本电脑中也陆续开始使用人脸识别技术作为计算机登录的凭证。近年来,在数码相机和手机中也集成了人脸检测算法,作为一个新的功能提供用户使用。在这些应用中,人脸检测都是发挥着至关重要的作用
#include<iostream> #include<algorithm> #include<functional> #include<cmath> #include<vector> using std::vector; using namespace std;#define FCOUNT 100//特征数 #define CCOUNT 30//弱分类器个数 #define PCOUNT 200//正样本数 #define NCOUNT 300//负样本数struct sample { int features[FCOUNT];//特征 int pos_neg;//正0,负1 float weight;//权值 int result;//分类器的识别结果 };struct weakClassifier
{ int indexF; float threshold; };
struct MySortFunction
{ int m_n; MySortFunction(int n):m_n(n) { } bool operator()(sample&s1,sample&s2)const { return s1.features[m_n]<s2.features[m_n]; }
};
//创建正样本voidCreatePos(vector<sample>&a) { int i,j; for(i=0;i<PCOUNT;i++) { sample temp; temp.pos_neg=0; temp.weight=(float)1/(2*PCOUNT); temp.result =0; for(j=0;j<FCOUNT;j++) temp.features[j]=rand()%10; a.push_back(temp); } } floatmin(floata,floatb) {return(a<=b?a:b); }
//创建负样本
void CreateNeg(vector<sample>&a) {
int i,j; for(i=0;i<NCOUNT;i++) {
sample temp; temp.pos_neg=1; temp.weight=(float)1/(2*NCOUNT); temp.result =1; for(j=0;j<FCOUNT;j++) temp.features[j]=rand()%10; a.push_back(temp);
} } //Training classifier
void Training(vector<sample>&a,vector<weakClassifier>&b,float*factors) {
int i,j; vector<sample>::size_type id=0,tcount=a.size(); for(i=0;i<CCOUNT;i++) {
weakClassifier temp; float totalWeights=0.0,totalPos=0.0,totalNeg=0.0,bPos=0,bNeg=0;//(当前样本之前的)正负样本权值和 float e,thr,besterr=1.0;//训练单个分类器时用到的错误率,阈值,最小错误率 float FThr[FCOUNT];//特征阈值 float minErr=1.0;//所有特征的最小错误率 float beta;//更新权值所需系数 for(id=0;id<tcount;id++) {
totalWeights+=a[id].weight ; if(a[id].pos_neg ) totalNeg+=a[id].weight ; else totalPos+=a[id].weight;
}
for(id=0;id<tcount;id++) a[id].weight /=totalWeights; for(j=0;j<FCOUNT;j++) {
//按特征j排序 sort(a.begin(),a.end (),MySortFunction(j)); besterr=1.0; //求单个弱分类器的阈值 for(id=0;id<tcount;id++) {
if(a[id].pos_neg ) bNeg+=a[id].weight ; else bPos+=a[id].weight ; e=min((bPos+totalNeg-bNeg),(bNeg+totalPos-bPos)); if(id==0)
thr=a[id].features[j]-0.5; else { if(id==tcount-1)
thr=a[a.size()-1].features[j]+0.5; else thr=(a[id].features[j]+a[id-1].features[j])/2;
}
if(e<besterr) {
besterr=e; FThr[j]=
thr;
//cout<<FThr[j]<<""<<j<<endl; }
}
}
for(j=0;j<FCOUNT;j++) {
float serror=0.0; for(id=0;id<tcount;id++) {
if(a[id].features[j]<=FThr[j]) a[id].result =0;//positive sample else a[id].result=1; serror+=a[id].weight*abs(a[id].result-a[id].pos_neg );
} if(serror<minErr) {
minErr=serror; temp.indexF=j; temp.threshold=FThr[j];
}
}
b.push_back(temp);//选出一个弱分类器
beta=minErr/(1-minErr); factors[i]=log(1/beta); for(id=0;id<tcount;id++
) {
if(a[id].pos_neg
==a[id].result )
a[id].weight *=beta;
} }//强分类器训练完毕}void main()
{ vector<sample>a; vector<weakClassifier>b; float factors[CCOUNT];//at CreatePos(a);//创建正样本 CreateNeg(a);//创建负样本 Training(a,b,factors);//训练分类器 //查看训练出的分类器的参数 int i=0;
for(i=0;i<CCOUNT;i++
) {
cout<<"系数"<<factors[i]<<"特征"<<b[i].indexF
<<"阈值"<<b[i].threshold<<endl;
}
}