大家好!明天为大家带来的是一篇教训帖文。本次分享的主人公是黑客松较量参赛者郑必城,他将为大家带来比赛项目“No.80瑞芯微RK3588:通过Paddle2ONNX买通5个飞桨模型的部署中如何为FastDeploy”工作中的一些心得体会,快来看看他是如何为FastDeploy奉献代码的吧!
RKNPU2是瑞芯微Rockchip推出的针对RK356X/RK3588/RV1103/RV1106的C++推理工具。在加入黑客松较量时,FastDeploy仓库[1]还没有集成RKNPU2的引擎。开发者须要应用RKNPU2从头编写代码。在加入完黑客松之后,我为FastDeploy仓库奉献了RKNPU2的后端推理引擎的代码,当初能间接应用FastDeploy疾速开发基于RKNPU2的代码。本次教程将以奉献SCRFD模型[2]为例,教你如何给FastDeploy奉献代码。
Zheng_Bicheng主页
https://github.com/Zheng-Bicheng
No.80瑞芯微RK3588:通过Paddle2ONNX买通5个飞桨模型的部署链接
https://github.com/PaddlePadd...
FastDeploy简介
FastDeploy是一款全场景、易用灵便、极致高效的AI推理部署工具,提供开箱即用的云边端部署体验,反对超过150+文本、计算机视觉、语音和跨模态模型,并实现端到端的推理性能优化。其利用于图像分类、物体检测、图像宰割、人脸检测、人脸识别、关键点检测、抠图、OCR、NLP、TTS等工作,满足开发者多场景、多硬件、多平台的产业部署需要。同时,FastDeploy集成了多种后端推理引擎,其中就包含RKNPU2。开发者可能疾速基于现有的模型以及后端来进行开发。
很多开发者可能会有纳闷,为什么Rockchip提供了RKNPU2和rknn-toolkit2这两个别离面向C++和Python的推理引擎,咱们还要应用FastDeploy进行开发呢?简略来说,RKNPU2和rknn-toolkit2是推理引擎,它们侧重于推理;FastDeploy是推理部署工具侧重于部署。给RKNPU2输出一张图片,会失去一串数字。给FastDeploy输出一张图片,会间接失去通过后处理后的图片。这样就能大大减少开发者在我的项目落地过程中的一些艰难。
RKNPU2
https://github.com/rockchip-linux/rknpu2
rknn-toolkit2
https://github.com/rockchip-linux/rknn-toolkit2
奉献步骤
给FastDeploy奉献代码,我个别按以下步骤进行,当然你能够依据本人的能力制订本人的开发步骤。
由上图所示,给FastDeploy奉献代码的步骤个别为编写C++代码、编写C++ example、编写Python代码、编写Python example代码、编写文档、提交PR。
奉献代码指南
上面我以奉献SCRFD模型为例子,给大家具体介绍每个奉献环节中的注意事项。
转换模型
不论你是在FastDeploy上开发C++还是Python的代码,转换模型都是你首先须要实现的工作。通常状况下,转换模型的工具个别应用rknn-toolkit2,然而这个工具API比拟多,用起来较为简单。为了让大家可能更疾速的转换模型,在FastDeploy中,我曾经编写了转换模型的代码并且提供了具体的文档。详情请查看FastDeploy RKNPU2模型转换文档。这里为了缩短篇幅,间接给出模型转换的配置文件以及模型转换的文档。大家能够参考这几个文档转换本人的模型。
FastDeploy RKNPU2模型转换文档
https://github.com/PaddlePaddle/FastDeploy/blob/develop/docs/cn/faq/rknpu2/export.md
模型转换的文档
https://github.com/PaddlePaddle/FastDeploy/tree/develop/examples/vision/facedet/scrfd/rknpu2
编写C++代码
上文提到,SCRFD的C++代码须要在fastdeploy/vision/facedet/contrib这个目录下编写,因而我创立了scrfd.h和scrfd.cc这两个文件,实现模型具体代码。这里要留神与常见的文件命名模式不同,scrfd.cc这个C++代码文件的后缀不是 .cpp而是 .cc ,如果scrfd.cc改为scrfd.cpp将无奈胜利编译!
- 编写scrfd.h
scrfd.h里定义了SCRFD模型的一些基本参数以及须要重定义的函数。其中定义的SCRFD模型须要继承FastDeployModel这个公共的模型类,为的是继承FastDeploy的一些公共个性。如上面的代码所示,在头文件中,咱们须要重写FastDeployModel中的以下几个函数,包含Initialize、Preprocess、Postprocess、Predict、ModelName。别离对应初始化、预处理、后处理、预测、模型名称。如果你须要残缺具体的代码,请拜访下方链接。
scrfd.h
https://github.com/PaddlePaddle/FastDeploy/blob/develop/fastdeploy/vision/facedet/contrib/scrfd.h
#pragma once#include <unordered_map>#include "fastdeploy/fastdeploy_model.h"#include "fastdeploy/vision/common/processors/transform.h"#include "fastdeploy/vision/common/result.h"namespace fastdeploy {namespace vision {namespace facedet {class FASTDEPLOY_DECL SCRFD : public FastDeployModel { public: SCRFD(const std::string& model_file, const std::string& params_file = "", const RuntimeOption& custom_option = RuntimeOption(), const ModelFormat& model_format = ModelFormat::ONNX); std::string ModelName() const { return "scrfd"; } virtual bool Predict(cv::Mat* im, FaceDetectionResult* result, float conf_threshold = 0.25f, float nms_iou_threshold = 0.4f); private: bool Initialize(); bool Preprocess(Mat* mat, FDTensor* output, std::map<std::string, std::array<float, 2>>* im_info); bool Postprocess(std::vector<FDTensor>& infer_result, FaceDetectionResult* result, const std::map<std::string, std::array<float, 2>>& im_info, float conf_threshold, float nms_iou_threshold);};} // namespace facedet} // namespace vision} // namespace fastdeploy
- 编写scrfd.cc
scrfd.cc负责对在scrfd.h中申明的函数进行了实现。在编写预处理的过程中要留神,RKNPU2目前仅反对NHWC格局的输出数据,因而必须屏蔽Permute操作。我这里应用disable_permute_ 变量管制Permute操作。此外因为FastDeploy采纳的是RKNPU2的零拷贝流程来实现后端的解决和运算,因而能够思考将Normalize操作放在NPU上来做,晋升速度,我这里应用disable_normalize_ 变量管制Normalize的开关。如果须要具体的代码,请拜访以下链接。
代码链接
https://github.com/PaddlePaddle/FastDeploy/blob/develop/fastdeploy/vision/facedet/contrib/scrfd.cc
#include "fastdeploy/vision/facedet/contrib/scrfd.h"#include "fastdeploy/utils/perf.h"#include "fastdeploy/vision/utils/utils.h"namespace fastdeploy {namespace vision {namespace facedet {bool SCRFD::Preprocess(Mat* mat, FDTensor* output, std::map<std::string, std::array<float, 2>>* im_info) { return true;}bool SCRFD::Postprocess(std::vector<FDTensor>& infer_result, FaceDetectionResult* result, const std::map<std::string, std::array<float, 2>>& im_info, float conf_threshold, float nms_iou_threshold) { return true;}bool SCRFD::Predict(cv::Mat* im, FaceDetectionResult* result, float conf_threshold, float nms_iou_threshold) { return true;}} // namespace facedet} // namespace vision} // namespace fastdeploy
- 在vision.h中增加咱们的模型
咱们编写完scrfd的代码之后,咱们还须要让FastDeploy晓得咱们曾经编写了scrfd代码,因而咱们须要在fastdeploy/vision.h文件中补充scrfd.h头文件的门路。
编译FastDeploy C++ SDK
编写完C++代码后,咱们须要编译C++版本的FastDeploy。一是为了测试咱们编写的代码是否有程序上的破绽,二是为了后续编写example能够链接FastDeploy编译进去的动静库。编译的细节详情请参考FastDeploy C++代码编译指南。
FastDeploy C++代码编译指南
https://github.com/PaddlePaddle/FastDeploy/blob/develop/docs/cn/faq/rknpu2/build.md
这里间接给出编译时的命令:
git clone https://github.com/PaddlePaddle/FastDeploy.gitcd FastDeploymkdir build && cd buildcmake .. -DENABLE_ORT_BACKEND=ON \ -DENABLE_RKNPU2_BACKEND=ON \ -DENABLE_VISION=ON \ -DRKNN2_TARGET_SOC=RK3588 \ -DCMAKE_INSTALL_PREFIX=${PWD}/fastdeploy-0.0.3make -j8make install
编写C++ example代码
为了调试咱们曾经实现的C++代码,以及不便用户应用,在编写完上述代码之后,咱们须要编写对应example的代码来验证咱们的想法是否正确。在编写C++ example时,目录下的文件个别由infer_model_name.cc以及CMakeLists.txt组成。在CMakeLists.txt中须要对不同的infer_model_name.cc生成不同的infer_model_name程序。
- 编写infer.cc
infer.cc次要负责调用FastDeploy的C++代码来对SCRFD进行测试。在上文中,咱们提到vision.h能够让fastdeploy晓得咱们曾经编写了SCRFD模型。因而在编写example时,咱们只须要蕴含vision.h,即可让程序晓得,咱们曾经申明了FastDeploy所有曾经实现的视觉模型。针对RKNPU的测试,其流程个别为初始化模型,而后依据转换模型时的配置决定是否须要disable_normalize和disable_permute,随后输出测试图片,调用Predict函数进行解决,最初应用对应的可视化函数进行可视化。
#include <iostream>#include <string>#include "fastdeploy/vision.h"void RKNPU2Infer(const std::string& model_dir, const std::string& image_file) { auto model = fastdeploy::vision::facedet::SCRFD(model_file, params_file, option, format); model.Initialized(); model.DisableNormalize(); model.DisablePermute(); auto im = cv::imread(image_file); fastdeploy::vision::FaceDetectionResult res; model.Predict(&im, &res) auto vis_im = fastdeploy::vision::VisFaceDetection(im, res); cv::imwrite("infer_rknn.jpg", vis_im); std::cout << "Visualized result saved in ./infer_rknn.jpg" << std::endl;}int main(int argc, char* argv[]) { if (argc < 3) { std::cout << "Usage: infer_demo path/to/model_dir path/to/image run_option, " "e.g ./infer_model ./picodet_model_dir ./test.jpeg" << std::endl; return -1; } RKNPU2Infer(argv[1], argv[2]); return 0;}
- 编写CMakeLists.txt
编写完C++ example的代码后,咱们还须要编写CMakeLists.txt。CMakeLists.txt相当于编译时的配置文件,负责链接infer_model_name.cc和FastDeploy的动静库,并且把模型推理须要用到的货色集成在install目录下。
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)project(rknpu_test)set(CMAKE_CXX_STANDARD 14)# 指定下载解压后的fastdeploy库门路set(FASTDEPLOY_INSTALL_DIR "thirdpartys/fastdeploy-0.7.0")include(${FASTDEPLOY_INSTALL_DIR}/FastDeployConfig.cmake)include_directories(${FastDeploy_INCLUDE_DIRS})add_executable(rknpu_test infer.cc)target_link_libraries(rknpu_test ${FastDeploy_LIBS})
编写Python代码
Python代码的编写次要包含pybind文件的编写以及py本体文件的编写。上文提到,在FastDeploy中,python代码通过调用pybind暴露出的C++ API来进行工作,因而咱们首先须要编写pybind.cc。
- 编写scrfd_pybind.cc
pybind.cc次要负责提供可用的API给Python调用。scrfd_pybind.cc中对SCRFD C++的代码进行了裸露,代码如下:
#include "fastdeploy/pybind/main.h"namespace fastdeploy {void BindSCRFD(pybind11::module& m) { // Bind SCRFD pybind11::class_<vision::facedet::SCRFD, FastDeployModel>(m, "SCRFD") .def(pybind11::init<std::string, std::string, RuntimeOption, ModelFormat>()) .def("predict", [](vision::facedet::SCRFD& self, pybind11::array& data, float conf_threshold, float nms_iou_threshold) { auto mat = PyArrayToCvMat(data); vision::FaceDetectionResult res; self.Predict(&mat, &res, conf_threshold, nms_iou_threshold); return res; }) .def("disable_normalize",&vision::facedet::SCRFD::DisableNormalize) .def("disable_permute",&vision::facedet::SCRFD::DisablePermute);}} // namespace fastdeploy
- 在facedet_pybind.cc中增加申明
和在vision.h文件中增加申明一样,在编写完pybind代码之后,咱们还须要在fastdeploy/vision/facedet/facedet_pybind.cc中增加申明。目标是通知编译器咱们曾经编写了pybind的代码,并且在编译Python时请把咱们的代码加上。外围代码如下:
#include "fastdeploy/pybind/main.h"namespace fastdeploy {void BindSCRFD(pybind11::module& m);void BindFaceDet(pybind11::module& m) { auto facedet_module = m.def_submodule("facedet", "Face detection models."); BindSCRFD(facedet_module);}}
- 编写scrfd.py
编写完pybind.cc后,咱们还须要编写对应的py文件调用pybind裸露进去的C++ API。代码如下
from __future__ import absolute_importimport loggingfrom .... import FastDeployModel, ModelFormatfrom .... import c_lib_wrap as Cclass SCRFD(FastDeployModel): def __init__(self, model_file, params_file="", runtime_option=None, model_format=ModelFormat.ONNX): super(SCRFD, self).__init__(runtime_option) self._model = C.vision.facedet.SCRFD(model_file, params_file, self._runtime_option, model_format) assert self.initialized, "SCRFD initialize failed." def predict(self, input_image, conf_threshold=0.7, nms_iou_threshold=0.3): return self._model.predict(input_image, conf_threshold, nms_iou_threshold)
编译FastDeploy Python SDK
编写example之前咱们必定须要编译Python版本的FastDeploy代码,请参考FastDeploy RKNPU2编译指南编译Python版本的FastDeploy。
FastDeploy RKNPU2编译指南
https://github.com/PaddlePaddle/FastDeploy/blob/develop/docs/cn/faq/rknpu2/build.md
这里给出我常常应用的编译命令:
cd FastDeploycd pythonexport ENABLE_ORT_BACKEND=ONexport ENABLE_RKNPU2_BACKEND=ONexport ENABLE_VISION=ONexport RKNN2_TARGET_SOC=RK3588python3 setup.py buildpython3 setup.py bdist_wheelcd distpip3 install fastdeploy_python-0.0.0-cp39-cp39-linux_aarch64.whl
编写Python example代码
为了调试咱们曾经实现的Python代码,以及不便用户应用,在编写完上述scrfd代码之后,咱们须要编写对应example的代码来验证咱们的想法是否正确。在编写Python example时,目录下的文件个别由infer_model_name.py组成。
- 编写infer.py
infer.py 次要负责调用FastDeploy的Python代码来对SCRFD的测试。与C++ example类似,针对RKNPU的测试,其流程个别为初始化模型,而后依据转换模型时的配置决定是否须要disable_normalize和disable_permute,随后输出测试图片,调用Predict函数进行解决,最初应用对应的可视化函数进行可视化。
import fastdeploy as fdimport cv2import osdef parse_arguments(): import argparse import ast parser = argparse.ArgumentParser() parser.add_argument("--model_file", required=True, help="Path of FaceDet model.") parser.add_argument("--image", type=str, required=True, help="Path of test image file.") return parser.parse_args()def build_option(args): option = fd.RuntimeOption() option.use_rknpu2() return optionargs = parse_arguments()# 配置runtime,加载模型runtime_option = build_option(args)model_file = args.model_fileparams_file = ""model = fd.vision.facedet.SCRFD(model_file, params_file, runtime_option=runtime_option, model_format=fd.ModelFormat.RKNN)model.disable_normalize()model.disable_permute()# 预测图片宰割后果im = cv2.imread(args.image)result = model.predict(im)print(result)# 可视化后果vis_im = fd.vision.vis_face_detection(im, result)cv2.imwrite("visualized_result.jpg", vis_im)print("Visualized result save in ./visualized_result.jpg")
编写文档以及提交pr
请参考SCRFD example编写模型的转换文档、模型的cpp example运行文档、模型的python运行文档共三份文档,而后向FastDeploy的Github仓库提交PR。待审核过后,你的奉献就会被记录啦。
SCRFD example
https://github.com/PaddlePaddle/FastDeploy/tree/develop/examples/vision/facedet/scrfd/rknpu2
总结
在飞桨做开源奉献的体验是无可比拟的,首先可能疾速实现编程能力晋升,在奉献代码的过程中,你会更加粗浅的了解书本上的内容,把握行业前沿的代码逻辑和编程标准。同时在开发过程中,你还会意识飞桨研发团队的同学以及很多气味相投的好友,与他们独特发明一些乏味的成绩,在修复bug的过程中体验成就感。欢送和我一起退出奉献代码的行列。
参考文献
[1]https://github.com/PaddlePadd...
[2]Guo J , Deng J , Lattas A , et al. Sample and Computation Redistribution for Efficient Face Detection[J]. 2021.