草庐IT

微信小程序人脸识别功能(wx.faceDetect)、带扫脸动画、人脸图片获取(upng.js)及位置展示

thj13896076523 2023-04-21 原文

目录

1660033284021056

1. 微信小程序人脸识别

方案已废弃: 用到的第三方框架: upng.js及pako.js(速度太慢,容易卡顿)
推荐使用:微信小程序人脸识别改进版

1. 初始化人脸识别

	wx.initFaceDetect()

2. 创建 camera 上下文 CameraContext 对象

	this.cameraEngine = wx.createCameraContext();

3.获取 Camera 实时帧数据

	const listener = this.cameraEngine.onCameraFrame()

4.人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据

	wx.faceDetect()

5.完整代码

<template>
	<view>
		<view class="d-flex justify-content-between align-items-center update-box mt-40 mr-32">
			<!-- 可用iconfont -->
			<image @click="devicePosition=!devicePosition" class="camera-change-image" mode="widthFix"
				src="@/static/images/camera_change.png"></image>
		</view>
		<view class="head-image-box w-100 text-center position-relative">
			<!-- resolution:获取人脸图片后的清晰度 low:低 -->
			<camera v-if='isAuthCamera' :device-position="devicePosition ?'back': 'front'" class="camera" flash="off"
				resolution='low' />

			<view class="title mt-40" v-show="!tempImg && tipsText">{{ tipsText }}</view>
			<cover-view class="cover-box" v-show="isShow">
				<cover-image class="image-box" src="@/static/images/camera_verify.png"></cover-image>

				<!-- cover-view 不支持动画所以只能变通的形式实现 -->
				<cover-image :style="'transform: translateY('+translateY+'rpx);'" class="line"
					src="@/static/images/bg_line.png"></cover-image>
				<!-- <cover-view class="line"></cover-view> -->
			</cover-view>
		</view>
	</view>
</template>

<script>
	import upng from "@/common/js/upng.js"

	export default {
		name: 'index',
		data() {
			return {
				isShow: false,
				tipsText: '', // 错误文案提示
				tempImg: '', // 本地图片路径
				cameraEngine: null, // 相机引擎
				devicePosition: true, // 摄像头朝向
				isAuthCamera: true, // 是否拥有相机权限
				isVerify: false,
				translateY: -24,
				timer: null,
				isFlag: true,
				origin: null
			}
		},
		onShow: function() {
			this.isVerify = false;
			this.tipsText = "";
			this.isFlag = true;
		},
		onLoad(options) {
			this.origin = options.origin;

			this.initData();
			this.lineAnimation()
		},
		onUnload: function() {
			clearInterval(this.timer);
			this.timer = null;
			this.isFlag = false;
		},
		methods: {
			// 初始化相机引擎
			initData() {
				// #ifdef MP-WEIXIN
				// 1、初始化人脸识别
				wx.initFaceDetect()
				// 2、创建 camera 上下文 CameraContext 对象
				this.cameraEngine = wx.createCameraContext();
				this.isShow = true;
				// 3、获取 Camera 实时帧数据
				const listener = this.cameraEngine.onCameraFrame((frame) => {
					if (this.tempImg) {
						return;
					}
					// 4、人脸识别,使用前需要通过 wx.initFaceDetect 进行一次初始化,推荐使用相机接口返回的帧数据
					wx.faceDetect({
						frameBuffer: frame.data,
						width: frame.width,
						height: frame.height,
						enablePoint: true,
						enableConf: true,
						enableAngle: true,
						enableMultiFace: true,
						success: (faceData) => {

							if (this.isVerify) return
							let face = faceData.faceInfo[0]
							if (face.x == -1 || face.y == -1) {
								this.tipsText = '检测不到人'
							}
							if (faceData.faceInfo.length > 1) {
								this.tipsText = '请保证只有一个人'
							} else {
								const {
									pitch,
									roll,
									yaw
								} = face.angleArray;
								const standard = 0.5
								if (Math.abs(pitch) >= standard || Math.abs(roll) >= standard ||
									Math.abs(yaw) >= standard) {
									this.tipsText = '请平视摄像头'
								} else if (face.confArray.global <= 0.8 || face.confArray.leftEye <=
									0.8 || face.confArray.mouth <= 0.8 || face.confArray.nose <= 0.8 ||
									face.confArray.rightEye <= 0.8) {
									this.tipsText = '请勿遮挡五官'
								} else {
									this.tipsText = '校验中...'
									this.isVerify = true;
									
									// 太快获取的人脸可能比较抽象,给用户一个准备时间
									setTimeout(() => {
										//借助upng把流转为二进制数据
										let pngData = upng.encode([frame.data], frame.width,
												frame.height),
											base64 = arrayBufferToBase64(pngData);
											
										// 获取到base64后可以已上传,展示,或保存到本地
										
										var imageType = getImageType();

										function getImageType() {
											let type = "center"
											if (face.y / frame.height < 0.3333) {
												type = "top"
											} else if (face.y / frame.height > 0.6666) {
												type = "bottom"
											}
											return type;
										}

										//二进制转base64
										function arrayBufferToBase64(buffer) {
											var binary = '';
											var bytes = new Uint8Array(buffer);
											var len = bytes.byteLength;
											for (var i = 0; i < len; i++) {
												// unicode编码还原
												binary += String.fromCharCode(bytes[i]);
											}
											return "data:image/png;base64," + btoa(binary);
										}

										// 小程序没有window对象,需手动实现btoa()方法
										// 作用: 字符转base64
										function btoa(string) {
											const b64 =
												"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
											string = String(string);
											var bitmap, a, b, c,
												result = "",
												i = 0,
												rest = string.length %
												3; // To determine the final padding

											for (; i < string.length;) {
												if ((a = string.charCodeAt(i++)) > 255 ||
													(b = string.charCodeAt(i++)) > 255 ||
													(c = string.charCodeAt(i++)) > 255)
													throw new TypeError(
														"Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."
													);

												bitmap = (a << 16) | (b << 8) | c;
												result += b64.charAt(bitmap >> 18 & 63) + b64
													.charAt(bitmap >> 12 & 63) +
													b64.charAt(bitmap >> 6 & 63) + b64.charAt(
														bitmap & 63);
											}

											return rest ? result.slice(0, rest - 3) + "==="
												.substring(rest) : result;
										};

										this.tipsText = '校验成功'
										setTimeout(() => {
											this.clickPushDetail(imageType);
										}, 50)
									}, 300)
								}
							}
						},
						fail: (err) => {
							if (this.isVerify) return
							if (err.x == -1 || err.y == -1) {
								this.tipsText = '检测不到人'
							} else {
								this.tipsText = err.errMsg || '网络错误,请退出页面重试'
							}
						},
					})
				})
				// 5、开始监听帧数据
				listener.start()
				// #endif
			},

			clickPushDetail(imageType) {
				if (!this.isFlag) return
				// 可以跳转到详情页面
			},
			lineAnimation() {
				if (this.timer) return
				this.timer = setInterval(() => {
					this.translateY += 3;
					if (this.translateY >= 460) {
						this.translateY = 10;
					}
				}, 12)
			},
		}
	}
</script>

<style>
	page {
		background-color: #000000;
	}
</style>
<style lang="scss" scoped>
	.d-flex {
	  display: flex;
	}
	
	.justify-content-center {
	  justify-content: center;
	}
	
	.align-items-center {
	  align-items: center;
	}
	
	
	.camera-change-image {
		width: 48rpx;
		margin-left: 40rpx;
	}
	
	.mr-32{
		margin-right: 32rpx;
	}
	
	.mt-40 {
	  margin-top: 40rpx;
	}
	
	.text-center {
	  text-align: center;
	}
	
	.position-relative{
		position: relative;
	}
	
	.w-100{
		width: 100%;
	}

	.update-box {
		color: #ffffff;
	}

	.head-image-box {
		position: absolute;
		top: 10vh;
		color: white;

		.camera {
			width: 100%;
			height: 872rpx;
		}

		.title {
			font-size: 48rpx;
			font-weight: bold;
		}

		.cover-box {
			position: absolute;
			top: 40%;
			left: 50%;
			transform: translate(-50%, -50%);
			width: 500rpx;
			height: 500rpx;
		}

		.image-box {
			width: 100%;
			height: 100%;
		}

		.line {
			position: absolute;
			top: 0rpx;
			left: 8rpx;
			right: 8rpx;
			width: auto;
			height: 30rpx;
			// transform: translateY(-24rpx);
			// animation: radar-beam 1.5s infinite;
			// background: linear-gradient(180deg, rgba(13, 110, 217, 0) 0%, rgba(13, 110, 217, 0.4000) 100%);
			z-index: 2;
		}

		// @keyframes radar-beam {
		// 	0% {
		// 		transform: translateY(24rpx);
		// 	}

		// 	100% {
		// 		transform: translateY(450rpx);
		// 	}
		// }

	}
</style>

附带绝对定位的圆角图片真机展示问题

父元素添加:-webkit-backface-visibility: hidden;
-webkit-transform: translate3d(0, 0, 0);

.image-box {
				overflow: hidden;
				border-radius: 50%;
				-webkit-backface-visibility: hidden;
				-webkit-transform: translate3d(0, 0, 0);
				z-index: 1;

				.face-image {
					width: 100%;
					height: 100%;
					position: absolute;
					left: 0;

					&.top {
						top: 0;
					}

					&.bottom {
						bottom: 0;
					}

					&.center {
						top: 50%;
						transform: translateY(-50%);
					}
				}
			}

借鉴于:ZegTsai

2. H5人脸识别

详见:h5人脸识别

有关微信小程序人脸识别功能(wx.faceDetect)、带扫脸动画、人脸图片获取(upng.js)及位置展示的更多相关文章

  1. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  2. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  3. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  4. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  5. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. ruby - 没有类方法获取 Ruby 类名 - 2

    如何在Ruby中获取BasicObject实例的类名?例如,假设我有这个:classMyObjectSystem我怎样才能使这段代码成功?编辑:我发现Object的实例方法class被定义为returnrb_class_real(CLASS_OF(obj));。有什么方法可以从Ruby中使用它? 最佳答案 我花了一些时间研究irb并想出了这个:classBasicObjectdefclassklass=class这将为任何从BasicObject继承的对象提供一个#class您可以调用的方法。编辑评论中要求的进一步解释:假设你有对象

  8. ruby-on-rails - 如何在 Gem 中获取 Rails 应用程序的根目录 - 2

    是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在

  9. ruby - 如何使用 CarrierWave 从 S3 获取真实文件 - 2

    我有一个应用程序可以读取文件的内容并为其编制索引。我将它们存储在磁盘本身中,但现在我使用的是AmazonS3,因此以下方法不再适用。事情是这样的:defperform(docId)@document=Document.find(docId)if@document.file?#Youshould'tcreateanewversion@document.versionlessdo|doc|@document.file_content=Cloudoc::Extractor.new.extract(@document.file.file)@document.saveendendend@docu

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐