移动端H5及微信公众号实现人脸识别的可行性demo

该博客介绍了一个使用HTML、Vue和tracking.js库实现的实时人脸识别系统。系统通过手机前置摄像头捕获视频流,利用tracking.js跟踪人脸,并在canvas上绘制人脸照片,转化为base64编码后上传至第三方平台进行识别对比。整个过程包括获取视频流、人脸检测、拍照、图片转换和上传。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现思路

1.利用手机的前置摄像头获取视频流
2.利用第三方插件tracking.js捕获人脸部分
3.捕获到人脸后利用canvas绘制带人脸的照片(业务需要:转为base64编码)
4.将绘制带人脸的照片和其他数据上传到第三方校验平台进行识别对比校验
5.结束完工 (getUserMedia要在https的安全域名下使用)

html

<!DOCTYPE html>
<html lang=en style="font-size: 37.5px">
<head>
  <meta charset=utf-8>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <title>人脸检测</title>
  <script src="../vue.min.2.6.js"></script>
  <script src="../tracking/tracking.js"></script>
  <script src="../tracking/data/face-min.js"></script>
</head>
<style>
  .face-capture {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    color: #FF0000;
    position: relative;
  }

  .face-capture .title {
    position: absolute;
    top: 100px;
    color: #FF0000;
    font-size: 18px;
    z-index: 10;
  }

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

</style>
<body>
<div id="app">
  <div v-show="showContainer" class="face-capture" id="face-capture">
    <video ref="refVideo" id="video" :width="screenSize.width" :height="screenSize.height" muted webkit-playsinline playsinline></video>
    <div class="title">{{scanTip}}</div>
    <canvas ref="refCanvas" :width="screenSize.width" :height="screenSize.height" :style="{opacity: 0}"></canvas>
    <!-- 人脸识别选框 -->
    <div class="rect" v-for="item in profile"
         :style="{ width: item.width + 'px', height: item.height + 'px', left: item.left + 'px', top: item.top + 'px'}">
    </div>
  </div>
   <!--捕获到人脸图片后显示图片 -->
  <img v-show="!showContainer" :src="imgUrl"/>
</div>
</body>
</html>

javascript

const app = new Vue({
        el: "#app",
        data() {
            return {
                // screenSize: {width: window.screen.width, height: window.screen.height},
                screenSize: {width: 320, height: 320},
                URL: null,
                streamIns: null,        // 视频流
                showContainer: true,   // 显示
                tracker: null,
                tipFlag: false,         // 提示用户已经检测到
                flag: false,            // 判断是否已经拍照
                context: null,          // canvas上下文
                profile: [],            // 轮廓
                removePhotoID: null,    // 停止转换图片
                scanTip: '加载中...',// 提示文字
                imgUrl: ''              // base64格式图片
            }
        },
        mounted() {
            this.playVideo()
        },
        methods: {
            // 访问用户媒体设备
            getUserMedia(constraints, success, error) {
                //访问用户媒体设备的兼容方法
                window.navigator.getUserMedia = navigator.getUserMedia || navigator.webKitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
                if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
                    //最新的标准API
                    navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
                } else if (navigator.webkitGetUserMedia) {
                    //webkit核心浏览器
                    navigator.webkitGetUserMedia(constraints, success, error)
                } else if (navigator.mozGetUserMedia) {
                    //firfox浏览器
                    navigator.mozGetUserMedia(constraints, success, error);
                } else if (navigator.getUserMedia) {
                    //旧版API
                    navigator.getUserMedia(constraints, success, 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.scanTip = '人脸识别中...';
                    this.$refs.refVideo.play();
                    this.initTracker()
                // }
            },
            error(e) {
                this.scanTip = "访问用户媒体失败" + e.name + "," + e.message
            },
            playVideo() {
                var constraints = {
                    video: {
                        width: 320,
                        height: 320,
                        facingMode: "user"
                    }
                };
                this.getUserMedia(constraints, this.success, this.error);
            },
            // 人脸捕捉
            initTracker() {
                this.context = this.$refs.refCanvas.getContext("2d");    // 画布
                this.tracker = new tracking.ObjectTracker(['face']);     // tracker实例
                this.tracker.setStepSize(1.7);                      // 设置步长
                this.tracker.on('track', this.handleTracked);            // 绑定监听方法
                try {
                    tracking.track('#video', this.tracker);     // 开始追踪
                } catch (e) {
                    this.scanTip = "访问用户媒体失败,请重试";
                }
            },
            // 追踪事件
            handleTracked(e) {
                console.log(e);
                if (e.data.length === 0) {
                    this.scanTip = '未检测到人脸';
                } else {
                    if (!this.tipFlag) {
                        this.scanTip = '检测成功,正在拍照,请保持不动2秒';
                    }
                    // 1秒后拍照,仅拍一次
                    if (!this.flag) {
                        this.scanTip = '拍照中...';
                        this.flag = true;
                        this.removePhotoID = setTimeout(() => {
                            this.tackPhoto();
                            this.tipFlag = true
                        }, 2000)
                    }
                    e.data.forEach(this.plot)
                }
            },
            // 绘制跟踪框
            plot({x, y, width: w, height: h}) {
                // 创建框对象
                this.profile.push({width: w, height: h, left: x, top: y})
            },
            // 拍照
            tackPhoto() {
                this.context.drawImage(this.$refs.refVideo, 0, 0, this.screenSize.width, this.screenSize.height)
                // 保存为base64格式
                this.imgUrl = this.saveAsPNG(this.$refs.refCanvas)
                /** 拿到base64格式图片之后就可以在this.compare方法中去调用后端接口比较了,也可以调用getBlobBydataURI方法转化成文件再去比较
                 * 我们项目里有一个设置个人头像的地方,先保存一下用户的图片,然后去拿这个图片的地址和当前拍照图片给后端接口去比较。
                 * */
                // this.compare(imgUrl)
                this.close()
            },
            // 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.3)
            },
            // 关闭并清理资源
            close() {
                this.flag = false;
                this.tipFlag = false;
                this.showFailPop = false;
                this.showContainer = false;
                this.tracker && this.tracker.removeListener('track', this.handleTracked)
                this.tracker = null;
                this.context = null;
                this.profile = []
                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
            }
        }
    })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值