前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零玩转RGB人脸活体检测

从零玩转RGB人脸活体检测

原创
作者头像
杨不易呀
修改2022-01-19 14:08:01
1.4K0
修改2022-01-19 14:08:01
举报
文章被收录于专栏:杨不易呀杨不易呀

从零玩转RGB人脸活体检测

前言

因工作需要手机端运用人脸识别打卡,本期教程人脸识别第三方平台为虹软科技,本文章讲解的是人脸识别RGB活体追踪技术,免费的功能很多可以自行搭配,希望在你看完本章课程有所收获。

人脸追踪示例
人脸追踪示例

ArcFace 离线SDK,包含人脸检测、性别检测、年龄检测、人脸识别、图像质量检测、RGB活体检测、IR活体检测等能力,初次使用时需联网激活,激活后即可在本地无网络环境下工作,可根据具体的业务需求结合人脸识别SDK灵活地进行应用层开发。

SDK功能模块图.png
SDK功能模块图.png

功能介绍

1. 人脸检测

对传入的图像数据进行人脸检测,返回人脸的边框以及朝向信息,可用于后续的人脸识别、特征提取、活体检测等操作;

  • 支持IMAGE模式和VIDEO模式人脸检测。
  • 支持单人脸、多人脸检测,最多支持检测人脸数为50。

2.人脸追踪

对来自于视频流中的图像数据,进行人脸检测,并对检测到的人脸进行持续跟踪。(我们是实时的所以就只能使用第三方操作,先不使用这个)

3.人脸特征提取

提取人脸特征信息,用于人脸的特征比对。

4.人脸属性检测

人脸属性,支持检测年龄、性别以及3D角度。

人脸3D角度:俯仰角(pitch), 横滚角(roll), 偏航角(yaw)。

3D角度
3D角度

5.活体检测

离线活体检测,静默式识别,在人脸识别过程中判断操作用户是否为真人,有效防御照片、视频、纸张等不同类型的作弊攻击,提高业务安全性,让人脸识别更安全、更快捷,体验更佳。支持单目RGB活体检测、双目(IR/RGB)活体检测,可满足各类人脸识别终端产品活体检测应用。

开造

访问地址: https://ai.arcsoft.com.cn/technology/faceTracking.html 进入开发者中心进行注册以及认证个人信息
1. 点击我的应用 > 新建应用
image.png
image.png
2.填写信息立即创建 点击 添加SDK
image.png
image.png
3.选中免费版人脸识别
image.png
image.png
4. 填写授权码信息
选择平台先选择windows的根据你的电脑配置来 是64位还是32位的, 语言选择Java
image.png
image.png
image.png
image.png
5. 介绍sdk文件
image.png
image.png

一、创建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依赖有小箭头就行
image.png
image.png

3.复制到测试类当中

image.png
image.png
image.png
image.png

4.填写好对应的appId和sdkKey

image.png
image.png

5.复制算法库路径

image.png
image.png
image.png
image.png

6.启动测试

我进行删除了一些功能就示范特征、活体检测, 其他的可自己试一试
image.png
image.png

体验到此结束,可以自己多玩玩

二、改造ArcFace工程

效果图
image.png
image.png
image.png
image.png

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可观看我就不带大家查看了
image.png
image.png

1. 创建前端index.html

js请下载demo获取,连接在最下面
<!DOCTYPE html>
<html lang="en">
<head>
	<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>
</head>
<body>

<div id="regcoDiv">

</div>
<div>
	<table frame="void">
		<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>
	</table>

</div>
<div>
	<img id="imageDivComp"/>
</div>

</body>
</html>
<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

点击前往获取demo

其他文章

1、从零玩转腾讯滑块验证码

2、从零玩转人脸识别之RGB人脸活体检测

3、从零玩转QQ登录

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从零玩转RGB人脸活体检测
    • 前言
      • 功能介绍
        • 1. 人脸检测
        • 2.人脸追踪
        • 3.人脸特征提取
        • 4.人脸属性检测
        • 5.活体检测
      • 开造
        • 一、创建Springboot工程:ArcFace
          • 1. maven依赖
          • 2.创建lib文件夹将sdk复制
          • 3.复制到测试类当中
          • 4.填写好对应的appId和sdkKey
          • 5.复制算法库路径
          • 6.启动测试
          • 体验到此结束,可以自己多玩玩
        • 二、改造ArcFace工程
          • 1. 创建FaceRecognitionUtils
          • 2.创建ErrorCodeEnum
          • 3.创建ArcFaceController
          • 4. 创建路由跳转前端页面 RouteController
        • 三. 前端人脸追踪插件
          • 访问地址: https://trackingjs.com/
          • 1. 创建前端index.html
          • 6. 启动工程 访问 http://localhost:7000/
        • 四. 人脸识别追踪就到这里啦,具体的代码已经提交到gitee请前往获取Java项目 ArcFace
          • 其他文章
          相关产品与服务
          人脸识别
          腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档