GRPC 应用

需求

在 python 中调用 C++,对照片进行清洗,只保留有人脸的图片,,并将返回的结果保存下来。

概述

grpc 是 google 推出的一款 rpc 框架,使用HTTP/2协议并用ProtoBuf作为序列化工具。其客户端提供Objective-C、Java接口,服务器侧则有Java、Golang、C++等接口,从而为移动端(iOS/Androi)到服务器端通讯提供了一种解决方案。 当然在当下的环境下,这种解决方案更热门的方式是RESTFull API接口。该方式需要自己去选择编码方式、服务器架构、自己搭建框架(JSON-RPC)。

总而言之,grpc 是用来实现跨语言通信的。比如在你的某个功能里需要用的同事写的接口,而你们俩又不是同一种语言。此时有两种方案,一是使用.so 文件;另一种则是使用 RPC 框架。

grpc 总共有三部分:.proto文件、server端、client 端

.proto 文件

xgface_service.proto 文件如下所示:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.xgrobotics.xgface";
option java_outer_classname = "XgfaceProto";
option objc_class_prefix = "XGF";

package xgface;

// Interface exported by the server.
service XgfaceService{
  // xgface RPC service
  //
  rpc GetFeature(Request) returns (Features) {}
  rpc GetBox(Request) returns (Boxs) {}
  rpc GetLandmark(Request) returns (Landmarks) {}
  rpc GetDetectInfo(Request) returns (DetectInfo){}
  rpc GetDetectInfo_mutli_images(Request) returns (DetectInfos){}
}

message Request {
  repeated string images = 1;
}

message Point {
  float coordinate_x = 1;
  float coordinate_y = 2;
}
message Rect {
  float width = 1;
  float height = 2;
}

message Feature {
  repeated float values = 1;
}
message Features {
  repeated Feature features = 1;
}

message Box {
  Point center = 1;
  Rect  rect = 2;
  float score = 3;
}
message Boxs{
  repeated Box boxs = 1;
}

message Landmark {
  repeated Point points = 1;
  float score = 2;
}
message Landmarks {
  repeated Landmark landmarks = 1;
}

message Face{
    Box box = 1;
    Landmark landmark = 2;
    Feature feature = 3;
      map attributes = 4; 
    float pitch = 5;
    float yaw = 6;
    float roll = 7;

}
message DetectInfo {
  repeated Face faces = 1;
}
message DetectInfos {
  repeated DetectInfo detectinfos = 1;
}

proto 文件有自己的语法,并需要安装相关的依赖,在此不再赘述。

事实上,这个 proto 文件第一句规定的是使用的版本,在 service 中规定服务的输入输出是什么,在 message 中规定 message 的属性与 属性序号,属性类型。

不难看出,在服务端,应以某种语言实现 GetFeature , GetBox , GetLandmark , GetDetectInfo , GetDetectInfo_multi_images 方法以供调用。

我们需要的方法是 GetDetectInfo 方法,从 proto 中可以看出,这个方法需要一个 Request ,返回一个 DetectInfo.

在 proto 文件所在目录运行以下命令:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./proto_name.proto

会生成一个pb2文件和pb2_grpc 文件,内容如下

xgface_service_pb2.py:

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: xgface_service.proto

import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor.FileDescriptor(
  name='xgface_service.proto',
  package='xgface',
  syntax='proto3',
  serialized_pb=_b('\n\x14xgface_service.proto\x12\x06xgface\"\x19\n\x07Request\x12\x0e\n\x06images\x18\x01 \x03(\t\"3\n\x05Point\x12\x14\n\x0c\x63oordinate_x\x18\x01 \x01(\x02\x12\x14\n\x0c\x63oordinate_y\x18\x02 \x01(\x02\"%\n\x04Rect\x12\r\n\x05width\x18\x01 \x01(\x02\x12\x0e\n\x06height\x18\x02 \x01(\x02\"\x19\n\x07\x46\x65\x61ture\x12\x0e\n\x06values\x18\x01 \x03(\x02\"-\n\x08\x46\x65\x61tures\x12!\n\x08\x66\x65\x61tures\x18\x01 \x03(\x0b\x32\x0f.xgface.Feature\"O\n\x03\x42ox\x12\x1d\n\x06\x63\x65nter\x18\x01 \x01(\x0b\x32\r.xgface.Point\x12\x1a\n\x04rect\x18\x02 \x01(\x0b\x32\x0c.xgface.Rect\x12\r\n\x05score\x18\x03 \x01(\x02\"!\n\x04\x42oxs\x12\x19\n\x04\x62oxs\x18\x01 \x03(\x0b\x32\x0b.xgface.Box\"8\n\x08Landmark\x12\x1d\n\x06points\x18\x01 \x03(\x0b\x32\r.xgface.Point\x12\r\n\x05score\x18\x02 \x01(\x02\"0\n\tLandmarks\x12#\n\tlandmarks\x18\x01 \x03(\x0b\x32\x10.xgface.Landmark\"\xf5\x01\n\x04\x46\x61\x63\x65\x12\x18\n\x03\x62ox\x18\x01 \x01(\x0b\x32\x0b.xgface.Box\x12\"\n\x08landmark\x18\x02 \x01(\x0b\x32\x10.xgface.Landmark\x12 \n\x07\x66\x65\x61ture\x18\x03 \x01(\x0b\x32\x0f.xgface.Feature\x12\x30\n\nattributes\x18\x04 \x03(\x0b\x32\x1c.xgface.Face.AttributesEntry\x12\r\n\x05pitch\x18\x05 \x01(\x02\x12\x0b\n\x03yaw\x18\x06 \x01(\x02\x12\x0c\n\x04roll\x18\x07 \x01(\x02\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\")\n\nDetectInfo\x12\x1b\n\x05\x66\x61\x63\x65s\x18\x01 \x03(\x0b\x32\x0c.xgface.Face\"6\n\x0b\x44\x65tectInfos\x12\'\n\x0b\x64\x65tectinfos\x18\x01 \x03(\x0b\x32\x12.xgface.DetectInfo2\xa0\x02\n\rXgfaceService\x12\x31\n\nGetFeature\x12\x0f.xgface.Request\x1a\x10.xgface.Features\"\x00\x12)\n\x06GetBox\x12\x0f.xgface.Request\x1a\x0c.xgface.Boxs\"\x00\x12\x33\n\x0bGetLandmark\x12\x0f.xgface.Request\x1a\x11.xgface.Landmarks\"\x00\x12\x36\n\rGetDetectInfo\x12\x0f.xgface.Request\x1a\x12.xgface.DetectInfo\"\x00\x12\x44\n\x1aGetDetectInfo_mutli_images\x12\x0f.xgface.Request\x1a\x13.xgface.DetectInfos\"\x00\x42,\n\x15\x63om.xgrobotics.xgfaceB\x0bXgfaceProtoP\x01\xa2\x02\x03XGFb\x06proto3')
)




_REQUEST = _descriptor.Descriptor(
  name='Request',
  full_name='xgface.Request',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='images', full_name='xgface.Request.images', index=0,
      number=1, type=9, cpp_type=9, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=32,
  serialized_end=57,
)


_POINT = _descriptor.Descriptor(
  name='Point',
  full_name='xgface.Point',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='coordinate_x', full_name='xgface.Point.coordinate_x', index=0,
      number=1, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='coordinate_y', full_name='xgface.Point.coordinate_y', index=1,
      number=2, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=59,
  serialized_end=110,
)


_RECT = _descriptor.Descriptor(
  name='Rect',
  full_name='xgface.Rect',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='width', full_name='xgface.Rect.width', index=0,
      number=1, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='height', full_name='xgface.Rect.height', index=1,
      number=2, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=112,
  serialized_end=149,
)


_FEATURE = _descriptor.Descriptor(
  name='Feature',
  full_name='xgface.Feature',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='values', full_name='xgface.Feature.values', index=0,
      number=1, type=2, cpp_type=6, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=151,
  serialized_end=176,
)


_FEATURES = _descriptor.Descriptor(
  name='Features',
  full_name='xgface.Features',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='features', full_name='xgface.Features.features', index=0,
      number=1, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=178,
  serialized_end=223,
)


_BOX = _descriptor.Descriptor(
  name='Box',
  full_name='xgface.Box',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='center', full_name='xgface.Box.center', index=0,
      number=1, type=11, cpp_type=10, label=1,
      has_default_value=False, default_value=None,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='rect', full_name='xgface.Box.rect', index=1,
      number=2, type=11, cpp_type=10, label=1,
      has_default_value=False, default_value=None,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='score', full_name='xgface.Box.score', index=2,
      number=3, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=225,
  serialized_end=304,
)


_BOXS = _descriptor.Descriptor(
  name='Boxs',
  full_name='xgface.Boxs',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='boxs', full_name='xgface.Boxs.boxs', index=0,
      number=1, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=306,
  serialized_end=339,
)


_LANDMARK = _descriptor.Descriptor(
  name='Landmark',
  full_name='xgface.Landmark',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='points', full_name='xgface.Landmark.points', index=0,
      number=1, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='score', full_name='xgface.Landmark.score', index=1,
      number=2, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=341,
  serialized_end=397,
)


_LANDMARKS = _descriptor.Descriptor(
  name='Landmarks',
  full_name='xgface.Landmarks',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='landmarks', full_name='xgface.Landmarks.landmarks', index=0,
      number=1, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=399,
  serialized_end=447,
)


_FACE_ATTRIBUTESENTRY = _descriptor.Descriptor(
  name='AttributesEntry',
  full_name='xgface.Face.AttributesEntry',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='key', full_name='xgface.Face.AttributesEntry.key', index=0,
      number=1, type=9, cpp_type=9, label=1,
      has_default_value=False, default_value=_b("").decode('utf-8'),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='value', full_name='xgface.Face.AttributesEntry.value', index=1,
      number=2, type=5, cpp_type=1, label=1,
      has_default_value=False, default_value=0,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')),
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=646,
  serialized_end=695,
)

_FACE = _descriptor.Descriptor(
  name='Face',
  full_name='xgface.Face',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='box', full_name='xgface.Face.box', index=0,
      number=1, type=11, cpp_type=10, label=1,
      has_default_value=False, default_value=None,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='landmark', full_name='xgface.Face.landmark', index=1,
      number=2, type=11, cpp_type=10, label=1,
      has_default_value=False, default_value=None,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='feature', full_name='xgface.Face.feature', index=2,
      number=3, type=11, cpp_type=10, label=1,
      has_default_value=False, default_value=None,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='attributes', full_name='xgface.Face.attributes', index=3,
      number=4, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='pitch', full_name='xgface.Face.pitch', index=4,
      number=5, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='yaw', full_name='xgface.Face.yaw', index=5,
      number=6, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
    _descriptor.FieldDescriptor(
      name='roll', full_name='xgface.Face.roll', index=6,
      number=7, type=2, cpp_type=6, label=1,
      has_default_value=False, default_value=float(0),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[_FACE_ATTRIBUTESENTRY, ],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=450,
  serialized_end=695,
)


_DETECTINFO = _descriptor.Descriptor(
  name='DetectInfo',
  full_name='xgface.DetectInfo',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='faces', full_name='xgface.DetectInfo.faces', index=0,
      number=1, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=697,
  serialized_end=738,
)


_DETECTINFOS = _descriptor.Descriptor(
  name='DetectInfos',
  full_name='xgface.DetectInfos',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='detectinfos', full_name='xgface.DetectInfos.detectinfos', index=0,
      number=1, type=11, cpp_type=10, label=3,
      has_default_value=False, default_value=[],
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None, file=DESCRIPTOR),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=740,
  serialized_end=794,
)

_FEATURES.fields_by_name['features'].message_type = _FEATURE
_BOX.fields_by_name['center'].message_type = _POINT
_BOX.fields_by_name['rect'].message_type = _RECT
_BOXS.fields_by_name['boxs'].message_type = _BOX
_LANDMARK.fields_by_name['points'].message_type = _POINT
_LANDMARKS.fields_by_name['landmarks'].message_type = _LANDMARK
_FACE_ATTRIBUTESENTRY.containing_type = _FACE
_FACE.fields_by_name['box'].message_type = _BOX
_FACE.fields_by_name['landmark'].message_type = _LANDMARK
_FACE.fields_by_name['feature'].message_type = _FEATURE
_FACE.fields_by_name['attributes'].message_type = _FACE_ATTRIBUTESENTRY
_DETECTINFO.fields_by_name['faces'].message_type = _FACE
_DETECTINFOS.fields_by_name['detectinfos'].message_type = _DETECTINFO
DESCRIPTOR.message_types_by_name['Request'] = _REQUEST
DESCRIPTOR.message_types_by_name['Point'] = _POINT
DESCRIPTOR.message_types_by_name['Rect'] = _RECT
DESCRIPTOR.message_types_by_name['Feature'] = _FEATURE
DESCRIPTOR.message_types_by_name['Features'] = _FEATURES
DESCRIPTOR.message_types_by_name['Box'] = _BOX
DESCRIPTOR.message_types_by_name['Boxs'] = _BOXS
DESCRIPTOR.message_types_by_name['Landmark'] = _LANDMARK
DESCRIPTOR.message_types_by_name['Landmarks'] = _LANDMARKS
DESCRIPTOR.message_types_by_name['Face'] = _FACE
DESCRIPTOR.message_types_by_name['DetectInfo'] = _DETECTINFO
DESCRIPTOR.message_types_by_name['DetectInfos'] = _DETECTINFOS
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), dict(
  DESCRIPTOR = _REQUEST,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Request)
  ))
_sym_db.RegisterMessage(Request)

Point = _reflection.GeneratedProtocolMessageType('Point', (_message.Message,), dict(
  DESCRIPTOR = _POINT,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Point)
  ))
_sym_db.RegisterMessage(Point)

Rect = _reflection.GeneratedProtocolMessageType('Rect', (_message.Message,), dict(
  DESCRIPTOR = _RECT,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Rect)
  ))
_sym_db.RegisterMessage(Rect)

Feature = _reflection.GeneratedProtocolMessageType('Feature', (_message.Message,), dict(
  DESCRIPTOR = _FEATURE,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Feature)
  ))
_sym_db.RegisterMessage(Feature)

Features = _reflection.GeneratedProtocolMessageType('Features', (_message.Message,), dict(
  DESCRIPTOR = _FEATURES,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Features)
  ))
_sym_db.RegisterMessage(Features)

Box = _reflection.GeneratedProtocolMessageType('Box', (_message.Message,), dict(
  DESCRIPTOR = _BOX,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Box)
  ))
_sym_db.RegisterMessage(Box)

Boxs = _reflection.GeneratedProtocolMessageType('Boxs', (_message.Message,), dict(
  DESCRIPTOR = _BOXS,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Boxs)
  ))
_sym_db.RegisterMessage(Boxs)

Landmark = _reflection.GeneratedProtocolMessageType('Landmark', (_message.Message,), dict(
  DESCRIPTOR = _LANDMARK,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Landmark)
  ))
_sym_db.RegisterMessage(Landmark)

Landmarks = _reflection.GeneratedProtocolMessageType('Landmarks', (_message.Message,), dict(
  DESCRIPTOR = _LANDMARKS,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Landmarks)
  ))
_sym_db.RegisterMessage(Landmarks)

Face = _reflection.GeneratedProtocolMessageType('Face', (_message.Message,), dict(

  AttributesEntry = _reflection.GeneratedProtocolMessageType('AttributesEntry', (_message.Message,), dict(
    DESCRIPTOR = _FACE_ATTRIBUTESENTRY,
    __module__ = 'xgface_service_pb2'
    # @@protoc_insertion_point(class_scope:xgface.Face.AttributesEntry)
    ))
  ,
  DESCRIPTOR = _FACE,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.Face)
  ))
_sym_db.RegisterMessage(Face)
_sym_db.RegisterMessage(Face.AttributesEntry)

DetectInfo = _reflection.GeneratedProtocolMessageType('DetectInfo', (_message.Message,), dict(
  DESCRIPTOR = _DETECTINFO,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.DetectInfo)
  ))
_sym_db.RegisterMessage(DetectInfo)

DetectInfos = _reflection.GeneratedProtocolMessageType('DetectInfos', (_message.Message,), dict(
  DESCRIPTOR = _DETECTINFOS,
  __module__ = 'xgface_service_pb2'
  # @@protoc_insertion_point(class_scope:xgface.DetectInfos)
  ))
_sym_db.RegisterMessage(DetectInfos)


DESCRIPTOR.has_options = True
DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n\025com.xgrobotics.xgfaceB\013XgfaceProtoP\001\242\002\003XGF'))
_FACE_ATTRIBUTESENTRY.has_options = True
_FACE_ATTRIBUTESENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001'))

_XGFACESERVICE = _descriptor.ServiceDescriptor(
  name='XgfaceService',
  full_name='xgface.XgfaceService',
  file=DESCRIPTOR,
  index=0,
  options=None,
  serialized_start=797,
  serialized_end=1085,
  methods=[
  _descriptor.MethodDescriptor(
    name='GetFeature',
    full_name='xgface.XgfaceService.GetFeature',
    index=0,
    containing_service=None,
    input_type=_REQUEST,
    output_type=_FEATURES,
    options=None,
  ),
  _descriptor.MethodDescriptor(
    name='GetBox',
    full_name='xgface.XgfaceService.GetBox',
    index=1,
    containing_service=None,
    input_type=_REQUEST,
    output_type=_BOXS,
    options=None,
  ),
  _descriptor.MethodDescriptor(
    name='GetLandmark',
    full_name='xgface.XgfaceService.GetLandmark',
    index=2,
    containing_service=None,
    input_type=_REQUEST,
    output_type=_LANDMARKS,
    options=None,
  ),
  _descriptor.MethodDescriptor(
    name='GetDetectInfo',
    full_name='xgface.XgfaceService.GetDetectInfo',
    index=3,
    containing_service=None,
    input_type=_REQUEST,
    output_type=_DETECTINFO,
    options=None,
  ),
  _descriptor.MethodDescriptor(
    name='GetDetectInfo_mutli_images',
    full_name='xgface.XgfaceService.GetDetectInfo_mutli_images',
    index=4,
    containing_service=None,
    input_type=_REQUEST,
    output_type=_DETECTINFOS,
    options=None,
  ),
])
_sym_db.RegisterServiceDescriptor(_XGFACESERVICE)

DESCRIPTOR.services_by_name['XgfaceService'] = _XGFACESERVICE

# @@protoc_insertion_point(module_scope)

xgface_service_pb2_grpc:

# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc

import index_service_pb2 as index__service__pb2


class IndexServiceStub(object):
  """Interface exported by the server.
  """

  def __init__(self, channel):
    """Constructor.

    Args:
      channel: A grpc.Channel.
    """
    self.BuildbyPoints = channel.unary_unary(
        '/xgindex.IndexService/BuildbyPoints',
        request_serializer=index__service__pb2.BuildRequest.SerializeToString,
        response_deserializer=index__service__pb2.BuildResponse.FromString,
        )
    self.BuildbyClusters = channel.unary_unary(
        '/xgindex.IndexService/BuildbyClusters',
        request_serializer=index__service__pb2.IndexMap.SerializeToString,
        response_deserializer=index__service__pb2.Error.FromString,
        )
    self.Find = channel.unary_unary(
        '/xgindex.IndexService/Find',
        request_serializer=index__service__pb2.Feature.SerializeToString,
        response_deserializer=index__service__pb2.Cluster.FromString,
        )
    self.InsertPoint = channel.unary_unary(
        '/xgindex.IndexService/InsertPoint',
        request_serializer=index__service__pb2.Feature.SerializeToString,
        response_deserializer=index__service__pb2.Cluster.FromString,
        )
    self.InsertCluster = channel.unary_unary(
        '/xgindex.IndexService/InsertCluster',
        request_serializer=index__service__pb2.Feature.SerializeToString,
        response_deserializer=index__service__pb2.Cluster.FromString,
        )
    self.UpdateCluster = channel.unary_unary(
        '/xgindex.IndexService/UpdateCluster',
        request_serializer=index__service__pb2.Cluster.SerializeToString,
        response_deserializer=index__service__pb2.Error.FromString,
        )
    self.DeletebyCId = channel.unary_unary(
        '/xgindex.IndexService/DeletebyCId',
        request_serializer=index__service__pb2.Cluster.SerializeToString,
        response_deserializer=index__service__pb2.Error.FromString,
        )
    self.GetKdClusterCount = channel.unary_unary(
        '/xgindex.IndexService/GetKdClusterCount',
        request_serializer=index__service__pb2.Request.SerializeToString,
        response_deserializer=index__service__pb2.KDClusterCount.FromString,
        )


class IndexServiceServicer(object):
  """Interface exported by the server.
  """

  def BuildbyPoints(self, request, context):
    """index RPC service

    """
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def BuildbyClusters(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def Find(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def InsertPoint(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def InsertCluster(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def UpdateCluster(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def DeletebyCId(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def GetKdClusterCount(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')


def add_IndexServiceServicer_to_server(servicer, server):
  rpc_method_handlers = {
      'BuildbyPoints': grpc.unary_unary_rpc_method_handler(
          servicer.BuildbyPoints,
          request_deserializer=index__service__pb2.BuildRequest.FromString,
          response_serializer=index__service__pb2.BuildResponse.SerializeToString,
      ),
      'BuildbyClusters': grpc.unary_unary_rpc_method_handler(
          servicer.BuildbyClusters,
          request_deserializer=index__service__pb2.IndexMap.FromString,
          response_serializer=index__service__pb2.Error.SerializeToString,
      ),
      'Find': grpc.unary_unary_rpc_method_handler(
          servicer.Find,
          request_deserializer=index__service__pb2.Feature.FromString,
          response_serializer=index__service__pb2.Cluster.SerializeToString,
      ),
      'InsertPoint': grpc.unary_unary_rpc_method_handler(
          servicer.InsertPoint,
          request_deserializer=index__service__pb2.Feature.FromString,
          response_serializer=index__service__pb2.Cluster.SerializeToString,
      ),
      'InsertCluster': grpc.unary_unary_rpc_method_handler(
          servicer.InsertCluster,
          request_deserializer=index__service__pb2.Feature.FromString,
          response_serializer=index__service__pb2.Cluster.SerializeToString,
      ),
      'UpdateCluster': grpc.unary_unary_rpc_method_handler(
          servicer.UpdateCluster,
          request_deserializer=index__service__pb2.Cluster.FromString,
          response_serializer=index__service__pb2.Error.SerializeToString,
      ),
      'DeletebyCId': grpc.unary_unary_rpc_method_handler(
          servicer.DeletebyCId,
          request_deserializer=index__service__pb2.Cluster.FromString,
          response_serializer=index__service__pb2.Error.SerializeToString,
      ),
      'GetKdClusterCount': grpc.unary_unary_rpc_method_handler(
          servicer.GetKdClusterCount,
          request_deserializer=index__service__pb2.Request.FromString,
          response_serializer=index__service__pb2.KDClusterCount.SerializeToString,
      ),
  }
  generic_handler = grpc.method_handlers_generic_handler(
      'xgindex.IndexService', rpc_method_handlers)
  server.add_generic_rpc_handlers((generic_handler,))

暂时先不管这两个系统生成的文件,当我们碰到问题时再回头来看。

server 端

server 端是由别人实现的,在此不贴出源代码。

client端

端口

在 server 端,service 会通过 IP 和端口号暴露出来。

_HOST = 'xxx.xxx.xxx.xxx'    #IP地址
_PORT1 = '50000'        #xgface_service端口,使用 detectinfo 方法输入一个base64数组返回box,landmark,feature,attributes,pitch,yaw,roll
_PORT2 = '50001'        #index_service端口,使用 insertpoint 方法输入 feature 返回一个id

图片转化为 base64字符串

通过沟通得知,Request 需要的输入是一个 base64数组,返回的 DetectInfo 是一个自定义的数据结构,相当于一个 face 数组,每个 face 也是一个自定义数据结构,包含 box,landmark等

#将图片转化为 base64方法
def convert_base64(path):
    with open(path,'rb+') as f:
        data =  base64.b64encode(f.read())
    return [data]

调用 xgface_service

#调用xgface_service 服务
def DetectInfo(data):
    channel = grpc.insecure_channel(_HOST+':'+_PORT1)
    stub = xgface_service_pb2_grpc.XgfaceServiceStub(channel)
    response = stub.GetDetectInfo(xgface_service_pb2.Request(images=data))#.__str__()
    #print(response)

#可能返回多个 face( 若图中包含多个 face)
if len(response.faces):
    boxs = []
    landmarks = []
    features = []
    re_attributes=[{} for i in range(5)]        #这是一个包含五个字典的数组
    pitches = []
    yaws = []
    rolls = []

    #print(len(response.faces))
    s = ['age','gender','glass','hat','race']

    #每一个 face 中的特征存入对应数组
    for i in range(0,len(response.faces)):
        boxs.append(response.faces[i].box)
        landmarks.append(response.faces[i].landmark)
        features.append(response.faces[i].feature)
        print(response.faces[i].attributes['age'])
        for j in range(5):
            re_attributes[i]['%s'%s[j]] = response.faces[i].attributes['%s'%s[j]]
        pitches.append(response.faces[i].pitch)
        yaws.append(response.faces[i].pitch)
        rolls.append(response.faces[i].roll)
        
    return boxs,landmarks,features,re_attributes,pitches,yaws,rolls
else:
    print('不存在人脸')

在 DetectInfo 方法中,channel 和 stub 语句是固定的,用来构建一个通信管道,第三句是用这个管道来调用方法,在这里我们调用了 DetectInfo 方法,这个方法需要一个 Request作为参数,那么我们就给一个 Request 参数,而查看 pb2 文件 Request的 fields 中的 name,发现 Request 需要一个 images 参数,这就是我们通过图片生成的 base64数组。

调用 index_service服务

获得另一个所需数据 ID 是通过另一个 service获取的,在此不再贴出
proto文件,只给出 client 端代码:

#调用index_service服务
def GetFaceID(feature):
    feature_points = feature.__str__().split('values:')[1: ]        #将 feature 中的字符串切分处理得到192个 value
    pointlist = [float(x.replace('\n',' ').strip()) for x in feature_points]    #将这些 value 组成一个 float数组
    #print(featurelist)
    #print(len(featurelist))

    channel = grpc.insecure_channel(_HOST+':'+_PORT2)
    stub = index_service_pb2_grpc.IndexServiceStub(channel)
    response = stub.InsertPoint(index_service_pb2.Feature(value=pointlist))
    #print(response)

    return response

遍历所有文件夹

由于 photos 文件夹下还有许多子文件夹,并没有包含所有的图片,我们还要遍历这个文件夹下所有的文件夹,拿到所有图片的路径。

#递归遍历一个路径下的所有文件夹与子文件夹,并把名称中包含 jpg 的文件路径加入 filename_list 列表
def travel(path,filename_list):
    if os.path.isfile(path) :
        if 'jpg' in path:
            filename_list.append(path)
    else:
        for dir in os.listdir(path):
            travel(path+'/'+dir,filename_list)

return filename_list

主程序执行

#执行主程序,给定各初始条件并做记录
if __name__ == '__main__':
    start_time = datetime.now()
    start_date = start_time.strftime('%b' '%d')
    root_dir  = "/Users/junjieluo/MyGit/xgface/photos"
    filename_list = travel(root_dir,[])
    print(filename_list)
    face_count = 0
    noface_count = 0
    file_count = 0

    for filename in filename_list:
        try:
            data = convert_base64(filename)
            alist = DetectInfo(data)
            #print(alist[3])

            #返回的 alist是一个二维数组,alist[i]包含第 i 张脸的各特征
            #去掉各个特征中多余的字符与换行符
            if alist:
                for i in range(len(alist[0])):
                    box = str(alist[0][i]).replace('\n','  ')       
                    landmark = str(alist[1][i]).replace('\n','  ').replace('{','').replace('}','').replace('points','').replace('coordinate_x:','').replace('coordinate_y:','')
                    feature = str(alist[2][i]).replace('\n','  ').replace('{','').replace('}','').replace('values:','')
                    attributes = str(alist[3][i])

                    pitch = str(alist[4][i])
                    yaw = str(alist[5][i])
                    roll = str(alist[6][i])

                    ID = str(GetFaceID(alist[2][i])).replace('\n','  ')

                    #写入1.txt,每张 face 占一行
                    with open('1.txt','a+') as f:
                        f.write(filename+' ')
                        f.write(ID.strip('id:')+' ')
                        f.write(landmark+' ')
                        f.write(feature +' ')
                        f.write(attributes + '  ')
                        f.write(pitch+'  ')
                        f.write(yaw+'  ')
                        f.write(roll+'  ')
                        f.write('\n')
                    face_count += 1

                print(filename+'写入成功')

            #如果没有 face, 删除此图片
            else:
                noface_count += 1
                os.remove(filename)

        except:
            continue
            print('一个图片错误')

        file_count += 1

    end_time = datetime.now()
    do_time = (end_time - start_time).seconds//3600

    #记录执行时间,日期与数量
    with open('/Users/junjieluo/MyGit/xgface/photos/log.txt','a+',encoding='utf-8') as f:
        f.write("{} 处理了 {} 张图片,得到 {} 个 face_id,丢弃了{} 张无脸的图,耗时 {} 小时\n".format(start_date,file_count,face_count,noface_count,do_time))

你可能感兴趣的:(GRPC 应用)