http://lib.csdn.net/article/opencv/29324(原文地址)
前一篇文章分析了Haar特征,包括Haar特征生成、特征值计算和含义。这一篇则主要分析一下2个内容:
1. OpenCV中的Adaboost级联分类器的结构,包括强分类器和弱分类器的形式;
2. opencv自带的XML分类器中各项参数的含义,如internalNodes和leafValues标签里面的一大堆数字的意义。
下面进入正题。
-------------------------------------------
缩进众所周知,OpenCV中的Adaboost级联分类是树状结构,如图1,其中每一个stage都代表一级强分类器。当检测窗口通过所有的强分类器时才被认为是目标,否则拒绝。实际上,不仅强分类器是树状结构,强分类器中的每一个弱分类器也是树状结构。
![](https://img-blog.csdn.net/20151007151312374?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图1 强分类器和弱分类器示意图
(这张图有笔误,应该是stage0,stage1,...,stageN-1,各位看官理解就好)
缩进这篇文章将结合OpenCV-2.4.11中自带的haarcascade_frontalface_alt2.xml文件介绍整个级联分类器的结构。需要说明,自从2.4.11版本后所有分类器都被替换成新式XML,所以本文介绍新式XML结构。
1 XML的头部
缩进在了解OpenCV分类器结构之前,先来看看存储分类器的XML文件中有什么。图2中注释了分类器XML文件头部信息,括号中的参数为opencv_traincascade.exe训练程序对应参数,即训练时设置了多少生成的XML文件对应值就是多少(如果不明白,可以参考我的前一篇文章)。
![](https://img-blog.csdn.net/20170517003412862)
图2 分类器XML文件头部含义
其中<features>标签存储了所有的Haar特性,在本系列文章一中有讲解。
2 弱分类器结构
缩进之前看到有一部分文章将Haar特征和弱分类器的关系没有说清楚,甚至有些还把二者弄混了。其实Haar特征和弱分类器之间的关系很简单:
一个完整的弱分类器包括:
1.若干个Haar特征 + 和Haar特征数量相等的弱分类器阈值
2. 若干个leftValue
3. 若干个rightValue
缩进这些元素共同构成了弱分类器,缺一不可。haarcascade_frontalface_alt2.xml的弱分类器Depth=2,包含了2种形式,如图3。图3中的左边形式包含2个Haar特征、1个leftValue、2个rightValue和2个弱分类器阈(t1和t2);右边形式包括2个Haar特征、2个leftValue、1个rightValue和2个弱分类器阈值。
图3 Depth=2的树状弱分类器示意图
缩进
看图3应该明白了弱分类器的大致结构,接下来我们了解树状弱分类器是如何工作的。还是以图3左边的形式为例:
- 计算第一个Haar特征的特征值haar1,与第一个弱分类器阈值t1对比,当haar1<t1时,进入步骤2;当haar1>t1时,该弱分类器输出rightValue2并结束。
- 计算第二个Haar特征值haar2,与第二个弱分类器阈值t2对比,当haar2<t2时输出leftValue;当haar2>t2时输出rightValue1。
缩进即通过上述步骤计算弱分类器输出值,这与OpenCV的cascadedetect.hpp文件中的predictOrdered()函数代码对应(这里简单解释一下,在OpenCV中所有弱分类器的leftValue和rightValue都依次存储在一个一维数组中,代码中的leafOfs表示当前弱分类器中leftValue和rightValue在该数组中存储位置的偏移量,idx表示在偏移量leafOfs基础上的leftValue和rightValue值的索引,cascadeLeaves[leafOfs - idx]就是该弱分类器的输出):
- do
- {
- CascadeClassifier::Data::DTreeNode& node = cascadeNodes[root + idx];
- double val = featureEvaluator(node.featureIdx);
- idx = val < node.threshold ? node.left : node.right;
- }
- while( idx > 0 );
- sum += cascadeLeaves[leafOfs - idx];
看到这里,你应该明白了弱分类器的工作方式,即通过计算出的Haar特征值与弱分类器阈值对比,从而选择最终输出leftValue和rightValue值中的哪一个。
缩进那么第三个问题来了,这些Haar特征、leftValue、rightValue和弱分类器阈值t都是如何存储在xml文件中的?不妨来看haarcascade_frontalface_alt2.xml文件中的第一级的第三个弱分类器,如图4。图4中的弱分类器恰好是图3中左边类型,包含了<internalNodes>和<leafValues>两个标签。其中<leafValues>标签中的3个浮点数由左向右依次是rightValue2、leftValue和rightValue1;而<internalNodes>中有6个整数和2个浮点数,其中2个浮点数依次分别是弱分类器阈值t1和t2,剩下的6个整数容我慢慢分解。
缩进
首先来看两个浮点数前的整数,即4和5。这两个整数用于标示所属本弱分类器Haar特征存储在<features>标签中的位置。比如数值4表示该弱分类器的haar1特征存储在xml文件下面<features>标签中第4个位置,即为:
缩进
而<internalNodes>的其他4个整数1、0和-1
、
-2则用于控制弱分类器树的形状。在运行时,OpenCV会把1赋值给当前的node.left,并把0赋值给node.right(请注意do-while代码中的条件,只有idx<=0时才停止循环,参考图3应该可以理解这4个整数的含义)。如此,OpenCV通过这些巧妙的数值和结构,控制了整个分类器的运行(当然我举的例子alt2的弱分类器树深度为2,相对比较复杂,其他如alt等Depth=1的分类器则更加简单)。
可以看到,每个弱分类器内部都是类似于这种树状的“串联”结构,所以我称其为
“
串联组成的的弱分类器
”
。
图4 OpenCV弱分类器运行示意图
缩进上文为了深入分析选用了Depth=2的弱分类器。而Depth=1(如haarcascade_frontalface_alt.xml)类型的stump弱分类器,结构更加简单且运行方式对比可知,不在赘述。
图5 Depth=1的stump弱分类器示意图
3 强分类器结构
缩进在OpenCV中,强分类器是由多个弱分类器“并列”构成,即强分类器中的弱分类器是两两相互独立的。在检测目标时,每个弱分类器独立运行并输出cascadeLeaves[leafOfs - idx]值,然后把当前强分类器中每一个弱分类器的输出值相加,即:
- sum += cascadeLeaves[leafOfs - idx];
图6 OpenCV强分类器运行示意图
缩进之后与本级强分类器的stageThreshold阈值对比,当且仅当结果sum>stageThreshold时,认为当前检测窗口通过了该级强分类器。当前检测窗口通过所有强分类器时,才被认为是一个检测目标。可以看出,强分类器与弱分类器结构不同,是一种类似于“并联”的结构,我称其为“并联组成的强分类器”。
4 级联分类器
缩进通过之前的介绍,到这应该可以理解OpenCV中:由弱分类器“并联”组成强分类器,而由强分类器“串联”组成级联分类器。那么还剩最后一个内容,那就是检测窗口大小固定(例如alt2是20*20像素)的级联分类器如何遍历图像,以便找到在图像中大小不同、位置不同的目标。
1. 为了找到图像中不同位置的目标,需要逐次移动检测窗口(窗口中的Haar特征相应也随着移动),这样就可以遍历到图像中的每一个位置;
2. 而为了检测到不同大小的目标,一般有两种做法:逐步缩小图像or逐步放大检测窗口,这样即可遍历到图像中不同大小的目标
缩进缩小图像就是把图像按照一定比例逐步缩小然后滑动窗口检测,如图7;放大检测窗口是把检测窗口长宽按照一定比例逐步放大,这时位于检测窗口内的Haar特征也会对应放大,然后检测。一般来说,如果用用硬件实现则缩小图像更快,用软件实现算法则放大检测窗口更快。
图7 经典的Pymaid+Sliding-window检测(借用一张大佬的照片)
缩进新版c++函数CascadeClassifier::detectMultiScale()只实现了缩小图像检测;旧版的c函数cvHaarDetectObject()同时实现了缩小图像和放大窗口两种检测方式,当函数参数flag为CV_HAAR_SCALE_IMAGE时是缩小图像检测,默认flag=0时放检测大窗口检测。
- #define CV_HAAR_SCALE_IMAGE 2
-------------------------------------------
下一篇,我会介绍一个必须但又容易被忽略的问题——利用并查集合并检测结果窗口。