需求
在 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))