引言
如今,基于人臉的技術(shù)和話題可以說是炙手可熱谭梗,基于大數(shù)據(jù)和人工智能的人臉識別更是突破了我們的想象力的極限忘晤,如果應(yīng)用中不能集成人臉識別,那就太跟不上潮流了激捏。人臉識別是一個(gè)算法密集型的項(xiàng)目设塔,如果自行開發(fā),需要很深厚的數(shù)學(xué)功底和算法底蘊(yùn)远舅,成本較高闰蛔,我一個(gè)做C#的,自問沒有那么高的水平能夠?qū)懗瞿敲磸?fù)雜的算法图柏,即使能序六,我們的算法能和其它公司相比嗎。不過好在現(xiàn)在是一個(gè)互聯(lián)網(wǎng)時(shí)代蚤吹,自己開發(fā)不行例诀,那么使用其它現(xiàn)成的人臉識別引擎可行嗎随抠?答案當(dāng)然是可行的。
本系列文章就將先從靜態(tài)圖片的人臉檢測開始繁涂,逐步講解C#是如何進(jìn)行人臉識別的拱她。共分為以下四篇
1. 人臉識別入門—靜態(tài)照片人臉檢測
2. 人臉識別入門—基于視頻的人臉檢測
3. 人臉識別入門—人臉識別初應(yīng)用
4. 人臉識別入門—模擬簡單的門禁系統(tǒng)應(yīng)用
在開始之前,我們先來了解一些人臉識別的集成方式和基礎(chǔ)知識扔罪,為下面的課程做準(zhǔn)備秉沼。
選擇人臉識別引擎的心路歷程
通過搜索引擎,可以大致確定集成人臉識別的可選方式有以下幾種
1. 集成WebAPI
目前以百度云矿酵,騰訊云為首的互聯(lián)網(wǎng)公司提供了基于WEBAPI的集成方式唬复,可以通過HTTP的方式提交識別請求,識別結(jié)果通過JSON串的方式返回全肮≈迅В基于HTTP的方式識別人臉是比較慢的,慢的原因在于IO性能倔矾,相對來講,離線版本的API則能夠充分利用本機(jī)的機(jī)器資源柱锹,不用往返于所謂的算法云服務(wù)器哪自,直接在本地就能完成人臉識別和標(biāo)記工作。
2. 集成SDK
以Face++和訊飛語音為例禁熏,這些公司即提供了在線識別的方式也提供了基于SDK的本地識別方式壤巷。本地識別的優(yōu)點(diǎn)是速度快,集成度高瞧毙。而且胧华,作為C#,我們還可以搭建自己的云識別平臺宙彪。如果采用了WEBAPI的矩动,每一筆請求都需要再經(jīng)過WEBAPI中轉(zhuǎn),性能上會大打折扣释漆。
因此悲没,如果我們的項(xiàng)目不需要在互聯(lián)網(wǎng)上訪問,可以供選擇的只有本地集成SDK一條路了男图。
收費(fèi) OR 免費(fèi):免費(fèi)最好
軟件的成本包括人力成本和采購成本示姿,在考慮成本的時(shí)候,自然會想到逊笆,我們使用的引擎是否收費(fèi)呢栈戳?即使收費(fèi)再便宜,一旦流量上來了难裆,也是一筆不小的開支子檀。在做技術(shù)選型的時(shí)候,成本是一個(gè)必須要考慮的因素。有了成本因素命锄,再搜索時(shí)堰乔,就會悲劇的發(fā)現(xiàn),在百度排首頁的那些人臉識別引擎都不是免費(fèi)的脐恩。那么有沒有免費(fèi)的呢镐侯。有,網(wǎng)上搜索驶冒,這次使用Google搜索苟翻,可以發(fā)現(xiàn),github上有一系列的的人臉識別開源代碼骗污,但經(jīng)過試用崇猫,不太理想。
踏破鐵鞋無覓處需忿,得來全不費(fèi)功夫
正在一籌莫展之際诅炉,突然今日頭條推送了一條消息,“人臉識別技術(shù)從此免費(fèi)屋厘!虹軟一舉顛覆人工智能“視”界”涕烧,踏破鐵鞋無覓處,得來全不費(fèi)功夫汗洒,這么好的機(jī)會议纯,何不試試呢。
由于當(dāng)時(shí)并不了解虹軟溢谤,就是看中了人臉識別和免費(fèi)去的瞻凤,后來重新百度了一下虹軟,發(fā)現(xiàn)這個(gè)公司有點(diǎn)意意思世杀,竟然同時(shí)拿下了OPPO和VIVO阀参,SAMSUNG這三大手機(jī)巨頭的單子,還是有兩把刷子的
下載引擎發(fā)現(xiàn)只C++
想到就要做到瞻坝,于是趕緊打開電腦下載了SDK结笨,吐槽下今日頭條,做新聞不放鏈接太不厚道了湿镀。只能百度了炕吸,鏈接在這里http://www.arcsoft.com.cn/ai/arcface.html。
可是下載后勉痴,傻眼了赫模,不得不說虹軟的誠意,這次免費(fèi)的SDK可真夠厚道的蒸矛,包括了人臉識別瀑罗,人臉檢測胸嘴,人臉跟蹤所有的API。不過美中不足的是斩祭,這SDK竟然只有C++版本的劣像,Windows版本不出C#,這虹軟有點(diǎn)不近人情啊摧玫。不過傷心歸傷心耳奕,活得還做,沒有C#诬像,那我們就拿C++的包裹出C#來用屋群。其實(shí)有了C++就等于有了C#,因?yàn)镃#本身是兼容C++的坏挠,可以直接調(diào)用C++的庫芍躏。
如何用C#調(diào)用C++的庫
那么,如何使用C#調(diào)用C++的庫呢降狠,C#提供了兩種技術(shù)調(diào)用C++的DLL
* 靜態(tài)調(diào)用(DCOM+)
* 動態(tài)調(diào)用(P/Invoke)
我們可以將C或者C++的函數(shù)封裝成COM組件对竣,在C#中調(diào)用時(shí)比較方便,但是COM組件需要注冊榜配,而且多次注冊可能也會導(dǎo)致一些問題否纬,同時(shí)在處理C或者C++的類型與COM組件的類型轉(zhuǎn)換的時(shí)候也可能有些麻煩
采用動態(tài)的方式就是直接用C#調(diào)用C或者C++已經(jīng)寫好的動態(tài)鏈接庫,這幾種方式相對而言,P/Invoke要方便一些
* 因此我們選擇P/Invoke的方式*
* P/Invoke是什么
P/Invoke的全稱是Platform Invoke (平臺調(diào)用) 它實(shí)際上是一種函數(shù)調(diào)用機(jī)制芥牌,通過P/Invoke我們就可以調(diào)用非托管DLL中的函數(shù) ,實(shí)際上很多NET基類庫中定義的類 型內(nèi)部部調(diào)用了從Kernel32.dll聂使,User32.dll壁拉,gdi32.dll等非托管DLL中導(dǎo)出的函數(shù)。
來看一個(gè)簡單的例子
[DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
[return: MarshalAsAttribute(UnmanagedType.Bool)]? //可寫可不寫,定義如何封送返回參數(shù)
public static extern bool SetCursorPos(int X, int Y);
這段代碼的目的就是調(diào)用系統(tǒng)中獲取鼠標(biāo)參數(shù)的方法柏靶。
P/INVOKE的過程
關(guān)于P/Invoke的過程弃理,我找到了MSDN上的一張圖,如下所示屎蜓。
在使用P/Invoke調(diào)用C/C++方法時(shí)痘昌,會依次執(zhí)行以下操作
1 查找包含該函數(shù)的非托管DLL
2 將該非托管DLL加載到內(nèi)存中
3 查找函數(shù)在內(nèi)存中的地址并將其參數(shù)按照函數(shù)的調(diào)用約定壓棧
4 將控制權(quán)轉(zhuǎn)移給非托管函數(shù)
注意:只在第一次調(diào)用函數(shù)時(shí),才會查找和加載非托管DLL并查找函數(shù)在內(nèi)存中的地址炬转。當(dāng)非托管函數(shù)產(chǎn)生異常時(shí)辆苔,P/Invoke會將異常傳遞給托管調(diào)用方
看起來很復(fù)雜,但使用起來卻很簡單扼劈,只需要在C#中重新聲明函數(shù)的定義就可以了驻啤,然后可以像其它函數(shù)一樣調(diào)用。
注意:只在第一次調(diào)用函數(shù)時(shí)荐吵,才會查找和加載非托管DLL并查找函數(shù)在內(nèi)存中的地址骑冗。當(dāng)非托管函數(shù)產(chǎn)生異常時(shí)赊瞬,P/Invoke會將異常傳遞給托管調(diào)用方
看起來很復(fù)雜,但使用起來卻很簡單贼涩,只需要在C#中重新聲明函數(shù)的定義就可以了巧涧,然后可以像其它函數(shù)一樣調(diào)用。
有關(guān)本系列的全部代碼可以到我的資源中下載遥倦,也可以參考https://github.com/smartkids77/ArcSoft_FreeSDK_Demo中提供的源代碼來尋找各個(gè)版本的實(shí)現(xiàn)谤绳,本系列文章就是從這里找到的靈感
下一篇我們將以項(xiàng)目實(shí)踐的方式來給大家具體介紹是怎么一步步來實(shí)現(xiàn)靜態(tài)照片的人臉檢測的。