人臉識别Demo解析C#
2019-08-19 17:53
碼仔很忙
閱讀(5512)
評論(1)
編輯
收藏
舉報
概述
不管你注意到沒有,人臉識别已經走進了生活的角角落落,釘釘已經支援人臉打卡,火車站實名認證已經增加了人臉自助驗證通道,更别提各個城市建設的『智能城市』和智慧大腦了。在人臉識别業界,通常由人臉識别提供商和人臉識别應用接入方組成,從頭到尾研發人臉識别技術需要極強的專用技術知識和數學算法功底,對于大多數企業來說,選擇人工智能AI公司現成的人臉識别技術引擎是一個比較适合的解決方法。虹軟公司在2017年開放了人臉識别平台1.0版本,經過三年的技術疊代和更新,目前已經推出了2.2版本,主打離線,免費,适合場景比較廣泛。為了友善開發者接入,虹軟官方提供了各個語言版本的Demo程式,由于虹軟并沒有提供C#版本的SDK,是以,他們提供的C#版本的SDK就更有參考價值了。
虹軟Demo的下載下傳位址如下:https://github.com/ArcsoftEscErd/ArcfaceDemo_CSharp_2.2 在開始之前,建議你下載下傳它。
什麼是人臉識别
人臉識别,是基于人的臉部特征資訊進行身份識别的一種生物識别技術。用錄影機或攝像頭采集含有人臉的圖像或視訊流,并自動在圖像中檢測和跟蹤人臉,進而對檢測到的人臉進行臉部識别的一系列相關技術,通常也叫做人像識别、面部識别。 而人臉識别的過程可以簡的概括為:檢測人臉框->提取人臉特征資訊->人臉庫檢索比對資訊三個過程。
人臉識别的應用場景
人臉識别主要用于身份識别。由于視訊監控正在快速普及,衆多的視訊監控應用迫切需要一種遠距離、使用者非配合狀态下的快速身份識别技術,以求遠距離快速确認人員身份,實作智能預警。人臉識别技術無疑是最佳的選擇,采用快速人臉檢測技術可以從監控視訊圖象中實時查找人臉,并與人臉資料庫進行實時比對,進而實作快速身份識别。 在現實生活中,從最常見的人臉門禁,到實名制安檢,景區驗票,公司或者學校的人臉簽到,無人超市等都有廣泛的應用。
什麼是活體檢測
活體檢測檢測顧名思義就是通過識别活體上的生理資訊,來區分用照片、矽膠、塑膠等非生命物質僞造的生物特征。人臉識别應用中的活體檢測技術用來判斷系統采集到的人臉圖像是否來源于真實的人臉,以防止照片、視訊等僞造的人臉圖像被輸入到系統造成誤判,活體檢測在無人值守場景下的人臉識别商業應有中顯得至關重要。
虹軟人臉識别SDK
目前市面上有很多人臉識别技術方案,從是否需要使用時聯網可以分為線上和離線,從接入方式上可以分為本地識别和伺服器大資料識别等方式,虹軟提供的是基于本地算法特征的離線識别SDK,其基礎算法使用C編寫,提供全平台的離線支援.
虹軟視覺視覺開放平台
虹軟人臉識别SDK通過視覺開放平台提供,包含人臉識别場景中最常用到的功能元件,例如:人臉檢測,人臉識别,年齡性别檢測,活體檢測 等,其中人臉檢測針對靜态和動态檢測場景分别進行了算法優化,從中派生的性别和年齡檢測擴充了人臉識别的使用場景,活體檢測元件可以有效的保證人臉識别應用的安全性。 通路https://ai.arcsoft.com.cn/third/mobile.html?cnblogs 按照網站的提示,可以注冊使用者和下載下傳SDK包。
虹軟的人臉識别Demo簡介
虹軟的SDK,和大量基于Restful風格的接口不同,沒有使用普通的基于HTTP的方式,也并沒有提供C#語言的SDK包,僅提供了C語言的SDK,對于C#接入有一定的困難,在釋出之初有不少大神自行編寫接入Demo程式,後來,虹軟官方也出了Demo程式,從2018年1月的第一個版本到現在随着SDK更新的2.2版本,代碼結構和注釋更為清晰。
Demo效果展示
Demo是标準的C# WINFORM工程樣式,通過GitHub,下載下傳下來之後,可以直接使用VS打開. 打開之後,有一個readme.md檔案,十分重要,在開始之前,請務必仔細看一下。這裡把要點給大家總結一下。
- 注冊并登入虹軟開發者賬号,下載下傳Win32/Win64的ArcFace的SDK,建議下載下傳2.2版本的。
- 将下載下傳時生成的APPID和KEY填放app.config檔案中的對應位置。
- 将下載下傳的檔案解壓,根據本平台将dll解壓到對應平台的目錄下面 如果上述幾步都OK,程式可以正常運作,如果中間有問題,可以參考readme中的内容進行排查。
一般OK之後,系統彈出正常運作的視窗,網上找幾張明星照片進行注冊,對比。 如下圖所示:
可以看到,虹軟Demo已經可以正确的識别人臉資訊。
Demo中還提供了活體檢測功能,如果你的機器沒有攝像頭,可以外插一個USB的攝像頭,點選啟用攝像頭,打開它。
如果我們用自己的人臉識别,會顯示RGB活體,如果是用照片或者視訊嘗試識别,會顯示『RGB假體』
人臉識别Demo代碼解析
接下來進入正題,讓我們打開工程視圖,從代碼角度解析一下虹軟人臉識别Demo的代碼結構及主要流程。
從上圖中可以發現代碼結構還是很清晰
目錄 | 說明 |
---|---|
Entity | 用于放置一些實體類 |
lib | 放置的第三庫,主要是用于擷取視訊幀的内容 |
SDKModels | SDK的字段模型類,主要是和SDK進行互動,普通使用時無需關注 |
SDKUtils | 針對SDK功能的C#封裝,建議使用Utils中的二次封裝類 |
Utils | 提供的一些工具類,這些類将複雜的SDK操作變得簡單,我們可以在項目中直接使用這些類 |
所有的界面功能都在FaceForm.cs中,我們打開代碼視圖,代碼各個區域的代碼結構清晰,我們來看一下主要部分的功能。
參數定義
參數定義部分主要是針對一些參數進行定義,有相應的注釋,我們需要關注的是圖檔大小和相似度。
private long maxSize = 1024 * 1024 * 2;
這個參數定義了可以識别的最大圖檔大小,可以根據需要進行調整。
private float threshold = 0.8f;
這個參數定義了置信度,也就是當相似度達到多少時,我們認為是一個人
引擎初始化
初始化部分的一個重要的方法 InitEngines(),作用是用來初始化人臉識别引擎。
這部分的代碼首先擷取配置檔案的資訊,然後讀取這些資訊,并進行引擎的激活操作,如果出現錯誤,則彈出提示資訊。
這裡需要注意的是,由于C#是支援多CPU架構的,虹軟SDK的32和64位的版本對應的dll并不相同,是以需要我們自行判斷目前是運作在哪個模式下的。
var is64CPU = Environment.Is64BitProcess;
在判斷CPU之後,嘗試加載對應的DLL,并調用激活過程。
int retCode = 0;
try
{
retCode = ASFFunctions.ASFActivation(appId, is64CPU ? sdkKey64 : sdkKey32);
}
catch (Exception ex)
{
//禁用相關功能按鈕
ControlsEnable(false, chooseMultiImgBtn, matchBtn, btnClearFaceList, chooseImgBtn);
if (ex.Message.Contains("無法加載 DLL"))
{
MessageBox.Show("請将sdk相關DLL放入bin對應的x86或x64下的檔案夾中!");
}
else
{
MessageBox.Show("激活引擎失敗!");
}
return;
}
虹軟SDK需要激活才能使用,在激活時,必須保證你的裝置可以連接配接到網際網路。如果無法連接配接會激活失敗。
接下來的代碼,對于引擎的功能進行配置,在大多數情況下,我們保持預設配置即可。如果需要調整,可以重點關注下面的參數
//人臉在圖檔中所占比例,如果需要調整檢測人臉尺寸請修改此值,有效數值為2-32
int detectFaceScaleVal = 16;
//最大需要檢測的人臉個數
int detectFaceMaxNum = 5;
detectFaceScaleVal 為人臉占用圖檔的比例,簡單的說,就是一張臉在圖檔中的比例,這個數值越大,能夠檢測到的人臉越小。detectFaceMaxNum 就是檢測到的最大人臉數,檢測人臉越多,程式需要占用的記憶體也就越多。
接下來的參數combinedMask,定義了引擎的能力,建議預設保持全開,如果對性能有所要求,可以隻開啟必要的功能。
//引擎初始化時需要初始化的檢測功能組合
int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_AGE | FaceEngineMask.ASF_GENDER | FaceEngineMask.ASF_FACE3DANGLE;
調用 ASFFunctions.ASFInitEngine 就可以初始化引擎
retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority,
detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
retCode傳回值為0時代表初始化成功。
按照相同的方法初始化其它引擎,包括 人臉檢測FR引擎,RGB專用FR引擎,IR專用RGB引擎,它們隻是參數不同,在實際使用中,我們可以根據需要進行微調。
其它類似的操作,可以在此頁面中檢視,由于虹軟Demo已經對操作進行了詳細的封裝,展現在FaceForm.cs中的代碼都是一些和控件互動的代碼,再詳細分析意義并不大,接下來我們分析一些比較細緻的代碼,也就是潛藏在FaceUtil類中的一些函數的實作.
檢測人臉資訊
檢測人臉資訊有兩種方式,從照片中檢測和從視訊中檢測,先看從照片中檢測
public static ASF_MultiFaceInfo DetectFace(IntPtr pEngine, Image image)
{
lock (locks)
{
ASF_MultiFaceInfo multiFaceInfo = new ASF_MultiFaceInfo();
if (image != null)
{
/*如果照片大小過大,則進行縮放并對齊*/
if (image.Width > 1536 || image.Height > 1536)
{
image = ImageUtil.ScaleImage(image, 1536, 1536);
}
else
{
/*如果照片大小正常,直接進行對齊*/
image = ImageUtil.ScaleImage(image, image.Width, image.Height);
}
if(image == null)
{
return multiFaceInfo;
}
/*轉化為SDK專用格式,後面需要手工釋放記憶體*/
ImageInfo imageInfo = ImageUtil.ReadBMP(image);
if(imageInfo == null)
{
return multiFaceInfo;
}
/*調用引擎*/
multiFaceInfo = DetectFace(pEngine, imageInfo);
/*釋放圖檔占用的記憶體*/
MemoryUtil.Free(imageInfo.imgData);
return multiFaceInfo;
}
else
{
return multiFaceInfo;
}
}
}
注意上述代碼中兩個比較重要的 ScaleImage 和 ReadBMP 方法,其中ScaleImage方法是将圖檔處理成虹軟人臉引擎建議的格式,需要圖檔的寬度為4的整數倍。
public static ImageInfo ReadBMP(Image image)
{
ImageInfo imageInfo = new ImageInfo();
Image<Bgr, byte> my_Image = null;
try
{
//圖像灰階轉化
my_Image = new Image<Bgr, byte>(new Bitmap(image));
imageInfo.format = ASF_ImagePixelFormat.ASVL_PAF_RGB24_B8G8R8;
imageInfo.width = my_Image.Width;
imageInfo.height = my_Image.Height;
imageInfo.imgData = MemoryUtil.Malloc(my_Image.Bytes.Length);
MemoryUtil.Copy(my_Image.Bytes, 0, imageInfo.imgData, my_Image.Bytes.Length);
return imageInfo;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (my_Image != null)
{
my_Image.Dispose();
}
}
return null;
}
這裡需要注意,此方法中調用了 MemoryUtil.Malloc 方法配置設定了非托管記憶體,在後面需要調用 MemoryUtil.Free() 方法釋放記憶體。
檢測結果傳回為 ASF_MultiFaceInfo 結構體,其中的faceRects 為人臉結果集,faceNum 為人臉數目,通過下面的代碼,可得到人臉識别的位置資訊
MRECT rect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
性别和年齡檢測
FaceUtil類中還提供了 年領檢測和性能檢測的方法。AgeEstimation和GenderEstimation,其基本操作方式也是先申請記憶體,然後調用原生的Native的對應方法,再釋放記憶體的過程。
public static ASF_AgeInfo AgeEstimation(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out int retCode)
{
retCode = -1;
IntPtr pMultiFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_MultiFaceInfo>());
MemoryUtil.StructureToPtr(multiFaceInfo, pMultiFaceInfo);
if (multiFaceInfo.faceNum == 0)
{
return new ASF_AgeInfo();
}
//人臉資訊處理
retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_AGE);
if (retCode == 0)
{
//擷取年齡資訊
IntPtr pAgeInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_AgeInfo>());
retCode = ASFFunctions.ASFGetAge(pEngine, pAgeInfo);
Console.WriteLine("Get Age Result:" + retCode);
ASF_AgeInfo ageInfo = MemoryUtil.PtrToStructure<ASF_AgeInfo>(pAgeInfo);
//釋放記憶體
MemoryUtil.Free(pMultiFaceInfo);
MemoryUtil.Free(pAgeInfo);
return ageInfo;
}
else
{
return new ASF_AgeInfo();
}
}
需要注意的是,要使用性别和年齡檢測,必須在人臉檢測SDK初始化的時候開啟對應的功能,也就是說combinedMask值中必須包含 FFaceEngineMask.ASF_AGE |FaceEngineMask.ASF_GENDER;
從照片中擷取特征資訊
上一步擷取人臉框後,就可以調用人臉識别引擎擷取人臉特征資訊了,将照片資訊傳入人臉識别引擎,傳回人臉模型資訊
IntPtr pFaceModel = ExtractFeature(pEngine, imageInfo, multiFaceInfo, out singleFaceInfo);
我們來看看 ExtractFeature 方法,這裡的Demo寫的比較複雜,而且幾個方法都是同名的方法,我們來詳細分析一下
首先找到
IntPtr ExtractFeature(IntPtr pEngine, Image image, out ASF_SingleFaceInfo singleFaceInfo)
方法.
由于人臉識别的第一步是先檢測到人臉框的位置,是以這個方法就是對傳入的圖檔進行一個預先處理的分析,并且調用了人臉檢測的方法檢測人臉。
.... 其它代碼,主要是對傳入圖檔進行分析,轉換大小,如果為空或者圖檔不合法,直接傳回空的特征。
ASF_MultiFaceInfo multiFaceInfo = DetectFace(pEngine, imageInfo);
singleFaceInfo = new ASF_SingleFaceInfo();
IntPtr pFaceModel = ExtractFeature(pEngine, imageInfo, multiFaceInfo, out singleFaceInfo);
return pFaceModel;
我們按照調用順序看一下
IntPtr ExtractFeature(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out ASF_SingleFaceInfo singleFaceInfo)
方法,
public static IntPtr ExtractFeature(IntPtr pEngine, ImageInfo imageInfo, ASF_MultiFaceInfo multiFaceInfo, out ASF_SingleFaceInfo singleFaceInfo)
{
/*定義要傳回的單個人臉資訊結構體*/
singleFaceInfo = new ASF_SingleFaceInfo();
/*如果沒有人臉框,直接傳回空特征*/
if (multiFaceInfo.faceRects == null)
{
ASF_FaceFeature emptyFeature = new ASF_FaceFeature();
IntPtr pEmptyFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
MemoryUtil.StructureToPtr(emptyFeature, pEmptyFeature);
return pEmptyFeature;
}
/*将FaceDetect中的人臉框和人臉角度指派到out對象*/
singleFaceInfo.faceRect = MemoryUtil.PtrToStructure<MRECT>(multiFaceInfo.faceRects);
singleFaceInfo.faceOrient = MemoryUtil.PtrToStructure<int>(multiFaceInfo.faceOrients);
/*将單個人臉對象轉化成非托管結構體*/
IntPtr pSingleFaceInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_SingleFaceInfo>());
MemoryUtil.StructureToPtr(singleFaceInfo, pSingleFaceInfo);
IntPtr pFaceFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
/*調用人臉識别接口提取人臉特征*/
int retCode = ASFFunctions.ASFFaceFeatureExtract(pEngine,
imageInfo.width, imageInfo.height, imageInfo.format,
imageInfo.imgData,
pSingleFaceInfo, pFaceFeature);
Console.WriteLine("FR Extract Feature result:" + retCode);
if (retCode != 0)
{
/*異常處理,注:由于使用了非托管對象,需要釋放記憶體*/
MemoryUtil.Free(pSingleFaceInfo);
MemoryUtil.Free(pFaceFeature);
ASF_FaceFeature emptyFeature = new ASF_FaceFeature();
IntPtr pEmptyFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
MemoryUtil.StructureToPtr(emptyFeature, pEmptyFeature);
return pEmptyFeature;
}
//處理傳回值,這裡又是一堆互操作通路
ASF_FaceFeature faceFeature = MemoryUtil.PtrToStructure<ASF_FaceFeature>(pFaceFeature);
byte[] feature = new byte[faceFeature.featureSize];
MemoryUtil.Copy(faceFeature.feature, feature, 0, faceFeature.featureSize);
ASF_FaceFeature localFeature = new ASF_FaceFeature();
localFeature.feature = MemoryUtil.Malloc(feature.Length);
MemoryUtil.Copy(feature, 0, localFeature.feature, feature.Length);
localFeature.featureSize = feature.Length;
IntPtr pLocalFeature = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_FaceFeature>());
MemoryUtil.StructureToPtr(localFeature, pLocalFeature);
//最後,别忘記釋放記憶體
MemoryUtil.Free(pSingleFaceInfo);
MemoryUtil.Free(pFaceFeature);
/*傳回提取到的人臉特征資料*/
return pLocalFeature;
}
人臉檢索
人臉檢索時要先建立本地人臉素材庫,上一步提取到的人臉特征是一串二進制的資料,實際使用時,我們可以将特征存儲到資料庫或者本地檔案中,Demo為了示範友善,直接放置在imagesFeatureList變量中.
在進行人臉檢索時,擷取到了待檢索的人臉特征後,調用
ASFFunctions.ASFFaceFeatureCompare
方法就可以完成檢索了。
for (int i = 0; i < imagesFeatureList.Count; i++)
{
IntPtr feature = imagesFeatureList[i];
float similarity = 0f;
int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, image1Feature, feature, ref similarity);
//增加異常值處理
if(similarity.ToString().IndexOf("E") > -1)
{
similarity = 0f;
}
AppendText(string.Format("與{0}号比對結果:{1}\r\n", i, similarity));
imageList.Items[i].Text = string.Format("{0}号({1})", i, similarity);
if (similarity > compareSimilarity)
{
compareSimilarity = similarity;
compareNum = i;
}
}
ASFFunctions.ASFFaceFeatureCompare 方法實際上調用的是SDK的對應方法,其傳回值 simiarity為相似度。 Demo将擷取到的人臉和人臉庫中所有人臉都進行了對比,找出最為接近的一個特征。 在實際應用中,如果找到了一個符合我們置信度要求的特征,就可以直接退出循環了。
小提示:在實際應用中,如果人臉庫的基數很大,可以開啟多個FR執行個體進行檢索,也可以啟用人臉檢測中的 性别 ,年齡資料縮小查詢的範圍
從視訊中檢測人臉
如果說從照片中檢測人臉是人臉識别的基礎,那麼從視訊中檢測人臉則人臉識别最為實際的應用,實際的人臉實時檢測系統都是基于視訊檢測的,活體檢測也是基于視訊模式下的人臉檢測。 簡單說來說,從視訊中檢測人臉的方式就是從視訊中抓取包含人臉的幀并分析識别的過程。在FaceForm的
videoSource_Paint
方法中對這一過程進行了詳細的描述。
從攝像頭(RGB攝像頭)中抓取一幀
調用适用于視訊的人臉檢測引擎,檢測到人臉
根據傳回的人臉位置畫人臉框,利用上一幀的檢測結果辨別識别到的人臉資訊
根據傳回的人臉資訊調用活體檢測功能
判斷是活體的情況下調用人臉識别引擎提取人臉特征
将人臉特征和人臉庫的資訊進行比對
記錄結果
等待捕獲下一幀
第一個要注意的點是pVideoEngine
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pVideoEngine, bitmap);
這個pVideoEngine是首先,是視訊模式下的人臉檢測引擎,它是在InitEngines()方法中啟用的
uint detectModeVideo = DetectionMode.ASF_DETECT_MODE_VIDEO;
int combinedMaskVideo = FaceEngineMask.ASF_FACE_DETECT |
FaceEngineMask.ASF_FACERECOGNITION;
retCode = ASFFunctions.ASFInitEngine(detectModeVideo,
videoDetectFaceOrientPriority, detectFaceScaleVal,
detectFaceMaxNum, combinedMaskVideo, ref pVideoEngine);
這裡調用的是pVideoEngine,和基于圖檔的pImageEngine使用的是不同的引擎,兩個引擎的差別是pVideoEngine在初始化的時候,使用的是視訊模式,在視訊檢測的場景下,建議使用視訊模式,在基于圖檔處理的情況下,建議使用圖像模式。 這裡因為 視訊(攝像頭)情況下 ,每秒産生資料有25-30幀,由于算法實作上的不同,每秒隻能做20次左右的圖像模式的檢測,是以視訊情況下,用圖像模式來做人臉檢測是不适合的,因為算力不夠;但視訊模式可以每秒運作100次 ,而且在2.2版本,視訊模式還增加了TrackID參數輸出,更容易判斷同一個人。而單張圖檔檢測情況下,一般都采用圖像模式來做人臉檢測,圖像模式的檢測更為細緻,而且對多人臉和大圖檔的支援較好,由于是單張檢測,圖檔模式的性能也不存在問題,一般情況1s内做5-10張就滿足産品要求了。
這裡需要注意的一點,Demo中也指出來了,一定要保證同一時刻隻檢測一幀,不能同時檢測多幀,以免頁面在顯示時出現卡頓。另外,提取特征值和資訊比對是比較耗時的,需要另外開線程以避免主線程界面的卡頓。
檢測活體
活體接入的方式和方法
目前常用的活體識别算法主要有 互動式 活體檢測和非互動式的兩種方式,我們在登入支付寶時候張張嘴,搖搖頭都屬于互動式,如果不需要其它動作,就屬于非互動式,虹軟的SDK針對識别攝像頭的不同,提供了基于RGB攝像頭和基于紅外深度攝像頭的兩種非互動算法。
RGB活體
隻需單目RGB攝像頭即可完成硬體搭設成本低,靜默識别無需使用者動作配合,隻需要普通的攝像頭就可以。人性化程度高,應用場景廣泛
IR活體
通過紅外成像原理(螢幕無法成像、不同材質反射率不同等)以及深度學習算法,實作高魯棒性的活體判斷,靜默式識别,可有效防禦圖檔、視訊、螢幕、面具等攻擊,可滿足雙目人臉識别終端産品活體檢測應用
有一個簡單判斷,如果你的攝像頭隻一個一個彩色鏡頭,沒有IR紅外鏡頭,那麼就可以使用RGB活體,如果你的是雙目攝像頭,并且一個是紅外的,那麼就可以使用IR活體,從可信度上來說,IR活體的可信度更多,但需要專用的裝置。
虹軟SDK在2.1之前的版本中,隻提供了RGB活體檢測功能,如果你需要使用IR活體檢測功能,需要使用2.2版本的SDK。
RGB活體接口解析
虹軟的活體檢測内置于FR(面部識别)引擎之中,要使用活體檢測功能,就必須先啟用它,在Demo的InitEngines()方法中,我們可以看到關于FR引擎初始化的方法.
初始化
根據使用攝像頭不同,啟用不同的活體檢測引擎,下面這段代碼啟用了RGB模式的FR引擎
//RGB視訊專用FR引擎
detectFaceMaxNum = 1;
combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION | FaceEngineMask.ASF_LIVENESS;
retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pVideoRGBImageEngine);
其中
FaceEngineMask.ASF_LIVENESS
為普通的RGB活體,如果是紅外雙射,則為
FaceEngineMask.ASF_IR_LIVENESS
檢測是否為活體
進行人臉檢測的最好時機是在分析人臉特征之前,抓取到人臉框之後,這個時候隻需要調用faceUtil的LivenessInfo_RGB方法就可以。其傳回的liveInfo中isLive是否為一判斷是否活體。 LivenessInfo_RGB 在内部調用了SDK的ASFProcess 方法
retCode = ASFFunctions.ASFProcess(pEngine, imageInfo.width, imageInfo.height, imageInfo.format, imageInfo.imgData, pMultiFaceInfo, FaceEngineMask.ASF_LIVENESS);
if (retCode == 0)
{
//擷取活體檢測結果
IntPtr pLivenessInfo = MemoryUtil.Malloc(MemoryUtil.SizeOf<ASF_LivenessInfo>());
retCode = ASFFunctions.ASFGetLivenessScore(pEngine, pLivenessInfo);
Console.WriteLine("Get Liveness Result:" + retCode);
ASF_LivenessInfo livenessInfo = MemoryUtil.PtrToStructure<ASF_LivenessInfo>(pLivenessInfo);
//釋放記憶體
MemoryUtil.Free(pMultiFaceInfo);
MemoryUtil.Free(pLivenessInfo);
return livenessInfo;
}
其傳回值livenessInfo 中的 isLive 定義了人臉活體的結果,當為1時為活體,為-1時為假體,程式判斷為假體後,就不再進行特征提取和比對的動作了。
IR活體接口解析
待補充。。。
問題及解決思路分享
非托管記憶體的管理與記憶體溢出
在C#程式中,我們經常和托管記憶體打交道,經常使用new 來建立對象。但是虹軟提供的SDK是基于原生代碼的,采用C語言編寫,其在使用時需要配置設定和使用非托管記憶體,參數也使用C的結構體類型,為了友善使用,Demo程式提供了MemoryUtil類,它通過對Marshal類相應方法的封裝,提供了直接調用C方法的便捷使用方式.
在使用Demo代碼編寫自己的程式時,要注意到有些FaceUtils的方法調用了Malloc方法配置設定了記憶體,但沒有釋放記憶體,而是在其它的方法中釋放.有一個非托管記憶體管理原則很重要:調用Marshal.AllocHGlobal必須調用 Marshal.FreeHGlobal(ptr)來手動釋放記憶體,即使調用GC.Collect();方法也無法釋放,導緻記憶體洩露。
Marshal 類是.net 互操作通路中需要使用到的最重要的一個類,它提供了一個方法集合,這些方法用于配置設定非托管記憶體、複制非托管記憶體塊、将托管類型轉換為非托管類型,此外還提供了在與非托管代碼互動時使用的其他雜項方法。具體可以參考MSDN的文檔 https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.marshal?redirectedfrom=MSDN&view=netframework-4.8#methods
找不到dll
VS不同版本對于DLL的放置有一定的要求,選擇程式的CPU類型也會影響到最終DLL的使用,一般來說,如果 使用的是32位程式,就放入x86檔案夾,如果是x64就放64檔案夾。
激活失敗
2.2版本的SDK在首次使用時,需要自動聯網激活,是以,請在首次使用時,連接配接網際網路。 如果在啟動程式時報90118裝置不比對,通常是硬體資訊發生了變化,這時候隻需要删除SDK目錄下面的ArcFace32.dat或者ArcFace64.dat檔案,SDK在檢測不到這個檔案會自動聯網激活。
Demo中的代碼可以在WPF或者Asp.net中使用嗎
這個當然是可以的,我們可以根據官方的Demo按照自己的業務邏輯改造成asp.net 應用或者WPF應用。 Demo已經對大部的功能進行了封裝,在了解業務邏輯之後,可以直接使用FaceUtil中的方法。不過當使用WPF或者asp.net時,可能會遇到堆棧記憶體不足,這是因為.net預設的堆棧大小為256K或者以下,而SDK需要使用512KB以上。隻需要在建立線程時調整堆棧大小就可以解決了,參見下面的方法
new Thread(new ThreadStart(delegate {
ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pEngine, imageInfo);
}), 1024 * 512).Start();
更多問題及支援
虹軟開放平台論壇提供了官方的資訊交流平台,可以通路https://ai.arcsoft.com.cn/bbs/index.php 了解更多資訊,上面有技術人員蹲守解決你的問題,如果你有好的Demo需要分享給其它小夥伴兒,也可以在論壇中上傳你的作品。