注意
需要首先完成 准备工作空间 所需环境配置 使用MMDeploy得到onnx三个部分之后,再进行本文接下来的操作。
上述三个部分的内容及详细配置过程,请参考本人另外一篇文章使用MMDeploy(预编译包)转换MMxx(MMDeploy支持库均可)pth权重到onnx,并使用python SDK进行部署验证
- 准备工作空间
- 所需环境配置
- 使用MMDeploy得到onnx
- 使用MMDeploy加载onnx模型对单张图片进行推理
- 使用python SDK对onnx模型进行验证
在完成上述三个部分的配置之后,请接着进行本文以下部分的实践操作,来完成使用MMDeploy C++ SDK开发包加载onnx,并最终打包成dll动态链接库,并加载dll动态链接库进行推理。
MMDeploy cpp部署实践记录
注意下文中出现的“mmdeploy-0.14.0-windows-amd64-cuda11.3”等,凡是在本文文字中不存在的,均在另外一篇博文**使用MMDeploy(预编译包)转换MMxx(MMDeploy支持库均可)pth权重到onnx,并使用python SDK进行部署验证**中给予了详细地说明,请移步该博文查看。
工作空间说明
按照上述本人的另外一篇博文完成配置之后,得到的工作空间为work_space_1,那么现在需要创建一个新的工作空间work_space_2,该工作空间具体结构如下:
--------work_space_2
--------------------------bin(将之前下载好的mmdeploy-0.14.0-windows-amd64-cuda11.3中bin文件夹拷贝到这里)
--------------------------images(用于加载dll动态链接库来进行推理的图片文件夹)
--------------------------include(存放用于C++编译所需要的mmdeploy头文件,将mmdeploy-0.14.0-windows-amd64-cuda11.3中的include/mmdeploy文件夹拷贝到这里即可)
--------------------------lib(将mmdeploy-0.14.0-windows-amd64-cuda11.3文件夹下的lib拷贝到这里即可)
--------------------------onnx(将在上述本人的另一篇博文中得到的onnx以及其配套的deploy.json/detail.json/pipeline.json复制到该文件夹下即可,共四个文件)
--------------------------src(用于创建推理动态链接库dll的cpp文件,自行创建,后续会有详细示例代码demo)
--------------------------thirdparty(将mmdeploy-0.14.0-windows-amd64-cuda11.3文件夹下的thirdparty文件夹拷贝到这里即可)
--------------------------CMakeLists.txt(cmake文件,自行创建,后续会有详细地配置示例)
编译环境配置
为了实现对C++语言的编译,我这里参考了官方给出的编译环境(Visual Studio 2019),由于网上存在很多质量很好的VC配置教程,因此这里不再赘述,重点将放在使用MMDeploy需要的配置过程。
- CMake版本说明
cmd 运行:cmake --version
cmake version 3.25.0
CMake suite maintained and supported by Kitware (kitware.com/cmake).
- opencv配置
1) 这里需要说明,本人使用的为opencv 3.4.6的exe预编译版本,如果你想通过自行编译opencv,请查看相关博文。
直接从如下链接下载:
opencv 3.4.6 exe官方release下载
2) 下载完成之后,解压exe文件到任意位置,然后将如下路径 “path/to/opencv_3_4_6_build_from_exe/opencv/build” 添加到系统环境变量中即可。
src目录下cpp推理文件的创建
详细地的cpp示例代码如下,参考于MMDeploy官方代码,文件命名为:object_detection_my.cpp
#include <fstream>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <mmdeploy/detector.h>
// 加入extern "C"用于编译为dll使用
extern "C"{
__declspec(dllexport) int do_inference(const char *device_name, const char *model_path, const char *image_path);
}
int do_inference(const char *device_name, const char *model_path, const char *image_path) {
cv::Mat img = cv::imread(image_path);
if (!img.data) {
fprintf(stderr, "failed to load image: %s\n", image_path);
return 1;
}
mmdeploy_detector_t detector{};
int status{};
status = mmdeploy_detector_create_by_path(model_path, device_name, 0, &detector);
if (status != MMDEPLOY_SUCCESS) {
fprintf(stderr, "failed to create detector, code: %d\n", (int)status);
return 1;
}
mmdeploy_mat_t mat{
img.data, img.rows, img.cols, 3, MMDEPLOY_PIXEL_FORMAT_BGR, MMDEPLOY_DATA_TYPE_UINT8};
mmdeploy_detection_t* bboxes{};
int* res_count{};
status = mmdeploy_detector_apply(detector, &mat, 1, &bboxes, &res_count);
if (status != MMDEPLOY_SUCCESS) {
fprintf(stderr, "failed to apply detector, code: %d\n", (int)status);
return 1;
}
fprintf(stdout, "bbox_count=%d\n", *res_count);
for (int i = 0; i < *res_count; ++i) {
const auto& box = bboxes[i].bbox;
const auto& mask = bboxes[i].mask;
fprintf(stdout, "box %d, left=%.2f, top=%.2f, right=%.2f, bottom=%.2f, label=%d, score=%.4f\n",
i, box.left, box.top, box.right, box.bottom, bboxes[i].label_id, bboxes[i].score);
// skip detections with invalid bbox size (bbox height or width < 1)
if ((box.right - box.left) < 1 || (box.bottom - box.top) < 1) {
continue;
}
// skip detections less than specified score threshold
if (bboxes[i].score < 0.3) {
continue;
}
// generate mask overlay if model exports masks
if (mask != nullptr) {
fprintf(stdout, "mask %d, height=%d, width=%d\n", i, mask->height, mask->width);
cv::Mat imgMask(mask->height, mask->width, CV_8UC1, &mask->data[0]);
auto x0 = std::max(std::floor(box.left) - 1, 0.f);
auto y0 = std::max(std::floor(box.top) - 1, 0.f);
cv::Rect roi((int)x0, (int)y0, mask->width, mask->height);
// split the RGB channels, overlay mask to a specific color channel
cv::Mat ch[3];
split(img, ch);
int col = 0; // int col = i % 3;
cv::bitwise_or(imgMask, ch[col](roi), ch[col](roi));
merge(ch, 3, img);
}
cv::rectangle(img, cv::Point{(int)box.left, (int)box.top},
cv::Point{(int)box.right, (int)box.bottom}, cv::Scalar{0, 255, 0});
}
cv::imwrite("output_detection.png", img);
mmdeploy_detector_release_result(bboxes, res_count, 1);
mmdeploy_detector_destroy(detector);
return 0;
}
CMakeList.txt文件示例
如下为本人编写的CMakeList.txt文件示例:
PROJECT(ObjectDetection)
cmake_minimum_required(VERSION 3.20)
# 设置C++17标准
set(CMAKE_CXX_STANDARD 17)
# 包含需要编译得到的头文件
SET(LIBOD_SRC ./src/object_detection_my.cpp)
ADD_LIBRARY(objectdetection SHARED ${LIBOD_SRC})
# 这里需要更改为自己的3.4.6版本opencv包的build路径
set(OpenCV_DIR "path/to/opencv_3_4_6_build_from_exe/opencv/build")
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
target_include_directories(objectdetection PUBLIC ${OpenCV_INCLUDE_DIRS})
target_link_libraries(objectdetection PRIVATE ${OpenCV_LIBS})
endif()
# 添加mmdeploy编译需要的文件
if (NOT (${CMAKE_PROJECT_NAME} STREQUAL "MMDeploy"))
find_package(MMDeploy REQUIRED)
endif ()
if (MMDEPLOY_BUILD_SDK_MONOLITHIC)
target_link_libraries(objectdetection PRIVATE mmdeploy)
else ()
# Load MMDeploy modules
mmdeploy_load_static(objectdetection MMDeployStaticModules)
mmdeploy_load_dynamic(objectdetection MMDeployDynamicModules)
# Link to MMDeploy libraries
target_link_libraries(objectdetection PRIVATE MMDeployLibs)
endif ()
编译上述cpp文件得到dll动态链接库
在work_space_2工作空间目录下,运行windows power shell,然后运行如下命令:
cmake -G "Visual Studio 16 2019" -B "./build/" -DTENSORRT_DIR="path\to\work_space_2\thirdparty\tensorrt" -DONNXRUNTIME_DIR="path\to\work_space_2\thirdparty\onnxruntime"
然后,运行如下命令进行build:
cmake --build "./build/"
编译成功之后,你会在work_space_2工作空间的build\Debug目录下找到所需要的dll动态链接库objectdetection.dll
加载dll动态链接库传入需要的字符串数据,得到返回结果
然后在work_space_2工作空间下创建test文件夹,其中,放入以下运行依赖的dll库:
mmdeploy_ort_net.dll
mmdeploy_trt_net.dll
mmdeploy.dll
上述三个dll可以在work_space_2工作空间下的bin目录下找到。
opencv_world346.dll
opencv_world346d.dll
上述两个dll可以在以下路径:path\to\opencv_3_4_6_build_from_exe\opencv\build\x64\vc15\bin中找到
然后在test文件夹下加入objectdetection.dll。
接着,在test文件夹下创建一个test.py文件,用于测试得到的dll是否可以成功做图片推理任务,test.py文件示例代码如下:
import ctypes
import os
# 这行代码很重要,告诉系统运行objectdetection.dll需要的其他的dll依赖的位置
os.chdir(r'path/to/your/work_space_2/test')
# 加载dll动态链接库
object_detection_dll = ctypes.cdll.LoadLibrary('objectdetection.dll')
# 得到推理结果
results = object_detection_dll.do_inference(b"cpu", b"path/to/your/work_space_2/onnx",
b"path/to/your/work_space_2/images/demo.jpg")
print("调用C DLL动态库得到的结果为: {}".format(results))
然后,你会在test文件夹找到推理结果图片。
参考
[1] https://github.com/open-mmlab/mmdeploy/blob/main/docs/en/01-how-to-build/windows.md
2023.4.22 于 西安