引言
如今,基于人脸的技术和话题可以说是炙手可热,基于大数据和人工智能的人脸识别更是突破了我们的想象力的极限,如果应用中不能集成人脸识别,那就太跟不上潮流了。人脸识别是一个算法密集型的项目,如果自行开发,需要很深厚的数学功底和算法底蕴,成本较高,我一个做C#的,自问没有那么高的水平能够写出那么复杂的算法,即使能,我们的算法能和其它公司相比吗。不过好在现在是一个互联网时代,自己开发不行,那么使用其它现成的人脸识别引擎可行吗?答案当然是可行的。
本系列文章就将先从静态图片的人脸检测开始,逐步讲解C#是如何进行人脸识别的。共分为以下四篇
1. 人脸识别入门—静态照片人脸检测
2. 人脸识别入门—基于视频的人脸检测
3. 人脸识别入门—人脸识别初应用
4. 人脸识别入门—模拟简单的门禁系统应用
在开始之前,我们先来了解一些人脸识别的集成方式和基础知识,为下面的课程做准备。
选择人脸识别引擎的心路历程
通过搜索引擎,可以大致确定集成人脸识别的可选方式有以下几种
1. 集成WebAPI
目前以百度云,腾讯云为首的互联网公司提供了基于WEBAPI的集成方式,可以通过HTTP的方式提交识别请求,识别结果通过JSON串的方式返回。基于HTTP的方式识别人脸是比较慢的,慢的原因在于IO性能,相对来讲,离线版本的API则能够充分利用本机的机器资源,不用往返于所谓的算法云服务器,直接在本地就能完成人脸识别和标记工作。
2. 集成SDK
以Face++和讯飞语音为例,这些公司即提供了在线识别的方式也提供了基于SDK的本地识别方式。本地识别的优点是速度快,集成度高。而且,作为C#,我们还可以搭建自己的云识别平台。如果采用了WEBAPI的,每一笔请求都需要再经过WEBAPI中转,性能上会大打折扣。
因此,如果我们的项目不需要在互联网上访问,可以供选择的只有本地集成SDK一条路了。
收费 OR 免费:免费最好
软件的成本包括人力成本和采购成本,在考虑成本的时候,自然会想到,我们使用的引擎是否收费呢?即使收费再便宜,一旦流量上来了,也是一笔不小的开支。在做技术选型的时候,成本是一个必须要考虑的因素。有了成本因素,再搜索时,就会悲剧的发现,在百度排首页的那些人脸识别引擎都不是免费的。那么有没有免费的呢。有,网上搜索,这次使用Google搜索,可以发现,github上有一系列的的人脸识别开源代码,但经过试用,不太理想。
踏破铁鞋无觅处,得来全不费功夫
正在一筹莫展之际,突然今日头条推送了一条消息,“人脸识别技术从此免费!虹软一举颠覆人工智能“视”界”,踏破铁鞋无觅处,得来全不费功夫,这么好的机会,何不试试呢。
由于当时并不了解虹软,就是看中了人脸识别和免费去的,后来重新百度了一下虹软,发现这个公司有点意意思,竟然同时拿下了OPPO和VIVO,SAMSUNG这三大手机巨头的单子,还是有两把刷子的
下载引擎发现只C++
想到就要做到,于是赶紧打开电脑下载了SDK,吐槽下今日头条,做新闻不放链接太不厚道了。只能百度了,链接在这里http://www.arcsoft.com.cn/ai/arcface.html。
可是下载后,傻眼了,不得不说虹软的诚意,这次免费的SDK可真够厚道的,包括了人脸识别,人脸检测,人脸跟踪所有的API。不过美中不足的是,这SDK竟然只有C++版本的,Windows版本不出C#,这虹软有点不近人情啊。不过伤心归伤心,活得还做,没有C#,那我们就拿C++的包裹出C#来用。其实有了C++就等于有了C#,因为C#本身是兼容C++的,可以直接调用C++的库。
如何用C#调用C++的库
那么,如何使用C#调用C++的库呢,C#提供了两种技术调用C++的DLL
* 静态调用(DCOM+)
* 动态调用(P/Invoke)
我们可以将C或者C++的函数封装成COM组件,在C#中调用时比较方便,但是COM组件需要注册,而且多次注册可能也会导致一些问题,同时在处理C或者C++的类型与COM组件的类型转换的时候也可能有些麻烦
采用动态的方式就是直接用C#调用C或者C++已经写好的动态链接库,这几种方式相对而言,P/Invoke要方便一些
* 因此我们选择P/Invoke的方式*
* P/Invoke是什么
P/Invoke的全称是Platform Invoke (平台调用) 它实际上是一种函数调用机制,通过P/Invoke我们就可以调用非托管DLL中的函数 ,实际上很多NET基类库中定义的类 型内部部调用了从Kernel32.dll,User32.dll,gdi32.dll等非托管DLL中导出的函数。
来看一个简单的例子
[DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
[return: MarshalAsAttribute(UnmanagedType.Bool)] //可写可不写,定义如何封送返回参数
public static extern bool SetCursorPos(int X, int Y);
这段代码的目的就是调用系统中获取鼠标参数的方法。
P/INVOKE的过程
关于P/Invoke的过程,我找到了MSDN上的一张图,如下所示。
在使用P/Invoke调用C/C++方法时,会依次执行以下操作
1 查找包含该函数的非托管DLL
2 将该非托管DLL加载到内存中
3 查找函数在内存中的地址并将其参数按照函数的调用约定压栈
4 将控制权转移给非托管函数
注意:只在第一次调用函数时,才会查找和加载非托管DLL并查找函数在内存中的地址。当非托管函数产生异常时,P/Invoke会将异常传递给托管调用方
看起来很复杂,但使用起来却很简单,只需要在C#中重新声明函数的定义就可以了,然后可以像其它函数一样调用。
注意:只在第一次调用函数时,才会查找和加载非托管DLL并查找函数在内存中的地址。当非托管函数产生异常时,P/Invoke会将异常传递给托管调用方
看起来很复杂,但使用起来却很简单,只需要在C#中重新声明函数的定义就可以了,然后可以像其它函数一样调用。
有关本系列的全部代码可以到我的资源中下载,也可以参考https://github.com/smartkids77/ArcSoft_FreeSDK_Demo中提供的源代码来寻找各个版本的实现,本系列文章就是从这里找到的灵感
下一篇我们将以项目实践的方式来给大家具体介绍是怎么一步步来实现静态照片的人脸检测的。