小程序人脸动态识别摇头点头采坑建议

前言

  应需求,在小程序内部弄一个人脸动态活体扫描,检测一下摇摇头,点点头之类的。但是在网上的相关信息却没有,偶尔有几个思路,所以就在这里记录下自己的探究历程

说明

  首先说明的是这篇文章采用的是百度云的人脸识别,与腾讯的人脸识别不同的是,百度人脸识别的api还提供三维旋转的角度检测,这样对于实现检测人脸识别摇头和点头是非常简单的。

百度云人脸识别接口说明

探究

实现思路

  创造一个定时器,在定时器里面使用拍照之后使用人脸识别接口,这样就变成动态检测了。

定时器

setTimeout和setInterval区别及选择

  setTimeout和setInterval都可以用作定时器,但是我们得先大致区分一下两者的部分区别:

  setTimeout会保证在指定好的延时时间后执行,但是setInterval则不会这样。 如果function中的代码有耗时载操作,那么使用setTimeout方法递归,则可能会增加总递归的时间。

  而使用setInterval方法,如果程序中耗时比延时间隔长,则会立刻回调函数。( 更多关于setInterval计时不准确可以点击这里了解。)

  因为setInterval计时并不准确,同时我们定时器同时存在耗时操作(调用百度云接口),所以我们这里就采用setTimeout +递归方式来实现定时。

定时器代码

every_camera_upload: function(acstoken) { //定时拍摄照片 let that = this; timer = setTimeout(function() {//设置定时器,并赋给全局变量timer //that.camera().then(resx => {//循环拍照 // that.uploadBaiDuPicture(acstoken).then(rx =>{//上传百度云接口 // that.judge_face(rx);//获取返回的json数据并分析 // }); that.every_camera_upload(that.data.getAccessToken); //方法中调用定时器实现循环 // }); }, 1500); } //clearTimeout(timer); //此方法不能放在定时器方法内部, //用于清除新一轮循环中函数还未运行时清除定时器, //也不能放在every_camera_upload()方法内部,应为要重复调用,应放于其他方法内部。

  解释一下代码,在方法内部,设置一个setTimeout赋给全局变量,在定时器内部调用camer方法,之后则重复调用every_camera_upload()方法实现递归。clearTimeout()的作用用于清除循环,必须在其他方法中使用。

实现功能过程

获取accessToken

  阅读百度云人脸识别接口得知,先获取access_token进行身份验证,但我们不能将密钥密匙放在客户端上防止别人抓包逆工程获取到,此时可以将其放在服务器或者使用云开发的云函数上(博主使用的云函数)

云函数代码

// 云函数入口文件 const cloud = require('wx-server-sdk'); cloud.init(); // 云函数入口函数 var getAccessToken = function () { //人脸识别API var https = require('https'); var qs = require('querystring'); const param = qs.stringify({ 'grant_type': 'client_credentials', 'client_id': '你的 Api Key', 'client_secret': '你的 Secret Key' }); var access_token; return new Promise((resolve,reject) => { let body =[]; https.get( { hostname: 'aip.baidubce.com', path: '/oauth/2.0/token?' + param, agent: false }, function (res) { res.on('data',(chunk) => { body.push(chunk); }); res.on('end', () =>{ let data = Buffer.concat(body).toString(); let getData = JSON.parse(data); access_token = getData.access_token; console.log(access_token); resolve(access_token); }); } ); }).then(res =>{ return access_token; }); } exports.main = (event, context) => { let datas = getAccessToken(); return datas; }

小程序端代码

getAccessToken: function() { //获取accesstoken var x = new Promise((resolve, reject) => { wx.cloud.callFunction({ name: 'getBaiDuAccessToken', data: {}, success: resy => { console.log(resy); console.log(resy.result); resolve(resy.result); }, fail: resy => { wx.showToast({ title: 'accesstoken报错', icon: 'none', duration: 2000 }); } }); }); return x; }

开始定时循环拍摄

  获取到access_token后,就可以在小程序循环拍摄上传了,但注意的是access_token最好放在服务器上让小程序将照片发送到后端让后端发送请求接口,但考虑到诸多性能问题,博主就将其放在小程序端上。

循环拍照

every_camera_upload: function(acstoken) { //定时拍摄照片 let that = this; timer = setTimeout(function() { that.camera().then(resx => { that.uploadBaiDuPicture(acstoken).then(rx =>{ that.judge_face(rx); }); that.every_camera_upload(that.data.getAccessToken); //方法中调用定时器实现循环 }); }, 1500);

发送请求

  发送请求最好由服务器来做,服务器解析之后返回给小程序端,博主将其放在小程序端。

uploadBaiDuPicture: function(restoken) { //上传百度云照片并解析 var base64 = wx.getFileSystemManager().readFileSync(this.data.tempImagePath, 'base64'); let data = [{ //百度云的锅,在json格式外需要外加一个[] image: base64, image_type: 'BASE64' }]; return new Promise((resolve, reject) => { wx.request({ url: 'https://aip.baidubce.com/rest/2.0/face/v3/faceverify?access_token=' + restoken, data: data, // dataType: "json", method: 'POST', header: { 'Content-Type': 'application/json' }, success(res) { resolve(res); } }) }); }

  由于拍摄的照片是在本地上,发送请求就必须将本地照片进行base64编码后放入data请求参数中,特别重要的一点,如果你碰上这种错误返回:

接口返回错误222200

  那八成是由于请求参数外没有加 [ ],如上代码显示,json请求格式外要 [] ,这个错误网上信息很少,解释为百度人脸识别v3版本的api表单是个list,接口开发文档也没有相关注明,所以请注意这个坑,引用:活体检测error_code":222200"

摇头点头顺序设置

judge_face: function(res) { //判断用户点头摇头动作 let that = this; if (res.data.error_code == 0) { if (this.data.prove_face_front == false){ that.judge_prove_face_front(res); }else if (that.data.judge_left_change_right_head_count < 2) { that.judge_left_change_right_head(res); //判断用户摇头 } else if(that.data.judge_down_to_up_head_count < 2){ that.judge_down_to_up_head(res); //判断用户点头 }else { wx.showLoading({ title: '上传中', }); innerAudioConext.src = "/music/upload.mp3" innerAudioConext.play(); this.uploadUserPictureProve(); clearTimeout(timer); } } else if (res.data.error_code == 222202) { wx.showToast({ title: '未识别到脸部', icon: 'none', duration: 500 }); } else { wx.showToast({ title: '未知错误', icon: 'none', duration: 500 }); }}

解析返回json文件

  获取返回的json的参数pitch,yaw,为三维旋转角度,以此判断正脸

judge_prove_face_front:function(res){ let pitch = Math.abs(res.data.result.face_list[0].angle.pitch); let yaw = Math.abs(res.data.result.face_list[0].angle.yaw); if ((pitch < 5 ) && (yaw<5)) { let font_face=this.data.tempImagePath this.setData({ prove_face_front: true, save_font_face:font_face }) } else { wx.showToast({ title: '未检测到正脸', icon: 'none', duration: 1000 }); } }

  播放语音,获取返回的json的参数yaw,用上一次获取的yaw减去这一次的yaw参数的绝对值判断是否摇头。

judge_left_change_right_head: function(res) { if (this.data.music_count == 0) { innerAudioConext.src = "/music/leftright.mp3" innerAudioConext.play(); this.data.music_count++; } let yaw = parseInt(res.data.result.face_list[0].angle.yaw); if ((yaw < 0 || yaw > 0) && (Math.abs(this.data.last_yaw - yaw)>40)) { this.data.judge_left_change_right_head_count++; this.setData({ last_yaw: yaw }) console.log("摇头 " + this.data.judge_left_change_right_head_count) } else { wx.showToast({ title: '未检测到摇头', icon: 'none', duration: 1000 }); } }

  播放语音,获取返回的json的参数pitch,用上一次获取的pitch减去这一次的pitch参数的绝对值判断是否点头。

judge_down_to_up_head: function(res) { if(this.data.music_count==1){ innerAudioConext.src = "/music/headupdown.mp3" innerAudioConext.play(); this.data.music_count++; } let pitch = res.data.result.face_list[0].angle.pitch; if ((pitch < 0 || pitch > 0) && (Math.abs(this.data.last_pitch - pitch) > 5.5)) { this.data.judge_down_to_up_head_count++; this.setData({ last_pitch: pitch }) } else { wx.showToast({ title: '未检测到点头', icon: 'none', duration: 1000 }); } }

人脸对比

  所有验证成功后,就可以直接上传人脸对比了,关于人脸对比同样使用静默人脸对比,由于源码解释众多,本篇文章就不再重复讲解了。

最后

  只看开发者文档注定会踩许多坑,希望能将有坑的地方记录下来,让更多人注意。感谢其他文章论坛提问的帮助,链接如下:
  微信小程序—setTimeOut定时器的坑
  活体检测error_code":222200"


__EOF__

本文作者damarkday知识库
本文链接https://www.cnblogs.com/GoodMemoryBlog/p/14158553.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   DAmarkday  阅读(1358)  评论(0编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示