手把手教學從0到1搭建人臉融合小程式(下)

  • 2019 年 12 月 26 日
  • 筆記

前提提要

上篇文章中,我們已經設計好了這次要開發的融合小程式的系統架構,給大家複習下:

下面讓我們跟著這個思路,搭建屬於自己的融合小程式~

小程式前端開發

由於本文主要介紹小程式端調用人臉融合雲介面,所以前端這裡簡單設計,整個前端頁面分為三個區域:

模版區

展示模版圖列表,標柱各模版圖人臉,每次融合只能選擇一張融合圖,每次選擇只能選中一張人臉

輸入區

展示用戶上傳的輸入圖列表,標柱各輸入圖人臉,每次選擇只能選中一張人臉

融合結果區

展示融合結果圖

使用說明

  • 上傳輸入圖,展示輸入人臉->選擇模版人臉->選擇輸入人臉,組合成MergeInfos元素->點擊融合->獲取融合結果並展示
  • 可以上傳多張輸入圖
  • 可以重複【選擇模版人臉->選擇輸入人臉】操作,最多三組,實現多臉融合

調用人臉介面

main雲函數

main雲函數實際是一個統一請求雲sdk的介面,裡面封裝好大部分請求雲API的操作:

  • 公共參數處理:組裝好包括service、version、action,以及用於身份校驗的sercetId、secretKey等參數
  • 處理圖片數據:由於小程式從本地相冊或相機獲取的圖片數據,雲服務無法直接處理,因此小程式需要將這些圖片先上傳,再請求雲函數 在這個demo採取的方式是先將圖片上傳的雲開發的資料庫,獲取到fileId,在雲函數請求雲API之前,通過雲開發提供的cloud.getTempFileURL方法,獲取雲文件真實的鏈接。
async function getFileUrl(url) {    // 如果是 cloud:// 則,換取雲文件真實鏈接    if (/^cloud:///.test(url)) {      const { fileList } = await cloud.getTempFileURL({        fileList: [url],      });      if (!fileList || !fileList[0]) throw new Error('無法獲取文件');      const file = fileList[0];      return file.tempFileURL;    } else {      return url;    }  };
  • 封裝請求雲API的統一介面:
// 騰訊雲sdk  const tencentcloud = require('tencentcloud-sdk-nodejs');    const { Credential } = tencentcloud.common;  const { ClientProfile } = tencentcloud.common;  const { HttpProfile } = tencentcloud.common;  // 請求騰訊雲API介面  function requestAPI({    endpoint, // 請求域名 (可選)    service, // 服務前綴    action, // 介面名稱    version, // 版本號    region, // 地域 (可選)    secretId, // 密鑰ID    secretKey, // 密鑰Key    sessionToken, // 密鑰Token (可選)    data, // 請求數據  }) {    const { Client } = tencentcloud[service][version];    const { Models } = tencentcloud[service][version];    const cred = new Credential(secretId, secretKey, sessionToken);    const httpProfile = new HttpProfile();    httpProfile.endpoint = endpoint || `${service}.tencentcloudapi.com`;    const clientProfile = new ClientProfile();    clientProfile.httpProfile = httpProfile;    const client = new Client(cred, region || 'ap-shanghai', clientProfile);    const req = new Models[`${action}Request`]();    const reqParams = JSON.stringify({ ...data });    req.from_json_string(reqParams);    return new Promise((resolve, reject) => {      client[action](req, (errMsg, response) => {        if (errMsg) {          reject(errMsg);          return;        }        resolve(JSON.parse(response.to_json_string() || {}));      });    });  }

main雲函數實現如下:

// 雲函數入口文件  const cloud = require('wx-server-sdk')  const { handleImage, requestAPI } = require('./utils');  const actionConfig = {    DetectFace: {      service: 'iai',      action: 'DetectFace',      version: 'v20180301',      secretId: '你的secretId',      secretKey: '你的secretKey'    },    FuseFace: {      service: 'facefusion',      action: 'FuseFace',      version: 'v20181201',      secretId: '你的secretId',      secretKey: '你的secretKey'    }  };  // 雲函數入口函數  exports.main = async (event, context) => {    const wxContext = cloud.getWXContext();    // 整理默認參數,action指定請求那個雲API介面    let config = actionConfig[event.action];    if (!config) {      return {        Response: {          Error: {            Code: -1,            Message: 'Action錯誤'          }        }      };    }    // 整理本次請求入參,處理入參的圖片數據,這裡不展開邏輯    config.data = handleImage(event.data || {})    let requestRes;    try {      // 發起請求      requestRes = {          Response: await requestAPI(config),      };    } catch (err) {      console.error(err);      requestRes = {        Response: {          Error: {            Code: err.code || -1,            Message: err.message || '未知錯誤',          },        }      };    }      return requestRes;  }

小程式調用雲函數

參考官網文檔,實現小程式調用雲函數即可:

  • 人臉檢測:獲取本地圖片後執行人臉檢測,獲取人臉框資訊:
const self = this;  // 選擇本地圖片  chooseImage(async function(res) {    // 上傳圖片    const fileId = await uploadImage(res);    wx.showLoading({      title: '載入中',      mask: true    });    // 指定請求人臉檢測,填好請求入參    wx.cloud.callFunction({      name: 'main',      data: {        action: 'DetectFace',        data: {          MaxFaceNum: 3,          MinFaceSize: 30,          Url: fileId,          NeedFaceAttributes: 0,          NeedQualityDetection: 0        }      },      success(result) {        wx.hideLoading();        // TODO:success event      },      fail(error) {        wx.hideLoading();        console.log(error);      }    });  }, function(err) {    console.log(err);  })
  • 人臉融合:
let { mergeInfos, activeModelId, projectId } = this.data;  wx.showLoading({    title: '融合中',    mask: true  });  const self = this;  // 發起融合  wx.cloud.callFunction({    name: 'main',    data: {      action: 'FuseFace',      data: {        RspImgType: 'url',        ProjectId: projectId,        ModelId: activeModelId,        MergeInfos: mergeInfos      }    },    success(result) {      wx.hideLoading();      // TODO:success event    },    fail(err) {      console.log(err);      wx.hideLoading();    }  });

有個坑

由於小程式使用的雲SDK版本問題,當我們編寫完main的雲函數,上傳並部署時,若選擇【雲端安裝依賴】,那麼此時雲端安裝的雲SDK並不包含FuseFace這個服務!! 因此這裡推薦大家本地安裝好依賴(拉取最新版本latest)後,一併上傳部署。

"dependencies": {      "wx-server-sdk": "latest",      "tencentcloud-sdk-nodejs": "latest"    }

至此,整個人臉融合小程式就開發完畢了,有興趣的同學可以體驗一下:

感謝閱讀兩篇文章的小夥伴,對使用人臉融合服務有問題的同學可以評論區留言,下來一起討論下。