💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 人脸识别 人脸识别,特别是二维的人脸识别现在应该是非常成熟了。 而且。。自此IPhone X之后,各个安卓手机也都开始增加人脸识别。虽然安卓常见的二维识别安全的远低于IPhone X三维的识别,但并不影响我们玩玩。 这里,我将根据Azure Cognitive Service中的Face API来实现一个很简单的人脸识别Demo。 > 当然不是自己写的识别服务ˋ( ° ▽、° ) ## 技术选用 1. 客户端:微信小程序 选用的理由非常简单,和电脑相比,手机的摄像头明显好不止一个档次。 同时,微信小程序非常接近Web开发的模式,还提供了Camera组件方便我们调用。 2. 服务端:ASP.NET Core 顺手 + Azure有提供对应的.net Core版SDK。 ## 准备工作 首先,我们需要有一个Azure订阅用来获取Face API的key。 > 国内世纪互联版的可以通过身份证进行1元试用注册。 > > 国际版需要VISA或者MasterCard信用卡才能注册,且注意**所在地区不能选中国**。 > > 这里我用的是国际版Azure。 然后,我们转到[Face API Reference](https://westus.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236)。在这里我们可以看到Face API的所有功能介绍与调用方式。 根据阅读文档,调用的大致流程如下: 1. 创建PersonGroup PUT /persongroups/{personGroupId} 2. 创建Person POST /persongroups/{personGroupId}/persons ```json { "name": "person1", "usuerData": "UserData(optional)" } ``` 3. 添加人脸数据(Add Face) POST /persongroups/{personGroupId}/persons/{personId}/persistedFaces > 参数有些多这里就不具体写了 > > 值得关注的是,有两种上传方式 > 1. 通过`application/octet-stream`直接上传图片文件。 > 2. 通过`application/json`上传对应图片文件的url。 4. 重复步骤2, 3添加所有的数据 每个Person最多可以有248张人脸数据。 > Each person entry can hold up to 248 faces. 5. 训练人脸识别模型 POST /persongroups/{personGroupId}/train > 在训练期间可以调用`/persongroups/{personGroupId}/training`来获取训练状态。 至此,人脸识别的模型就训练好了,我们之后所要做的就是调用这个模型。 > 我根据SDK,简单的写了一个控制台应用(FaceAPIDemo.Console),可以用来简化实现步骤2~5。当然直接用POSTMAN调用也行。 调用流程大概如下: 1. 调用Detect API来识别人脸并获取对应的Face Id。 2. 将Face Id传递给Identity API,来获取识别结果。 具体的内容,我会在服务端代码中细说。 ## 构建服务器端 首先还是建立一个Empty Web应用,我们通过NuGet安装Face API的SDK:`Microsoft.ProjectOxford.Face.DotNetCore`,以及用于图像压缩的库:`Magick.NET-Q8-AnyCPU`。 然后,在Startup中向服务容器添加FaceServiceClient以及MVC服务。 ```cs public void ConfigureServices(IServiceCollection services) { // 添加FaceServiceClient用来调用Azure Face API // 第二个参数为Face API的终结点 services.AddTransient(_ => new FaceServiceClient("Your Cognitive Service Key", "https://eastasia.api.cognitive.microsoft.com/face/v1.0/")); services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); } ``` 随后,便是添加熟悉的Controller。 这里我们定义两个API。 1. api/Face/Upload 上传图片返回FaceId和人脸位置信息 2. api/Face/Identify 根据FaceId识别对应实体 最后的代码如下: ```cs [Produces("application/json")] [Route("api/[Controller]")] public class FaceController : Controller { private readonly FaceServiceClient _faceClient; private const string groupId = "Your Group Id"; public FaceController(FaceServiceClient faceClient) { _faceClient = faceClient; } /// <summary> /// 上次图片获取Face Id /// </summary> /// <param name="image">待识别的图片</param> /// <returns></returns> [Route("[Action]")] [HttpPost] public async Task<JsonResult> Upload(IFormFile image) { // 启用图片压缩,提高传输速度 var magickImage = new MagickImage(image.OpenReadStream()) { Quality = 50 }; var faces = await _faceClient.DetectAsync(new MemoryStream(magickImage.ToByteArray())); // 返回Face Id以及人脸位置信息 return Json(faces.Select(face => new { Id = face.FaceId, Rect = face.FaceRectangle })); } /// <summary> /// 根据Face Id识别人脸 /// </summary> /// <param name="model"></param> /// <returns></returns> [Route("[Action]")] [HttpPost] public async Task<JsonResult> Identify([FromBody]IdentifyModel model) { // 识别人脸 var identifyResults = await _faceClient.IdentifyAsync(groupId, model.Faces, 0.6f); List<IdentifyResult> result = new List<IdentifyResult>(); foreach (var item in identifyResults) { // 跳过无识别结果的人脸 if (item.Candidates.Length == 0) { continue; } // 获取第一个识别结果的对应实体 var person = await _faceClient.GetPersonAsync(groupId, item.Candidates.First().PersonId); result.Add(new IdentifyResult { Name = person.Name, StudentId = person.UserData }); } return Json(result); } } ``` 因为只是简单的演示Demo,我这边把Key和GroupId都写死了,实际应用中可以对应的修改。 ## 构建微信小程序 其实Demo介绍到这里,也没太多要做的了。 我们的微信小程序需要做的只是两件事: 1. 通过Camera组件捕捉包含人脸的图片。 2. 调用上一步写好的API。 这里我就不写小程序创建的步骤了,以后有空的会单独开一个章节来介绍。 这里我们创建了Index界面,相关代码如下: index.wxml ```html <!--pages/index/index.wxml--> <!-- 调用Camera组件来捕捉图像 --> <camera device-position="front" flash="off" binderror="error" style="width: 750rpx; height: 750rpx;"> <!-- 由于Camera是原生组件,只能通过cover-view才能覆盖在其上方 --> <block wx:for="{{faces}}" wx:key="id"> <!-- 通过CSS实现矩形框 --> <cover-view class="box" style="left:{{item.rect.left}}px;top:{{item.rect.top}}px;width:{{item.rect.width}}px;height:{{item.rect.height}}px;"> </cover-view> </block> </camera> <button type="primary" bindtap="takePhoto">识别</button> <!-- 显示人脸数据 --> <view wx:for="{{faces}}" wx:key="id"> <view>Face {{index}}:</view> <view>{{item.id}}</view> </view> <view wx:for="{{results}}" wx:key="studentId"> <view>Name: {{item.name}}</view> <view>StudentId: {{item.studentId}}</view> </view> ``` index.wxss ```css /* pages/index/index.wxss */ /* * 用于标出人脸位置 */ .box { border: 5rpx solid green; position: relative; } ``` index.js ```js // pages/index/index.js // 定义API终结点 const baseUrl = 'http://localhost:5000' Page({ // 页面的初始数据 data: { // 保存FaceId和人脸位置信息 faces: [], // 保存识别到的实体信息 results: [] }, // 步骤图像,并上传到UploadAPI takePhoto() { let that = this // 获取Camera上下文 const ctx = wx.createCameraContext() // 捕捉图像 ctx.takePhoto({ quality: 'low', success: (res) => { // 在捕捉成功后将图片直接上传到Upload API wx.uploadFile({ url: baseUrl + '/api/Face/Upload', filePath: res.tempImagePath, name: 'Image', success: function (res) { let obj = JSON.parse(res.data) // 保存检测到的人脸数据 that.setData({ faces: obj, results: [] }) // 若检测到人脸就就进一步调用识别API if(obj.length > 0) { that.identifyFace(obj.map(face => face.id)) } } }) } }) }, // 输出错误信息 error(e) { console.log(e.detail) }, // 调用Identify API identifyFace(faceIds) { let that = this wx.request({ url: baseUrl + '/api/Face/Identify', method: 'POST', data: { faces: faceIds }, dataType: 'json', success: function(res) { that.setData({ results: res.data }) } }) } }) ``` 需要讲的部分都已经注释在代码当中了,如果有问题的欢迎开Issue讨论。 最后放一张效果图。。。算了还是不放了。。大家有兴趣单独联系吧。 ## 总结 这样一个流程走下来,其实并没有什么特别困难的地方。 对应类似于人脸识别这样商业化成熟的技术,我们所需做的其实也就是调用相应服务的API就完工了。 最后,所有的代码已经上传Github: [https://github.com/yiluomyt/FaceAPIDemo](https://github.com/yiluomyt/FaceAPIDemo),觉得有帮助的可以给个Star⭐,欢迎提Issue讨论。