人工智能时代正在快速到来,其中人脸识别是一项热门技术,在国内的应用也越来越多,比如刷脸打卡、刷脸APP、身份识别、人脸门禁等。本文将介绍基于虹软的Android人脸识别演示。来和边肖一起学习吧。
1.在虹软的开发者中心创建自己的应用,记录APP_ID和SDK_KEY,后面会用到。创建完成后,您可以下载SDK。
2.下载后可以参考和学习SDK包中的开发说明文档和代码。以下是开发说明文档中SDK包结构的截图。
3.创建一个空项目并复制。jar文件和。所以把SDK包中的文件放到项目的下面的包中。接下来的配置很重要。一个不处理,就是一个大bug。
4.“build.gradle in app”的第一个红框原本属于androidx,与支持不兼容,需要更改。所以整个项目中使用androidx的地方需要改变。第二个红框是ndk。添加此内容以查找。所以文件刚复制进来。第三个红框也应该更改如下。下面的依赖应该注意改变androidx的。
5.“整个项目中的build.gradle”记得添加jcenter()。
6.gradle.properties里可能有androidx的东西,也要删掉。
7.在AndroidManifest.xml中添加权限申请,并将其添加到。
清单:
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 '/
提供商:
供应者
Android:name=' Android . support . v4 . content . file provider '
Android:authorities=' $ { application id }。'提供商'
android:exported='false '
Android:grantUriPermissions=' true '
元数据
Android:name=' Android . support . file _ PROVIDER _ PATHS '
Android:resource=' @ XML/provider _ paths '/
/提供商
添加后,在res下创建一个xml包,并在其中添加一个provider_paths.xml文件,代码如下:
?xml版本='1.0 '编码='utf-8 '?
路径xmlns:Android=' http://schemas . Android . com/apk/RES/Android '
外部路径名='外部文件'路径=','/
根路径
name='根路径'
路径=“.”/
/路径
8.从SDK包中引入下面的函数包模块和BaseActivity,将常用包下常量中的APP_ID和SDK_KEY改为刚才记录的内容。
9.创建三个acvitity,一个是主界面,一个是人脸数据库的管理界面,一个是人脸识别功能界面。
10.布局包下需要引入以下五个布局文件。
11.主界面的主要功能是激活权限,连接动态库,激活引擎。我修改了onCreate()和Util包下ConfigUtil.class的代码,让它自动激活,自动修改为全方位人脸检测(其他选项好像无法实现人脸识别)。下面是激活引擎的代码。
公共void activeEngine(最终视图视图){
如果(!libraryExists) {
Toast.makeText(this,'找不到库文件!',吐司。LENGTH_SHORT)。show();
返回;
}
如果(!check PERMISSIONS(NEEDED _ PERMISSIONS)){
activity compat . REQUEST PERMISSIONS(this,NEEDED_PERMISSIONS,ACTION _ REQUEST _ PERMISSIONS);
返回;
}
如果(查看!=null) {
view . set clickable(false);
}
observable . create(new ObservableOnSubscribeInteger(){
@覆盖
公共void subscribe(observablemitterinteger发射器){
int active code=face engine . active online(main activity . this,常量。APP_ID,常量。SDK _ KEY);
emitter . on next(active code);
}
})。subscribeOn(Schedulers.io())。观察者离子(机器人调度程序。主线程())。subscribe(new observer integer(){
@覆盖
公共订阅无效(一次性d) {
}
@覆盖
公共void on next(整数活动代码){
if (activeCode==ErrorInfo .莫){
吐司。制作文本(主要活动。“这,”激活成功!',吐司. LENGTH_SHORT)。show();
} else if (activeCode==ErrorInfo .MERR ASF已经_激活){
吐司。制作文本(主要活动。“这,”已激活!',吐司. LENGTH_SHORT)。show();
}否则{
吐司。制作文本(主要活动。“这,”激活失败!',吐司. LENGTH_SHORT)。show();
}
如果(查看!=null) {
查看。设置clickable(真);
}
active fileinfo active fileinfo=new active fileinfo();
}
@覆盖
public void onError(Throwable e) {
吐司。制作文本(主要活动。这个,e.getMessage(),Toast .LENGTH_SHORT).show();
如果(查看!=null) {
查看。设置clickable(真);
}
}
@覆盖
公共void onComplete() {
}
});
}
12、人脸识别界面是最复杂的。其中不仅有人脸识别的功能,还有注册人脸和活体检测的功能。
通过手机自带的摄像头来实现人脸识别和活体检测的逻辑:
private void initCamera() {
显示指标metrics=新显示指标();
getWindowManager().getDefaultDisplay().getMetrics(度量);
最终人脸监听器人脸监听器=新人脸监听器(){
@覆盖
公共失效(例外e) {
Log.e(TAG,' on fail:' e . getmessage());
}
//请求神父的回调
@覆盖
public void onFaceFeatureInfoGet(@ Nullable final face feature face feature,final Integer requestId,final Integer errorCode) {
//FR成功
如果(faceFeature!=null) {
//Log.i(TAG,' on preview:fr end=' system。当前时间毫秒()'曲目id='请求id ');
整数活性=活性图。get(请求id);
//不做活体检测的情况,直接搜索
如果(!livenessDetect) {
searchFace(faceFeature,请求id);
}
//活体检测通过,搜索特征
else if(活跃度!=空活性==LivenessInfo .活着){
searchFace(faceFeature,请求id);
}
//活体检测未出结果,或者非活体,延迟执行该函数
否则{
if(requestfeaturestatusmap。包含密钥(请求id)){
可观察的。定时器(等待活动间隔,时间单位。毫秒)。订阅(新观察者Long() {
一次性的一次性的;
@覆盖
公共订阅无效(一次性d) {
一次性=d;
getfeaturedelayeddisposables。添加(一次性);
}
@覆盖
公共void onNext(Long aLong) {
onFaceFeatureInfoGet(人脸特征,requestId,错误代码);
}
@覆盖
public void onError(Throwable e) {
}
@覆盖
公共void onComplete() {
getfeaturedelayeddisposables。移除(一次性);
}
});
}
}
}
//特征提取失败
否则{
if(increasesandgetvalue(extractErrorRetryMap,requestId) MAX_RETRY_TIME) {
extracterrorretrymap。put(请求id,0);
字符串味精
//传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊
if (errorCode!=空错误代码==错误信息FSDK MERR面部特征_低_置信度_级别){
msg='人脸置信度低!';
}否则{
msg=' extract code:'错误代码;
}
faceHelper.setName(requestId,'未通过!');
//在尝试最大次数后,特征提取仍然失败,则认为识别未通过
requestfeaturestatusmap。put(请求id,RequestFeatureStatus .失败);
retryRecognizeDelayed(请求id);
}否则{
requestfeaturestatusmap。put(请求id,RequestFeatureStatus .TO _ RETRY);
}
}
}
@覆盖
public void onFaceLivenessInfoGet(@ Nullable LivenessInfo LivenessInfo,final Integer requestId,Integer errorCode) {
if (livenessInfo!=null) {
int活性=活性信息。get liveness();
livenessMap.put(requestId,liveness);
//非活体,重试
if (liveness==LivenessInfo .NOT_ALIVE) {
faceHelper.setName(requestId,'未通过!非活体!');
//延迟失败重试间隔后,将该人脸状态置为未知,帧回调处理时会重新进行活体检测
retrylivenesdetectdelayed(请求id);
}
}否则{
if(increasesandgetvalue(livenesserrretrymap,requestId) MAX_RETRY_TIME) {
livenesserrretrymap。put(请求id,0);
字符串味精
//传入的FaceInfo在指定的图像上无法解析人脸,此处使用的是RGB人脸数据,一般是人脸模糊
if (errorCode!=空错误代码==错误信息FSDK MERR面部特征_低_置信度_级别){
msg='人脸置信度低!';
}否则{
msg=' process code:'错误代码;
}
faceHelper.setName(requestId,'未通过!');
retrylivenesdetectdelayed(请求id);
}否则{
livenessMap.put(requestId,LivenessInfo .未知);
}
}
}
};
摄像机监听器摄像机监听器=新摄像机监听器(){
@覆盖
公共打开的相机上的void(Camera Camera,int cameraId,int displayOrientation,boolean isMirror) {
相机size lastPreviewSize=previewSize;
预览尺寸=相机。获取参数().getPreviewSize();
绘图助手=新绘图助手(预览大小。width,previewSize.height,previewView.getWidth(),previewView.getHeight(),displayOrientation
,cameraId,isMirror,false,false);
Log.i(TAG,' onCameraOpened:' draw helper。tostring());
//切换相机的时候可能会导致预览尺寸发生变化
if (faceHelper==null ||
lastPreviewSize==null ||
lastPreviewSize.width!=预览大小。宽度| | lastpreviewsize。身高!=previewSize.height) {
整数trackedFaceCount=null
//记录切换时的人脸序号
如果(faceHelper!=null) {
trackedface count=face helper。gettrackedface count();
脸帮手。发布();
}
faceHelper=新的faceHelper .构建器()。弗泰因(弗泰因)。法语(法语)。氟引擎(氟引擎)。frQueueSize(最大检测数量)。flQueueSize(最大检测数量)。预览大小(预览大小)。面部监听器(面部监听器)。跟踪的面部计数(跟踪的面部计数==null?配置实用程序。gettrackedface计数(faceregisterandrecognise。这个。getapplicationcontext()):tracked face count)。build();
}
}
@覆盖
预览时公共无效(最终字节[]nv21,相机相机){
if (faceRectView!=null) {
facerectview。清除faceinfo();
}
list face previewinfo face previewfolist=face helper。onpreviewframe(nv21);
if (facePreviewInfoList!=null faceRectView!=null drawHelper!=null) {
drawPreviewInfo(facepreviewfolist);
}
registerFace(nv21,facepreviewfolist);
清左脸(facepreviewfolist);
if (facePreviewInfoList!=null facepreviewfolist。size()0 previewSize!=null) {
for(int I=0;我面对预览列表。size();i ) {
整数status=requestfeaturestatusmap。get(face previewinfolist。get(I)).getTrackId());
/**
* 在活体检测开启,在人脸识别状态不为成功或人脸活体状态不为处理中(分析)且不为处理完成(活着、不活着)时重新进行活体检测
*/
if(livenessDetect(status==null | | status!=RequestFeatureStatus .成功)){
整数活性=活性图。get(face previewinfolist。get(I)).getTrackId());
如果(活跃度==空
||(活跃度!=LivenessInfo .活蹦乱跳!=LivenessInfo .不活着活跃度!=RequestLivenessStatus .正在分析){
活跃度图。put(facepreviewfolist。get(I)).getTrackId(),RequestLivenessStatus .正在分析);
脸帮手。requestfaceliveness(nv21,facepreviewfolist。get(I)).getFaceInfo(),previewSize.width,previewSize.height,FaceEngine .CP_PAF_NV21,facepreviewfolist。得到(I).getTrackId(),LivenessType .RGB);
}
}
/**
* 对于每个人脸,若状态为空或者为失败,则请求特征提取(可根据需要添加其他判断以限制特征提取次数),
* 特征提取回传的人脸特征结果在{ @ link face listener # onFaceFeatureInfoGet(face feature,Integer,Integer)}中回传
*/
如果(状态==空
|| status==RequestFeatureStatus .TO_RETRY) {
requestfeaturestatusmap。put(facepreviewfolist。get(I)).getTrackId(),RequestFeatureStatus .正在搜索);
脸帮手。请求面特征(nv21,facepreviewfolist。get(I)).getFaceInfo(),previewSize.width,previewSize.height,FaceEngine .CP_PAF_NV21,facepreviewfolist。得到(I).getTrackId());
//Log.i(TAG,' on preview:fr start=' system。当前时间毫秒()' track id=' facepreviewfolist。get(I)).getTrackedFaceCount());
}
}
}
}
@覆盖
public void onCameraClosed() {
Log.i(标记,' onCameraClosed:');
}
@覆盖
公共void onCameraError(异常e) {
Log.i(TAG,' onCameraError:' e . getmessage());
}
@覆盖
public void oncameraconconfigurationchanged(int camera id,int displayOrientation) {
if (drawHelper!=null) {
绘图助手。setcameradidisplayorientation(显示方向);
}
Log.i(TAG,' onCameraConfigurationChanged:' cameraID ' ' display orientation ');
}
};
cameraHelper=新cameraHelper .构建器()。预览视图大小(新的点(previewView.getMeasuredWidth()),previewView.getMeasuredHeight())。rotation(getWindowManager().getDefaultDisplay().getRotation())。specificCameraId(rgbCameraID!=null?rgbCameraID:相机100 . CameraInfo。相机_正面_正面)。isMirror(假)。预览(预览视图)。摄像师。build();
摄像助手。init();
摄像助手。start();
}
注册人脸的逻辑:
private void register face(final byte[]nv21,final list face previewinfo face previewfolist){
if(REGISTER STATUS==REGISTER _ STATUS _ READY face previewinfolist!=null face previewinfolist。size()0){
寄存器状态=寄存器_状态_处理;
可观察的。create(new ObservableOnSubscribeBoolean(){
@覆盖
公共void subscribe(observablemitterboolean发射器){
布尔成功=faceserver。getinstance()registernv 21(faceregisterandrecognise。this,nv21.clone(),previewSize.width,previewSize.height,
facepreviewfolist。获取(0).getFaceInfo(),'已注册'的面部助手。gettrackedfacecount());
emitter.onNext(成功);
}
})。订阅(计划程序。计算())。观察者离子(机器人调度程序。主线程())。subscribe(new ObserverBoolean(){
@覆盖
公共订阅无效(一次性d) {
}
@覆盖
公共void onNext(布尔成功){
字符串结果=成功?注册成功!":'注册失败!';
showToast(结果);
寄存器状态=寄存器状态完成;
}
@覆盖
public void onError(Throwable e) {
e。printstacktrace();
showToast('注册失败!');
寄存器状态=寄存器状态完成;
}
@覆盖
公共void onComplete() {
}
});
}
}
13、人脸库的管理界面。
公共类FaceLibs扩展基础活动{
私人执行服务执行服务;
私有文本视图文本视图
private TextView tvNotificationRegisterResult;
进度对话框进度对话框=空;
private static final int ACTION _ REQUEST _ PERMISSIONS=0x 001;
私有静态字符串[]需要_权限=新字符串[]{
显化。许可。阅读_外部_存储,
清单。权限。写_外部_存储
};
@覆盖
受保护的void onCreate(Bundle saved instancestate){
超级棒。oncreate(savedInstanceState);
setContentView(r . layout。activity _ face _ libs);
getWindow().addFlags(WindowManager .布局参数。FLAG _ KEEP _ SCREEN _ ON);
执行者服务=执行者。newsinglethreadexecutor();
tvNotificationRegisterResult=findViewById(r . id。通知_注册_结果);
进度对话框=新进度对话框(this);
int faceLibNum=faceserver。getinstance().get face number(this);
textView=findViewById(r . id。号);
文本视图。settext(faceLibNum ' ');
FaceServer.getInstance().初始化(这个);
}
@覆盖
受保护的void onDestroy() {
if (executorService!=null!executorService.isShutdown()) {
执行服务。立即关闭();
}
if (progressDialog!=空进度对话框。正在显示()){
进度对话框。dissolve();
}
FaceServer.getInstance().unInit();
超级棒。on destroy();
}
@覆盖
void afterRequestPermission(int请求代码,布尔值isAllGranted) {
}
公共空心净空面(视图视图){
int faceNum=faceserver。getinstance().get face number(this);
if (faceNum==0) {
showToast('人脸库已空!');
}否则{
报警对话框=新报警对话框。建筑商(本)。setTitle('通知)。setMessage('确定要删除' faceNum '个人脸吗?')。setPositiveButton('确定,新的对话界面OnClickListener() {
@覆盖
public void onClick(dialog interface dialog,int which) {
int deleteCount=FaceServer.getI
nstance().clearAllFaces(FaceLibs.this); showToast(deleteCount + "个人脸已删除!"); textView.setText("0"); } }) .setNegativeButton("取消", null) .create(); dialog.show(); } } }14、以上就是大体的介绍,还有一些小的细枝末节需要同志们动手实操一下。下面就来看看实现的效果。
主界面:
注册成功并通过识别:
通过手机照片识别出不是活体:
清理人脸库:
以上就是Android基于虹软(ArcSoft)实现人脸识别的详细内容,更多关于Android人脸识别的资料请关注我们其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。