這篇文章將為大家詳細(xì)講解有關(guān)如何解析利用人臉識(shí)別SDK實(shí)現(xiàn)人證比對(duì)全過(guò)程,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
10余年的清水河網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)營(yíng)銷(xiāo)推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整清水河建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“清水河網(wǎng)站設(shè)計(jì)”,“清水河網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
人證比對(duì)在如今的社會(huì)中隨處可見(jiàn),如高鐵、飛機(jī)、酒店入住、甚至景區(qū)入口都可以看到各種各樣的人證應(yīng)用,人臉識(shí)別SDK的也如雨后春筍一般層出不窮,如百度、商湯、Face++、虹軟等。在嘗試使用各家的SDK之后,最讓我青睞的要數(shù)虹軟科技的SDK了,最直接的一個(gè)原因就是虹軟承諾永久免費(fèi)。我從2.0版本開(kāi)始就在使用了,實(shí)測(cè)效果確實(shí)不錯(cuò),就在上個(gè)月收到消息ArcFace3.0更新了,作為一個(gè)白嫖黨自然不會(huì)錯(cuò)過(guò)這次的更新,在上手了3.0 之后,發(fā)現(xiàn)ArcFace 3.0有以下新特性。
特征比對(duì)支持比對(duì)模型選擇,有生活照比對(duì)模型和人證比對(duì)模型
Android平臺(tái)新增64位的SDK
新增了一種圖像數(shù)據(jù)傳入方式
1.業(yè)務(wù)自由度變高
??以人證 2.0為例,我們只能傳入數(shù)據(jù)、傳出結(jié)果,而一些中間產(chǎn)物,例如人臉特?cái)?shù)據(jù)征就獲取不到了?,F(xiàn)在采用ArcFace 3.0之后,取消了固定的流程,檢測(cè)、比對(duì)、提取等流程都可以由自己控制。
2.可以在同一個(gè)工程內(nèi)實(shí)現(xiàn)生活照比對(duì)與人證比對(duì)
??人證 SDK與ArcFace SDK 存在沖突,無(wú)法同時(shí)使用,若我們既想用人證又想用生活照,就要寫(xiě)兩個(gè)工程,并且兩個(gè)工程的流程還有些不同。而現(xiàn)在只需要接口內(nèi)選擇模型就可以實(shí)現(xiàn)模型的切換,完全可以在一個(gè)工程內(nèi)實(shí)現(xiàn)人證與生活照程序的集成。
3.代碼復(fù)用性
??ArcFace 3.0中人證與身份證區(qū)別只有compare接口中的模型選擇,其他完全一致,因此大部分的代碼都可以復(fù)用,大大提高了開(kāi)發(fā)的效率。
1.接口變動(dòng)
??萬(wàn)事有得必有失,由于ArcFace 3.0沒(méi)有關(guān)于人證部分的封裝,致使在升級(jí)過(guò)程中所有的接口都需要變更,相信也是所有程序員都不愿意看到的問(wèn)題。
2.實(shí)現(xiàn)變困難
??同樣由于ArcFace 3.0沒(méi)有關(guān)于人證部分的封裝,使得原本接口中自帶的一些流程與回調(diào)需要自己來(lái)實(shí)現(xiàn),這對(duì)于剛上手的人來(lái)說(shuō),不是十分友好。
??雖然上面說(shuō)了一些ArcFace 3.0的缺點(diǎn),但是我本人還是很贊成這次的升級(jí),畢竟每個(gè)產(chǎn)品的革新總會(huì)帶來(lái)一些沖擊,但是相對(duì)于這些沖擊來(lái)說(shuō),我相信接口、識(shí)別流程的統(tǒng)一為程序的適用性與業(yè)務(wù)的自由性都提高了,相信對(duì)于人證2.0來(lái)說(shuō)這次“壯士斷腕”的舉措長(zhǎng)遠(yuǎn)來(lái)看是值得的。
??在上面我們看到了由于接口的變動(dòng),致使人證2.0程序所有的接口都要修改,接下來(lái)我將以 人證2.0 Demo為例,講解一下我是如何使用ArcFace 3.0 SDK進(jìn)行升級(jí)的。
??考慮到可能有些用戶對(duì)人證 2.0 Demo不太熟悉,先簡(jiǎn)單介紹一下官方Demo如何配置使用。
?首先,先將人證引擎如圖所示放入demo內(nèi),接下來(lái)修改Constants內(nèi)的APP_ID與SDK_KEY,APP_ID與SDK_KEY以及人證引擎均由官網(wǎng)的
開(kāi)放平臺(tái)上進(jìn)行獲取。然后在設(shè)備的SDCard根目錄下放置一張命名為“sample.jpg”的圖片做為模擬人證輸入的圖片(圖片路徑可以在MainActivity下的SAMPLE_FACE變量?jī)?nèi)進(jìn)行修改),下圖為配置完畢后運(yùn)行的截圖。
??首先我們要先獲取ArcFace3.0的SDK,同樣可以在 開(kāi)放平臺(tái)上進(jìn)行獲取。用新的SDK庫(kù)替換掉原本的SDK,替換后的項(xiàng)目目錄如下圖所示
??上面提到了,由于3.0的全面變更,所有的接口全部都發(fā)生了改變,因此我們要把原本2.0的接口全部替換為3.0。
??激活方面接口參數(shù)沒(méi)有任何變化
人證 2.0 :
IdCardVerifyManager.getInstance().active(Context context, String appId, String sdkKey);
ArcFace 3.0 :
FaceEngine.active(Context context, String appId, String sdkKey);
??從初始化開(kāi)始,人證 2.0與ArcFace3.0接口有了較大的區(qū)別,人證 2.0有對(duì)Id Card信息與Camera信息監(jiān)聽(tīng),而3.0取消了這個(gè)監(jiān)聽(tīng)機(jī)制,接口內(nèi)的參數(shù)就不一一介紹了, 官方文檔介紹的非常詳細(xì),大家可以去參考一下官方文檔。
人證 2.0 :
IdCardVerifyManager.getInstance().init(Context context, IdCardVerifyListener listener)
ArcFace 3.0 :
FaceEngine.init(Context context, DetectMode detectMode, DetectFaceOrientPriority detectFaceOrientPriority, int detectFaceScaleVal, int detectFaceMaxNum, int combinedMask)
??下面是我對(duì)2.0進(jìn)行替換后的前后代碼,可以給大家做一個(gè)參考:
人證 2.0 :
private void initEngine() { int result = IdCardVerifyManager.getInstance().init(this, idCardVerifyListener); LogUtils.dTag(TAG, "initResult: " + result); if (result == IdCardVerifyError.MERR_ASF_NOT_ACTIVATED) { Executors.newSingleThreadExecutor().execute(new Runnable() { @Override public void run() { int activeResult = IdCardVerifyManager.getInstance().active( MainActivity.this, APP_ID, SDK_KEY); runOnUiThread(new Runnable() { @Override public void run() { LogUtils.dTag(TAG, "activeResult: " + activeResult); if (activeResult == IdCardVerifyError.OK) { int initResult = IdCardVerifyManager.getInstance().init( MainActivity.this, idCardVerifyListener); LogUtils.dTag(TAG, "initResult: " + initResult); if (initResult != IdCardVerifyError.OK) { toast("人證引擎初始化失敗,錯(cuò)誤碼: " + initResult); } } else { toast("人證引擎激活失敗,錯(cuò)誤碼: " + activeResult); } } }); } }); } else if (result != IdCardVerifyError.OK) { toast("人證引擎初始化失敗,錯(cuò)誤碼: " + result); } }
ArcFace 3.0 :
private void initEngine() { int result = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 1, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_FACE_RECOGNITION); LogUtils.dTag(TAG, "initResult: " + result); if (result == ErrorInfo.MERR_ASF_NOT_ACTIVATED) { Executors.newSingleThreadExecutor().execute(() -> { int activeResult = FaceEngine.active( MainActivity.this, Constants.APP_ID, Constants.SDK_KEY); runOnUiThread(() -> { LogUtils.dTag(TAG, "activeResult: " + activeResult); if (activeResult == ErrorInfo.MOK) { int initResult = faceEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 1, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_FACE_RECOGNITION); LogUtils.dTag(TAG, "initResult: " + initResult); if (initResult != ErrorInfo.MOK) { toast("人證引擎初始化失敗,錯(cuò)誤碼: ", initResult)); } } else { toast("人證引擎激活失敗,錯(cuò)誤碼: ", activeResult)); } }); }); } else if (result != ErrorInfo.MOK) { toast("人證引擎初始化失敗,錯(cuò)誤碼: " , result)); } }
??證件照部分我們需要將原本2.0的引擎自帶的圖像處理方法換成3.0包內(nèi)的ArcSoftImageUtil的方法,同時(shí)由于特征提取成功后的回調(diào)監(jiān)聽(tīng)從引擎內(nèi)刪除掉了,所以這個(gè)回調(diào)需要自己來(lái)寫(xiě),這里我偷了一下懶,抄了一下人證 2.0 demo與3.0 demo中均有的faceHelper中的FaceListener作為監(jiān)聽(tīng)回調(diào),當(dāng)然大家也可以自己實(shí)現(xiàn)回調(diào)。
人證 2.0 :
private void inputIdCard() { if (bmp == null) { return; } int width = bmp.getWidth(); int height = bmp.getHeight(); //圖像裁剪 boolean needAdjust = false; while (width % 4 != 0) { width--; needAdjust = true; } if (height % 2 != 0) { height--; needAdjust = true; } if (needAdjust) { bmp = ImageUtils.imageCrop(bmp, new Rect(0, 0, width, height)); } //轉(zhuǎn)換為NV21數(shù)據(jù)格式 byte[] nv21Data = ImageUtils.getNV21(width, height, bmp); //身份證圖像數(shù)據(jù)輸入 DetectFaceResult result = IdCardVerifyManager.getInstance().inputIdCardData( nv21Data, width, height); LogUtils.dTag(TAG, "inputIdCardData result: " + result.getErrCode()); }
ArcFace 3.0 :
private void inputIdCard() { if (bmp == null) { return; } //圖像4字節(jié)對(duì)齊 裁剪 bmp = ArcSoftImageUtil.getAlignedBitmap(bmp, true); int width = bmp.getWidth(); int height = bmp.getHeight(); //轉(zhuǎn)換為bgr格式 byte[] bgrData = ArcSoftImageUtil.createImageData(bmp.getWidth(), bmp.getHeight(), ArcSoftImageFormat.BGR24); int translateResult = ArcSoftImageUtil.bitmapToImageData(bmp, bgrData, ArcSoftImageFormat.BGR24); //轉(zhuǎn)換成功 if (translateResult == ArcSoftImageUtilError.CODE_SUCCESS) { List<FaceInfo> faceInfoList = new ArrayList<>(); //video模式不適合靜態(tài)圖片檢測(cè),這里新建了一個(gè)idFaceEngine 除了檢測(cè)模式修改為Image其他參數(shù)與faceEngine一樣 int detectResult = idFaceEngine.detectFaces(bgrData, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList); if (detectResult == ErrorInfo.MOK && faceInfoList.size() > 0) { //這里的-2為trackID 因?yàn)镃amera與證件照提取共用faceHelper 用trackID區(qū)分是哪邊來(lái)的數(shù)據(jù) faceHelper.requestFaceFeature(bgrData, faceInfoList.get(0), width, height, FaceEngine.CP_PAF_BGR24, -2); } } else { LogUtils.dTag(TAG, "translate Error result: " + translateResult); } }
??人證2.0的onPreviewData接口內(nèi)部其實(shí)是存在一個(gè)特征提取保護(hù),即上一個(gè)特征提取未完成前,不能進(jìn)行下一個(gè)特征提取,但是在3.0沒(méi)有外部的封裝了,所以我們要自己來(lái)進(jìn)行特征提取的控制,基礎(chǔ)的策略就是根據(jù)trackId,每一個(gè)trackId若未進(jìn)行提取或提取失敗才會(huì)進(jìn)行特征提取。
人證 2.0 :
public void onPreview(byte[] nv21, Camera camera) { if (faceRectView != null) { faceRectView.clearFaceInfo(); } if (nv21 == null) { return; } //預(yù)覽數(shù)據(jù)傳入 DetectFaceResult result = IdCardVerifyManager.getInstance().onPreviewData(nv21, previewSize.width, previewSize.height, true); Rect rect = result.getFaceRect(); if (faceRectView != null && drawHelper != null && rect != null) { //生成實(shí)時(shí)人臉框 drawHelper.draw(faceRectView, new DrawInfo(drawHelper.adjustRect(rect), "", Color.YELLOW)); } }
ArcFace 3.0 :
public void onPreview(byte[] nv21, Camera camera) { if (faceRectView != null) { faceRectView.clearFaceInfo(); } if (nv21 == null) { return; } List<FaceInfo> faceInfoList = new ArrayList<>(); int ftResult = faceEngine.detectFaces(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList); //人證比對(duì)場(chǎng)景下只有最大人臉有效,因此直接取第一個(gè)人臉即可,若有其他場(chǎng)景可以自行調(diào)整 if (ftResult == ErrorInfo.MOK && faceInfoList.size() > 0) { Rect rect = faceInfoList.get(0).getRect(); if (faceRectView != null && drawHelper != null && rect != null) { drawHelper.draw(faceRectView, new DrawInfo(drawHelper.adjustRect(rect), "", Color.YELLOW)); } //等待身份證數(shù)據(jù)準(zhǔn)備完畢后,才開(kāi)始對(duì)Camera的數(shù)據(jù)進(jìn)行特征提取 并根據(jù)trackId防止重復(fù)提取 int trackId = faceInfoList.get(0).getFaceId(); if (isIdCardReady && requestFeatureStatusMap != null && requestFeatureStatusMap.containsKey(trackId)) { //若一個(gè)人臉提取失敗則進(jìn)行重試 if (requestFeatureStatusMap.get(trackId) == null || requestFeatureStatusMap.get(trackId) == RequestFeatureStatus.FAILED) { requestFeatureStatusMap.put(trackId, RequestFeatureStatus.SEARCHING); faceHelper.requestFaceFeature(nv21, faceInfoList.get(0), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList.get(0).getFaceId()); } } } }
??上文我們已經(jīng)提到過(guò),人證 2.0的引擎內(nèi)對(duì)camera數(shù)據(jù)idCard數(shù)據(jù)分別有兩個(gè)接口作為區(qū)分,同時(shí)有兩個(gè)回調(diào)函數(shù)分別用于兩個(gè)數(shù)據(jù)的處理。而ArcFace3.0時(shí)不僅取消了回調(diào),而且camera數(shù)據(jù)idCard數(shù)據(jù)共用一個(gè)detect、extractFaceFeature,所以我們可以采用trackId來(lái)作為區(qū)分,并且因?yàn)橐娴淖兓?,引擎?nèi)不再存儲(chǔ)特征值,導(dǎo)致我們需要記錄兩個(gè)數(shù)據(jù)源處獲得的特征值。
人證 2.0 :
private IdCardVerifyListener idCardVerifyListener = new IdCardVerifyListener() { @Override public void onPreviewResult(DetectFaceResult detectFaceResult, byte[] bytes, int i, int i1) { runOnUiThread(() -> { //預(yù)覽人臉特征提取成功 if (detectFaceResult.getErrCode() == IdCardVerifyError.OK) { isCurrentReady = true; compare(); } }); } @Override public void onIdCardResult(DetectFaceResult detectFaceResult, byte[] bytes, int i, int i1) { LogUtils.dTag(TAG, "onIdCardResult: " + detectFaceResult.getErrCode()); runOnUiThread(() -> { //身份證人臉特征提取成功 if (detectFaceResult.getErrCode() == IdCardVerifyError.OK) { isIdCardReady = true; restartHandler.removeCallbacks(restartRunnable); readHandler.postDelayed(readRunnable, READ_DELAY); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.PNG, 80, baos); byte[] bmpBytes = baos.toByteArray(); Glide.with(MainActivity.this).load(bmpBytes).into(ivIdCard); compare(); } }); } };
ArcFace 3.0 :
FaceListener faceListener = new FaceListener() { @Override public void onFail(Exception e) { } @Override public void onFaceFeatureInfoGet(@Nullable FaceFeature faceFeature, Integer requestId, Integer errorCode, long frTime, byte[] nv21) { //特征提取失敗 將比對(duì)狀態(tài)置為失敗 if (ErrorInfo.MOK != errorCode) { requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED); return; } //requestId 為-2則為身份證數(shù)據(jù) if (requestId == -2) { isIdCardReady = true; //由于接口變更feature不能在引擎內(nèi)存儲(chǔ) 所以用全局變量進(jìn)行存儲(chǔ) idFaceFeature = faceFeature; restartHandler.removeCallbacks(restartRunnable); readHandler.postDelayed(readRunnable, 5000); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.PNG, 100, baos); runOnUiThread(() -> { Glide.with(MainActivity.this).load(bmp).into(ivIdCard); compare(); }); } else { //由于接口變更feature不能在引擎內(nèi)存儲(chǔ) 所以用全局變量進(jìn)行存儲(chǔ) MainActivity.this.faceFeature = faceFeature; isCurrentReady = true; runOnUiThread(() -> { compare(); }); } } };
??比對(duì)接口相對(duì)于之前的來(lái)說(shuō)改動(dòng)就小很多了,只需要注意一下將比對(duì)模式修改為ID_CARD模式即可。
人證 2.0 :
private void compare() { //....... //人證特征比對(duì)接口 CompareResult compareResult = IdCardVerifyManager.getInstance().compareFeature(THRESHOLD); LogUtils.dTag(TAG, "compareResult: result " + compareResult.getResult() + ", isSuccess " + compareResult.isSuccess() + ", errCode " + compareResult.getErrCode()); if (compareResult.isSuccess()) { playSound(R.raw.compare_success); ivCompareResult.setBackgroundResource(R.mipmap.compare_success); tvCompareTip.setText(name); } else { playSound(R.raw.compare_fail); ivCompareResult.setBackgroundResource(R.mipmap.compare_fail); tvCompareTip.setText(R.string.tip_retry); } //....... }
ArcFace 3.0 :
private void compare() { //....... //人證特征比對(duì)接口 FaceSimilar compareResult = new FaceSimilar(); faceEngine.compareFaceFeature(idFaceFeature, faceFeature, CompareModel.ID_CARD, compareResult); //人證比對(duì)閾值為0.82 if (compareResult.getScore() > 0.82) { playSound(R.raw.compare_success); ivCompareResult.setBackgroundResource(R.mipmap.compare_success); tvCompareTip.setText(name); } else { playSound(R.raw.compare_fail); ivCompareResult.setBackgroundResource(R.mipmap.compare_fail); tvCompareTip.setText(R.string.tip_retry); } //....... }
??至此只要將人證 2.0 demo無(wú)用的代碼刪除掉,我們就將2.0成功升級(jí)為3.0了,讓我們看看部隊(duì)成功后的運(yùn)行截圖。
??相比較于將人證 2.0升級(jí)為將ArcFace3.0來(lái)說(shuō),直接在將ArcFace3.0版本上進(jìn)行修改可簡(jiǎn)單太多了,畢竟不用將所有的接口全部都更改一遍,我們需要做的就只是增加人證部分的輸入,人證部分的回調(diào)以及比對(duì)的邏輯。因此在這里我強(qiáng)烈推薦直接上手ArcFace3.0,如果不是有特殊原因修改3.0可比人證2.0快太多了。
??首先我們要選擇demo中的一個(gè)Activity做為我們修改的模板,我看了一下RegisterAndRecognizeActivity是我認(rèn)為最為合適的了,因?yàn)樗腃amera的比對(duì)流程已經(jīng)全部完成了,我們需要做的就是兩點(diǎn):
增加Id Card數(shù)據(jù)輸入源
Id Card數(shù)據(jù)輸入源我們采用與人證demo相同的方式模擬證件信息傳入,因此可以完全套用inputIdCard方法。
public void onClickIdCard(View view) { //模擬身份證姓名,可修改 FileInputStream fis; //身份證圖像數(shù)據(jù) bmp = null; try { //模擬身份證圖像數(shù)據(jù)來(lái)源,可修改 fis = new FileInputStream(SAMPLE_FACE); bmp = BitmapFactory.decodeStream(fis); fis.close(); } catch (Exception e) { e.printStackTrace(); } inputIdCard(); } private void inputIdCard() { if (bmp == null) { return; } //圖像4字節(jié)對(duì)齊 裁剪 bmp = ArcSoftImageUtil.getAlignedBitmap(bmp, true); int width = bmp.getWidth(); int height = bmp.getHeight(); //轉(zhuǎn)換為bgr格式 byte[] bgrData = ArcSoftImageUtil.createImageData(bmp.getWidth(), bmp.getHeight(), ArcSoftImageFormat.BGR24); int translateResult = ArcSoftImageUtil.bitmapToImageData(bmp, bgrData, ArcSoftImageFormat.BGR24); //轉(zhuǎn)換成功 if (translateResult == ArcSoftImageUtilError.CODE_SUCCESS) { List<FaceInfo> faceInfoList = new ArrayList<>(); //video模式不適合靜態(tài)圖片檢測(cè),我們選擇frEngine 作為檢測(cè)證件照的引擎 初始化時(shí)要增加 FaceEngine.ASF_FACE_DETECT 哦 int detectResult = frEngine.detectFaces(bgrData, width, height, FaceEngine.CP_PAF_BGR24, faceInfoList); if (detectResult == ErrorInfo.MOK && faceInfoList.size() > 0) { //這里的-2為trackID 因?yàn)镃amera與證件照提取共用faceHelper 用trackID區(qū)分是哪邊來(lái)的數(shù)據(jù) faceHelper.requestFaceFeature(bgrData, faceInfoList.get(0), width, height, FaceEngine.CP_PAF_BGR24, -2); } } else { LogUtils.dTag(TAG, "translate Error result: " + translateResult); } }
修改比對(duì)的底庫(kù)
??由于絕大部分場(chǎng)景下,人證比對(duì)都是1:1進(jìn)行對(duì)比的,因而要在onFaceFeatureInfoGet回調(diào)內(nèi)進(jìn)行調(diào)整。首先通過(guò)我們?cè)谏厦鎖nputIdCard鋪墊的以-2為trackID,作為標(biāo)識(shí)身份證數(shù)據(jù)的手段。其次我們要記錄一下要對(duì)比的身份證feature與camera下的人臉feature信息,這里我們采用全局變量的方式進(jìn)行記錄。最后由于比對(duì)的feature獲取會(huì)有前后順序區(qū)分,我們用一個(gè)狀態(tài)位進(jìn)行記錄(當(dāng)然也可以判斷兩個(gè)feature是否有數(shù)據(jù),對(duì)此數(shù)據(jù)進(jìn)行維護(hù)來(lái)進(jìn)行兩邊數(shù)據(jù)的同步),等待兩邊的數(shù)據(jù)都準(zhǔn)備完畢后,就可以進(jìn)行比對(duì)了。
public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) { //FR成功 if (faceFeature != null) { //接收身份證數(shù)據(jù) if (requestId == -2) { isIdCardReady = true; //feature用全局變量進(jìn)行存儲(chǔ) idFaceFeature = faceFeature; compare(); return; } // Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId); Integer liveness = livenessMap.get(requestId); //不做活體檢測(cè)的情況,直接搜索 if (!livenessDetect) { isCurrentReady = true; //防止對(duì)同一個(gè)人臉進(jìn)行多次特征提取 requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED); compare(); // searchFace(faceFeature, requestId); } //活體檢測(cè)通過(guò),搜索特征 else if (liveness != null && liveness == LivenessInfo.ALIVE) { isCurrentReady = true; //防止對(duì)同一個(gè)人臉進(jìn)行多次特征提取 RegisterAndRecognizeActivity.this.faceFeature = faceFeature; requestFeatureStatusMap.put(requestId, RequestFeatureStatus.SUCCEED); compare(); // searchFace(faceFeature, requestId); } //活體檢測(cè)未出結(jié)果,或者非活體,延遲執(zhí)行該函數(shù) else { //...... } } //特征提取失敗 else { //......... } } @Override public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) { //..... } };
compare 函數(shù):
private void compare() { if (isCurrentReady && isIdCardReady) { FaceSimilar similar = new FaceSimilar(); int compareResult = frEngine.compareFaceFeature(idFaceFeature, faceFeature, CompareModel.ID_CARD, similar); if (compareResult == ErrorInfo.MOK && similar.getScore() > 0.82) { Log.i(TAG, "compare: success"); } else { Log.i(TAG, "compare: fail"); } //比對(duì)完成后重置比對(duì)狀態(tài) isIdCardReady = false; isCurrentReady = false; //給同一個(gè)人臉若比對(duì)后仍想嘗試,允許其進(jìn)行特征提取 requestFeatureStatusMap.clear(); } }
??使用ArcFace3.0進(jìn)行修改,可以明顯的感覺(jué)到修改“絲滑”了很多,我們?cè)谠a的基礎(chǔ)上只需要注意Id Card的數(shù)據(jù)輸入,以及比對(duì)前后的邏輯即可,比對(duì)的難度幾乎可以忽略不計(jì),只是簡(jiǎn)單的調(diào)用接口而已。我這里也寫(xiě)的比較簡(jiǎn)單,有些業(yè)務(wù)邏輯如:增加身份證數(shù)據(jù)有效時(shí)間;規(guī)定雙方數(shù)據(jù)強(qiáng)制的先后順序;界面部分的展示都沒(méi)有做,只打印了一下比對(duì)的結(jié)果。本文只提供思路給大家參考,業(yè)務(wù)邏輯還是需要自己添加,最后給大家看一下修改完成后運(yùn)行比對(duì)成功的日志。
關(guān)于如何解析利用人臉識(shí)別SDK實(shí)現(xiàn)人證比對(duì)全過(guò)程就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
分享文章:如何解析利用人臉識(shí)別SDK實(shí)現(xiàn)人證比對(duì)全過(guò)程
當(dāng)前地址:http://jinyejixie.com/article42/gpedec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動(dòng)態(tài)網(wǎng)站、商城網(wǎng)站、電子商務(wù)、網(wǎng)站設(shè)計(jì)公司、、網(wǎng)站設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)