前端人脸识别

<template>
    <view>
        <view v-show="showContainer" class="face-capture">
            <text class="tip">请保持人像在取景框内</text>
            <video id="faceVideo" :autoplay="true" object-fit="cover" :show-mute-btn="false" :show-play-btn="false"
                :show-progress="false"></video>
            <canvas id="refCanvas" :width="300" :height="300"></canvas>
            <text class="contentp">{{ scanTip }}</text>
        </view>
        <view v-if="!showContainer" class="img-face">
            <img class="imgurl" :src="imgUrl" />
        </view>
        <view v-if="!scanTip" style="padding:1rem 2rem ; border:1px solid #ccc;position:fixed" @click="tackPhoto()">
            拍照
        </view>
    </view>
</template>
<script>
    import "@/static/tracking/tracking-min.js";
    import "@/static/tracking/face-min.js";
    import "@/static/tracking/eye-min.js";
    import "@/static/tracking/mouth-min.js";
    export default {
        data() {
            return {
                URL: null,
                streamIns: null, // 视频流
                showContainer: true, // 显示
                tracker: null,
                tipFlag: false, // 提示用户已经检测到
                flag: false, // 判断是否已经拍照
                context: null, // canvas上下文
                removePhotoID: null, // 停止转换图片
                scanTip: "人脸识别中...", // 提示文字
                imgUrl: "",
                canvas: null,
                trackertask: null
            };
        },
        mounted() {
            this.initGetUserMedia();
        },
        methods: {
            async initGetUserMedia() {
                const constraints = {
                    video: {
                        width: 300,
                        height: 300
                    }
                };
                try {
                    const stream = await navigator.mediaDevices.getUserMedia(constraints);
                    this.success(stream);
                } catch (error) {
                    this.error(error);
                }
            },
            success(stream) {
                this.streamIns = stream;
                const video = document.querySelector('#faceVideo video')
                video.srcObject = stream;
                // 苹果手机的系统弹框会阻止js的线程的继续执行 手动0.1秒之后自动执行代码
                setTimeout(() => {
                    video.play();
                    this.initTracker(); // 人脸捕捉
                }, 100);
            },
            error(err) {
                this.scanTip = `访问用户媒体失败: ${err.name} - ${err.message}`;
                if (err.name === 'NotAllowedError') {
                    this.scanTip = "用户拒绝了访问摄像头的请求。";
                } else if (err.name === 'NotFoundError') {
                    this.scanTip = "没有找到任何可用的媒体设备。";
                } else if (err.name === 'NotReadableError') {
                    this.scanTip = "媒体设备被占用或存在硬件问题。";
                } else if (err.name === 'OverconstrainedError') {
                    this.scanTip = "指定的约束无法满足。";
                } else if (err.name === 'SecurityError') {
                    this.scanTip = "安全错误,可能是由于不安全的上下文或其他限制。";
                } else if (err.name === 'TypeError') {
                    this.scanTip = "设备请求类型错误,请检查约束对象。";
                } else {
                    this.scanTip = `未知错误: ${err.message}`;
                }
            },
            // 人脸捕捉 设置各种参数 实例化人脸捕捉实例对象,注意canvas上面的动画效果。
            initTracker() {
                this.context = document.querySelector('#refCanvas canvas').getContext("2d"); // 画布
                this.canvas = document.querySelector('#refCanvas canvas');
                this.tracker = new window.tracking.ObjectTracker("face"); // tracker实例
                this.tracker.setInitialScale(4);
                this.tracker.setStepSize(2); // 设置步长
                this.tracker.setEdgesDensity(0.1);
                try {
                    const video = document.querySelector('#faceVideo video')
                    this.trackertask = window.tracking.track(video, this.tracker); // 开始追踪
                } catch (e) {
                    this.scanTip = "访问用户媒体失败,请重试";
                }
                //开始捕捉方法 一直不停的检测人脸直到检测到人脸
                this.tracker.on("track", (e) => {
                    //画布描绘之前清空画布
                    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
                    if (e.data.length === 0) {
                        this.scanTip = "未检测到人脸";
                    } else {
                        e.data.forEach((rect) => {
                            //设置canvas 方框的颜色大小
                            this.context.strokeStyle = "#42e365";
                            this.context.lineWidth = 2;
                            this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
                        });
                        if (!this.tipFlag) {
                            this.scanTip = "检测成功,正在拍照,请保持不动2秒";
                        }
                        // 1.5秒后拍照,仅拍一次 给用户一个准备时间
                        // falg 限制一直捕捉人脸,只要拍照之后就停止检测
                        if (!this.flag) {
                            this.scanTip = "拍照中...";
                            this.flag = true;
                            this.removePhotoID = setTimeout(() => {
                                this.tackPhoto();
                                document.querySelector('#faceVideo video').pause();
                                this.tipFlag = true;
                            }, 1500);
                        }
                    }
                });
            },
            // 拍照
            tackPhoto() {
                // 在画布上面绘制拍到的照片
                this.context.drawImage(document.querySelector('#faceVideo video'), 0, 0, 300, 300);
                // 保存为base64格式
                this.imgUrl = this.saveAsPNG(document.querySelector('#refCanvas canvas'));
                console.log('imgUrl', this.imgUrl)
                /** 拿到base64格式图片之后就可以在this.compare方法中去调用后端接口比较了,也可以调用getBlobBydataURI方法转化成文件再去比较
                 * 我们项目里有一个设置个人头像的地方,先保存一下用户的图片,然后去拿这个图片的地址和当前拍照图片给后端接口去比较。
                 * */
                // this.compare(imgUrl)
                //判断图片大小
                this.imgSize();
                //this.faceToTengXun(); // 人脸比对
                this.close();
            },
            imgSize() {
                if (this.imgUrl) {
                    // 获取base64图片byte大小
                    const equalIndex = this.imgUrl.indexOf("="); // 获取=号下标
                    let size;
                    if (equalIndex > 0) {
                        const str = this.imgUrl.substring(0, equalIndex); // 去除=号
                        const strLength = str.length;
                        const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
                        size = Math.floor(fileLength / 1024); // 向下取整
                    } else {
                        const strLength = this.imgUrl.length;
                        const fileLength = strLength - (strLength / 8) * 2;
                        size = Math.floor(fileLength / 1024); // 向下取整
                    }
                    if (size > 1024) {
                        // 图片超过1M 按比例压缩
                        this.imgUrl = document.querySelector('#refCanvas canvas').toDataURL("image/png", 1024 / size);
                    }
                }
            },
            // Base64转文件
            getBlobBydataURI(dataURI, type) {
                var binary = window.atob(dataURI.split(",")[1]);
                var array = [];
                for (var i = 0; i < binary.length; i++) {
                    array.push(binary.charCodeAt(i));
                }
                return new Blob([new Uint8Array(array)], {
                    type: type,
                });
            },
            // compare(url) {
            //     let blob = this.getBlobBydataURI(url, 'image/png')
            //     let formData = new FormData()
            //     formData.append("file", blob, "file_" + Date.parse(new Date()) + ".png")
            //     // TODO 得到文件后进行人脸识别
            // },
            // 保存为png,base64格式图片
            saveAsPNG(c) {
                return c.toDataURL("image/png", 0.4);
            },
            close() {
                this.flag = false;
                this.tipFlag = false;
                this.showContainer = false;
                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;
                this.trackertask.stop();
                this.tracker = null;
            }
        },
    }
</script>
<style>
    .face-capture {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }

    .tip {
        position: fixed;
        top: 48px;
        z-index: 5;
        font-size: 18px;
        font-family: PingFangSC-Medium, PingFang SC;
        font-weight: 500;
        color: #333333;
        line-height: 25px;
    }

    video {
        height: 300px;
        width: 300px;
        border-radius: 150px;
        border: 1px solid #ccc;
    }

    .face-capture video,
    .face-capture canvas {
        position: fixed;
        top: 120px;
        height: 300px;
        width: 300px;
        object-fit: cover;
        z-index: 2;
        background-repeat: no-repeat;
        background-size: 100% 100%;
    }

    .face-capture .contentp {
        position: fixed;
        top: 500px;
        font-size: 18px;
    }

    .face-capture .rect {
        border: 2px solid #0aeb08;
        position: fixed;
        z-index: 4;
    }

    .img-face {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }

    .img-face .imgurl {
        position: fixed;
        top: 120px;
        width: 300px;
        height: 300px;
        border-radius: 150px;
    }

    /deep/ .uni-video-cover {
        display: none !important;
    }
</style>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值