在Ubuntu 20.04上用VSCode和C++集成ONNX Runtime,手把手搞定YOLOv8模型推理(附完整CMake配置)

张开发
2026/4/11 18:25:16 15 分钟阅读

分享文章

在Ubuntu 20.04上用VSCode和C++集成ONNX Runtime,手把手搞定YOLOv8模型推理(附完整CMake配置)
在Ubuntu 20.04上用VSCode和C集成ONNX Runtime实现YOLOv8模型推理最近在项目中需要将YOLOv8模型部署到边缘设备上考虑到性能和跨平台需求最终选择了ONNX Runtime作为推理引擎。本文将分享如何在Ubuntu 20.04系统中使用VSCode搭建C开发环境并通过CMake完整配置ONNX Runtime的GPU版本最终实现YOLOv8模型的推理功能。1. 环境准备与工具安装在开始项目前我们需要确保系统环境配置正确。Ubuntu 20.04作为长期支持版本提供了稳定的开发基础。以下是必要的准备工作基础软件安装sudo apt update sudo apt install -y build-essential cmake git libopencv-devCUDA和cuDNN安装如果使用GPU加速sudo apt install -y nvidia-cuda-toolkit注意CUDA版本需要与ONNX Runtime GPU版本兼容建议使用CUDA 11.x系列VSCode配置从官网下载并安装VSCode安装必要的扩展C/CCMake ToolsCMake2. ONNX Runtime库的获取与配置ONNX Runtime提供了预编译的二进制包我们可以直接从GitHub Release页面下载。以下是详细步骤访问ONNX Runtime官方仓库下载对应版本的GPU包如onnxruntime-linux-x64-gpu-1.13.1.tgz解压到合适位置tar -xzf onnxruntime-linux-x64-gpu-1.13.1.tgz -C ~/libs/解压后的目录结构包含include/头文件目录lib/预编译的共享库文件环境变量设置echo export ONNXRUNTIME_ROOT~/libs/onnxruntime-linux-x64-gpu-1.13.1 ~/.bashrc source ~/.bashrc3. 项目结构与CMake配置创建一个清晰的目录结构有助于项目管理。建议采用如下布局yolov8_cpp/ ├── CMakeLists.txt ├── include/ │ ├── yolov8.h │ └── utils.h ├── src/ │ ├── yolov8.cpp │ └── utils.cpp └── main.cpp核心CMake配置cmake_minimum_required(VERSION 3.10) project(yolov8_demo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 查找OpenCV find_package(OpenCV REQUIRED) # 设置ONNX Runtime路径 set(ONNXRUNTIME_INCLUDE_DIRS $ENV{ONNXRUNTIME_ROOT}/include) set(ONNXRUNTIME_LIBS $ENV{ONNXRUNTIME_ROOT}/lib/libonnxruntime.so $ENV{ONNXRUNTIME_ROOT}/lib/libonnxruntime_providers_cuda.so $ENV{ONNXRUNTIME_ROOT}/lib/libonnxruntime_providers_shared.so ) # 包含目录 include_directories( include ${OpenCV_INCLUDE_DIRS} ${ONNXRUNTIME_INCLUDE_DIRS} ) # 添加可执行文件 add_executable(yolov8_demo src/yolov8.cpp src/utils.cpp main.cpp ) # 链接库 target_link_libraries(yolov8_demo ${OpenCV_LIBS} ${ONNXRUNTIME_LIBS} )4. YOLOv8推理实现在C中实现YOLOv8推理需要处理以下几个关键环节模型加载与初始化#include onnxruntime_cxx_api.h class YOLOv8 { public: YOLOv8(const std::string model_path) { // 初始化环境 env_ Ort::Env(ORT_LOGGING_LEVEL_WARNING, YOLOv8); // 设置会话选项 Ort::SessionOptions session_options; session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); #ifdef USE_CUDA Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0)); #endif // 创建会话 session_ Ort::Session(env_, model_path.c_str(), session_options); } private: Ort::Env env_; Ort::Session session_; };预处理与后处理// 图像预处理 cv::Mat preprocess(const cv::Mat image) { cv::Mat resized; cv::resize(image, resized, cv::Size(640, 640)); cv::Mat blob; cv::dnn::blobFromImage(resized, blob, 1.0/255.0, cv::Size(640, 640), cv::Scalar(), true, false); return blob; } // 后处理 std::vectorDetection postprocess(const float* output, const cv::Size original_size) { std::vectorDetection detections; // 解析输出并应用NMS // ... return detections; }5. 完整推理流程与性能优化将各个模块组合起来形成完整的推理流程int main() { // 初始化模型 YOLOv8 detector(yolov8n.onnx); // 读取图像 cv::Mat image cv::imread(test.jpg); // 预处理 cv::Mat input_blob preprocess(image); // 创建输入张量 Ort::MemoryInfo memory_info Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); std::vectorint64_t input_shape {1, 3, 640, 640}; Ort::Value input_tensor Ort::Value::CreateTensorfloat( memory_info, input_blob.ptrfloat(), input_blob.total(), input_shape.data(), input_shape.size()); // 运行推理 const char* input_names[] {images}; const char* output_names[] {output0}; auto outputs detector.session_.Run( Ort::RunOptions{nullptr}, input_names, input_tensor, 1, output_names, 1); // 后处理 float* output outputs[0].GetTensorMutableDatafloat(); auto detections postprocess(output, image.size()); // 可视化结果 visualize(image, detections); return 0; }性能优化技巧批处理调整输入张量的第一维度实现批处理IO绑定使用IO绑定减少内存拷贝线程控制通过会话选项设置线程数模型量化考虑使用FP16或INT8量化模型6. 常见问题排查在实际部署过程中可能会遇到以下问题动态库加载失败error while loading shared libraries: libonnxruntime.so.1.13.1: cannot open shared object file解决方案sudo ldconfig $ONNXRUNTIME_ROOT/libCUDA版本不兼容 确保系统安装的CUDA版本与ONNX Runtime构建时使用的版本一致。可以通过以下命令检查nvcc --version内存不足 对于大型模型可能需要调整内存分配策略Ort::SessionOptions session_options; session_options.SetMemoryPatternThreshold(0); // 禁用内存模式优化7. 进阶配置与扩展对于更复杂的应用场景可以考虑以下扩展多模型管理class ModelManager { public: void load_model(const std::string name, const std::string path) { models_[name] std::make_uniqueYOLOv8(path); } std::vectorDetection infer(const std::string name, const cv::Mat image) { return models_[name]-infer(image); } private: std::unordered_mapstd::string, std::unique_ptrYOLOv8 models_; };自定义算子支持 如果需要使用ONNX Runtime不直接支持的算子可以通过以下方式添加Ort::CustomOpDomain custom_op_domain(custom_ops); // 添加自定义算子 session_options.Add(custom_op_domain);模型更新热加载 实现模型热加载机制无需重启应用即可更新模型void YOLOv8::reload(const std::string new_model_path) { Ort::Session new_session(env_, new_model_path.c_str(), session_options_); std::swap(session_, new_session); }在实际项目中我发现ONNX Runtime的C API虽然功能强大但文档相对较少遇到问题时最好的解决方式是查阅源码和社区讨论。特别是在处理动态输入形状和自定义输出时需要特别注意内存管理和张量生命周期的控制。

更多文章