在使用opencv自带的分类器haarcascade_frontalface_alt.xml进行人脸识别的基础认识后,决定自己训练一个分类器看一下效果。该过程大致可分为三个阶段:样本采集、分类器训练和运用训练好的分类器进行人脸检测。
1、样本的采集
在训练前,我们需要进行正样本及负样本的采集。
正样本采用的是ORL人脸数据库中的部分图像,本次共选用63张图像,初始图像大小为92*112,但在训练时出现了内存不足的问题,因此将其尺寸归一化为20*20。部分截图如下:
负样本采用的是weizmann团队http://www.wisdom.weizmann.ac.il/~vision/Seg_Evaluation_DB/dl.html 网站上的图像分割数据库里面的灰色图像,总共200幅图片,大小大约在300*200像素,截图如下所示:
图像采集完毕后,将其分别放在两个文件夹pos_img(正样本)和neg_img(负样本)下。再新建一个xml文件夹,xml文件夹存放后面训练过程中产生的数据模型,最后opencv会将其转换生成一个xml文件,也就是最终的分类器。
然后,从OpenCv安装目录中查找出如下两个exe可执行文件:
opencv_createsamples.exe:用于创建样本描述文件,后缀名是.vec。专门为OpenCV训练准备,只有正样本需要,负样本不需要。
opencv_haartraining.exe:是OpenCV自带的一个工具,封装了haar特征提取以及adaboost分类器训练过程。
一般来说,正负样本数目比例为1:3时训练结果较好,但是不是绝对。由于每个样本的差异性不同等因素,所以没有绝对的比例关系。但是负样本需要比正样本多,因为原则上说负样本的多样性越大越好,我们才能有效降低误检率,而不仅仅是通过正样本的训练让其能识别物体。在本次训练中,我选择了63个正样本和200个负样本,均为灰度图像。
样本准备完毕后,打开Windows下的命令行窗口cmd,进入指定目录下。
在当前pos_img目录下生成一个pos.txt记录所有图片的名称。
打开该txt文件,将pos.txt去掉,并将pgm 改为pgm 1 0 0 20 20。
这里1表示当前图片重复出现的次数是1, 0 0 20 20表示目标图片大小是矩形框从(0,0)到(20,20)。
同理,进入neg_img目录,生成neg.txt,然后打开文件去掉neg.txt,其余不做改动。
结束后将pos.txt和neg.txt拷出,与opencv_createsamples.exe文件放在一个目录下:
2、使用opencv_createsamples.exe建立训练需要的参数列表
Windows控制台进入指定目录下,我们之前已经在目录下放了opencv_createsamples.exe文件,在控制台下输入opencv_createsamples.exe可以得到各参数信息:
在命令行窗口输入:
则在当前目录下,产生一个pos.vec文件。
指令介绍:
-vec pos.vec:指定生成的文件,最终生成的就是pos.vec;
-info pos_img\pos.txt:目标图片描述文件,在pos\pos.txt;
-bg neg_img\neg.txt:背景图片描述文件,在neg\neg.txt;
-w 20:输出样本的宽度,20;
-h 20:输出样本的高度,20;
-num 63:要产生的正样本数量,63;
3、训练模型
Windows控制台进入指定目录下,我们之前已经在目录下放了opencv_haartraining.exe文件,在控制台下输入opencv_haartraining.exe可以得到各参数信息:
然后输入指令进行训练:
指令介绍:
-vec pos.vec:正样本文件名;
-bg neg_img\neg.txt:背景描述文件;
-data xml:指定存放训练好的分类器的路径名,也就是前面建立的xml文件夹;
-w 20:样本图片宽度,20;
-h 20:样本图片高度,20;
-mem 1024:提供的以MB为单位的内存,很明显,这个值越大,提供的内存越多,运算也越快;
-npos 45:取45个正样本,小于总正样本数;
-neg 180:取180个负样本,小于总负样本数;
-nstages 5:指定训练层数,层数越高耗时越长;
-nsplits 5:分裂子节点数目, 默认值 为2;(本来设置为5 ,但训练时一直出差错,改为默认值后可正常训练)。
在这里-npos -neg 两个参数若直接与正负样本总数相同,则在训练过程中会出现错误:训练中途,程序突然终止,提示"OpenCV Error: Assertion failed (elements_read == 1) in icvGetHaarTraininDataFromVecCallback, file ..\..\..\..\opencv\apps\haartraining\cvhaartraining.cpp, line 1861"。-npos的意思是每次训练从.vec文件中随机选取npos个正样本。由于存在虚警,在每一次训练一个强分类器之后,会把那些分类错误的从整个样本库中剔除掉,总的样本就剩下 CountVec = CountVec - (1 - minhitrate)* npos,在第二个强分类器的训练过程中就是从剩下的Countvec抽样,一直这样进行nstage次,所以就有CountVec >= (npos + (nstages - 1)*(1 -minhitrate) * npos ) + nneg 。当把npos设置与vec中总样本数相同时,第二个强分类器训练时,必然就会报错,提示样本数不足。只要将取值改小即可。
接下来要做的就是等待训练结束:
训练过程参数解释:
N:层数 %
SMP:样本的使用率
F : +表示通过翻转,否则是-
ST.THR : 分类器的阈值
HR:当前分类器 对正样本识别正确的概率
FA:当前分类器 对负样本识别错误的概率
EXP.ERR : 分类器的期望错误率
训练结束后会在根目录下生成xml.xml文件,在xml文件夹下生成最终的分类器:
4、测试
用自己训练的分类器替换opencv自带的分类器进行人脸识别:
因为训练的样本过少,分类器的层数也不多,分类效果并不好,还有待提高。