上一张效果图,渣画质,能看就好

功能说明:

人脸识别使用的是虹软的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

若有不当的地方望指出。

转载于:https://www.cnblogs.com/Zzz-/p/10572138.html

Android打开相机进行人脸识别,使用虹软人脸识别引擎相关推荐

  1. android调用相机与相册的方法,Android打开相机和相册实例代码

    本文实例为大家分享了Android打开相机和相册具体代码,供大家参考,具体内容如下 打开相机 /** * 选择相机 */ private void showCamera() { // 跳转到系统照相机 ...

  2. OpenCV学习笔记(六)—— OpenCV for Android打开相机

    在之前的篇章中,我们完成了Android平台开发环境的配置,也找到了剔除OpenCV Manager API的办法,那么接下来我们开始从零开始,完成一个个人的程序,实现功能如下: 1.识别指定的图片, ...

  3. 详记Android打开相机拍照流程

    写在前面 本文并不是基于Camera2的,所以想要了解Camera2的同学可以先散了.文题加了详记二字,因为相机整个打开的流程的确是比较复杂的,稍有疏忽可能就会引发一系列问题.我也是看了一下Andro ...

  4. Android打开相机,报出 android.os.FileUriExposedException: file:///storage/emulated/0/test/img.jpg exposed

    执行以下代码后,打开相机 //调用相机自带的照相功能 指定图片目录 if (Environment.getExternalStorageState().endsWith(Environment.MED ...

  5. 人脸识别 无法打开相机 笔记本_笔记本上的人脸识别怎么用?需要安装驱动吗?...

    越来越多的电脑支持人脸识别 通过IR红外镜头识别面孔 进系统又快又安全 那么问题来了 怎么看你的电脑支不支持人脸识别 需不需要安装驱动 如何设置呢 1.是否支持人脸识别 右击开始菜单进入[设备管理器] ...

  6. android 打开相机拍照功能吗,Android调用相机实现拍照功能

    引言 在Android开发中相信大家都会遇到修改用户头像的问题,用户信息常常包含用户头像,一般流程为:默认头像-->用户修改(拍照/相册选择)-->保存头像图片. 本期我们就来实现调用系统 ...

  7. VB6.0人脸识别(使用虹软人脸识别SDK)

    需求:现有VB6.0的项目需要实现人脸识别功能. 问题:  自身完成VB6.0人脸识别不行.故借助虹软SDK.虹软SDK没有Demo可看.所以下载C#2.2版本Demo. 解决:  1.虹软SDK使用 ...

  8. android打开相机拍照及打开相册选择照片

    照相机拍照 Intent intent = new Intent();intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);// 照相机拍照// 需要说明 ...

  9. 基于虹软人脸识别,实现超市人脸支付

    前言 随着计算机和网络技术的不断发达,人脸识别在我们的生活中也不断的被应用,而无人超市以及在超市中的人脸支付并没有普及,本文作为一个项目进行超市人脸支付的场景测试与探索. 实现功能 使用python语 ...

最新文章

  1. js在PageOffice打开的Word文档光标处插入书签
  2. jQuery获取Select选择的Text和 Value
  3. 跟计算机断层扫描相关的技术,计算机断层扫描技术(简称PET)
  4. 具有GlassFish和一致性的高性能JPA –第1部分
  5. 4个空格和一个tab有什么区别_火花塞为什么一换就是4个?只换一个不行吗?
  6. boost 线程 linux,Boost Linux线程第一课
  7. 一款漂亮的赞助打赏单页界面美化版源码
  8. java版 二叉树 所有递归和非递归遍历算法
  9. PHP技术亮点,我眼里的THINKPHP5新亮点(1)
  10. c#操作Xml(五)
  11. 读取Flash w25x64未响应 导致卡死的问题
  12. Android终端系统APP应用性能测试之响应速度流畅度
  13. 华为企业组网实例:VRRP+MSTP典型组网配置
  14. CTGU实验5_2-创建还书罚款触发器
  15. 图片在相应页面变化的时候拉长
  16. Excel中替换单元格中的换行符
  17. 再谈:数据治理的长效运营机制!
  18. Unknown column 'xxx' in 'field list'
  19. 【vue教程】来自JSpang 技术胖的视频
  20. 1028 人口普查(C语言)

热门文章

  1. php7与mysql相关配置_PHP7连接mysql,文件配置问题
  2. mysql存储过程 begin_MySQL存储过程的BEGIN和END
  3. C语言计算表达式咋写,C语言如何计算表达式(x++)+(++x)+(x++)
  4. mysql 字符串运算_使用MySQL字符串运算实施精巧化SQL注入攻击
  5. java lbs_在 Java 中利用 redis 实现 LBS 服务
  6. dump的文件 查看pg_详解linux查看磁盘读写信息--blockdump、blktrace、systemtap
  7. 计算机的拓扑 树状结构图,树型网络拓扑结构
  8. java设计模式工厂模式_Java中的桥梁设计模式
  9. pytorch macos_Windows,Linux和MacOS上的PyTorch安装
  10. jsf用于页面判断的标签_用于操作和导航的JSF命令组件标签