Android基于虹軟(ArcSoft)實現(xiàn)人臉識別
1、在虹軟的開發(fā)者中心創(chuàng)建一個自己的應(yīng)用,將APP_ID與SDK_KEY記錄下來,后面會用到。創(chuàng)建完后就可以下載SDK了。

2、下載完后,就可以根據(jù)SDK包里的開發(fā)說明文檔和代碼進行參考和學(xué)習(xí)。以下是開發(fā)說明文檔中的SDK包結(jié)構(gòu)的截圖。

3、創(chuàng)建一個空項目,將SDK包里的.jar文件和.so文件復(fù)制到該項目的如下包下。接下來的配置十分重要,稍微沒處理一個,就是一個頭大的bug。

4、“在app里的build.gradle” 第一個紅框原本是androidx的,與support是不兼容的,所以要改,因此,整個項目用到androidx的地方都需要改。第二個紅框是ndk,加了這個才能找到剛才復(fù)制進去的.so文件。第三個紅框也要改成如下。下面的dependencies要注意把androidx的改掉。


5、“在整個項目里的build.gradle” 記得加上jcenter()。

6、在gradle.properties里可能會有androidx的東西,也要刪掉。

7、在AndroidManifest.xml中的中添加權(quán)限申請,在中添加。
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)建一個xml包,里面添加一個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改成剛才所記錄下來的內(nèi)容。

9、創(chuàng)建3個acvitity,一個是主界面,一個是人臉庫的管理界面,一個是人臉識別功能界面。

10、layout包下需要引入以下5個布局文件。

11、主界面主要的功能就是激活權(quán)限、連接動態(tài)庫和激活引擎,我通過修改onCreate()和util包下的ConfigUtil.class的代碼,讓其能夠自動激活和自動修改為全方向人臉檢查(其他選擇好像不能夠?qū)崿F(xiàn)人臉識別)。以下是激活引擎的代碼。
public void activeEngine(final View view) {
if (!libraryExists) {
Toast.makeText(this, "未找到庫文件!", 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、人臉識別界面是最復(fù)雜的。其中不僅有人臉識別的功能,還有注冊人臉和活體檢測的功能。
通過手機自帶的攝像頭來實現(xiàn)人臉識別和活體檢測的邏輯:
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());
}
//請求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);
//不做活體檢測的情況,直接搜索
if (!livenessDetect) {
searchFace(faceFeature, requestId);
}
//活體檢測通過,搜索特征
else if (liveness != null && liveness == LivenessInfo.ALIVE) {
searchFace(faceFeature, requestId);
}
//活體檢測未出結(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在指定的圖像上無法解析人臉,此處使用的是RGB人臉數(shù)據(jù),一般是人臉模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = "人臉置信度低!";
} else {
msg = "ExtractCode:" + errorCode;
}
faceHelper.setName(requestId, "未通過!");
// 在嘗試最大次數(shù)后,特征提取仍然失敗,則認為識別未通過
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, "未通過!非活體!");
// 延遲 FAIL_RETRY_INTERVAL 后,將該人臉狀態(tài)置為UNKNOWN,幀回調(diào)處理時會重新進行活體檢測
retryLivenessDetectDelayed(requestId);
}
} else {
if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {
livenessErrorRetryMap.put(requestId, 0);
String msg;
// 傳入的FaceInfo在指定的圖像上無法解析人臉,此處使用的是RGB人臉數(shù)據(jù),一般是人臉模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = "人臉置信度低!";
} else {
msg = "ProcessCode:" + errorCode;
}
faceHelper.setName(requestId, "未通過!");
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());
// 切換相機的時候可能會導(dǎo)致預(yù)覽尺寸發(fā)生變化
if (faceHelper == null ||
lastPreviewSize == null ||
lastPreviewSize.width != previewSize.width || lastPreviewSize.height != previewSize.height) {
Integer trackedFaceCount = null;
// 記錄切換時的人臉序號
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());
/**
* 在活體檢測開啟,在人臉識別狀態(tài)不為成功或人臉活體狀態(tài)不為處理中(ANALYZING)且不為處理完成(ALIVE、NOT_ALIVE)時重新進行活體檢測
*/
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);
}
}
/**
* 對于每個人臉,若狀態(tài)為空或者為失敗,則請求特征提?。筛鶕?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();
}
注冊人臉的邏輯:
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、人臉庫的管理界面。
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("人臉庫已空!");
} else {
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle("通知")
.setMessage("確定要刪除" + faceNum + "個人臉嗎?")
.setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int deleteCount = FaceServer.getInstance().clearAllFaces(FaceLibs.this);
showToast(deleteCount + "個人臉已刪除!");
textView.setText("0");
}
})
.setNegativeButton("取消", null)
.create();
dialog.show();
}
}
}
14、以上就是大體的介紹,還有一些小的細枝末節(jié)需要同志們動手實操一下。下面就來看看實現(xiàn)的效果。
主界面:

注冊成功并通過識別:

通過手機照片識別出不是活體:

清理人臉庫:

以上就是Android基于虹軟(ArcSoft)實現(xiàn)人臉識別的詳細內(nèi)容,更多關(guān)于Android人臉識別的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開發(fā)中關(guān)于組件導(dǎo)出的風(fēng)險及防范
這篇文章主要介紹了Android開發(fā)中關(guān)于組件導(dǎo)出的風(fēng)險及防范,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
Android-Service實現(xiàn)手機壁紙自動更換
這篇文章主要為大家詳細介紹了Android-Service實現(xiàn)手機壁紙自動更換,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11
Android自定義控件RatingBar調(diào)整字體大小
這篇文章主要為大家詳細介紹了Android自定義控件RatingBar調(diào)整字體大小的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03
Android studio 引用aar 進行java開發(fā)的操作步驟
這篇文章主要介紹了Android studio 引用aar 進行java開發(fā)的操作步驟,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09
Android學(xué)習(xí)筆記-保存文件(Saving Files)
這篇文章主要介紹了Android中保存文件(Saving Files)的方法,需要的朋友可以參考下2014-10-10
android?studio實現(xiàn)簡單的計算器小功能
這篇文章主要為大家詳細介紹了android?studio實現(xiàn)簡單的計算器小功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05

