由于课程学习的原因,最近一个月一直在了解AdaBoost的原理及实现方法。这算是我学习的第一个比较复杂的算法,最后也没有完全实现,只是实现了一个类似与教学用的demo。但这个过程也得益于网络上各路大神们经典博文的参考,才能够更深入一点理解AdaBoost。下面列出相关参考论文以及博文链接。其中也会添加一些自己在学习中对该算法细节的一些思考,如有错误尽请拍砖指正!主要参考论文:
《Rapid Object Detection using a Boosted Cascade of Simple Features》Paul Viola & Michael Jones
《Robust Real-Time Face Detection》Paul Viola & Michael Jones
在这两位大牛写的论文中,使用了Haar-Like Features 通过积分图的方法进行了人脸特征的计算;通过Haar-Like Features的特征值,通过设计、构建、和选择弱分类器,找出其中产生最小误差的特征(即弱分类器),在每一轮AdaBoost的权值更新中进行样本分布,从而实现了增加误分类样本的权值,减少正确分类的样本的权值。这里弱分类器的设计使用了树桩(stump)的方法,类似于decision tree, 但是每一层只有一个节点,即底层节点,故称为树桩。论文的最后一部分,构建了级联算法结构(Cascade Structure)。大大增加了算法运行的速度,实现实时检测。同时分析了增加级联结构所需要折衷的方法。因为级联结构中:其一,高的阈值,得到低的误检率和低的检测率;低的阈值导致高的误检率和高的检测率,据说这是由于级联分类器中阈值的设置引起的。其二,如果不断增加特征的数量,就会导致计算量的增加,实现实时效果困难;如果减少特征数量,就会导致检测率下降,这也需要折衷。总结为三大板块:Haar-Like Features + 积分图、AdaBoost算法及弱分类器的设计、级联分类器。
下面这篇论文中把基本的Haar-Like特征进行了扩展,提出了旋转和中心Haar-Like特征,以及检测窗中特征数量的计算公式:
《An Extended Set of Haar-Like Features for Rapid Object Detection》R.Leinhart
积分图的计算: 点这里
其他论文: 《Integral Image for Block Matching》 Gabriele Facciolo,Nicolas Limare, Enric Meinhardt
对整篇论文,Haar-like特征,积分图,AdaBoost算法,级联分类器总结的比较好的博文,请看这位大神: 点这里 这篇博文中也有Haar-like特征的详细计算过程
专门讲AdaBoost算法,一直到误差界的理解,请看这位大神:点这里 通过公式推导,理解算法也很重要
这篇博文中介绍了很多关于Boosting、AdaBoost、Haar分类器等很多背景内容,如果需要了解的,可以点这里
下面我将结合自己的学习过程谈谈积分图和AdaBoost:
很多博文中提出了很多关于积分图的计算方法,称为快速积分图计算的方法,有时间可以去学习一下,因为这段时间的关系,确实没来得及学习。下面贴出我根据论文写的可能没考虑速度的积分图计算方法,适合于初学者。由于在积分图中得到图像信息的计算时间是常数,速度很快,应该在以后的很多算法和领域都会用到这个概念吧!
void integralImage(long **srcImg, long **integralImg, int height, int width)
{
integralImg[0][0] = srcImg[0][0];
char j,i;
long s; //scalar accumulator
for(j = 1; j < height; j++)
{
integralImg[j][0] = integralImg[j-1][0] + srcImg[j][0]; //the integral for only first column
}
for(i = 1; i < width; i++)
{
s = srcImg[0][i];
integralImg[0][i] = integralImg[0][i-1] + s; //the integral for only first row
for(j = 1; j < height; j++)
{
s = s + srcImg[j][i];
integralImg[j][i] = integralImg[j][i-1] + s;
}
}
}
1、怎么计算最小(或者满足要求)的误差,确定那个Haar-Like特征的阈值?
2、怎么根据选择的阈值,分类效果等确定最好的那个弱分类器(即选择哪个特征)?
首先假设有一个训练样本集{x1,x2,x3,...xm},为了更方便理解,可以假设这些样本都是24 x 24的样本图像,当然这些都是被标定(label)过的。每幅图像内有特征{h1,h2,h3,...,hn},特征数量很多,n也许很大,根据前面的参考博文及参考论文中可以知道,这里假设的24 x 24图片中有160,000个特征。因为每个样本大小相同,在同一个样本中,同一类特征对应不同的位置,被当做是不同特征。既然大小相同,那么可以认为在每个样本之间,相对应的位置会出现相同的特征,即使特征数量一样,但是特征值不一样。对上图的整个过程,我的理解可以用下面一个类似矩阵的来表示:
这一堆数据表示:用每个特征h去处理每一幅图像(即训练样本),然后得到特征值,所以每一列代表同一个特征处理所有样本的特征值。上面的数据有m行,n列。然后根据误差e定义的公式,遍历每一列数据,得到每一列的一系列误差值,在这中间寻找最小的误差值作为这一列的最优误差,同时可以通过这个最优误差值确定这一列的最优阈值。这样我们就得到了n个代表每一列的最优误差。再次,在这n个每一列的最优误差集中,寻找最小误差。这样我们就能选择产生这个最小误差的特征,即诞生了第一个弱分类器。对于这个理解,同时还参考了《基于AdaBoost的人脸检测方法及眼睛定位算法研究》 龙伶敏。
下面贴出Matlab的弱分类器设计代码,基于某位网友的idea,自己做了一些修改,感谢那位网友。
function [thresh,parity,err,finalTarget] = weakClassifier(srcData,weight,target,dataNum)
%
% srcData -- feature values
% weight -- the weight of weak classifier
% target -- the original labele array of training samples
% dataNum -- the number of samples
%
posWeight = 0;
negWeight = 0;
for i = 1: dataNum
if target(i) == 1
posWeight = weight(i) + posWeight; % the total sum of positive example weights T+
elseif target(i) == 0
negWeight = weight(i) + negWeight; % the total sum of negative example weights T-
else
disp('the weakClassifier is wrong!');
end
end
[sortData,index] = sort(srcData);
lposWeight = 0; % sum of positive weights below the current example S+
lnegWeight = 0; % sum of negative weights below the current example S-
bestErr = 0.5;
bestParity = 0;
bestThreshold = -1;
%the process of creating weak classifiers
for i = 2 : dataNum
if target(index(i - 1)) == 1
lposWeight = weight(index(i - 1)) + lposWeight;
else
lnegWeight = weight(index(i - 1)) + lnegWeight;
end
if sortData(i) ~= sortData(i - 1)
if (lposWeight + negWeight - lnegWeight) < bestErr
bestErr = lposWeight + negWeight - lnegWeight;
bestParity = -1;
bestThreshold = (sortData(i) + sortData(i - 1))/2;
elseif (lnegWeight + posWeight - lposWeight) < bestErr
bestErr = lnegWeight + posWeight - lposWeight;
bestParity = 1;
bestThreshold = (sortData(i) + sortData(i - 1))/2;
end
end
end
finalTarget = zeros(1,dataNum); % weak classifier with lowest error
thresh = bestThreshold;
parity = bestParity;
err = bestErr;
for i = 1 : dataNum
if parity * srcData(i) < parity * thresh;
finalTarget(i) = 1;
else
finalTarget(i) = 0;
end
end
最后,如果你对本文有意见或者建议,请及时留言,大家一起多多交流!