您好,登錄后才能下訂單哦!
小編給大家分享一下Android基于ArcSoft如何實(shí)現(xiàn)人臉識(shí)別,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
1、在虹軟的開(kāi)發(fā)者中心創(chuàng)建一個(gè)自己的應(yīng)用,將APP_ID與SDK_KEY記錄下來(lái),后面會(huì)用到。創(chuàng)建完后就可以下載SDK了。
2、下載完后,就可以根據(jù)SDK包里的開(kāi)發(fā)說(shuō)明文檔和代碼進(jìn)行參考和學(xué)習(xí)。以下是開(kāi)發(fā)說(shuō)明文檔中的SDK包結(jié)構(gòu)的截圖。
3、創(chuàng)建一個(gè)空項(xiàng)目,將SDK包里的.jar文件和.so文件復(fù)制到該項(xiàng)目的如下包下。接下來(lái)的配置十分重要,稍微沒(méi)處理一個(gè),就是一個(gè)頭大的bug。
4、“在app里的build.gradle” 第一個(gè)紅框原本是androidx的,與support是不兼容的,所以要改,因此,整個(gè)項(xiàng)目用到androidx的地方都需要改。第二個(gè)紅框是ndk,加了這個(gè)才能找到剛才復(fù)制進(jìn)去的.so文件。第三個(gè)紅框也要改成如下。下面的dependencies要注意把a(bǔ)ndroidx的改掉。
5、“在整個(gè)項(xiàng)目里的build.gradle” 記得加上jcenter()。
6、在gradle.properties里可能會(huì)有androidx的東西,也要?jiǎng)h掉。
7、在AndroidManifest.xml中的中添加權(quán)限申請(qǐng),在中添加。
manifest:
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
provider:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
在添加后要在res下創(chuàng)建一個(gè)xml包,里面添加一個(gè)provider_paths.xml文件,里面的代碼如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> <root-path name="root_path" path="." /> </paths>
8、從SDK包中引入如下功能包模塊和BaseActivity,并將common包下的Constants中的APP_ID,SDK_KEY改成剛才所記錄下來(lái)的內(nèi)容。
9、創(chuàng)建3個(gè)acvitity,一個(gè)是主界面,一個(gè)是人臉庫(kù)的管理界面,一個(gè)是人臉識(shí)別功能界面。
10、layout包下需要引入以下5個(gè)布局文件。
11、主界面主要的功能就是激活權(quán)限、連接動(dòng)態(tài)庫(kù)和激活引擎,我通過(guò)修改onCreate()和util包下的ConfigUtil.class的代碼,讓其能夠自動(dòng)激活和自動(dòng)修改為全方向人臉檢查(其他選擇好像不能夠?qū)崿F(xiàn)人臉識(shí)別)。以下是激活引擎的代碼。
public void activeEngine(final View view) { if (!libraryExists) { Toast.makeText(this, "未找到庫(kù)文件!", Toast.LENGTH_SHORT).show(); return; } if (!checkPermissions(NEEDED_PERMISSIONS)) { ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS); return; } if (view != null) { view.setClickable(false); } Observable.create(new ObservableOnSubscribe<Integer>() { @Override public void subscribe(ObservableEmitter<Integer> emitter) { int activeCode = FaceEngine.activeOnline(MainActivity.this, Constants.APP_ID, Constants.SDK_KEY); emitter.onNext(activeCode); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Integer>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Integer activeCode) { if (activeCode == ErrorInfo.MOK) { Toast.makeText(MainActivity.this, "激活成功!", Toast.LENGTH_SHORT).show(); } else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED){ Toast.makeText(MainActivity.this, "已激活!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "激活失??!", Toast.LENGTH_SHORT).show(); } if (view != null) { view.setClickable(true); } ActiveFileInfo activeFileInfo = new ActiveFileInfo(); } @Override public void onError(Throwable e) { Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); if (view != null) { view.setClickable(true); } } @Override public void onComplete() { } }); }
12、人臉識(shí)別界面是最復(fù)雜的。其中不僅有人臉識(shí)別的功能,還有注冊(cè)人臉和活體檢測(cè)的功能。
通過(guò)手機(jī)自帶的攝像頭來(lái)實(shí)現(xiàn)人臉識(shí)別和活體檢測(cè)的邏輯:
private void initCamera() { DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); final FaceListener faceListener = new FaceListener() { @Override public void onFail(Exception e) { Log.e(TAG, "onFail: " + e.getMessage()); } //請(qǐng)求FR的回調(diào) @Override public void onFaceFeatureInfoGet(@Nullable final FaceFeature faceFeature, final Integer requestId, final Integer errorCode) { //FR成功 if (faceFeature != null) { // Log.i(TAG, "onPreview: fr end = " + System.currentTimeMillis() + " trackId = " + requestId); Integer liveness = livenessMap.get(requestId); //不做活體檢測(cè)的情況,直接搜索 if (!livenessDetect) { searchFace(faceFeature, requestId); } //活體檢測(cè)通過(guò),搜索特征 else if (liveness != null && liveness == LivenessInfo.ALIVE) { searchFace(faceFeature, requestId); } //活體檢測(cè)未出結(jié)果,或者非活體,延遲執(zhí)行該函數(shù) else { if (requestFeatureStatusMap.containsKey(requestId)) { Observable.timer(WAIT_LIVENESS_INTERVAL, TimeUnit.MILLISECONDS) .subscribe(new Observer<Long>() { Disposable disposable; @Override public void onSubscribe(Disposable d) { disposable = d; getFeatureDelayedDisposables.add(disposable); } @Override public void onNext(Long aLong) { onFaceFeatureInfoGet(faceFeature, requestId, errorCode); } @Override public void onError(Throwable e) { } @Override public void onComplete() { getFeatureDelayedDisposables.remove(disposable); } }); } } } //特征提取失敗 else { if (increaseAndGetValue(extractErrorRetryMap, requestId) > MAX_RETRY_TIME) { extractErrorRetryMap.put(requestId, 0); String msg; // 傳入的FaceInfo在指定的圖像上無(wú)法解析人臉,此處使用的是RGB人臉數(shù)據(jù),一般是人臉模糊 if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) { msg = "人臉置信度低!"; } else { msg = "ExtractCode:" + errorCode; } faceHelper.setName(requestId, "未通過(guò)!"); // 在嘗試最大次數(shù)后,特征提取仍然失敗,則認(rèn)為識(shí)別未通過(guò) requestFeatureStatusMap.put(requestId, RequestFeatureStatus.FAILED); retryRecognizeDelayed(requestId); } else { requestFeatureStatusMap.put(requestId, RequestFeatureStatus.TO_RETRY); } } } @Override public void onFaceLivenessInfoGet(@Nullable LivenessInfo livenessInfo, final Integer requestId, Integer errorCode) { if (livenessInfo != null) { int liveness = livenessInfo.getLiveness(); livenessMap.put(requestId, liveness); // 非活體,重試 if (liveness == LivenessInfo.NOT_ALIVE) { faceHelper.setName(requestId, "未通過(guò)!非活體!"); // 延遲 FAIL_RETRY_INTERVAL 后,將該人臉狀態(tài)置為UNKNOWN,幀回調(diào)處理時(shí)會(huì)重新進(jìn)行活體檢測(cè) retryLivenessDetectDelayed(requestId); } } else { if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) { livenessErrorRetryMap.put(requestId, 0); String msg; // 傳入的FaceInfo在指定的圖像上無(wú)法解析人臉,此處使用的是RGB人臉數(shù)據(jù),一般是人臉模糊 if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) { msg = "人臉置信度低!"; } else { msg = "ProcessCode:" + errorCode; } faceHelper.setName(requestId, "未通過(guò)!"); retryLivenessDetectDelayed(requestId); } else { livenessMap.put(requestId, LivenessInfo.UNKNOWN); } } } }; CameraListener cameraListener = new CameraListener() { @Override public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) { Camera.Size lastPreviewSize = previewSize; previewSize = camera.getParameters().getPreviewSize(); drawHelper = new DrawHelper(previewSize.width, previewSize.height, previewView.getWidth(), previewView.getHeight(), displayOrientation , cameraId, isMirror, false, false); Log.i(TAG, "onCameraOpened: " + drawHelper.toString()); // 切換相機(jī)的時(shí)候可能會(huì)導(dǎo)致預(yù)覽尺寸發(fā)生變化 if (faceHelper == null || lastPreviewSize == null || lastPreviewSize.width != previewSize.width || lastPreviewSize.height != previewSize.height) { Integer trackedFaceCount = null; // 記錄切換時(shí)的人臉序號(hào) if (faceHelper != null) { trackedFaceCount = faceHelper.getTrackedFaceCount(); faceHelper.release(); } faceHelper = new FaceHelper.Builder() .ftEngine(ftEngine) .frEngine(frEngine) .flEngine(flEngine) .frQueueSize(MAX_DETECT_NUM) .flQueueSize(MAX_DETECT_NUM) .previewSize(previewSize) .faceListener(faceListener) .trackedFaceCount(trackedFaceCount == null ? ConfigUtil.getTrackedFaceCount(FaceRegisterAndRecognise.this.getApplicationContext()) : trackedFaceCount) .build(); } } @Override public void onPreview(final byte[] nv21, Camera camera) { if (faceRectView != null) { faceRectView.clearFaceInfo(); } List<FacePreviewInfo> facePreviewInfoList = faceHelper.onPreviewFrame(nv21); if (facePreviewInfoList != null && faceRectView != null && drawHelper != null) { drawPreviewInfo(facePreviewInfoList); } registerFace(nv21, facePreviewInfoList); clearLeftFace(facePreviewInfoList); if (facePreviewInfoList != null && facePreviewInfoList.size() > 0 && previewSize != null) { for (int i = 0; i < facePreviewInfoList.size(); i++) { Integer status = requestFeatureStatusMap.get(facePreviewInfoList.get(i).getTrackId()); /** * 在活體檢測(cè)開(kāi)啟,在人臉識(shí)別狀態(tài)不為成功或人臉活體狀態(tài)不為處理中(ANALYZING)且不為處理完成(ALIVE、NOT_ALIVE)時(shí)重新進(jìn)行活體檢測(cè) */ if (livenessDetect && (status == null || status != RequestFeatureStatus.SUCCEED)) { Integer liveness = livenessMap.get(facePreviewInfoList.get(i).getTrackId()); if (liveness == null || (liveness != LivenessInfo.ALIVE && liveness != LivenessInfo.NOT_ALIVE && liveness != RequestLivenessStatus.ANALYZING)) { livenessMap.put(facePreviewInfoList.get(i).getTrackId(), RequestLivenessStatus.ANALYZING); faceHelper.requestFaceLiveness(nv21, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId(), LivenessType.RGB); } } /** * 對(duì)于每個(gè)人臉,若狀態(tài)為空或者為失敗,則請(qǐng)求特征提?。筛鶕?jù)需要添加其他判斷以限制特征提取次數(shù)), * 特征提取回傳的人臉特征結(jié)果在{@link FaceListener#onFaceFeatureInfoGet(FaceFeature, Integer, Integer)}中回傳 */ if (status == null || status == RequestFeatureStatus.TO_RETRY) { requestFeatureStatusMap.put(facePreviewInfoList.get(i).getTrackId(), RequestFeatureStatus.SEARCHING); faceHelper.requestFaceFeature(nv21, facePreviewInfoList.get(i).getFaceInfo(), previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, facePreviewInfoList.get(i).getTrackId()); // Log.i(TAG, "onPreview: fr start = " + System.currentTimeMillis() + " trackId = " + facePreviewInfoList.get(i).getTrackedFaceCount()); } } } } @Override public void onCameraClosed() { Log.i(TAG, "onCameraClosed: "); } @Override public void onCameraError(Exception e) { Log.i(TAG, "onCameraError: " + e.getMessage()); } @Override public void onCameraConfigurationChanged(int cameraID, int displayOrientation) { if (drawHelper != null) { drawHelper.setCameraDisplayOrientation(displayOrientation); } Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + " " + displayOrientation); } }; cameraHelper = new CameraHelper.Builder() .previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight())) .rotation(getWindowManager().getDefaultDisplay().getRotation()) .specificCameraId(rgbCameraID != null ? rgbCameraID : Camera.CameraInfo.CAMERA_FACING_FRONT) .isMirror(false) .previewOn(previewView) .cameraListener(cameraListener) .build(); cameraHelper.init(); cameraHelper.start(); }
注冊(cè)人臉的邏輯:
private void registerFace(final byte[] nv21, final List<FacePreviewInfo> facePreviewInfoList) { if (registerStatus == REGISTER_STATUS_READY && facePreviewInfoList != null && facePreviewInfoList.size() > 0) { registerStatus = REGISTER_STATUS_PROCESSING; Observable.create(new ObservableOnSubscribe<Boolean>() { @Override public void subscribe(ObservableEmitter<Boolean> emitter) { boolean success = FaceServer.getInstance().registerNv21(FaceRegisterAndRecognise.this, nv21.clone(), previewSize.width, previewSize.height, facePreviewInfoList.get(0).getFaceInfo(), "registered " + faceHelper.getTrackedFaceCount()); emitter.onNext(success); } }) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Boolean>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(Boolean success) { String result = success ? "register success!" : "register failed!"; showToast(result); registerStatus = REGISTER_STATUS_DONE; } @Override public void onError(Throwable e) { e.printStackTrace(); showToast("register failed!"); registerStatus = REGISTER_STATUS_DONE; } @Override public void onComplete() { } }); } }
13、人臉庫(kù)的管理界面。
public class FaceLibs extends BaseActivity { private ExecutorService executorService; private TextView textView; private TextView tvNotificationRegisterResult; ProgressDialog progressDialog = null; private static final int ACTION_REQUEST_PERMISSIONS = 0x001; private static String[] NEEDED_PERMISSIONS = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_face_libs); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); executorService = Executors.newSingleThreadExecutor(); tvNotificationRegisterResult = findViewById(R.id.notification_register_result); progressDialog = new ProgressDialog(this); int faceLibNum = FaceServer.getInstance().getFaceNumber(this); textView = findViewById(R.id.number); textView.setText(faceLibNum + ""); FaceServer.getInstance().init(this); } @Override protected void onDestroy() { if (executorService != null && !executorService.isShutdown()) { executorService.shutdownNow(); } if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } FaceServer.getInstance().unInit(); super.onDestroy(); } @Override void afterRequestPermission(int requestCode, boolean isAllGranted) { } public void clearFaces(View view) { int faceNum = FaceServer.getInstance().getFaceNumber(this); if (faceNum == 0) { showToast("人臉庫(kù)已空!"); } else { AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("通知") .setMessage("確定要?jiǎng)h除" + faceNum + "個(gè)人臉嗎?") .setPositiveButton("確定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { int deleteCount = FaceServer.getInstance().clearAllFaces(FaceLibs.this); showToast(deleteCount + "個(gè)人臉已刪除!"); textView.setText("0"); } }) .setNegativeButton("取消", null) .create(); dialog.show(); } } }
14、以上就是大體的介紹,還有一些小的細(xì)枝末節(jié)需要同志們動(dòng)手實(shí)操一下。下面就來(lái)看看實(shí)現(xiàn)的效果。
主界面:
注冊(cè)成功并通過(guò)識(shí)別:
通過(guò)手機(jī)照片識(shí)別出不是活體:
清理人臉庫(kù):
以上是“Android基于ArcSoft如何實(shí)現(xiàn)人臉識(shí)別”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。