浅析人脸检测之 Haar 分类器方法
由 于工作需要,我开始研究人脸检测部分的算法,这期间断断续续地学习 Haar 分类器
的训练以及检测过程,在这里根据各种论文、网络资源的查阅和对代码的理解 做一个简单
的总结。我试图概括性的给出算法的起源、全貌以及细节的来龙去脉,但是水平有限,只能
解其大概,希望对初学者起到帮助,更主要的是对我个人学习 的一次提炼。
一、Haar 分类器的前世今生
人脸检测属于计算机视觉的范畴,早期人们的主要研究方向是人脸识别,即根据人脸来
识别人物的身份,后来在复杂背景下的人脸检测需求越来越大,人脸检测也逐渐作为一个单
独的研究方向发展起来。
目前的人脸检测方法主要有两大类:基于知识和基于统计。
“基 于知识的方法主要利用先验知识将人脸看作器官特征的组合,根据眼睛、眉毛、
嘴巴、鼻子等器官的特征以及相互之间的几何位置关系来检测人脸。基于统计的方法 则将
人脸看作一个整体的模式——二维像素矩阵,从统计的观点通过大量人脸图像样本构造人脸
模式空间,根据相似度量来判断人脸是否存在。在这两种框架之下, 发展了许多方法。目
前随着各种方法的不断提出和应用条件的变化,将知识模型与统计模型相结合的综合系统将
成为未来的研究趋势。”(来自论文《基于 Adaboost 的人脸检测方法及眼睛定位算法研究》)
基于知识的人脸检测方法
模板匹配
人脸特征
形状与边缘
纹理特性
颜色特征
基于统计的人脸检测方法
主成分分析与特征脸
神经网络方法
支持向量机
隐马尔可夫模型
Adaboost 算法
本文中介绍的 Haar 分类器方法,包含了 Adaboost 算法,稍候会对这一算法做详细介绍。
所谓分类器,在这里就是指对人脸和非人脸进行分类的算法,在机器学习领域,很多算法都
是对事物进行分类、聚类的过程。OpenCV 中的 ml 模块提供了很多分类、聚类的算法。
注: 聚类和分类的区别是什么?一般对已知物体类别总数的识别方式我们称之为分类,并
且训练的数据是有标签的,比如已经明确指定了是人脸还是非人脸,这是一种有 监督学习。
也存在可以处理类别总数不确定的方法或者训练的数据是没有标签的,这就是聚类,不需要
学习阶段中关于物体类别的信息,是一种无监督学习。
其中包括 Mahalanobis 距离、K 均值、朴素贝叶斯分类器、决策树、Boosting、随机森
林、Haar 分类器、期望最大化、K 近邻、神经网络、支持向量机。
我 们要探讨的 Haar 分类器实际上是 Boosting 算法的一个应用,Haar 分类器用到了
Boosting 算法中的 AdaBoost 算法,只是把 AdaBoost 算法训练出的强分类器进行了级联,
并且在底层的特征提取中采用了高效率的矩形特征和积分图方法,这里涉及到的几个名词接
下来会具体讨论。
虽 说 haar 分类器采用了 Boosting 的算法,但在 OpenCV 中,Haar 分类器与 Boosting
OpenCV》中有这样的解释:“Haar 分类器,
没有采用同一套底层数据结构, 《Learning
它建立了 boost 筛选式级联分类器。它与 ML 库中其他部分相比,有不同的格局, 因为它是
在早期开发的,并完全可用于人脸检测。”
是的,在 2001 年,Viola 和 Jones 两位大牛发表了经典的
《Rapid Object Detection using a Boosted Cascade of Simple Features》【1】和
《Robust Real-Time Face Detection》【2】, 在 AdaBoost 算法的基础上,使用 Haar-like
小波特征和积分图方法进行人脸检测,他俩不是最早使用提出小波特征的,但是他们设计了
针对人脸检 测更有效的特征,并对 AdaBoost 训练出的强分类器进行级联。这可以说是人脸
检测史上里程碑式的一笔了,也因此当时提出的这个算法被称为 Viola- Jones 检测器。又
过了一段时间,Rainer
Maydt 两位大牛将这个检测器进行了扩
展【3】,最终形成了 OpenCV 现在的 Haar 分类器。之前我有个误区,以为 AdaBoost 算法就
是 Viola 和 Jones 搞出来的,因为网上讲 Haar 分类器的地方都在大讲特讲 AdaBoost,所
以我错觉了,后来理清脉络,AdaBoost 是 Freund 和 Schapire 在 1995 年提出的算法,是对
传统 Boosting 算法的一大提升。Boosting 算法的核心思想,是将弱学习方法提升成强学习
算法,也就是“三个臭皮匠顶一个诸葛亮”,它的理论基础来自于 Kearns 和 Valiant
牛的相关证明【4】,在此不深究了。反正我是能多简略就多简略的把 Haar 分类器的前世今
生说完鸟,得出的结论是,大牛们都是成对儿的。。。额,回到正题,Haar 分类
Lienhart 和 Jochen
器 =
Haar-like 特征 + 积分图方法 +
AdaBoost + 级联;
注:为何称其为 Haar-like?这个名字是我从网上看来的,《Learning
OpenCV》中文
版提到 Haar 分类器使用到 Haar 特征,但这种说法不确切,应该称为类 Haar 特征,Haar-like
就是类 Haar 特征的意思。
二、Haar 分类器的浅入浅出
之所以是浅入浅出是因为,我暂时深入不能,只是根据其他人的总结,我加以梳理归纳,
用自己的理解阐述出来,难免会有错误,欢迎指正。
Haar 分类器算法的要点如下:
① 使用 Haar-like 特征做检测。
② 使用积分图(Integral Image)对 Haar-like 特征求值进行加速。
③ 使用 AdaBoost 算法训练区分人脸和非人脸的强分类器。
④ 使用筛选式级联把强分类器级联到一起,提高准确率。
2.1 Haar-like 特征你是何方神圣?
一看到 Haar-like 特征这玩意儿就头大的人举手。好,很多人。那么我 先说下什么是特征,
我把它放在下面的情景中来描述,假设在人脸检测时我们需要有这么一个子窗口在待检测的
图片窗口中不断的移位滑动,子窗口每到一个位置, 就会计算出该区域的特征,然后用我
们训练好的级联分类器对该特征进行筛选,一旦该特征通过了所有强分类器的筛选,则判定
该区域为人脸。
那么这个特征如何表示呢?好了,这就是大牛们干的好事了。后人称这他们搞出来的这
些东西叫 Haar-Like 特征。
下面是 Viola 牛们提出的 Haar-like 特征。
下面是 Lienhart 等牛们提出的 Haar-like 特征。
这些所谓的特征不就是一堆堆带条纹的矩形么,到底是干什么用的?我这样给出解释,将上
面的任意一个矩形放到人脸区域上,然后,将白色区域的像素和减去黑色 区域的像素和,
得到的值我们暂且称之为人脸特征值,如果你把这个矩形放到一个非人脸区域,那么计算出
的特征值应该和人脸特征值是不一样的,而且越不一样越 好,所以这些方块的目的就是把
人脸特征量化,以区分人脸和非人脸。
为 了增加区分度,可以对多个矩形特征计算得到一个区分度更大的特征值,那么什么
样的矩形特征怎么样的组合到一块可以更好的区分出人脸和非人脸呢,这就是 AdaBoost 算
法要做的事了。这里我们先放下积分图这个概念不管,为了让我们的思路连贯,我直接开始
介绍 AdaBoost 算法。
2.2 AdaBoost 你给我如实道来!
本节旨在介绍 AdaBoost 在 Haar 分类器中的应用,所以只是描述了它在 Haar 分类器中
的特性,而实际上 AdaBoost 是一种具有一般性的分类器提升算法,它使用的分类器并不局
限某一特定算法。
上面说到利用 AdaBoost 算法可以帮助我们选择更好的矩阵特征组合,其实这里提到的
矩阵特征组合就是我们之前提到的分类器,分类器将矩阵组合以二叉决策树的形式存储起来。
我现在脑子里浮现了很多问题,总结起来大概有这么些个:
弱分类器和强分类器是什么?
弱分类器是怎么得到的?
强分类器是怎么得到的?
二叉决策树是什么?
要回答这一系列问题,我得跟你罗嗦一会儿了,这得从 AdaBoost 的身世说起。
2.2.1 AdaBoost 的身世之谜
关于 AdaBoost 的身世,我把相关英文文献从上世纪 80 年代一直下到 2001 年,我发现
我在短时间内没法读完,所以我只能尝试着从别人的总结中拼凑那些离散的片段,难免有误。
之前讲 Haar 分类器的前世今生也简单说过 AdaBoost 的身世,但是说的还不透。我比较
喜欢查算法的户口,所以新写了一章查了下去。
AdaBoost 的老祖宗可以说是机器学习的一个模型,它的名字叫
PAC(Probably Approximately
Correct)。
PAC 模型是计算学习理论中常用的模型,是 Valiant 牛在我还没出生的 1984 年提出来
的【5】,他认为“学习"是模式明显清晰或模式不存在时仍能获取知识的一种“过程”,并
给出了一个从计算角度来获得这种“过程"的方法,这种方法包括:
(1)适当信息收集机制的选择;
(2)学习的协定;
(3)对能在合理步骤内完成学习的概念的分类。
PAC 学习的实质就是在样本训练的基础上,使算法的输出以概率接近未知的目标概念。
PAC 学习模型是考虑样本复杂度(指学习器收敛到成功假设时至少所需的训练样 本数)和计
算复杂度(指学习器收敛到成功假设时所需的计算量)的一个基本框架,成功的学习被定义为
形式化的概率理论。(来自论文《基于 Adaboost 的 人脸检测方法及眼睛定位算法研究》)
简单说来,PAC 学习模型不要求你每次都正确,只要能在多项式个样本和多项式时间内
得到满足需求的正确率,就算是一个成功的学习。
基于 PAC 学习模型的理论分析,Valiant 牛 提出了 Boosting 算法【5】,Boosting
算法涉及到两个重要的概念就是弱学习和强学习,所谓的弱学习,就是指一个学习算法对一
组概念的识别率 只比随机识别好一点,所谓强学习,就是指一个学习算法对一组概率的识
别率很高。现在我们知道所谓的弱分类器和强分类器就是弱学习算法和强学习算法。弱学习
算法是比较容易获得的,获得过程需要数量巨大的假设集合,这个假设集合是基于某些简单
规则的组合和对样本集的性能评估而生成的,而强学习算法是不容易获得 的,然而,
Kearns 和 Valiant 两头牛提出了弱学习和强学习等价的问题 【6】 并证明了只要有足
够的数据,弱学习算法就能通过集成的方式生成任意高精度的强学习方法。这一证明使得
Boosting 有了可靠的理论基础,Boosting 算法成为了一个提升分类器精确性的一般性方法。
【4】
1990 年,Schapire 牛提出了第一个多项式时间的算法【7】,1 年后 Freund 牛又提
出了一个效率更高的 Boosting 算法【8】。然而,Boosting 算法还是存在着几个主要的问
题,其一 Boosting 算法需要预先知道弱学习算法学习正确率的下限即弱分类器的误差,其
二 Boosting 算法可能导致后来的训练过分集中于少数特别难区分的样本,导致不稳定。针
对 Boosting 的若干缺陷,Freund 和 Schapire 牛于 1996 年前后提出了一个实际可用的自
适应 Boosting 算法 AdaBoost【9】,AdaBoost 目前已发展出了大概四种形式的算法,
Discrete AdaBoost(AdaBoost.M1)、Real AdaBoost、LogitBoost、gentle
AdaBoost,
本文不做一一介绍。至此,AdaBoost 的身世之谜就这样揭开鸟。同时弱分类器和强分类器
是什么的问题也解释清楚了。剩下 3 个问题,我们先看一下,弱分类器是如何得到的。
2.2.2 弱分类器的孵化
最 初的弱分类器可能只是一个最基本的 Haar-like 特征,计算输入图像的 Haar-like
特征值,和最初的弱分类器的特征值比较,以此来判断输入图像 是不是人脸,然而这个弱
分类器太简陋了,可能并不比随机判断的效果好,对弱分类器的孵化就是训练弱分类器成为
最优弱分类器,注意这里的最优不是指强分类 器,只是一个误差相对稍低的弱分类器,训
练弱分类器实际上是为分类器进行设置的过程。至于如何设置分类器,设置什么,我们首先
分别看下弱分类器的数学结构 和代码结构。
数学结构
一个弱分类器
由子窗口图像 x,一个特征 f,指示不等号方向的 p 和阈值
组成。P 的作用是控制不等式的方向,使得不等式都是<号,形式方便。
代码结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
*
*/
CART
classifier
typedef
{
struct
CvCARTHaarClassifier
count;
CV_INT_HAAR_CLASSIFIER_FIELDS()
int
int*
CvTHaarFeature*
compidx;
feature;
fastfeature;
CvFastHaarFeature*
float*
threshold;
int*
int*
float*
left;
right;
val;
}
CvCARTHaarClassifier;
代码结构中的 threshold 即代表数学结构中的 阈值。
这个阈值究竟是干什么的?我们先了解下 CvCARTHaarClassifier 这个结构,注意 CART
这个词,它是一种二叉决策树,它的提出者 Leo
什么是决策树?我如果细讲起来又得另起一章,我只简略介绍它。
Breiman 等牛称其为“分类和回归树(CART)”。
“机器学习中, 决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映
射关系。树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个
叶结 点则对应从根节点到该叶节点所经历的路径所表示的对象的值。决策树仅有单一输出,
若欲有复数输出,可以建立独立的决策树以处理不同输出。从数据产生决策树 的机器学习
技术叫做决策树学习, 通俗说就是决策树。”(来自《维基百科》)
决策树包含:分类树,回归树,分类和回归树(CART),CHAID 。
分类和回归的区别是,分类是当预计结果可能为两种类型(例如男女,输赢等)使用的概
念。 回归是当局域结果可能为实数(例如房价,患者住院时间等)使用的概念。
决策树用途很广可以分析因素对事件结果的影响(详见维基百科),同时也是很常用的
分类方法,我举个最简单的决策树例子,假设我们使用三个 Haar-like 特征 f1,f2,f3 来
判断输入数据是否为人脸,可以建立如下决策树:
可以看出,在分类的应用中,每个非叶子节点都表示一种判断,每个路径代表一种判断
的输出,每个叶子节点代表一种类别,并作为最终判断的结果。
一个弱分类器就是一个基本和上图类似的决策树,最基本的弱分类器只包含一个
Haar-like 特征,也就是它的决策树只有一层,被称为树桩(stump)。
最重要的就是如何决定每个结点判断的输出,要比较输入图片的特征值和弱分类器中特
征,一定需要一个阈值,当输入图片的特征值大于该阈值时才判定其为人脸。训练最优弱分
类器的过程实际上就是在寻找合适的分类器阈值,使该分类器对所有样本的判读误差最低。
具体操作过程如下:
1)对于每个特征 f,计算所有训练样本的特征值,并将其排序。
扫描一遍排好序的特征值,对排好序的表中的每个元素,计算下面四个值:
全部人脸样本的权重的和 t1;
全部非人脸样本的权重的和 t0;
在此元素之前的人脸样本的权重的和 s1;
在此元素之前的非人脸样本的权重的和 s0;
2)最终求得每个元素的分类误差
在表中寻找 r 值最小的元素,则该元素作为最优阈值。有了该阈值,我们的第一个最优
弱分类器就诞生了。
在这漫长的煎熬中,我们见证了一个弱分类器孵化成长的过程,并回答了如何得到弱分
类器以及二叉决策树是什么。最后的问题是强分类器是如何得到的。
2.2.3 弱分类器的化蝶飞
首先看一下强分类器的代码结构:
stage
classifier
*/
CvStageHaarClassifier
struct
internal
/*
typedef
{
1
2
3
4
5
6
7
8 }CvStageHaarClassifier;
count;
CV_INT_HAAR_CLASSIFIER_FIELDS()
int
float
CvIntHaarClassifier**
threshold;
classifier;
internal
weak
classifier*/
struct
CvIntHaarClassifier
/*
typedef
{
CV_INT_HAAR_CLASSIFIER_FIELDS()
}
CvIntHaarClassifier;
这里要提到的是 CvIntHaarClassifier 结构: 它 就相当于一个接口类,当
然是用 C 语言模拟的面向对象思想,利用 CV_INT_HAAR_CLASSIFIER_FIELDS()这个宏让弱分
类 CvCARTHaarClassifier 强分类器和 CvStageHaarClassifier 继承于
CvIntHaarClassifier。
强分类器的诞生需要 T 轮的迭代,具体操作如下:
1. 给定训练样本集 S,共 N 个样本,其中 X 和 Y 分别对应于正样本和负样本; T 为训
练的最大循环次数;
2. 初始化样本权重为 1/N ,即为训练样本的初始概率分布;
3. 第一次迭代训练 N 个样本,得到第一个最优弱分类器,步骤见 2.2.2 节
4. 提高上一轮中被误判的样本的权重;
5. 将新的样本和上次本分错的样本放在一起进行新一轮的训练。
6. 循环执行 4-5 步骤,T 轮后得到 T 个最优弱分类器。
7.组合 T 个最优弱分类器得到强分类器,组合方式如下: