# 2 分钟完成颜值打分小程序
# 体验 demo
选择克隆或下载代码到小程序端 IDE, 更新项目 project.config.json
中的 appid
字段为您的小程序 ID,在当前环境中部署部署 (opens new window) AI 人脸特征分析与检测
扩展能力,即可体验完整颜值打分小程序。亦可扫码体验线上版本。
# 克隆
- 确保您已安装 git 客户端。
- 在命令行中通过以下指令,克隆 demo 仓库:
git clone https://github.com/TencentCloudBase/Cloudbase-Examples.git--branch feature/keylessCall
- 进入 clone 下来的 tcb-demo-ai 目录可见项目代码
cd ./tcb-demo-ai
# 下载
若无 git 客户端,或其他原因,可以选择手动下载方式:点击下载 (opens new window)项目代码包,解压到您喜欢的目录。
# 开发
在小程序 IDE 中打开项目,注意更新 project.config.json
中的 appid
字段为您的小程序 ID。
# 目录说明
/client/pages/index
- 首页导航目录/client/pages/face-detect/detect
- 人脸检测与识别入口/client/components/iai
- 人脸识别组件/client/components/iai/iai.js
- 组件业务逻辑,处理图片上传,调用云函数进行识别分析,展示分析结果等。
/client/components/iai/iai.wxml
- 组件页面框架结构。
/client/components/iai/iai.wxss
- 组件样式,依赖 weui。
/client/libs/
- 依赖库相关/client/libs/tcb-services-mp-sdk
- 小程序中使用的tcb-services-mp-sdk
,封装 tcb 相关公共能力及方法
/client/libs/runtime.js
- 在某页面引用后,可在该页面使用async/await
语法,主要用于兼容iOS
手机
/client/libs/weui.wxss
- weui 样式文件,在人脸识别组件中有使用,可选。
# 上传图片
iai.wxml #23 (opens new window) 中上传按钮绑定
tap
事件处理函数handleUploadTap
,点击按钮将触发上传图片操作。<view class="button-container"> <button type="primary" bindtap="handleUploadTap"> 上传人脸 </button> </view>
在 iai.js (opens new window) 中,组件 method 对象上实现 handleUploadTap 方法。通过调用
wx.chooseImage
选择需要上传的图片,获取到图片对象后,使用云开发存储能力,调用 wx.cloud.uploadFile (opens new window) 将图片上传到云端,在成功回调中获取云端文件对象,拿到fileID
存储在组件data
中。handleUploadTap() { // 通过微信 API,选择上传的图片 wx.chooseImage({ success: dRes => { wx.showLoading({ title: "上传中" }); const fileName = dRes.tempFilePaths[0]; const dotPosition = fileName.lastIndexOf("."); const extension = fileName.slice(dotPosition); const cloudPath = `${Date.now()}-${Math.floor( Math.random(0, 1) * 10000 )}${extension}`; // 云开发上传图片到云端 wx.cloud.uploadFile({ cloudPath, filePath: dRes.tempFilePaths[0], // 上传成功回调 success: res => { console.log(res); // 将 fileID 存在组件 data 上 this.setData( { fileID: res.fileID, hasUploaded: true }, () => { wx.hideLoading(); } ); }, // 上传失败回调,可用于捕获错误,进行提示 fail: () => { wx.hideLoading(); wx.showToast({ title: "上传失败", icon: "none" }); } }); } }); }
# 图片分析
在上一步,我们将一张本地图片上传到云端,并且获取到了云端 fileID,接下来我们将调用云开发扩展能力解决方案 - AI 人脸特征分析与检测
,进行图片分析。首先请确保在云开发中部署了 AI 人脸特征分析与检测
能力,详见 AI 人脸特征分析与检测使用指引 (opens new window)。
在 iai.wxml #29 (opens new window) 中添加分析按钮并绑定
tap
事件处理函数handleRecognizeTap
,点击按钮将触发图片分析动作。<view class="button-container"> <button type="primary" bindtap="handleRecognizeTap"> 上传人脸 </button> <!-- add --> <button type="primary" disabled="{{!hasUploaded}}" bindtap="handleRecognizeTap" > 识别人脸 </button> </view>
首先在文件头引入依赖
/client/libs/tcb-services-mp-sdk
与/client/libs/runtime.js
。import regeneratorRuntime from "../../../libs/runtime"; import TcbService from "../../../libs/tcb-service-mp-sdk/index"; const tcbService = new TcbService();
之后在 iai.js (opens new window) 中,组件 method 对象上实现 handleRecognizeTap 方法。通过
tcbService.callService
方法,传入之前存储于组件 data 中的fileID
,调用AI 人脸特征分析与检测
能力,获取图片识别结果,简单处理后存储于组件 data 待用。async handleRecognizeTap() { wx.showLoading({ title: "识别中", icon: "none" }); try { let result = await tcbService.callService({ service: "ai", action: "tcbService-ai-detectFace", data: { FileID: this.data.fileID } }); /** 或可简单的直接调用云函数 let result = await wx.cloud.callFunction({ name: 'tcbService-ai-detectFace', data: { FileID: this.data.fileID } }); **/ wx.hideLoading(); if (!result.code && result.data) { // 图像识别成功,结果对象 result.data // 对结果作简单的解析,存储于组件 data this.setData( { // this.getFaceRects 将在下一步进行实现 faceRects: this.getFaceRects(result.data) }, () => { this.triggerEvent("finish", result.data); } ); } else { // 识别失败,抛出错误进行错误处理 throw result; } } catch (e) { wx.hideLoading(); wx.showToast({ title: "识别失败", icon: "none" }); console.log(e); } }
# 渲染结果
上一步我们获取到了分析结果对象 DetectFactData 类型的 result.data (opens new window),并使用 this.getFaceRects
进行简单解析,接下来我们实现 getFaceRects
方法。并根据解析的结果进行简单的渲染。
在 iai.js (opens new window) 中,组件 method 对象上实现 getFaceRects 方法。主要为了计算人脸在图片中的相对位置,以框出人脸。该方法接受的参数即 DetectFactData 类型 (opens new window)的对象。处理后的对象存储于 data.faceRects 中。
getFaceRects(res) { const { ImageWidth, ImageHeight, FaceInfos } = res; return FaceInfos.map(item => ({ ...item, rectX: (item.X / ImageWidth) * 100 + "%", // 人脸方框 左上角,X位置 rectY: (item.Y / ImageHeight) * 100 + "%", // 人脸方框 左上角,Y位置 rectWidth: (item.Width / ImageWidth) * 100 + "%", // 人脸方框 相对图片宽度 rectHeight: (item.Height / ImageHeight) * 100 + "%" // 人脸方框 相对图片高度 })); }
在 iai.wxml #38 (opens new window),根据解析结果 faceRects,渲染页面。
wx:for=""
遍历人脸数组,对每张人脸遍历FaceAttributesInfo
和FaceQualityInfo
对象中的所有属性值,列出key-value
,通过事先定义的 data.resMap 将数值解释成可以理解的值。{ "resMap": { "Gender": { "label": "性别", "valMap": { 0: "女", 1: "男" } }, "Age": { "label": "年龄" }, "Expression": { "label": "微笑", "valMap": { 0: "否", 1: "是" } }, "Glass": { "label": "是否有眼镜" }, "Beauty": { "label": "魅力" }, "Hat": { "label": "是否有帽子" }, "Mask": { "label": "是否有口罩" }, "Score": { "label": "质量分" }, "Sharpness": { "label": "清晰分" }, "Brightness": { "label": "光照分" } } }
<view class="result" wx:if="{{faceRects && faceRects.length}}"> <view class="weui-cells__title">分析结果:</view> <!-- 遍历人脸结果 --> <view class="weui-cells" wx:for="{{faceRects}}" wx:key="*this" wx:for-item="face" > <!-- 遍历每个人脸对象中的 FaceAttributesInfo 和 FaceQualityInfo --> <block wx:for="{{ ['FaceAttributesInfo', 'FaceQualityInfo'] }}" wx:key="*this" wx:for-item="attr" > <view class="weui-cell" wx:for="{{face[attr]}}" wx:key="*this" wx:for-index="key" wx:if="{{resMap[key]}}" > <!-- 根据 resMap,解析属性中文名 --> <view class="weui-cell__bd"> <p>{{ resMap[key].label }}</p> </view> <!-- 对于 true/false 类型的属性值,翻译为 是/否 --> <view class="weui-cell__ft" wx:if="{{ item === true || item === false }}" >{{ item ? '是' : '否' }}</view > <!-- 对于区间类型的属性值,特殊含义的通过 resMap 翻译(例如,性别),其他值直接展示 --> <view class="weui-cell__ft" wx:else >{{ resMap[key].valMap && resMap[key].valMap[item > 50 ? '1' : '0'] || item }}</view > </view> </block> </view> </view>
getFaceRects
算出了人脸方框相对原图的相对位置与大小,在 iai.wxml #8 (opens new window) 中,通过对<view>
设置绝对定位,固定大小,在图片中框出人脸。<view class="image-container"> <image class="image" src="{{fileID || imgUrl}}" mode="widthFix"></image> <!-- 遍历人脸结果数组 --> <block wx:if="faceRects && faceRects.length"> <!-- view.face-result-item 通过 iai.wxss 设置样式为 position: absolute; border: 1px solid #1aad16;--> <!-- 再通过内联style 设置位置 top,left 与大小 width,height。通过在图片上叠加方框元素的方式圈出人脸--> <view wx:for="{{faceRects}}" wx:key="*this" class="face-result-item" style="left:{{item.rectX}};top:{{item.rectY}};height:{{item.rectHeight}};width:{{item.rectWidth}};" > <view class="face-result-text"> <text>颜值 {{ item.FaceAttributesInfo.Beauty }} 分</text> <text>年龄 {{ item.FaceAttributesInfo.Age }} 岁</text> </view> </view> </block> </view>
至此,我们完成了一整套的上传、识别、展示流程。我们将识别结果存储在了 data 上,为了可以连续上传识别,我们需要做一点改动,在上传前清除掉上一次识别的结果。操作也很简单,在 handleUploadTap
方法中,选择图片前加入以下代码,清除 data 上的结果即可。
// 重新上传,清空结果
this.setData({
imgUrl,
faceRects: []
});
一个完整的使用 AI 人脸特征分析与检测
拓展能力的小程序就开发完成了。