c#结合emgucv进行人脸检测_机器视觉入门(17)之emgucv实现人脸识别

第三节 又一个EmguCV程序:人脸识别

对于用惯了halcon的玩家,对emguCV其实应该是各种不习惯的,特别是数据类型,我本来准备了一个例子,结果最后一个方法的地方因为数据类型的问题,搞了半天没成功,如果绕开那个方法,整个例子又少了一大亮点,所以我干脆不讲那个例子了。。。这一节我们可以学习下emguCV某些厉害的地方。比如人脸识别。用halcon做人脸识别貌似是比较麻烦的,用emguCV就方便很多了,因为我们可以踩在前人的肩膀上:OpenCV的人脸识别已经有现成的库了,虽然不是很好用。这一节我们就学习下用emguCV调用OpenCV这些库(分类器)做下人脸识别。

首先我们进入下面(4-3-1)这个文件夹:

9c3f08ef8dac912568f572565e1e5ca1.png

图 4-3-1

不出意外你应该只有上面两个XML文件,看字面意思应该分别是用来检测眼睛,前脸,嘴巴,鼻子的。下面的两个XML文件可以自己去网上找,也可以来我们的QQ群文件下载。下面首先,在上一节的工程解决方案上右键-->添加-->新建项目,如下图(4-3-2):

6250fc3c2377f608198a997fae8bdadc.png

图 4-3-2

然后继续选择Windows窗体应用程序,命名为facesDetect。然后按照上一节的方法添加emguCV的四个dll引用,在工具栏右键选择项-->浏览-->添加Emgu.CV.UI.dll。接着在form里面添加两个imagebox控件,两个button控件,如下图(4-3-3):

06a2773b7d9522e45b19697ccc082844.png

图 4-3-3

然后进入编程页面,继续添加using……,然后编写用第一个button(读取图片),来打开一张图片的方法。这个上一节已经有讲解。直接贴图代码吧(4-3-4):

82d37c009b2884cd77605f106bb04cc9.png

图 4-3-4

聪明的朋友肯定会有一个问题:为什么我直接把打开图片的地址给mat变量然后显示,却非要重新声明一个image<>变量来读取图片呢?因为经过多次测试,发现直接用mat来读取图片,有的会显示不了,所以我改成了这个。机智如我~(那么Mat和Image之间具体怎么转换呢?详见本节TIPS 1)好啦,敲完这么多,先调试一下!你肯定记得,要先在项目属性里面把平台目标设置为x64的;再聪明的还会记得要把cvextern.dll库复制到debug文件下。然后运行,发现竟然跑的是上一节的程序。。对的,因为你千算万算,还是棋差一招(4-3-5):

fbfd35e604487e8530bb7384c9fd571d.png

图 4-3-5

如上图(4-3-5),在你新建项目(facesDetect)上右键,选择"设为启动项"。这样就可以了!这时候再运行下,打开几张图片试试,一切ok后,我们进行下一步:人脸检测:

ae6440ffe4386c28c6cafcc9372fbc0f.png

图 4-3-6

9f9c3a126f59062e40196291112541f1.png

图 4-3-7

还是粗鲁的先上了代码,而且一上上两页,完全不考虑客户能不能接受。定睛一看,其实也就三个关键代码而已,只不过我无耻的"重复"了四次。

第一个关键代码是,声明并实例化一个 CascadeClassifier类的变量,并且该类的实例化是带参数的,也就是开头介绍的那个XML文件的地址。我重复了四次,因为我要检测四个部位:脸,眼,鼻,嘴。分别对应四个XML文件。有了这个变量,就可以调用一个方法,

也就是第二个关键代码:DetectMultiScale方法。这个方法有点小难,我们F12进去看看(4-3-8):

5d9c57444daf8781727eee39ceac67e4.png

图 4-3-8

拿人脸来举例,这个意思就是找到可能是人脸的地方,然后求出最小外接矩形。然后返回这个矩形。在我看来,这个方法的逻辑是:用一个小框框在输入的图片上走一遍(遍历),找到人脸后,就求出人脸的最小外接矩形;然后再吧小框框放大一点点,再走一遍,再放大再走一遍……所以这个方法的参数就好理解很多了:第一个参数就是输入的图像了;第二个参数是缩放因子,就是上面说的小框框走完一遍要放大一点,具体放大多少就是这个参数了,如果你填1.1就意味着每次放大10%。第三个参数我的理解是每个人脸总共需要被检测到的次数,负责任的告诉你,这个理解是不准确的,但是这个理解跟正确的意思是正相关的。所以这么理解简单方便且不会影响你的使用;第四个参数就是小框框的最小的尺寸了;第五个参数是小框框最大尺寸。这个算子返回的是矩形变量,所以我们要声明矩形变量作为这个方法的返回值,但是因为一张图中我们是要找出所有的人脸,所以矩形框可能就不止一个,所以其实返回的是一个成员类型为矩形的数组。就像上图中我的代码一样。然后我又重复了四次,毕竟找了四个特征嘛。那么接下来,就是显示了:

第三个关键算子: CvInvoke.Rectangle()是在图片上画一个矩形。第一个参数就是要画图的原图,第二个参数是要画的矩形,第三个参数是矩形的颜色,第四个参数画笔的宽度。本例中用的颜色是 MCvScalar 类,其实跟.net自带的颜色类也是差不多的,三个参数分别代表蓝,绿,红的色度。然后用到一个foreach循环(这是一个C#知识点,不了解的朋友可以看看本节 TIPS 2)。循环完所有的矩形也就都画完了,同样重复四次。

这下就是真的大功告成了!赶快找一张图片试一下吧。不过效果不一定好,因为我们用的图片大小不一样,而我代码里面第二个关键算子的参数都是按照我的图片来选的,还有就是这个库通用性本来就不是那么好。。。建议同学们先找一张自拍慢慢调参数测试看看。合影离得远,就不清晰了,的确容易误判。下图(4-3-9)是我的测试结果,找脸还是蛮准的(蓝色),其次是嘴巴(红色),眼睛(绿色)和鼻子(黄色)就不敢恭维了。。。。

40dbb580c9575020b8aef131c64b513d.png

图 4-3-9

本节TIPS:

1) Mat与Image之间的互相转换如下(4-3-10):

16710b82e3ff11fb49332ccbc968219d.png

图 4-3-10

由Mat转为Image是一个方法,而由Image转为Mat,却只是Image的一个属性。

2)foreach其实是对广义上数组的一个遍历。本例中就是对每一个含有矩形框的数组进行遍历,大白话就是挨个挑出来,然后画到图片上。它的语法结构是:

foreach( 类型 a in A ){ }

其中,大A就是要遍历的数组,小a就是表示A里面每个成员的变量了,所以a的类型就是A里面每个成员的类型,且你要在括号里面写上a的类型。比如本例每个a面前我都加了Rectangele。然后在大括号里面你就可以对每个成员尽情蹂躏了。

感觉自己没说清楚啊,,,举个例子吧,看下图(4-3-11):

3f32d881da853952fd74add80d5fe529.png

图 4-3-11

这是一个控制台应用程序:声明了一个double类型的数组A,里面四个成员,foreach里面的小a就是遍历了这个A的每个成员,每挑出来一个,就加给sum,所以这个算法相当于是把数组A里面的所有成员加一起求和了。我们来看看运行结果(4-3-12):

3736a5b96e6f7ac233ae51b44032f3e2.png

图4-3-12

13.2!对不对呢?不用一点点心算加一起验证的,因为我给的四个数字都是1.1的倍数,所以所有的数加一起也是1.1的倍数,我们忽略小数点,会发现所有跟11相乘的两位数都有一个特点,如下图(4-3-13):

bc74b9bae221854fc312bb938ca0c6d0.png

图 4-3-13

我们用mn表示两位数,那这个特点就是当m+n<10,也就是不产生进位的时候,首尾两个数相加就等于中间那个数。这样就可以一眼辨别输出结果是不是正确了,比如本例的13.2。1+2=3,直接看出结果。其实很多计算也会很有用,比如13 x 22=26 x 11=286 啦啦啦~~~有没有很快?