使用opencv的Haar训练自己的人脸分类器

转载: ttps://blog.csdn.net/yangleo1987/article/details/52883864

以下是我学习的几个博文:

http://www.cnblogs.com/wengzilin/p/3849118.html

http://www.cnblogs.com/wengzilin/p/3845271.html

http://www.cnblogs.com/tornadomeet/archive/2012/03/28/2420936.html

http://blog.csdn.net/u010603823/article/details/52557760


1.准备训练样本图片

1.1样本的采集:

      样本图片最好使用灰度图,且最好根据实际情况做一定的预处理;样本数量越多越好,尽量高于1000,样本间差异性越大越好

正负样本比例为13最佳尺寸为20x20最佳。

1.1.1正样本

        训练样本的尺寸为20*20(opencv推荐的最佳尺寸),且所有样本的尺寸必须一致。如果不一致的或者尺寸较大的,可以先将所有样本统一缩放到20*20。   

        以下就是我用来训练的样本

     使用opencv的Haar训练自己的人脸分类器


1.1.2负样本

       这里要提醒一下,虽然负样本就是样本中不存在正样本的内容。但也不能随意的找些图片来作为负样本,比如什么天空,大海,森林等等。最好是根据不同的项目选择不同的负样本,比如一个项目是做机场的人脸检测,那么就最好从现场拍摄一些图片数据回来,从中采集负样本。其实正样本的采集也应该这样。不同的项目,就采集不同的正样本和负样本。因为项目不同,往往相机的安装规范不同,场景的拍摄角度就不同。

        关于负样本的尺寸,只要不小于正样本的尺寸就好。至于为什么,我没有深究,知道原因的也可以讲讲。

       以下就是我用来训练的样本

使用opencv的Haar训练自己的人脸分类器


1.1.3 准备好工作目录

negdata目录: 放负样本的目录

posdata目录: 放正样本的目录

xml目录: 新建的一个目录,为之后存放分类器文件使用

negdata.txt: 负样本路径列表

posdata.txt: 正样本路径列表

pos.vec: 后续自动生成的样本描述文件

opencv_createsamples.exe: 生成样本描述文件的可执行程序(opencv自带)

opencv_haartraining.exe: 样本训练的可执行程序(opencv自带)


使用opencv的Haar训练自己的人脸分类器


1.1.4获取样本路径列表

        接下来就需要或者正负样本的路径列表,方便训练的时候能够找到每一个样本。

       先进入正样本数据的目录下(posdata目录),新建一个文本文档,然后把文件扩展名修改为bat,然后编辑文本如下:

使用opencv的Haar训练自己的人脸分类器

     编辑完成后,双击该文件。就会在该目录下生成一个num.txt,如下:

使用opencv的Haar训练自己的人脸分类器

       打开num.txt,使用文本编辑器的替换功能,做一些替换工作

       替换1:将绝对路径替换成相对路径

       使用opencv的Haar训练自己的人脸分类器

  

        替换21代表个数,后四个分别对应left top width height

       使用opencv的Haar训练自己的人脸分类器

       将替换好的num.txt复制到posdata目录的同级目录下,重命名为posdata.txt


       同理,负样本的路径列表按照上述方法进行。在negdata目录下准备好num.txt后,复制到negdata目录的同级目录下,重命令为negdata.txt



2.准备样本描述文件

      样本描述文件就是一个.vec文件opencv训练准备的,只有正样本需要,负样本不需要

       打开cmd.exe,进入到工作目录下,执行以下命令



opencv_createsamples.exe-info  posdata.txt-vec pos.vec -num 17 -w 20 -h 20

-info,指样本说明文件

-vec,样本描述文件的名字及路径

-num,总共几个样本,要注意,这里的样本数是指标定后的20x20的样本数,而不是大图的数目,其实就是样本说明文件第2列的所有数字累加

-w-h指明想让样本缩放到什么尺寸。这里的奥妙在于你不必另外去处理第1步中被矩形框出的图片的尺寸,因为这个参数帮你统一缩放!(我们这里准备的样本都是20*20)


   

        执行结束后,会在工作目录下自动生成一个pos.vec文件


3.样本训练

       样本训练需要用到的工具:(1)opencv_haartraining.exe,opencv自带的一个工具该工具封装了haar特征提取以及adaboost分类器训练过程 (2)convert_casade.exe用于合并各级分类器成为最终的xml文件.

      打开cmd.exe进入工作目录,执行以下命令



opcnv_haartraining.exe–data xml –vec pos.vec –bg negdata.txt –nsplits 1 –sym –w24 –h 24 –mode all –mem 1280

-data:           指定生成的文件目录(将来存放各级分类的地方)

-vec:           样本描述文件
-bg              负样本描述文件名称,就是负样本的路径列表
-nstage 20       指定训练层数,推荐15~20,层数越高,耗时越长。
-nsplits         分裂子节点数目,选取默认值 2 1表示使用简单的stump classfier分类
-minhitrate      最小命中率,即训练目标准确度。
-maxfalsealarm   最大虚警(误检率),每一层训练到这个值小于0.5时训练结束,进入下一层训练
-npos            每个阶段用来训练的正样本数目
-nneg          每个阶段用来训练的负样本数目
-mode          all指定haar特征的种类,basic仅仅使用垂直特征,all表示使用垂直以及45度旋转特征
-sym或者-nonsym:后面不用跟其他参数,用于指定目标对象是否垂直对称,若你的对象是垂直对称的,比如脸,则垂直对称有利于提高训练速度

-mem:           表示允许使用计算机的1280M内存


3.1训练过程


  在训练过程中可能会遇到很多问题,大家如果遇到什么问题,就直接到网搜,肯定是搜得到的,我这里把我遇到的问题贴出来,我开始训练后,大约过了20几分钟,程序就卡住了,感觉进入死循环,出不来了。如下图所示。

使用opencv的Haar训练自己的人脸分类器

 

       但是进入xml目录下,已经生成了10个目录(这10个就是弱分类器),通过网上查找,解决如下:

      

       训练到第10层后再也没有反应,这是由于FA值已经达到0,没有负样本能够进入下一层进行训练了。

         解决方案之一是增加负样本的数量。

        解决方案之二是convert_cascade.exe生成xml文件。因为此时各层的训练信息已经有了,FA值达到0也说明训练结果可用。同样   适用于FA值已经很低,而下一层的训练时间过长不想等待的情况。

    因为没有找到convert_cascade.exe这个可执行文件,在网上搜到了这个源代码,将其写成一个函数,然后直接调用即可,代码如下


intcascade()

{

char*haartraining_ouput_dir =”G://图片数据//人脸//face//xml”; //根据实际情况修改

char*ouput_file =”G://图片数据//人脸//face//xml//haar_adaboost.xml”;//根据实际情况修改


constchar*size_opt = ”–size=”;

charcomment[1024];

CvHaarClassifierCascade*cascade = 0;

CvSizesize;


size.width= 20; //根据实际情况修改

size.height= 20; //根据实际情况修改


cascade= cvLoadHaarClassifierCascade(haartraining_ouput_dir,size);

if(!cascade ){

fprintf(stderr,”Inputcascade could not be found/opened\n”);

return-1;

}

sprintf(comment, ”Automaticallyconverted from %s, window size = %dx%d”,ouput_file, size.width, size.height );

cvSave(ouput_file, cascade, 0, comment, cvAttrList(0,0) );

return0;

}


  

4.开始测试




inthaarDection()

{

Matimage;

CascadeClassifiercascade, nestedCascade;//创建级联分类器对象

doublescale = 1.3;

// image = imread(“obama_gray.bmp”,1);

image= imread(”D://haarcascade_frontalface_alt.jpg”,1);

namedWindow(”result”,1 );//opencv2.0以后用namedWindow函数会自动销毁窗口


if(!cascade.load( cascadeName ) )//从指定的文件目录中加载级联分类器

{

cerr<< ”ERROR:Could not load classifier cascade”<< endl;

return0;

}


if(!image.empty() )//读取图片数据不能为空

{

detectAndDraw(image, cascade, scale );


IplImageqImg;

qImg= IplImage(image);

cvSaveImage(”D://aaa.jpg”,&qImg);


waitKey(0);

}


return0;

}


voiddetectAndDraw( Mat&img,

CascadeClassifier&cascade,

doublescale)

{

inti = 0;

doublet = 0;

vector<Rect>faces;

conststaticScalarcolors[] = { CV_RGB(0,0,255),

CV_RGB(0,128,255),

CV_RGB(0,255,255),

CV_RGB(0,255,0),

CV_RGB(255,128,0),

CV_RGB(255,255,0),

CV_RGB(255,0,0),

CV_RGB(255,0,255)};//用不同的颜色表示不同的人脸


Matgray, smallImg( cvRound (img.rows/scale),cvRound(img.cols/scale),CV_8UC1);//将图片缩小,加快检测速度


cvtColor(img,gray, CV_BGR2GRAY);//因为用的是类haar特征,所以都是基于灰度图像的,这里要转换成灰度图像

resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);//将尺寸缩小到1/scale,用线性插值

equalizeHist(smallImg, smallImg );//直方图均衡


t= (double)cvGetTickCount();//用来计算算法执行时间


//检测人脸

//detectMultiScale函数中smallImg表示的是要检测的输入图像为smallImgfaces表示检测到的人脸目标序列,1.1表示

//每次图像尺寸减小的比例为1.12表示每一个候选矩形需要记录2个邻居,CV_HAAR_SCALE_IMAGE表示使用haar特征,Size(30,30)

//为目标的最小最大尺寸

cascade.detectMultiScale(smallImg, faces,

1.1,2, 0

//|CV_HAAR_FIND_BIGGEST_OBJECT

//|CV_HAAR_DO_ROUGH_SEARCH

|CV_HAAR_SCALE_IMAGE

,

Size(30,30) );


t= (double)cvGetTickCount()- t;//相减为算法执行的时间

printf(”detectiontime = %g ms\n”,t/((double)cvGetTickFrequency()*1000.));

/*

for(vector<Rect>::const_iterator r = faces.begin(); r !=faces.end(); r++, i++ )

{

MatsmallImgROI;

vector<Rect>nestedObjects;

Pointcenter;

Scalarcolor = colors[i%8];

intradius;

center.x= cvRound((r->x + r->width*0.5)*scale);//还原成原来的大小

center.y= cvRound((r->y + r->height*0.5)*scale);

radius= cvRound((r->width + r->height)*0.25*scale);

circle(img, center, radius, color, 3, 8, 0 );

smallImgROI= smallImg(*r);

}*/

cv::imshow(”result”,img);

}


效果如下:人脸都检测到了,但还是有误检情况,需要继续优化

使用opencv的Haar训练自己的人脸分类器