手把手教学从0到1搭建人脸融合小程序(下)

2019-12-26 11:09:25 浏览数 (1)

前提提要

在上篇文章中,我们已经设计好了这次要开发的融合小程序的系统架构,给大家复习下:

下面让我们跟着这个思路,搭建属于自己的融合小程序~

小程序前端开发

由于本文主要介绍小程序端调用人脸融合云接口,所以前端这里简单设计,整个前端页面分为三个区域:

模版区

展示模版图列表,标柱各模版图人脸,每次融合只能选择一张融合图,每次选择只能选中一张人脸

输入区

展示用户上传的输入图列表,标柱各输入图人脸,每次选择只能选中一张人脸

融合结果区

展示融合结果图

使用说明

  • 上传输入图,展示输入人脸->选择模版人脸->选择输入人脸,组合成MergeInfos元素->点击融合->获取融合结果并展示
  • 可以上传多张输入图
  • 可以重复【选择模版人脸->选择输入人脸】操作,最多三组,实现多脸融合

调用人脸接口

main云函数

main云函数实际是一个统一请求云sdk的接口,里面封装好大部分请求云API的操作:

  • 公共参数处理:组装好包括service、version、action,以及用于身份校验的sercetId、secretKey等参数
  • 处理图片数据:由于小程序从本地相册或相机获取的图片数据,云服务无法直接处理,因此小程序需要将这些图片先上传,再请求云函数 在这个demo采取的方式是先将图片上传的云开发的数据库,获取到fileId,在云函数请求云API之前,通过云开发提供的cloud.getTempFileURL方法,获取云文件真实的链接。
代码语言:txt复制
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的统一接口:
代码语言:txt复制
// 腾讯云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云函数实现如下:

代码语言:txt复制
// 云函数入口文件
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;
}

小程序调用云函数

参考官网文档,实现小程序调用云函数即可:

  • 人脸检测:获取本地图片后执行人脸检测,获取人脸框信息:
代码语言:txt复制
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);
})
  • 人脸融合:
代码语言:txt复制
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)后,一并上传部署。

代码语言:txt复制
"dependencies": {
    "wx-server-sdk": "latest",
    "tencentcloud-sdk-nodejs": "latest"
  }

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

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

0 人点赞