前言
最近新公司刚来就一个新项目启动,让实现一个人脸识别的平板APP用于门店开门(人脸识别只是其中一个模块),刚好现在AI大潮来袭早晚都要接触这些,这也是个契机,二话不说直接开干。
目前市面上做人脸方面的公司非常多,列举几个:
- 百度人脸识别
- Face++
- 商汤科技
- 腾讯
- 虹软
当然还有一些其他的,目前比较出名的大概就这些,差别其实更多是在技术支持上(至少我是这么认为的)以及费用,我接触的几个:百度、Face++、虹软,大概对比下: - 百度人脸识
优点:价格便宜
缺点:技术支持真的懒得说,常年不在线,找技术基本都是让看看这个看看那个没点用 - Face++
优点:技术更好,文档很清晰,因为没有接入暂且不知道技术支持怎么样不过应该不差
缺点:有点小贵 - 虹软
优点:全套离线,后台前端都可以离线实现,文档也挺详细的
缺点:看了下他们技术论坛,貌似问题也不少,而且回复也不是很及时的样子,最主要是需要自己搭建一套,他们的人脸对比居然也是放在本地APP数据库的(当然这不算是缺点了)
说了这么多,想用啥自己选择就行,我们公司目前用的百度人脸识别,进入正题不多BB。
一、注册百度开发者账号
这个注册稍微要点时间,大概需要几个工作日,而且需要公司的资质信息,我们当时已经有了所以我就直接拿来用
二、新建项目获取授权文件
这一步算是前期测试的重要步骤,先要到控制台(默认第一步已经完成了)链接
这个控制台基本就是人脸识别的所有开发SDK,技术资料的地方了,前端的SDK在SDK管理里面进行下载
首先你需要在采集SDK管理里面下载授权文件,他会让你跟你据包名跟key的MD5来进行生成,具体步骤按着他们的操作就行了
就是上图的样子,这个里面的License ID和包名都很重要,包名要根据你自己项目的包名来写。
然后下载License等下会用到
SDK下载跟示例工程下载
在采集SDK管理这里下拉会看到下载SDK跟示例工程这两栏
但是我建议你可以直接根据你们公司的业务需求下载对应的示例工程,比如我下载的就是人脸登陆/考勤这个,这里面已经包含了全套的离线SDK功能(活体检测,人脸追踪,质量检测等等)
下载完示例工程后把项目导入AS,下面就是改动下包名跟License
- 修改包名
在app build里面改成自己上图里面的包名:com.test.facere - 修改License
把刚下载的License文件导入到对应的 assets包下,修改工程里面Config.java的licenseID(上图的)以及licenseFileName(对应License的文件名)
以上两步做完没啥问题示例工程就可以跑起来了
代码讲解
以我用的人脸登陆/考勤这个示例工程修改优化后的项目做参考来大致讲解下(其实每个示例工程都有集成文档,可以自己看)
我们项目目前的业务逻辑就是用人脸对比(1:N)进行人脸识别开门,所以要运用到的就是人脸活体检测,人脸追踪以及人脸对比这几个技术,其中人脸活体检测跟人脸追踪是在离线SDK里面实现的,人脸对比是跟百度云服务器进行匹配的,当然我们会在手机端先把人脸进行注册,不过手机APP端的人脸注册重心在我们自己的服务器调用百度的人脸注册,所以手机端就是上传人脸就OK了。
人脸登陆/考勤这个示例工程这个项目里面有几个Activity,主要是注册人脸,人脸登陆,快速检测人脸登陆,我用的是快速检测人脸登陆(DetectLoginActivity.java)
1.FaceDetectManager
这个类封装了人脸检测的整体逻辑包括开启人脸检测start
,关闭人脸检测stop
,设置人脸检测监听器setOnFaceDetectListener
,设置人检跟踪回调setOnTrackListener
2faceDetectManager.setOnFaceDetectListener
设置人脸检测监听器
这个监听器是人脸识别主要方法之一
public interface OnFaceDetectListener {
void onDetectFace(int status, FaceInfo[] infos, ImageFrame imageFrame);
}
这是它的回调方法,里面包含了人脸检测状态 status(用于处理人脸距离角度方向等等检测),人脸信息infos,这里面是一组人脸人信息不过只用到Infos[0]这个就好,还有封装了一帧图片的imageFrame,包含了该帧图片的大小等信息
我是在外围做了一些动画效果,修改了一下它本来的这个圆形遮罩
这个类里面已经有完善的提示功能其他代码就暂时不贴了可以对照项目看。
说下遇到的问题
- 因为我是平板做的摄像头用的USB所以距离检测(其实就是人脸的长宽高)就暂时屏蔽了(因为摄像头跟手机摄像头相比还是有点模糊)
- 用的平板USB摄像头所以会出现镜像卡帧,解决办法就是
ICameraControl control = cameraImageSource.getCameraControl();
control.setPreviewView(previewView);
control.setCameraFacing(ICameraControl.CAMERA_USB);
previewView.getTextureView().setScaleX(-1);
设置为USB模式以及previewView预览反转
这里设置USB模式有个问题,就是这个工程其实是一个手机端的项目,但是我用到了平板端,在ICameraControl这个接口中是没有CAMERA_USB这个字段的,需要在里面加入
int CAMERA_FACING_BACK = 0;
int CAMERA_FACING_FRONT = 1;
int CAMERA_USB = 2;
@IntDef({CAMERA_FACING_FRONT, CAMERA_FACING_BACK, CAMERA_USB})
@interface CameraFacing {
}
这样子就可以了
3.faceDetectManager.setOnTrackListener
设置人脸检测监听器
回调方法是
public void onTrack(FaceFilter.TrackedModel trackedModel)
乍一看这个回调跟
faceDetectManager.setOnFaceDetectListener
差不多,其实如果仔细看FaceDetectManager
这个类的话会发现在
private void process(int[] argb, int width, int height, ArgbPool pool)
这个方法里面有这样一段代码
if (value == 0) {
faceFilter.filter(faces, frame);//等于0的时候才带过去
}
if (listener != null) {
listener.onDetectFace(value, faces, frame); //检测人脸把value值也带过去,用于判断人脸位置
}
当value为0(表示是一张合格人脸)的时候会在FaceFilter中调用filter方法,并且在该方法中把一个单个face设置到onTrace回调中,如果listener不为空的话直接放到onDetectFace这个回调中,所以从这里也可以看出来其实
faceDetectManager.setOnFaceDetectListener
就是为了让你获取一张合格的人脸(可以在这个里面处理你具体的合格人脸操作)
回归正题,既然onTrace回调是一个合格的人脸就好办了,可以直接拿到TrackedModel里面的人脸图片和服务器进行比对,具体代码逻辑示例代码里面也已经实现了,对比结束后会返回一定的分数给你,如果大于80或者你觉得的分数就认定这个是你在手机端注册过的人脸,然后进行逻辑处理(比如开门)
遇到的问题
- 由于我们的业务需求是要求平板一直运行,就算是识别失败或者成功也要返回到人脸识别的页面(不finish人脸识别页面),造成了性能影响很大,主要现象就是每次返回过来了在进行识别会出现卡住动不了,解决办法就是在onPause和onResume里面加上一个是否在前台的标志,如果不在前台就让人脸识别停止识别,在前台后继续识别(不是单纯的faceDetectManager.stop()这样子会报错)
- 由于这个示例工程是手机端的,我拿到平板端使用肯定需要修改,代码讲解部分已经说了USB这个问题,其实虽然就是两行代码的事,但是当时我试了很久才解决(问百度那边一直在扯犊子,写工单也是很久回复说些没用的东西,拉了个微信群也是带理不理的,最后放弃了,自己去研究)
- 裁剪处理器出现问题
// 设置检测裁剪处理器
faceDetectManager.addPreProcessor(cropProcessor);
这行代码放在平板端会报错,所以需要进行修改,经过调试发现出错的原因在FaceCropper
这个类里面
/**
* 裁剪argb中的一块儿,裁剪框如果超出图片范围会被调整,所以记得检查。
* @param argb 图片argb数据
* @param width 图片宽度
* @param rect 裁剪框
*/
public static int[] crop(int[] argb, int width, Rect rect) {
adjustRect(argb, width, rect);
int[] image = new int[rect.width() * rect.height()];
for (int i = rect.top; i < rect.bottom; i++) {
int rowIndex = width * i;//9
try {
System.arraycopy(argb, Math.abs(rowIndex + rect.left), image, rect.width() * (i - rect.top), rect.width());
} catch (Exception e) {
e.printStackTrace();
return argb;
}
}
return image;
}
以上是修改后的代码,主要是
System.arraycopy(argb, Math.abs(rowIndex + rect.left), image, rect.width() * (i - rect.top), rect.width());
这段,需要把rowIndex+rect.left 变成正数,之前是一个负数造成越界报错
- 判断脸部的中心点位置用于处理脸是否在对应的布局里面
faceDetectManager.setOnFaceDetectListener(new FaceDetectManager.OnFaceDetectListener() { //设置人脸检测监听器,检测后的结果会回调。
@Override
public void onDetectFace(final int retCode, FaceInfo[] infos, ImageFrame frame) {
上面代码里里面的infos[0] 里面有两个参数,一个是脸中心点X轴坐标,一个是Y坐标
public class FaceInfo {
public int mWidth;
public int mAngle;
public int mCenter_y;
public int mCenter_x;
public float mConf;
public int[] landmarks;
public int face_id;
public float[] headPose;
public int[] is_live;
也就是mCenter_x
和mCenter_y
,有了这俩神器你就可以轻松处理脸部位置,让他在你想要的布局中,我自己逻辑的完整代码:
if (infos[0]!= null) {
if(info.mCenter_x<218||info.mCenter_x>600){
headXY=false;
str ="请把脸移入框内";
}else {
headXY=true;
}
}
以上,我也是刚接触这块,很多地方也是慢慢琢磨,有问题的地方还请大家多多指教