OpenCV 之 HaarTraining 算法剖析
来源:互联网 发布:python 10分钟 编辑:程序博客网 时间:2024/06/11 17:50
1.总体框架
要训练一个Haar分类器,总体上包括3步:1)准备正负样本;2)用CreateSamples程序建正样本集;3)用HaarTraining程序训练,得到最终的分类器模型(xml文件)。
2.样本准备
HaarTraining需要使用正样本和负样本进行训练。下面分别进行描述。
2.1 正样本
对于正样本,通常的做法是先把所有正样本裁切好,并对尺寸做规整(即缩放至指定大小)。
由于HaarTraining训练时输入的正样本是vec文件,所以需要使用OpenCV自带的CreateSamples程序将准备好的正样本转换为vec文件。转换的步骤如下:
1) 制作一个正样本描述文件,用于描述正样本文件名(包括绝对路径或相对路径),正样本数目以及各正样本在图片中的位置和大小。典型的正样本描述文件如下:
face_100/face00001.bmp 1 0 0 20 20
face_100/face00002.bmp 1 0 0 20 20
face_100/face00003.bmp 1 0 0 20 20
2) 运行CreateSamples程序。如果直接在VC环境下运行,可以在Project\Settings\Debug属性页的Program arguments栏设置运行参数。下面是一个运行参数示例:
-info F:\FaceDetect\samples.dat -vec F:\FaceDetect\samples.vec -num 200 -w 20 -h 20
表示有200个样本,样本宽20,高20,正样本描述文件为samples.dat,结果输出到samples.vec。
3) 运行完了会生成一个*.vec的文件。该文件包含正样本数目,宽高以及所有样本图像数据。
2.2 负样本
负样本图像可以是不含有正样本模式的任何图像,比如一些风景照等。训练时,OpenCV需要一个负样本描述文件,该文件只需包含所有负样本的文件名及绝对(或相对)路径名。以下是一个负样本描述文件内容示例:
nonface_200/00001.bmp
nonface_200/00002.bmp
nonface_200/00003.bmp
负样本描述文件的生成方法可参照正样本描述文件生成方法。
负样本图像的大小只要不小于正样本就可以,在使用负样本时,OpenCV自动从负样本图像中抠出一块和正样本同样大小的区域作为负样本,具体可查看函数icvGetNextFromBackgroundData()。具体抠图过程为:
1) 确定抠图区域的左上角坐标(Point.x, Point.y)
2) 确定一个最小缩放比例,使得原负样本图像缩放后恰好包含选中负样本区域
3) 对原负样本图象按计算好的缩放比例进行缩放
3.训练
准备好正样本集(即samples.vec文件),负样本集及其描述文件后,就可以开始训练了。
在VC中进行调试,我在调试时设置的参数如下:
-data F:\FaceDetect\trainout -vec F:\FaceDetect\samples.vec -bg F:\FaceDetect\negatives.dat -nstages 3 -nsplits 2 -minhitrate 0.999 -maxfalsealarm 0.5 -npos 100 -nneg 200 -w 20 -h 20 -mem 512 -eqw 1 -mode ALL -bt GAB -minpos 50
为调试方便,正负样本数目用的非常少,正样本100个,负样本200个。
3.1 创建Haar特征
函数icvCreateIntHaarFeatures( winsize, mode, symmetric )负责创建所有可能的Haar特征。Mode决定使用基本的5种特征还是所有upright特征抑或所有特征。Symmetric为1时表示只创建Haar特征的中心在左半部分的所有特征,为0时创建所有特征。当训练人脸图像时,由于人脸的左右对称性可以设置Symmetric为1,以加速训练。
3.2 载入正样本
int icvGetHaarTrainingDataFromVec( CvHaarTrainingData* data, int first, int count,
CvIntHaarClassifier* cascade,
const char* filename,
int* consumed )
函数icvGetHaarTrainingDataFromVec()负责从正样本集*.vec文件中载入count个正样本。在程序第一次运行到此(即训练第一个分类器之前)时,只要正样本集中有count个样本,就一定能取出count个正样本。在以后运行到此时,有可能取不到count个样本,因为必须是用前面的级联强分类器分类为正样本(即分类正确的样本)的样本才会被取出作为下一个强分类器训练样本,具体可参考icvGetHaarTrainingData和icvEvalTreeCascadeClassifierFilter函数。
传递返回值的Consumed参数表示为取count个正样本,查询过的正样本总数。
此外,函数内还通过调用icvGetAuxImages计算积分图像。
3.3 载入负样本
int icvGetHaarTrainingDataFromBG( CvHaarTrainingData* data, int first, int count,
CvIntHaarClassifier* cascade, double* acceptance_ratio )
函数icvGetHaarTrainingDataFromBG ()负责从负样本集中载入count个负样本。在程序第一次运行到此(即训练第一个分类器之前)时,只要负样本集中有count个样本,就一定能取出count个负样本。在以后运行到此时,有可能取不到count个样本,因为必须是用前面的级联强分类器分类为正样本的样本(即分类错误的样本)才会被取出作为下一个强分类器训练样本,具体可参考icvGetHaarTrainingDataFromBG和icvEvalTreeCascadeClassifierFilter函数。
传递返回值的acceptance_ratio参数记录的是实际取出的负样本数与查询过的负样本数之比(acceptance_ratio = ((double) count) / consumed_count),也就是虚警率,用于判断已训练的级联分类器是否达到指标,若达到指标,则停止训练过程。
此外,函数内还通过调用icvGetAuxImages计算积分图像。
注意函数icvGetHaarTrainingDataFromBG中一个主要的For循环:
for( i = first; i < first + count; i++ ) //共读取count个负样本,当读取不到
{ //这么多负样本时将出现死循环!
对上面代码中的注释有必要进一步说明一下:只有当之前的强分类器对负样本集内的样本全部分类正确时才会出现死循环。因为只要有一个样本会被错分为正样本,那么通过count次扫描整个负样本集就能得到count个负样本,当然这count个负样本实际上就是一个负样本的count个拷贝。为避免这些情况的发生,负样本集中的样本数需要足够多。
在负样本图像大小与正样本大小完全一致时,假设最终的分类器虚警率要求是falsealarm,参加训练的负样本要求是count个,则需要的负样本总数可计算如下:
TotalCount = count / falsealarm
以Rainer Lienhart的文章中的一些参数为例,falsealarm=0.5^20=9.6e-07, count=3000,则TotalCount=3000/(0.5^20)= 3,145,728,000=31亿。
当正负样本顺利载入,屏幕上会出现类似下面的输出界面:
含义:
POS(正样本): 取出的正样本数目查询过的正样本数目 两者之比
NEG(负样本): 取出的负样本数目查询过的负样本数目 两者之比
上面的输出由下面两行代码得到:
{
printf( "POS: %d %d %f\n", poscount, consumed, ((double) poscount)/consumed );
printf( "NEG: %d %g\n", negcount, false_alarm );
// false_alarm = ((double) negcount) / consumed_negcount;
}
3.4 计算Haar特征值
void icvPrecalculate( CvHaarTrainingData* data, CvIntHaarFeatures* haarFeatures,
int numprecalculated )
函数icvPrecalculate ()负责计算所有取出的正负样本的前numprecalculated个Haar特征值(由icvGetTrainingDataCallback实现),并且对每种特征,将所有样本标号按其特征值升序排序(由cvGetSortedIndices实现,每种特征分别排序)。
Numprecalculated的计算公式如下:
numprecalculated = (int) ( ((size_t) mem) * ((size_t) 1048576) /
( ((size_t) (npos + nneg)) * (sizeof( float ) + sizeof( short )) ) );
其中mem 是内存大小,以M为单位,1048576=1024*1024,表示1M字节。sizeof( float )为保存一个特征值需占用的字节数,sizeof( short )表示对特征值排序后保存一个排序序号需占用的字节数。
3.5 训练一个强分类器
CvIntHaarClassifier* icvCreateCARTStageClassifier( CvHaarTrainingData* data,
CvMat* sampleIdx,
CvIntHaarFeatures* haarFeatures,
float minhitrate,
float maxfalsealarm,
int symmetric,
float weightfraction,
int numsplits,
CvBoostType boosttype,
CvStumpError stumperror,
int maxsplits )
函数icvCreateCARTStageClassifier负责训练一个强分类器。
3.6 保存强分类器信息到临时文件中
函数icvSaveStageHaarClassifier负责将新训练得到的强分类器信息保存到临时文件AdaBoostCARTHaarClassifier.txt中。icvSaveStageHaarClassifier首先根据当前强分类器在级联强分类器中序号(cur_node->idx)在dirname目录下创建一个文件夹,然后在该文件夹下创建AdaBoostCARTHaarClassifier.txt文件,并将强分类器信息写到这个文件中。
3.7 将级联强分类器信息写到一个XML文件中
首先是从先前保存的临时文件中读取级联强分类器信息(cascade = cvLoadHaarClassifierCascade( dirname, cvSize(winwidth,winheight) ) )然后用cvSave( xml_path, cascade )将级联强分类器信息保存到xml文件中。至此,整个训练部分完毕。
3.8 测试最终分类器性能
这部分工作通过简单地调用两个函数实现。调用icvGetHaarTrainingDataFromVec测试检出率;通过调用icvGetHaarTrainingDataFromBG测试虚警率。
- OpenCV 之 HaarTraining 算法剖析
- OpenCV 之 HaarTraining 算法剖析
- OpenCV 之 HaarTraining 算法剖析
- OpenCV 之 HaarTraining 算法剖析
- OpenCV 之 HaarTraining 算法剖析
- OpenCV 之 HaarTraining 算法剖析
- OpenCV 之 HaarTraining 算法剖析
- opencv Haartraining
- 基础学习笔记之opencv(1):haartraining
- Opencv研读笔记:haartraining程序之cvCreateMTStumpClassifier函数详解~
- Opencv研读笔记:haartraining程序之icvCreateCARTStageClassifier函数详解~
- Opencv HaarTraining初体验
- OpenCV Haartraining 常见问题
- 经典的HaarTraining算法
- 经典的HaarTraining算法
- opencv haartraining 分析一:cvCreateTreeCascadeClassifier
- OpenCV Haartraining训练相关问题
- 基础学习笔记之opencv(3):haartraining生成.xml文件过程
- 第七周——友元函数求坐标距离
- C#程序中的路径问题
- HTML5开发环境PhoneGap视频教程
- 挖掘机配件
- SQL server 小笔记
- OpenCV 之 HaarTraining 算法剖析
- C中.h头文件的写法
- C++编程->加法的七种方式实现(命名空间,类,模板,结构体,函数,位运算,内联汇编)
- 常见的数字验证正则表达式整理,开发的朋友经常用的到
- 数学归纳法的计算机实现——递归
- 谈“发表(撰写)学术论文的注意事项”
- 音乐类型及不同风格音乐的代表作
- Gamma曲线矫正
- Hadoop安装配置