opencv人脸检测实现(使用MFC做可视化)(一)
(本篇主要为原理介绍以及效果和结果的讨论,源代码解析在下一篇)
一.实现方法
VS2010可视化程序界面设计:采用MFC
人脸检测部分:采用OpenCV 2.4.9
1 检测方法
采用基于统计的人脸检测方法——Haar分类器。
Haar分类器=Haar-like特征+AdaBoost + 级联 +积分图
①Haar-like特征做检测。
②使用AdaBoost算法训练区分人脸和非人脸的强分类器。
(此处利用自己的白视频照片做训练集,来进行训练,后附上自己的训练过程)
③使用筛选式级联把强分类器级联到一起,提高准确率。
④使用积分图对Haar-like特征求值进行加速
1.1 得到Haar-like特征,区分人脸和非人脸部分
Lienhart等人提出的Haar-like特征如下图
获取Haar-like特征:
拿出其中任意一个矩形框去移位滑动,扫描人脸的区域,用白色部分像素值减去黑色部分像素值,结果即为人脸此区域的量化特征值,扫描完全脸,即得到人脸的量化特征值(矩阵特征),做初步弱分类器。
需要检测时,再对待检测图片做相同操作,最终将所得量化特征值进行对比,相同则为人脸,不同则为非人脸区域,且越不同越好。
1.2 利用AdaBoast算法得到准确的矩阵特征组合(强分类器)
获得初步弱分类器,训练弱分类器,得到合适的阈值
我们先得到样本人脸的Haar-like特征作为初步弱分类器,在代码中可以发现其实其为一个决策二叉树。
对于每次判断,我们需要一个阈值,当特征值大于此阈值才进行下一特征判断。需要训练弱分类器,得到合适的阈值,以尽量达到较低的判断误差,得到最优弱分类器。
方法如下:
① 对于每个特征 f,计算所有训练样本的特征值,并将其排序。
扫描一遍排好序的特征值,对排好序的表中的每个元素,计算下面四个值:
全部人脸样本的权重的和t1;
全部非人脸样本的权重的和t0;
在此元素之前的人脸样本的权重的和s1;
在此元素之前的非人脸样本的权重的和s0;
② 最终求得每个元素的分类误差
在表中寻找r值最小的元素,则该元素作为最优阈值。
获得强分类器
多次迭代得到T个弱分类器,让所有弱分类器投票,再对投票结果按照弱分类器的错误率加权求和,将投票加权求和的结果与平均投票结果比较得出最终的结果。
这里是具体操作:
①给定训练样本集S,共N个样本,其中X和Y分别对应于正样本和负样本; T为训练的最大循环次数;
②初始化样本权重为1/N ,即为训练样本的初始概率分布;
③第一次迭代训练N个样本,得到第一个最优弱分类器,步骤见2.2.2节
④提高上一轮中被误判的样本的权重;
⑤将新的样本和上次本分错的样本放在一起进行新一轮的训练。
⑥循环执行4-5步骤,T轮后得到T个最优弱分类器。
⑦组合T个最优弱分类器得到强分类器,组合方式如下:
1.3 级联强分类器,提高检测率D的同时降低误识率F
设K是一个级联强分类器的层数,D是该级联分类器的检测率,F是该级联分类器的误识率,di是第i层强分类器的检测率,fi是第i层强分类器的误识率。如果要训练一个级联分类器达到给定的F值和D值,只需要训练出每层的d值和f值。
具体训练方法如下:
1)设定每层最小要达到的检测率d,最大误识率f,最终级联分类器的误识率Ft;
2)P=人脸训练样本,N=非人脸训练样本,D0=1.0,F0=1.0;
3)i=0;
4) for Fi>Ft
++i;
ni=0;Fi=Fi-1;
for Fi>f*Fi-1
++ni;
利用AdaBoost算法在P和N上训练具有ni个弱分类器的强分类器;
衡量当前级联分类器的检测率Di和误识率Fi;
For di<d*Di-1;
降低第i层的强分类器阈值;
衡量当前级联分类器的检测率Di和误识率Fi;
N Φ;
利用当前的级联分类器检测非人脸图像,将误识的图像放入N;
1.4 用积分图进行计算加速,降低时间消耗
积分图就是只遍历一次图像就可以求出图像中所有区域像素和的快速算法。
对应于两矩形特征2,矩阵A的值可以用i(5)+ii(1)-ii(4)-ii(2)表示,矩阵B的值用ii(6)+ii(2)-ii(3)-ii(5)表示。此时特征值就是A-B。
2 可视化界面部分
直接看代码中MyFaceDetectDlg.Cpp内容,我已经注释好。
①视频列表,点击我的文件就出现所有在文件夹中的视频名称,点击后选择播放视频的任意一个按钮即可,不必弹出某个文件夹再选择,添加视频的话则放进对应文件夹即可。
②保存功能,原视频保存在文件夹—video中,保存后的视频在文件夹—1,2,3,4...(此处还没来得及修改,当时直接就写了个变量计数,第一次点击播放的视频,则会保存在1中..)中。并且保存已经检测到的人脸的每帧照片(不带红框)。
③具有选择保存和不保存的功能。选择退出人脸检测的功能。
④具有暂停的退出播放对的功能。
如果需要更换播放视频,则一定要点击退出再点击下一个,不然会两个一个交织着播..这个需要改进。
下面有一些效果图:
退出了人脸检测 还在进行人脸检测
二、自己训练一个检测人脸的xml文件(程序在压缩包train内)
准备人脸样本文件,包括从我的白背景视频里保存下来的950张正样本和找来的负样本。
对正负样本做样本描述文件。
把正样本描述文件利用opencv提供的createsamples.exe转换成.vec文件。
利用opencv提供的haartraining.exe对.vec文件进行训练,完成后会自动生成xml文件
我的opencv装在C:\programfiles\opencv。样本放在d:\train。其中正样本放在train\pos_image文件夹下,有950个样本。负样本放在train\neg_image文件夹下,有341个样本,这些样本都是24x24的。
2.2 生成样本描述文件正样本。命令提示符下(窗口+R打开)->cmd。进入D盘的train\pos_image目录。输入dir/b>pos_image.txt。会在pos_image文件夹下生成一个pos_image.txt文件。
样本描述文件格式要加工一下,如fig3。Ctrl+h把bmp替换成bmp 1 0 0 24 24,然后把最后一行的pos_image.txt去掉就Ok了。这里bmp后面的5个参数分别代表个数、和样本矩形
负样本。做法和正样本差不多,只是更简单,加入生成的是neg_image,只需要把最后一行neg_image.txt去掉就行了。
2.3 生成.vec文件
这是针对正样本的。先介绍下Createsamples的命令行代表的意义,这些参数都可以从createsamples的main(int argc,char *argv[])里面去看,是用一个for循环实现的。
主要用的主要是-info、-vec、-bg、-num、-w、-h,如图所示。Opencv_Createsamples.exe –vec pos.vec –infopos_image.txt –bg neg.image.txt –num 950 –w 24 –h 24
2.4 生成.xml文件
这个要用到opencv的haartraining.exe文件,这只说怎么用了。可以去opencv\apps\haartraining\src的haartraining.cpp看。主要用到的参数是:-vec–data –bg –npos –nneg –mem –mode –h –w –nstages。
Opcnv_haartraining.exe –data cascade–vec pos.vec –bg neg_image.txt –nsplits 1 –sym –w 24 –h 24 –mode all –mem 1280
三、结果分析
1.使用opencv给的xml文件时,每个视频都可使用。
2.使用我在白背景下的脸训练的xml文件时,白背景视频表现非常好,其他非常差。
1.使用opencv所给xml:
基本每个视频可达98%,950帧可检测到940帧。
2.使用自己的xml:
白背景下可达100%,其他准确率很差,检测率很低,误测率很高。
我认为应该修改训练时的正样本。
(我的代码里,把我自己的xml那行代码注释掉了...留下了opencv所给的,老师你不嫌弃可以试试看)
由于我自己拍摄视频时很少出现:脸部侧过很多,或者仰头俯头角度过大,以及遮挡脸部超过3/5,如把鼻子和某半边脸全部遮住的情况,其实这些情况都是无法识别出来的,这和算法里Haar-like特征获取有关。
其次是如果识别框选取尺寸偏小,则有些时候可能出现误测的问题。且尺寸大小不同时,视频播放速度可能会变化。
3.3 算法效率
直接用我MFC所设计的可视化软件播放。
不检测时播放时间为11.6s,检测时播放时间为33.7s。每秒处理约28帧。时间变三倍,太慢了。这点需要改进。
我思考并参照了网上的说法。得到三种解决办法:
①修改OpenCV中Haar检测函数的参数,效果非常明显,得出的结论是,搜索窗口的搜索区域是提高效率的关键。
②根据①的启发,可利用颜色空间,粗估肤色区域,以减少人脸的搜索面积。
③考虑到视频中人脸检测的特殊性,上一帧人脸的位置信息对下一帧的检测有很高的指导价值,所以采有帧间约束的方法, 减少了人脸搜索的区域,并且动态调整Haar检测函数的参数,得到了较高的效率。