我自认为不想做curd程序员,但是免不了的会对数据基本原子操作进行处理,项目开发过程中的增删改查少不了的,但是又不甘心于curd下去,所以想要在掌握现有知识的基础上,甚至逼迫自己去学习一些东西,去接触新的技术,足够的深度做不到,最起码广度要有所保证,每天进步一点点。
开篇写的算是自己的程序员生涯的一丢丢想法,言归正传,本篇介绍通过阿里云实现人脸登录功能(百度云原理类似,不过百度云的人脸不是免费的了,so果断阿里云了)。
说实话本篇的说明思路我自己感觉有点乱,只好边写边梳理。。
前提:系统中已经开户成功,user表内有相关用户信息及用户ID(唯一标识)
整体思路:
- 阿里云开通人脸人体服务,现阶段是免费的,以后说不准哈哈。
https://homenew.console.aliyun.com - 选择人脸数据库管理模块,进行人脸管理,可以新增样本,其实就是将人脸图片与系统内user表ID进行关联与维护。
- 系统PC或移动端摄像头人脸识别,获取人脸base64,调用后端接口,后端接口进行阿里云OSS上传并获得存储路径(后续人脸搜索api方法会要求前端识别人脸图片格式为OSS存储格式,这点有些麻烦),而后调用人脸搜索api从阿里云人脸数据库中搜索是否能匹配到,搜索结果会返回matchList。(人脸搜索可以限制返回最高匹配的几个结果,而不是所有结果)
- 搜索结果getScore按照匹配分数进行排序,获取分数最高的也就是匹配度最高的,判断分数是否高于0.8(匹配分数按实际情况自己定义)。
- 根据最高匹配结果获取系统user表内的ID(人脸识别管理库的样本ID已经维护),后续调用系统login接口即可,获取token等操作。
@ApiOperation(value = "人脸检测接口")
@PostMapping(value = "/check")
public Result faceCheck(@RequestBody JSONObject faceJson) {
String base64Str = faceJson.getString("img");
String url = aliyunOss.uploadFiles(base64Str);
if (StringUtils.isBlank(url)) {
return Result.fail("人脸检测图片失败");
}
SearchFaceResponse response = aliFaceRecognize.searchFace(groupId, url);
if (response==null) {
return Result.fail("人脸检测失败");
}
if (response.getData()==null){
return Result.fail("未匹配到人脸");
}
if (CollectionUtils.isEmpty(response.getData().getMatchList())) {
return Result.fail("未匹配到人脸");
}
List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> finals = new ArrayList<>();
List<SearchFaceResponse.Data.MatchListItem> matchList = response.getData().getMatchList();
for (SearchFaceResponse.Data.MatchListItem matchListItem : matchList) {
List<SearchFaceResponse.Data.MatchListItem.FaceItemsItem> items = matchListItem.getFaceItems();
for (SearchFaceResponse.Data.MatchListItem.FaceItemsItem item : items) {
finals.add(item);
}
}
SearchFaceResponse.Data.MatchListItem.FaceItemsItem maxMaterial = finals.stream().max(Comparator.comparingDouble(SearchFaceResponse.Data.MatchListItem.FaceItemsItem::getScore)).get();
if(maxMaterial.getScore()<0.8) {
return Result.fail("人脸匹配精度不准确,请重新识别");
}
User user = userService.findById(maxMaterial.getEntityId());
if (user == null) {
return Result.fail("未匹配到用户,请开户");
}
return Result.success("登录成功!", maxMaterial.getEntityId());
}
前端使用antdVue实现的,附代码:
<template>
<div>
<div class="user-icon">
<video width="500" height="400" ref="videoDom" id="video" preload autoplay loop muted></video>
<canvas width="500" height="400" ref="canvasDOM"></canvas>
</div>
<div align="center">{{loding}}</div>
<!--<div @click="initTracker">人脸识别</div>-->
</div>
</template>
<script>
require('tracking/build/tracking-min.js')
require('tracking/build/data/face-min.js')
export default {
name: 'FaceLoginAli',
data() {
return {
// 记录拍照到了几次
count: 0,
isdetected: '请您保持脸部在画面中央',
loding: ''
}
},
methods: {
// 初始化racker
initTracker(){
// 启用摄像头,这一个是原生调用摄像头的功能,不写的话有时候谷歌浏览器调用摄像头会失败
navigator.mediaDevices
.getUserMedia({video: true,audio: true})
.then(this.getMediaStreamSuccess)
.catch(this.getMediaStreamError)
this.context = this.canvas.getContext('2d')
// 初始化tracking参数
this.tracker = new tracking.ObjectTracker("face");
this.tracker.setInitialScale(4);
this.tracker.setStepSize(2);
this.tracker.setEdgesDensity(0.1);
this.tracker.on("track", event => {
this.onTracked(event);
});
// tracking启用摄像头,这里我选择调用原生的摄像头
// tracking.track(this.video, this.tracker, { camera: true });
// 如果是读取视频,可以用trackerTask.stop trackerTask.run来暂停、开始视频
this.trackerTask = tracking.track(this.video, this.tracker);
},
// 监听中
onTracked(event){
// 判断终止条件, stop是异步的,不返回的话,还会一直截图
if (this.count >= 21) {
this.onStopTracking();
return;
}
// 画框框
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
event.data.forEach(rect => {
this.context.lineWidth = 1;
this.context.strokeStyle = "#a64ceb";
//'#a64ceb';
this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
this.context.font = "11px Helvetica";
this.context.fillStyle = "#fff";
// 截图
if (event.data.length > 0 && this.count <= 20) {
if (this.count < 0) {
this.count = 0
}
this.count += 1
if (this.count > 20) {
this.isdetected = '已检测到人脸,正在识别'
this.getPhoto()
}
} else {
this.count -= 1
if (this.count < 0){
this.isdetected = '请您保持脸部在画面中央'
}
}
});
// 视频中心展示文字
this.context.fillText(this.isdetected, 200,50);
},
// 停止监听
onStopTracking() {
this.trackerTask.stop();
this.video.pause();
// 关闭摄像头
this.video.srcObject = null
window.stream.getTracks().forEach(track => track.stop())
},
// 获取人脸照片
getPhoto(){
this.isdetected = ''
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
let video = document.getElementById('video')
this.context.drawImage(video, 0,0, this.canvas.width, this.canvas.height)
let dataUrl = this.canvas.toDataURL('image/jpeg', 1);
this.imgbase64 = dataUrl.replace(/^data:image\/\w+;base64,/, "");
// 开始人脸识别
this.postFace()
},
// 人脸验证
postFace(){
this.loding = '正在识别中,请稍后................'
this.$http.post('admin/face/check',{
img: this.imgbase64
}).then((res)=>{
console.log(res)
this.loding = ''
if(res.success){
this.faceLogin(res.data);
this.$message.success(res.msg);
}else {
this.$message.error(res.msg);
}
})
},
// 视频流启动
getMediaStreamSuccess(stream) {
window.stream = stream
this.video.srcObject = stream
},
// 视频媒体流失败
getMediaStreamError(error) {
this.$message.error('视频媒体流获取错误' + error)
},
faceLogin(userId) {
const that = this
return this.$http.post('/admin/face/login/'+ userId).then(function (res) {
if (res.success) {
//这儿写自己登录成功的逻辑
}
});
}
},
mounted() {
this.video = this.$refs.videoDom
this.canvas = this.$refs.canvasDOM
//初始化获取tonken
this.initTracker();
},
destroyed() {
}
}
</script>
<style scoped>
.user-icon {
position: relative;
margin: 0 auto;
margin-top: 50px;
width: 560px;
height: 560px;
}
.button {
width: 90vw;
height: 50px;
line-height: 50px;
margin: 0 auto;
background-color: skyblue;
color: white;
text-align: center;
border-radius: 5px;
font-size: 16px;
}
video, canvas {
position: absolute;
}
</style>
写到这儿感觉也就这些东西,我自己是明白整个流程,看这篇文章的不知道是否清楚,如果有疑问可以留言回复。