一、概述
下面描述的目标检测器最初由 Paul Viola 提出,并由 Rainer Lienhart改进。
保罗维奥拉和迈克尔 J. 琼斯。 使用增强级联的简单特征进行快速对象检测。 在计算机视觉和模式识别中,2001 年。CVPR 2001。2001 年 IEEE 计算机学会会议论文集,第 1 卷,第 I-511 页。 IEEE,2001 年。
Rainer Lienhart 和 Jochen Maydt。 一组扩展的类似 haar 的特征,用于快速对象检测。 在图像处理中。 2002. 诉讼。 2002 年国际会议,第 1 卷,第 I-900 页。 IEEE,2002 年。
首先,分类器(即使用类似 haar 的特征的增强分类器的级联)使用特定对象(即人脸或汽车)的数百个样本视图(称为正样本)进行训练,这些样本视图被缩放到相同的 大小(例如,20x20)和反例 - 相同大小的任意图像。
训练分类器后,可以将其应用于输入图像中的感兴趣区域(与训练期间使用的大小相同)。 如果该区域可能显示对象(即人脸/汽车),则分类器输出“1”,否则输出“0”。 要在整个图像中搜索对象,可以在图像上移动搜索窗口并使用分类器检查每个位置。 分类器的设计使其可以轻松“调整大小”,以便能够找到不同大小的感兴趣对象,这比调整图像本身的大小更有效。 因此,要在图像中找到一个未知大小的对象,扫描过程应该在不同的尺度上进行多次。
分类器名称中的“级联”一词意味着生成的分类器由几个更简单的分类器(阶段)组成,这些分类器随后应用于感兴趣的区域,直到在某个阶段候选被拒绝或所有阶段都通过。 “提升”这个词意味着级联每个阶段的分类器本身都是复杂的,它们是使用四种不同的提升技术(加权投票)中的一种由基本分类器构建的。 目前支持 Discrete Adaboost、Real Adaboost、Gentle Adaboost 和 Logitboost。 基本分类器是至少有 2 个叶子的决策树分类器。 Haar 类特征是基本分类器的输入,其计算方法如下所述。 当前算法使用以下类似 Haar 的特征:
特定分类器中使用的特征由其形状(1a、2b 等)、感兴趣区域内的位置和尺度(这个尺度与检测阶段使用的尺度不同,尽管这两个尺度是 倍增)。 例如,在第三条线特征(2c)的情况下,响应计算为覆盖整个特征(包括中间的两条白色条纹和黑色条纹)的矩形下的图像像素之和与 黑色条纹下的图像像素总和乘以 3,以补偿区域大小的差异。 使用积分图像快速计算矩形区域上的像素值总和(见下文和积分描述)。
在新的 C++ 接口中,除了类似 Haar 的功能外,还可以使用 LBP(本地二进制模式)功能。 .. [Viola01] Paul Viola 和 Michael J. Jones。 使用增强的简单特征级联进行快速目标检测。 IEEE CVPR,2001。
二、类参考
1、函数原型
这里描述两个主要的函数
Ptr< BaseCascadeClassifier::MaskGenerator > cv::createFaceDetectionMaskGenerator ()
void cv::CascadeClassifier::detectMultiScale ( InputArray image,
std::vector< Rect > & objects,
double scaleFactor = 1.1,
int minNeighbors = 3,
int flags = 0,
Size minSize = Size(),
Size maxSize = Size()
)
2、参数详解
image | CV_8U 类型的矩阵,包含检测到对象的图像。 |
objects | 矩形向量,其中每个矩形包含检测到的对象,矩形可能部分位于原始图像之外。 |
scaleFactor | 指定在每个图像比例下图像大小减小多少的参数。 |
minNeighbors | 参数指定每个候选矩形应该有多少个邻居来保留它。 |
flags | 与函数 cvHaarDetectObjects 中的旧级联具有相同含义的参数。 它不用于新的级联。 |
minSize | 最小可能的对象大小。 小于该值的对象将被忽略。 |
maxSize | 最大可能的对象大小。 大于该值的对象将被忽略。 如果 maxSize == minSize 模型在单一尺度上进行评估。 |
三、OpenCV源码
1、源码路径
opencv\modules\objdetect\src\cascadedetect.cpp
2、源码代码
部分源码如下
bool CascadeClassifierImpl::load(const String& filename)
{
oldCascade.release();
data = Data();
featureEvaluator.release();
FileStorage fs(filename, FileStorage::READ);
if( !fs.isOpened() )
return false;
FileNode fs_root = fs.getFirstTopLevelNode();
if( read_(fs_root) )
return true;
// probably, it's the cascade in the old format;
// let's try to convert it to the new format
FileStorage newfs(".yml", FileStorage::WRITE+FileStorage::MEMORY);
haar_cvt::convert(fs_root, newfs);
std::string newfs_content = newfs.releaseAndGetString();
newfs.open(newfs_content, FileStorage::READ+FileStorage::MEMORY);
fs_root = newfs.getFirstTopLevelNode();
if( read_(fs_root) )
return true;
return false;
}
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
std::vector<int>& rejectLevels,
std::vector<double>& levelWeights,
double scaleFactor, int minNeighbors,
int /*flags*/, Size minObjectSize, Size maxObjectSize,
bool outputRejectLevels )
{
CV_INSTRUMENT_REGION();
CV_Assert( scaleFactor > 1 && _image.depth() == CV_8U );
if( empty() )
return;
detectMultiScaleNoGrouping( _image, objects, rejectLevels, levelWeights, scaleFactor, minObjectSize, maxObjectSize,
outputRejectLevels );
const double GROUP_EPS = 0.2;
if( outputRejectLevels )
{
groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
}
else
{
groupRectangles( objects, minNeighbors, GROUP_EPS );
}
}
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
double scaleFactor, int minNeighbors,
int flags, Size minObjectSize, Size maxObjectSize)
{
CV_INSTRUMENT_REGION();
std::vector<int> fakeLevels;
std::vector<double> fakeWeights;
detectMultiScale( _image, objects, fakeLevels, fakeWeights, scaleFactor,
minNeighbors, flags, minObjectSize, maxObjectSize );
}
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
std::vector<int>& numDetections, double scaleFactor,
int minNeighbors, int /*flags*/, Size minObjectSize,
Size maxObjectSize )
{
CV_INSTRUMENT_REGION();
Mat image = _image.getMat();
CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
if( empty() )
return;
std::vector<int> fakeLevels;
std::vector<double> fakeWeights;
detectMultiScaleNoGrouping( image, objects, fakeLevels, fakeWeights, scaleFactor, minObjectSize, maxObjectSize );
const double GROUP_EPS = 0.2;
groupRectangles( objects, numDetections, minNeighbors, GROUP_EPS );
}