前言
上一篇SpringBoot集成百度人脸demo中我使用的是调用本机摄像头完成人脸注册,本次demo根据业务需求的不同我采用文件上传的方式实现人脸注册。
效果演示
注册
后端响应数据:
登录
后端响应数据:
项目结构
后端代码实现
1、BaiduAiUtils工具类封装
package com.jzj.utils;
import com.baidu.aip.face.AipFace;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
/**
* 百度AI工具类封装
*
* @author 黎明
* @version 1.0
* @date 2023/8/5 9:35
*/
@Component
@Slf4j
public class BaiduAiUtils {
/*
注入百度个人用户相关配置
*/
@Value("${baidu.face.appId}")
private String APP_ID;
@Value("${baidu.face.apiKey}")
private String API_KEY;
@Value("${baidu.face.secretKey}")
private String SECRET_KEY;
@Value("${baidu.face.imageType}")
private String IMAGE_TYPE;
@Value("${baidu.face.groupId}")
private String groupId;
// 声明私有变量client,AipFace是百度人脸识别 API 的 Java 客户端类,用于与人脸识别服务进行通信。
private AipFace client;
// 用于存储一些参数配置(图片质量控制、活体检测控制)
private HashMap<String, String> map = new HashMap<>();
/*
私有的构造函数,表明该类是一个单例类,只能通过静态方法获取实例
*/
private BaiduAiUtils() {
// 图片质量控制 NONE: 不进行控制 LOW:较低的质量要求 NORMAL: 一般的质量要求 HIGH: 较高的质量要求 默认 NONE
map.put("quality_control", "NORMAL");
// 活体检测控制 NONE: 不进行控制 LOW:较低的活体要求(高通过率 低拒绝率) NORMAL: 一般的活体要求(平衡的拒绝率, 通过率) HIGH: 较高的活体要求(高拒绝率 低通过率) 默认NONE
map.put("liveness_control", "LOW");
}
/*
用于在类初始化时执行client的初始化操作
*/
@PostConstruct
public void init() {
client = new AipFace(APP_ID, API_KEY, SECRET_KEY);
}
/**
* 人脸注册:用户照片存入人脸库中
*/
public Boolean faceRegister(String userId, String image) {
JSONObject res = client.addUser(image, IMAGE_TYPE, groupId, userId, map);
log.info("人脸注册响应数据 :{}", res);
Integer errorCode = res.getInt("error_code");
return errorCode == 0 ? true : false;
}
/**
* 人脸更新:更新人脸库中的用户照片
*/
public Boolean faceUpdate(String userId, String image) {
JSONObject res = client.updateUser(image, IMAGE_TYPE, groupId, userId, map);
log.info("人脸更新响应数据 :{}", res);
Integer errorCode = res.getInt("error_code");
return errorCode == 0 ? true : false;
}
/**
* 人脸检测:判断上传的图片中是否具有面部信息
*/
public Boolean faceCheck(String image) {
JSONObject res = client.detect(image, IMAGE_TYPE, map);
log.info("人脸检测响应数据 :{}", res);
if (res.has("error_code") && res.getInt("error_code") == 0) {
JSONObject resultObject = res.getJSONObject("result");
Integer faceNum = resultObject.getInt("face_num");
return faceNum == 1 ? true : false;
} else {
return false;
}
}
/**
* 1.搜索人脸库中相似的人脸并返回数据
* <p>
* 2.判断人脸匹配得分(score)大于80分则认为是同一个人
*/
public String faceSearch(String image) {
JSONObject res = client.search(image, IMAGE_TYPE, groupId, map);
log.info("人脸搜索响应数据 :{}", res);
if (res.has("error_code") && res.getInt("error_code") == 0) {
JSONObject result = res.getJSONObject("result");
JSONArray userList = result.getJSONArray("user_list");
if (userList.length() > 0) {
JSONObject user = userList.getJSONObject(0);
double score = user.getDouble("score");
if (score > 80) {
return user.getString("user_id");
}
}
}
return null;
}
}
2、FaceServiceImpl
package com.jzj.service.impl;
import com.jzj.service.FaceService;
import com.jzj.utils.BaiduAiUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* FaceService实现类
*
* @author 黎明
* @version 1.0
* @date 2023/8/5 9:53
*/
@Service
@Slf4j
public class FaceServiceImpl implements FaceService {
// 注入BaiduAiUtils
@Autowired
private BaiduAiUtils baiduAiUtils;
/**
* 人脸登录
*
* @param imagebast64 图片base64编码
* @return userId
*/
@Override
public String loginByFace(StringBuffer imagebast64) {
// 处理base64编码内容
String image = imagebast64.substring(imagebast64.indexOf(",") + 1, imagebast64.length());
return baiduAiUtils.faceSearch(image);
}
/**
* 人脸注册
*
* @param userId 用户Id
* @param imagebast64 图片base64编码
* @return ”“
*/
@Override
public Boolean registerFace(String userId, StringBuffer imagebast64) {
// 处理base64编码内容
String image = imagebast64.substring(imagebast64.indexOf(",") + 1, imagebast64.length());
log.info("处理后的图片base64编码:{}",image);
return baiduAiUtils.faceRegister(userId, image);
}
}
3、FaceController
package com.jzj.controller;
import com.alibaba.fastjson.JSON;
import com.jzj.common.Result;
import com.jzj.service.FaceService;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.UUID;
/**
* controller控制层
*
* @author 黎明
* @version 1.0
* @date 2023/8/5 10:01
*/
@RestController
@RequestMapping("/face")
@Slf4j
public class FaceController {
// 注入FaceService
@Autowired
private FaceService faceService;
/**
* 人脸登录
* @param request 图片base64编码
* @return userId
*/
@RequestMapping("/login")
public Result searchface(@RequestBody String request) {
StringBuffer image = new StringBuffer(request);
String userId = faceService.loginByFace(image);
/*
判断人脸库中是否有改用户
*/
if (StringUtils.hasText(userId)){
// !null且不为空,返回用户ID,和状态码:0
return Result.ok(userId);
}
return Result.err(userId);
}
/**
* 人脸注册
* @param request 图片base64编码
* @return res
*/
@PostMapping("/register")
public Result registerFace(@RequestBody String request) {
StringBuffer image = new StringBuffer(request);
String userId = UUID.randomUUID().toString().substring(0, 4);
Boolean registerFace = faceService.registerFace(userId, image);
/*
判断是否注册成功
*/
if (registerFace){
// 注册成功,返回用户ID、状态码:0
return Result.ok(userId);
}
// 注册失败,返回用户ID、状态码:1
return Result.err(userId);
}
}
前端代码实现
1、register
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
<script type="application/javascript" src="js/axios_0.18.0_axios.min.js"></script>
</head>
<body>
<h1>Register</h1>
<input type="file" id="fileInput">
<button id="registerBtn">注册</button>
<script>
// 获取注册按钮元素,并为其添加点击事件监听器
document.getElementById("registerBtn").addEventListener("click", function () {
// 获取文件选择框元素和选择的文件
var fileInput = document.getElementById("fileInput");
var file = fileInput.files[0];
// 验证图片格式
if (file) {
var allowedFormats = ['image/png', 'image/jpeg', 'image/jpg', 'image/bmp'];
if (allowedFormats.includes(file.type)) {
// 创建一个 FileReader 对象,用于读取文件的内容
var reader = new FileReader();
// 文件读取完成后的处理逻辑
reader.onloadend = function () {
// 从读取的结果中提取图像数据的 Base64 编码部分,使用正则表达式去除前缀部分
const imageBase64 = reader.result
// 发送POST请求
axios.post('/face/register', {
imagebast64: imageBase64
}).then(function (response) {
// 请求成功处理逻辑
console.log("响应数据:", response.data);
console.log("用户userId:", response.data.userId);
console.log("用户注册状态码:", response.data.code);
if (response.data.code === 0) {
alert("注册成功! UserId: " + response.data.userId);
// 跳转到index页面
window.location.href = "https://www.baidu.com";
} else {
alert("注册失败!");
}
}).catch(function (error) {
// 请求失败处理逻辑
console.error("错误注册信息: ", error);
});
};
// 读取文件内容,并将其保存在 reader.result 中
reader.readAsDataURL(file);
} else {
// 文件格式不符合要求,显示错误提示
alert("只能上传PNG、JPG、JPEG和BMP格式的图片!");
}
}
});
</script>
</body>
</html>
2、login
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>人脸识别登录</title>
<script type="application/javascript" src="js/axios_0.18.0_axios.min.js"></script>
</head>
<body>
<h1>Login</h1>
<video id="video" width="320" height="240" autoplay></video>
<button id="loginBtn">登录</button>
<script>
// 获取视频流
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(function(stream) {
var video = document.getElementById("video");
video.srcObject = stream;
})
.catch(function(error) {
console.error("访问视频流出错: ", error);
});
// 登录事件监听
document.getElementById("loginBtn").addEventListener("click", function() {
var video = document.getElementById("video");
var canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
var context = canvas.getContext('2d');
context.drawImage(video, 0, 0, canvas.width, canvas.height);
var imageBase64 = canvas.toDataURL("image/jpeg");
axios.post("/face/login", {
imagebast64: imageBase64
}).then(function(response) {
var res = response.data;
if (res.code === 0) {
alert("登录成功! UserId: " + res.userId);
// 跳转到index页面
window.location.href = "welcome.html";
} else {
alert("登录失败!");
}
})
.catch(function(error) {
console.error("登录错误: ", error);
});
});
</script>
</body>
</html>
详细代码已放到gitee仓库需要的源码请自取,链接https://gitee.com/bitliming/baidu_face.git