Bootstrap

uniapp + vue3微信小程序开发(2)活体人脸识别

最新提示:微信小程序上线前审核,要求你必须使用微信自带活体识别的sdk,自己开发的不会被允许通过,望周知!

这相信有很多小程序都会用,为啥呢?因为小程序压根不得给你用户身份证信息,所以替换做法就是你让用户上传身份证照片,然后你在人脸识别,判断是同一个人后,再将你信息录入数据库

1、在小程序端的活体识别方法

1、uniapp camera组件拍照 takePhoto

2、uniapp camera组件录像 startRecord

3、uniapp live-pusher组件 视频推流方式

2、拍照方式实现人脸识别

因为项目要得紧,所以我这里使用最简单的拍照,拍八张,定时器每秒一张(后端用的是百度人脸识别api,有照片活体识别和视频活体识别),然后一起传给后端,不过更好的做法是拍一张传一张,成功则完成,识别失败那就再调拍照api,总的时间不超过十秒,在用户端看来是没有任何异常的。

<template>
	<view class="face-detection">
		<view class="live-father">
			<view v-if="status==-1" class="camera-box">
				<view class="camera-tip">
					<image class="controls-play" src="../../static/img/own/face.png" mode=""></image>
				</view>
			</view>
			<camera v-else class="camera-box" device-position="front" flash="off" @initdone="initdone" @stop="stop"
				@error="error">
				<cover-view class="camera-tip">
					<cover-image class="controls-play" src="../../static/img/own/face.png"></cover-image>
				</cover-view>
			</camera>
		</view>
		<button class="bottom" type="primary" :disabled="status==1"  @click="startFace">{{statusFilter()}}</button>
	</view>
</template>

<script setup>
	import {
		ref,
		reactive,
		computed,
		getCurrentInstance,
		onBeforeUnmount,
		watch
	} from 'vue'
	import {
		onLoad,
		onReady,
		onHide,
		onError
	} from '@dcloudio/uni-app'
	import { pullAuth } from '@/utils'
	const {
		proxy
	} = getCurrentInstance()
	const context = ref(null)
	onHide(() => {
		photoStatus(3)
	})
	onError(() => {
		photoStatus(3)
	})
	onBeforeUnmount(() => {
		console.log(1);
		photoStatus()
	})
	const flag = ref(0)
	const time = ref(0) // 重新调用接口限制为10次
	const ctx = ref(null) // 相机对象
	const realUser = reactive({
		name: '',
		idNumber: ''
	})
	/**
	 * 相机初始化完成
	 */
	const initdone = () => {
		console.log(2);
		photoStatus()
	}
	const startFace = () => {
		pullAuth('scope.camera', firstSuccess, firstFail)
	}
	/**
	 * 相机状态回调
	 */
	const photoStatus = (type = 0) => {
		status.value = type
	}
	const status = ref(-1) // -1 => 相机未就绪,申请摄像头权限, 0 => 相机已就绪,但未开始, 1 => 进行中, 2 => 已结束, 3 => 重新识别
	const statusFilter = () => {
		switch (status.value) {
			case -1:
				return '开始识别'
			case 0:
				return '开始识别'
			case 1:
				return '正在识别中...'
			case 2:
				return '识别完成'
			case 3:
				return '重新识别'
			default:
				return '开始识别'
		}
	}
	/**
	 * 拍照
	 */
	const takePhoto = () => {
		time.value++
		photoStatus(1)
		ctx.value.takePhoto({
			quality: 'high',
			success: (result) => {
				proxy.$api.user.faceRecognition({
					file: result.tempImagePath,
					userName: realUser.name,
					userNo: realUser.idNumber
				}).then(res => {
					if (res.code == 200) {
						uploadId()
					}
				}).catch(e => {
					flag.value = Date.now()
				})
			},
			fail: () => {
				console.log('fail');
			}
		});
	}
	watch(flag, val => {
		if (time.value >= 10) {
			uni.showToast({
				title: '人脸识别失败,请重试',
				icon: 'error'
			})
			photoStatus(3)
			time.value = 0
		} else {
			takePhoto()
		}
	})
	const uploadId = () => {
		proxy.$api.user.updateUser({
			userName: realUser.name,
			userNo: realUser.idNumber,
			editType: 1,
			id: uni.getStorageSync('id')
		}).then(res => {
			if (res.code == 200) {
				uni.setStorageSync('idCard', 1)
				uni.navigateTo({
					url: './userInfo'
				})
			}
		}).catch(e => {
		})
	}
	/**
	 * 摄像头在非正常终止时触发,如退出后台等情况
	 */
	const stop = (e) => {
		photoStatus(3)
	}
	/**
	 * 用户不允许使用摄像头时触发
	 */
	const error = (e) => {
		console.log(3);
		photoStatus()
	}
	const firstSuccess = () => {
		uni.showLoading({
			title: '初始化...'
		})
		photoStatus()
		if (!ctx.value) {
			ctx.value = uni.createCameraContext();
		}
		setTimeout(() => {
			uni.hideLoading()
			takePhoto()
		}, 500)
	}
	const firstFail = () => {
		photoStatus(-1)
	}
	onLoad((options) => {
		realUser.name = options.name
		realUser.idNumber = options.idNumber
	})
</script>

<style lang="scss" scoped>
	.face-detection {
		background-color: #F3F3F3;
		height: 100vh;
		box-sizing: border-box;
		padding: 30rpx;
		position: relative;

		.live-father {
			width: 600rpx;
			height: 600rpx;
			border-radius: 50%;
			margin: 100rpx auto;
			.camera-box {
				width: 100%;
				height: 100%;
				border-radius: 50%;
				position: relative;
				background-image: linear-gradient(to right, #acb6e5, #86fde8);
				.camera-tip {
					text-align: center;
					line-height: 600rpx;
					font-size: 70rpx;
					font-weight: bold;
					color: #fff;
					.controls-play{
						width: 390rpx;
						height: 480rpx;
						position: absolute;
						top: 50%;
						left: 50%;
						z-index: 99;
						transform: translate(-50%, -50%);
					}
				}
			}
		}

		.bottom {
			margin: 60rpx auto 0;
			width: 690rpx;
			height: 98rpx;
			background-color: #CA2915;
			border-radius: 16rpx;
			font-size: 34rpx;
			font-weight: 400;
			color: #FFFFFF;
			line-height: 98rpx;
			text-align: center;
		}
	}
</style>

;