H5平台端结合虹软实现人脸识别登录功能
·
前端页面人脸识别页面
<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="正在验证登录,请稍候..." onclick="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>
后台代码如有需要作者将在下篇文章进行更新
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)