人脸识别实践haar-like特征+积分图+adaboost+级联

来源:互联网 发布:软件架构风格 编辑:程序博客网 时间:2024/05/23 02:12

本篇博客分为四个部分

实现篇

原理篇

提高篇

注意事项

实现篇

1.样本

卡内基梅隆大学人脸样本http://cbcl.mit.edu/cbcl/software-datasets/FaceData2.html 

注:2429个人脸样本,4548个非人脸样本,像素为19*19。训练一个线上可使用的人脸识别模型,非人脸样本是远远不够的,所以需要自己去找非人脸样本,我是这么做的,

a. 用这部分数据训练出来一个简单的模型

b. 用这个模型放到线上实际的环境中去预测,将非人脸预测为人脸的图片下载下来作为非人脸样本

c. 继续训练模型

d. 重复23步直到假正率很低为止。

我们这里有两种样本,一种是卡梅提供的样本,大小都是19*19的,一种是从我们找的不包含人脸的图片中切割出小的patch,以便在样本不足够时补充非人脸样本,对于样本因为光照等的影响,我们一般需要对样本进行处理

样本预处理

a.样本像素值减去均值,目的避免整体亮度的影响,有的图像亮一点有的图像暗一点,通过减去均值就可以排除这个影响

b.除以方差,目的:例如有的人脸眼睛和脸的对比度明显一些,有的不明显,这一步的目的就是排除图片内部对比度的影响

在预处理19*19的样本的时候就是按照一般的方法走,在处理从图片上切割下来的patch的时候有几个点是可以加快速度的

1. 图片像素值积分图(求均值的时候会用到)

2. 图片求像素值平方的积分图(求方差的时候会用到)

求均值就用区域的像素和(使用积分图求得)除以面积就可以了

求方差,这里用到的方差公式不是标准的方差公式,为了加快计算方差公式:s^2 = (区域像素平方的和)/n - 均值的平方。其中区域像素平方的和可以用平方的积分图来快速得到

效果:经过这样的预处理,会比不处理的召回率有所提高,当然假正率的情况看会有所下降,当然假正率可以通过提高负样本的数量来提升

2. haar特征

只要理解了haar特征的概念,实现haar特征的计算还是比较容易的

在对样本(19*19)进行haar-like的特征进行计算的时候,不会有什么问题,对图片进行预测的时候就有几个问题

a. 边长的增长步长,这里选用的是常规的1.25倍的增长,例如刚开始边长是19,那么下一次边长就是[19*1.25],[]指的是向下取整

b. 边长变化的过程中我们发现再计算haar各种的时候点会出现小数,曾经想过办法将小数部分的区域也求出来。后来如果简单地将小数抛弃掉也不影响最后的结果,而且还会带来性能的影响,所以最后就直接把小数抛弃掉

c. 特征值的计算都是通过特征所占区域的灰度和除以当前区域的面积进行统一,这样就不会因为边长不同计算出来的特征值有相应的倍数关系


3. 积分图

这了给出积分图的计算代码

//求出第一行的积分图,代码中的ipfa.data是灰度化后的像素值for(int i = 0; i < width; i ++) {sum += ipfa.data[i];data[i] = sum;}for(int i = 1; i < height; i ++) {sum = 0f;for(int j = 0; j < width; j ++) {sum += ipfa.data[i * width + j];//sum指的是当前像素的那一行前面像素的和data[i * width + j] = sum + data[(i - 1) * width + j];}}

4. adaboost

adaboost主要需要完成两部分,一是弱分类器的实现,二是adaboost的实现

在这里,一个特征对应一个弱分类器,被adaboost选中的标准是分类器的误差最小,下面是这个弱分类器的实现代码,功能很简单

/** * 分类器训练,训练处分类阈值和方向* @param instances 实验样本* @param W 样本的权重* @param dim 对样本的哪个维度进行训练*/public void train(Instance[] instances, float[] W, int dimNum) {this.dimNum = dimNum;float positive = 0;//所有正样本的权重和float negative = 0;//所有负样本的权重和float[] positiveArray = new float[instances.length + 1];//当前元素前面所有正样本权重的和,与积分图异曲同工啊float[] negativeArray = new float[instances.length + 1];//当前元素前面所有负样本权重的和List<FeatureElement> featureList = new ArrayList<FeatureElement>();positiveArray[0] = 0;negativeArray[0] = 0;//这段代码两个作用,一是将第dimNum个特征进行排序,二是赋值positive和negativefor(int i = 0; i < instances.length; i++) {FeatureElement fe = new FeatureElement(instances[i].dim[dimNum], instances[i].label, W[i]);featureList.add(fe);if(instances[i].label == 1) {positive += W[i];} else {negative += W[i];}//升序Collections.sort(featureList);errorRate = Float.MAX_VALUE;//元素全部相等if(featureList.get(0).value == featureList.get(instances.length-1).value) return;//遍历排好序的list,求出fuhao和thresholdfor(int i = 0; i < featureList.size(); i++) {float acc1 = positiveArray[i] + negative - negativeArray[i];float acc2 = negativeArray[i] + positive - positiveArray[i];float error = Math.min(acc1, acc2);if(error < errorRate) {errorRate = error;threshold = featureList.get(i).value;if(acc2 >= acc1) {fuhao = 1; } else {fuhao = -1;}}if(featureList.get(i).label == 1) {positiveArray[i + 1] = positiveArray[i] + featureList.get(i).w;negativeArray[i + 1] = negativeArray[i];} else {negativeArray[i + 1] = negativeArray[i] + featureList.get(i).w;positiveArray[i + 1] = positiveArray[i];}}}

其实我们可以看到,代码中的positiveArray和negativeArray也是在用积分图的思想。弱分类器的实现就是求出来对当前特征进行分类误差最小的阈值和符号,符号指的就是大于阈值是+1还是-1,方向由符号控制

adaboost的实现

之前写过一篇adaboost的实现,基本差不多,有一些改动请参考http://blog.csdn.net/nwpuwyk/article/details/19678353

5.级联

我这里设定了两个值,检测率=100%,假正率=40%,这里的检测率指的是在每一层的adaboost中人脸的图片必须百分之百的被检测出来,假正率就是每一层的daaboost中国被模型预测为人脸的非人脸数/非人脸数的比值

这一步是我觉得不太好理解的地方,如何做才能保证检测率和假正率,我是这么做的

a.  首先第一层adaboost选出第一个特征(也就是在所有的特征中误差最小的那个特征),此时计算线性组合的值f(x),f(x)指的是分类器输出的加权和(后面的原理篇介绍adaboost算法的时候会降到),加的权就是分类器的误差,那么对于adaboost选出的第一个特征来说,f(x)就等于第一个分类器和此分类器的误差的乘积。计算所有样本的f(x)值,找到在正样本里最小的那个值A,用A去跟负样本计算出来的所有f(x)值比较,计算负样本里大于A的数量/小于A的数量。这个比值就是假正率fp。

b. 判断fp是否满足假正率的要求,如果满足,那么当前层的adaboost就已经训练完成了,将当前层的adaboost加入到已经定义好的list或者其他结构里,然后继续训练下一层

c. 如果fp不满足假正率要求,那么继续选取第二个.特征,那么此时f(x)已经变了,是第一个分类器和第二个分类器的加权和,权值还是每个分类器的误差。

d. 第三个第四个...第N个特征,直到满足假正率为止

样本补充

a. 人脸样本始终是确定的,非人脸样本需要不断补充,训练完一层adaboost,就需要补充一些负样本。

这个举例说明:最初的正样本、负样本各2000个,训练完第一层adaboost后,这时已经能够过滤掉部分非人脸样本,按照40%的假正率要求,可以过滤掉800个负样本,这时负样本的数量只有1200个了,所以需要补充800个负样本,补充的方法是这样,就用我们刚刚训练出来的adaboost来对非人脸的候选样本进行预测,预测为人脸的加入到负样本中,当负样本到达2000个时候,这时负样本已经充足,可以进行下一层的adaboost的训练了

注:这里的假正率的计算、负样本的补充我觉得是比较重要的

实际用的过程中,训练速度会比较慢,所以可以采用多线程的方式实现,至少可以在弱分类器的训练时使用多线程


0 0