Android 实例开发基于ArcSoft实现人脸识别
作者:FranzLiszt1847 发布时间:2022-01-01 16:34:06
标签:Android,人脸识别,ArcSoft
效果图
激活引擎
第一步配置APP_ID和SDK_KEY
int activeCode = FaceEngine.activeOnline( ChooseFunctionActivity.this, Param.APP_ID, Param.SDK_KEY);
public static final String APP_ID = "AwY6okHQHxtM92YRYSEqJQwb8cED5huPvYyMhK1w7BSo";
public static final String SDK_KEY = "AF8SaLYtP3ALsmaTR55y9UXaykBZjTtMt5gwCBkUGugh";
第二步:判断是否添加动态链接库(so文件与jar包)
private boolean checkSoFile(String[] libraries) {
File dir = new File(getApplicationInfo().nativeLibraryDir);
File[] files = dir.listFiles();
if (files == null || files.length == 0) {
return false;
}
List<String> libraryNameList = new ArrayList<>();
for (File file : files) {
libraryNameList.add(file.getName());
}
boolean exists = true;
for (String library : libraries) {
exists &= libraryNameList.contains(library);
}
return exists;
}
第三步:判断是否申明所有权限
protected boolean CheckPermissions(String[] neededPermissions) {
if (neededPermissions == null || neededPermissions.length == 0) {
return true;
}
boolean allGranted = true;
for (String neededPermission : neededPermissions) {
allGranted &= ContextCompat.checkSelfPermission(this, neededPermission) == PackageManager.PERMISSION_GRANTED;
}
return allGranted;
}
激活引擎代码如下
public void ActivationDevice(final View view) {
if (!libraryExists) {
ShowToast(getString(R.string.library_not_found));
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) {
RuntimeABI runtimeABI = FaceEngine.getRuntimeABI();
Log.i(TAG, "subscribe: getRuntimeABI() " + runtimeABI);
long start = System.currentTimeMillis();
int activeCode = FaceEngine.activeOnline( ChooseFunctionActivity.this, Param.APP_ID, Param.SDK_KEY);
Log.i(TAG, "subscribe cost: " + (System.currentTimeMillis() - start));
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) {
ShowToast(getString(R.string.activation_succeeded));
} else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {
ShowToast(getString(R.string.already_activated));
} else {
ShowToast(getString(R.string.active_failed, activeCode));
}
if (view != null) {
view.setClickable(true);
}
ActiveFileInfo activeFileInfo = new ActiveFileInfo();
int res = FaceEngine.getActiveFileInfo(ChooseFunctionActivity.this, activeFileInfo);
if (res == ErrorInfo.MOK) {
Log.i(TAG, activeFileInfo.toString());
}
}
@Override
public void onError(Throwable e) {
ShowToast(e.getMessage());
if (view != null) {
view.setClickable(true);
}
}
@Override
public void onComplete() {
}
});
}
人脸比对 1:N
第一步:初始化本地人脸库
FaceServer.getInstance().init(this);
第二步:初始化引擎和相机
public void onGlobalLayout() {
previewView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (!CheckPermissions(NEEDED_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, ACTION_REQUEST_PERMISSIONS);
} else {
initEngine();
initCamera();
}
}
第三步:初始化引擎
private void initEngine() {
ftEngine = new FaceEngine();
ftInitCode = ftEngine.init(this, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(this),
16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_DETECT);
frEngine = new FaceEngine();
frInitCode = frEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, MAX_DETECT_NUM, FaceEngine.ASF_FACE_RECOGNITION);
flEngine = new FaceEngine();
flInitCode = flEngine.init(this, DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_0_ONLY,
16, MAX_DETECT_NUM, FaceEngine.ASF_LIVENESS);
Log.i(TAG, "initEngine: init: " + ftInitCode);
if (ftInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "ftEngine", ftInitCode);
Log.i(TAG, "initEngine: " + error);
ShowToast(error);
}
if (frInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "frEngine", frInitCode);
Log.i(TAG, "initEngine: " + error);
ShowToast(error);
}
if (flInitCode != ErrorInfo.MOK) {
String error = getString(R.string.specific_engine_init_failed, "flEngine", flInitCode);
Log.i(TAG, "initEngine: " + error);
ShowToast(error);
}
}
第四步: * 检测
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的回调
@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);
}
// * 检测未出结果,或者非 * ,延迟执行该函数
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人脸数据,一般是人脸模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = getString(R.string.low_confidence_level);
} else {
msg = "ExtractCode:" + errorCode;
}
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, msg));
// 在尝试最大次数后,特征提取仍然失败,则认为识别未通过
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, getString(R.string.recognize_failed_notice, "NOT_ALIVE"));
// 延迟 FAIL_RETRY_INTERVAL 后,将该人脸状态置为UNKNOWN,帧回调处理时会重新进行 * 检测
retryLivenessDetectDelayed(requestId);
}
} else {
if (increaseAndGetValue(livenessErrorRetryMap, requestId) > MAX_RETRY_TIME) {
livenessErrorRetryMap.put(requestId, 0);
String msg;
// 传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊
if (errorCode != null && errorCode == ErrorInfo.MERR_FSDK_FACEFEATURE_LOW_CONFIDENCE_LEVEL) {
msg = getString(R.string.low_confidence_level);
} else {
msg = "ProcessCode:" + errorCode;
}
faceHelper.setName(requestId, getString(R.string.recognize_failed_notice, msg));
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());
// 切换相机的时候可能会导致预览尺寸发生变化
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(FaceComparison_RGB.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());
/**
* 在 * 检测开启,在人脸识别状态不为成功或人脸 * 状态不为处理中(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);
}
}
/**
* 对于每个人脸,若状态为空或者为失败,则请求特征提取(可根据需要添加其他判断以限制特征提取次数),
* 特征提取回传的人脸特征结果在{@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(FaceComparison_RGB.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);
// AlertDialog.Builder builder = new AlertDialog.Builder( FaceComparison_RGB.this );
// AlertDialog dialog = builder.create();
// View AlertDialog_View = View.inflate( FaceComparison_RGB.this,R.layout.register_result,null );
// dialog.setView( AlertDialog_View );
// dialog.show();
ShowPopWindows(success);
registerStatus = REGISTER_STATUS_DONE;
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
ShowToast("register failed!");
ShowFailPopWindows();
registerStatus = REGISTER_STATUS_DONE;
}
@Override
public void onComplete() {
}
});
}
}
切换前置、后置摄像头
public void switchCamera(View view) {
if (cameraHelper != null) {
boolean success = cameraHelper.switchCamera();
if (!success) {
ShowToast(getString(R.string.switch_camera_failed));
} else {
ShowToast(getString(R.string.notice_change_detect_degree));
}
}
}
尾言
本示例工程基于虹软(ArcSoft)官方Demo改编而成,若有唐突之处,望君海涵
来源:https://blog.csdn.net/News53231323/article/details/120216233


猜你喜欢
- 前言项目中时不时遇到查字典表等数据,只需要返回数据,不需要写其他业务,每个字典表可能都需要写一个接口给前端调用,比较麻烦,所以采用下面这种方
- C#删除只读文件的方法: if (File.GetAttributes(FFName).ToString().IndexOf("R
- 1、谷歌浏览器配置管理在代理服务器中,按上图进行设置,可以把localhost换成 127.0.0.1 ,端口换成你想设置的,但是不要与别的
- C#备忘录设计模式大家好,老胡又和大家见面了。首先承认今天的博客有点标题党了,人生是没有存档,也没有后悔药的。有存档和后悔药的,那是游戏,不
- 1.顺
- Spark_SQL的UDF使用用户自定义函数,也叫UDF,可以让我们使用Python/Java/Scala注册自定义函数,并在SQL中调用。
- volatilevolatile是一种轻量同步机制。请看例子MyThread25类public class MyThread25 exten
- 一、HandlerThread的介绍及使用举例  
- 前言Web Service也叫XML Web Service WebService是一种可以接收从Internet或者Intranet上的其
- Unity IPostBuildPlayerScriptDLLsUnity IPostBuildPlayerScriptDLLs是Unity
- Android虚拟键盘的弹起会遮挡住部分ui,虽然通过在清单文件中设置,可以随着虚拟键盘的弹出,布局往上推,但是面对登陆界面时,并没有太大的
- 简介在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需
- 目录事件最基本的用法理解路由事件WPF中使用路由事件升级了传统应用开发中的事件,在WPF中使用路由事件能更好的处理事件相关的逻辑,我们从这篇
- 使用try-with-resource机制关闭连接JAVA的一大特性就是JVM会对内部资源实现自动回收即自动GC,给开发者带来了极大的便利。
- 前言记得去年做一个聊天项目需要实现类似QQ的下拉刷新并且有侧滑删除的功能,在网上找了很久都没有QQ的完美,多多少少存在各种的问题,最后把下拉
- 预期当前安卓的所有proto都生成在一个module中,但是其实业务同学需要的并不是一个大杂烩, 只需要其中他们所关心的proto生成的类则
- 在一些音乐类应用中, 经常会展示随着节奏上下起伏的波纹信息, 这些波纹形象地传达了声音信息, 可以提升用户体验, 那么是如何实现的呢? 可以
- 在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计。有没有那么一
- 本节我们基于一个发表文章的案例来说明SpringBoot如何elasticsearch集成。elasticsearch本身可以是一个独立的服
- package com.test; import java.io.BufferedReader; import jav