gRPC(一)构建人证比对微服务:服务定义与实现

同系列文章:

前言
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc、grpc-java、grpc-go。其中 C 版本支持 C、C++、Node.js、Python、Ruby、Objective-C、PHP 和 C# 语言。
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性,这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

服务定义
FrService.proto

syntax = "proto3";

option csharp_namespace = "Com.FirstSolver.FR";

option java_multiple_files = true;
option java_package = "Com.FirstSolver.FR";
option java_outer_classname = "FrServiceProto";

option optimize_for = SPEED; // 优化:速度

package splash;

// --------------- 服务定义 ---------------
service FrService {
	// 比较当前图像和目标图像,给出相似度得分+人脸框位置
	rpc Compare(stream FaceIdRequestMsg) returns (stream FaceIdResponseMsg) {}
}

// --------------- 消息定义 ---------------
// 图像数据
message FaceIdRequestMsg {
	// 图像编号
	int32 index = 1;

	// 图像数据
	bytes photo = 2;
}

// 人脸矩形框
message FrFaceRect {
	int32 top = 1;
	int32 bottom = 2;
	int32 left = 3;
	int32 right = 4;
}

// 比对结果
message FaceIdResponseMsg {
	// 图像编号
	int32 index = 1;

	// 比对状态 
	int32 status = 2;
	
	// 相似度得分
	float score = 3;

	// 人脸框位置
	FrFaceRect face = 4;
}

编译命令文件
generate_protos.bat

@rem 自动生成C#代码

setlocal

@rem 更改当前目录为批处理本身的目录
cd /d %~dp0

set TOOLS_PATH=packages\Grpc.Tools.1.6.1\tools\windows_x64

%TOOLS_PATH%\protoc.exe --proto_path=./FrService/protos --csharp_out=FrService ./FrService/protos/FrService.proto --grpc_out=FrService --plugin=protoc-gen-grpc=%TOOLS_PATH%\grpc_csharp_plugin.exe

endlocal

服务实现
FrServiceImpl.cs

using Com.FirstSolver.Splash;
using Grpc.Core;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;

namespace Com.FirstSolver.FR
{
    public class FrServiceImpl : FrService.FrServiceBase, IDisposable
    {
        public delegate void FaceIdCompareEventHandler(object sender, FaceIdCompareEventArgs e);
        public event FaceIdCompareEventHandler OnFaceIdCompareEvent = null;

        /// <summary>
        /// 人证比对引擎
        /// </summary>
        protected HWFRCore FaceReader = null;

        /// <summary>
        /// 构造函数
        /// </summary>
        public FrServiceImpl()
        {   // 初始化人证比对引擎
            FaceReader = new HWFRCore();
        }

        // 人证比对
        public override async Task Compare(IAsyncStreamReader<FaceIdRequestMsg> requestStream, IServerStreamWriter<FaceIdResponseMsg> responseStream, ServerCallContext context)
        {
            FaceIdCompareEventArgs Args = new FaceIdCompareEventArgs();

            byte[] ReferenceFeature = null;
            while (await requestStream.MoveNext())
            {
                Args.CountAll++;

                FaceIdRequestMsg RequestMsg = requestStream.Current;
                FaceIdResponseMsg ResponseMsg = new FaceIdResponseMsg() { Index = RequestMsg.Index, Status = 0 };
                try
                {
                    using (MemoryStream ms = new MemoryStream(RequestMsg.Photo.ToByteArray()))
                    {
                        using (Bitmap bmp = new Bitmap(ms))
                        {
                            byte[] GrayLinearArray = bmp.ToGrayLinearArray(out byte[] RgbLinearArray);

                            // 检测人脸
                            bool IsOK = false;
                            byte[] SourceFeature = null;
                            if (FaceReader.DetectOne(GrayLinearArray, RgbLinearArray, bmp.Width, bmp.Height, out FR_TFACEINFO FaceInfo) == 1)
                            {   // 提取人脸特征
                                SourceFeature = FaceReader.ExtractFeature(GrayLinearArray, bmp.Width, bmp.Height, ref FaceInfo);
                                if (SourceFeature != null) IsOK = true;
                            }

                            if (IsOK)
                            {
                                FR_TRECT Face = FaceInfo.m_FaceRect;
                                ResponseMsg.Status = 1; // 成功
                                ResponseMsg.Face = new FrFaceRect() { Top = Face.top, Bottom = Face.bottom, Left = Face.left, Right = Face.right };
                                if (RequestMsg.Index == 1)
                                {   // 用于比对的基准图像
                                    ReferenceFeature = SourceFeature;
                                }
                                else if (ReferenceFeature != null)
                                {   // 比对人脸特征
                                    if (FaceReader.Compare(SourceFeature, ReferenceFeature, out float fScore))
                                    {
                                        ResponseMsg.Score = fScore;
                                        Args.CountPass++;
                                    }
                                    else
                                    {
                                        ResponseMsg.Status = 0; // 失败
                                        Args.CountDeny++;
                                    }
                                }
                                else
                                {
                                    Args.CountException++;
                                }
                            }
                        }
                    }
                }
                catch
                {
                    Args.CountException++;
                }

                await responseStream.WriteAsync(ResponseMsg);
            } // End While

            OnFaceIdCompareEvent?.Invoke(this, Args);
        }

        /// <summary>
        /// 资源释放
        /// </summary>
        public void Dispose()
        {   // 关闭人证比对引擎
            if (FaceReader != null) FaceReader.Dispose();
        }
    }

    public class FaceIdCompareEventArgs
    {
        /// <summary>
        /// 图像处理数
        /// </summary>
        public int CountAll = 0;

        /// <summary>
        /// 比对通过数
        /// </summary>
        public int CountPass = 0;

        /// <summary>
        /// 比对失败数
        /// </summary>
        public int CountDeny = 0;

        /// <summary>
        /// 比对异常数
        /// </summary>
        public int CountException = 0;
    }
}

Comments are closed.