如何使用opencv 训练opencv 分类器 下载以及训练过程中的问题

OpenCV - 训练分类器
一.收集样本
正样本:待检目标样本
负样本:其它任意图片
PS.所有样本图片都应该有同一尺寸,如20 * 20
ACDSee软件:对图像批量处理为20x20大小的图片
1.把所有正样本图片放在posdata的文件夹下,把所有负样本图片放在negdata文件夹下
负样本数量应大于正样本数量,如(3:1),否则在训练时会陷入死循环。。。
1.分别为正样本和负样本创建描述文件
(1)为正样本创建描述文件格式文件info.txt,并且把这个文件放在与样本图片同一目录下,D:\OpenCv\opencv\build\x64\
12\bin\posdata
a)在命令行下 输入以下命令: dir/b & info.txt
b)删除info.txt最后一行的 &info.txt&
c)打开info.txt, 按ctrl+h, 把所有的&bmp& 换成
&bmp 1 0 0 20 20&
1代表一个文件 ||0 0顶点坐标 || 20 20右下顶点坐标
(2)为负样本创建集合文件格式文件bg.txt, 并且把这个文件放在与样本图片同一目录下
a)在命令行下 输入以下命令: dir /b & bg.txt
b)删除bg.txt最后一行的 &bg.txt&
二.创建样本
Ps: createsamples程序的命令行参数
正样本描述文件
训练好的正样本的输出文件名
要产生的正样本的数量,和正样本图片数目相同
样本的宽高
执行脚本后,将生成正样本描述文件a.vec
-------------------------------------------------------------
其他的一些参数:
源目标图片(例如:一个公司图标)
背景描述文件。
背景色最大的偏离度。
-maxangel
-maxangle,
-maxzangle
最大旋转角度,以弧度为单位。
如果指定,每个样本会被显示出来,按下&esc&会关闭这一开关,即不显示样本图片,而创建过程继续。这是个有用的debug选项。
二.训练级联分类器
Ps: Haartraining的命令行参数如下
存放训练好的分类器的路径名
正样本文件名(由trainingssamples程序或者由其他的方法创建的)绝对路径
背景描述文件。绝对路径
用来训练每一个分类器阶段的正样本数量Ps:正样本的数目(-npos)要略大于实际数
用来训练每一个分类器阶段的负样本数量
训练的阶段(层)数
决定用于阶段分类器的弱分类器。如果值为1,则一个简单的stumpclassifier被使用。如果是2或者更多,则带有number_of_splits个内部节点的CART分类器被使用
预先计算的以MB为单位的可用内存。内存越大则训练的速度越快
指定训练的目标对象是否垂直对称。垂直对称提高目标的训练速度。例如,正面部是垂直对称的
表示使用haar特征集的各类既有垂直的,又有45度角旋转的
训练样本的尺寸,(以像素为单位)。必须和训练样本创建的尺寸相同
其他的一些参数:
-sym(default)
-minhitrate
每个阶段分类器需要的最小的命中率。总的命中率为min_hit_rate的number_of_stages次方。
-maxfalsealarm
没有阶段分类器的最大错误报警率。总的错误警告率为 max_false_alarm_rate的number_of_stages次方。每一层训练到这个值小于0.5时训练结束,进入下一层训练
-weighttrimming
指定是否使用权修正和使用多大的权修正。一个基本的选择是0.9
选择用来训练的haar特征集的种类。basic仅仅使用垂直特征。all使用垂直和45度角旋转特征。
? 参数的配置十分重要,很多时候训练不成功多是因为参数的配置问题!!!
-----------------------------------------------------------
训练过程参数值解释:
SMP:样本的使用率
F : +表示通过翻转,否则是-
ST.THR : 分类器的阈值
HR:当前分类器对正样本识别正确的概率
FA:当前分类器对负样本识别错误的概率
EXP.ERR : 分类器的期望错误率
-----------------------------------------------------------
测试代码:人脸识别
因样本数量仅为 5 所以无精度可言
五.错误分析
负样本数太少,该阶段已无可提取负样本
解决方法:
正 : 负 = 1 : 2.5~3
2.停止工作
报错信息:
参数配置:
在第一层训练的时候已经用完了vec文件中所有的正样本,以至于在后续的样本训练中没有新的正样本可以加入使用了,导致出现了错误的现象。
那个vec文件中的正样本数目
= npos +(nstages - 1)*(1 - minHitRate)* npos + s
S的意思是指在正样本中能够直接识别成背景的样本个数,如果npos=120的话,s的取值为个位数就行,不用太大。
解决方法:
在训练时,-npos参数值取 正样本数的 1/3
如 实际正样本数为 300 参数取值则设为 100Opencv学习笔记(八)opencv_cascade级联分类器训练haar、LBP特征
使用级联分类器工作包括两个阶段:训练和检测。 检测部分在OpenCVobjdetect
模块的文档中有介绍,在那个文档中给出了一些级联分类器的基本介绍。当前的指南描述了如何训练分类器:准备训练数据和运行训练程序。
重点注意事项
OpenCV中有两个程序可以训练级联分类器: opencv_haartraining 和
opencv_traincascade 。
opencv_traincascade 是一个新程序,根据OpenCV 2.x
API 用C++ 编写。这二者主要的区别是opencv_traincascade 支持
(Local Binary Patterns)
两种特征,并易于增加其他的特征。与Haar特征相比,LBP特征是整数特征,因此训练和检测过程都会比Haar特征快几倍。LBP和Haar特征用于检测的准确率,是依赖训练过程中的训练数据的质量和训练参数。训练一个与基于Haar特征同样准确度的LBP的分类器是可能的。
opencv_traincascade
和opencv_haartraining
所输出的分类器文件格式并不相同。注意,新的级联检测接口(参考objdetect 模块中的
CascadeClassifier
类)支持这两种格式。opencv_traincascade
可以旧格式导出选练好的级联分类器。但是在训练过程被中断后再重启训练过程,opencv_traincascade
andopencv_haartraining
不能装载与中断前不同的文件格式。
opencv_traincascade
程序使用TBB来处理多线程。如果希望使用多核并行运算加速,请使用TBB来编译OpenCV。
还有一些与训练相关的辅助程序。
opencv_createsamples
用来准备训练用的正样本数据和测试数据。opencv_createsamples
能够生成能被opencv_haartraining 和
opencv_traincascade
程序支持的正样本数据。它的输出为以 *.vec 为扩展名的文件,该文件以二进制方式存储图像。
opencv_performance
可以用来评估分类器的质量,但只能评估opencv_haartraining
输出的分类器。它读入一组标注好的图像,运行分类器并报告性能,如检测到物体的数目,漏检的数目,误检的数目,以及其他信息。
既然 opencv_haartraining
是一个将被弃用的程序,下面将不再介绍,而会主要介绍opencv_traincascade
。opencv_createsamples
程序用来为opencv_traincascade
准备训练样本,因此也会介绍它。
准备训练数据
训练需要一些列样本。样本分两类:负样本和正样本。负样本是指不包括物体的图像。正样本是待检测的物体的图像。负样本必须手工准备,正样本使用opencv_createsamples
负样本可以是任意图像,但是这些图像中不能包含待检测的物体。用于抠取负样本的图像文件名被列在一个文件中。这个文件是纯文本文件,每行是一个文件名(包括相对目录和文件名)。负样本和样本图像也叫做背景样本,或者背景样本图像,本文档中对之不予区分。这些图像可以是不同的尺寸,但是图像尺寸应该比训练窗口的尺寸大,因为这些图像将被用于抠取负样本,并将负样本缩小到训练窗口大小。
下面是一个描述文件的例子:
假如目录结构如下:
/img img1.jpg img2.jpg bg.txt
则bg.txt文件中的内容将如下所示:
img/img1.jpg img/img2.jpg
正样本由 opencv_createsamples
生成。正样本可以由包含待检测物体的一张图片生成,也可由一系列标记好的图像生成。
请注意你需要一个很大的负样本库送给训练程序进行训练。如果是绝对刚性的物体,如OpenCV的标志,你只有一张正样本图像;如果是人脸,你需要几百甚至几千个正样本。在待检测物体是人脸的情况下,你需要考虑所有的人种、年龄、表情甚至胡子的样式。
如果只有一张包含物体的图像,如一个公司的标志,那么可以通过对物体图像的随机旋转、改变标志亮度以及将标志放在任意的背景上而获得大量的正样本。生成的正样本数目以及随机的程度都可以通过opencv_createsamples
的命令行参数控制。
命令行参数:
输出文件,内含用于训练的正样本。
输入图像文件名(例如一个公司的标志)。
背景图像的描述文件,文件中包含一系列的图像文件名,这些图像将被随机选作物体的背景。
生成的正样本的数目。
背景颜色(目前为灰度图);背景颜色表示透明颜色。因为图像压缩可造成颜色偏差,颜色的容差可以由
指定。所有处于 bgcolor-bgthresh
和bgcolor+bgthresh
之间的像素都被设置为透明像素。
如果指定该标志,前景图像的颜色将翻转。
如果指定该标志,颜色将随机地翻转。
前景样本里像素的亮度梯度的最大值。
-maxxangle
X轴最大旋转角度,必须以弧度为单位。
-maxyangle
Y轴最大旋转角度,必须以弧度为单位。
-maxzangle
Z轴最大旋转角度,必须以弧度为单位。
很有用的调试选项。如果指定该选项,每个样本都将被显示。如果按下
键,程序将继续创建样本但不再显示。
输出样本的宽度(以像素为单位)。
输出样本的高度(以像素为单位)。
创建样本的流程如下: 输入图像沿着三个轴随机旋转。旋转的角度由
-max?angle
限定。然后像素的亮度值位于 [bg_color-bg_color_threshold;bg_color+bg_color_threshold]范围的像素被设置为透明像素。将白噪声加到前景图像上。如果指定了-inv
,那么前景图像的颜色将被翻转。如果指定了-randinv
,程序将随机选择是否将颜色进行翻转。任选背景图像,将获得的前景图像放到背景图像上,并将图像调整到-w
指定的大小。最后将图像存入vec文件,vec文件名由命令行参数-vec 指定。
正样本也可从一系列事先标记好的图像中创建。标记信息可以存储于一个文本文件,与背景描述文件类似。文件中的每行对应一个图像文件。每行的第一个元素为图像文件名,后面是物体的数目,最后是物体位置和大小的描述
(x, y, width, height)。
下面是描述文件的例子:
假设目录结构如下:
/img img_with_faces_1.jpg img_with_faces_2.jpg info.dat
文件info.dat里的内容如下:
img/img_with_faces_1.jpg 1 140 100 45 45 img/img_with_faces_2.jpg 2 100 200 50 50 50 30 25 25
图像img_with_faces_1.jpg中包含一个物体实例(如人脸),标示其在图像中的位置和大小的矩形为(140,
100, 45, 45)。图像img_with_faces_2.jpg包含两个物体实例。
从这样的一系列数据中创建正样本,需要在命令行指定
-info 而非前面所用的
描述物体所在图像以及大小位置的描述文件。
此部分样本创建过程如下:将物体实例从图像中抠取出,然后将之调整尺寸到目标尺寸,然后保存到输出的vec文件。在此过程中不会对图像进行变形,所以有效的命令行参数仅有-w,-h,-show
opencv_createsamples
也可以用来查看和检查保存于vec正样本文件中的正样本。这时只需指定-vec ,-w 和 -h 三个参数则可。opencv_createsamples
将逐一显示正样本图像。
在训练中,训练程序并不关心包含正样本的vec文件如何生成的,你可以自己写程序来生成vec文件。但是OpenCV提供的工具中,只有
opencv_createsamples
程序能够创建包含正样本的vec文件。
一个vec文件的例子位于 opencv/data/vec_files/trainingfaces_24-24.vec
。它可用来训练人脸分类器,窗口大小为:-w24-h24
训练级联分类器
下一步是训练分类器。如前面所述,
opencv_traincascade
和opencv_haartraining
都可用来训练一个级联分类器,但是此处只介绍opencv_traincascade 。
opencv_haartraining
的用法与opencv_traincascade 类似。
下面是 opencv_traincascade
的命令行参数,以用途分组介绍:
通用参数:
目录名,如不存在训练程序会创建它,用于存放训练好的分类器。
包含正样本的vec文件名(由
opencv_createsamples
程序生成)。
背景描述文件,也就是包含负样本文件名的那个描述文件。
每级分类器训练时所用的正样本数目。
每级分类器训练时所用的负样本数目,可以大于
指定的图片数目。
-numStages
训练的分类器的级数。
-precalcValBufSize
缓存大小,用于存储预先计算的特征值(feature
values),单位为MB。
-precalcIdxBufSize
缓存大小,用于存储预先计算的特征索引(feature
indices),单位为MB。内存越大,训练时间越短。
-baseFormatSave
这个参数仅在使用Haar特征时有效。如果指定这个参数,那么级联分类器将以老的格式存储。
级联参数:
-stageType
级别(stage)参数。目前只支持将BOOST分类器作为级别的类型。
-featureType&{HAAR(default),LBP}&
特征的类型: HAAR -
类Haar特征;LBP -
局部纹理模式特征。
训练样本的尺寸(单位为像素)。必须跟训练样本创建(使用
opencv_createsamples
程序创建)时的尺寸保持一致。
Boosted分类器参数:
-bt&{DAB,RAB,LB,GAB(default)}&
Boosted分类器的类型:
Discrete AdaBoost,RAB - Real
AdaBoost,LB -
LogitBoost, GAB - Gentle AdaBoost。
-minHitRate
分类器的每一级希望得到的最小检测率。总的检测率大约为
min_hit_rate^number_of_stages。
-maxFalseAlarmRate
分类器的每一级希望得到的最大误检率。总的误检率大约为
max_false_alarm_rate^number_of_stages.
-weightTrimRate
Specifies whether trimming should
be used and its weight. 一个还不错的数值是0.95。
弱分类器树最大的深度。一个还不错的数值是1,是二叉树(stumps)。
-maxWeakCount
每一级中的弱分类器的最大数目。The boosted
classifier (stage) will have so many weak trees
(&=maxWeakCount), as needed to
achieve the given-maxFalseAlarmRate.
类Haar特征参数:
-mode(default)|
选择训练过程中使用的Haar特征的类型。 BASIC 只使用右上特征,
使用所有右上特征和45度旋转特征。更多细节请参考
LBP特征参数:
LBP特征无参数。
当 opencv_traincascade
程序训练结束以后,训练好的级联分类器将存储于文件cascade.xml中,这个文件位于-data
指定的目录中。这个目录中的其他文件是训练的中间结果,当训练程序被中断后,再重新运行训练程序将读入之前的训练结果,而不需从头重新训练。训练结束后,你可以删除这些中间文件。
训练结束后,你就可以测试你训练好的级联分类器了!
Paul Viola, Michael Jones.
Rapid Object Detection using a Boosted Cascade of Simple
Features. Conference on Computer Vision and Pattern
Recognition (CVPR), 2001, pp. 511-518.
Rainer Lienhart and Jochen Maydt.
An Extended Set of Haar-like Features for Rapid Object
Detection. Submitted to ICIP2002.
Shengcai Liao, Xiangxin Zhu, Zhen
Lei, Lun Zhang and Stan Z. Li.Learning Multi-scale Block Local
Binary Patterns for Face Recognition. International Conference
on Biometrics (ICB), 2007, pp. 828-837.
1、准备好正负样本图片,正样本就是含有目标的图片,负样本就是不还有目标的图片,建好文件夹,如图所示。其中正样本图片最好是裁剪成同一尺寸,我这里是建成30*30,便于后期的说明文件的建立。图片的编辑我使用的是美图看看,批量编辑工具,可以批量将图片改成同一尺寸。
2、建立正负样本说明文件,在cmd下进入pos文件夹目录里,输入
pos.txt,用editplus打开该文件,删除最后一行,最后将名字归一化如下所示:同样的方法进入neg文件夹内,用同样的方法建立描述文件neg.txt,用editplus打开该文件,只需删掉最后一行
创建vec文件:将opencv_createsamples.exe和
opencv_traincascade.exe放到图片文件夹的上层目录,利用opencv_createsamples.exe应用程序在该目录下使用如图cmd命令:
其中的-vec是指定后面输出vec文件的文件名,-info指定正样本描述文件,-bg指定负样本描述文件,-w和-h分别指正样本的宽和高,-num表示正样本的个数。执行完该命令后就会在当前目录下生产一个pos.vec文件了。
opencv_cascade.exe -vec vec.vec -info pos/pos.txt -bg
neg/neg.txt -w 30 -h 30 -num 100
使用opencv_traincascade.exe文件进行训练
首先在当前目录下新建一个dt文件夹用于存放生成的.xml文件。
在当前目录使用cmd命令:
D:\&opencv_traincascade.exe -data dt -vec pos.vec -bg
neg/neg.txt -numPos 100 -n
umNeg 300 -numStages 16 -precalcValbufSize 200 -precalcdxBufSize
1000 -featureTy
pe LBP -w 30 -h 30截图如下:其中-data
输出目录,-numPos正样本数目-numNeg负样本数目-numStages训练级数
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。使用级联分类器工作包括两个阶段:训练和检测。 检测部分在OpenCVobjdetect 模块的文档中有介绍,在那个文档中给出了一些级联分类器的基本介绍。当前的指南描述了如何训练分类器:准备训练数据和运行训练程序。参考:/article/4dcc8d946f197.html
利用自带的程序训练一个分类器,需要经过以下几个步骤:
(1)收集训练样本:
训练样本包括正样本和负样本。正样本,通俗点说,就是图片中只有你需要的目标。而负样本的图片只要其中不含有目标就可以了。但需要说明的是,负样本也并非随便选取的。例如,你需要检测的目标是汽车,那么正样本就应该是仅仅含有汽车的图片,而负样本显然不能是一些包含天空的,海洋的,风景的图片。因为你最终训练分类器的目的是检测汽车,而汽车应该出现在马路上。也就是说,分类器最终检测的图片应该是那些包含马路,交通标志,建筑物,广告牌,汽车,摩托车,三轮车,行人,自行车等在内的图片。很明显,这里的负样本应该是包含摩托车、三轮车、自行车、行人、路面、灌木丛、花草、交通标志、广告牌等。
另外,需要提醒的是,adaboost方法也是机器学习中的一个经典算法,而机器学习算法的前提条件是,测试样本和训练样本独立同分布。所谓的独立同分布,可以简单理解为:训练样本要和最终的应用场合非常接近或者一致。否则,基于机器学习的算法并不能保证算法的有效性。此外,足够的训练样本(至少得几千张正样本、几千张负样本)也是保证训练算法有效性的一个前提条件。
这里,假设所有的正样本都放在 /pos文件夹下,所有的负样本都放在 /neg文件夹下;
(2)对所有的正样本进行尺寸归一化:
上一步收集到的正样本,有很多的尺寸大小,有的是,有的是尺寸归一化的目的,就是把所有的图片都缩放到同一大小。比如,都缩放到的大小。(程序或者光影魔术手都可以)
(3)生成正样本描述文件:
所谓的正样本描述文件,其实就是一个文本文件,只不过,很多人喜欢将这个文件的后缀改成而已。正样本描述文件中的内容包括:文件名 目标个数 目标在图片中的位置()典型的正样本描述文件如下所示:0.jpg 1 0 0 30 40….
假如,文件夹下有个正样本图片,每个图片中仅有一个目标。命令行下 dir /b & pos.dat生成一个文件作为正样本描述文件。
(4)创建正样本文件
由于训练的时候需要输入的正样本是文件,所以需要使用程序来将正样本转换为文件。(例如:)。
Createsamples程序的命令行参数:命令行参数:-vec &vec_file_name&训练好的正样本的输出文件名。-img&image_file_name&源目标图片(例如:一个公司图标)-bg&background_file_name&背景描述文件。-num&number_of_samples&要产生的正样本的数量,和正样本图片数目相同。-bgcolor&background_color&背景色(假定当前图片为灰度图)。背景色制定了透明色。对于压缩图片,颜色方差量由bgthresh参数来指定。则在bgcolor-bgthresh和bgcolor+bgthresh中间的像素被认为是透明的。-bgthresh&background_color_threshold&-inv如果指定,颜色会反色-randinv如果指定,颜色会任意反色-maxidev&max_intensity_deviation&背景色最大的偏离度。-maxangel&max_x_rotation_angle&-maxangle&max_y_rotation_angle&,-maxzangle&max_x_rotation_angle&最大旋转角度,以弧度为单位。-show如果指定,每个样本会被显示出来,按下"esc"会关闭这一开关,即不显示样本图片,而创建过程继续。这是个有用的debug选项。-w&sample_width&输出样本的宽度(以像素为单位)-h《sample_height》输出样本的高度,以像素为单位。
(5) 创建负样本描述文件
在保存负样本的文件夹下生成一个负样本描述文件,具体步骤同(),此处不再赘叙;
(6)进行样本训练
该步骤通过调用目录下的程序新版本的改名为来完成。其中,的命令行参数为:-data&dir_name&存放训练好的分类器的路径名。-vec&vec_file_name&正样本文件名(由trainingssamples程序或者由其他的方法创建的)-bg&background_file_name&背景描述文件。-npos&number_of_positive_samples&,-nneg&number_of_negative_samples&用来训练每一个分类器阶段的正/负样本。合理的值是:nPos = 7000;nNeg = 3000-nstages&number_of_stages&训练的级联分类器层数。-nsplits&number_of_splits&决定用于阶段分类器的弱分类器。如果1,则一个简单的stump classifier被使用。如果是2或者更多,则带有number_of_splits个内部节点的CART分类器被使用。-mem&memory_in_MB&预先计算的以MB为单位的可用内存。内存越大则训练的速度越快。-sym(default)-nonsym指定训练的目标对象是否垂直对称。垂直对称提高目标的训练速度。例如,正面部是垂直对称的。-minhitrate《min_hit_rate》每个阶段分类器需要的最小的命中率。总的命中率为min_hit_rate的number_of_stages次方。-maxfalsealarm&max_false_alarm_rate&没有阶段分类器的最大错误报警率。总的错误警告率为max_false_alarm_rate的number_of_stages次方。-weighttrimming&weight_trimming&指定是否使用权修正和使用多大的权修正。一个基本的选择是0.9-eqw-mode&basic(default)|core|all&选择用来训练的haar特征集的种类。basic仅仅使用垂直特征。all使用垂直和45度角旋转特征。-w《sample_width》-h《sample_height》训练样本的尺寸,(以像素为单位)。必须和训练样本创建的尺寸相同。一个训练分类器的例子:"D:\Program Files\OpenCV\bin\haartraining.exe"
-data data\cascade -vec data\pos.vec -bg negdata\negdata.dat -npos 49 -nneg 49 -mem 200 -mode ALL -w 20 -h 20
训练结束后,会在目录data下生成一些子目录,即为训练好的分类器。
(7) 生成文件
上一步在进行的时候,会在目录下生成一些目录及文件,我们需要调用opencv\bin\将这些文件转换为文件,也就是所谓的分类器。
至此,分类器的训练工作已完成。剩下的,就是在程序中加载文件,并调用相应的函数接口来实现分类检测的作用了。
其实这个方法有点老了,新一点有这个:http://blog.csdn.net/liulina603/article/details/8598681
阅读(...) 评论()相关内容推荐
爱脚本网()为你提供软件编程和硬件技术方面资料,信息,方法,是你完成项目及工作的好帮手。OpenCV中的Haar+Adaboost(七):分类器训练过程
时间: 00:29:39
&&&& 阅读:831
&&&& 评论:
&&&& 收藏:0
标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&  本节文章讲解OpenCV中Haar+Adaboost的训练过程。此文章假定读者已经了解前面5章的内容,包括Haar特征,弱分类器和强分类器结构,以及GAB等内容。
  在opencv_traincascade.exe程序中,有如下参数
如上输入的boostParams中的6个参数决用于决定训练过程:
1. 参数bt选择Boosting类型(默认GAB),本系列文章五中已经介绍了
2. minHitRate和maxFalseAlarmRate限定训练过程中各种阈值大小,文章六已经介绍了
3. 参数weightTrimRate非核心内容,只是个小tricks,本文忽略
4. 参数maxDepth限定树状弱分类器的深度,如本系列文章三图3中的为2或图5中为1。
5. 参数maxWeakCount为每个stage中树状弱分类器最大数量,超过此数量会强制break
本节的强分类器stage训练过程将围绕这些参数展开。强烈建议读者在阅读本章节前,自行收集人脸样本并使用程序opencv_traincascade.exe训练一个简单的人脸分类器,以便理解
(一)样本收集过程
  首先分析每一个stage训练时如何收集样本,事实上每一个stage训练使用的正负样本都不同。
1. 正样本patches收集过程
  opencv_trancascade.exe使用的正样本是一个vec文件,即由opencv_createsamples.exe把一组固定w x h大小的图片转换为二进制vec文件(只是读取图片并转化为灰度图,并按照二进制格式保存下来而已,不做任何改变)。由于经过如此处理的正样本就是固定w x h大小的patches,所以正样本可以直接进入训练。
2. 负样本patches收集过程
  而使用的负样本就不一样了,是一个包含任意大小图片路径的txt文件。在寻找负样本的过程中,程序会以图像金字塔(pyramid)+滑动窗口的模式(sliding-window)去遍历整个负样本集,以获取w x h大小的负样本patches。
3. 对1和2步骤来中这些自的正负样本的patches进行分类
  在获取到这些w x h固定大小的正负样本patches后,利用已经训练好的stage分类这些patches,并且从正样本中收集TP patches,直到收集够numPos个;从负样本中收集FP patches,直到收集够了numNeg个(为了简化问题,假设样本是足够的)。之后利用TP&patches作为正样本,FP&patches作为负样本训练下一个stage。
图1 每个Stage训练前收集样本示意图
  那么对于第0个stage,直接收集numPos个来自正样本的patches + numNeg个来自负样本的patches进行训练;对于第 i 个stage,则利用已经训练好的 0 到 i - 1 的stage分类这些patches,分别从正样本patches中收集TP,从负样本patches中收集FP(需要说的的是,这里的numPos和numNeg两个值是在opencv_traincascade.exe中设置的)。每一个stage都要进行上述收集+分类过程,所以实际中每一个stage所使用的训练样本也都不一样!
  思考1:看到这里,读者不妨思考一下为什么要用TP和FP训练,而不用FN和TN?
  作者的答案:对于正样本,FN已经被之前的stage拒绝掉了,没法挽救了,所以当前stage也所以没有必要去学习这些FN;而对于负样本,TN已经被之前的stage正确拒绝了,而FP被错误的通过了,所以才要专门学习这些FP。
  看完我的讲解之后再来看看代码,讲解一定要和代码相符。在OpenCV的cascadeclassifier.cpp中,有如下fillPassedSamples()函数负责填充训练样本(修改此函数可以实现下文提到的保存TP和FP)。
1 int CvCascadeClassifier::fillPassedSamples( int first, int count, bool isPositive, double minimumAcceptanceRatio, int64& consumed )
int getcount = <span style="color: #;
Mat img(cascadeParams.winSize, CV_8UC1);
for( int i = i & first + i++ )
for( ; ; )
if( consumed != <span style="color: # && ((double)getcount+<span style="color: #)/(double)(int64)consumed &= minimumAcceptanceRatio )
<span style="color: #
<span style="color: #
<span style="color: #
bool isGetImg = isPositive ? imgReader.getPos( img ) : imgReader.getNeg( img ); //读取样本
<span style="color: #
if( !isGetImg )
<span style="color: #
return //如果不能读取样本(出错or样本消耗光了),返回
<span style="color: #
consumed++; //只要读取到了样本,不管判断结果如何,消耗量consumed增加1
<span style="color: #
<span style="color: #
//当参数isPositive = true时,填充正样本队列,此时选择TP进入队列
<span style="color: #
//当参数isPositive = false时,填充负样本队列,此时选择FP进入队列
<span style="color: #
featureEvaluator-&setImage( img, isPositive ? <span style="color: # : <span style="color: #, i ); //将样本img塞入训练队列中
<span style="color: #
if( predict( i ) == <span style="color: # ) //根据isPositive判断是否是TP/FP, 是则break进入下一个;反之继续循环,并覆盖上面setImage的样本
<span style="color: #
<span style="color: #
getcount++; //真正添加进训练队列的数量
<span style="color: #
printf("%s current samples: %d\r", isPositive ? "POS":"NEG", getcount);
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: #
<span style="color: # }
接下来看看第20行的predict()函数:
1 int CvCascadeClassifier::predict( int sampleIdx )
CV_DbgAssert( sampleIdx & numPos + numNeg );
for (vector& Ptr&CvCascadeBoost& &::iterator it = stageClassifiers.begin();
it != stageClassifiers.end(); it++ )
if ( (*it)-&predict( sampleIdx ) == <span style="color: #.f )
return <span style="color: #;
<span style="color: #
return <span style="color: #;
<span style="color: # }
再进入第7行的(*it)-&predict()函数:
1 float CvCascadeBoost::predict( int sampleIdx, bool returnSum = false ) const
CV_Assert( weak );
double sum = <span style="color: #;
cvStartReadSeq( weak, &reader );
cvSetSeqReaderPos( &reader, <span style="color: # );
for( int i = <span style="color: #; i & weak-& i++ )
<span style="color: #
CvBoostTree*
<span style="color: #
CV_READ_SEQ_ELEM( wtree, reader );
<span style="color: #
sum += ((CvCascadeBoostTree*)wtree)-&predict(sampleIdx)-& //stage内的弱分类器wtree输出值求和sum
<span style="color: #
<span style="color: #
if( !returnSum )
<span style="color: #
//默认进行sum和stageThreshold比较
<span style="color: #
//当sum&stageThreshold,输出0,否决当前样本;sum&stageThreshold,输出1,通过
<span style="color: #
sum = sum & threshold - CV_THRESHOLD_EPS ? <span style="color: #.0 : <span style="color: #.0;
<span style="color: #
return (float) //若returnSum==true则不与stageThreshold比较,直接返回弱分类器输出之和。下文用到
<span style="color: # }
看到这就很清晰了,默认returnSum为false时每个stage内部弱分类器wtree的输出值加起来和stageTheshold比较,当样本通过时输出1,不通过输出0(参考文系列文章三)。那么对于positive samples,输出1即是TP;对于negative samples,输出1即是FP。至此代码与上述内容对应,over!
(二)分类器的训练过程
  为了方便理解,以下章节都是以maxDepth=1为例分析训练过程,其他深度请自行分析代码。在收集到numPos个TP和numNeg个FP后,就可以训分类器了,过程如下:
1. 首先计算所有Haar特征对这numPos+numNeg个样本patches的特征值,排序后分别保存在的vector中,如图2
图2 分类器训练过程示意图
2. 按照如下方式遍历每个存储特征值的vector
(1) for k= 1 : (numPos+numNeg)
&&&& a. 阈值threshold = 0.5 * (vector[k]+vector[k+1])将vector分为left和right两部分
&&&& b. 计算GAB的输出leftvalue和rightvalue,其中wi为样本的权重,yi为样本标签(1为本 and&0为负)
&&&&&&&& (在训练每个stage的首次迭代中初始化wi= 1 / (numPos+numNeg))
&&&&c. 计算GAB WSE ERROR
(2) 在k的遍历过程中,找到error最小的threshold作为当前vector的Best spilt,以及对应的leftvalue和rightvalue保存下来。可以看出这里的Best spilt threshold就是弱分类器阈值,与Haar特征+leftvalue+rightvalue共同构成一个完成的弱分类器。(注意在第三章中提到的:一个完整的弱分类器包含:Haar特征 + leftValue + rightValue + 弱分类器阈值)
下面给出一个计算弱分类器的流程图,即1-2步骤():
3. 至此,已经有很多弱分类器了。但是哪一个弱分类器最好呢?所以要挑选最优弱分类器放进stage中。
至此,通过步骤(1)-(2)后每一个弱分类器都有一个基于Best split threshold的GAB WSE ERROR,那么显然选择ERROR最小的那个弱分类器作为最优弱分类器放进当前训练的stage中。
4. 依照GAB方法更新当前训练的stage中每个样本的权重
对numPos+numNeg个权重按照如下公式更新权重(注意更新后需要对权重进行归一化)。
5. 计算当前的强分类器阈值stageThreshold
(1) 使用当前的stage中已经训练好的弱分类器去检测样本中的每一TP,计算弱分类器输出值之和保存在eval中(如果不明白,请查阅第三节“并联的弱分类器”)。
(2) 对eval升序排序
(3) 利用minHitRate参数估计一个比例thresholdIdx,以eval[thresholdIdx]作为stage阈值stageThreshold,显然TP越多估计的stageThreshold越准确。
上述(1)-(3)过程由boost.cpp中的CvCascadeBoost::isErrDesired()函数实现,关键代码如下:
<span style="color: # int numPos = <span style="color: #;
<span style="color: # for( int i = <span style="color: #; i & sC i++ ) //遍历样本
<span style="color: #
if( ((CvCascadeBoostTrainData*)data)-&featureEvaluator-&getCls( i ) == <span style="color: #.0F ) //if current sample is TP
<span style="color: #
eval[numPos++] = predict( i, true ); //predict加入true参数后,会返回当前stage中弱分类器输出之和(如上文predict介绍)
<span style="color: # icvSortFlt( &eval[<span style="color: #], numPos, <span style="color: # ); //升序排序
<span style="color: # int thresholdIdx = (int)((<span style="color: #.0F - minHitRate) * numPos); //按照minHitRate估计一个比例作为index
<span style="color: # threshold = eval[ thresholdIdx ]; //取该index的值作为stageThreshold
至此,stage中的弱分类器+stageThreshold等参数都是完整的
6. 重复1-5步骤,直到满足下列任意一个条件后停止并输出当前的stage
(1) stage中弱分类器的数量 &= maxWeakCount参数
(2) 利用当前的stage去检测FP获得当前stage的falseAlarmRate,当falseAlarmRate & maxFalseAlarmRate停止
同样是boost.cpp中的CvCascadeBoost::isErrDesired()函数:
1 int numNeg = <span style="color: #;
2 for( int i = <span style="color: #; i & sC i++ )
if( ((CvCascadeBoostTrainData*)data)-&featureEvaluator-&getCls( i ) == <span style="color: #.0F )
if( predict( i ) ) //predict==1也就是falseAlarm,虚警,即俗称的误报
numFalse++;
<span style="color: # }
<span style="color: # float falseAlarm = ((float) numFalse) / ((float) numNeg);
<span style="color: # return falseAlarm &= maxFalseA
返回false时,停止当前stage训练。
7. 然后重复1-6依次训练每一个stage,直到满足下面任意一个条件:
(1) stage数量 &= numStages
(2) 所有stage总的falseAlarmRate & pow(maxFalseAlarmRate,numStages)
(三)一个小例子
  作者选用了约12000张人脸样本,使用opencv_traincascade.exe程序,设置numPos=numNeg=10000,w=h=24,进行一次简单的训练。如下图红线所示,在stage0的时候,程序直接从正负样本中各抽取1大小的子图片进行训练,获得了一个包含3个决策树的强分类器。
  在stage1的时,扫描了10008张正样本后获得了10000个TP(当前实际hitRate=);扫描了很多负样本窗口后获得了10000个FP(比例为acceptanceRatio=0.243908,即是当前实际falseAlarmRate)。训练完成后获得一个包含5个决策树的强分类器。
缩进可以看到,在一般情况下,随着训练的进行,acceptancesRatio会越来越低,即直观上看每一级收集的FP会越来越像正样本;那么为了区分TP与FP,每一个stage包含的决策树也会逐渐增多。This is&make sense!
(注:保存FP可以通过修改fillPassedSamples()函数实现。)
(四)训练过程总结
其实回顾一下,整个分类器的训练过程可以分为以下几个步骤:
1. 寻找TP和FP作为训练样本
2. 计算每个Haar特征在当前权重下的Best split threshold+leftvalue+rightvalue,组成了一个个弱分类器
3. 通过WSE寻找最优的弱分类器
4. 更新权重
5. 按照minHitRate估计stageThreshold
6. 重复上述1-5步骤,直到falseAlarmRate到达要求,或弱分类器数量足够。停止循环,输出stage。
7. 进入下一个stage训练
到此,整个系列文章就结束了。
可以看到,相比CNN传统的Boosting方法有极强的理论基础,虽然复杂但是非常严谨。
由于weightTrim不是核心内容,所以留给读者自己探索。
另外此文主旨是分析算法,而非工具使用说明,所以不会写有关如何使用opencv_traincascade.exe内容,请读者谅解。
都看到这里了,还不点个赞?标签:&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&国之画&&&& &&&&chrome插件&&
版权所有 京ICP备号-2
迷上了代码!

我要回帖

更多关于 opencv 分类器 的文章

 

随机推荐