我自认为不想做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: {// 初始化rackerinitTracker(){// 启用摄像头,这一个是原生调用摄像头的功能,不写的话有时候谷歌浏览器调用摄像头会失败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 += 1if (this.count > 20) {this.isdetected = '已检测到人脸,正在识别'this.getPhoto()}} else {this.count -= 1if (this.count < 0){this.isdetected = '请您保持脸部在画面中央'}}});// 视频中心展示文字this.context.fillText(this.isdetected, 200,50);},// 停止监听onStopTracking() {this.trackerTask.stop();this.video.pause();// 关闭摄像头this.video.srcObject = nullwindow.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 = streamthis.video.srcObject = stream},// 视频媒体流失败getMediaStreamError(error) {this.$message.error('视频媒体流获取错误' + error)},faceLogin(userId) {const that = thisreturn this.$http.post('/admin/face/login/'+ userId).then(function (res) {if (res.success) {//这儿写自己登录成功的逻辑}});}},mounted() {this.video = this.$refs.videoDomthis.canvas = this.$refs.canvasDOM//初始化获取tonkenthis.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>
写到这儿感觉也就这些东西,我自己是明白整个流程,看这篇文章的不知道是否清楚,如果有疑问可以留言回复。