云开发:让你拥有自己的第一个AI人脸识别小程序

博主绪论

微信小程序云开发可以免费调用云开发控制台的云存储,云函数,云计算,当然只是一定程度上可以免费调用。不过这也比较有意思,逼格也高,而且云开发可以让我们轻易的在小程序当中和用户进行交互,接下来让我们紧跟时代潮流,利用图灵完备打造出鼠于我们的一个AI人脸识别神器吧。

准备过程

在此之前,我们需要安装几个东西。

GIT的安装

进入GIT官网,点击这,根据自己的电脑系统版本下载,下载完成以后,打开命令行(cmd),输入命令git,如果成功显示出一大段我们看不懂的字符,那就对了。
在这里插入图片描述

NodeJS和npm的安装

进入NodeJS官网下载,点击,下载对应版本即可,下载完成以后,在自己的cmd上面输入node -v和npm -v即可,如果显示出版本,就证明安装成功了。
在这里插入图片描述

腾讯云人脸识别API

首先注册一个自己的腾讯云账号,点击进入官网,点击注册,注册完账号以后,还得实名认证才能够正常使用,这个过程很快的。
之后我们还得调用腾讯云的人脸识别API接口,我们就得先开通这个服务,点击产品->人工智能->人脸识别->立即使用即可。之后如果我们向调用腾讯云API的话,我们还得去控制台->访问管理创建API密匙,之后会出现APPID,SecretID,SecretKey,之后我们调用API需要使用。
在这里插入图片描述

正式开始

创建云开发小程序

创建的时候勾选小程序-云开发即可:
在这里插入图片描述
之后小程序会自动生成一个模板,我们看它不爽就全删了,自己重新创建。把所有文件删掉(处理project.config.json),重新打开开发工具,然后新建两个目录,一个事服务器(命名为server),一个是客户端(命名为client),建立完目录以后,再到配置文件project.config.json里面改动一下:就改动这两行

{
	"miniprogramRoot": "client/",           //客户端
	"cloudfunctionRoot": "server/",       //服务器
	"setting": {
		"urlCheck": true,

改动完以后,保存,然后重新打开开发工具,我们可以看见服务器目录出现了一个云图标:
在这里插入图片描述
这个时候我们的环境就准备好了。但是或许有点人会出现环境未知,不要怕,博主也是,你就点击:
在这里插入图片描述
点击里面的云开发选项就可以看到提示,教你创建环境。

开发前的思考准备,思维决定行动

功能设想:

  • 客户端选择或者拍照图片进行上传,然后在小程序当中调用云存储函数上传到我们个人的云存储当中,并且云存储会返回一个文件ID到客户端。

  • 客户端获取文件ID,调用云函数,云函数就会读取这个文件ID,在云存储当中找到这个文件图片的位置,并且进行读取,获得其真实的URL地址

  • 将获取到的URL发送到腾讯云的人脸识别API,之后人脸识别API会返回图片的相关信息

  • 我们将这些信息一一进行处理,然后发给客户端,并且进行显示。

  • 优化显示界面

服务器开发(写一个云函数端)

首先,我们右键选择server文件夹,然后选择建立NodeJS云函数,命名为FaceDetect(名字随意,只要容易辨认就好),建立完成以后,目录下面会自动生成index.js和package.json文件,其中index.js文件里面有官方给出的模板,我们把多余的代码删掉

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {

}

分析

这个我们暂时先放下,我们先去腾讯云,找到人脸识别的开发文档,点击这里
之后选择人脸检测与分析相关接口,即DetectFace。
在这个文档我们可以看到很多详细的信息,比如说面部属性和人脸质量信息的识别。当然这个部署最重要的,最重要的是我们该怎么用,用哪些。

  • 首先我们看到输入参数:
    里面的Action(接口名称)和Verxion(版本)是必填的,而我们需要识别的图片是云存储上传的图片,因此需要URL参数,而且我们获取的是人脸的属性信息,因此我们还需要参数NeedFaceAttributes.

  • 再看到输出参数,里面有:

  • 在这里插入图片描述

图片的宽高,人脸信息列表(最重要的),因此我们点击FaceInfo
里面有:
在这里插入图片描述
其中我们会用到FaceAttributesInfo即人脸属性信息,然后点击FaceAttributesInfo查看详细信息,发现里面有很多人脸信息输出,比如说头发信息,咦,头发信息还有一个文档可以点击,有什么呢,我们看看:FaceHairAttributesInfo
里面有头发的颜色,程度,刘海,信息看来十分具体。
以上是人脸识别API的参数分析,我们了解参数分析以后,后面的执行步骤才不会那么的懵逼。

调用人脸识别API

进入这一页,我们可以看到相关的SDK,SDK用起来有些许麻烦,我们可以常常新鲜东西,API Explorer,即SDK版本的GUI界面,调用API十分方便:
在这里插入图片描述

点击API Explorer我们可以进入到以下页面:
在这里插入图片描述

里面有我们前面说到的SecretId和SecretKey,还有我们前面的输入参数URL和NeedFaceAttributes(人脸属性列表),我们把这些内容给填写了,其他的不管,URL我们可以随便从网上找一张人脸图片的地址,填入,然后后面那个Need什么的参数就填写1就OK了,之后我们点击右侧的在线调用进行测试,看我们的接口调用成功没,然后选择发送请求,会出现以下结果:
在这里插入图片描述
如果你出现了以下类似的结构,人脸信息,那么就对了,如果出现错误,那么就是你的人脸检测服务没启用或者没有实名认证,去上面看看吧。
之后选择代码生成,选择NodeJS,此时我们就当一个搬运工,把这里的代码复制即可:
在这里插入图片描述
接下来我们把复制的代码加到我们云函数的index.js上面(稍微做点改动):

const cloud = require('wx-server-sdk')// 云函数入口文件
const tencentcloud = require("tencentcloud-sdk-nodejs"); //腾讯云API 3.0 SDK

cloud.init() // 云开发初始化

var synDetectFace = function (url) { //调用人脸识别API函数
  const IaiClient = tencentcloud.iai.v20180301.Client; //API版本
  const models = tencentcloud.iai.v20180301.Models; //API版本

  const Credential = tencentcloud.common.Credential;
  const ClientProfile = tencentcloud.common.ClientProfile;
  const HttpProfile = tencentcloud.common.HttpProfile;

  let cred = new Credential("SecretId", "SecretKey"); //腾讯云的SecretId和SecretKey
  let httpProfile = new HttpProfile();
  httpProfile.endpoint = "iai.tencentcloudapi.com"; //腾讯云人脸识别API接口
  let clientProfile = new ClientProfile();
  clientProfile.httpProfile = httpProfile;
  let client = new IaiClient(cred, "", clientProfile);

  let req = new models.DetectFaceRequest();

  let params = '{"Url":"输入你自己的样例图片的地址","NeedFaceAttributes":1}'
  req.from_json_string(params);

  client.DetectFace(req, function (errMsg, response) {

    if (errMsg) {
      console.log(errMsg);
      return;
    }

    console.log(response.to_json_string());
  });
}

synDetectFace();
// 云函数入口函数
exports.main = async (event, context) => {

}

记住,把第二行代码多余的那些点点杠杠给删了,因为我们将会使用npm install的方式安装这几个依赖包。
右键选择云函数FaceDetect,然后选择使用终端打开,输入下面两行命令:

npm install tencentcloud-sdk-nodejs --save
npm install wx-server-sdk --save

很诡异的是,在你输入第一个命令的时候,安装成功了,但是输入第二个命令的时候却见鬼了,不管输入几次,都会报错:

npm ERR! Unexpected end of JSON input while parsing near '...-/buffer-2.1.11.tgz"}'

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\23521\AppData\Roaming\npm-cache\_logs\2020-02-04T07_54_43_051Z-debug.log

遇到这个错误不要怕,因为博主也为例这个bug花了很长的时间才解决,后面才发现,我们需要首先清理缓存,输入以下命令:

npm cache clean --force

之后我们把下载改为镜像下载(不是淘宝镜像哦),输入以下命令:

npm set registry https://registry.npmjs.org/

输入完上面那行命令以后,系统还会继续让你输入,此时已经改为镜像下载,你可以输入你的第二行下载命令了:

npm install wx-server-sdk --save

好!现在总算下载完了,下载完以后,你可别着急,测试一下看看,我们下载的东西是不是真的有用,在当前终端中输入node index.js.
如果出现了一些人脸的信息,那么就是下载成功了。依赖包下载这部总算圆满完成了。

云存储

现在我们进入到云存储环节,打开之前开发工具的云开发按钮,我们就可以看到云开发控制台,之后进入存储管理界面:
在这里插入图片描述

之后我们随便上传一张图片(前提是我们小程序image目录里面的,如果没有,可以建一个专门存放图片),上传以后,会出现一个文件的ID,复制一下。
接下来我们可以看到微信小程序开发的云存储文档,点击这里,我们在这里面可以看到一个函数,可以专门把云存储当中文件对应的ID转换成真实的URL地址,我们每个人都可以调用。我们可以看到这样的代码:

const cloud = require('wx-server-sdk')

exports.main = async (event, context) => {
  const fileList = ['cloud://xxx', 'cloud://yyy']
  const result = await cloud.getTempFileURL({
    fileList: fileList,
  })
  return result.fileList
}

这个时候咱们应该毫不犹豫的把它复制粘贴搬砖搬过来给我们用。
但是呢我们需要把第三行代码当中的cloud://xxx改成我们上面的文件ID ,我们可以把这几行代码放入到我们云函数的index.js文件当中的主函数:

exports.main = async (event, context) => {
  const fileList = ['测试上传文件的ID']
  const result = await cloud.getTempFileURL({
    fileList,
  })
  return result.fileList
}

测试环节

我们干完上面的操作以后,可以缓一口气了,右键选择我们的云函数FaceDetect,然后选择上次并部署所有文件,等待上传成功以后,我们可以在云开发控制台那选择云函数,并且选择云端测试按钮,进行相应的测试,看看我们的云函数是不是可以正常调用,输出结果是不是对的:
在这里插入图片描述

之后点击运行测试,如果运行结果当中顺利的出现了fileID和其对应的tempFileURL,就说明云函数部署成功,测试成功。

服务端代码完成

经过上述测试成功以后,我们进行代码整合:

const cloud = require('wx-server-sdk') //小程序云开发SDK
const tencentcloud = require("tencentcloud-sdk-nodejs"); //腾讯云API 3.0 SDK
cloud.init() //云开发初始化
var synDetectFace = function(url) { //人脸识别API
  const IaiClient = tencentcloud.iai.v20180301.Client; //API版本
  const models = tencentcloud.iai.v20180301.Models; //API版本

  const Credential = tencentcloud.common.Credential;
  const ClientProfile = tencentcloud.common.ClientProfile;
  const HttpProfile = tencentcloud.common.HttpProfile;
  let cred = new Credential("你的SecretID", "SecretKey"); //腾讯云的SecretId和SecretKey
  let httpProfile = new HttpProfile();
  httpProfile.endpoint = "iai.tencentcloudapi.com"; //腾讯云人脸识别API接口
  let clientProfile = new ClientProfile();
  clientProfile.httpProfile = httpProfile;
  let client = new IaiClient(cred, "", clientProfile); //调用就近地域

  let req = new models.DetectFaceRequest();
  let params = '{"Url":"' + url + '","NeedFaceAttributes":1}' //拼接参数,由于人脸识别图片的URL会变化,所有URL采用变量
  req.from_json_string(params);
  return new Promise(function(resolve, reject) { //构造异步函数,把人脸识别内容返回
    client.DetectFace(req, function(errMsg, response) {
      if (errMsg) {
        reject(errMsg)
      } else {
        resolve(response);
      }
    })
  })
}


exports.main = async(event, context) => {
  const data = event            //在客户端调用云函数的时候返回的数据
  const fileList = [data.fileID] //读取来自客户端的fileID
  const result = await cloud.getTempFileURL({
    fileList, //向云存储发起读取文件临时地址请求
  })
  const url = result.fileList[0].tempFileURL //一次次调用,fileList里面只有一个data.fileID,因此只需要fileList[0]
  datas = await synDetectFace(url) //调用异步函数,向腾讯云API发起请求
  return datas
}

服务端代码完成以后,我们上传到云函数,右键选择云函数,选择上次并部署所有文件,更新云函数。

客户端开发(真正的小程序前端界面+后端调用)

准备阶段

右键选择client文件目录(存放各种页面的目录),然后新建一个app.json文件,app.js文件(这些是页面目录必须的文件),选择app.json文件,在里面加入代码:

{
  "pages": [
    "pages/index/index"
  ],
  "window": {
    "navigationBarBackgroundColor": "#333366",
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "容颜下的密码",
    "backgroundColor": "#eeeeee",
    "backgroundTextStyle": "light",
    "enablePullDownRefresh": false
  },
  "cloud": true,
  "sitemapLocation": "sitemap0.json"
}

保存文件,就会自动生成相关目录和文件,当然,如果没有生成,也别着急,你可以尝试把pages/index/index重新自己写一遍,然后保存,就可以出来了。

客户端提交上传图片

对于这个功能实现,我们需要在client下面生成的index.js文件里面写入一个相关的功能函数,并且于相关的上传按钮绑定起来即可。对于上传图片功能,开发文档里面讲的比较清楚,博主就负责整合这个资源给大家使用,点击这里进入,我们可以看到里面有各种参数,其中属性我们可以相应的选择好,当选择完图片以后,会把相应的输出结果放入到res当中,我们从res当中得到tempFilePaths,即当前上传图片的图片的本地临时文件路径列表 (本地路径),代码如下:

 UploadImage(){
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths
        console.log(tempFilePaths)
      }
    })
  },

当我们得到这个当前需要上传的文件临时存储路径以后,我们可以把它上传到云存储上面,得到对应的fileID,然后我们通过对应的函数得到URL,再调用云函数得到脸部信息即可,具体上传云存储的函数可以参考这里,根据这个文档提供的函数我们可以修改代码,在上传文件成功以后,获得相应的临时路径,然后调用这个路径,上传到云存储当中,加入随机数,生成随机的云路径,以便于更多的用户上传:

UploadImage:function(){
     var random = Date.parse(new Date())+Math.ceil(Math.random()*1000)   //设置云存储路径的随机数,以便于多个用户使用时生成多个路径 
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const tempFilePaths = res.tempFilePaths[0]   //保存当前生成的临时路径
        console.log(tempFilePaths)
        wx.cloud.uploadFile({
          cloudPath: random+'.jpg',  //云存储路径
          filePath: tempFilePaths, // 文件路径
          success: res => {         //成功则回调
            console.log(res.fileID)      //生成对应的fileID
          },
          
        })
      }
    })
  },

当然,在进入小程序调用云函数之前,我们必须得有一个初始化的环节,即在加载页面的时候,对云环境进行初始化:

/**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    wx.cloud.init({
      env: 'test-ws9kn'            //初始化的环境ID,之前建立云环境的时候的ID
    })

  },

接下来我们可以进行一个小小的测试,运行小程序,选择图片,如果上传成功以后,控制台打印出了文件的fileID,那么就证明测试成功。

调用云函数处理fileID,返回脸部信息

具体可以参考微信开发者文档当中云函数,我们需要的参数有:name(调用的云函数的名字),data(云函数需要的参数,即fileID),因此在上传云存储成功以后,我们再调用云函数处理对应的fileID,得到我们需要的脸部信息,加入相应的代码,其它不变:

wx.cloud.uploadFile({
          cloudPath: random+'.jpg',
          filePath: tempFilePaths, // 文件路径
          success: res => {
            console.log(res.fileID)
            wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                console.log(res.result)
              },
            })
          },

我们继续进行小小的测试,运行小程序,上传我们需要进行人脸识别的图片,然后观察控制台,如果打印出了对应的人脸信息,那么测试成功。

把得到的人脸数据显示到前端页面

根据上面输出的人脸信息,我们可以看到很多类型的人脸信息,我们具体可以看看腾讯云文档的内容
假设我们需要去看的参数有年龄,是否戴眼镜,是否戴帽子,是否戴口罩,性别,头发长度,是否有刘海,头发颜色等等。此时可以在WXML文件当中加入相应的组件:

<view class="button_view">
<button class="button_size" type="primary" bindtap="UploadImage">上传图片</button>
</view>
<view class="info_view">
<text class="info">颜值:{{Beauty}}</text>
<text class="info">年龄:{{age}}</text>
<text class="info">是否带眼镜:{{glasses}}</text>
<text class="info">是否戴口罩:{{mask}}</text>
<text class="info">是否戴帽子:{{hat}}</text>
<text class="info">性别:{{gender}}</text>
<text class="info">头发长度:{{hair_length}}</text>
<text class="info">有无刘海:{{hair_bang}}</text>
<text class="info">头发颜色:{{hair_color}}</text>
</view>

当然此时前端界面能够看到的参数只有一些具体的数字或者true,false等等,为了更加人性化的设计,我们可以进行相应的改进,比如说gender小于50 的时候,性别为女,再比如说hair_length结果输出为0的时候我们可以把输出改为光头显示在界面上等等,具体改法,可以参考文档,可以利用if语句,switch语句进行改写:

//。。。。。上面代码不变,就在云函数调用成功以后,把信息显示在界面上,当然为了能够把信息显示在界面上就得调用setData函数,但是this不能使用,首先得在UploadImage函数开始的时候写入代码:var that = this    把this给that。
wx.cloud.callFunction({
              name: 'Face_Detection',
              data: {
                fileID:res.fileID
              },
              success: res => {
                  that.setData({
                  age: res.result.FaceInfos[0].FaceAttributesInfo.Age,
                  glasses: res.result.FaceInfos[0].FaceAttributesInfo.Glass,
                  beauty: res.result.FaceInfos[0].FaceAttributesInfo.Beauty,
                  mask: res.result.FaceInfos[0].FaceAttributesInfo.Mask,
                  hat: res.result.FaceInfos[0].FaceAttributesInfo.Hat,
                })
                if (res.result.FaceInfos[0].FaceAttributesInfo.Gender < 50) {
                  myThis.setData({
                    gender: "女"
                  });
                } else {
                  myThis.setData({
                    gender: "男"
                  });
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Length) {
                  case 0:
                    myThis.setData({
                      hair_length: "光头"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_length: "短发"
                    });
                    break;
                  case 2:
                    myThis.setData({
                      hair_length: "中发"
                    });
                    break;
                  case 3:
                    myThis.setData({
                      hair_length: "长发"
                    });
                    break;
                  case 4:
                    myThis.setData({
                      hair_length: "绑发"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Bang) {
                  case 0:
                    myThis.setData({
                      hair_bang: "有刘海"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_bang: "无刘海"
                    });
                    break;
                }
                switch (res.result.FaceInfos[0].FaceAttributesInfo.Hair.Color) {
                  case 0:
                    myThis.setData({
                      hair_color: "黑色"
                    });
                    break;
                  case 1:
                    myThis.setData({
                      hair_color: "金色"
                    });
                    break;
                  case 3:
                    myThis.setData({
                      hair_color: "棕色"
                    });
                    break;
                  case 4:
                    myThis.setData({
                      hair_color: "灰白色"
                    });
                    break;
                }
              },
            })
          },
          
        })
      }
    })
  },

对应的如果前端页面的各种值需要随着函数而改变,就必须定义变量,需要在data当中声明这些变量:

 /**
   * 页面的初始数据
   */
  data: {
    Beauty: '请上传照片',
    age: '请上传照片',
    glasses: '请上传照片',
    mask: '请上传照片',
    hat: '请上传照片',
    gender: '请上传照片',
    hair_length: '请上传照片',
    hair_bang: '请上传照片',
    hair_color: '请上传照片',

显示我们上传的图片

首先得在前端加上图片组件,而图片会随着上传的图片而改变,因此图片地址src也需要变量处理:

<view class="image_view">
<image src="{{image_src}}" style="width:200px;height:200px;"></image>
</view>       

并且把这个变量加入到data当中。
我们可以在上传函数执行成功,即上传图片,生成临时路径成功的时候,把临时路径赋值给这个地址变量:

//上面和下面的代码不变
const tempFilePaths = res.tempFilePaths[0]     //上传文件的图片的本地临时文件路径列表 (本地路径)
        console.log(tempFilePaths)
        that.setData({
          image_src: res.tempFilePaths[0]
        })
		wx.cloud.uploadFile({                      //将图片上传到云存储

之后就可以显示出我们上传的图片了。

布局优化

打开我们WXSS文件,根据我们的设计需要,设计相关的布局:

.info_view{
  padding-left: 20px;
  padding-top: 10px;
  padding-right: 20px;
  padding-bottom: 20px;
}
.info{
  display: flex;
  flex-direction: column;
  
  color: blue;
}
.image_view{
  display: flex;
  justify-content: center;
  background: blanchedalmond
}
.button_view{
  display: flex;
  padding-top: 20px;
  padding-bottom: 20px;
}
.button_size{
  width: 200px;
  height: 50px;

}

博主还加了一个进度条(根据上传进度,时间,加入到上传进度当中具体参考API,并且这里的代码中,我们将wx.cloud.uploadFile存为名为uploadTask的变量,然后在最后调用onProgressUpdate(res)方法,并通过setData方法,将数据展示在前端
,识别状态栏(信息读取成功会在界面上显示加载成功,识别过程中会显示消息栏:加载中,具体参考这里)
整个AI人脸识别的小程序样例大概就出来了,界面可以进行相关的优化,这个大家可以根据自己的需要把界面优化,变得更加漂亮,博主写了这么久,也写不下去了,接下来就看大家自己去优化了,有想法就去做。我上面讲的也差不多了。这是我上传图片后的样例:
在这里插入图片描述
最后,看博主写了一天的博客了,博主不求什么打赏啥的,博主觉得转发这篇博客就是对博主最大的馈赠了,最后,祝大家新的一年里,越来越好!!!也欢迎大家关注我的个人博客,在下所有的文章都是从个人博客导入进来的,文章会首先更新在个人博客里。希望我的博客能够给大家带来收获!

与这篇文章一个系列的还有:
微信小程序从入门到入坑
天气之子

发布了22 篇原创文章 · 获赞 27 · 访问量 2845

猜你喜欢

转载自blog.csdn.net/weixin_44346470/article/details/104612542