基于Haar特征的Adaboost级联人脸检测分类器
基于Haar特征的Adaboost级联人脸检测分类器,简称haar分类器。通过这个算法的名字,我们可以看到这个算法其实包含了几个关键点:Haar特征、Adaboost、级联。理解了这三个词对该算法基本就掌握了。
1        算法要点
Haar分类器 = Haar-like特征 + 积分图方法 + AdaBoost +级联;
Haar分类器算法的要点如下:
a)        使用Haar-like特征做检测。
b)       使用积分图(IntegralImage)对Haar-like特征求值进行加速。
c)        使用AdaBoost算法训练区分人脸和非人脸的强分类器。
d)       使用筛选式级联把分类器级联到一起,提高准确率。
2        历史
  在2001年,Viola和Jones两位大牛发表了经典的《Rapid Object Detectionusing a Boosted Cascade of Simple Features》和《Robust Real-Time Face Detection》,在AdaBoost算法的基础上,使用Haar-like小波特征和积分图方法进行人脸检测,他俩不是最早使用提出小波特征的,但是他们设计了针对人脸检测更有效的特征,并对AdaBoost训练出的强分类器进行级联。这可以说是人脸检测史上里程碑式的一笔了,也因此当时提出的这个算法被称为Viola-Jones检测器。又过了一段时间,RainerLienhart和Jochen
Maydt两位大牛将这个检测器进行了扩展,最终形成了OpenCV现在的Haar分类器。
AdaBoost是Freund和Schapire在1995年提出的算法,是对传统Boosting算法的一大提升。Boosting算法的核心思想,是将弱学习方法提升成强学习算法,也就是“三个臭皮匠顶一个诸葛亮”
3        Haar特征
  什么是特征,特征就是分类器的输入。把它放在下面的情景中来描述,假设在人脸检测时我们需要有这么一个子窗口在待检测的图片窗口中不断的移位滑动,子窗口每到一个位置,就会计算出该区域的特征,然后用我们训练好的级联分类器对该特征进行筛选,一旦该特征通过了所有强分类器的筛选,则判定该区域为人脸。
 clc;
clear;
close all; % Haar-like特征矩形计算 board = % 检测窗口宽度
num = % 检测窗口分划数 show = ; % 1为作图
time = 0.001; % 作图间隔 %% if mod(board,num)~=
error('检测窗口宽度必须是分划数的整数倍')
else
delta = board/num % 滑动步进值 
end % Haar特征1:左白,右黑,(s,t)=(,) s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数 % '---- Haar特征1:左白,右黑,(s,t)=(1,2) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口宽
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 列方向移动个数 Px0 = [ r]; % 矩形坐标初始化
Py0 = [ c/ c];
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+; if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(Px,repmat(Py',1,2),'r','LineWidth',5)
plot(repmat(Px,,),repmat([Py() Py(end)]',1,2),'r','LineWidth',5); hold off
pause(time)
end end
end end
end
NUM
%% Haar特征2:上白,下黑,(s,t)=(,)
s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征2:上白,下黑,(s,t)=(2,1) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口宽
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 列方向移动个数 Px0 = [ r/ r]; % 矩形坐标初始化
Py0 = [ c];
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+;
if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(repmat(Px,,),repmat(Py',1,length(Px)),'r','LineWidth',3);
plot(repmat([Px() Px(end)]',1,2),repmat(Py,2,1),'r','LineWidth',3); hold off
pause(time)
end end
end end
end
NUM
%% Haar特征3:左右白,中间黑,(s,t)=(,)
s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征3:左右白,中间黑,(s,t)=(1,3) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口宽
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 列方向移动个数 Px0 = [ r]; % 矩形坐标初始化
Py0 = [ c/ c*/ c];
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+; if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(Px,repmat(Py',1,2),'r','LineWidth',5)
plot(repmat(Px,,),repmat([Py() Py(end)]',1,2),'r','LineWidth',5); hold off
pause(time)
end
end
end end
end
NUM
%% Haar特征4:左右白,中间黑(2倍宽度),(s,t)=(,)
s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征4:左右白,中间黑(2倍宽度),(s,t)=(1,4) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口宽
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 列方向移动个数 Px0 = [ r]; % 矩形坐标初始化
Py0 = [ c/ c*/ c];
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+; if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(Px,repmat(Py',1,2),'r','LineWidth',5)
plot(repmat(Px,,),repmat([Py() Py(end)]',1,2),'r','LineWidth',5); hold off
pause(time)
end end
end end
end
NUM
%% Haar特征5:上下白,中间黑,(s,t)=(,)
s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征5:上下白,中间黑,(s,t)=(3,1) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口宽
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 列方向移动个数 Px0 = [ r/ r*/ r]; % 矩形坐标初始化
Py0 = [ c];
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+; if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(repmat(Px,,),repmat(Py',1,length(Px)),'r','LineWidth',3);
plot(repmat([Px() Px(end)]',1,2),repmat(Py,2,1),'r','LineWidth',3); hold off
pause(time)
end end
end end
end
NUM
%% Haar特征6:上下白,中间黑(2倍宽度),(s,t)=(,)
s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征6:上下白,中间黑(2倍宽度),(s,t)=(4,1) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口宽
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 列方向移动个数 Px0 = [ r/ r*/ r]; % 矩形坐标初始化
Py0 = [ c];
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+;
if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(repmat(Px,,),repmat(Py',1,length(Px)),'r','LineWidth',3);
plot(repmat([Px() Px(end)]',1,2),repmat(Py,2,1),'r','LineWidth',3); hold off
pause(time)
end end
end end
end
NUM
%% Haar特征7:左上右下白,其它黑,(s,s)=(,) s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征7:左上右下白,其它黑,(s,s)=(2,2) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口高
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 行方向移动个数 Px0 = [ r/ r]; % 矩形坐标初始化
Py0 = [ c/ c]; % 矩形坐标初始化
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+; if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(repmat(Px,,),repmat(Py',1,length(Px)),'r','LineWidth',3);
plot(repmat([Px() Px(end)]',1,3),repmat(Py,2,1),'r','LineWidth',3); hold off
pause(time)
end end
end end
end
NUM
%% Haar特征8:四周白,中间黑,(s,s)=(,)
s = ;
t = ;
R = s:s:floor(num/s)*s; % Haar窗口高
C = t:t:floor(num/t)*t; % Haar窗口宽
NUM = ; % Haar特征总数
'---- Haar特征8:四周白,中间黑,(s,s)=(3,3) ---'
for I = :length(R)
for J = :length(C) r = R(I)*delta; % Haar窗口高
c = C(J)*delta; % Haar窗口高
nr = num-R(I)+; % 行方向移动个数
nc = num-C(J)+; % 行方向移动个数 Px0 = [ r/ r*/ r]; % 矩形坐标初始化
Py0 = [ c/ c*/ c]; % 矩形坐标初始化
for i = :nr
for j = :nc
Px = Px0+(i-)*delta; % 滑动取点
Py = Py0+(j-)*delta;
NUM = NUM+; if show
plot([ board],repmat((:delta:board)',1,2),'k'); hold on;
plot(repmat((:delta:board)',1,2),[0 board],'k'); axis tight; axis square;
title('Haar矩形遍历演示');xlabel('x');ylabel('y'); plot(repmat(Px,,),repmat(Py',1,length(Px)),'r','LineWidth',3);
plot(repmat([Px() Px(end)]',1,4),repmat(Py,2,1),'r','LineWidth',3); hold off
pause(time)
end end
end end
end
NUM

运行效果部分动态图如下

  那么这个特征如何表示呢?好了,这就是大牛们干的好事了。后人称这他们搞出来的这些东西叫Haar-Like特征。
Viola大牛在[1]中提出的haar特征如下:

Rainer大牛改进了这些特征,提出了更多的haar特征。如下图所示:

  这些所谓的特征不就是一堆堆带条纹的矩形么,到底是干什么用的?我这样给出解释,将上面的任意一个矩形放到人脸区域上,然后,将白色区域的像素和减去黑色区域的像素和,得到的值我们暂且称之为人脸特征值,如果你把这个矩形放到一个非人脸区域,那么计算出的特征值应该和人脸特征值是不一样的,而且越不一样越好,所以这些方块的目的就是把人脸特征量化,以区分人脸和非人脸。

haar-like特征的特点
  Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。
    通过改变特征模板的大小和位置,可在图像子窗口中穷举出大量的特征。上图的特征模板称为“特征原型”;特征原型在图像子窗口中扩展(平移伸缩)得到的特征称为“矩形特征”;矩形特征的值称为“特征值”。
4       积分图

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

积分图主要的思想是将图像从起点开始到各个点所形成的矩形区域像素之和作为一个数组的元素保存在内存中,当要计算某个区域的像素和时可以直接索引数组的元素,不用重新计算这个区域的像素和,从而加快了计算(这有个相应的称呼,叫做动态规划算法)。积分图能够在多种尺度下,使用相同的时间(常数时间)来计算不同的特征,因此大大提高了检测速度。

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

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

积分图构建算法:

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就构造好了。

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

          

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

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

而Haar-like特征值无非就是两个矩阵像素和的差,同样可以在常数时间内完成。所以矩形特征的特征值计算,只与此特征矩形的端点的积分图有关,所以不管此特征矩形的尺度变换如何,特征值的计算所消耗的时间都是常量。这样只要遍历图像一次,就可以求得所有子窗口的特征值。

5       Adaboost算法
本节旨在介绍AdaBoost在Haar分类器中的应用,所以只是描述了它在Haar分类器中的特性,而实际上AdaBoost是一种具有一般性的分类器提升算法,它使用的分类器并不局限某一特定算法。
[1]中给出的Adaboost算法流程如下图。

 

由adaboost在haar特征上构建分类器的流程可知,adaboost算法就是构建多个简单的分类器,每个简单的分类器都建立在之前分类器的基础上(对之前分类器分错了的样例提高其权重),然后将这些分类器加权,得到一个强大的分类器。
Adaboost的每一步训练出的分类器,如下图所示。其中,f表示特征的值,theta表示阈值,p则表示不等式的方向。这样的一个分类器就是基于一个特征的弱分类器。
 

更进一步,adaboost的一般算法框架如下。可以看到,Discrete Adaboost和GentleAdaboost在分类器的计算上和权重的更新上是有差别的。还有一种是RealAdaboost,即分类器输出的是一个概率而不只是+1与-1。[3]中就比较了这三种Adaboost的变种的效果。

 

有关adaboost算法的实现可以参考这篇博客:https://blog.csdn.net/u012679707/article/details/80369772

6        级联
什么是级联分类器?级联分类器就是如下图所示的一种退化了的决策树。为什么说是退化了的决策树呢?是因为一般决策树中,判断后的两个分支都会有新的分支出现,而级联分类器中,图像被拒绝后就直接被抛弃,不会再有判断了。

级联强分类器的策略是,将若干个强分类器由简单到复杂排列,希望经过训练使每个强分类器都有较高检测率,而误识率可以放低,比如几乎99%的人脸可以通过,但50%的非人脸也可以通过,这样如果有20个强分类器级联,那么他们的总识别率为0.99^20约等于98%,错误接受率也仅为0.5^20约等于0.0001%。这样的效果就可以满足现实的需要了。文献[1]中给出了一种由简单到复杂设计级联分类器的方法,那就是添加特征法,对于第一个分类器,只用少数几个特征,之后的每个分类器都在上一个的基础上添加特征,直到满足该级的要求。
训练级联分类器的目的就是为了检测的时候,更加准确,这涉及到Haar分类器的另一个体系,检测体系,检测体系是以现实中的一幅大图片作为输入,然后对图片中进行多区域,多尺度的检测,所谓多区域,是要对图片划分多块,对每个块进行检测,由于训练的时候用的照片一般都是20*20左右的小图片,所以对于大的人脸,还需要进行多尺度的检测,多尺度检测机制一般有两种策略,一种是不改变搜索窗口的大小,而不断缩放图片,这种方法显然需要对每个缩放后的图片进行区域特征值的运算,效率不高,而另一种方法,是不断初始化搜索窗口size为训练时的图片大小,不断扩大搜索窗口,进行搜索,解决了第一种方法的弱势。

7        总结
基于Haar特征的Adaboost级联分类器,在人脸的识别效果上并没有比其他算法高,其亮点在于检测速度。而速度的提升,有如下几方面的因素。第一:使用的特征简单,haar特征只需计算像素的和就可以了。第二:即便是如此简单的特征,还添加了积分图进行加速。第三,级联分类器的设定,使得大量的没有人脸的子窗口被抛弃
 

基于Haar特征的Adaboost级联人脸检测分类器的更多相关文章

  1. 照片美妆---基于Haar特征的Adaboost级联人脸检测分类器

    原文:照片美妆---基于Haar特征的Adaboost级联人脸检测分类器 本文转载自张雨石http://blog.csdn.net/stdcoutzyx/article/details/3484223 ...

  2. 基于Haar特征Adaboost人脸检测级联分类

    基于Haar特征Adaboost人脸检测级联分类 基于Haar特征Adaboost人脸检测级联分类,称haar分类器. 通过这个算法的名字,我们能够看到这个算法事实上包括了几个关键点:Haar特征.A ...

  3. 【计算机视觉】如何使用opencv自带工具训练人脸检测分类器

    前言 使用opencv自带的分类器效果并不是很好,由此想要训练自己的分类器,正好opencv有自带的工具进行训练.本文就对此进行展开. 步骤 1.查找工具文件: 2.准备样本数据: 3.训练分类器: ...

  4. OpenCV中基于Haar特征和级联分类器的人脸检测

    使用机器学习的方法进行人脸检测的第一步需要训练人脸分类器,这是一个耗时耗力的过程,需要收集大量的正负样本,并且样本质量的好坏对结果影响巨大,如果样本没有处理好,再优秀的机器学习分类算法都是零. 今年3 ...

  5. HAAR与DLib的实时人脸检测之实现与对比

    人脸检测方法有许多,比如opencv自带的人脸Haar特征分类器和dlib人脸检测方法等. 对于opencv的人脸检测方法,优点是简单,快速:存在的问题是人脸检测效果不好.正面/垂直/光线较好的人脸, ...

  6. opencv人脸检测分类器训练小结

    这两天在初学目标检测的算法及步骤,其中人脸检测作为最经典的算法,于是进行了重点研究.该算法最重要的是建立人脸检测分类器,因此我用了一天的时间来学习分类器的训练.这方面的资料很多,但是能按照一个资料运行 ...

  7. 基于Adaboost的人脸检测算法

    AdaBoost算法是一种自适应的Boosting算法,基本思想是选取若干弱分类器,组合成强分类器.根据人脸的灰度分布特征,AdaBoost选用了Haar特征[38].AdaBoost分类器的构造过程 ...

  8. 基于AdaBoost的人脸检测

    原地址:http://blog.csdn.net/celerychen2009/article/details/8839097 人脸检测和人脸识别都是属于典型的机器学习的方法,但是他们使用的方法却相差 ...

  9. OpenCV学习记录(二):自己训练haar特征的adaboost分类器进行人脸识别 标签: 脸部识别opencv 2017-07-03 21:38 26人阅读

    上一篇文章中介绍了如何使用OpenCV自带的haar分类器进行人脸识别(点我打开). 这次我试着自己去训练一个haar分类器,前后花了两天,最后总算是训练完了.不过效果并不是特别理想,由于我是在自己的 ...

随机推荐

  1. git版本控制-详细操作

    - git,软件帮助使用者进行版本的管理 阶段一git 命令: git init 初始化 git config --global user.email "you@example.com&qu ...

  2. Centos7.2:搭建Ceph管理系统Inscope

    0.引言 好几天没有更新博客了,这几天分配有任务:calamari与inscope管理系统调研.下面就管理系统的环境搭建做一个总结,总结一下搭建流程以及搭建过程中遇到的一些问题.calcamari的搭 ...

  3. js中的class

    js中的class 类写法 class SuperClass { constructor(option) { this.a = option; } fn() { console.log(this.b) ...

  4. Protege4.3使用入门(一)

    1.下载 下载地址http://protege.stanford.edu/download/protege/4.3/installanywhere/Web_Installers/,目前版本信息Prot ...

  5. spring cloud微服务搭建(一)

    martin fowler大神提出微服务的概念后,各种微服务的技术满天飞,现在用的比较多的是spring cloud和阿里的dubbo,由于dubbo 在16年10月份就停止更新了,不过好像前些天又更 ...

  6. jenkins 学习记录2

    主题 在之前的学习中(http://www.cnblogs.com/abcwt112/p/6274575.html)我已经学会怎么打包了..这篇文章记录分享我学习如何利用jenkins将打完的包发布到 ...

  7. mysql多个字段拼接

    Mysql的查询结果行字段拼接,可以用下面两个函数实现: 1. concat函数 mysql') from test ; +---------------------+ ') | +--------- ...

  8. System.Security.Cryptography.CryptographicException: 出现了内部错误。

    引用:http://www.cnblogs.com/ithome8/p/5189926.html 我总结了一下出现证书无法加载的原因有以下三个 1.证书密码不正确,微信证书密码就是商户号 解决办法:请 ...

  9. PHP内核介绍及扩展开发指南—Extensions 的编写

    Extensions 的编写 理解了这些运行机制以后,本章着手介绍Extensions 的编写,但凡写程序的人都知道hello world,那好,就从hello world开始. 1.1Hello W ...

  10. AOP基本概念、AOP底层实现原理、AOP经典应用【事务管理、异常日志处理、方法审计】

    1 什么是AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...