小程式人臉動態識別搖頭點頭採坑建議
前言
應需求,在小程式內部弄一個人臉動態活體掃描,檢測一下搖搖頭,點點頭之類的。但是在網上的相關資訊卻沒有,偶爾有幾個思路,所以就在這裡記錄下自己的探究歷程
說明
首先說明的是這篇文章採用的是百度雲的人臉識別,與騰訊的人臉識別不同的是,百度人臉識別的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請求引數中,特別重要的一點,如果你碰上這種錯誤返回:
那八成是由於請求引數外沒有加 [ ],如上程式碼顯示,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"