前端页面人脸识别页面
<div id="face_login" > <div class="body-bg"> <div class="WgciCg LCN0VA"></div> <h2 class="scanTip">{{scanTip}}</h2> <div v-show="showContainer" class="take-photo"> <video ref="refVideo" id="video" width="500" height="400" preload autoplay loop muted></video> <canvas ref="refCanvas" id="canvas" width="500" height="400"></canvas> </div> </div> </div>
引入前端识别js
<script src="../static/face/tracking-min.js" th:src="@{/face/tracking-min.js}"></script> <script src="../static/face/face-min.js" th:src="@{/face/face-min.js}"></script>
js调取摄像头
playVideo() { this.getUserMedia({ video: { width: 500, height: 400, facingMode: "user" } /* 前置优先 */ }, this.success, this.error) this.video = document.getElementById('video') this.canvas = document.getElementById('canvas') this.context = this.canvas.getContext('2d') // eslint-disable-next-line no-undef this.tracker = new tracking.ObjectTracker('face') this.tracker.setInitialScale(4) this.tracker.setStepSize(2) this.tracker.setEdgesDensity(0.1) // eslint-disable-next-line no-undef tracking.track('#video', this.tracker, {camera: true}) this.tracker.on('track', this.handleTracked) },
前端代码如下
<!DOCTYPE html> <html lang="zh" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"> <title>登录XXXXXXXXXXXXXXXX系统</title> <meta name="description" content="若依后台管理框架"> <link href="../static/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/> <link href="../static/css/font-awesome.min.css" th:href="@{/css/font-awesome.min.css}" rel="stylesheet"/> <link href="../static/css/style.min.css" th:href="@{/css/style.min.css}" rel="stylesheet"/> <link href="../static/css/login.min.css" th:href="@{/css/login.min.css}" rel="stylesheet"/> <link href="../static/ruoyi/css/ry-ui.css" th:href="@{/ruoyi/css/ry-ui.css?v=4.7.5}" rel="stylesheet"/> <!-- 360浏览器急速模式 --> <meta name="renderer" content="webkit"> <!-- 避免IE使用兼容模式 --> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <link rel="shortcut icon" href="../static/favicon.ico" th:href="@{favicon.ico}"/> <style type="text/css">label.error { position:inherit; }</style> <script> if(window.top!==window.self){alert('未登录或登录超时。请重新登录');window.top.location=window.location}; </script> </head> <body class="signin"> <div class="signinpanel"> <h1>XXXXXXXXXXXXXXXX系统</h1> <div class="row"> <div class="col-sm-7"> <div id="face_login" > <div class="body-bg"> <div class="WgciCg LCN0VA"></div> <h2 class="scanTip">{{scanTip}}</h2> <div v-show="showContainer" class="take-photo"> <video ref="refVideo" id="video" width="500" height="400" preload autoplay loop muted></video> <canvas ref="refCanvas" id="canvas" width="500" height="400"></canvas> </div> </div> </div> </div> <div class="col-sm-5"> <form id="signupForm" autocomplete="off"> <h4 class="no-margins">用户登录:</h4> <div class="panel-body"> <input type="text" name="username" class="form-control uname" placeholder="用户名" /> <input type="password" name="password" class="form-control pword" placeholder="密码" /> <div class="row m-t" th:if="${captchaEnabled==true}"> <div class="col-xs-6"> <input type="text" name="validateCode" class="form-control code" placeholder="验证码" maxlength="5" /> </div> <div class="col-xs-6"> <a href="javascript:void(0);" title="点击更换验证码"> <img th:src="@{/captcha/captchaImage(type=${captchaType})}" class="imgcode" width="85%"/> </a> </div> </div> </div> <button class="btn btn-success btn-block" id="btnSubmit" data-loading="正在验证登录,请稍候...">登录</button> <button class="btn btn-success btn-block" id="btnSubmit1" data-loading="正在验证登录,请稍候..." οnclick="loginWay('camera')" >扫脸登陆</button> </form> </div> </div> </div> <script th:inline="javascript"> var ctx = [[@{/}]]; var captchaType = [[${captchaType}]]; </script> <!--[if lte IE 8]><script>window.location.href=ctx+'html/ie.html';</script><![endif]--> <!-- 全局js --> <script src="../static/js/jquery.min.js" th:src="@{/js/jquery.min.js}"></script> <script src="../static/ajax/libs/validate/jquery.validate.min.js" th:src="@{/ajax/libs/validate/jquery.validate.min.js}"></script> <script src="../static/ajax/libs/layer/layer.min.js" th:src="@{/ajax/libs/layer/layer.min.js}"></script> <script src="../static/ajax/libs/blockUI/jquery.blockUI.js" th:src="@{/ajax/libs/blockUI/jquery.blockUI.js}"></script> <script src="../static/ruoyi/js/ry-ui.js" th:src="@{/ruoyi/js/ry-ui.js?v=4.7.5}"></script> <script src="../static/ruoyi/login.js" th:src="@{/ruoyi/login.js}"></script> <script src="../static/face/tracking-min.js" th:src="@{/face/tracking-min.js}"></script> <script src="../static/face/face-min.js" th:src="@{/face/face-min.js}"></script> <script src="../static/face/stats.min.js" th:src="@{/face/stats.min.js}" ></script> <script src="../static/face/vue.min.js" th:src="@{/face/vue.min.js}" type="text/javascript"></script> <script src="../static/face/axios.min.js" th:src="@{/face/axios.min.js}" type="text/javascript"></script> <style> h1 { font-size: 40px; margin-top: 100px; text-align: center; } .col-sm-5 { width: 43.666667%; left: 28%; } .body-bg { position: fixed; top: 12%; left: 18%; width: 18%; height: 50%; z-index: -10; zoom: 1; background-repeat: no-repeat; background-size: cover; -webkit-background-size: cover; -o-background-size: cover; background-position: center 0; } .filmvideo { margin: 200px auto; width: 600px; height: 400px; display: block; clear: both; } .take-photo { position: relative; z-index: 99999; } .title { text-align: center; color: white; margin: -50px auto; font-size: 18px; } .close { width: 0.8rem; height: 0.8rem; text-align: center; margin: -50px auto; } .rect { border: 2px solid #0aeb08; position: fixed; z-index: 3; } .imgpre { width: 500px; height: 400px; display: block; clear: both; position: absolute; margin: 0px auto; left: 0; right: 0; z-index: 7; border-radius: 10px; } video, canvas { width: 500px; height: 400px; margin: 0px auto; position: absolute; left: 0; right: 0; border-radius: 10px; } .scanTip { padding-top: 100px; padding-bottom: 40px; position: relative; z-index: 99999; text-align: center; color: white; margin: 0px auto; font-size: 18px; } .WgciCg { backdrop-filter: blur(2px); background: linear-gradient(180deg, rgba(0, 0, 0, .8), rgba(0, 0, 0, .4), rgba(0, 0, 0, .8)); min-height: 100%; height: 100%; width: 100%; position: fixed; top: 0; left: 0; right: 0; z-index: 1; } </style> <script> const app = new Vue({ el: "#face_login", data() { return { showContainer: true, // 显示 tracker: null, tipFlag: false, // 提示用户已经检测到 flag: false, // 判断是否已经拍照 context: null, // canvas上下文 removePhotoID: null, // 停止转换图片 scanTip: '正在调取摄像头...', // 提示文字 imgUrl: '', // base64格式图片 canvas: null, video: null, streamIns: null, // 视频流 isLoading: false, userData: '' } }, mounted() { this.playVideo() }, methods: { // 访问用户媒体设备 getUserMedia(constrains, success, error) { if (navigator.mediaDevices.getUserMedia) { // 最新标准API navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error); } else if (navigator.webkitGetUserMedia) { // webkit内核浏览器 navigator.webkitGetUserMedia(constrains).then(success).catch(error); } else if (navigator.mozGetUserMedia) { // Firefox浏览器 // eslint-disable-next-line no-undef navagator.mozGetUserMedia(constrains).then(success).catch(error); } else if (navigator.getUserMedia) { // 旧版API navigator.getUserMedia(constrains).then(success).catch(error); } else { this.scanTip = "你的浏览器不支持访问用户媒体设备" } }, success(stream) { this.streamIns = stream // webkit内核浏览器 this.URL = window.URL || window.webkitURL if ("srcObject" in this.$refs.refVideo) { this.$refs.refVideo.srcObject = stream } else { this.$refs.refVideo.src = this.URL.createObjectURL(stream) } this.$refs.refVideo.onloadedmetadata = e => { this.$refs.refVideo.play() } }, error(e) { this.scanTip = "访问用户媒体失败" + e.name + "," + e.message }, playVideo() { this.getUserMedia({ video: { width: 500, height: 400, facingMode: "user" } /* 前置优先 */ }, this.success, this.error) this.video = document.getElementById('video') this.canvas = document.getElementById('canvas') this.context = this.canvas.getContext('2d') // eslint-disable-next-line no-undef this.tracker = new tracking.ObjectTracker('face') this.tracker.setInitialScale(4) this.tracker.setStepSize(2) this.tracker.setEdgesDensity(0.1) // eslint-disable-next-line no-undef tracking.track('#video', this.tracker, {camera: true}) this.tracker.on('track', this.handleTracked) }, handleTracked(event) { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) if (event.data.length === 0) { this.scanTip = '未识别到人脸' } else { if (!this.tipFlag) { this.scanTip = '识别到人脸,请保持当前姿势~' } // 1秒后拍照,仅拍一次 if (!this.flag) { this.scanTip = '拍照中...' this.flag = true this.removePhotoID = setTimeout(() => { this.tackPhoto() this.tipFlag = true }, 2000 ) } event.data.forEach(this.plot) } }, plot(rect) { this.context.strokeStyle = '#eb652e' this.context.strokeRect(rect.x, rect.y, rect.width, rect.height) this.context.font = '11px Helvetica' this.context.fillStyle = '#fff' this.context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11) this.context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22) }, // 拍照 tackPhoto() { this.context.drawImage(this.$refs.refVideo, 0, 0, 500, 400) // 保存为base64格式 this.imgUrl = this.saveAsPNG(this.$refs.refCanvas) var formData = new FormData() formData.append('file', this.imgUrl) axios({ method: 'post', url: '/detectFaces', data: formData, }).then(function (response) { if (response.data.code==0) { window.location.href = "/index" } else { window.location.href = "/loginFace" } }).catch(function (error) { console.log(error); }); // detectFaces(formData).then(res => { // this.isLoading = true // if (res.code === 200) { // alert(111) // // 登录成功保存token // this.userData = res.data // } // }).catch(err => { // console.log(err); // }) this.close() this.scanTip = '登录中,请稍等~' this.isLoading = true }, // 保存为png,base64格式图片 saveAsPNG(c) { return c.toDataURL('image/png', 0.3) }, // 关闭并清理资源 close() { this.video.srcObject.getTracks()[0].stop() this.flag = false this.tipFlag = false this.showContainer = false this.tracker && this.tracker.removeListener('track', this.handleTracked) && tracking.track('#video', this.tracker, {camera: false}) this.tracker = null this.context = null this.scanTip = '' clearTimeout(this.removePhotoID) if (this.streamIns) { this.streamIns.enabled = false this.streamIns.getTracks()[0].stop() this.streamIns.getVideoTracks()[0].stop() } this.streamIns = null }, } }) </script> </body> </html>
后台代码如有需要作者将在下篇文章进行更新