uniapp人脸识别解决方案

APP端:
因为APP端无法使用uni的camera组件,最开始考虑使用内嵌webview的方式,通过原生dom调用video渲染画面然后通过canvas截图。但是此方案兼容性在ios几乎为0,如果app只考虑安卓端的话可以采用此方案。后面又想用live-pusher组件来实现,但是发现快照api好像需要真实流地址才能截取图像。因为种种原因,也是安卓ios双端兼容性不佳。最终决定采用5+api实现。经实测5+api兼容性还算可以,但是毕竟是调用原生能力,肯定是没有原生开发那么丝滑的,难免会出现一些不可预测的兼容性问题。所以建议app和手机硬件交互强的话还是不要用uni开发了。不然真的是翻文档能翻死人。社区也找不到靠谱的解决方案。

5+api 文档
https://www.html5plus.org/doc/zh_cn/video.html#plus.video.createLivePusher

image.png

就是使用这个api调用原生的camera完成。并且可以直接在预览模式下完成快照,也不需要真实的推流地址。







以上是完整的包含逻辑的代码

关键代码
//初始化播放器
            pusherInit() {
                const currentWebview = this.$mp.page.$getAppWebview();
                this.pusher = plus.video.createLivePusher('livepusher', {
                    url: '',
                    top: '0px',
                    left: '0px',
                    width: '100%',
                    height: '50%',
                    position: 'absolute',
                    aspect: '9:16',
                    muted: false,
                    'z-index': 999999,
                    'border-radius': '50%',
                });
                currentWebview.append(this.pusher);
                //反转摄像头
                this.pusher.switchCamera();
                //开始预览
                this.pusher.preview();
                uni.hideLoading()
            },

 //快照
            snapshotPusher() {
                if (this.cilckSwitch) {
                    uni.showToast({
                        title: '请勿频繁点击',
                        icon: 'none'
                    })
                    return
                }
                this.cilckSwitch = true
                uni.showLoading({
                    title: '正在比对,请勿退出'
                })
                let that = this
                this.snapshTimeout = setTimeout(() => {
                    this.pusher.snapshot(
                        e => { 
                            //拿到本地文件路径
                            var src = e.tempImagePath;
  //这里因为接口参数需要加密,用base64的话加密出来的参数太大了,所以选择了直接读取本地文件上传文件流的方式。
                            this.uploadImg(src)
                            //获取图片base64
                            // this.getMinImage(src);
                        },
                        function(e) {
                            plus.nativeUI.alert('snapshot error: ' + JSON.stringify(e));
                            that.cilckSwitch = false
                            uni.hideLoading()
                        }
                    );
                }, 500);
            },
            //获取图片base64
            getMinImage(imgPath) {
                plus.zip.compressImage({
                        src: imgPath,
                        dst: imgPath,
                        overwrite: true,
                        quality: 40
                    },
                    zipRes => {
                        setTimeout(() => {
                            var reader = new plus.io.FileReader();
                            reader.onloadend = res => {
                                var speech = res.target.result; //base64图片
                                console.log(speech.length);
                                console.log(speech)
                                this.imgData = speech;
                            };
                            reader.readAsDataURL(plus.io.convertLocalFileSystemURL(zipRes.target));
                        }, 1000);
                    },
                    function(error) {
                        console.log('Compress error!', error);
                    }
                );
            },
            //初始化读取本地文件
            initUploader() {
                let that = this
                this.uploadFileTask = plus.uploader.createUpload(
                    "完整的接口请求地址", {
                        method: "POST",
                        headers: {
                            // 修改请求头Content-Type类型 此类型为文件上传
                            "Content-Type": "multipart/form-data"
                        }
                    },
                    // data:服务器返回的响应值 status: 网络请求状态码
                    (data, status) => {
                        // 请求上传文件成功
                        if (status == 200) {
                            console.log(data)
                            // 获取data.responseText之后根据自己的业务逻辑做处理
                            let result = JSON.parse(data.responseText);
                            console.log(result.data.xh)
                            that.handleFaceContrast({
                                xh: result.data.xh,
                                path: result.data.path
                            })
                        }
                        // 请求上传文件失败
                        else {
                            uni.showToast({
                                title: '上传图片失败',
                                icon: 'none'
                            })
                            console.log("上传失败", status)
                            that.cilckSwitch = false
                            uni.hideLoading()
                        }
                    }
                );
            },
     //调用原生能力读取本地文件并上传
            uploadImg(imgPath) {
                this.uploadFileTask.addFile('file://' + imgPath, {
                    key: "file" // 填入图片文件对应的字段名
                });
                //添加其他表单字段(参数) 两个参数可能都只支持传字符串
                // uploadFileTask.addData("参数名", 参数值);
                this.uploadFileTask.start();
            },

以上就是关键的代码
接下来补充几个坑的地方。创建出来的livepusher层级很高,无法在同一页面被别的元素遮挡。所以想要在他上面写样式是行不通了。只能再创建一个webview。然后将这个webview覆盖在livepusher上,达到人脸识别页面的样式。

//覆盖在视频之上的内容,根据实际情况编写
                     this.scanWin = plus.webview.create('/hybrid/html/faceTip.html', '', {
                      background: 'transparent'
                     });
                    //新引入的webView显示
                   this.scanWin.show();
                   //新引入的webView影藏
                     this.scanWin.hide();

这种方案在ios基本没问题。至少目前没遇到过。但是安卓就一言难尽了。首先这个组件默认调起的是后置摄像头,这显然不符合我们的需求。但是官方提供的文档里也没有明确支持可以配置优先调起哪个摄像头。好早提供了一个switchCamera的api可以翻转摄像头。

但是在安卓系统上,尤其是鸿蒙系统,调用这个api就会导致程序闪退,而且发生频率还特别高。这个问题至今不知道该怎么解决。

除了闪退问题,安卓还存在一个麻烦事儿,那就是首次进入app,翻转摄像头的api没有用,拉起的还是后置摄像头。但是后续再进入app就无此问题了。后面折腾来折腾去,发现好像是首次进入拉起授权弹窗的时候才会出现这种问题。
然后写了个定时器做测试,五秒之后再拉起摄像头再去翻转摄像头。然后再五秒内赶紧把授权给同意了。结果发现翻转竟然生效了。
然后决定再渲染推流元素之前先让用户通过权限授权,然后再拉起摄像头。 也就是上文完整代码中的

 //创建livepusher
                    if (uni.getSystemInfoSync().platform === 'android') {
                        const data1 = await permission.requestAndroidPermission(
                            "android.permission.RECORD_AUDIO")
                        const data2 = await permission.requestAndroidPermission("android.permission.CAMERA")
                        console.log(data1,data2,1111)
                        if (data1 == 1 && data2 == 1) {
                            this.pusherInit();
                        }
                    } else {
                        this.pusherInit();
                    }

具体的意思就不过多赘述了,自行看permission的文档。或者看他的代码。很简单
permission下载地址
https://ext.dcloud.net.cn/plugin?id=594
以上就是调用原生能力拉起摄像头实现快照功能的所有内容了。

下面也记录一下web端如果实现这种功能,毕竟当时搞出来也不容易,但是最终还是败在了兼容性上

方案的话大致有两种,一种是借助tracking js 有兴趣的可以了解一下,一个web端人脸识别库。他可以识别画面中是否出现人脸。以及一下更高级的功能我就没有去探索了。有需要的可以自行研究



    
        
        人脸识别
        
        
        
    
    
        

另外一种就是纯video+canvas截取一张视频中的画面。



    
        
        人脸采集
        
        
        
        
        
        
        
        
    
    
        
1、请保证本人验证。
2、请使头像正面位于画框中。
3、请使头像尽量清晰。
4、请保证眼镜不反光,双眼可见。
5、请保证无墨镜,口罩,面膜等遮挡物。
6、请不要化浓妆,不要戴帽子。
采集本人人脸

版权声明:
作者:Alex
链接:https://www.techfm.club/p/52043.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>