# 人脸隐私保护
# 体验 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/smooth
- 人脸隐私保护页面入口/client/pages/face-detect/smooth/util.js
- 对人脸区域进行高斯平滑的工具方法,主要包括获取一维高斯核、横向扫描平滑、纵向扫描平滑的方法。
/client/libs/
- 依赖库相关/client/libs/runtime.js
- 在某页面引用后,可在该页面使用async/await
语法,主要用于兼容iOS
手机
/client/libs/weui.wxss
- weui 样式文件,在人脸识别组件中有使用,可选。
/client/libs/tcb-services-mp-sdk
- 小程序中使用的tcb-services-mp-sdk
,封装 tcb 相关公共能力及方法,可选。
# 上传图片
smooth/index.wxml #8 (opens new window) 中上传按钮绑定
tap
事件处理函数handleUploadTap
,点击按钮将触发上传图片操作。<view class="button-container"> <button type="primary" bindtap="handleUploadTap">上传图片</button> </view>
在 smooth/index.js #21 (opens new window) 中实现
handleUploadTap
方法。通过调用wx.chooseImage
选择需要上传的图片,获取到图片对象后,使用云开发存储能力,调用 wx.cloud.uploadFile (opens new window) 将图片上传到云端,在成功回调中获取云端文件对象,拿到fileID
存储在组件data
中,同时存储tempFilePaths
为temUrl
待用,调用this.recognize()
进行图片分析(下一步将实现该方法)。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( { temUrl: dRes.tempFilePaths[0] fileID: res.fileID, hasUploaded: true }, () => { // 图像识别方法,将在下一步实现 this.recognize(); wx.hideLoading(); } ); }, // 上传失败回调,可用于捕获错误,进行提示 fail: () => { wx.hideLoading(); wx.showToast({ title: "上传失败", icon: "none" }); } }); } }); }
# 图片分析
在上一步,我们将一张本地图片上传到云端,并且获取到了云端 fileID,接下来我们将调用云开发扩展能力解决方案 - AI 人脸特征分析与检测
,进行图片分析。首先请确保在云开发中部署了 AI 人脸特征分析与检测
能力,详见 AI 人脸特征分析与检测使用指引 (opens new window)。
首先在 smooth/index.js (opens new window) 文件头引入依赖
/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();
之后实现
recognize
方法。通过tcbService.callService
方法,传入之前存储于组件 data 中的fileID
,调用AI 人脸特征分析与检测
能力,获取图片识别结果。this.getFaceRect
对识别结果简单处理,获取原图像宽高、人脸相对于图片的位置大小,传递给this.smooth
方法对人脸区域进行高斯平滑后叠加回原图像。async recognize() { 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 let imgInfo = this.getFaceRect(result.data); this.smooth(this.data.temUrl, imgInfo); } else { // 识别失败,抛出错误进行错误处理 throw result; } } catch (e) { wx.hideLoading(); wx.showToast({ title: "识别失败", icon: "none" }); console.log(e); } }, getFaceRect(res) { const { ImageWidth, ImageHeight, FaceInfos } = res; let face = FaceInfos[0]; return { imageWidth: ImageWidth, imageHeight: ImageHeight, rectX: face.X / ImageWidth, rectY: face.Y / ImageHeight, rectWidth: face.Width / ImageWidth, rectHeight: face.Height / ImageHeight }; }
# 图像裁剪
在 smooth/index.wxml #11 (opens new window) 中添加画布容器,用于显示打码后的图像。
<view class="image-container" style="width: {{containerWidth}}rpx">
<canvas
canvas-id="canvas"
style="width: {{containerWidth}}rpx; height: {{containerHeight}}rpx"
class="canvas"
></canvas>
</view>
在 smooth/index.js (opens new window) 文件头引入依赖 /client/pages/face-detect/smooth/util.js
import { smoothX, smoothY, kernel } from "./util";
在 smooth/index.js #104 (opens new window) 实现 smooth
方法,使用 canvas (opens new window),获取图像像素信息进行处理:
- 由于上传图像体积不定,直接对大图像进行高斯平滑效率极低且意义不大。首先对原图像进行等比缩放,绘制在宽度 200rpx (
containerWidth
)的 canvas 中,高度计算获得,并存储为containerHeight
rpx,画布宽高 px 大小,通过系统windowWidth
可以算出。 - 绘制完成后,通过 wx.canvasGetImageData (opens new window) 获取图像的像素数据,每四项表示一个像素点的 rgba。通过人脸的相对位置及大小,在全体像素数据中获取人脸像素,使用高斯核对人脸像素,分别 x,y 方向进行 2 次平滑,完成对整个人脸的高斯平滑处理。
- 将人脸数据提取与一维
Uint8ClampedArray
smoothData 中,使用 wx.canvasPutImageData (opens new window) 将平滑后的人脸图像(smoothData)叠加回原始图像的人脸位置。最终画布上的图像即为打码处理后的图像。
示例代码
smooth(
url,
{ imageWidth, imageHeight, rectX, rectWidth, rectY, rectHeight }
) {
wx.showLoading({
title: "绘图中",
icon: "none"
});
wx.getSystemInfo({
success: res => {
let pixelRatio = res.pixelRatio;
let context = wx.createCanvasContext(`canvas`, this);
// 画布容器高度,单位 rpx
let containerHeight = Math.floor(
(imageHeight / imageWidth) * this.data.containerWidth
);
// 画布宽高,单位 px
let canvasWidthPx = Math.floor(
(res.windowWidth / 750) * this.data.containerWidth
);
let canvasHeightPx = Math.floor(
(res.windowWidth / 750) * containerHeight
);
// 人脸的像素位置/大小
rectX = Math.floor(rectX * canvasWidthPx);
rectY = Math.floor(rectY * canvasHeightPx);
rectWidth = Math.floor(rectWidth * canvasWidthPx);
rectHeight = Math.floor(rectHeight * canvasHeightPx);
this.setData({ containerHeight }, () => {
// 绘制原图并缩放充满画布
context.drawImage(
url,
0,
0,
imageWidth,
imageHeight,
0,
0,
canvasWidthPx,
canvasHeightPx
);
context.draw(false, function() {
// 回调完成后 绘制实际上可能未完成,
// 导致获取像素不稳定,延时之后再进行下一步
setTimeout(() => {
// 获取画布上的像素信息
wx.canvasGetImageData({
canvasId: "canvas",
x: 0,
y: 0,
width: canvasWidthPx,
height: canvasHeightPx,
success: ({ width, height, data }) => {
try {
// 对人脸的部分进行高斯平滑,并提取人脸的像素
let gKernel = kernel(Math.floor(rectWidth / 15));
let smoothData = new Uint8ClampedArray(
rectWidth * rectHeight * 4
);
for (let x = rectX; x < rectX + rectWidth; x++) {
for (let y = rectY; y < rectY + rectHeight; y++) {
let pointIndex = (x + y * width) * 4;
smoothX(
pointIndex,
gKernel,
y * width * 4,
((1 + y) * width - 1) * 4,
data
);
}
}
for (let x = rectX; x < rectX + rectWidth; x++) {
for (let y = rectY; y < rectY + rectHeight; y++) {
let pointIndex = (x + y * width) * 4;
smoothY(pointIndex, gKernel, width, data);
}
}
for (let x = rectX; x < rectX + rectWidth; x++) {
for (let y = rectY; y < rectY + rectHeight; y++) {
let pointIndex = (x + y * width) * 4;
let smoothIndex =
(x - rectX + (y - rectY) * rectWidth) * 4;
smoothData[smoothIndex] = data[pointIndex];
smoothData[smoothIndex + 1] = data[pointIndex + 1];
smoothData[smoothIndex + 2] = data[pointIndex + 2];
smoothData[smoothIndex + 3] = data[pointIndex + 3];
}
}
// 将平滑后的人脸像素输出到画布
wx.canvasPutImageData({
canvasId: "canvas",
x: rectX,
y: rectY,
width: rectWidth,
height: rectHeight,
data: smoothData,
fail: e => {
console.log(e);
}
});
} catch (e) {
console.log(e);
} finally {
wx.hideLoading();
}
}
});
}, 200);
});
});
},
fail: () => {
wx.hideLoading();
}
});
}
编译项目,可以实现从文件上传,到人脸识别,绘制图像到 canvas 并对人脸部位进行高斯模糊的能力。
← 智能裁剪 2 分钟完成颜值打分小程序 →