利用Emgu.CV实现人脸识别详解 (C#)--附源码



本文由:姚磊岳([email protected])于2016-6-9撰,转载请保留作者信息。

最近需要实现一个基于生理特征的门禁准入系统,其中一个环节就是要用到人脸识别。虽说人脸识别的算法已经非常成熟,网络上也有很多专业的介绍,但从零做起工作量还是太大,所以想找一些现成的控件。上网一搜,还真发现了不少:如果是基于移动端的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必要的类

2、 几个重要类型和方法的介绍

获取指定目录下所有的样本文件,并添加到之前自定义的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版本的第一篇中文文档了。看在辛苦写文档的份上,大家在转载的时候记得保留作者信息:)最后祝愿各位猿门端午节快乐。源代码和效果如下:

源代码及可执行文件:http://121.43.57.87:8011/code.rar;

DEBUG下的文件可以COPY出来先执行,里面有必要的DLL,避免直接打开工程编译后被删除。

时间: 2024-08-07 21:32:26

利用Emgu.CV实现人脸识别详解 (C#)--附源码的相关文章

JPA入门案例详解(附源码)

1.新建JavaEE Persistence项目 2.导入相关Jar包 3.修改persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <persistence-unit name="N

Java 中break和continue结合标签标示符中断循环示例详解(附源码)

臭名昭著的goto 编程语言中一开始就有goto关键词了.事实上,goto起源于汇编语言的程序控制:"若条件A成立,则跳到这里:否则跳到那里".如果阅读由编译器最终生成的汇编代码,就会发现程序控制里包含了许多跳转.(Java编译器生成它自己的"汇编代码",但是这个代码是运行在Java虚拟机上的,而不是直接运行在CPU硬件上.) goto语句是在源码级上的跳转,这使其招致了不好的声誉.若一个程序总是从一个地方跳到另一个地方,还有什么办法能识别程序的控制流程呢?自从Ed

Linux 下编译Android-VLC开源播放器详解(附源码下载)

这两天需要做音视频播放相关的东西,所以重新找了目前android下的解码库.Android自带的解码库支持不全,因此很多第三方播放器都是自带解码器,绝大部分都是使用FFMpeg作为解码库.我11年的时候也弄过视频播放器,当时也是基于FFMpeg来做.那时候网上有关Android视频解码库的资料不多,只在git上找到一个人移植FFMpeg,把它弄下来编译,有兴趣可以看看当时的文章:Android 视频播放器 faplayer 编译 . 言归正传,今天的主角是大名鼎鼎的VLC,做过视频播放器的人,应

Java之用继承进行设计详解(附源码)

前言 学习了多态之后,看起来似乎所有东西都可以被继承,因为多态是一种如此巧妙的工具.事实上,当我们用现成的类来建立新类时,如果首先考虑使用继承技术,反倒会加重我们的设计负担,使事情变得不必要地复杂起来. 更好的方法是首先选择"组合",尤其是不能十分确定应该使用哪一种方式时.组合不会强制我们的程序设计进入继承的层次结构中.而且,组合更加灵活,因为它可以动态选择类型(因此也就选择了行为):相反,继承在编译时就需要知道确切类型.下面举例说明这一点: 示例源码 package com.mufe

Android中Canvas绘图基础详解(附源码下载)

Android中,如果我们想绘制复杂的自定义View或游戏,我们就需要熟悉绘图API.Android通过Canvas类暴露了很多drawXXX方法,我们可以通过这些方法绘制各种各样的图形.Canvas绘图有三个基本要素:Canvas.绘图坐标系以及Paint.Canvas是画布,我们通过Canvas的各种drawXXX方法将图形绘制到Canvas上面,在drawXXX方法中我们需要传入要绘制的图形的坐标形状,还要传入一个画笔Paint.drawXXX方法以及传入其中的坐标决定了要绘制的图形的形状

Java之协变返回类型详解(附源码)

前言 Java SE5中添加了协变返回类型,它表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型: 示例源码 package com.mufeng.theeighthchapter; class Grain { @Override public String toString() { // TODO Auto-generated method stub return "Grain"; } } class Wheat extends Grain { @Override p

构造器内部的多态方法的行为详解(附源码)

前言 构造器调用的层次结构带来了一个有趣的两难问题.如果在一个构造器的内部调用正在构造的对象的某个动态绑定方法,那会发生什么情况呢?在一般的方法内部,动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所在的那个类,还是属于那个类的导出类. 如果要调用构造器内部的一个动态绑定方法,就要用到那个方法的被覆盖后的定义.然而,这个调用的效果可能相当难于预料,因为被覆盖的方法在对象被完全构造之前就会被调用.这可能会造成一些难于发现的隐藏错误. 从概念上讲,构造器的工作实际上是创建对象(这并非是

java中break和continue的区别详解(附源码)

序言 在自己学习java语言的过程中,很容易把break和continue的用法混淆.为了便于以后快速查阅及温习,在此特留学习笔记一份. 简述 在任何迭代语句的主体部分,都可以用break和continue控制循环的流程.其中,break用于强行退出循环,不执行循环中剩余的语句.而continue则停止执行当前迭代,然后退回循环起始处,开始下一次迭代. 源码 下面这个程序向大家展示了break和continue在for和while循环中的例子: package com.mufeng.thefou

Java之内部类的初级应用详解(附源码)

示例源码 在本节中我们将讲述内部类应用中的一个更典型的情况:外部类将有一个方法,该方法返回一个指向内部类的引用,就像在to()和contents()方法中看到的那样. package com.mufeng.thetenthchapter; public class Parcell2 { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String