从零玩转人脸识别之RGB人脸活体检测
从零玩转RGB人脸活体检测
前言
作者个人博客: 点击前往
本期教程人脸识别第三方平台为虹软科技,本文章讲解的是人脸识别RGB活体追踪技术,免费的功能很多可以自行搭配,希望在你看完本章课程有所收获。
ArcFace 离线SDK,包含人脸检测、性别检测、年龄检测、人脸识别、图像质量检测、RGB活体检测、IR活体检测等能力,初次使用时需联网激活,激活后即可在本地无网络环境下工作,可根据具体的业务需求结合人脸识别SDK灵活地进行应用层开发。
功能介绍
1. 人脸检测
对传入的图像数据进行人脸检测,返回人脸的边框以及朝向信息,可用于后续的人脸识别、特征提取、活体检测等操作;
- 支持IMAGE模式和VIDEO模式人脸检测。
- 支持单人脸、多人脸检测,最多支持检测人脸数为50。
2.人脸追踪
对来自于视频流中的图像数据,进行人脸检测,并对检测到的人脸进行持续跟踪。(我们是实时的所以就只能使用第三方操作,先不使用这个)
3.人脸特征提取
提取人脸特征信息,用于人脸的特征比对。
4.人脸属性检测
人脸属性,支持检测年龄、性别以及3D角度。
人脸3D角度:俯仰角(pitch), 横滚角(roll), 偏航角(yaw)。
5.活体检测
离线活体检测,静默式识别,在人脸识别过程中判断操作用户是否为真人,有效防御照片、视频、纸张等不同类型的作弊攻击,提高业务安全性,让人脸识别更安全、更快捷,体验更佳。支持单目RGB活体检测、双目(IR/RGB)活体检测,可满足各类人脸识别终端产品活体检测应用。
开造
访问地址: https://ai.arcsoft.com.cn/technology/faceTracking.html
进入开发者中心进行注册以及认证个人信息
1. 点击我的应用 > 新建应用
2.填写信息立即创建 点击 添加SDK
3.选中免费版人脸识别
4. 填写授权码信息
选择平台先选择windows的根据你的电脑配置来 是64位还是32位的, 语言选择Java
5. 介绍sdk文件
一、创建Springboot工程:ArcFace
1. maven依赖
<dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-configuration-processor</artifactid> <optional>true</optional> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency> <!--支持html--> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <!--虹软sdk--> <dependency> <groupid>com.arcsoft.face</groupid> <artifactid>arcsoft-sdk-face</artifactid> <version>3.0.0.0</version> <scope>system</scope> <systempath>${basedir}/lib/arcsoft-sdk-face-3.0.0.0.jar</systempath> </dependency> </dependencies>
2.创建lib文件夹将sdk复制
进来记得add依赖有小箭头就行
3.复制到测试类当中
4.填写好对应的appId和sdkKey
5.复制算法库路径
6.启动测试
我进行删除了一些功能就示范特征、活体检测, 其他的可自己试一试
体验到此结束,可以自己多玩玩
二、改造ArcFace工程
效果图
1. 创建FaceRecognitionUtils
package top.yangbuyi.utils; import com.arcsoft.face.*; import com.arcsoft.face.enums.*; import com.arcsoft.face.toolkit.ImageFactory; import com.arcsoft.face.toolkit.ImageInfo; import com.arcsoft.face.toolkit.ImageInfoEx; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.StringUtils; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * @program: ArcFace * @ClassName: FaceRecognitionUtils * @create: 2021-07-01 11:00 * @author: Yang shuai * @FaceRecognitionUtils: 人脸识别简易版$实现人脸检测活体是否为人脸,追踪人脸 **/ public class FaceRecognitionUtils { /** * APP ID,请先在虹软开发者中心注册、认证之后创建应用获取 */ @Value("crm.appId") private static String APP_ID = ""; /** * SDK KEY,请先在虹软开发者中心注册、认证之后创建应用获取 */ @Value("crm.sdk") private static String SDK_KEY = ""; /** * dll插件库地址 * linx 和 win 是不一样的 */ @Value("crm.face") private static String FACE_ENGINE = "WIN64"; private final static Logger logger = LogManager.getLogger(FaceRecognitionUtils.class.getName()); // 人脸引擎 private static FaceEngine faceEngine = new FaceEngine(FACE_ENGINE); // 创建引擎功能对象(用于初始化引擎) private static FunctionConfiguration functionConfiguration1 = new FunctionConfiguration(); // 创建引擎功能对象(用于人脸检测) private static FunctionConfiguration functionConfiguration2 = new FunctionConfiguration(); static { // 初始化引擎功能(用于初始化引擎) { // 是否支持年龄检测功能 functionConfiguration1.setSupportAge(true); // 是否支持3D检测功能 functionConfiguration1.setSupportFace3dAngle(true); // 是否支持人脸检测功能 functionConfiguration1.setSupportFaceDetect(true); // 是否支持人脸识别功能 functionConfiguration1.setSupportFaceRecognition(true); // 是否支持性别检测功能 functionConfiguration1.setSupportGender(true); // 是否支持RGB活体检测功能 functionConfiguration1.setSupportLiveness(true); // 是否支持IR活体检测功能 functionConfiguration1.setSupportIRLiveness(true); } // 初始化引擎功能(用于人脸检测) { // 是否支持年龄检测功能 functionConfiguration2.setSupportAge(true); // 是否支持3D检测功能 functionConfiguration2.setSupportFace3dAngle(true); // 是否支持性别检测功能 functionConfiguration2.setSupportGender(true); // 是否支持RGB活体检测功能 functionConfiguration2.setSupportLiveness(true); } } /** * 在线激活SDK * * @return */ public static void sdkActivation() { int errorCode = faceEngine.activeOnline(APP_ID, SDK_KEY); if (errorCode != ErrorInfo.MOK.getValue() && errorCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) { // SDK激活失败 logger.error("在线激活SDK失败!错误码:" + errorCode); } else { // SDK激活成功 logger.info("在线激活SDK成功!"); } } /** * 初始化引擎 * * @param detectMode 检测模式(推荐IMAGE 模式) * @param detectOrient 人脸检测角度(推荐人脸检测角度,逆时针0度) * @param faceMaxNum 人脸检测最大数量(推荐10) * @param faceScaleVal 最小人脸比例(VIDEO模式推荐16;IMAGE模式推荐32) */ public static void InitializeTheEngine(DetectMode detectMode, DetectOrient detectOrient, int faceMaxNum, int faceScaleVal) { // 创建引擎配置类 EngineConfiguration engineConfiguration = new EngineConfiguration(); // 设置detectMode参数 engineConfiguration.setDetectMode(detectMode); // 设置detectFaceOrientPriority参数 engineConfiguration.setDetectFaceOrientPriority(detectOrient); // 设置人脸检测最大数量 engineConfiguration.setDetectFaceMaxNum(faceMaxNum); // 设置detectFaceScaleVal参数为:识别的最小人脸比例 = 图片长边 / 人脸框长边的比值 engineConfiguration.setDetectFaceScaleVal(faceScaleVal); // 配置引擎功能 engineConfiguration.setFunctionConfiguration(functionConfiguration1); // 检测角度 // engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT); // 初始化引擎 int errorCode = faceEngine.init(engineConfiguration); if (errorCode != ErrorInfo.MOK.getValue()) { // 引擎初始化失败 logger.error("引擎初始化失败!错误码:" + errorCode); } else { // 引擎初始化成功 logger.info("引擎初始化成功!"); } } /** * 人脸检测(传入分离的图像信息数据) * * @param imageInfo 图像信息 * @param faceInfoList 人脸信息列表 * @return 检测结果,检测成功或是失败! */ public static boolean faceDetection1(ImageInfo imageInfo, List<faceinfo> faceInfoList) { int errorCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸检测失败 logger.error("人脸检测失败!错误码:" + errorCode); return false; } else { // 人脸检测成功 logger.info("人脸检测成功!"); return true; } } /** * 人脸检测(传入ImageInfoEx图像信息数据) * * @param imageInfo 图像信息 * @param faceInfoList 人脸信息列表 * @return 检测结果,检测成功或是失败! */ public static boolean faceDetection2(ImageInfo imageInfo, List<faceinfo> faceInfoList) { ImageInfoEx imageInfoEx = new ImageInfoEx(); imageInfoEx.setHeight(imageInfo.getHeight()); imageInfoEx.setWidth(imageInfo.getWidth()); imageInfoEx.setImageFormat(imageInfo.getImageFormat()); imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()}); imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3}); int errorCode = faceEngine.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸检测失败 logger.error("人脸检测失败!错误码:" + errorCode); return false; } else { // 人脸检测成功 logger.info("人脸检测成功!"); return true; } } /** * 人脸特征提取,是否检测到人脸 * * @param imageInfo * @return */ public static byte[] extractFaceFeature(ImageInfo imageInfo) { try { //人脸检测得到人脸列表 List<faceinfo> faceInfoList = new ArrayList<faceinfo>(); //人脸检测 int i = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList); if (faceInfoList.size() > 0) { FaceFeature faceFeature = new FaceFeature(); //提取人脸特征 faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList.get(0), faceFeature); return faceFeature.getFeatureData(); } } catch (Exception e) { logger.error("", e); } return null; } /** * 人脸特征提取(传入分离的图像信息数据) * * @param imageInfo 图像信息 * @param faceInfo 人脸信息 * @return 人脸特征 */ public static FaceFeature faceFeatureExtraction1(ImageInfo imageInfo, FaceInfo faceInfo) { FaceFeature faceFeature = new FaceFeature(); int errorCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfo, faceFeature); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸特征提取失败 logger.error("人脸特征提取失败!错误码:" + errorCode); return null; } else { // 人脸特征提取成功 logger.info("人脸特征提取成功!"); return faceFeature; } } /** * 人脸特征提取(传入ImageInfoEx图像信息数据) * * @param imageInfo 图像信息 * @param faceInfo 人脸信息 * @return 人脸特征 */ public static FaceFeature faceFeatureExtraction2(ImageInfo imageInfo, FaceInfo faceInfo) { ImageInfoEx imageInfoEx = new ImageInfoEx(); imageInfoEx.setHeight(imageInfo.getHeight()); imageInfoEx.setWidth(imageInfo.getWidth()); imageInfoEx.setImageFormat(imageInfo.getImageFormat()); imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()}); imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3}); // 创建人脸特征对象 FaceFeature faceFeature = new FaceFeature(); int errorCode = faceEngine.extractFaceFeature(imageInfoEx, faceInfo, faceFeature); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸特征提取失败 logger.error("人脸特征提取失败!错误码:" + errorCode); return null; } else { // 人脸特征提取成功 logger.info("人脸特征提取成功!"); return faceFeature; } } /** * 人脸特征比对 * * @param targetFaceFeature 目标人脸特征 * @param sourceFaceFeature 来源人脸特征 * @param compareModel 比对模型 * @return 比对相似度 */ public static Float faceFeatureComparison(FaceFeature targetFaceFeature, FaceFeature sourceFaceFeature, CompareModel compareModel) { // 创建比对相似度对象 FaceSimilar faceSimilar = new FaceSimilar(); int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, compareModel, faceSimilar); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸特征比对失败 logger.error("人脸特征比对失败!错误码:" + errorCode); return null; } else { // 人脸特征比对成功 logger.info("人脸特征比对成功!"); return faceSimilar.getScore(); } } /** * 人脸特征比对(默认LIFE_PHOTO比对模型) * * @param targetFaceFeature 目标人脸特征 * @param sourceFaceFeature 来源人脸特征 * @return 比对相似度 */ public static Float faceFeatureComparison(FaceFeature targetFaceFeature, FaceFeature sourceFaceFeature) { // 创建比对相似度对象 FaceSimilar faceSimilar = new FaceSimilar(); int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸特征比对失败 logger.error("人脸特征比对失败!错误码:" + errorCode); return null; } else { // 人脸特征比对成功 logger.info("人脸特征比对成功!"); return faceSimilar.getScore(); } } /** * 人脸属性检测(传入分离的图像信息数据) * * @param imageInfo 图像信息 * @param faceInfoList 人脸信息列表 * @return 检测结果,检测成功或是失败! */ public static boolean faceAttributeDetection1(ImageInfo imageInfo, List<faceinfo> faceInfoList) { int errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList, functionConfiguration2); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸属性检测失败 logger.error("人脸属性检测失败!错误码:" + errorCode); return false; } else { // 人脸属性检测成功 logger.info("人脸属性检测成功!"); return true; } } /** * 人脸属性检测(传入ImageInfoEx图像信息数据) * * @param imageInfo 图像信息 * @param faceInfoList 人脸信息列表 * @return 检测结果,检测成功或是失败! */ public static boolean faceAttributeDetection2(ImageInfo imageInfo, List<faceinfo> faceInfoList) { ImageInfoEx imageInfoEx = new ImageInfoEx(); imageInfoEx.setHeight(imageInfo.getHeight()); imageInfoEx.setWidth(imageInfo.getWidth()); imageInfoEx.setImageFormat(imageInfo.getImageFormat()); imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()}); imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3}); int errorCode = faceEngine.process(imageInfoEx, faceInfoList, functionConfiguration2); if (errorCode != ErrorInfo.MOK.getValue()) { // 人脸属性检测失败 logger.error("人脸属性检测失败!错误码:" + errorCode); return false; } else { // 人脸属性检测成功 logger.info("人脸属性检测成功!"); return true; } } /** * 获取年龄信息 * 注意:人脸属性检测之后方可调用 * * @return 获取结果,获取失败或是成功! */ public static boolean getAgeInfo(List<ageinfo> ageInfoList) { int errorCode = faceEngine.getAge(ageInfoList); if (errorCode != ErrorInfo.MOK.getValue()) { // 获取年龄信息失败 logger.error("获取年龄信息失败!错误码:" + errorCode); return false; } else { // 已成功获取年龄信息 logger.info("已成功获取年龄信息!"); return true; } } /** * 获取性别(0为男性,1为女性。) * 注意:人脸属性检测之后方可调用 * * @return 获取结果,获取失败或是成功! */ public static boolean getGender(List<genderinfo> genderInfoList) { // 性别检测 int errorCode = faceEngine.getGender(genderInfoList); if (errorCode != ErrorInfo.MOK.getValue()) { // 获取性别失败 logger.error("获取性别失败!错误码:" + errorCode); return false; } else { // 已成功获取年龄信息 logger.info("已成功获取性别!"); return true; } } /** * 获取人脸三维角度信息 * 人脸3D角度:俯仰角(pitch), 横滚角(roll), 偏航角(yaw)。 * 注意:人脸属性检测之后方可调用 * * @return 获取结果,获取失败或是成功! */ public static boolean getFace3DAngle(List<face3dangle> face3DAngleList) { // 人脸三维角度检测 int errorCode = faceEngine.getFace3DAngle(face3DAngleList); if (errorCode != ErrorInfo.MOK.getValue()) { // 获取人脸三维角度信息失败 logger.error("获取人脸三维角度信息失败!错误码:" + errorCode); return false; } else { // 已成功获取人脸三维角度信息 logger.info("已成功获取人脸三维角度信息!"); return true; } } /** * 获取RGB活体信息 * RGB活体值,未知=-1 、非活体=0 、活体=1、超出人脸=-2 * 注意:人脸属性检测之后方可调用 * * @return 获取结果,获取失败或是成功! */ public static boolean getLiveness(List<livenessinfo> livenessInfoList) { // RGB活体检测 int errorCode = faceEngine.getLiveness(livenessInfoList); if (errorCode != ErrorInfo.MOK.getValue()) { // 获取RGB活体信息失败 logger.error("获取RGB活体信息失败!错误码:" + errorCode); return false; } else { // 已成功获取RGB活体信息 logger.info("已成功获取RGB活体信息!"); return true; } } private static String base64Process(String base64Str) { if (!StringUtils.isEmpty(base64Str)) { String photoBase64 = base64Str.substring(0, 30).toLowerCase(); int indexOf = photoBase64.indexOf("base64,"); if (indexOf > 0) { base64Str = base64Str.substring(indexOf + 7); } return base64Str; } else { return ""; } } /** * IR活体检测(传入分离的图像信息数据) * 注意: * 引擎需要支持IR活体检测功能 * * @return 检测结果,检测成功或是失败! */ public static boolean detectionLiveness_IR1(String string) throws IOException { // 创建图像信息 // ImageInfo imageInfoGray = getGrayData(file); byte[] decode = Base64.decode(base64Process(string)); BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode)); ImageInfo imageInfoGray = ImageFactory.bufferedImage2ImageInfo(bufImage); // 创建人脸信息列表 List<faceinfo> faceInfoListGray = new ArrayList<faceinfo>(); // 人脸检测(传入分离的图像信息数据) int errorCode1 = faceEngine.detectFaces(imageInfoGray.getImageData(), imageInfoGray.getWidth(), imageInfoGray.getHeight(), imageInfoGray.getImageFormat(), faceInfoListGray); // 创建引擎功能实例对象 FunctionConfiguration configuration = new FunctionConfiguration(); // 设置引擎支持IR活体检测 configuration.setSupportIRLiveness(true); // IR活体检测 int errorCode2 = faceEngine.processIr(imageInfoGray.getImageData(), imageInfoGray.getWidth(), imageInfoGray.getHeight(), imageInfoGray.getImageFormat(), faceInfoListGray, configuration); if (errorCode1 != ErrorInfo.MOK.getValue() || errorCode2 != ErrorInfo.MOK.getValue()) { String errorCode = errorCode1 == 0 ? errorCode2 + "" : errorCode1 + ""; // IR活体检测失败 logger.error("IR活体检测失败!错误码:" + errorCode); return false; } else { // IR活体检测成功 logger.info("IR活体检测成功!"); return true; } } /** * IR活体检测(传入ImageInfoEx图像信息数据) * 注意: * 引擎需要支持年龄检测功能 * * @param imageInfo 图像信息 * @return 检测结果,检测成功或是失败! */ public static boolean detectionLiveness_IR2(ImageInfo imageInfo) { ImageInfoEx imageInfoEx = new ImageInfoEx(); imageInfoEx.setHeight(imageInfo.getHeight()); imageInfoEx.setWidth(imageInfo.getWidth()); imageInfoEx.setImageFormat(imageInfo.getImageFormat()); imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()}); imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3}); List<faceinfo> faceInfoList1 = new ArrayList<>(); int errorCode1 = faceEngine.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList1); FunctionConfiguration fun = new FunctionConfiguration(); fun.setSupportAge(true); int errorCode2 = faceEngine.processIr(imageInfoEx, faceInfoList1, fun); if (errorCode1 != ErrorInfo.MOK.getValue() || errorCode2 != ErrorInfo.MOK.getValue()) { String errorCode = errorCode1 == 0 ? errorCode2 + "" : errorCode1 + ""; // IR活体检测失败 logger.error("IR活体检测失败!错误码:" + errorCode); return false; } else { // IR活体检测成功 logger.info("IR活体检测成功!"); return true; } } /** * 获取IR活体信息 * IR活体值,未知=-1 、非活体=0 、活体=1、超出人脸=-2 * * @return 获取结果,获取失败或是成功! */ public static boolean getIrLiveness(List<irlivenessinfo> irLivenessInfo) { // IR活体检测 int errorCode = faceEngine.getLivenessIr(irLivenessInfo); if (errorCode != ErrorInfo.MOK.getValue()) { // 获取IR活体信息失败 logger.error("获取IR活体信息失败!错误码:" + errorCode); return false; } else { // 已成功获取IR活体信息 logger.info("已成功获取IR活体信息!"); return true; } } /** * 销毁SDK引擎 */ public static void destroyTheSDKEngine() { int errorCode = faceEngine.unInit(); if (errorCode != ErrorInfo.MOK.getValue()) { // 销毁SDK引擎失败 logger.error("销毁SDK引擎失败!错误码:" + errorCode); } else { // 销毁SDK引擎成功 logger.info("销毁SDK引擎成功!"); } } }
2.创建ErrorCodeEnum
package top.yangbuyi.constant; /** * 错误代码枚举 * * @author yang buyi * @date 2021/07/02 */ public enum ErrorCodeEnum { MOK(0, "成功"), UNKNOWN(1, "未知错误"), INVALID_PARAM(2, "无效参数"), UNSUPPORTED(3, "引擎不支持"), NO_MEMORY(4, "内存不足"), BAD_STATE(5, "状态错误"), USER_CANCEL(6, "用户取消相关操作"), EXPIRED(7, "操作时间过期"), USER_PAUSE(8, "用户暂停操作"), BUFFER_OVERFLOW(9, "缓冲上溢"), BUFFER_UNDERFLOW(10, "缓冲下溢"), NO_DISKSPACE(11, "存贮空间不足"), COMPONENT_NOT_EXIST(12, "组件不存在"), GLOBAL_DATA_NOT_EXIST(13, "全局数据不存在"), NO_FACE_DETECTED(14, "未检出到人脸"), FACE_DOES_NOT_MATCH(15, "人脸不匹配"), INVALID_APP_ID(28673, "无效的AppId"), INVALID_SDK_ID(28674, "无效的SdkKey"), INVALID_ID_PAIR(28675, "AppId和SdkKey不匹配"), MISMATCH_ID_AND_SDK(28676, "SdkKey 和使用的SDK 不匹配"), SYSTEM_VERSION_UNSUPPORTED(28677, "系统版本不被当前SDK所支持"), LICENCE_EXPIRED(28678, "SDK有效期过期,需要重新下载更新"), APS_ENGINE_HANDLE(69633, "引擎句柄非法"), APS_MEMMGR_HANDLE(69634, "内存句柄非法"), APS_DEVICEID_INVALID(69635, " Device ID 非法"), APS_DEVICEID_UNSUPPORTED(69636, "Device ID 不支持"), APS_MODEL_HANDLE(69637, "模板数据指针非法"), APS_MODEL_SIZE(69638, "模板数据长度非法"), APS_IMAGE_HANDLE(69639, "图像结构体指针非法"), APS_IMAGE_FORMAT_UNSUPPORTED(69640, "图像格式不支持"), APS_IMAGE_PARAM(69641, "图像参数非法"), APS_IMAGE_SIZE(69642, "图像尺寸大小超过支持范围"), APS_DEVICE_AVX2_UNSUPPORTED(69643, "处理器不支持AVX2指令"), FR_INVALID_MEMORY_INFO(73729, "无效的输入内存"), FR_INVALID_IMAGE_INFO(73730, "无效的输入图像参数"), FR_INVALID_FACE_INFO(73731, "无效的脸部信息"), FR_NO_GPU_AVAILABLE(73732, "当前设备无GPU可用"), FR_MISMATCHED_FEATURE_LEVEL(73733, "待比较的两个人脸特征的版本不一致"), FACEFEATURE_UNKNOWN(81921, "人脸特征检测错误未知"), FACEFEATURE_MEMORY(81922, "人脸特征检测内存错误"), FACEFEATURE_INVALID_FORMAT(81923, "人脸特征检测格式错误"), FACEFEATURE_INVALID_PARAM(81924, "人脸特征检测参数错误"), FACEFEATURE_LOW_CONFIDENCE_LEVEL(81925, "人脸特征检测结果置信度低"), ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_INIT(86017, "Engine不支持的检测属性"), ASF_EX_BASE_FEATURE_UNINITED(86018, "需要检测的属性未初始化"), ASF_EX_BASE_FEATURE_UNPROCESSED(86019, "待获取的属性未在process中处理过"), ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_PROCESS(86020, "PROCESS不支持的检测属性,例如FR,有自己独立的处理函数"), ASF_EX_BASE_INVALID_IMAGE_INFO(86021, "无效的输入图像"), ASF_EX_BASE_INVALID_FACE_INFO(86022, "无效的脸部信息"), ASF_BASE_ACTIVATION_FAIL(90113, "人脸比对SDK激活失败,请打开读写权限"), ASF_BASE_ALREADY_ACTIVATED(90114, "人脸比对SDK已激活"), ASF_BASE_NOT_ACTIVATED(90115, "人脸比对SDK未激活"), ASF_BASE_SCALE_NOT_SUPPORT(90116, "detectFaceScaleVal 不支持"), ASF_BASE_VERION_MISMATCH(90117, "SDK版本不匹配"), ASF_BASE_DEVICE_MISMATCH(90118, "设备不匹配"), ASF_BASE_UNIQUE_IDENTIFIER_MISMATCH(90119, "唯一标识不匹配"), ASF_BASE_PARAM_NULL(90120, "参数为空"), ASF_BASE_SDK_EXPIRED(90121, "SDK已过期"), ASF_BASE_VERSION_NOT_SUPPORT(90122, "版本不支持"), ASF_BASE_SIGN_ERROR(90123, "签名错误"), ASF_BASE_DATABASE_ERROR(90124, "数据库插入错误"), ASF_BASE_UNIQUE_CHECKOUT_FAIL(90125, "唯一标识符校验失败"), ASF_BASE_COLOR_SPACE_NOT_SUPPORT(90126, "输入的颜色空间不支持"), ASF_BASE_IMAGE_WIDTH_NOT_SUPPORT(90127, "输入图像的byte数据长度不正确"), ASF_NETWORK_BASE_COULDNT_RESOLVE_HOST(94209, "无法解析主机地址"), ASF_NETWORK_BASE_COULDNT_CONNECT_SERVER(94210, "无法连接服务器"), ASF_NETWORK_BASE_CONNECT_TIMEOUT(94211, "网络连接超时"), ASF_NETWORK_BASE_UNKNOWN_ERROR(94212, "未知错误"); private Integer code; private String description; ErrorCodeEnum(Integer code, String description) { this.code = code; this.description = description; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public static ErrorCodeEnum getDescriptionByCode(Integer code) { for (ErrorCodeEnum errorCodeEnum : ErrorCodeEnum.values()) { if (code.equals(errorCodeEnum.getCode())) { return errorCodeEnum; } } return ErrorCodeEnum.UNKNOWN; } }
3.创建ArcFaceController
package top.yangbuyi.controller; import com.arcsoft.face.*; import com.arcsoft.face.enums.DetectMode; import com.arcsoft.face.enums.DetectOrient; import com.arcsoft.face.toolkit.ImageFactory; import com.arcsoft.face.toolkit.ImageInfo; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import top.yangbuyi.constant.ErrorCodeEnum; import top.yangbuyi.utils.FaceRecognitionUtils; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @program: ArcFace * @ClassName: ArcFaceController * @create: 2021-07-01 14:30 * @author: Yang Shuai * @since: JDK1.8 * @ArcFaceController: 人脸活体检测$ **/ @RestController @Slf4j @RequestMapping("arcFace") public class ArcFaceController { /** * 人脸识别检测 * * @param url base64 地址 * @param oId 组织架构 ID * @param uid 当前登录检测的用户ID * @return */ @RequestMapping(value = "arcFaceSearch", method = RequestMethod.POST) public Map arcFaceSearch(@RequestParam String url, @RequestParam Integer oId, @RequestParam Integer uid) { // 前端展示原图 String urlTemp = url; // ...业务 final HashMap<string, object=""> stringObjectHashMap = new HashMap<>(14); stringObjectHashMap.put("success", false); // 初始化引擎 FaceRecognitionUtils.InitializeTheEngine(DetectMode.ASF_DETECT_MODE_IMAGE, DetectOrient.ASF_OP_0_ONLY, 10, 32); if (!StringUtils.isEmpty(url)) { String photoBase64 = url.substring(0, 30).toLowerCase(); int indexOf = photoBase64.indexOf("base64,"); if (indexOf > 0) { url = url.substring(indexOf + 7); } // 开始转码 byte[] decode = Base64.decode(url); BufferedImage bufImage = null; try { bufImage = ImageIO.read(new ByteArrayInputStream(decode)); } catch (IOException e) { e.printStackTrace(); return stringObjectHashMap; } // 获取图片信息 ImageInfo imageInfo = ImageFactory.bufferedImage2ImageInfo(bufImage); //人脸特征获取 byte[] bytes = FaceRecognitionUtils.extractFaceFeature(imageInfo); // 校验是否显示出人脸 if (bytes == null) { System.out.println(ErrorCodeEnum.NO_FACE_DETECTED.getDescription()); stringObjectHashMap.put("msg", ErrorCodeEnum.NO_FACE_DETECTED.getDescription()); return stringObjectHashMap; } // 创建图像中的人脸信息对象列表 List<faceinfo> faceInfoList1 = new ArrayList<>(); // 检测图像中人脸 FaceRecognitionUtils.faceDetection1(imageInfo, faceInfoList1); // 检测图像中人脸属性 FaceRecognitionUtils.faceAttributeDetection1(imageInfo, faceInfoList1); // 检测人脸特征 /* 图像中的人脸年龄 */ { // 创建图像中的人脸年龄列表 List<ageinfo> ageInfoList1 = new ArrayList<>(); // 检测图像中的人脸年龄列表 FaceRecognitionUtils.getAgeInfo(ageInfoList1); // 将图像中的年龄列表打印到控制台 if (ageInfoList1.size() > 0) { stringObjectHashMap.put("age", ageInfoList1.get(0).getAge()); } } /* 图像中的人脸性别 */ // 创建图像中的人脸性别列表 List<genderinfo> genderInfoList1 = new ArrayList<>(); // 检测图像中的人脸性别列表 FaceRecognitionUtils.getGender(genderInfoList1); // 将图像中的性别列表打印到控制台 if (genderInfoList1.size() > 0) { stringObjectHashMap.put("gender", genderInfoList1.get(0).getGender() == 0 ? "男" : "女"); } /* 图像1中的人脸三维角度 */ // 创建图像中的人脸三维角度信息列表 List<face3dangle> face3DAngleList1 = new ArrayList<>(); // 获取图像1中的人脸三维角度信息列表 FaceRecognitionUtils.getFace3DAngle(face3DAngleList1); // 将图像中的人脸三维角度信息列表打印到控制台 if (face3DAngleList1.size() > 0) { List<map<string, object="">> td = new ArrayList<>(); Map<string, object=""> map = new HashMap<>(); map.put("俯仰角", face3DAngleList1.get(0).getPitch()); map.put("横滚角", face3DAngleList1.get(0).getRoll()); map.put("偏航角", face3DAngleList1.get(0).getYaw()); td.add(map); stringObjectHashMap.put("ThreeDimensional", td); } /* 图像1中的人脸RGB活体值 */ // 创建图像中的RGB活体信息列表 List<livenessinfo> livenessInfoList1 = new ArrayList<>(); // 获取图像1中的RGB活体信息列表 FaceRecognitionUtils.getLiveness(livenessInfoList1); // 将图像中的RGB活体信息列表打印到控制台 if (livenessInfoList1.size() > 0) { stringObjectHashMap.put("RgbLiveness", livenessInfoList1.get(0).getLiveness()); } /** * 注意: 活体只能支持一个人脸否则返回未知 * 所以我们可以进行使用他来判断是否有多个人检测 直接判定失败 */ if (livenessInfoList1.size() > 0 && livenessInfoList1.get(0).getLiveness() == 1) { stringObjectHashMap.put("success", true); stringObjectHashMap.put("baseUrl", urlTemp); } } else { stringObjectHashMap.put("data", "url,不允许为空"); } return stringObjectHashMap; } }
4. 创建路由跳转前端页面 RouteController
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * @program: ArcFace * @ClassName: RouteController * @create: 2021-07-02 09:14 * @author: Yang Shuai * @since: JDK1.8 * @RouteController: $ **/ @Controller public class RouteController { @GetMapping("/") public String yby() { // ...业务 return "index"; } }
三. 前端人脸追踪插件
访问地址: https://trackingjs.com/
里面有demo可观看我就不带大家查看了
1. 创建前端index.html
js请下载demo获取,连接在最下面
<meta charset="UTF-8"> <title>人脸检测</title> <script src="jquery-3.3.1.min.js"></script> <script src="tracking.js"></script> <script src="face-min.js"></script> <script src="training/Landmarks.js"></script> <script src="training/Regressor.js"></script> <script src="stats.min.js"></script> <style> #regcoDiv { width: 100%; height: 530px; position: relative; background: #eee; overflow: hidden; border-bottom-right-radius: 10px; border-bottom-left-radius: 10px; /*-webkit-animation: twinkling 1s infinite ease-in-out;*/ /*-webkit-animation-duration: 1s;*/ /*animation-duration: 1s;*/ /*-webkit-animation-fill-mode: both;*/ /*animation-fill-mode: both*/ } video, canvas { margin-left: 230px; /*margin-top: 120px;*/ position: absolute; } .className { -webkit-animation: twinkling 1s infinite ease-in-out } .animated { -webkit-animation-duration: 1s; animation-duration: 1s; -webkit-animation-fill-mode: both; animation-fill-mode: both } @-webkit-keyframes twinkling { 0% { background: #eee; } 35% { background: #08e800; } 56% { background: #1f25d4; } 100% { background: #eee; } } @keyframes twinkling { 0% { background: #eee; } 35% { background: #08e800; } 56% { background: #1f25d4; } 100% { background: #eee; } } </style> <div id="regcoDiv"> </div> <div> <table frame="void"> <tbody><tr> <td> <button title="人脸识别" value="人脸识别" onclick="getMedia2()" style="color:#FFFFFF;height: 30px;display:block;margin:0 auto;margin-top:10px;width:120px;background-color: #3F51B5;border-radius:5px;text-align: center;line-height: 30px;font-size: 20px"> 摄像头识别 </button> </td> </tr> <tr> <td colspan="2"> <button id="snap" onclick="chooseFileChangeComp()" style="color:#FFFFFF;height: 30px;display:block;margin:0 auto;margin-top:10px;width:100px;background-color: #3F51B5;border-radius:5px;text-align: center;line-height: 30px;font-size: 20px"> 提交 </button> </td> </tr> </tbody></table> </div> <div> <img id="imageDivComp" src=""> </div> <script> getMedia2() $("#imageDivComp").click(function () { $("#chooseFileComp").click(); }); var t1; /** * 开始画摄像头 */ function getMedia2() { $("#regcoDiv").empty(); let vedioComp = "<video id='video2' width='500px' height='500px' autoplay='autoplay' playsinline webkit-playsinline='true' ></video><canvas id='canvas2' width='500px' height='500px'></canvas>"; $("#regcoDiv").append(vedioComp); let constraints = { video: {width: 500, height: 500}, audio: true }; //获得video摄像头区域 let video = document.getElementById("video2"); // 这里介绍新的方法,返回一个 Promise对象 // 这个Promise对象返回成功后的回调函数带一个 MediaStream 对象作为其参数 // then()是Promise对象里的方法 // then()方法是异步执行,当then()前的方法执行完后再执行then()内部的程序 // 避免数据没有获取到 let promise = navigator.mediaDevices.getUserMedia(constraints); promise.then(function (MediaStream) { video.srcObject = MediaStream; video.play(); }); /** * 模拟手机端 三秒主动提交检测 * @type {number} */ t1 = window.setInterval(function () { chooseFileChangeComp() }, 3000) } /** * 提交检测 请求接口 */ function chooseFileChangeComp() { let regcoDivComp = $("#regcoDiv"); if (regcoDivComp.has('video').length) { let video = document.getElementById("video2"); let canvas = document.getElementById("canvas2"); let ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, 500, 500); var base64File = canvas.toDataURL(); var formData = new FormData(); formData.append("url", base64File); formData.append("oId", 1); formData.append("uid", 1); $.ajax({ type: "post", url: "/arcFace/arcFaceSearch", data: formData, contentType: false, processData: false, async: false, success: function (text) { var res = JSON.stringify(text) if (text.success == true && text.RgbLiveness == 1) { console.log(text); clearInterval(t1); console.log(text.baseUrl); } else { console.log(text); } }, error: function (error) { alert(JSON.stringify(error)) } }); } } /** * 人脸追踪画框 **/ window.onload = function () { let video = document.getElementById("video2"); let canvas = document.getElementById("canvas2"); let context = canvas.getContext('2d'); var tracker = new tracking.LandmarksTracker(); tracker.setInitialScale(4); tracker.setStepSize(2); tracker.setEdgesDensity(0.1); tracking.track(video, tracker); tracker.on('track', function (event) { context.clearRect(0, 0, canvas.width, canvas.height); if (!event.data) return; // 画框样式 event.data.faces.forEach(function (rect) { context.strokeStyle = '#eb4c4c'; context.strokeRect(rect.x, rect.y, rect.width, rect.height); context.font = '16px Helvetica'; context.fillStyle = "#000"; context.lineWidth = '5'; context.fillText('人脸横向: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11); context.fillText('人脸纵向: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 50); }); /** * 人脸追踪 颗粒 */ event.data.landmarks.forEach(function (landmarks) { for (var l in landmarks) { context.beginPath(); context.fillStyle = "#fff"; context.arc(landmarks[l][0], landmarks[l][1], 1, 0, 2 * Math.PI); context.fill(); } }); }); // 这里如果报错 不用管 var gui = new dat.GUI(); gui.add(tracker, 'edgesDensity', 0.1, 0.5).step(0.01).listen(); gui.add(tracker, 'initialScale', 1.0, 10.0).step(0.1).listen(); gui.add(tracker, 'stepSize', 1, 5).step(0.1).listen(); }; </script>
6. 启动工程 访问 http://localhost:7000/
四. 人脸识别追踪就到这里啦,具体的代码已经提交到gitee请前往获取Java项目 ArcFace
你的压力来源于无法自律,只是假装努力,现状跟不上内心欲望,所以你焦虑又恐慌。——杨不易
本文作者:杨不易呀
本文链接:https://www.cnblogs.com/yby6/p/14998010.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性