uniapp H5人脸识别带前后摄像头

该代码段展示了一个H5前端页面,用于通过用户摄像头进行拍照采集,并实现人脸识别的功能。页面包含初始化摄像头、调整分辨率、切换前后摄像头、拍照上传以及从相册选择图片的逻辑。同时,还处理了非HTTPS访问时的错误提示。
摘要由CSDN通过智能技术生成

前端页面拍照采集页面

<template>
    <view class="takePhotoView">
        <video  ref="video" style="object-fit:fill" :show-center-play-btn="false" muted preload :width="videoWidth"
        :height="videoHeight"></video>
        <div  :device-position="device" flash="off" @error="error"
            style="width: 100%; height: 100vh;position: fixed; top: 0;left: 0;">
            <cover-view
                style="position: relative; width: 100vw;height: 100vh; display: flex;flex-direction: column; align-items: center;justify-content: center;">
                <cover-view
                    style="position: absolute;width: 100%;height: 100%;z-index: -9;display: flex;flex-direction: row;justify-content: center;align-items: center;">
                    <cover-image class="coverImage" style="width: 100vw;height:177.8vw;"
                        src="../../static/images/allrlk.png"></cover-image>
                </cover-view>
                <cover-view
                    style="flex: 1;width: 100vw;display: flex;align-items: center;center;background:rgba(0,0,0,1);">
                    <cover-image class="coverImage" @click="close"
                        style="z-index: 1001;width: 60rpx;height: 60rpx;margin-left: 50rpx;"
                        src="../../static/images/gb.png"></cover-image>
                </cover-view>
                <cover-view :style="'width: 100vw; height:'+scale*100+'vw;'"></cover-view>
                <cover-view
                    style="flex: 1;width: 100vw;display: flex;justify-content: space-around;align-items: center;center;background:rgba(0,0,0,1);">
                    <cover-image class="coverImage" @click="deviceQH"
                        :style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasSwitch?'':'opacity: 0;')"
                        src="../../static/images/qh.png"></cover-image>
                    <cover-image class="coverImage" @click="photoShoot"
                        style="width: 100rpx;height: 100rpx;z-index: 1001;"
                        src="../../static/images/pz.png"></cover-image>
                    <cover-image class="coverImage" @click="chooseImage"
                        :style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasAlbum?'':'opacity: 0;')"
                        src="../../static/images/tk.png"></cover-image>
                </cover-view>
                <cover-view
                    style="position: absolute;z-index: 1001;width: 100%;bottom:250rpx;font-size: 66upx;text-align: center; color: #000000;font-weight: bold;">
                    避免头发口罩遮挡
                </cover-view>
            </cover-view>
        </div >
    </view>
</template>

摄像头初始化设置

import config from '@/config'
    const baseUrl = config.baseUrl
    const constraints = {
        audio: false,
        video: {
            width: 600,
            height: 400,
            facingMode: "user"
        }
    }

初始化采集页面和画布

this.video = this.$refs.video;
            uni.getSystemInfo({
            success: function (res) {
                this.videoWidth=Math.max(res.windowWidth, res.windowHeight) - 120
                constraints.video.width=this.videoWidth
                this.videoHeight=Math.min(res.windowWidth, res.windowHeight)
                constraints.video.height=this.videoHeight
                },
            });
 this.useCamera()

调用方法

                /**
             * 使用摄像头
             */
            useCamera(){
                let that = this;
                if (window.stream) {
                       window.stream.getTracks().forEach(track => {
                           track.stop();
                       });
                }
                
                // 如果不是通过loacalhost或者通过https访问会将报错捕获并提示
                try{
                    if (navigator.mediaDevices === undefined) {
                        navigator.mediaDevices = {};
                     }
                    if (navigator.mediaDevices.getUserMedia === undefined) {
                        navigator.mediaDevices.getUserMedia = function (constraints) {
                        var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
                        if (!getUserMedia) {
                            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                        }
                        return new Promise(function (resolve, reject) {
                            getUserMedia.call(navigator, constraints, resolve, reject);
                        });
                      }
                    }
                    
                    let promise =navigator.mediaDevices.getUserMedia(constraints);
                    promise.then((stream) => {
                        window.stream=stream;
                        // 返回参数
                        const video = document.querySelector("video");
                        if ("srcObject" in video) {
                            video.srcObject = stream;
                          } else {
                            // 防止在新的浏览器里使用它,应为它已经不再支持了
                            video.src = window.URL.createObjectURL(stream);
                        }
                        video.onloadedmetadata = function (e) {
                            video.play();
                        };
                    }).catch((error) => {
                        console.log(error);
                    });
                    
                }catch(err){
                    this.httpsAlert = `您现在在使用非Https访问,
                    请先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
                    添将当前链接${window.location.href}添加到列表,
                    并且将Insecure origins treated as secure修改为enabled,
                    修改完成后请重启浏览器后再次访问!`
                }
            },
             // 关闭摄像头
            closeCamera () {
                if (!this.$refs['video'].srcObject) return
                let stream = this.$refs['video'].srcObject
                let tracks = stream.getTracks()
                tracks.forEach(track => {
                    track.stop()
                })
                this.$refs['video'].srcObject = null
            },

业务功能

/**
             * 拍照上传
             */
            photoShoot(){
                // 拿到图片的base64
                this.canvas = document.createElement("canvas");
                let context = this.canvas.getContext("2d");
                const video = document.querySelector("video");
                this.canvas.width = Math.min(video.videoWidth, video.videoHeight);
                this.canvas.height = Math.max(video.videoWidth, video.videoHeight);
                context.drawImage(video, 0, 0, this.canvas.width,this.canvas.height);
            
                let canvas = this.canvas.toDataURL("image/png");
                // 拍照以后将video隐藏
                // 停止摄像头成像
                video.srcObject.getTracks()[0].stop()
                video.pause()
                if(canvas) {
                    // 拍照将base64转为file流文件
                    let blob = this.dataURLtoBlob(canvas);
                    let file = this.blobToFile(blob, "imgName");
                    let image = window.URL.createObjectURL(file)
                    this.uploadFilePromise(image);
                } else {
                    console.log('canvas生成失败')
                }
            },
            /**
             * 将图片转为blob格式
             * dataurl 拿到的base64的数据
             */
            dataURLtoBlob(dataurl) {
                let arr = dataurl.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);
                while(n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                return new Blob([u8arr], {
                    type: mime
                });
            },
            /**
             * 生成文件信息
             * theBlob 文件
             * fileName 文件名字
             */
            blobToFile(theBlob, fileName) {
                theBlob.lastModifiedDate = new Date().toLocaleDateString();
                theBlob.name = fileName;
                return theBlob;
            },
             deviceQH() {
                this.useFrontCamera = !this.useFrontCamera;
                if (window.stream) {
                    window.stream.getTracks().forEach((track) => {
                        track.stop();
                    });
                }
                constraints.video.facingMode = this.useFrontCamera ? 'user' : { exact: 'environment' };
                
                let    promise = navigator.mediaDevices.getUserMedia(constraints); // 调用将询问用户是否允许访问摄像机。如果用户拒绝,它将引发异常并且不返回流。因此,必须在 try/catch 块内处理这种情况,它返回一个Promise,必须使用 async/await 或 then 块
                promise.then((stream) => {
                    window.stream=stream;
                    // 返回参数
                    const video = document.querySelector("video");
                    if ("srcObject" in video) {
                        video.srcObject = stream;
                      } else {
                        // 防止在新的浏览器里使用它,应为它已经不再支持了
                        video.src = window.URL.createObjectURL(stream);
                    }
                    video.onloadedmetadata = function (e) {
                        video.play();
                    };
                }).catch((error) => {
                    console.log(error);
                });
            },
            close() {
                this.$emit("close")
            },
            chooseImage() {
                let _this = this;
                if (!this.hasAlbum) {
                    return
                }
                if (window.stream) {
                   window.stream.getTracks().forEach(track => {
                       track.stop();
                   });
                }
                uni.chooseImage({
                    count: 1, //默认9
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
                    sourceType: ['album'], //从相册选择
                    success: (res) => {
                        _this.uploadFilePromise(res.tempFilePaths[0]);
                    }
                });
            },
            error(e) {
                console.log(e);
            },
            uploadFilePromise(url) {
                //弹出照片核验的效果
                let _self=this;
                return new Promise((resolve, reject) => {
                let a = uni.uploadFile({
                url: baseUrl +'/file/uploadMinio', 
                filePath: url,
                name: 'file',
                success: (res) => {
                    //弹出验证情况
                     _self.$modal.loading("验证中,请耐心等待...")
                     let result = JSON.parse(res.data)
                     console.log(result)
                    if(result.code==''||result.code==null){
                        _self.$emit('takePhoto',null)
                        return
                    }
                    if(result.code!=200){
                        this.$tab.reLaunch('/pages/face/faceValidate?code='+result.code+"&msg="+result.msg)
                        return
                    }
                    setTimeout(() => {
                        let param={};
                        param.url=result.data.url
                        param.feature=result.data.feature
                        _self.$emit('takePhotoTwo',param)
                    }, 500)
                }
                });
                })
            },
        }
    }

以上是前端H5人脸识别的方法,下面是全代码

<template>
    <view class="takePhotoView">
        <video  ref="video" style="object-fit:fill" :show-center-play-btn="false" muted preload :width="videoWidth"
        :height="videoHeight"></video>
        <div  :device-position="device" flash="off" @error="error"
            style="width: 100%; height: 100vh;position: fixed; top: 0;left: 0;">
            <cover-view
                style="position: relative; width: 100vw;height: 100vh; display: flex;flex-direction: column; align-items: center;justify-content: center;">
                <cover-view
                    style="position: absolute;width: 100%;height: 100%;z-index: -9;display: flex;flex-direction: row;justify-content: center;align-items: center;">
                    <cover-image class="coverImage" style="width: 100vw;height:177.8vw;"
                        src="../../static/images/allrlk.png"></cover-image>
                </cover-view>
                <cover-view
                    style="flex: 1;width: 100vw;display: flex;align-items: center;center;background:rgba(0,0,0,1);">
                    <cover-image class="coverImage" @click="close"
                        style="z-index: 1001;width: 60rpx;height: 60rpx;margin-left: 50rpx;"
                        src="../../static/images/gb.png"></cover-image>
                </cover-view>
                <cover-view :style="'width: 100vw; height:'+scale*100+'vw;'"></cover-view>
                <cover-view
                    style="flex: 1;width: 100vw;display: flex;justify-content: space-around;align-items: center;center;background:rgba(0,0,0,1);">
                    <cover-image class="coverImage" @click="deviceQH"
                        :style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasSwitch?'':'opacity: 0;')"
                        src="../../static/images/qh.png"></cover-image>
                    <cover-image class="coverImage" @click="photoShoot"
                        style="width: 100rpx;height: 100rpx;z-index: 1001;"
                        src="../../static/images/pz.png"></cover-image>
                    <cover-image class="coverImage" @click="chooseImage"
                        :style="'width: 60rpx;height: 60rpx;z-index: 1001;'+(hasAlbum?'':'opacity: 0;')"
                        src="../../static/images/tk.png"></cover-image>
                </cover-view>
                <cover-view
                    style="position: absolute;z-index: 1001;width: 100%;bottom:250rpx;font-size: 66upx;text-align: center; color: #000000;font-weight: bold;">
                    避免头发口罩遮挡
                </cover-view>
            </cover-view>
        </div >
    </view>
</template>

<script>
    import config from '@/config'
    const baseUrl = config.baseUrl
    const constraints = {
        audio: false,
        video: {
            width: 600,
            height: 400,
            facingMode: "user"
        }
    }
    export default {
        data() {
            return {
                hasSwitch:true,
                hasAlbum:true,
                initScale:1.277,
                original:true,
                canvasW: 0,
                canvasH: 0,
                device: 'front',
                videoShow: false,
                pictureShow: false,
                // 图片地址
                picture: '',
                // 用于视频识别的节点
                canvas: null,
                video: null,
                image: null,
                timeout: 0,
                // 模型识别的条件
                options: '',
                // 提示控制
                noOne: '',
                moreThanOne: '',
                // 不是通过Https访问提示
                httpsAlert: '',
                videoWidth:600,
                videoHeight:400,
                useFrontCamera: true,
            }
        },
        mounted(){
            this.video = this.$refs.video;
            uni.getSystemInfo({
            success: function (res) {
                this.videoWidth=Math.max(res.windowWidth, res.windowHeight) - 120
                constraints.video.width=this.videoWidth
                this.videoHeight=Math.min(res.windowWidth, res.windowHeight)
                constraints.video.height=this.videoHeight
                },
            });
            this.useCamera()
            
        },
        computed: {
            scale: function() {
                if (this.initScale > 1.77) return 1.77;
                if (this.initScale < 1.27) return 1.27;
                return this.initScale;
            }
        },
        methods: {
            
            /**
             * 使用摄像头
             */
            useCamera(){
                let that = this;
                if (window.stream) {
                       window.stream.getTracks().forEach(track => {
                           track.stop();
                       });
                }
                
                // 如果不是通过loacalhost或者通过https访问会将报错捕获并提示
                try{
                    if (navigator.mediaDevices === undefined) {
                        navigator.mediaDevices = {};
                     }
                    if (navigator.mediaDevices.getUserMedia === undefined) {
                        navigator.mediaDevices.getUserMedia = function (constraints) {
                        var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
                        if (!getUserMedia) {
                            return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                        }
                        return new Promise(function (resolve, reject) {
                            getUserMedia.call(navigator, constraints, resolve, reject);
                        });
                      }
                    }
                    
                    let promise =navigator.mediaDevices.getUserMedia(constraints);
                    promise.then((stream) => {
                        window.stream=stream;
                        // 返回参数
                        const video = document.querySelector("video");
                        if ("srcObject" in video) {
                            video.srcObject = stream;
                          } else {
                            // 防止在新的浏览器里使用它,应为它已经不再支持了
                            video.src = window.URL.createObjectURL(stream);
                        }
                        video.onloadedmetadata = function (e) {
                            video.play();
                        };
                    }).catch((error) => {
                        console.log(error);
                    });
                    
                }catch(err){
                    this.httpsAlert = `您现在在使用非Https访问,
                    请先在chrome://flags/#unsafely-treat-insecure-origin-as-secure中修改配置,
                    添将当前链接${window.location.href}添加到列表,
                    并且将Insecure origins treated as secure修改为enabled,
                    修改完成后请重启浏览器后再次访问!`
                }
            },
             // 关闭摄像头
            closeCamera () {
                if (!this.$refs['video'].srcObject) return
                let stream = this.$refs['video'].srcObject
                let tracks = stream.getTracks()
                tracks.forEach(track => {
                    track.stop()
                })
                this.$refs['video'].srcObject = null
            },

            /**
             * 人脸识别方法
             * 通过canvas节点识别
             * 节点对象执行递归识别绘制
             */
            /* async recognizeFace(){
                if (this.video.paused) return clearTimeout(this.timeout);
                this.canvas.getContext('2d', { willReadFrequently: true }).drawImage(this.video, 0, 0, 400, 400);
                const results = await faceApi.detectAllFaces(this.canvas, this.options).withFaceLandmarks();
                if(results.length === 0){
                    if(this.moreThanOne !== ''){
                        this.moreThanOne.close()
                        this.moreThanOne = ''
                    }
                    if(this.noOne === ''){
                        this.noOne = this.$message({
                            message: '未识别到人脸',
                            type: 'warning',
                            duration: 0
                        });
                    }
                }else if(results.length > 1){
                    if(this.noOne !== ''){
                        this.noOne.close()
                        this.noOne = ''
                    }
                    if(this.moreThanOne === ''){
                        this.moreThanOne = this.$message({
                            message: '检测到镜头中有多个人',
                            type: 'warning',
                            duration: 0
                        });
                    }
                }else{
                    if(this.noOne !== ''){
                        this.noOne.close()
                        this.noOne = ''
                    }
                    if(this.moreThanOne !== ''){
                        this.moreThanOne.close()
                        this.moreThanOne = ''
                    }
                }
                // 通过canvas显示video信息
                this.timeout = setTimeout(() => {
                    return this.recognizeFace()
                });
            }, */
            /**
             * 拍照上传
             */
            photoShoot(){
                // 拿到图片的base64
                this.canvas = document.createElement("canvas");
                let context = this.canvas.getContext("2d");
                const video = document.querySelector("video");
                this.canvas.width = Math.min(video.videoWidth, video.videoHeight);
                this.canvas.height = Math.max(video.videoWidth, video.videoHeight);
                context.drawImage(video, 0, 0, this.canvas.width,this.canvas.height);
            
                let canvas = this.canvas.toDataURL("image/png");
                // 拍照以后将video隐藏
                // 停止摄像头成像
                video.srcObject.getTracks()[0].stop()
                video.pause()
                if(canvas) {
                    // 拍照将base64转为file流文件
                    let blob = this.dataURLtoBlob(canvas);
                    let file = this.blobToFile(blob, "imgName");
                    let image = window.URL.createObjectURL(file)
                    this.uploadFilePromise(image);
                } else {
                    console.log('canvas生成失败')
                }
            },
            /**
             * 将图片转为blob格式
             * dataurl 拿到的base64的数据
             */
            dataURLtoBlob(dataurl) {
                let arr = dataurl.split(','),
                    mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]),
                    n = bstr.length,
                    u8arr = new Uint8Array(n);
                while(n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                return new Blob([u8arr], {
                    type: mime
                });
            },
            /**
             * 生成文件信息
             * theBlob 文件
             * fileName 文件名字
             */
            blobToFile(theBlob, fileName) {
                theBlob.lastModifiedDate = new Date().toLocaleDateString();
                theBlob.name = fileName;
                return theBlob;
            },
             deviceQH() {
                this.useFrontCamera = !this.useFrontCamera;
                if (window.stream) {
                    window.stream.getTracks().forEach((track) => {
                        track.stop();
                    });
                }
                constraints.video.facingMode = this.useFrontCamera ? 'user' : { exact: 'environment' };
                
                let    promise = navigator.mediaDevices.getUserMedia(constraints); // 调用将询问用户是否允许访问摄像机。如果用户拒绝,它将引发异常并且不返回流。因此,必须在 try/catch 块内处理这种情况,它返回一个Promise,必须使用 async/await 或 then 块
                promise.then((stream) => {
                    window.stream=stream;
                    // 返回参数
                    const video = document.querySelector("video");
                    if ("srcObject" in video) {
                        video.srcObject = stream;
                      } else {
                        // 防止在新的浏览器里使用它,应为它已经不再支持了
                        video.src = window.URL.createObjectURL(stream);
                    }
                    video.onloadedmetadata = function (e) {
                        video.play();
                    };
                }).catch((error) => {
                    console.log(error);
                });
            },
            close() {
                this.$emit("close")
            },
            chooseImage() {
                let _this = this;
                if (!this.hasAlbum) {
                    return
                }
                if (window.stream) {
                   window.stream.getTracks().forEach(track => {
                       track.stop();
                   });
                }
                uni.chooseImage({
                    count: 1, //默认9
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
                    sourceType: ['album'], //从相册选择
                    success: (res) => {
                        _this.uploadFilePromise(res.tempFilePaths[0]);
                    }
                });
            },
            error(e) {
                console.log(e);
            },
            uploadFilePromise(url) {
                //弹出照片核验的效果
                let _self=this;
                return new Promise((resolve, reject) => {
                let a = uni.uploadFile({
                url: baseUrl +'/file/uploadMinio', 
                filePath: url,
                name: 'file',
                success: (res) => {
                    //弹出验证情况
                     _self.$modal.loading("验证中,请耐心等待...")
                     let result = JSON.parse(res.data)
                     console.log(result)
                    if(result.code==''||result.code==null){
                        _self.$emit('takePhoto',null)
                        return
                    }
                    if(result.code!=200){
                        this.$tab.reLaunch('/pages/face/faceValidate?code='+result.code+"&msg="+result.msg)
                        return
                    }
                    setTimeout(() => {
                        let param={};
                        param.url=result.data.url
                        param.feature=result.data.feature
                        _self.$emit('takePhotoTwo',param)
                    }, 500)
                }
                });
                })
            },
        }
    }
</script>

<style scoped>
    .takePhotoView {
        width: 100vw;
        height: 100vh;
        position: fixed;
        top: 0;
        left: 0;
        z-index: 9999;
        box-sizing: border-box;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
    }

    .canvas {
        opacity: 0;
        position: fixed;
        top: 100vh;
        z-index: -999;
    }

    .coverImage {
        z-index: 999;
    }
    uni-video {
        width: 100%;
        height: 100%;
    }
</style>

allrlk.png

gb.png

pz.png

qh.png

tk.png

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值