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="正在验证登录,请稍候..." ο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>

后台代码如有需要作者将在下篇文章进行更新

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值