小程序人臉動態識別搖頭點頭采坑建議


前言

  應需求,在小程序內部弄一個人臉動態活體掃描,檢測一下搖搖頭,點點頭之類的。但是在網上的相關信息卻沒有,偶爾有幾個思路,所以就在這里記錄下自己的探究歷程

說明

  首先說明的是這篇文章采用的是百度雲的人臉識別,與騰訊的人臉識別不同的是,百度人臉識別的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"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM