本文介绍如何使用Haar训练自己的人脸分类器。
参考blog:
https://www.cnblogs.com/tornadomeet/archive/2012/03/28/2420936.html
https://www.cnblogs.com/tornadomeet/archive/2012/03/27/2420088.html
https://blog.csdn.net/yangleo1987/article/details/52883864
https://blog.csdn.net/qq_32502511/article/details/79010509
训练数据包含正样本和负样本,比例设为1:3为好。另外将图片都resize成20x20。
正样本采用我之前一篇blog(https://blog.csdn.net/qq_35290955/article/details/99291496 )提到的
ORL人脸数据集。我只用了其中333张图片,样本少,训练快,只做演示效果,也为了达到1:3。建立一个VS2017工程resize_pic,将上面我那篇blog提到的labels.txt复制到自己的工作目录下,并改名为origin_ORL_datapath.txt。
resize_pic代码如下:
#include "pch.h"
#include
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include //ifstream用到
#include
using namespace std;
using namespace cv;
#define IMG_WIDTH 20 //需要调整图片后的尺寸宽度
#define IMG_HEIGH 20 //需要调整图片后的尺寸高度
int main()
{
Mat src_img;
string path;
//neg_pic转换
/*
string src_img_txt = "origin_neg.txt";//包含所有原始图片路径的txt文件
int convert_num=1000;//3:1 neg:pos
*/
//pos_pic转换
string src_img_txt = "origin_ORL_datapath.txt";//包含所有原始图片路径的txt文件
int convert_num = 333 ;
ifstream file(src_img_txt,ifstream::in);
for (int i = 0; i < convert_num; i++)
{
//string dst_img_name = "D:\\yanxue\\openCV\\项目\\人脸识别\\haar_face_train\\20x20_neg\\";//neg目的文件夹路径
string dst_img_name = "D:\\yanxue\\openCV\\项目\\人脸识别\\haar_face_train\\20x20_ORL_data\\";//pos目的文件夹路径
getline(file, path);
if (!path.empty())//如果读取成功,则将图片和对应标签压入对应容器中
{
src_img=imread(path);//图像读取
Mat dst_img_rsize(IMG_WIDTH, IMG_HEIGH, src_img.type());//resize
resize(src_img, dst_img_rsize, dst_img_rsize.size(), 0, 0, INTER_LINEAR);
char name_num[5];// 千位数的4+\0的1
_itoa_s(i, name_num,10);//10进制 _itoa()--->integer to ascii
dst_img_name += name_num;
dst_img_name += ".jpg";
imwrite(dst_img_name,dst_img_rsize);
printf("%d张图片已完成转换。\n", i + 1);
}
}
printf("Finished!");
}
运行后在20x20_ORL_data文件夹下得到resize后的图片。然后在该文件下像生成labels.txt一样生成pos.txt。然后记事本打开它—>点击编辑—>点击替换---->.jpg全部替换为.jpg 1 0 0 20 20,1代表一张,0 0 20 20代表图片人脸区域。然后需要将其转化为.vec文件,需要从自己的opencv下(我的在
D:\study software\opencv\build\bin\Debug )copy 文件opencv_createsamplesd.exe到自己的工作目录下,打开cmd.exe,进入到工作目录下,执行以下命令:opencv_createsamplesd.exe -info pos.txt -vec pos.vec -num 333 -w 20 -h 20。这样就得到了pos.vec。
负样本要求是:不能包含人脸,且负样本一定不能重复,且尽可能增大负样本的差异性。开始我只从cifar10中的一个类抽取若干图片做负样本,导致负样本单一,且我选的样本量太少,导致训练失败。后来我在以前用过的道路交通数据集里抽取了1000张图片用来训练,效果尚可。其实自己训练可以按照要求在网上随便下载一个数据集使用即可。像上方正样本一样,得到origin_neg.txt放在resize_pic工作目录下,适当修改代码,即可在20x20_neg文件夹下得到图片。继而得到neg.txt,这里不需要修改操作。
找到opencv_traincascaded.exe复制到工作目录,cmd执行命令:
opencv_traincascaded.exe –data xml –vec pos.vec –bg neg.txt –w 20 –h 20 –mode ALL -numPos 33 -numNeg 100
Usage: opencv_traincascaded.exe
-data
-vec
-bg
[-numPos ]
[-numNeg ]
[-numStages ]
[-precalcValBufSize ]
[-precalcIdxBufSize ]
[-baseFormatSave]
[-numThreads ]
[-acceptanceRatioBreakValue = -1>]
--cascadeParams--
[-stageType ]
[-featureType <{HAAR(default), LBP, HOG}>]
[-w ]
[-h ]
--boostParams--
[-bt <{DAB, RAB, LB, GAB(default)}>]
[-minHitRate = 0.995>]
[-maxFalseAlarmRate ]
[-weightTrimRate ]
[-maxDepth ]
[-maxWeakCount ]
--haarFeatureParams--
[-mode
训练则在xml文件夹下最终得到分类器cascade.xml。
补充:
若训练中途中止,看一下是否生成了cascade.xml,生成了就是训练好了。
否则可能是由于FA值已经达到0,没有负样本能够进入下一层进行训练了。
解决方案之一是增加负样本的数量;解决方案之二是用convert_cascade.exe生成xml文件。因为此时各层的训练信息已经有了,FA值达到0也说明训练结果可用。同样适用于FA值已经很低,而下一层的训练时间过长不想等待的情况。(参考 https://blog.csdn.net/yangleo1987/article/details/52883864 )
将自己训练的cascade.xml文件用于 https://blog.csdn.net/qq_35290955/article/details/99291496 中人脸检测部分,测试结果如图:可以看到是有效果的,但因训练样本少,效果不好。