
1. 环境准备与工具安装在Ubuntu 20.04上部署YOLOv8模型首先需要搭建好基础开发环境。我推荐使用VSCode作为主要开发工具它不仅轻量高效还拥有丰富的C插件支持。实测下来这个组合在模型部署场景中非常稳定。安装VSCode很简单直接通过官方PPA源即可sudo apt update sudo apt install -y wget wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor packages.microsoft.gpg sudo install -o root -g root -m 644 packages.microsoft.gpg /usr/share/keyrings/ sudo sh -c echo deb [archamd64 signed-by/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/vscode stable main /etc/apt/sources.list.d/vscode.list sudo apt update sudo apt install -y code接下来安装必要的编译工具链sudo apt install -y build-essential cmake git libopencv-dev对于CUDA加速支持如果你有NVIDIA显卡建议安装CUDA 11.7和对应版本的cuDNN。这里有个小技巧安装完成后记得将CUDA路径加入环境变量echo export PATH/usr/local/cuda/bin:$PATH ~/.bashrc echo export LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRARY_PATH ~/.bashrc source ~/.bashrc2. ONNX Runtime安装与配置ONNX Runtime是微软开源的跨平台推理引擎对YOLOv8的支持非常好。我建议直接从GitHub下载预编译版本这样能避免很多编译时的依赖问题。最新稳定版目前是1.15.1但实际测试发现1.13.1版本与YOLOv8的兼容性最佳。下载和解压命令如下wget https://github.com/microsoft/onnxruntime/releases/download/v1.13.1/onnxruntime-linux-x64-gpu-1.13.1.tgz tar -xzvf onnxruntime-linux-x64-gpu-1.13.1.tgz解压后会得到包含include和lib的目录结构。这里有个关键点需要将库文件路径加入系统查找路径echo export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/path/to/onnxruntime/lib ~/.bashrc source ~/.bashrc在VSCode中建议安装CMake Tools和C插件。配置settings.json时特别注意包含路径的设置{ cmake.configureArgs: [ -DCMAKE_PREFIX_PATH/path/to/onnxruntime ] }3. YOLOv8模型转换与准备Ultralytics官方提供的YOLOv8模型通常有PyTorch格式我们需要先将其转换为ONNX格式。这里我推荐使用官方export.py脚本from ultralytics import YOLO model YOLO(yolov8n.pt) # 加载预训练模型 model.export(formatonnx, dynamicTrue) # 动态轴导出很重要转换时容易踩的坑是输入输出的动态维度设置。实测发现设置dynamicTrue能更好地适配不同尺寸的输入图像。转换完成后建议用Netron工具检查模型结构确认输入输出节点名称。将生成的.onnx文件放在项目目录的models文件夹下。我通常会创建这样的目录结构project_root/ ├── models/ │ └── yolov8n.onnx ├── include/ ├── src/ └── CMakeLists.txt4. CMake工程配置详解CMake的配置是整个项目的核心这里分享我的完整配置方案。首先创建基础CMakeLists.txtcmake_minimum_required(VERSION 3.16) project(yolov8_deploy) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # ONNX Runtime配置 set(ONNXRUNTIME_DIR /path/to/onnxruntime-linux-x64-gpu-1.13.1) set(ONNXRUNTIME_INCLUDE_DIRS ${ONNXRUNTIME_DIR}/include) set(ONNXRUNTIME_LIBS ${ONNXRUNTIME_DIR}/lib/libonnxruntime.so ${ONNXRUNTIME_DIR}/lib/libonnxruntime_providers_cuda.so ${ONNXRUNTIME_DIR}/lib/libonnxruntime_providers_shared.so ) # OpenCV查找 find_package(OpenCV REQUIRED) # 包含目录 include_directories( include ${ONNXRUNTIME_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ) # 添加库 add_library(yolov8 src/yolov8_onnx.cpp src/yolov8_utils.cpp ) # 链接库 target_link_libraries(yolov8 ${OpenCV_LIBS} ${ONNXRUNTIME_LIBS} ) # 可执行文件 add_executable(inference main.cpp) target_link_libraries(inference yolov8)关键点说明C17标准是必须的因为ONNX Runtime的C API使用了现代C特性必须链接providers_cuda.so才能启用GPU加速OpenCV建议使用4.5版本因为YOLOv8的后处理需要较新的DNN模块5. 核心推理类实现在yolov8_onnx.h中定义我们的推理类框架#pragma once #include onnxruntime_cxx_api.h #include opencv2/opencv.hpp class YOLOv8 { public: YOLOv8(const std::string model_path, bool use_gpu true); std::vectorDetection detect(cv::Mat image); private: Ort::Env env; Ort::SessionOptions session_options; Ort::Session session; void preprocess(cv::Mat image, float* blob); std::vectorDetection postprocess(float* output, const cv::Size img_size); };实现部分的关键在于会话创建和IO绑定YOLOv8::YOLOv8(const std::string model_path, bool use_gpu) : env(ORT_LOGGING_LEVEL_WARNING, YOLOv8), session_options(Ort::SessionOptions{}) { // GPU加速设置 if (use_gpu) { Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0)); } // 会话创建 session Ort::Session(env, model_path.c_str(), session_options); // 获取输入输出信息 Ort::AllocatorWithDefaultOptions allocator; input_name session.GetInputName(0, allocator); output_name session.GetOutputName(0, allocator); }预处理要特别注意图像归一化和通道顺序void YOLOv8::preprocess(cv::Mat image, float* blob) { cv::Mat resized; cv::resize(image, resized, cv::Size(640, 640)); // 归一化到0-1并转换为RGB resized.convertTo(resized, CV_32FC3, 1.0/255.0); cv::cvtColor(resized, resized, cv::COLOR_BGR2RGB); // HWC - CHW std::vectorcv::Mat channels(3); cv::split(resized, channels); // 填充blob数据 for (int c 0; c 3; c) { memcpy(blob c * 640 * 640, channels[c].data, 640 * 640 * sizeof(float)); } }6. 后处理与结果解析YOLOv8的输出解析是其部署中最复杂的部分。与早期版本不同v8的输出是直接预测的bbox坐标std::vectorDetection YOLOv8::postprocess(float* output, const cv::Size img_size) { std::vectorDetection detections; // output形状通常是[1,84,8400] const int num_classes 80; const int bbox_attrs 4 num_classes; for (int i 0; i 8400; i) { float* ptr output i * bbox_attrs; // 解析bbox float cx ptr[0]; float cy ptr[1]; float w ptr[2]; float h ptr[3]; // 解析类别置信度 int class_id std::max_element(ptr 4, ptr 84) - (ptr 4); float confidence ptr[4 class_id]; if (confidence 0.5) { // 置信度阈值 Detection det; det.bbox cv::Rect( (cx - w/2) * img_size.width, (cy - h/2) * img_size.height, w * img_size.width, h * img_size.height ); det.class_id class_id; det.confidence confidence; detections.push_back(det); } } // NMS处理 std::vectorint indices; cv::dnn::NMSBoxes( detections.bboxes(), detections.confidences(), 0.5, // 置信度阈值 0.4, // NMS阈值 indices ); return filter_by_indices(detections, indices); }7. 性能优化技巧经过多次测试我总结了几个关键优化点内存复用创建Ort::MemoryInfo一次并重复使用避免频繁分配释放static Ort::MemoryInfo memory_info Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault );IO绑定优化显式指定输入输出张量std::vectorOrt::Value input_tensors; input_tensors.emplace_back(Ort::Value::CreateTensorfloat( memory_info, blob.data(), blob.size(), input_shape.data(), input_shape.size() )); std::vectorOrt::Value output_tensors; session.Run( Ort::RunOptions{nullptr}, input_name, input_tensors, 1, output_name, output_tensors, 1 );批处理支持通过修改输入形状实现std::arrayint64_t, 4 input_shape{2, 3, 640, 640}; // 批大小2混合精度推理在支持TensorRT的情况下启用FP16Ort::SessionOptions session_options; OrtTensorRTProviderOptionsV2* trt_options; session_options.AppendExecutionProvider_TensorRT(trt_options);8. 完整示例与调试技巧最后给出一个完整的使用示例#include yolov8_onnx.h #include chrono int main() { YOLOv8 detector(models/yolov8n.onnx, true); cv::Mat image cv::imread(test.jpg); auto start std::chrono::high_resolution_clock::now(); auto detections detector.detect(image); auto end std::chrono::high_resolution_clock::now(); auto duration std::chrono::duration_caststd::chrono::milliseconds(end - start); std::cout Inference time: duration.count() ms std::endl; for (const auto det : detections) { cv::rectangle(image, det.bbox, cv::Scalar(0, 255, 0), 2); } cv::imwrite(result.jpg, image); return 0; }调试时常见的几个问题如果遇到InvalidGraph错误通常是ONNX模型版本不兼容尝试重新导出模型CUDA error可能是由于CUDA版本与ONNX Runtime不匹配导致内存泄漏问题可以通过Valgrind工具检测性能分析建议使用Nsight Systems进行时间线分析