基於虹軟人臉識別,實現身份認證和自助髮卡
去年下半年開始從BS開發轉戰CS開發了,相繼做了一些大大小小的專案。最近在做的一個人臉識別挺有意思,作為一個初學者我也是摸著石頭過河。這個專案主要是通過攝像頭捕獲的人臉特徵與身份證資訊中的人臉照片進行比對,比對通過的話,可以通過髮卡機寫入資訊至卡片並吐出這張卡片,使用者拿著這張卡片進行後續操作。對於髮卡機只需要把一些操作方法進行封裝,通過串列埠傳送命令就可以了,身份證資訊可以通過讀卡器進行獲取,這裡主要聊一聊進行人臉識別的業務整合,希望對你有所幫助。
基本流程如下圖,使用者在自助髮卡機前選擇髮卡操作,這時自助機會開啟攝像頭,只需要把身份證放到身份證讀卡器上即可,然後攝像頭捕獲到的人臉與身份證上的人臉進行相似度比對,如果比對通過則由髮卡機寫入資訊並進行髮卡操作。
我這裡使用的是虹軟視覺開發平臺的SDK,首先註冊開發者,然後新建應用,你會得到全新的APP_ID和SDK_KEY。每個SDK(免費版的)可以啟用100個裝置也就是100個電腦,而且有效期是一年,一年之後需要給程式更換新的SDK。
然後我們拿到對應的APP_ID以及SDK_KEY之後,就可以下載開發包了,我這裡選擇V3.0版本的SDK,然後配置到程式中。
大致的介面是這個樣子,很普通,左側是視訊,右邊是身份證照片以及一些狀態
開發時用到了三個引擎,
第一個是圖片模式下的人臉檢測引擎
#region 圖片引擎pImageEngine初始化 //初始化引擎 uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE; //檢測臉部的角度優先值 int detectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT; //人臉在圖片中所佔比例,如果需要調整檢測人臉尺寸請修改此值,有效數值為2-32 int detectFaceScaleVal = 16; //最大需要檢測的人臉個數 int detectFaceMaxNum = 5; //引擎初始化時需要初始化的檢測功能組合 int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE; //初始化引擎,正常值為0,其他返回值請參考http://ai.arcsoft.com.cn/bbs/forum.php?mod=viewthread&tid=19&_dsign=dbad527e retCode = ASFFunctions.ASFInitEngine(detectMode, detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine); if (retCode == 0) { lbl_msg.Text=("圖片引擎初始化成功!\n"); } else { lbl_msg.Text = (string.Format("圖片引擎初始化失敗!錯誤碼為:{0}\n", retCode)); } #endregion
第二個是視訊模式下的人臉檢測引擎
#region 初始化視訊模式下人臉檢測引擎 uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO; int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION; retCode = ASFFunctions.ASFInitEngine(detectModeVideo, detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine); if (retCode == 0) { lbl_msg.Text=("視訊引擎初始化成功!\n"); } else { lbl_msg.Text = (string.Format("視訊引擎初始化失敗!錯誤碼為:{0}\n", retCode)); } #endregion
第三個是視訊專用FR引擎,進行活體檢測
#region 視訊專用FR引擎 detectFaceMaxNum = 1; combinedMask = FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_FACE3DANGLE | FaceEngineMask.ASF_LIVENESS; retCode = ASFFunctions.ASFInitEngine(detectMode, detectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoImageEngine); Console.WriteLine("InitVideoEngine Result:" + retCode); if (retCode == 0) { lbl_msg.Text = ("視訊專用FR引擎初始化成功!\n"); } else { lbl_msg.Text = (string.Format("視訊專用FR引擎初始化失敗!錯誤碼為:{0}\n", retCode)); } // 攝像頭初始化 filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice); lbl_msg.Text = (string.Format("攝像頭初始化完成...\n")); #endregion
視訊處理這裡使用的是AForge.Video 視訊處理類庫,然後我們在電腦上接上USB攝像頭,通過此類庫就可以呼叫攝像頭的開關了,那具體的人臉識別我們肯定要放在視訊流渲染事件上了。
我們首先將身份證放在身份證閱讀器上,獲取到身份資訊,並把身份資訊中的人臉照片拿出來,這時我們要用到pImageEngine圖片引擎去從證件照中提取人臉特徵值。然後我在能夠讀到身份證資訊 和 視訊資訊都OK的情況下再去獲取當前攝像頭下的圖片,
//得到當前攝像頭下的圖片 Bitmap bitmap = videoSource.GetCurrentVideoFrame(); //傳入比對函式中進行比對 CompareImgWithIDImg(bitmap, e);
我們這個時候是獲取的視訊中的圖片,需要用到視訊引擎pVideoEngine去從bitmap中檢測人臉並獲取最大的那張臉,並提取人臉特徵值。獲取到視訊中和照片中兩張人臉的特徵值了,那接下來就是將兩張照片交給虹軟人臉比對演算法獲取相似度,我們可以設定一個閾值,超過90%我們就可以認定是同一個人,這個還是要在實際專案中去做權衡。
這樣我們就算是比對成功,可以進行後續業務了。但是還沒完,我刷完身份證後,迅速用身份證對著攝像頭,發現竟然也比對成功了,那如果這樣的話,即使不是本人,別人從手機裡面拿著照片就可以進行認證了,必然造成不安全性,於是我就把活體檢測加上了,活體檢測,顧名思義,就是看看是不是個大活人而非照片。
int retCode_Liveness = -1; //RGB活體檢測 ASF_LivenessInfo liveInfo = FaceUtil.LivenessInfo_RGB(pVideoImageEngine, imageInfo, multiFaceInfo, out retCode_Liveness); //判斷檢測結果 if (retCode_Liveness == 0 && liveInfo.num > 0) { int isLive = MemoryUtil.PtrToStructure<int>(liveInfo.isLive); isLiveness = (isLive == 1) ? true : false; } if (isLiveness)//活體檢測成功
加上活體檢測,即使是照片,就算相似度達到90%以上,我們也不會放過。
到這裡基本功能都已經結束了,但是在多次的除錯中發現,時不時就會來一次閃退,就是記憶體溢位。因為我的這個頁面是單獨彈出來的,這是一個子頁面,之前關閉視窗的時候沒有把引擎釋放,所以導致每次初始化一個引擎大概需要50M左右的記憶體,遲早會出現記憶體溢位的情況。於是我就在這個子視窗關閉的時候,對這三個引擎進行釋放,這個問題就最終解決了。
/// <summary> /// 窗體關閉事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void IdentityVerify_FormClosed(object sender, FormClosedEventArgs e) { //銷燬引擎 int retCode = ASFFunctions.ASFUninitEngine(pImageEngine); Console.WriteLine("UninitEngine pImageEngine Result:" + retCode); //銷燬引擎 retCode = ASFFunctions.ASFUninitEngine(pVideoEngine); Console.WriteLine("UninitEngine pVideoEngine Result:" + retCode); //銷燬引擎 retCode = ASFFunctions.ASFUninitEngine(pVideoImageEngine); Console.WriteLine("UninitEngine pVideoImageEngine Result:" + retCode); if (videoSource.IsRunning) { videoSource.SignalToStop(); //關閉攝像頭 } idCardHelper.CloseService(); this.Dispose(); this.Close(); MemoryUtil.ClearMemory(); }
GitHub已開源此Demo。