最近需要实现一个基于生理特征的门禁准入系统,其中一个环节就是要用到人脸识别。虽说人脸识别的算法已经非常成熟,网络上也有很多专业的介绍,但从零做起工作量还是太大,所以想找一些现成的控件。上网一搜,还真发现了不少:如果是基于移动端的android开发,科大讯飞的人脸识别插件应该是个不错的选择;百度也提供人脸识别的插件(没深入看,不知道要不要收费)。对比再三,发现如果是要做C/S程序,精度又不是要求非常高非常高的话,开源的Emgu.CV是最好的选择。但比较无语的是Emgu.CV各版本之间的函数变化较大,目前网上的案例资料基本都是v2.4版本的,最新版V3.1版本的资料可谓少之又少。经过4天的摸索,终于比较完美地实现了人脸识别并对部分方法进行了封装。中间过程由于缺乏相关资料,非常痛苦,所以感觉非常有必要写一篇技术文章。
一、准备工作
1、 摄像头
2、 下载最新版Emgu.CV(http://sourceforge.net/projects/emgucv/files/),我下载的是V3.1版本的,这里一定要注意,不同版本的Emgu.CV某些函数用法相差会比较大。当然熟悉后自然会发现大同小异,但在初用的时候还是比较痛苦的。
3、 安装(安装是傻瓜式的,一路下一步即可),安装完毕后需要通过“我的电脑à属性à高级系统设置à环境变量à系统环境变量”找到“Path”,点击编辑,在最后面增加EMGU的bin目录路径,我的是:C:\Emgu\emgucv-windesktop 3.1.0.2282\bin(你安装在哪个目录,就增加这个目录下bin的路径)
4、 Copy必要的dll至程序运行目录(就是你程序编译后的那个Debug目录)。如图所示:
这些文件都在C:\Emgu\emgucv-windesktop 3.1.0.2282\bin目录下面,其中:x86文件夹是做32位系统时要拷贝的,如果需要做64位的,那就拷贝x64这个目录即可。
5、 在项目工程下引用Emgu.CV和Emgu.CV.word
6、 在工具栏添加Emgu.CV.UI.dll
这时,工具栏会出现4个控件,其中我们这是要用的是“ImageBox”
7、 在Debug目录下建一个文件夹“trainedFaces”。名字可以根据自己喜好,用来存放人脸样本。
至此,准备工作完毕!我的Debug目录下的文件清单如下:
二、界面设计
这个比较简单,拖控件就可以,我的界面如下:
三、代码设计
首先,为方便程序实现,自定义几个必要的类
这个类是用来存放所有人脸样本的,和Emgu.CV v1.0版本不同,从V2.4版本开始,进行样本训练后,得到的不再是直接的名字,而是返回一个识别后的人脸顺号。
这个类主要是用来存放训练好的人脸识别器,我是为了方便将样本文件打包在一起了。
这个类用来存放识别后相关信息,包括:originalImg(原始图片),facesRectangle(识别后的人脸矩形框,包括坐标和大小),names(识别出来的人的姓名)
这是一个枚举,Emgu.CV从v2.4版本开始,为人脸识别器提供了三种实现类型,我这里为少打字,定义了个枚举。待会用switch case的时候可以看到。仅此而已。
其次,创建识别类及方法
1、 Using必要的类
获取指定目录下所有的样本文件,并添加到之前自定义的TrainedFileList类。
根据人脸识别器的类型进行不同的参数设定,并进行训练。Emgu.CV中的faceRecognizer为抽象类,提供了三种具体的实现方式,分别为:EigenFaceRecognizer、FisherFaceRecognizer、LBPHFaceRecognizer,V2.4版本中默认为EigenFaceRecognizer,但在最新的V3.1版本中,是必须制定具体类实现的。(没有深入研究Emgu.CV的源代码,但应该用的是抽象方法工厂模式)。
每个类的初始化值,是从网上找的建议值,如果有兴趣可以自己标定,重新设置。
此方法用来将特定图片中的人脸识别出来,过程很简单:
1)灰度化图片
这里切记要将灰度化后的图片EqualizeHist(灰度均衡化),否则会出现问题。其中:faceClassifier是Emgu.CV提供的名为:CascadeClassifier的人脸检测类,在类创建的时候,我初始化了(源代码文后附上,一看就明白)。这里值得注意的是CascadeClassifier好像是在V3.1版本中提供的,之前的版本貌似有类似的类,具体名字不记得了。
2)通过CascadeClassifier检测人脸
这里仅仅是检测到人脸,具体这个人叫什么还是不知道的。检测后,返回的是一个Rectangle类型的数组。
关键的人脸识别程序,其实也就是用到了Emgu.CV.Face.FaceRecognizer.PredictionResult,这个类的用法和V2.4版本有一个比较大的方法名变化,v2.4版本中不是叫predict,而是叫recognise。返回值也不同,v2.4版本是直接返回名称,而v3.1版本返回的是样本人脸在list中的顺号。
至于怎么在图片上画出姓名,直接看代码即可,此处不再赘述。
最后,程序实现
代码封装好后,实现起来就非常方便了。总共代码157行,呵呵~~也就是说157行代码就可以实现人脸识别程序了:)
几个关键的代码段如下:
Capture是Emgu.CV提供的摄像头控制类,里面的ImageGrabbed事件表示的是获取到图片后触发。可以用来实现模拟摄像头视频获取(其实是在picturebox中显示图片,由于很快,就跟视频一样)
Capture另一个非常关键的方法是QueryFrame()这个方法是用来获取当前的摄像头捕捉到的图面。
特别注意:样本图片的大小一定要和比对图片的大小一致,否则程序不会报错,但会卡死。这个问题在我初做的时候让我几乎抓狂。后来灵感突发,直觉可能是图片大小不一致的问题,结果一试还真是!这里要鄙视下Emgu.CV,好歹也抛出个异常啊。
我的程序中采用的是100*100像素。不宜过大,否则影响程序效率。
由于文档不全,参考了很多其他版本的代码,因为各版本之间的函数、类型相差又比较大,中间过程相当痛苦。上述总结,应该算是最新v3.1版本的第一篇中文文档了。看在辛苦写文档的份上,大家在转载的时候记得保留作者信息:)最后祝愿各位猿门2016端午节快乐。源代码和效果如下:
源代码及可执行文件:http://download.csdn.net/detail/u011616825/9545230
DEBUG下的文件可以COPY出来先执行,里面有必要的DLL,避免直接打开工程编译后被删除。