手把手教学从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"    }

至此,整个人脸融合小程序就开发完毕了,有兴趣的同学可以体验一下:

感谢阅读两篇文章的小伙伴,对使用人脸融合服务有问题的同学可以评论区留言,下来一起讨论下。