TengineInferPipe开源啦,超详细使用教程奉上
$article.post_title}

2021-09-24

TengineInferPipe 是由 OPEN AI LAB 基于mediapipe框架进行改进、迭代的一套低代码、模块化的算法落地框架。通过解析部署配置文件,构建整个部署流程。可以用于快速构建算法部署sdk,以及可以作为一些可视化部署方案的中间层。


TengineInferPipe开源啦.jpg


使用过程

以下内容以使用tengine-lite作为后端推理引擎部署检测模型为例。

编译 Tengine

下载 Tengine 源码,位于 Tengine 的分支 tengine-lite 上:

git clone -b tengine-lite https://github.com/OAID/Tengine.gitTengine

执行编译

cd Tenginemkdir build cd buildcmake ..makemake install

编译完成后 build/install/lib 目录会生成 libtengine-lite.so 文件,如下所示:

install├── bin│ ├── tm_benchmark├── include│ └── tengine_c_api.h└── lib└── libtengine-lite.so

编译inferpipe动态运行库

环境需求

• bazel >= 3.7 参考 bazel安装说明 安装bazel到系统中,或者下载对应

版本Releases · bazelbuild/bazel · GitHub的可执行文件放到/usr/bin

目录下

• gcc >= 7.4• opencv >= 3.4

拷贝tengine动态库

cdmkdir tenginecp -r/build/install/* ./tengine/

生成inferpipe动态库

mkdir bazel_buildbazel --output_base=./bazel_build build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/object_detection:libdesktop_tengine_calculators.so

有关bazel创建c++工程时的用法可以查看C / C++ Rules - Bazel main

根据上述命令bazel在创建c++工程时,使用了

/mediapipe/examples/desktop/object_detection

文件夹下的BUILD文件中的name为

libdesktop_tengine_calculators.so的cc_binary

规则生成的库文件在

/bazel-bin/mediapipe/examples/desktop/object_detection

目录下头文件见

/mediapipe/interface/inferpipe_tengine.h


在工程中使用inferpipe

示例程序见/使用cmake构建,调用SDK编译时生成的库文件和头文件

复制所需的依赖项

mkdir tenginecp -r/build/install/* ./tengine/mkdir includecp/mediapipe/interface/inferpipe_tengine.h ./include/mkdir libscp/bazel-bin/mediapipe/examples/desktop/object_detection/libdesktop_tengine_calculators.so ./libs/

编译工程

mkdir buildcd buildcmake ..make -j`nproc`

得到demo_run_detect_main文件用于测试检测

测试检测示例

执行命令

 ./demo_run_detect_main ${构建计算流程的配置文件} ${计算流程输入节点的名称} ${计算流程输出节点的名称} ${测试视频路径}

 其中${计算流程输入节点的名称} ${计算流程输出节点的名称}两个值可以参考${构建计算流程的配置文件}中的input_stream

retinanet export LD_LIBRARY_PATH=../tengine/lib/$LD_LIBRARY_PATH./demo_run_detect_main ../object_detection_retina_x86.pbtxt input_frame output_detect ../test.mp4 yolov5 export LD_LIBRARY_PATH=../tengine/lib/$LD_LIBRARY_PATH./demo_run_detect_main ../object_detection_yolov5_x86.pbtxt input_frameoutput_detect ../test.mp4

 程序默认保存每一帧的检测结果到build目录


TengineInferPipe开源啦1.jpg

运行过程解析


部分场景下用户希望使用配置文件在不改动代码的情况下运行不同的模型,或者希望通过图形界面改动模型部署方法来运行模型。此时一个通过配置文件驱动的算法部署sdk就存在很大的实用意义。考虑到同类模型的共性,以及输入数据预处理、推理前向、网络输出后处理等不同阶段的差异性,参考mediapipe中将部署流程拆分成多个计算节点,不同节点之间通过输入、输出数据的名称决定整个流程的结构。使用protobuf格式的配置文件,通过mediapipe可视化工具可以直观的查看配置文件中的部署流程及数据流向。


下面的说明以retinanet检测为例(配置文件见object_detection_retina_x86.pbtxt):

TengineInferPipe开源啦2.jpg

一般该类检测网络执行的流程分为以下几个步骤:



数据预处理:


– ImageTransformation:将图像数据变换为指定的尺寸处理等操作;代码见image_transformation_calculator.cc;常用参数包括:

• output_width:需要变换到的图像宽度

• output_height:需要变换到的图像高度

– TengineConverterCalculator:将图像数据变换为推理引擎需求的形状,进行归一化处理等操作;代码见tengine_converter_calculator.cc;常用参数包括:

• tensor_mean:数据进行归一化处理时的均值

• tensor_scale:数据进行归一化处理时的比例;dst = (src-tensor_mean)*tensor_scale



推理前向:


– TengineInferenceCalculator:使用tengine作为后端推理引擎,加载模型、将数据放到网络、运行网络前向;代码见tengine_inference_calculator.cc

• model_path:模型路径

• data_type:网络输入数据的类型,目前支持"fp32"和"uint8";值为"uint8"时该计算节点需要输出量化参数

• output_num:网络输出分支数

• max_dim:网络输出的数据最大维度

• tengine_backend:tengine-lite运行的接口,目前支持"cpu"和"timvx"



网络输出后处理:


– RetinaAnchorsCalculator:生成用于计算检测目标位置的anchor框;代码见retina_anchors_calculator.cc;常用参数包括:

• num_layers:不同stride的输出数量

• min_scale、max_scale、input_size_height、input_size_width、anchor_offset_x、anchor_offset_y、strides、aspect_ratios:用于计算预设anchor位置的参数

– TengineTensorsToDetectionsCalculator:根据网络输出和anchor框得到目标检测位置;代码见tengine_retina_tensors_to_detections_calculator.cc;常用参数包括:

• num_classes:检测类别

• num_boxes:生成的anchor总数

• num_coords:单个特征图点上产生的anchor数量

• min_score_thresh:检测阈值

• sigmoid_score:最终产生的网络结果是否需要进行sigmoid(部分移动端npu设备上的模型经常去掉网络最后一层的sigmoid来加快计算速度)

• data_type:处理的数据类型,目前支持"fp32"和"uint8";值为"uint8"时该计算节点需要输入量化参数

– NonMaxSuppressionCalculator:对得到的目标检测位置做非极大值抑制;代码见non_max_suppression_calculator.cc;常用参数包括:

• overlap_type:计算位置重叠度的方式,支持

UNSPECIFIED_OVERLAP_TYPE, JACCARD, MODIFIED_JACCARD, INTERSECTION_OVER_UNION

• return_empty_detections:非极大值抑制的结果为空时是否返回一个空的结果



将推理流程拆分成多个计算节点的好处有:


1. 当遇到不同训练参数(输入大小、数据归一化参数、预设anchor位置等)的retinanet模型,只需要修改相应的参数即可进行相应的推理;框架使用者的灵活性较大;


2. ImageTransformationCalculator、RetinaAnchorsCalculator、NonMaxSuppressionCalculator 的代码可用于不同推理框架;TengineConverterCalculator、TengineInferenceCalculator的代码可用于tengine推理框架的不同模型;添加新模型支持时可以减少开发量;

添加新的模型支持

以在tengine作为后端时添加一个分类网络模型支持作为示例,数据预处理和推理前向涉及到的计算节点与模型类型无关,可以使用已有的计算节点并更改其对应参数,因此只需要添加一个对网络输出的结果转换为分类结果的后处理节点TengineTensorsToClassificationsCalculator即可:


添加计算节点所需参数定义

在mediapipe/calculators/tengine文件夹下添加该节点的参数定义文件tengine_tensors_to_classification_calculator.proto,设置参数结构体名称为"TengineTensorsToClassificationsCalculatorOptions",定义方法遵循protobuf文档;


在mediapipe/calculators/tengine/BUILD文件中添加mediapipe_proto_library类型的tengine_tensors_to_classification_calculator_proto结构,设置srcs为"tengine_tensors_to_classification_calculator.proto"

mediapipe_proto_library类型意味着编译时会使用protobuf生成tengine_tensors_to_classification_calculator.proto对应结构的头文件"tengine_tensors_to_classification_calculator.pb.h",引用该头文件即可得到TengineTensorsToClassificationsCalculatorOptions结构体的定义;


添加计算节点代码

1.在mediapipe/calculators/tengine文件夹下添加该节点的代码文件tengine_tensors_to_classification_calculator.cc;继承父类CalculatorBase来定义子类TengineTensorsToClassificationsCalculator;可以通过CalculatorContext中的Options<::mediapipe::tenginetensorstoclassificationscalculatoroptions>()函数获取运行时配置文件中对TengineTensorsToClassificationsCalculator节点设置的参数;


2. 定义GetContract函数用于初始化节点中的运行结构:设置节点的Inputs和Outputs数据变量类型,存在多个Inputs和Outputs的情况下可以通过Tag来区分;

例如: 

//constexpr char kShapeTag[] = "TENSOR_SHAPE";if (cc->Inputs().HasTag(kShapeTag)) {    cc->Inputs().Tag(kShapeTag).Set<:vector>>>();}
//constexpr char kCLASSTag[] = "CLASSIFICATION";if (cc->Outputs().HasTag(kCLASSTag)) {    cc->Outputs()         .Tag(kCLASSTag)         .Set<:vector>>>();}

Tag为"TENSOR_SHAPE"的输入数据类型为std::vector<:vector>>,用于表示上一节点传送到该节点的数据形状;


 Tag为"CLASSIFICATION"的输出数据类型为std::vector<:pair float="">>,用于表示该节点输出的分类结果,包含类别class_id和置信度score信息;

 该函数为类的静态成员函数,在部署流程运行前被调用;


3. 定义Open函数用于初始化类运行时的一些参数,例如数据类型 data_type_ ,该函数在运行时仅被调用一次;


4. 定义Process函数用于定义节点操作逻辑,该函数运行时被调用多次;


5. 定义Close函数用于释放节点关闭时需要的操作;


6. 注册TengineTensorsToClassificationsCalculator节点 


REGISTER_CALCULATOR(TengineTensorsToClassificationsCalculator);

 

7. 在mediapipe/calculators/tengine/BUILD文件中添加cc_library类型的tengine_tensors_to_classification_calculator结构,设置srcs为"tengine_tensors_to_classification_calculator.cc",并将参数定义结构":tengine_tensors_to_classification_calculator_cc_proto"添加到deps中(指定在c++中使用,名称以_cc_proto结尾而非 _proto);


最后,将已定义好的

tengine_tensors_to_classification_calculator节点添加到

mediapipe/graphs/object_detection/BUILD文件中的desktop_tengine_calculators结构的deps中,如此一来定义于

mediapipe/examples/desktop/object_detection/BUILD

中的libdesktop_tengine_calculators.so结构所生成的动态运行库就可以调用TengineTensorsToClassificationsCalculator类型的节点;


以上就是TengineInferPipe的全部使用教程,感兴趣的小伙伴搭配GithubInferPipe项目进行使用体验感会更好哦~


欢迎star/watch/fork三连

https://github.com/OAID/Tengine

了解更多可扫码联系我们

分享.jpg