共计 4152 个字符,预计需要花费 11 分钟才能阅读完成。
Protobuf 是 google 开发的一个序列化和反序列化的协定库,咱们能够本人设计传递数据的格局,通过 .proto 文件
定义咱们的要传递的数据格式。例如,在深度学习中罕用的 ONNX 替换模型就是应用 .proto
编写的。咱们能够通过多种前端 (MNN、NCNN、TVM 的前端) 去读取这个.onnx 这个模型,然而首先你要装置 protobuf。
在之前的博文中曾经简略介绍了 onnx,其中 onnx.proto 就代表了 onnx 模型的根本数据结构。一般来说,protobuf 常常搭配 Cmake 应用,Cmake 有官网的 modules,能够通过简略的几个命令 protobuf_generate_cpp
来生成对应的 .pb.cc
和.pb.h
。
简略的例子:
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS foo.proto)
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS EXPORT_MACRO DLL_EXPORT foo.proto)
protobuf_generate_python(PROTO_PY foo.proto)
add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})
target_link_libraries(bar ${Protobuf_LIBRARIES})
然而这个例子太简略了,如果咱们的.proto 文件只有一个或者说都只在一个目录里,那用这个命令没什么故障 …
但如果是这种状况,咱们的文件目录如下:
├── CMakeLists.txt
├── README.md
├── meta
│ └── proto
│ ├── CMakeLists.txt
│ └── common
│ ├── bar
│ │ ├── CMakeLists.txt
│ │ └── bar.proto
│ └── foo
│ ├── CMakeLists.txt
│ └── foo.proto
└── src
├── CMakeLists.txt
├── c_proto.cc
└── c_proto.hh
其中 foo.proto
文件如下:
message foo_msg
{optional string name = 1;}
bar.proto
的文件如下:
import "common/foo/foo.proto";
message bar_msg
{
optional foo_msg foo = 1;
optional string name = 2;
}
如上,bar 文件援用 foo,而且这两个不在一个目录,如果间接应用 protobuf_generate_cpp 来生成,间接会报错。(这个例子取自 Yu 的一篇博文)
也想过把他俩放到同一个目录 … 而后 bar.proto
中 import 的代码就要批改,尽管这样能够,但显然是不适宜大型的我的项目。
而这个大型项目显然就是 mediapipe… 折磨了我良久。
对于 mediapipe 的具体介绍在另一篇文章。mediapipe 中应用了大量的 ProtoBuf 技术来示意图构造,而且 mediapipe 原生并不是采纳 cmake 来构建我的项目,而是应用 google 自家研发的 bazel,这个我的项目构建零碎我就不评估了,而当初我须要应用 Cmake 来对其进行构建。
这也是噩梦的开始,mediapipe 的.proto 文件很多,外围的 framework 的目录下存在很多的.proto 文件,根目录和子目录都有.proto 文件:
而且每个 proto 文件之间存在援用的程序,framework 根目录下的 calculator.proto
文件:
// mediapipe/framework/calculator.proto
syntax = "proto3";
package mediapipe;
import public "mediapipe/framework/calculator_options.proto";
import "google/protobuf/any.proto";
import "mediapipe/framework/mediapipe_options.proto";
import "mediapipe/framework/packet_factory.proto";
import "mediapipe/framework/packet_generator.proto";
import "mediapipe/framework/status_handler.proto";
import "mediapipe/framework/stream_handler.proto";
每个.proto 文件都 import 了其余目录下的文件,这里的 import
相似于 C ++ 中的 include,然而这里的 import 又能够互相援用,例如上述的 status_handler.proto
也援用了mediapipe_options.proto
。
如果间接对上述所有的.proto 文件间接应用 protobuf_generate_cpp
命令,会间接报错,因为这些文件不在一个目录,而且 import 的绝对目录也无奈剖析。另外,不同目录内的 .cc
文件会援用相应目录生成的 .pb.h
文件,咱们须要生成的 .pb.cc
和.pb.h
在原始的目录中,这样才能够失常援用,要不然须要批改其余源代码的 include 地址,比拟麻烦。
CLion 中 Cmake 来编译 proto 生成的 .pb.cc
和.pb.h
不在原始目录,而是集中在 cmake-build-debug(release)中,咱们额定须要将其中生成的 .pb.cc
和.pb.h
文件挪动到原始地址(Clion 的状况是这样)。
正确批改 cmake
对于这种状况,比拟适合的做法是间接应用命令进行生成。
首先找到所有须要编译的.proto 文件:
file(GLOB protobuf_files
mediapipe/framework/*.proto
mediapipe/framework/tool/*.proto
mediapipe/framework/deps/*.proto
mediapipe/framework/testdata/*.proto
mediapipe/framework/formats/*.proto
mediapipe/framework/formats/annotation/*.proto
mediapipe/framework/formats/motion/*.proto
mediapipe/framework/formats/object_detection/*.proto
mediapipe/framework/stream_handler/*.proto
mediapipe/util/*.proto
mediapipe/calculators/internal/*.proto
)
接下来,定义相干的目录地址,PROTO_META_BASE_DIR
为编译之后生成文件的目录。PROTO_FLAGS
很重要,指定编译 .proto
文件时的总的寻找门路,.proto
中的 import 命令依据依据这个地址去连贯其余的 .proto
文件:
SET(PROTO_META_BASE_DIR ${CMAKE_CURRENT_BINARY_DIR})
LIST(APPEND PROTO_FLAGS -I${CMAKE_CURRENT_SOURCE_DIR})
设置好之后,通过 FOREACH
去循环之前的.proto 文件,顺次编译每个文件,而后将生成的 .pb.cc
和.pb.h
挪动回原始的目录,至此就能够失常工作了。
FOREACH(FIL ${protobuf_files})
GET_FILENAME_COMPONENT(FIL_WE ${FIL} NAME_WE)
string(REGEX REPLACE ".+/(.+)\\..*" "\\1" FILE_NAME ${FIL})
string(REGEX REPLACE "(.+)\\${FILE_NAME}.*" "\\1" FILE_PATH ${FIL})
string(REGEX MATCH "(/mediapipe/framework.*|/mediapipe/util.*|/mediapipe/calculators/internal/)" OUT_PATH ${FILE_PATH})
set(PROTO_SRCS "${CMAKE_CURRENT_BINARY_DIR}${OUT_PATH}${FIL_WE}.pb.cc")
set(PROTO_HDRS "${CMAKE_CURRENT_BINARY_DIR}${OUT_PATH}${FIL_WE}.pb.h")
EXECUTE_PROCESS(COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTO_FLAGS} --cpp_out=${PROTO_META_BASE_DIR} ${FIL}
)
message("Copying" ${PROTO_SRCS} "to" ${FILE_PATH})
file(COPY ${PROTO_SRCS} DESTINATION ${FILE_PATH})
file(COPY ${PROTO_HDRS} DESTINATION ${FILE_PATH})
ENDFOREACH()
参考链接
http://blog.argcv.com/article…
https://www.v2ex.com/t/602363
https://stackoverflow.com/que…
撩我吧
- 如果你与我气味相投于此,老潘很违心与你交换;
- 如果你喜爱老潘的内容,欢送关注和反对。
- 如果你喜爱我的文章,心愿点赞???? 珍藏 ???? 评论 ???? 三连一下~
想晓得老潘是如何学习踩坑的,想与我交换问题~ 请关注公众号「oldpan 博客」。
老潘也会整顿一些本人的私藏,心愿能帮忙到大家,点击神秘传送门获取。