首次学习使用opencv的opencv_traincascade来训练级联分类器,特做记录。
首先是准备正负样本,正样本我准备了1486张64*64的人脸图片,负样本随意寻找了3000张图片,不包含人脸。
data–存放训练出来的数据
neg-背景图片
pos-人脸图片
test-用于测试的图片
将3000张负样本图片放入neg目录下,
进入neg目录,执行dir /b > neg.txt
得到neg.txt文件:
neg-0002.jpg
neg-0003.jpg
neg-0004.jpg
neg-0005.jpg
neg-0006.jpg
neg-0007.jpg
neg-0009.jpg
neg-0010.jpg
...
这样发现无法训练,因为可执行未见和负样本不在同一个目录,所以又使用UE编辑器的列块模式,在所有条目前面加了neg/,最后记得删掉最后一行–一般是文件名
neg/neg-0002.jpg
neg/neg-0003.jpg
neg/neg-0004.jpg
neg/neg-0005.jpg
neg/neg-0006.jpg
neg/neg-0007.jpg
neg/neg-0009.jpg
neg/neg-0010.jpg
...
将1486张64*64的人脸图片放入pos目录下,
进入pos目录,执行dir /b > pos.txt
得到pos.txt文件:
fd_001_1.jpg
fd_001_2.jpg
fd_002_1.jpg
fd_002_2.jpg
fd_003_1.jpg
fd_003_2.jpg
fd_004_1.jpg
...
我们必须给每一个图片添加描述信息:
图片中有几个人脸 人脸开始的位置(左上角) 人脸结束的位置(右下角)
1 (0,0) (64,64)
因此,pos.txt变为这样:
fd_001_1.jpg 1 0 0 64 64
fd_001_2.jpg 1 0 0 64 64
fd_002_1.jpg 1 0 0 64 64
fd_002_2.jpg 1 0 0 64 64
fd_003_1.jpg 1 0 0 64 64
fd_003_2.jpg 1 0 0 64 64
fd_004_1.jpg 1 0 0 64 64
...
最后记得删掉最后一行–一般是文件名
可能是为了加快训练的速度,需要把训练的输入数据写入一个.vec文件中,这就用opencv_createsamples.exe了。
opencv_createsamples.exe -info pos/pos.txt -bg neg/neg.txt -w 64 -h 64 -num 1486 -vec pos.vec
上述命令结果如下:
Info file name: pos/pos.txt
Img file name: (NULL)
Vec file name: pos.vec
BG file name: neg/neg.txt
Num: 1486
BG color: 0
BG threshold: 80
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: 64
Height: 64
Max Scale: -1
Create training samples from images collection...
Done. Created 1486 samples
提示成功生成1486个samples了,接下来就可以训练了。
opencv_traincascade.exe -data data -vec pos.vec -bg neg/neg.txt -numPos 500 -numNeg 2000 -numThreads 10 -featureType LBP -w 64 -h 64 -minHitRate 0.95 -numStages 16
可以看到,我只使用了500个正样本(我想这样可能会快一点),2000个负样本(网上说正负样本1:4比较合理),另外我指定的特征为LBP,默认的是HAAR哦,-w -h指定图片的宽高,minHitRate 是做小命中率,numStages 指定为16,如果这个值比较小,比如6,就会很容易出现如下问题:
Required leaf false alarm rate achieved. Branch training terminated.
这个时候你把numStages 改大一些,然后发现又可以训练了。
训练到Stages为12的时候,不小心退出了,索性测试下,能不能识别:
测试代码如下:
#include
#include
#include
#include
#include "wininet.h"
#include
#include
#include
#pragma comment(lib,"Wininet.lib")
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/ml/ml.hpp"
#include
#include
using namespace std;
using namespace cv;
String cascadeName = "F:\\work\\ml\\face-rec\\data\\cascade.xml";//训练数据
//String cascadeName = "haarcascade_frontalface_alt2.xml";//训练数据
int main()
{
CascadeClassifier cascade;//创建级联分类器对象
vector rects;
vector ::const_iterator pRect;
double scale = 1.;
double t;
if (!cascade.load(cascadeName))//从指定的文件目录中加载级联分类器
{
cerr << "ERROR: Could not load classifier cascade" << endl;
return 0;
}
Mat smallImg = imread("bc.jpg");
rects.clear();
printf("begin...\n");
t = (double)cvGetTickCount();//用来计算算法执行时间
cascade.detectMultiScale(smallImg, rects, 1.1, 2, 0,Size(10,10),Size(400, 400));
//|CV_HAAR_FIND_BIGGEST_OBJECT//|CV_HAAR_DO_ROUGH_SEARCH|CV_HAAR_SCALE_IMAGE,
t = (double)cvGetTickCount() - t;
printf("detection time = %g ms\n\n", t / ((double)cvGetTickFrequency()*1000.));
for (pRect = rects.begin(); pRect != rects.end(); pRect++)
{
rectangle(smallImg, cvPoint(pRect->x, pRect->y), cvPoint(pRect->x + pRect->width, pRect->y + pRect->height), cvScalar(0, 255, 0));
}
imshow("result",smallImg);
waitKey(0);
return 0;
}
测试中,我分别使用了自己训练的分类器和opencv自带的分类,发现还是opencv自带的明显好一些。
这张图片是使用我训练出来的参数识别数来的人脸,在脸比较正的时候,识别出来的概率还蛮大的。
这张也是脸比较正的,也能识别,但是左下角的绿色方块是什么鬼?
像这样环境比较杂乱的就完全乱了…