从零玩转人脸识别之RGB人脸活体检测
從零玩轉(zhuǎn)RGB人臉活體檢測(cè)
前言
作者個(gè)人博客: 點(diǎn)擊前往
本期教程人臉識(shí)別第三方平臺(tái)為虹軟科技,本文章講解的是人臉識(shí)別RGB活體追蹤技術(shù),免費(fèi)的功能很多可以自行搭配,希望在你看完本章課程有所收獲。
ArcFace 離線SDK,包含人臉檢測(cè)、性別檢測(cè)、年齡檢測(cè)、人臉識(shí)別、圖像質(zhì)量檢測(cè)、RGB活體檢測(cè)、IR活體檢測(cè)等能力,初次使用時(shí)需聯(lián)網(wǎng)激活,激活后即可在本地?zé)o網(wǎng)絡(luò)環(huán)境下工作,可根據(jù)具體的業(yè)務(wù)需求結(jié)合人臉識(shí)別SDK靈活地進(jìn)行應(yīng)用層開發(fā)。
功能介紹
1. 人臉檢測(cè)
對(duì)傳入的圖像數(shù)據(jù)進(jìn)行人臉檢測(cè),返回人臉的邊框以及朝向信息,可用于后續(xù)的人臉識(shí)別、特征提取、活體檢測(cè)等操作;
支持IMAGE模式和VIDEO模式人臉檢測(cè)。
支持單人臉、多人臉檢測(cè),最多支持檢測(cè)人臉數(shù)為50。
2.人臉追蹤
對(duì)來自于視頻流中的圖像數(shù)據(jù),進(jìn)行人臉檢測(cè),并對(duì)檢測(cè)到的人臉進(jìn)行持續(xù)跟蹤。(我們是實(shí)時(shí)的所以就只能使用第三方操作,先不使用這個(gè))
3.人臉特征提取
提取人臉特征信息,用于人臉的特征比對(duì)。
4.人臉屬性檢測(cè)
人臉屬性,支持檢測(cè)年齡、性別以及3D角度。
人臉3D角度:俯仰角(pitch), 橫滾角(roll), 偏航角(yaw)。
5.活體檢測(cè)
離線活體檢測(cè),靜默式識(shí)別,在人臉識(shí)別過程中判斷操作用戶是否為真人,有效防御照片、視頻、紙張等不同類型的作弊攻擊,提高業(yè)務(wù)安全性,讓人臉識(shí)別更安全、更快捷,體驗(yàn)更佳。支持單目RGB活體檢測(cè)、雙目(IR/RGB)活體檢測(cè),可滿足各類人臉識(shí)別終端產(chǎn)品活體檢測(cè)應(yīng)用。
開造
訪問地址: https://ai.arcsoft.com.cn/technology/faceTracking.html 進(jìn)入開發(fā)者中心進(jìn)行注冊(cè)以及認(rèn)證個(gè)人信息
1. 點(diǎn)擊我的應(yīng)用 > 新建應(yīng)用
2.填寫信息立即創(chuàng)建 點(diǎn)擊 添加SDK
3.選中免費(fèi)版人臉識(shí)別
4. 填寫授權(quán)碼信息
選擇平臺(tái)先選擇windows的根據(jù)你的電腦配置來 是64位還是32位的, 語言選擇Java
5. 介紹sdk文件
一、創(chuàng)建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.創(chuàng)建lib文件夾將sdk復(fù)制
進(jìn)來記得add依賴有小箭頭就行
3.復(fù)制到測(cè)試類當(dāng)中
4.填寫好對(duì)應(yīng)的appId和sdkKey
5.復(fù)制算法庫(kù)路徑
6.啟動(dòng)測(cè)試
我進(jìn)行刪除了一些功能就示范特征、活體檢測(cè), 其他的可自己試一試
體驗(yàn)到此結(jié)束,可以自己多玩玩
二、改造ArcFace工程
效果圖
1. 創(chuàng)建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: 人臉識(shí)別簡(jiǎn)易版$實(shí)現(xiàn)人臉檢測(cè)活體是否為人臉,追蹤人臉
**/
public class FaceRecognitionUtils {
/**
* APP ID,請(qǐng)先在虹軟開發(fā)者中心注冊(cè)、認(rèn)證之后創(chuàng)建應(yīng)用獲取
*/
@Value("crm.appId")
private static String APP_ID = "";
/**
* SDK KEY,請(qǐng)先在虹軟開發(fā)者中心注冊(cè)、認(rèn)證之后創(chuàng)建應(yīng)用獲取
*/
@Value("crm.sdk")
private static String SDK_KEY = "";
/**
* dll插件庫(kù)地址
* 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);
// 創(chuàng)建引擎功能對(duì)象(用于初始化引擎)
private static FunctionConfiguration functionConfiguration1 = new FunctionConfiguration();
// 創(chuàng)建引擎功能對(duì)象(用于人臉檢測(cè))
private static FunctionConfiguration functionConfiguration2 = new FunctionConfiguration();
static {
// 初始化引擎功能(用于初始化引擎)
{
// 是否支持年齡檢測(cè)功能
functionConfiguration1.setSupportAge(true);
// 是否支持3D檢測(cè)功能
functionConfiguration1.setSupportFace3dAngle(true);
// 是否支持人臉檢測(cè)功能
functionConfiguration1.setSupportFaceDetect(true);
// 是否支持人臉識(shí)別功能
functionConfiguration1.setSupportFaceRecognition(true);
// 是否支持性別檢測(cè)功能
functionConfiguration1.setSupportGender(true);
// 是否支持RGB活體檢測(cè)功能
functionConfiguration1.setSupportLiveness(true);
// 是否支持IR活體檢測(cè)功能
functionConfiguration1.setSupportIRLiveness(true);
}
// 初始化引擎功能(用于人臉檢測(cè))
{
// 是否支持年齡檢測(cè)功能
functionConfiguration2.setSupportAge(true);
// 是否支持3D檢測(cè)功能
functionConfiguration2.setSupportFace3dAngle(true);
// 是否支持性別檢測(cè)功能
functionConfiguration2.setSupportGender(true);
// 是否支持RGB活體檢測(cè)功能
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失敗!錯(cuò)誤碼:" + errorCode);
} else {
// SDK激活成功
logger.info("在線激活SDK成功!");
}
}
/**
* 初始化引擎
*
* @param detectMode 檢測(cè)模式(推薦IMAGE 模式)
* @param detectOrient 人臉檢測(cè)角度(推薦人臉檢測(cè)角度,逆時(shí)針0度)
* @param faceMaxNum 人臉檢測(cè)最大數(shù)量(推薦10)
* @param faceScaleVal 最小人臉比例(VIDEO模式推薦16;IMAGE模式推薦32)
*/
public static void InitializeTheEngine(DetectMode detectMode, DetectOrient detectOrient, int faceMaxNum, int faceScaleVal) {
// 創(chuàng)建引擎配置類
EngineConfiguration engineConfiguration = new EngineConfiguration();
// 設(shè)置detectMode參數(shù)
engineConfiguration.setDetectMode(detectMode);
// 設(shè)置detectFaceOrientPriority參數(shù)
engineConfiguration.setDetectFaceOrientPriority(detectOrient);
// 設(shè)置人臉檢測(cè)最大數(shù)量
engineConfiguration.setDetectFaceMaxNum(faceMaxNum);
// 設(shè)置detectFaceScaleVal參數(shù)為:識(shí)別的最小人臉比例 = 圖片長(zhǎng)邊 / 人臉框長(zhǎng)邊的比值
engineConfiguration.setDetectFaceScaleVal(faceScaleVal);
// 配置引擎功能
engineConfiguration.setFunctionConfiguration(functionConfiguration1);
// 檢測(cè)角度
// engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
// 初始化引擎
int errorCode = faceEngine.init(engineConfiguration);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 引擎初始化失敗
logger.error("引擎初始化失敗!錯(cuò)誤碼:" + errorCode);
} else {
// 引擎初始化成功
logger.info("引擎初始化成功!");
}
}
/**
* 人臉檢測(cè)(傳入分離的圖像信息數(shù)據(jù))
*
* @param imageInfo 圖像信息
* @param faceInfoList 人臉信息列表
* @return 檢測(cè)結(jié)果,檢測(cè)成功或是失敗!
*/
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()) {
// 人臉檢測(cè)失敗
logger.error("人臉檢測(cè)失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 人臉檢測(cè)成功
logger.info("人臉檢測(cè)成功!");
return true;
}
}
/**
* 人臉檢測(cè)(傳入ImageInfoEx圖像信息數(shù)據(jù))
*
* @param imageInfo 圖像信息
* @param faceInfoList 人臉信息列表
* @return 檢測(cè)結(jié)果,檢測(cè)成功或是失敗!
*/
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()) {
// 人臉檢測(cè)失敗
logger.error("人臉檢測(cè)失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 人臉檢測(cè)成功
logger.info("人臉檢測(cè)成功!");
return true;
}
}
/**
* 人臉特征提取,是否檢測(cè)到人臉
*
* @param imageInfo
* @return
*/
public static byte[] extractFaceFeature(ImageInfo imageInfo) {
try {
//人臉檢測(cè)得到人臉列表
List<faceinfo> faceInfoList = new ArrayList<faceinfo>();
//人臉檢測(cè)
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;
}
/**
* 人臉特征提取(傳入分離的圖像信息數(shù)據(jù))
*
* @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("人臉特征提取失敗!錯(cuò)誤碼:" + errorCode);
return null;
} else {
// 人臉特征提取成功
logger.info("人臉特征提取成功!");
return faceFeature;
}
}
/**
* 人臉特征提取(傳入ImageInfoEx圖像信息數(shù)據(jù))
*
* @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});
// 創(chuàng)建人臉特征對(duì)象
FaceFeature faceFeature = new FaceFeature();
int errorCode = faceEngine.extractFaceFeature(imageInfoEx, faceInfo, faceFeature);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 人臉特征提取失敗
logger.error("人臉特征提取失敗!錯(cuò)誤碼:" + errorCode);
return null;
} else {
// 人臉特征提取成功
logger.info("人臉特征提取成功!");
return faceFeature;
}
}
/**
* 人臉特征比對(duì)
*
* @param targetFaceFeature 目標(biāo)人臉特征
* @param sourceFaceFeature 來源人臉特征
* @param compareModel 比對(duì)模型
* @return 比對(duì)相似度
*/
public static Float faceFeatureComparison(FaceFeature targetFaceFeature, FaceFeature sourceFaceFeature, CompareModel compareModel) {
// 創(chuàng)建比對(duì)相似度對(duì)象
FaceSimilar faceSimilar = new FaceSimilar();
int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, compareModel, faceSimilar);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 人臉特征比對(duì)失敗
logger.error("人臉特征比對(duì)失敗!錯(cuò)誤碼:" + errorCode);
return null;
} else {
// 人臉特征比對(duì)成功
logger.info("人臉特征比對(duì)成功!");
return faceSimilar.getScore();
}
}
/**
* 人臉特征比對(duì)(默認(rèn)LIFE_PHOTO比對(duì)模型)
*
* @param targetFaceFeature 目標(biāo)人臉特征
* @param sourceFaceFeature 來源人臉特征
* @return 比對(duì)相似度
*/
public static Float faceFeatureComparison(FaceFeature targetFaceFeature, FaceFeature sourceFaceFeature) {
// 創(chuàng)建比對(duì)相似度對(duì)象
FaceSimilar faceSimilar = new FaceSimilar();
int errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature,
faceSimilar);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 人臉特征比對(duì)失敗
logger.error("人臉特征比對(duì)失敗!錯(cuò)誤碼:" + errorCode);
return null;
} else {
// 人臉特征比對(duì)成功
logger.info("人臉特征比對(duì)成功!");
return faceSimilar.getScore();
}
}
/**
* 人臉屬性檢測(cè)(傳入分離的圖像信息數(shù)據(jù))
*
* @param imageInfo 圖像信息
* @param faceInfoList 人臉信息列表
* @return 檢測(cè)結(jié)果,檢測(cè)成功或是失敗!
*/
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()) {
// 人臉屬性檢測(cè)失敗
logger.error("人臉屬性檢測(cè)失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 人臉屬性檢測(cè)成功
logger.info("人臉屬性檢測(cè)成功!");
return true;
}
}
/**
* 人臉屬性檢測(cè)(傳入ImageInfoEx圖像信息數(shù)據(jù))
*
* @param imageInfo 圖像信息
* @param faceInfoList 人臉信息列表
* @return 檢測(cè)結(jié)果,檢測(cè)成功或是失敗!
*/
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()) {
// 人臉屬性檢測(cè)失敗
logger.error("人臉屬性檢測(cè)失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 人臉屬性檢測(cè)成功
logger.info("人臉屬性檢測(cè)成功!");
return true;
}
}
/**
* 獲取年齡信息
* 注意:人臉屬性檢測(cè)之后方可調(diào)用
*
* @return 獲取結(jié)果,獲取失敗或是成功!
*/
public static boolean getAgeInfo(List<ageinfo> ageInfoList) {
int errorCode = faceEngine.getAge(ageInfoList);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 獲取年齡信息失敗
logger.error("獲取年齡信息失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 已成功獲取年齡信息
logger.info("已成功獲取年齡信息!");
return true;
}
}
/**
* 獲取性別(0為男性,1為女性。)
* 注意:人臉屬性檢測(cè)之后方可調(diào)用
*
* @return 獲取結(jié)果,獲取失敗或是成功!
*/
public static boolean getGender(List<genderinfo> genderInfoList) {
// 性別檢測(cè)
int errorCode = faceEngine.getGender(genderInfoList);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 獲取性別失敗
logger.error("獲取性別失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 已成功獲取年齡信息
logger.info("已成功獲取性別!");
return true;
}
}
/**
* 獲取人臉三維角度信息
* 人臉3D角度:俯仰角(pitch), 橫滾角(roll), 偏航角(yaw)。
* 注意:人臉屬性檢測(cè)之后方可調(diào)用
*
* @return 獲取結(jié)果,獲取失敗或是成功!
*/
public static boolean getFace3DAngle(List<face3dangle> face3DAngleList) {
// 人臉三維角度檢測(cè)
int errorCode = faceEngine.getFace3DAngle(face3DAngleList);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 獲取人臉三維角度信息失敗
logger.error("獲取人臉三維角度信息失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// 已成功獲取人臉三維角度信息
logger.info("已成功獲取人臉三維角度信息!");
return true;
}
}
/**
* 獲取RGB活體信息
* RGB活體值,未知=-1 、非活體=0 、活體=1、超出人臉=-2
* 注意:人臉屬性檢測(cè)之后方可調(diào)用
*
* @return 獲取結(jié)果,獲取失敗或是成功!
*/
public static boolean getLiveness(List<livenessinfo> livenessInfoList) {
// RGB活體檢測(cè)
int errorCode = faceEngine.getLiveness(livenessInfoList);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 獲取RGB活體信息失敗
logger.error("獲取RGB活體信息失敗!錯(cuò)誤碼:" + 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活體檢測(cè)(傳入分離的圖像信息數(shù)據(jù))
* 注意:
* 引擎需要支持IR活體檢測(cè)功能
*
* @return 檢測(cè)結(jié)果,檢測(cè)成功或是失敗!
*/
public static boolean detectionLiveness_IR1(String string) throws IOException {
// 創(chuàng)建圖像信息
// ImageInfo imageInfoGray = getGrayData(file);
byte[] decode = Base64.decode(base64Process(string));
BufferedImage bufImage = ImageIO.read(new ByteArrayInputStream(decode));
ImageInfo imageInfoGray = ImageFactory.bufferedImage2ImageInfo(bufImage);
// 創(chuàng)建人臉信息列表
List<faceinfo> faceInfoListGray = new ArrayList<faceinfo>();
// 人臉檢測(cè)(傳入分離的圖像信息數(shù)據(jù))
int errorCode1 = faceEngine.detectFaces(imageInfoGray.getImageData(),
imageInfoGray.getWidth(), imageInfoGray.getHeight(),
imageInfoGray.getImageFormat(), faceInfoListGray);
// 創(chuàng)建引擎功能實(shí)例對(duì)象
FunctionConfiguration configuration = new FunctionConfiguration();
// 設(shè)置引擎支持IR活體檢測(cè)
configuration.setSupportIRLiveness(true);
// IR活體檢測(cè)
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活體檢測(cè)失敗
logger.error("IR活體檢測(cè)失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// IR活體檢測(cè)成功
logger.info("IR活體檢測(cè)成功!");
return true;
}
}
/**
* IR活體檢測(cè)(傳入ImageInfoEx圖像信息數(shù)據(jù))
* 注意:
* 引擎需要支持年齡檢測(cè)功能
*
* @param imageInfo 圖像信息
* @return 檢測(cè)結(jié)果,檢測(cè)成功或是失敗!
*/
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活體檢測(cè)失敗
logger.error("IR活體檢測(cè)失敗!錯(cuò)誤碼:" + errorCode);
return false;
} else {
// IR活體檢測(cè)成功
logger.info("IR活體檢測(cè)成功!");
return true;
}
}
/**
* 獲取IR活體信息
* IR活體值,未知=-1 、非活體=0 、活體=1、超出人臉=-2
*
* @return 獲取結(jié)果,獲取失敗或是成功!
*/
public static boolean getIrLiveness(List<irlivenessinfo> irLivenessInfo) {
// IR活體檢測(cè)
int errorCode = faceEngine.getLivenessIr(irLivenessInfo);
if (errorCode != ErrorInfo.MOK.getValue()) {
// 獲取IR活體信息失敗
logger.error("獲取IR活體信息失敗!錯(cuò)誤碼:" + 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引擎失敗!錯(cuò)誤碼:" + errorCode);
} else {
// 銷毀SDK引擎成功
logger.info("銷毀SDK引擎成功!");
}
}
}
2.創(chuàng)建ErrorCodeEnum
package top.yangbuyi.constant;
/**
* 錯(cuò)誤代碼枚舉
*
* @author yang buyi
* @date 2021/07/02
*/
public enum ErrorCodeEnum {
MOK(0, "成功"),
UNKNOWN(1, "未知錯(cuò)誤"),
INVALID_PARAM(2, "無效參數(shù)"),
UNSUPPORTED(3, "引擎不支持"),
NO_MEMORY(4, "內(nèi)存不足"),
BAD_STATE(5, "狀態(tài)錯(cuò)誤"),
USER_CANCEL(6, "用戶取消相關(guān)操作"),
EXPIRED(7, "操作時(shí)間過期"),
USER_PAUSE(8, "用戶暫停操作"),
BUFFER_OVERFLOW(9, "緩沖上溢"),
BUFFER_UNDERFLOW(10, "緩沖下溢"),
NO_DISKSPACE(11, "存貯空間不足"),
COMPONENT_NOT_EXIST(12, "組件不存在"),
GLOBAL_DATA_NOT_EXIST(13, "全局?jǐn)?shù)據(jù)不存在"),
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, "系統(tǒng)版本不被當(dāng)前SDK所支持"),
LICENCE_EXPIRED(28678, "SDK有效期過期,需要重新下載更新"),
APS_ENGINE_HANDLE(69633, "引擎句柄非法"),
APS_MEMMGR_HANDLE(69634, "內(nèi)存句柄非法"),
APS_DEVICEID_INVALID(69635, " Device ID 非法"),
APS_DEVICEID_UNSUPPORTED(69636, "Device ID 不支持"),
APS_MODEL_HANDLE(69637, "模板數(shù)據(jù)指針非法"),
APS_MODEL_SIZE(69638, "模板數(shù)據(jù)長(zhǎng)度非法"),
APS_IMAGE_HANDLE(69639, "圖像結(jié)構(gòu)體指針非法"),
APS_IMAGE_FORMAT_UNSUPPORTED(69640, "圖像格式不支持"),
APS_IMAGE_PARAM(69641, "圖像參數(shù)非法"),
APS_IMAGE_SIZE(69642, "圖像尺寸大小超過支持范圍"),
APS_DEVICE_AVX2_UNSUPPORTED(69643, "處理器不支持AVX2指令"),
FR_INVALID_MEMORY_INFO(73729, "無效的輸入內(nèi)存"),
FR_INVALID_IMAGE_INFO(73730, "無效的輸入圖像參數(shù)"),
FR_INVALID_FACE_INFO(73731, "無效的臉部信息"),
FR_NO_GPU_AVAILABLE(73732, "當(dāng)前設(shè)備無GPU可用"),
FR_MISMATCHED_FEATURE_LEVEL(73733, "待比較的兩個(gè)人臉特征的版本不一致"),
FACEFEATURE_UNKNOWN(81921, "人臉特征檢測(cè)錯(cuò)誤未知"),
FACEFEATURE_MEMORY(81922, "人臉特征檢測(cè)內(nèi)存錯(cuò)誤"),
FACEFEATURE_INVALID_FORMAT(81923, "人臉特征檢測(cè)格式錯(cuò)誤"),
FACEFEATURE_INVALID_PARAM(81924, "人臉特征檢測(cè)參數(shù)錯(cuò)誤"),
FACEFEATURE_LOW_CONFIDENCE_LEVEL(81925, "人臉特征檢測(cè)結(jié)果置信度低"),
ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_INIT(86017, "Engine不支持的檢測(cè)屬性"),
ASF_EX_BASE_FEATURE_UNINITED(86018, "需要檢測(cè)的屬性未初始化"),
ASF_EX_BASE_FEATURE_UNPROCESSED(86019, "待獲取的屬性未在process中處理過"),
ASF_EX_BASE_FEATURE_UNSUPPORTED_ON_PROCESS(86020, "PROCESS不支持的檢測(cè)屬性,例如FR,有自己獨(dú)立的處理函數(shù)"),
ASF_EX_BASE_INVALID_IMAGE_INFO(86021, "無效的輸入圖像"),
ASF_EX_BASE_INVALID_FACE_INFO(86022, "無效的臉部信息"),
ASF_BASE_ACTIVATION_FAIL(90113, "人臉比對(duì)SDK激活失敗,請(qǐng)打開讀寫權(quán)限"),
ASF_BASE_ALREADY_ACTIVATED(90114, "人臉比對(duì)SDK已激活"),
ASF_BASE_NOT_ACTIVATED(90115, "人臉比對(duì)SDK未激活"),
ASF_BASE_SCALE_NOT_SUPPORT(90116, "detectFaceScaleVal 不支持"),
ASF_BASE_VERION_MISMATCH(90117, "SDK版本不匹配"),
ASF_BASE_DEVICE_MISMATCH(90118, "設(shè)備不匹配"),
ASF_BASE_UNIQUE_IDENTIFIER_MISMATCH(90119, "唯一標(biāo)識(shí)不匹配"),
ASF_BASE_PARAM_NULL(90120, "參數(shù)為空"),
ASF_BASE_SDK_EXPIRED(90121, "SDK已過期"),
ASF_BASE_VERSION_NOT_SUPPORT(90122, "版本不支持"),
ASF_BASE_SIGN_ERROR(90123, "簽名錯(cuò)誤"),
ASF_BASE_DATABASE_ERROR(90124, "數(shù)據(jù)庫(kù)插入錯(cuò)誤"),
ASF_BASE_UNIQUE_CHECKOUT_FAIL(90125, "唯一標(biāo)識(shí)符校驗(yàn)失敗"),
ASF_BASE_COLOR_SPACE_NOT_SUPPORT(90126, "輸入的顏色空間不支持"),
ASF_BASE_IMAGE_WIDTH_NOT_SUPPORT(90127, "輸入圖像的byte數(shù)據(jù)長(zhǎng)度不正確"),
ASF_NETWORK_BASE_COULDNT_RESOLVE_HOST(94209, "無法解析主機(jī)地址"),
ASF_NETWORK_BASE_COULDNT_CONNECT_SERVER(94210, "無法連接服務(wù)器"),
ASF_NETWORK_BASE_CONNECT_TIMEOUT(94211, "網(wǎng)絡(luò)連接超時(shí)"),
ASF_NETWORK_BASE_UNKNOWN_ERROR(94212, "未知錯(cuò)誤");
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.創(chuàng)建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: 人臉活體檢測(cè)$
**/
@RestController
@Slf4j
@RequestMapping("arcFace")
public class ArcFaceController {
/**
* 人臉識(shí)別檢測(cè)
*
* @param url base64 地址
* @param oId 組織架構(gòu) ID
* @param uid 當(dāng)前登錄檢測(cè)的用戶ID
* @return
*/
@RequestMapping(value = "arcFaceSearch", method = RequestMethod.POST)
public Map arcFaceSearch(@RequestParam String url, @RequestParam Integer oId, @RequestParam Integer uid) {
// 前端展示原圖
String urlTemp = url;
// ...業(yè)務(wù)
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);
}
// 開始轉(zhuǎn)碼
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);
// 校驗(yàn)是否顯示出人臉
if (bytes == null) {
System.out.println(ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
stringObjectHashMap.put("msg", ErrorCodeEnum.NO_FACE_DETECTED.getDescription());
return stringObjectHashMap;
}
// 創(chuàng)建圖像中的人臉信息對(duì)象列表
List<faceinfo> faceInfoList1 = new ArrayList<>();
// 檢測(cè)圖像中人臉
FaceRecognitionUtils.faceDetection1(imageInfo, faceInfoList1);
// 檢測(cè)圖像中人臉屬性
FaceRecognitionUtils.faceAttributeDetection1(imageInfo, faceInfoList1);
// 檢測(cè)人臉特征
/* 圖像中的人臉年齡 */
{
// 創(chuàng)建圖像中的人臉年齡列表
List<ageinfo> ageInfoList1 = new ArrayList<>();
// 檢測(cè)圖像中的人臉年齡列表
FaceRecognitionUtils.getAgeInfo(ageInfoList1);
// 將圖像中的年齡列表打印到控制臺(tái)
if (ageInfoList1.size() > 0) {
stringObjectHashMap.put("age", ageInfoList1.get(0).getAge());
}
}
/* 圖像中的人臉性別 */
// 創(chuàng)建圖像中的人臉性別列表
List<genderinfo> genderInfoList1 = new ArrayList<>();
// 檢測(cè)圖像中的人臉性別列表
FaceRecognitionUtils.getGender(genderInfoList1);
// 將圖像中的性別列表打印到控制臺(tái)
if (genderInfoList1.size() > 0) {
stringObjectHashMap.put("gender", genderInfoList1.get(0).getGender() == 0 ? "男" : "女");
}
/* 圖像1中的人臉三維角度 */
// 創(chuàng)建圖像中的人臉三維角度信息列表
List<face3dangle> face3DAngleList1 = new ArrayList<>();
// 獲取圖像1中的人臉三維角度信息列表
FaceRecognitionUtils.getFace3DAngle(face3DAngleList1);
// 將圖像中的人臉三維角度信息列表打印到控制臺(tái)
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活體值 */
// 創(chuàng)建圖像中的RGB活體信息列表
List<livenessinfo> livenessInfoList1 = new ArrayList<>();
// 獲取圖像1中的RGB活體信息列表
FaceRecognitionUtils.getLiveness(livenessInfoList1);
// 將圖像中的RGB活體信息列表打印到控制臺(tái)
if (livenessInfoList1.size() > 0) {
stringObjectHashMap.put("RgbLiveness", livenessInfoList1.get(0).getLiveness());
}
/**
* 注意: 活體只能支持一個(gè)人臉否則返回未知
* 所以我們可以進(jìn)行使用他來判斷是否有多個(gè)人檢測(cè) 直接判定失敗
*/
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. 創(chuàng)建路由跳轉(zhuǎn)前端頁面 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() {
// ...業(yè)務(wù)
return "index";
}
}
三. 前端人臉追蹤插件
訪問地址: https://trackingjs.com/
里面有demo可觀看我就不帶大家查看了
1. 創(chuàng)建前端index.html
js請(qǐng)下載demo獲取,連接在最下面
<meta charset="UTF-8">
<title>人臉檢測(cè)</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 {
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="人臉識(shí)別" value="人臉識(shí)別" onclick="getMedia2()">
攝像頭識(shí)別
</button>
</td>
</tr>
<tr>
<td colspan="2">
<button id="snap" onclick="chooseFileChangeComp()">
提交
</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: { 500, height: 500},
audio: true
};
//獲得video攝像頭區(qū)域
let video = document.getElementById("video2");
// 這里介紹新的方法,返回一個(gè) Promise對(duì)象
// 這個(gè)Promise對(duì)象返回成功后的回調(diào)函數(shù)帶一個(gè) MediaStream 對(duì)象作為其參數(shù)
// then()是Promise對(duì)象里的方法
// then()方法是異步執(zhí)行,當(dāng)then()前的方法執(zhí)行完后再執(zhí)行then()內(nèi)部的程序
// 避免數(shù)據(jù)沒有獲取到
let promise = navigator.mediaDevices.getUserMedia(constraints);
promise.then(function (MediaStream) {
video.srcObject = MediaStream;
video.play();
});
/**
* 模擬手機(jī)端 三秒主動(dòng)提交檢測(cè)
* @type {number}
*/
t1 = window.setInterval(function () {
chooseFileChangeComp()
}, 3000)
}
/**
* 提交檢測(cè) 請(qǐng)求接口
*/
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();
}
});
});
// 這里如果報(bào)錯(cuò) 不用管
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. 啟動(dòng)工程 訪問 http://localhost:7000/
四. 人臉識(shí)別追蹤就到這里啦,具體的代碼已經(jīng)提交到gitee請(qǐng)前往獲取Java項(xiàng)目 ArcFace
點(diǎn)擊前往獲取demo
你的壓力來源于無法自律,只是假裝努力,現(xiàn)狀跟不上內(nèi)心欲望,所以你焦慮又恐慌。——楊不易
總結(jié)
以上是生活随笔為你收集整理的从零玩转人脸识别之RGB人脸活体检测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《像计算机科学家一样思考Java》——
- 下一篇: word怎么利用MathType实现公式