YOLOv8_obb的C++工程部署实战:从模型转换到实时视频推理

张开发
2026/6/10 1:53:29 15 分钟阅读
YOLOv8_obb的C++工程部署实战:从模型转换到实时视频推理
1. YOLOv8_obb模型部署概述旋转目标检测Oriented Object Detection是计算机视觉领域的一个重要研究方向相比传统的水平框检测旋转框能更精确地描述物体的位置和方向。YOLOv8_obb作为YOLO系列在旋转目标检测上的扩展在航拍图像、遥感检测、文档分析等场景中表现出色。但在实际工业应用中如何将训练好的PyTorch模型高效部署到C环境中是很多开发者面临的挑战。我最近在一个无人机巡检项目中就遇到了这个问题。训练好的YOLOv8_obb模型在Python环境下表现良好但客户要求必须集成到他们的C工业软件中。经过两周的摸索和调试终于总结出一套完整的部署方案。本文将分享从模型转换到实时视频推理的全流程包含那些官方文档没写的实战细节。部署流程主要分为四个关键阶段模型转换PyTorch→ONNX、环境配置OpenCVONNX Runtime、工程构建CMake和推理实现预处理→推理→后处理。每个阶段都有需要注意的坑比如ONNX导出时的参数设置、旋转框NMS的实现技巧等。下面我会结合代码实例详细讲解每个环节的具体实现。2. 模型转换与环境配置2.1 PyTorch模型转ONNX模型转换是部署的第一步也是容易出错的环节。YOLOv8_obb官方提供了导出ONNX的接口但直接使用默认参数可能会导致后续C推理出错。这里分享经过验证的可靠导出命令yolo export modelbest.pt formatonnx opset12 dynamicFalse关键参数说明opset12ONNX算子版本建议12及以上以保证兼容性dynamicFalse固定输入尺寸简化后续处理imgsz640指定输入尺寸需与训练时一致我遇到过导出成功后ONNX模型无法加载的问题后来发现是OpenCV版本过低导致的。经过测试OpenCV必须≥4.5.5推荐使用4.9.0版本。可以通过以下命令检查版本opencv_version --version2.2 环境配置详解C部署需要准备以下环境组件ONNX Runtime建议1.15版本提供跨平台推理能力OpenCV4.5.5版本用于图像处理和结果可视化CUDA可选如果使用GPU加速需匹配CUDA版本在Ubuntu系统下可以通过apt快速安装基础组件sudo apt install -y build-essential cmake libopencv-dev对于ONNX Runtime建议从官网下载预编译包。我测试过从源码编译整个过程可能需要2-3小时而预编译包解压即可使用。下载后需要设置环境变量export ONNXRUNTIME_ROOT/path/to/onnxruntime export LD_LIBRARY_PATH$ONNXRUNTIME_ROOT/lib:$LD_LIBRARY_PATH3. CMake工程构建3.1 工程目录结构一个规范的工程目录能大幅提高开发效率。推荐按功能模块组织代码YOLOv8_OBB_CPP/ ├── CMakeLists.txt ├── include/ │ ├── preprocess.h │ └── postprocess.h ├── src/ │ ├── main.cpp │ ├── preprocess.cpp │ └── postprocess.cpp ├── models/ │ └── best.onnx └── images/ └── test.jpg3.2 CMake关键配置CMakeLists.txt是项目的构建核心需要正确链接所有依赖库。以下是经过验证的配置模板cmake_minimum_required(VERSION 3.10) project(YOLOv8_OBB) set(CMAKE_CXX_STANDARD 14) # OpenCV配置 find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) # ONNX Runtime配置 set(ONNXRUNTIME_ROOT /path/to/onnxruntime) set(ONNXRUNTIME_INCLUDE ${ONNXRUNTIME_ROOT}/include) set(ONNXRUNTIME_LIB ${ONNXRUNTIME_ROOT}/lib) link_directories(${ONNXRUNTIME_LIB}) # 添加可执行文件 add_executable(yolo_obb_infer src/main.cpp src/preprocess.cpp src/postprocess.cpp) # 链接库 target_link_libraries(yolo_obb_infer ${OpenCV_LIBS} onnxruntime )实际部署时最容易遇到的问题是库路径错误。建议使用绝对路径并通过message()打印检查路径是否正确message(STATUS OpenCV libs: ${OpenCV_LIBS})4. 核心推理实现4.1 图像预处理YOLOv8_obb的输入需要归一化到0-1范围并调整为640x640大小。关键点是保持长宽比的同时进行填充避免图像变形。以下是经过优化的预处理代码void preprocess(const cv::Mat src, cv::Mat dst, cv::Vec4d params) { // 输入图像尺寸 int img_h src.rows; int img_w src.cols; // 计算缩放比例(保持长宽比) float scale std::min(640.0f/img_w, 640.0f/img_h); int new_w int(img_w * scale); int new_h int(img_h * scale); // 中心填充 cv::Mat resized; cv::resize(src, resized, cv::Size(new_w, new_h)); int dw 640 - new_w; int dh 640 - new_h; int top dh / 2; int bottom dh - top; int left dw / 2; int right dw - left; cv::copyMakeBorder(resized, dst, top, bottom, left, right, cv::BORDER_CONSTANT, cv::Scalar(114, 114, 114)); // 保存预处理参数用于后处理 params[0] scale; // scale_x params[1] scale; // scale_y params[2] left; // pad_x params[3] top; // pad_y }4.2 ONNX Runtime推理初始化ONNX Runtime会话时有几点需要注意设置线程数CPU推理时启用CUDA加速如有GPU正确处理输入输出张量形状// 初始化环境 Ort::Env env(ORT_LOGGING_LEVEL_WARNING, YOLOv8_OBB); Ort::SessionOptions session_options; // 配置选项 session_options.SetIntraOpNumThreads(4); // CPU线程数 // session_options.AppendExecutionProvider_CUDA(0); // 启用CUDA // 加载模型 Ort::Session session(env, best.onnx, session_options); // 获取输入输出信息 auto input_name session.GetInputName(0, allocator); auto input_shape session.GetInputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape(); // 通常是[1, 3, 640, 640] // 创建输入张量 std::vectorfloat input_tensor_values(3*640*640); Ort::Value input_tensor Ort::Value::CreateTensorfloat( memory_info, input_tensor_values.data(), input_tensor_values.size(), input_shape.data(), input_shape.size() ); // 执行推理 auto outputs session.Run(Ort::RunOptions{nullptr}, input_name, input_tensor, 1, output_names, output_count);4.3 旋转框后处理旋转框后处理是YOLOv8_obb部署中最复杂的部分主要包含解码预测框cx,cy,w,h,angle置信度过滤旋转框NMS以下是旋转框NMS的核心实现struct RotatedBox { cv::RotatedRect rect; float score; int class_id; }; float rotatedIoU(const cv::RotatedRect box1, const cv::RotatedRect box2) { std::vectorcv::Point2f intersect; cv::rotatedRectangleIntersection(box1, box2, intersect); if (intersect.empty()) return 0.0f; float inter_area cv::contourArea(intersect); float iou inter_area / (box1.size.area() box2.size.area() - inter_area); return iou; } void rotatedNMS(std::vectorRotatedBox boxes, float iou_thresh) { std::sort(boxes.begin(), boxes.end(), [](const RotatedBox a, const RotatedBox b) { return a.score b.score; }); std::vectorbool suppressed(boxes.size(), false); for (size_t i 0; i boxes.size(); i) { if (suppressed[i]) continue; for (size_t j i 1; j boxes.size(); j) { if (suppressed[j]) continue; float iou rotatedIoU(boxes[i].rect, boxes[j].rect); if (iou iou_thresh) { suppressed[j] true; } } } // 移除被抑制的框 size_t index 0; for (size_t i 0; i boxes.size(); i) { if (!suppressed[i]) { boxes[index] boxes[i]; } } boxes.resize(index); }5. 实时视频流处理将单图推理扩展到视频流主要增加以下功能视频帧读取OpenCV VideoCapture帧率计算与显示结果视频保存关键实现代码cv::VideoCapture cap(input.mp4); if (!cap.isOpened()) { std::cerr Error opening video file std::endl; return -1; } // 获取视频信息 double fps cap.get(cv::CAP_PROP_FPS); int width cap.get(cv::CAP_PROP_FRAME_WIDTH); int height cap.get(cv::CAP_PROP_FRAME_HEIGHT); // 创建VideoWriter cv::VideoWriter writer(output.mp4, cv::VideoWriter::fourcc(M,J,P,G), fps, cv::Size(width, height)); cv::Mat frame; while (cap.read(frame)) { auto start std::chrono::high_resolution_clock::now(); // 执行推理 std::vectorRotatedBox results infer(frame); // 绘制结果 for (const auto box : results) { cv::Point2f vertices[4]; box.rect.points(vertices); for (int j 0; j 4; j) { cv::line(frame, vertices[j], vertices[(j1)%4], cv::Scalar(0,255,0), 2); } } // 计算并显示FPS auto end std::chrono::high_resolution_clock::now(); float infer_time std::chrono::durationfloat(end-start).count(); float fps 1.0f / infer_time; cv::putText(frame, FPS: std::to_string(fps), cv::Point(10,30), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0,0,255), 2); // 写入输出视频 writer.write(frame); // 实时显示 cv::imshow(YOLOv8-OBB, frame); if (cv::waitKey(1) 27) break; // ESC退出 } cap.release(); writer.release();6. 性能优化技巧在实际部署中我总结出几个有效的性能优化方法内存复用避免频繁申请释放内存对中间结果使用静态变量批处理当处理多路视频时合并多帧进行批量推理异步处理使用生产者-消费者模式分离图像获取和推理量化加速将FP32模型转为INT8可提升2-3倍速度INT8量化的示例代码// 在导出ONNX时添加量化参数 yolo export modelbest.pt formatonnx int8True // 推理时启用量化 Ort::SessionOptions session_options; session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); session_options.SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL); session_options.AddConfigEntry(session.quant_mode, 1);经过这些优化在Intel i7-12700K CPU上YOLOv8_obb的推理速度可以从原始的45ms/帧提升到18ms/帧满足实时性要求。

更多文章