上一篇文章只是封装了人脸检测的一些工具类,要实现刷脸登录,我们首先得思考一个问题,就是如何将我们的人脸和登录账户信息进行绑定,让它通过人脸就能识别到当前登录的账户是谁的账户。
这个问题我们可以这样解决,我浏览Face++的官网发现它还有人脸比对的一个API,我点进去一看,这不就是实现人脸登录的关键API吗?
继续查看它需要的参数。
我们可以看到它需要的是两个图片的face_token,用两个图片的face_token,进行对比,得出它是不是同一个人。那就简单多了。许多问题都迎刃而解。接下来就是我具体的实现过程,大家可以参考一下。
1.准备工作
首先,在我们封装的工具类中,添加人脸比对的方法:
/**
* 对比两个人脸的face_token
* @param face_token1
* @param face_token2
* @return 如果为true则是同一个人
* @throws Exception
*/
public static boolean compareFace(String face_token1, String face_token2) throws Exception {
String url = "https://api-cn.faceplusplus.com/facepp/v3/compare";
HashMap<String, byte[]> byteMap = new HashMap<>();
map.put("face_token1", face_token1);
map.put("face_token2", face_token2);
byte[] bacd = HttpUtils.post(url, map, null);
String str = new String(bacd);
System.out.println("str = " + str);
//转为为对象,获取检索的置信度
if (str.indexOf("error_message") != -1) {
return false;
}
JSONObject jsonObject = JSONObject.parseObject(str);
JSONObject thresholds = (JSONObject) jsonObject.get("thresholds");
//获取到十万份之一的阈值
Double le5 = thresholds.getDoubleValue("1e-5");
//获取置信率
Double confidence = jsonObject.getDoubleValue("confidence");
if (confidence > le5) {
//说明存在这个人脸,比对成功
return true;
}
return false;
}
其次,在登录账户的数据库中,添加两个字段,用于保存图片的face_token和图片,由于我配置了一个FastDFS的文件服务器,我的图片都是保存在文件服务器上面,所有这里我就用varchar类型的数据保存我的文件地址即可,我还配置了Nginx服务器,所以可以直接通过保存图片的地址直接预览图片,各位可以根据需求设置自己的字段。
注:如果大家没有配置文件服务器,可以增加blob类型的字段,用于保存图片的二进制数据。
数据中增加了两个字段,我们映射数据库的实体类也得增加相应的属性。注意属性的命名要和数据库中的字段对应。
2.正文开始
完成准备工作之后,开始编写后端的接口。
注:我们在登录时,前端返回的数据是图片的64位编码。(这里有一个坑,也不算坑,就是比较麻烦,因为我们封装的人脸检测的方法,它的参数是File类型,我们需要将图片保存到本地,然后在将图片作为参数去请求接口,后面我发现它的参数除了File类型的文件,还有直接就是Base64编码的图片。如图)
所以为了方便,我们在去多封装一个参数是base64编码的图片的人脸检测的方法:
/**
* 通过图片的Base64为编码获取图片的face_token
* @param base64img
* @return
* @throws Exception
*/
public static String getFaceTokenBase64Img(String base64img) throws Exception {
//定义要请求接口的地址
String url = "https://api-cn.faceplusplus.com/facepp/v3/detect";
//封装参数
map.put("image_base64",base64img);
//请求Face++人脸检测的图片,拿到Face++接口返回的数据
byte[] bacd = HttpUtils.post(url, map, null);
//拿到接口返回的Json数据,提取出Face_Token
String str = new String(bacd);
System.out.println(str);
//判断接口返回的数据是否出错
if (str.indexOf("error_message") != -1) {
System.out.println("请求发送错误!");
return null;
}
//转换为Json对象
JSONObject jsonObject = JSONObject.parseObject(str);
Integer face_num = jsonObject.getInteger("face_num");
if (face_num == 1) {
//存在人脸,取出face_token
JSONArray facesArray = (JSONArray) jsonObject.get("faces");
JSONObject faceJson = (JSONObject) facesArray.get(0);
String face_token = faceJson.getString("face_token");
return face_token;
}
return null;
}
2.1 后端人脸注册的接口
我们需要通过人脸就能知道这个人脸属于那个账户,通过人脸识别就能自动登录账户,所以我们需要将人脸和账户绑定起来。
我们知道Face++中对于人脸检测的图片会返回一个face_token,我们只需要将前端传递过来的图片的base64编码,获取到它的face_token ,然后将他和前端返回过来的用户绑定在一起,实际上就是保存用户的face_token,也就是前面我们在登录表中新增face_token字段。
代码如下,代码中有相应的注释:
/**
* 注册人脸,将人脸和账户信息绑定在一起
* @param base64img
* @param userId
* @return
*/
@Override
public Result registerFace(String base64img, Integer userId) {
//根据id获取需要绑定的用户
User user = userMapper.selectById(userId);
//获取face_token
try {
String base = base64img.split(",")[1];
String faceToken = FaceUtils.getFaceTokenBase64img(base);
//保存face_token到用户中
user.setFaceToken(faceToken);
//获取base64编码数据
//将base64转化为byte数组
byte[] img = Base64.getDecoder().decode(base);
//设置用户的img
user.setImg(img);
//最后将其保存到数据库,即可完成人脸绑定
int updateById = userMapper.updateById(user);
//如果受影响的行数为1,则保存成功
if(updateById==1){
return Result.success("注册成功!");
}
return Result.error("注册失败");
} catch (Exception e) {
e.printStackTrace();
return Result.error("出错了!");
}
}
}
2.2 人脸注册前端代码编写:
前端使用Vue2.0+Element-UI进行编写,大家可以随意发挥,主要的功能就是点击注册人脸,能够打开摄像头,拍照传递给后端即可。
比如我们前端页面是这样的:
我们给他增加一个列来展示注册的人脸,在操作中增加一个采集人脸的按钮,当我们点击采集人脸,会打开摄像头进行拍照,同时将图片和用户id传递给后端。
修改后的效果如下:
2.3 人脸注册按钮事件编写:
当我们点击registerFace按钮,我们会打开摄像头,进行拍摄,然后将数据传递给后端。
主要代码如下:
startCamera() { //打开摄像头
const constraints = {
video: {
width: {exact: 400},
height: {exact: 400},
},
};
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
this.$refs.video.srcObject = stream;
this.stream = stream;
});
},
takePhoto() { //点击拍照
const canvas = document.createElement("canvas");
const video = this.$refs.video;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext("2d").drawImage(video, 0, 0, canvas.width, canvas.height);
this.photo = canvas.toDataURL("image/png");
},
beforeDestroy() { //关闭摄像头
if (this.stream) {
this.stream.getTracks().forEach((track) => {
track.stop();
});
}
},
uploadPhoto() {
//封装参数
const formData = new FormData();
formData.append('base64img', this.photo); //图片的base64编码
formData.append('userId', this.user.id); //用户id
// 向后端接口发送请求
this.request.post("/user/login/face/register",formData).then((data) => {
if (data) {
this.$message.success("人脸注册成功")
this.load() //初始化当前数据
this.beforeDestroy() //关闭摄像头
this.dialogVisible1 = false; //关闭dia标签
}
})
},
2.4 后端刷脸登陆的接口
完成人脸注册,将人脸信息和账户绑定之后,我们就可以进行刷脸登录了。
刷脸登录其实很简单,前端向后端传递拍摄到的图片64编码,后端收到图片的base64编码,然后调用人脸检测的接口,得到face_token,然后查询所有的登录用户,得到一个用户列表,遍历列表,拿到每一个用户的face_token,将它和前端传递过来的图片的face_token调用人脸比对的api进行比对,如果返回true,则说明当前登录的是这个用户。接下来就是生成token返回给前端。
下面是刷脸登录的后端接口:
@PostMapping("/login/face")
public Result loginFace(@RequestBody String base64img){
//获取所有的登录用户
List<User> list = userService.list();
boolean isFace=false;
String username=null;
try {
//获取前端传递的人脸的face_token
String base64 = base64img.split(",")[1];
String face_token = FaceUtils.getFaceTokenBase64img(base64);
System.out.println("face_token ====================== " + face_token);
//遍历所有的用户,对内一个用户的face_token进行比较
for(User user:list) {
String userFaceTokenfaceToken = user.getFaceToken();
try {
//调用人脸比对的接口
if(userFaceTokenfaceToken!=null) {
boolean isEqu = FaceUtils.compareFace(face_token, userFaceTokenfaceToken);
//说明找到和人脸匹配的张账户,
if (isEqu) {
isFace = true;
username = user.getUsername(); //提取当前登录的用户
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//如果人脸比对成功,说明前端传递过来的人脸,是绑定了账户的,说明人脸验证成功
//账户名称就是之前提取的username
//接下来在if中,我们进行登录成功设置,比如生成token返回给前端,设置全局对象等。
if(isFace){
//根据用户名称获取到用户
User user = userService.getOne(new QueryWrapper<User>().eq("username", username));
// 设置token,编写你自己的登录成功,也就是账号密码验证成功之后的逻辑。
//TODO
}
} catch (Exception e) {
e.printStackTrace();
}
return Result.error("当前人脸没有绑定账户!请注册人脸后重试!");
}
2.2 前端刷脸登录
在登录页面增加一个刷脸登录的按钮,点击这个按钮会打开摄像头,然后拍摄一张照片发送给后端登录接口。
关键代码如下(其实和人脸注册差不多):
文章来源:https://www.toymoban.com/news/detail-762169.html
loginFace(){
this.dialogVisible=true
this.startCamera()
//等待一秒钟进行拍照
setTimeout(() => {
this.takePhoto()
const formData = new FormData();
formData.append('base64img', this.photo);
this.request.post("/user/login/face",formData).then(res=>{
console.log(res)
if (res.code === '200') {
//编写拿到后端返回的数据时的逻辑
//比如保存token,保存用户信息等。
this.$message.success("登录成功")
setRoutes()
if (res.data.role === 'CUSTOMER') {
// 存储用户信息到浏览器
this.$router.push("/MallHome");
} else {
this.$router.push("/")
}
} else {
this.$message.error(res.msg)
}
})
}, 1000); // 1000毫秒 = 1秒
},
startCamera() {
const constraints = {
video: {
width: { exact: 400 },
height: { exact: 400 },
},
};
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
this.$refs.video.srcObject = stream;
this.stream = stream;
});
},
takePhoto() {
const canvas = document.createElement("canvas");
const video = this.$refs.video;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext("2d").drawImage(video, 0, 0, canvas.width, canvas.height);
this.photo = canvas.toDataURL("image/png");
},
beforeDestroy() {
if (this.stream) {
this.stream.getTracks().forEach((track) => {
track.stop();
});
}
},
这样我们就实现了刷脸登录,以上内容仅供大家参考,主要的是思路。有问题的小伙伴欢迎在后台私信我。文章来源地址https://www.toymoban.com/news/detail-762169.html
到了这里,关于基于Face++,使用Spring Boot+Elemnet-UI实现人脸识别登录。的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!