uniapp使用live-pusher实现模拟人脸识别效果

需求:

1、前端实现模拟用户人脸识别,识别成功后抓取视频流或认证的一张静态图给服务端。

2、服务端调用第三方活体认证接口,验证前端传递的人脸是否存在,把认证结果反馈给前端。

3、前端根据服务端返回的状态,显示在页面上用于提示用户。

难点:

1、前端APP如果要实现人脸活体校验验证,需要对接大厂的SDK实现。

2、一开始我采用用使用在App内嵌套H5来单独部署一套人脸验证,把结果通过webview与APP进行数据交互,但是H5试了使用好用高效的effet.js 库人脸识别 目前仅支持H5(但是发现最后在手机上识别人像整个人被压缩似得,而且是反向镜头,跟作者已反馈,等待作者持续更新)。

3、抛弃了使用App嵌套H5方法,因此最终选择了使用原生live-pusher直播流来模拟实现人脸识别效果。本打算给大家写成组件方便大家直接调用来着,但是发现组件内获取实例仅支持在onReady页面生命周期使用。

实现思路

1、首先需要获取手机是否有录音以及相机权限,没有的话引导用户前去设置页主动设置。

2、其次创建live-pusher实例,根据业务需求实现自己的模拟人脸识别思路(目前我们这是在用户手动点击5s后进行自动抓拍的)。

3、前端拿到抓拍最后一帧图片调用接口给服务端传递,服务端调用第三方进行人脸活体检测,一般是需要付费的哈。

4、最后前端把服务端返回的识别状态展示在页面上,方便后续用户操作。

代码步骤(当前是vue3项目示例,最低sdk 21版本并且勾选livepusher)

1、获取当前手机是否允许开启相机和录音权限。(建议直接使用官方大佬写的js sdk)App权限判断和提示

引入js sdk插件(vue3版本需要转换为export function,vue2版本直接按照官方大佬的直接使用即可)

import {requestAndroidPermission,gotoAppPermissionSetting} from '@/js_sdk/wa-permission/permission.js'

2、创建live-pusher实例,根据业务需求写业务逻辑(注意:一定要用nvue页面哈,cover-image覆盖一个矢量图在直播流画面上)。

ilve-pusher详细参数说明具体看live-pusher官方文档

<template><view class="container"><live-pusher id='livePusher' ref="livePusher" class="livePusher" url="" mode="FHD" :muted="true":enable-camera="true" :auto-focus="true" :beauty="2" whiteness="2" aspect="9:16" local-mirror="disable"@statechange="statechange" @error="error" @netstatus="netstatus" :style="[{width:'400rpx',height: '400rpx',marginLeft: '175rpx',marginTop:'20rpx',}]"></live-pusher><cover-image style="width: 400rpx;height: 400rpx;transform: scale(1.01);position: absolute;left: 175rpx;top: 190.5rpx;" src="@/static/circleBg.png" /></view>
</template>
<script>
export default{onReady() {//需要在onReady页面生命周期函数来写this.livePusher = uni.createLivePusherContext("livePusher", this);},
}
</script>

3、5s后抓拍最后一帧图片给服务端,人脸识别成功即登录系统,识别失败跳转认证失败页面(这里跟服务端对接采用的formdata格式上传文件流,你也可以采取转成base64

临时路径转base64

			// 定时器 几秒后自动抓拍handleSetTime() {this.timeFlag = setInterval(async () => {if (this.timeOut > 0) {this.timeOut--this.titleTips = this.countDownTimerStartTipsthis.buttonTip = `${this.countDownTimerStartBtnTips} ${this.timeOut}秒`}if (this.timeOut == 1) {this.livePusher.snapshot({success: (res) => {this.snapshotInfo = res.message}})}// 进行快照人脸认证if (this.timeOut == 0) {clearInterval(this.timeFlag);this.titleTips = this.countDownTimerZeroTipsthis.buttonTip = this.countDownTimerZeroBtnTipsuni.showLoading({title: this.countDownTimerZeroBtnTips})uni.uploadFile({url: 'http://192.168.60.2:8080/bsCheckImage/checkImg',filePath: this.snapshotInfo.tempImagePath,name: "file",success: (uploadFileRes) => {const jxStrData = JSON.parse(uploadFileRes.data)console.log(jxStrData)const resResultCode = jxStrData.codeconst resResultData = jxStrData.dataif (resResultCode !== '00000') {uni.navigateTo({url: '/pages/liveb-result/liveb-result?failResultObj=' +this.passData(jxStrData)})this.handleStop()return}if (resResultCode == '00000' && resResultData.score >= 0.8) {uni.showToast({title: this.faceSucessTips})this.buttonTip = this.faceSucessTipsthis.handleStop()return}if (resResultCode == '00000' && resResultData.score < 0.8) {const paramsData = {success: false,code: 'A9901',message: '人脸校验失败,请将人脸正对取景框内重新认证',failCode: 'A9901',faceScore: resResultData.score}uni.navigateTo({url: '/pages/liveb-result/liveb-result?failResultObj=' +this.passData(paramsData)})this.handleStop()return}},fail: (error) => {uni.hideLoading()uni.navigateTo({url: '/pages/liveb-result/liveb-result',animationType: 'zoom-out',animationDuration: 1000})this.handleStop()},complete: () => {uni.hideLoading()clearInterval(this.timeFlag) // 清除定时器,防止再次执行}});}}, 1000)},

4、人脸认证失败服务端返回状态,前端跳转认证失败页面,返回时给上个页面传递监听参数。(liveb-result.vue页面)

<template><view class="container"><view class="result-area"><view class="result-icon"><image class="result-icon-pic" src="../../static/fece_result.png"></image></view><view class="result-tips">{{failInfos.message}}</view><view class="result-button" @click="handleRetryFace">重新认证</view></view></view>
</template><script setup>import {ref} from 'vue'import {onLoad,onBackPress} from '@dcloudio/uni-app'const faceStatus = ref('')const failInfos = ref({})const failResultMsg = (() => {const data = {code: '3698',msg: '人脸认证失败'}uni.$emit('failResult', data);})onLoad((options) => {if (options.failResultObj) {const resultObj = JSON.parse(decodeURIComponent(options.failResultObj));failInfos.value = resultObj}})const handleRetryFace = (() => {console.log('handleRetryFace')failResultMsg()uni.navigateBack()})onBackPress((e) => {failResultMsg()})
</script><style lang="scss" scoped>.container {width: 750rpx;.result-area {position: absolute;top: 44%;left: 50%;transform: translate(-50%, -50%);.result-icon {display: flex;align-items: center;justify-content: center;.result-icon-pic {width: 140rpx;height: 140rpx;}}.result-tips {font-weight: 600;text-align: center;font-size: 32rpx;color: #515151;margin-top: 20rpx;margin-bottom: 60rpx;}.result-button {padding: 20rpx 100rpx;background-color: rgba(12, 75, 158, 1);border-radius: 60rpx;color: #eeeeee;}}}
</style>

人脸识别页面所有代码(liveb.nvue)

<template><view class="container"><view class="header"><view class="header-title"><text class="header-title-tips">{{titleTips}}</text><view class="header-title-carmera"><image class="header-title-img" src="../../static/change_camera.png" @click="handleChangeCrame"></image></view></view></view><live-pusher id='livePusher' ref="livePusher" class="livePusher" url="" mode="FHD" :muted="true":enable-camera="true" :auto-focus="true" :beauty="2" whiteness="2" aspect="9:16" local-mirror="disable"@statechange="statechange" @error="error" @netstatus="netstatus" :style="[{width:'400rpx',height: '400rpx',marginLeft: '175rpx',marginTop:'20rpx',}]"></live-pusher><cover-image style="width: 400rpx;height: 400rpx;transform: scale(1.01);position: absolute;left: 175rpx;top: 190.5rpx;" src="@/static/circleBg.png" /><view class="footer"><view class="footer-tips"><text class="footer-tips-first">{{footerTipsFirst}}</text><text class="footer-tips-second">{{footerTipsSecond}}</text></view><view class="footer-required"><view class="footer-required-row"><view class="row-area" v-for="(item,index) in tipList" :key="index"><image class="row-area-img" :src="item.icon"></image><text class="row-area-tip">{{item.name}}</text></view></view></view></view><!-- 手动抓拍 --><view class="start-button" :style="{marginTop:footerBtnStyle.marginTop}"><view class="button-hover":style="{width:footerBtnStyle.width,padding:footerBtnStyle.padding,borderRadius:footerBtnStyle.borderRadius,backgroundColor:footerBtnStyle.btnBackground}"@click="startFace"><text class="button-tip" :style="{fontSize:footerBtnStyle.fontSize,color:footerBtnStyle.textColor}">{{buttonTip}}</text></view></view></view>
</template><script>import {requestAndroidPermission,gotoAppPermissionSetting} from '@/js_sdk/wa-permission/permission.js'export default {name: 'sevenq-faceLiver',props: {//是否默认开启抓拍isDeaultStartLive: {type: Boolean,default: false},//默认开启的话需要设置延迟时间(毫秒级)defaultStartDelayTime: {type: Number,default: 600},//是否需要监听结果页传递的事件needListenResultPage: {type: Boolean,default: true},//是否开启可以翻转摄像头isCanChangeCarame: {type: Boolean,default: true},//抓拍倒计时 (如果默认开启需要+1)snapCountdownTimer: {type: Number,default: 6},//如果不允许翻转摄像头 提示词notAllowChangeCarameMsg: {type: String,default: "刷脸认证仅支持前置摄像头"},//顶部提示词topTitleTips: {type: String,default: "请把人脸放在圆圈内拍摄脸部,开始人脸识别"},//提示词 1 footerTipsFirst: {type: String,default: "确认为您本人照片"},//提示词 2footerTipsSecond: {type: String,default: "保持正脸在取景框中系统将在5s后自动抓拍"},//提示展示列表tipList: {type: Object,default: [{icon: "../../static/img3.png",name: '正对手机'},{icon: "../../static/img2.png",name: '光线充足'},{icon: "../../static/img1.png",name: '脸无遮挡'},]},//抓拍倒计时开始时提示词countDownTimerStartTips: {type: String,default: "请保存人脸在实景框中,正在进行抓拍"},//抓拍倒计时为0时提示词countDownTimerZeroTips: {type: String,default: "正在人脸认证中,请稍等..."},//按钮默认文本buttonTips: {type: String,default: "开始人脸识别"},//抓拍倒计时开始时按钮显示提示词countDownTimerStartBtnTips: {type: String,default: "正在抓拍中"},//抓拍倒计时为0时按钮提示词countDownTimerZeroBtnTips: {type: String,default: "人脸认证中...."},//认证成功按钮提示词faceSucessTips: {type: String,default: "认证成功"},//权限提示开启提示词premissonTips: {type: String,default: "当前应用需要使用相机权限进行拍照,但相机权限暂未开启。是否前往应用设置打开相机权限?"},//底部按钮样式footerBtnStyle: {type: Object,default: {marginTop: '120rpx',width: '480rpx',padding: '24rpx',btnBackground: 'rgba(12, 75, 158, 1)',borderRadius: '300rpx',textColor: '#dfdfdf',fontSize: '32rpx'}}},data() {return {titleTips: this.topTitleTips,buttonTip: this.buttonTips,livePusher: '', // livePusher实例snapshotInfo: '', // 快照信息showCountDown: false, // 拍摄倒计时timeOut: this.snapCountdownTimer, // 签到倒计时timeFlag: null, // 定时器isPass: null, // 是否通过人脸认证phoneSysInfos: {}, //当前手机系统信息}},onReady() {this.livePusher = uni.createLivePusherContext("livePusher", this);},onShow() {//监听结果页面传递的失败事件if (this.needListenResultPage) {uni.$on('failResult', (resultData) => {if (resultData.code == '3698') {clearInterval(this.timeFlag)this.resertAll()this.livePusher.startPreview()}});}},async mounted() {const that_ = thisif (!that_.showCountDown) {setTimeout(function() {that_.getCarmeraPremisson()}, this.defaultStartDelayTime)}that_.getPhoneSys()},onUnload() {if (this.needListenResultPage) {uni.$off('failResult');}clearInterval(this.timeFlag)uni.hideLoading();},onHide() {console.log('页面隐藏')},methods: {//校验是否获取相机权限async getCarmeraPremisson() {const currentSystem = uni.getSystemInfoSync().platformif (currentSystem == 'android') {const result = await requestAndroidPermission("android.permission.CAMERA")if (result == 1) {if (this.isDeaultStartLive) { //如果打开页面就进行抓拍this.showCountDown = true}this.startPreview()} else {uni.showModal({title: '提示',content: this.premissonTips,confirmText: '去设置',cancelText: '取消',success: function(res) {if (res.confirm) {gotoAppPermissionSetting()} else if (res.cancel) {uni.showToast({icon: 'error',title: '暂无相机权限'})}}});}}},//重置初始化值 需要在认证失败时候再次调用resertAll() {this.titleTips = this.topTitleTipsthis.buttonTip = this.buttonTipsthis.snapshotInfo = '' // 快照信息this.showCountDown = false // 拍摄倒计时this.timeOut = this.snapCountdownTimer // 签到倒计时this.timeFlag = null // 定时器this.isPass = null // 是否通过人脸认证},//手动翻转摄像头handleChangeCrame() {if (!this.isCanChangeCarame) {uni.showToast({icon: 'none',title: this.notAllowChangeCarameMsg})return}this.livePusher.switchCamera({success: (a) => {uni.showToast({icon: 'none',title: '摄像头翻转成功'})}});},//手动开始人脸识别startFace() {const that_ = thisif (!that_.showCountDown) {that_.showCountDown = trueif (that_.showCountDown) {const {platform,osVersion} = that_.phoneSysInfosif (platform == 'android' && osVersion < 10) { //判断兼容安卓10以下效果that_.startPreview()return}that_.handleSetTime()}}},// 开始预览直播流startPreview() {const _that = thisthis.livePusher.startPreview({success: (res) => {if (_that.showCountDown) {_that.handleSetTime()}}})},// 定时器 几秒后自动抓拍handleSetTime() {this.timeFlag = setInterval(async () => {if (this.timeOut > 0) {this.timeOut--this.titleTips = this.countDownTimerStartTipsthis.buttonTip = `${this.countDownTimerStartBtnTips} ${this.timeOut}秒`}if (this.timeOut == 1) {this.livePusher.snapshot({success: (res) => {this.snapshotInfo = res.message}})}// 进行快照人脸认证if (this.timeOut == 0) {clearInterval(this.timeFlag);this.titleTips = this.countDownTimerZeroTipsthis.buttonTip = this.countDownTimerZeroBtnTipsuni.showLoading({title: this.countDownTimerZeroBtnTips})// this.$emit(handleStartFaceApi, {// 	code: '4364',// 	msg: '开始人脸与服务端进行人脸',// 	currentTempImagePath: this.snapshotInfo.tempImagePath// })uni.uploadFile({url: 'http://192.168.60.2:8080/bsCheckImage/checkImg',filePath: this.snapshotInfo.tempImagePath,name: "file",success: (uploadFileRes) => {const jxStrData = JSON.parse(uploadFileRes.data)console.log(jxStrData)const resResultCode = jxStrData.codeconst resResultData = jxStrData.dataif (resResultCode !== '00000') {uni.navigateTo({url: '/pages/liveb-result/liveb-result?failResultObj=' +this.passData(jxStrData)})this.handleStop()return}if (resResultCode == '00000' && resResultData.score >= 0.8) {uni.showToast({title: this.faceSucessTips})this.buttonTip = this.faceSucessTipsthis.handleStop()return}if (resResultCode == '00000' && resResultData.score < 0.8) {const paramsData = {success: false,code: 'A9901',message: '人脸校验失败,请将人脸正对取景框内重新认证',failCode: 'A9901',faceScore: resResultData.score}uni.navigateTo({url: '/pages/liveb-result/liveb-result?failResultObj=' +this.passData(paramsData)})this.handleStop()return}},fail: (error) => {uni.hideLoading()uni.navigateTo({url: '/pages/liveb-result/liveb-result',animationType: 'zoom-out',animationDuration: 1000})this.handleStop()},complete: () => {uni.hideLoading()clearInterval(this.timeFlag) // 清除定时器,防止再次执行}});}}, 1000)},//向下个页面传递参数passData(obj) {let passDataStr = JSON.stringify(obj)let newPassDataStr = passDataStr.replace(/%/g, '%25');return encodeURIComponent(newPassDataStr);},//抛出停止推流 在调用成功与失败都得调用handleStop() {this.livePusher.stop()},//监听直播流状态变化statechange(val) {console.log(val, '监听直播流变化')},//监听直播流警告error(err) {console.log(err, '监听直播流警告')},//监听网络状态netstatus(status) {console.log(status, '监听直播流网络状态')},//获取手机型号getPhoneSys() {const system = uni.getDeviceInfo()this.phoneSysInfos = system}}}
</script><style lang="scss" scoped>.container {width: 750rpx;}
</style>

效果图如下所示

注意:代码仅可自己使用,不可进行二次转载哈,有问题在请私信我哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/65359.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker【初识Docker】

目录 为什么会出现Docker这门技术喃&#xff1f; 应用开发和部署的困境 容器技术的先兆 Docker 的出现&#xff1a;简化容器化 Docker 技术的关键创新&#xff1a; Docker 的广泛应用和变革 什么是 Docker&#xff1f; Docker的历史 早期背景&#xff1a;容器化和虚拟化…

探寻 OneCode 核心优势:MVVM 进阶与前后端协同之魅

在当今的软件开发领域&#xff0c;高效、可维护且功能强大的架构是开发者们不懈追求的目标。OneCode 凭借其独特的增强版 MVVM 架构、前后端一体化特性&#xff0c;以及创新的技术如 OneCode DSM&#xff08;Domain-Specific Modeling&#xff0c;领域特定建模&#xff09;、视…

网络安全检测

实验目的与要求 (1) 帮助学生掌握木马和入侵的防护和检测方法、提高学习能力、应用能力和解决实际问题的能力。 (2) 要求学生掌握方法, 学会应用软件的安装和使用方法, 并能将应用结果展示出来。 实验原理与内容 入侵检测是通过对计算机网络或计算机系统中若干关键点收集信…

24.12.27 SpringMVCDay02

enctype必须是 multipart/form-data <form action"/mvc/book/doUpload1" method"post" enctype"multipart/form-data"><p><input type"file" name"img_url"></p><p><input type"subm…

Maven核心概念总结

Maven 介绍 Maven 官方文档是这样介绍的 Maven 的&#xff1a; Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a projects build, reporting and documentation from a ce…

Unity 读Excel,读取xlsx文件解决方案

Unity读取表格数据 效果&#xff1a; 思路&#xff1a; Unity可以解析Json&#xff0c;但是读取Excel需要插件的帮助&#xff0c;那就把这个功能分离开&#xff0c;读表插件就只管读表转Json&#xff0c;Unity就只管Json解析&#xff0c;中间需要一个存储空间&#xff0c;使用…

PyQt + OpenCV综合训练

一、PyQt OpenCV 图像处理应用设计 创建一个 PyQt 应用程序&#xff0c;该应用程序能够&#xff1a; ①使用 OpenCV 加载一张图像。 ②在 PyQt 的窗口中显示这张图像。 ③提供四个按钮&#xff08;QPushButton&#xff09;&#xff1a; - 一个用于将图像转换为灰度图 - …

React中最优雅的异步请求

给大家分享在React19中使用useSuspense处理异步请求为什么是被认为最优雅的解决方案 一. 传统方案 解决异步请求的方案中&#xff0c;我们要处理至少两个最基本的逻辑 正常的数据显示数据加载的UI状态 例如&#xff1a; export default function Index(){const [content, …

数据库高安全—openGauss安全整体架构安全认证

openGauss作为新一代自治安全数据库&#xff0c;提供了丰富的数据库基础安全能力&#xff0c;并逐步完善各类高阶安全能力。这些安全能力涵盖了访问登录认证、用户权限管理、审计与追溯及数据安全隐私保护等。本章节将围绕openGauss安全机制进行源码解读&#xff0c;以帮助数据…

linux-23 文件管理(一)创建文件touch,stat

那接下来看看文件的创建和删除&#xff0c;那我们怎么去创建一个文件&#xff1f;各种方式都能实现&#xff0c;当然&#xff0c;这里先说一说&#xff0c;就像mkdir创建空目录一样&#xff0c;我们如何创建一个空文件&#xff1f;创建空文件其实很简单&#xff0c;有一个命令歪…

Linux 基本指令

目录 1.常见指令 1.1 ls指令 1.2 pwd指令 1.3 cd指令 1.4 touch指令 1.5 mkdir指令 1.6 rm和rmdir指令 1.7 man指令 1.8 cp指令 1.9 mv指令 ​编辑 1.10 cat指令 1.11 more指令 1.12 less指令 1.13 head指令 1.14.tail指令 1.15 时间相关的指令 1.16 cal…

金蝶V10中间件的使用

目录 环境准备搭建过程配置修改应用部署 环境准备 Linux内核服务器JDK1.8安装包&#xff1a;AAS-V10.zip程序包&#xff1a;***.war 搭建过程 将安装包上传至服务器opt目录下&#xff0c;官方给定的默认服务主目录为“/opt/AAS-V10/ApusicAS/aas/”&#xff1b;解压安装包(解…

前端开发 -- 自动回复机器人【附完整源码】

一&#xff1a;效果展示 本项目实现了一个简单的网页聊天界面&#xff0c;用户可以在输入框中输入消息&#xff0c;并点击发送按钮或按下回车键来发送消息。机器人会根据用户发送的消息内容&#xff0c;通过关键字匹配来生成自动回复。 二&#xff1a;源代码分享 <!DOCTYP…

Python数据可视化小项目

英雄联盟S14世界赛选手数据可视化 由于本学期有一门数据可视化课程&#xff0c;课程结课作业要求完成一个数据可视化的小Demo&#xff0c;于是便有了这个小项目&#xff0c;课程老师要求比较简单&#xff0c;只要求熟练运用可视化工具展示数据&#xff0c;并不要求数据来源&am…

Linux系统编程——详解页表

目录 一、前言 二、深入理解页表 三、页表的实际组成 四、总结&#xff1a; 一、前言 页表是我们之前在讲到程序地址空间的时候说到的&#xff0c;它是物理内存到进程程序地址空间的一个桥梁&#xff0c;通过它物理内存的数据和代码才能映射到进程的程序地址空间中&#xff…

【Java数据结构】LinkedList与链表

认识LinkedList LinkedList就是一个链表&#xff0c;它也是实现List接口的一个类。LinkedList就是通过next引用将所有的结点链接起来&#xff0c;所以不需要数组。LinkedList也是以泛型的方法实现的&#xff0c;所以使用这个类都需要实例化对象。 链表分为很多种&#xff0c;比…

《一文读懂卷积网络CNN:原理、模型与应用全解析》

《一文读懂卷积网络CNN&#xff1a;原理、模型与应用全解析》 一、CNN 基本原理大揭秘&#xff08;一&#xff09;从人类视觉到 CNN 灵感&#xff08;二&#xff09;核心组件详解 二、经典 CNN 模型巡礼&#xff08;一&#xff09;LeNet-5&#xff1a;开山鼻祖&#xff08;二&a…

教育元宇宙的优势与核心功能解析

随着科技的飞速发展&#xff0c;教育领域正迎来一场前所未有的变革。教育元宇宙作为新兴的教育形态&#xff0c;以其独特的优势和丰富的功能&#xff0c;正在逐步改变我们的学习方式。本文将深入探讨教育元宇宙的优势以及其核心功能&#xff0c;为您揭示这一未来教育的新趋势。…

openGauss与GaussDB系统架构对比

openGauss与GaussDB系统架构对比 系统架构对比openGauss架构GaussDB架构 GaussDB集群管理组件 系统架构对比 openGauss架构 openGauss是集中式数据库系统&#xff0c;业务数据存储在单个物理节点上&#xff0c;数据访问任务被推送到服务节点执行&#xff0c;通过服务器的高并…

idea 8年使用整理

文章目录 前言idea 8年使用整理1. 覆盖application配置2. 启动的时候设置编辑空间大小&#xff0c;并忽略最大空间3. 查询类的关系4. 查看这个方法的引用关系5. 查看方法的调用关系5.1. 查看被调用关系5.2. 查看调用关系 6. 方法分隔线7. 选择快捷键类型8. 代码预览插件9. JReb…