Android打开相机进行人脸识别,使用虹软人脸识别引擎
上一张效果图,渣画质,能看就好

功能说明:
人脸识别使用的是虹软的FreeSDK,包含人脸追踪,人脸检测,人脸识别,年龄、性别检测功能,其中本demo只使用了FT和FR(人脸追踪和人脸识别),封装了开启相机和人脸追踪、识别功能在FaceCameraHelper中。
实现逻辑:
打开相机,监听预览数据回调进行人脸追踪,且为每个检测到的人脸都分配一个trackID(上下帧位置变化不大的人脸框可认为是同一个人脸,具体实现的逻辑可见代码),同时,为了人脸搜索,为每个trackID都分配一个状态(识别中,识别失败,识别通过)、姓名,识别通过则在人脸框上显示姓名,否则只显示trackID(本demo没配服务端,只做了模拟操作)。流程说明见下图。

FaceCameraHelper包含的接口:
public interface FaceTrackListener {
/**
* 回传相机预览数据和人脸框位置
*
* @param nv21 相机预览数据
* @param ftFaceList 待处理的人脸列表
* @param trackIdList 人脸追踪ID列表
*/
void onPreviewData(byte[] nv21, List<AFT_FSDKFace> ftFaceList, List<Integer> trackIdList);
/**
* 当出现异常时执行
*
* @param e 异常信息
*/
void onFail(Exception e);
/**
* 当相机打开时执行
*
* @param camera 相机实例
*/
void onCameraOpened(Camera camera);
/**
* 根据自己的需要可以删除部分人脸,比如指定区域、留下最大人脸等
*
* @param ftFaceList 人脸列表
* @param trackIdList 人脸追踪ID列表
*/
void adjustFaceRectList(List<AFT_FSDKFace> ftFaceList, List<Integer> trackIdList);
/**
* 请求人脸特征后的回调
*
* @param frFace 人脸特征数据
* @param requestId 请求码
*/
void onFaceFeatureInfoGet(@Nullable AFR_FSDKFace frFace, Integer requestId);
}
```
FT人脸框绘制并回调数据:
@Override
public void onPreviewFrame(byte[] nv21, Camera camera) {
if (faceTrackListener != null) {
ftFaceList.clear();
int ftCode = ftEngine.AFT_FSDK_FaceFeatureDetect(nv21, previewSize.width, previewSize.height, AFT_FSDKEngine.CP_PAF_NV21, ftFaceList).getCode();
if (ftCode != 0) {
faceTrackListener.onFail(new Exception("ft failed,code is " + ftCode));
}
refreshTrackId(ftFaceList);
faceTrackListener.adjustFaceRectList(ftFaceList, currentTrackIdList);
if (surfaceViewRect != null) {
Canvas canvas = surfaceViewRect.getHolder().lockCanvas();
if (canvas == null) {
faceTrackListener.onFail(new Exception("can not get canvas of surfaceViewRect"));
return;
}
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
if (ftFaceList.size() > 0) {
for (int i = 0; i < ftFaceList.size(); i++) {
Rect adjustedRect = TrackUtil.adjustRect(new Rect(ftFaceList.get(i).getRect()), previewSize.width, previewSize.height, surfaceWidth, surfaceHeight, cameraOrientation, mCameraId);
TrackUtil.drawFaceRect(canvas, adjustedRect, faceRectColor, faceRectThickness, currentTrackIdList.get(i), nameMap.get(currentTrackIdList.get(i)));
}
}
surfaceViewRect.getHolder().unlockCanvasAndPost(canvas);
} faceTrackListener.onPreviewData(nv21, ftFaceList, currentTrackIdList);
}
}
大多数设备相机预览数据图像的朝向在横屏时为0度。其他情况按逆时针依次增加90度,因此人脸框的绘制需要做同步转化。CameraID为0时,也就是后置摄像头情况,相机预览数据的显示为原画面,而CameraID为1时,也就是前置摄像头情况,相机的预览画面显示为镜像画面,适配的代码:
/**
* @param rect FT人脸框
* @param previewWidth 相机预览的宽度
* @param previewHeight 相机预览高度
* @param canvasWidth 画布的宽度
* @param canvasHeight 画布的高度
* @param cameraOri 相机预览方向
* @param mCameraId 相机ID
* @return
*/
static Rect adjustRect(Rect rect, int previewWidth, int previewHeight, int canvasWidth, int canvasHeight, int cameraOri, int mCameraId) {
if (rect == null) {
return null;
}
if (canvasWidth < canvasHeight) {
int t = previewHeight;
previewHeight = previewWidth;
previewWidth = t;
} float horizontalRatio;
float verticalRatio;
if (cameraOri == 0 || cameraOri == 180) {
horizontalRatio = (float) canvasWidth / (float) previewWidth;
verticalRatio = (float) canvasHeight / (float) previewHeight;
} else {
horizontalRatio = (float) canvasHeight / (float) previewHeight;
verticalRatio = (float) canvasWidth / (float) previewWidth;
}
rect.left *= horizontalRatio;
rect.right *= horizontalRatio;
rect.top *= verticalRatio;
rect.bottom *= verticalRatio; Rect newRect = new Rect(); switch (cameraOri) {
case 0:
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
newRect.left = canvasWidth - rect.right;
newRect.right = canvasWidth - rect.left;
} else {
newRect.left = rect.left;
newRect.right = rect.right;
}
newRect.top = rect.top;
newRect.bottom = rect.bottom;
break;
case 90:
newRect.right = canvasWidth - rect.top;
newRect.left = canvasWidth - rect.bottom;
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
newRect.top = canvasHeight - rect.right;
newRect.bottom = canvasHeight - rect.left;
} else {
newRect.top = rect.left;
newRect.bottom = rect.right;
}
break;
case 180:
newRect.top = canvasHeight - rect.bottom;
newRect.bottom = canvasHeight - rect.top;
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
newRect.left = rect.left;
newRect.right = rect.right;
} else {
newRect.left = canvasWidth - rect.right;
newRect.right = canvasWidth - rect.left;
}
break;
case 270:
newRect.left = rect.top;
newRect.right = rect.bottom;
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
newRect.top = rect.left;
newRect.bottom = rect.right;
} else {
newRect.top = canvasHeight - rect.right;
newRect.bottom = canvasHeight - rect.left;
}
break;
default:
break;
}
return newRect;
}
由于FR引擎不支持多线程调用,因此只能串行执行,若需要更高效的实现,可创建多个FREngine实例进行任务分配。
FR线程队列:
private LinkedBlockingQueue<FaceRecognizeRunnable> faceRecognizeRunnables = new LinkedBlockingQueue<FaceRecognizeRunnable>(MAX_FRTHREAD_COUNT);
FR线程:
public class FaceRecognizeRunnable implements Runnable {
private Rect faceRect;
private int width;
private int height;
private int format;
private int ori;
private Integer requestId;
private byte[]nv21Data;
public FaceRecognizeRunnable(byte[]nv21Data,Rect faceRect, int width, int height, int format, int ori, Integer requestId) {
if (nv21Data==null) {
return;
}
this.nv21Data = new byte[nv21Data.length];
System.arraycopy(nv21Data,0,this.nv21Data,0,nv21Data.length);
this.faceRect = new Rect(faceRect);
this.width = width;
this.height = height;
this.format = format;
this.ori = ori;
this.requestId = requestId;
}
@Override
public void run() {
if (faceTrackListener!=null && nv21Data!=null) {
if (frEngine != null) {
AFR_FSDKFace frFace = new AFR_FSDKFace();
int frCode = frEngine.AFR_FSDK_ExtractFRFeature(nv21Data, width, height, format, faceRect, ori, frFace).getCode();
if (frCode == 0) {
faceTrackListener.onFaceFeatureInfoGet(frFace, requestId);
} else {
faceTrackListener.onFaceFeatureInfoGet(null, requestId);
faceTrackListener.onFail(new Exception("fr failed errorCode is " + frCode));
}
nv21Data = null;
}else {
faceTrackListener.onFaceFeatureInfoGet(null, requestId);
faceTrackListener.onFail(new Exception("fr failed ,frEngine is null" ));
}
if (faceRecognizeRunnables.size()>0){
executor.execute(faceRecognizeRunnables.poll());
}
}
}
}
上下帧是否为相同人脸的判断(trackID刷新):
/**
* 刷新trackId
*
* @param ftFaceList 传入的人脸列表
*/
public void refreshTrackId(List<AFT_FSDKFace> ftFaceList) {
currentTrackIdList.clear();
//每项预先填充-1
for (int i = 0; i < ftFaceList.size(); i++) {
currentTrackIdList.add(-1);
}
//前一次无人脸现在有人脸,填充新增TrackId
if (formerTrackIdList.size() == 0) {
for (int i = 0; i < ftFaceList.size(); i++) {
currentTrackIdList.set(i, ++currentTrackId);
}
} else {
//前后都有人脸,对于每一个人脸框
for (int i = 0; i < ftFaceList.size(); i++) {
//遍历上一次人脸框
for (int j = 0; j < formerFaceRectList.size(); j++) {
//若是同一张人脸
if (TrackUtil.isSameFace(SIMILARITY_RECT, formerFaceRectList.get(j), ftFaceList.get(i).getRect())) {
//记录ID
currentTrackIdList.set(i, formerTrackIdList.get(j));
break;
}
}
}
}
//上一次人脸框不存在此人脸
for (int i = 0; i < currentTrackIdList.size(); i++) {
if (currentTrackIdList.get(i) == -1) {
currentTrackIdList.set(i, ++currentTrackId);
}
}
formerTrackIdList.clear();
formerFaceRectList.clear();
for (int i = 0; i < ftFaceList.size(); i++) {
formerFaceRectList.add(new Rect(ftFaceList.get(i).getRect()));
formerTrackIdList.add(currentTrackIdList.get(i));
}
}
项目地址:https://github.com/wangshengyang1996/FaceTrackDemo
若有不当的地方望指出。
Android打开相机进行人脸识别,使用虹软人脸识别引擎的更多相关文章
- Android打开相机和打开相册
打开相机 /** * 选择相机 */ private void showCamera() { // 跳转到系统照相机 Intent cameraIntent = new Intent(MediaSto ...
- 虹软人脸识别 - Android Camera实时人脸追踪画框适配
在使用虹软人脸识别Android SDK的过程中 ,预览时一般都需要绘制人脸框,但是和PC平台相机应用不同,在Android平台相机进行应用开发还需要考虑前后置相机切换.设备横竖屏切换等情况,因此在人 ...
- Android 关于虹软人脸识别SDK引擎使用总结
虹软 最近开放了人脸识别的SDK引擎(免费的哦),刚好有Android版的,就体验了一波.下面来说说Android版的SDK使用心得: ArcFace 虹软人脸认知引擎简介 目前开放的版本有人脸比对( ...
- 虹软人脸识别ArcFace2.0 Android SDK使用教程
一.获取SDK 1.进入ArcFace2.0的申请地址 https://ai.arcsoft.com.cn/product/arcface.html 2.填写信息申请并提交 申请通过后即可下载SDK, ...
- 虹软人脸识别Android Sample Code
AFR_FSDKInterface engine = new AFR_FSDKEngine(); //用来存放提取到的人脸信息, face_1 是注册的人脸,face_2 是要识别的人脸 AFR_FS ...
- 虹软人脸识别SDK在网络摄像头中的实际应用
目前在人脸识别领域中,网络摄像头的使用很普遍,但接入网络摄像头和人脸识别SDK有一定门槛,在此篇中介绍过虹软人脸识别SDK的接入流程,本文着重介绍网络摄像头获取视频流并处理的流程(红色框内),以下内容 ...
- 虹软人脸识别 - faceId及IR活体检测的更新介绍
虹软人脸识别 - faceId及IR活体检测的介绍 前几天虹软推出了 Android ArcFace 2.2版本的SDK,相比于2.1版本,2.2版本中的变化如下: VIDEO模式新增faceId(类 ...
- 虹软人脸识别 - faceId及IR活体检测的介绍
虹软人脸识别 - faceId及IR活体检测的介绍 前几天虹软推出了 Android ArcFace 2.2版本的SDK,相比于2.1版本,2.2版本中的变化如下: VIDEO模式新增faceId(类 ...
- 虹软人脸识别SDK接入Milvus实现海量人脸快速检索
一.背景 人脸识别是近年来最热门的计算机视觉领域的应用之一,而且现在已经出现了非常多的人脸识别算法,如:DeepID.FaceNet.DeepFace等等.人脸识别被广泛应用于景区.客运.酒店.办公室 ...
随机推荐
- JDK源码之ThreadLocal
1.定义 ThreadLocal是线程变量,就是说每一个线程都有对应的该变量副本,线程修改该变量时,线程与线程之间的变量是相互隔离的,互相并看不见.这个结构附带在线程上,一个线程可以根据ThreadL ...
- php中通过Hashids将整数转化为唯一字符串
这个类主要是前台显示的主键ID转化成一串无规律的字符串,比较像 Youtube.Youku.Weibo之类的 id 名,从某种意义上可以防采集 在项目中,暴露给用户真实的项目ID,很有可能被恶意采集, ...
- Redis热点Key发现及常见解决方案!
一.热点Key问题产生的原因 1.用户消费的数据远大于生产的数据(热卖商品.热点新闻.热点评论.明星直播). 在日常工作生活中一些突发的的事件,例如:双十一期间某些热门商品的降价促销,当这其中的某一件 ...
- Linear Regression with PyTorch
Linear Regression with PyTorch Problem Description 初始化一组数据 \((x,y)\),使其满足这样的线性关系 \(y = w x + b\) .然后 ...
- Java字节码浅析(—)
英文原文链接,译文链接,原文作者:James Bloom,译者:有孚 明白Java代码是如何编译成字节码并在JVM上运行的非常重要,这有助于理解程序运行的时候究竟发生了些什么.理解这点不仅能搞清语言特 ...
- 你不知道的JavaScript(1)LHS查询和RHS查询
打算把<你不知道的JavaScript>中的知识点整理下,写点自己的心得,同时也敦促自己看书. 先做个整体的介绍,最后会再给个综合的例子. RHS 查询与简单地查找某个变量的值别无二致,而 ...
- day 27 异常处理
一.异常 1.什么是异常? 异常指的是与正常情况不同在程序中 程序的正常执行过程 按照代码顺序 一行一行的执行 直到所有的代码都执行完如果在执行过程中出现了错误导致代码无法执行完毕 这就称之为异常异常 ...
- An error occurred (500 Error)
Centos7 部署知乎出现上图问题 解决方法: [root@web02 ~]# vim /etc/php.ini #修改配置文件 session.auto_start = 0 #这条设置成0 [ro ...
- ibus-libpinyin 无法选择除第一个外的候选词
其实不只一个人遇到这问题 https://github.com/libpinyin/ibus-libpinyin/issues/127 临时可用的解决办法是: 清理libpinyin的cache目录相 ...
- apache安装时的一些术语
apache源码安装时,需要的哪些必须依赖模块? 主要需要apr, apr-util, pcre模块 其中 apr模块时必须的. 如何卸载 源码安装的软件? 在源码 的 解压目录下, 使用 make ...