样本集:链接:https://pan.baidu.com/s/1n6kRnARsYWr37iuglPbqeg 提取码:sj5r
一、样本准备
包括正样本和负样本,由于是手工准备的数据都是在百度截取的图片,所以样本不多。正样本:100个,负样本208个。
正样本:都是狗的图片,尽量选背景色单一的,由于我是百度出来的图片所以没那么多讲究。
负样本:可以是其他的除了狗狗的图片,我选了各种各样动物的图片。
注:样本数量越多训练出的结果越好,负样本一般是正样本的3~5倍。
二、训练数据准备
(1)准备正样本vec文件所需的info.dat
文件,如下所示(只有部分,根据自己的情况添加):
img/1-0001.png 1 0 0 114 114
img/1-0002.png 1 0 0 386 386
img/1-0003.png 1 0 0 321 321
img/1-0004.png 1 0 0 224 224
img/1-0005.png 1 0 0 212 212
img/1-0006.png 1 0 0 197 197
第一列是图片路径;
第二列是图片中能检测出的样本表数量,由于训练的目标是小狗,所以图片中只有一只狗。当然如果有两只就写2;
第三第四列是图像的坐标,(0,0)就行;
第五第六列是图像宽和高,需要注意是一样的,宽高比需要一样的。
(2)准备最终训练的负样本数据bg.txt
,如下(只有部分):
img/1-f001.png
img/1-f002.png
img/1-f003.png
img/1-f004.png
img/1-f005.png
这里就不需要这么讲究了,路径即可,图像比例随意因为后面训练的时候会切割的。
三、得到vec文件
使用opencv_createsamplesd.exe程序,安装完opencv后在目录:D:\ProgramFiles\opencv\build\install\x64\vc15\bin
可以根据自己的安装情况来找。
执行:.\opencv_createsamplesd -info D:\source\cascadetrain\positive\info .dat -vec D:\source\cascadetrain\mysamples_344.vec -num 100 -bgcolor 0 -bgthresh 0 -w 24 -h 24
即可得到vec文件。
PS D:\ProgramFiles\opencv\build\install\x64\vc15\bin> .\opencv_createsamplesd -info D:\source\cascadetrain\positive\info
.dat -vec D:\source\cascadetrain\mysamples_344.vec -num 100 -bgcolor 0 -bgthresh 0 -w 24 -h 24
Info file name: D:\source\cascadetrain\positive\info.dat
Img file name: (NULL)
Vec file name: D:\source\cascadetrain\mysamples_344.vec
BG file name: (NULL)
Num: 100
BG color: 0
BG threshold: 0
Invert: FALSE
Max intensity deviation: 40
Max x angle: 1.1
Max y angle: 1.1
Max z angle: 0.5
Show samples: FALSE
Width: 24
Height: 24
Max Scale: -1
RNG Seed: 12345
Create training samples from images collection...
Done. Created 100 samples
PS D:\ProgramFiles\opencv\build\install\x64\vc15\bin>
PS D:\ProgramFiles\opencv\build\install\x64\vc15\bin>
注意根据自己的目录来填写真实目录。
四、训练最终的分类器
使用opencv_traincascaded.exe程序,安装完opencv后在目录:D:\ProgramFiles\opencv\build\install\x64\vc15\bin
可以根据自己的安装情况来找。
训练命令:
.\opencv_traincascaded -data D:\source\cascadetrain\haarresult -ve c D:\source\cascadetrain\mysamples_344.vec -bg bg.txt -numPos 85 -numNeg 400 -numStages 12 -featureType HARR -w 24 -h 24 -minHitRate 0.996 -maxFalseAlarmRate 0.5 -mode ALL
这里说明一下,可以用LBP和HAAR两种来训练,但是HAAR训练会久一点,但是精准度好一点;LBP训练的快一点,但是识别度没那么好。我这里使用了HARR训练。
但是在使用Haar训练的时候,stage = 8(设置的是12)的时候突然不动,CPU降了下来,但是内存还占用着,卡住了,如下图:
但是在退出后再执行就能继续了,不知道什么原因。知道的小伙伴麻烦提示一下,谢谢。
至此训练完成。得到训练后的分类器:cascade.xml,可以看到和opencv自带的分类有着相似的结构:
<?xml version="1.0"?>
<opencv_storage>
<cascade>
<stageType>BOOST</stageType>
<featureType>HAAR</featureType>
<height>24</height>
<width>24</width>
<stageParams>
<boostType>GAB</boostType>
<minHitRate>9.9599999189376831e-01</minHitRate>
<maxFalseAlarm>5.0000000000000000e-01</maxFalseAlarm>
<weightTrimRate>9.4999999999999996e-01</weightTrimRate>
<maxDepth>1</maxDepth>
<maxWeakCount>100</maxWeakCount></stageParams>
<featureParams>
<maxCatCount>0</maxCatCount>
<featSize>1</featSize>
<mode>ALL</mode></featureParams>
<stageNum>11</stageNum>
<stages>
<!-- stage 0 -->
<_>
<maxWeakCount>6</maxWeakCount>
<stageThreshold>-1.5430381298065186e+00</stageThreshold>
<weakClassifiers>
注意根据自己的目录来填写真实目录。
五、参数解析
opencv_traincascaded,参数解析。
【-info】 就是跟存放正样本图片目录位置相同的描述文件的路径,可用txt,dat等格式保存,每一行的内容为:xxx.jpg nums left_x left_y width heght。
比如:samples_1.jpg 1 0 0 20 20这样,就是指某个正样本是samples_1.jpg的图片,图片中有1个目标区域,区域的左上角坐标为(0,0),区域的宽度为20像素,长度为20像素。注意样本图片和这里的参数相符。
【-img】如果你要通过一张图片的扭曲形变成多张图片作为样本,就填写这个参数,参数的内容为要扭曲的图片的路径。填入后,-info参数不再有效。
【-vec】要生成用于训练的vec文件的路径,内容为:xxx/xxx/xxx/xxx.vec
【-bg】 如果省略,则使用bgcolor的值填充作为背景。就是跟存放负样本图片(背景图片)目录位置相同的描述文件的路径,可用txt,dat等格式保存,,每一行的内容为:xxx.jpg。这里要注意的是,不要填入图片的完整路径,不然会报错。但训练样本opencv_traincascade也需要传入一个-bg参数,但那个参数的描述文件每一行是要用完整路径的,这有点怪。
【-num】要创建的样本的数量,使用-info生成时,不要比你准备的正样本图片数量大就行了
【-bgcolor】这是创建样本是样本扭曲函数中用来决定像素是有效还是作为背景过滤的基本值,因为操作的是灰度图,所以这个值0范围是~255。
【-bgthresh】决定背景掩码的实际取值范围为bgcolor-bgthresh ——bgcolor-bgthresh
【-inv】【-randinv】这是样本生成时,是否需要反相或随机反相,这个在车牌这样的数字中就需要,比如说白底黑字的车牌和蓝底白字的车牌,两者生成的样本是相反的,前者数字是黑色,后者数字是白色。所以这个参数按你实际需要使用吧。
【-maxidev】一个用于生成前景(有效像素区域)灰度值的常数值,实际样本的前景灰度值会根据这个参数结合随机数产生多种不同的灰度值。
【-maxxangle】对样本图片的x轴方向的扭曲的最大弧度,X轴即是图片水平方向的旋转。
【-maxyangle】对样本图片的y轴方向的扭曲的最大弧度,Y轴即是图片竖直方向的旋转。
【-maxyangle】对样本图片的z轴方向的扭曲的最大弧度,Z轴即是垂直于图像平面的方向的旋转(可理解为旋转轴是一条穿过显示器的垂线)。
【-show】样本创建期间,是否通过imshow显示出每一个生成的样本图片出来。
【-w】要创建的样本图片的宽度,后面的训练样本步骤要使用和这时一样的值,不然会报错
【-h】要创建的样本图片的高度,后面的训练样本步骤要使用和这时一样的值,不然会报错
opencv_traincascaded,参数解析:
-data
训练的分类器的存储目录
-vec
正样本文件,由open_createsamples.exe生成,正样本文件后缀名为.vec
-bg
负样本说明文件,主要包含负样本文件所在的目录及负样本文件名
-numPos
每级分类器训练时所用到的正样本数目,应小于vec文件中正样本的数目,具体数目限制条件为:numPos+(numStages-1)*numPos*(1-minHitRate)<=vec文件中正样本的数目
-numNeg
每级分类器训练时所用到的负样本数目,可以大于-bg指定的图片数目
-numStages
训练分类器的级数,强分类器的个数
-precalcValBufSize
缓存大小,用于存储预先计算的特征值,单位MB
-precalcIdxBufSize
缓存大小,用于存储预先计算的特征索引,单位MB
-baseFormatSave
仅在使用Haar特征时有效,如果指定,级联分类器将以老格式存储
-stageType
级联类型,目前只能取BOOST
-featureType
训练使用的特征类型,目前支持的特征有Haar,LBP和HOG
-w
训练的正样本的宽度,Haar特征的w和h一般为20,LBP特征的w和h一般为24,HOG特征的w和h一般为64
-h
训练的正样本的高
-bt
训练分类器采用的Adaboost类型,Adaboost分为Getle Adaboost,Real Adaboost,Discrete Adaboost,Logit Adaboost,训练中默认采用Getle Adaboost
-minHitRate
影响每个强分类器阈值,每一级分类器最小命中率,表示每一级强分类器对正样本的的分类准确率
-maxFalseAlarm
最大虚警率,影响弱分类器的阈值,表示每个弱分类器将负样本误分为正样本的比例,一般默认值为0.5
-weightTrimRate
0-1之间的阈值,影响参与训练的样本,样本权重更新排序后(从小到大),从前面累计权重小于(1-weightTrimRate)的样本将不参与下一次训练,一般默认值为0.95
-maxDepth
每一个弱分类器决策树的深度,默认是1,是二叉树(stumps),只使用一个特征。
-maxWeakCount
每级强分类器中弱分类器的最大个数,当FA降不到指定的maxFalseAlarm时可以通过指定最大弱分类器个数停止单个强分类器
- Haar特征,CvHaarFeatureParams继承于CvFeatureParams
-mode
值为BASIC、CORE、ALL三种,根据值不同采用不同的Haar特征,BASIC是基本的Haar特征(垂直),CORE是所有的上下Haar特征,ALL是使用所有的Haar特征(包括垂直和45度)
- LBP特征,CvLBPFeatureParams继承于CvFeatureParams
无参数
- HOG特征,CvHOGFeatureParams继承于CvFeatureParams
无参数
六、验证识别效果代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
String haarfile = "D:/source/cascadetrain/haarresult/cascade.xml";
String lbpfile = "D:/source/cascadetrain/cascade.xml";
CascadeClassifier g_haarFaceClassifier, g_lbpFaceClassifier;
int main()
{
if (!g_haarFaceClassifier.load(haarfile))
{
printf("load classifier error\n");
return -1;
}
Mat src, gray;
src = imread("cat3.png");
cvtColor(src, gray, COLOR_BGR2GRAY);
equalizeHist(gray, gray);
vector<Rect> faces;
g_haarFaceClassifier.detectMultiScale(gray, faces, 1.1, 4, 0, Size(65, 65));
for (int i = 0; i < faces.size(); i++)
{
rectangle(src, faces[i], Scalar(0, 0, 255), 2, 8, 0);
}
imshow("src", src);
waitKey(0);
return 0;
}