【C++】使用opencv+onnxruntime推理YOLOv8目标检测_c++ yolov8
通过C/C++语言能够实现深度学习模型的高效部署。在众多部署框架中,本文采用OpenCV结合ONNX Runtime的方案来部署YOLOv8目标检测模型。其中,ONNX Runtime作为核心推理引擎,而OpenCV则主要负责图像读取等辅助功能。
文章目录
- 前言
- 一、环境准备
-
- 1.1 yolov8推理源码
- 1.2 环境变量添加
- 二、项目属性
-
- 2.1 新建项目
- 2.2 配置项目属性(opencv+onnxruntime)
-
- 2.2.1 C++语言标准
- 2.2.2 附加包含目录
- 2.2.3 附加库目录
- 2.2.4 附加依赖项
- 2.2.5 生成解决方案文件
- 三、YOLOv8源代码修改
-
- 3.1 inference.cpp
- 3.2 opencv测试
- 3.3 main.cpp修改
- 3.4 运行检测
- 四、一些可能遇到的问题和解决方法
-
- 4.1 filesystem飘红
- 4.2 应用程序无法正常启动
- 4.3 Debug Error
- 4.4 头文件报错
前言
OpenCV(Open Source Computer Vision Library)集成了数百种先进的计算机视觉算法,涵盖图像处理、特征检测、目标跟踪及人脸识别等核心功能。该库支持包括C++、Python和Java在内的多种编程语言,并具备出色的跨平台兼容性,可在Windows、Linux、macOS、Android和iOS等主流操作系统上稳定运行。
ONNX Runtime(Open Neural Network Exchange Runtime)是一款开源的高性能推理引擎,专门用于执行 ONNX模型。作为机器学习模型的开放标准格式,ONNX 为模型交换提供了统一规范。ONNX Runtime 通过提供一致的 API 接口,实现了跨硬件平台和操作系统的无缝部署。
基于 OpenCV 和 ONNX Runtime 的目标检测流程主要包含以下步骤:
- 图像加载与预处理:通过 OpenCV 读取目标图像,并根据模型需求进行尺寸调整、归一化等预处理操作。
- 模型加载:使用 ONNX Runtime 加载预训练的 ONNX 格式目标检测模型,为推理做好准备。
- 模型推理:将预处理后的图像数据输入模型,利用 ONNX Runtime 进行推理计算,获取初步检测结果。
- 结果后处理:借助 OpenCV 对检测结果进行可视化处理,包括绘制目标边界框、添加类别标签等操作。
一、环境准备
- 系统:Win10;
- IDE:Visual Studio 2019;
- C++标准:>=C++17
- opencv4.9.0:https://opencv.org/releases/
- onnxruntime1.15.1(本文用的CPU版):https://github.com/microsoft/onnxruntime/releases
1.1 yolov8推理源码
获取yolov8代码:github:
https://github.com/ultralytics/ultralytics/tree/main/examples/YOLOv8-ONNXRuntime-CPP
或者gitcode(推荐,下载速度更快):
https://gitcode.com/gh_mirrors/ul/ultralytics/tree/main/examples/YOLOv8-ONNXRuntime-CPP?utm_source=csdn_github_accelerator&isLogin=1
重点是这三个文件:
- main.cpp
- inference.cpp
- inference.h
1.2 环境变量添加
不管是opencv还是onnxruntime,其本质都是给用户提供include和lib去使用。我这里将opencv和onnxruntime分别解压到以下文件夹里
D:\\opencvD:\\onnxruntime
下载好opencv和onnxruntime后,将其加入到环境变量中。右键/设置 此电脑–》属性–》高级系统设置–》高级–》环境变量,将onnxruntime文件夹里include/lib以及opencv文件夹下…\\build\\x64\\vc16添加进环境变量。
D:\\onnxruntime\\includeD:\\onnxruntime\\libD:\\opencv\\build\\x64\\vc16\\binD:\\opencv\\build\\x64\\vc16\\lib
二、项目属性
2.1 新建项目
打开VS2019创建一个空项目,命名为“yolov8”。将上面下载的yolov8中推理源码main.cpp、inference.cpp、inference.h复制到项目文件夹下,方便下一步添加进项目里:
然后将这三个文件添加进项目里:在相应文件夹上右键–》添加–》现有项,将代码添加进项目中
添加后是这样:
2.2 配置项目属性(opencv+onnxruntime)
然后重点来了,打开项目属性:(或者直接右击目标项目,选择属性)。在配置前一定确保配置在dubug、x64模式下!!!(也可以配置为release,但也需要在x64模式下)。
2.2.1 C++语言标准
在常规-》C++语言标准中修改为C++17(>=17就行)
2.2.2 附加包含目录
在C/C++栏–》常规中选择“附加包含项目”,将opencv和onnxruntime中相关文件添加进去:
D:\\onnxruntime\\includeD:\\opencv\\build\\include\\opencv2D:\\opencv\\build\\include
2.2.3 附加库目录
接着在链接器–》常规–》附加库目录中将opencv和onnxruntime的‘“lib”文件夹添加进去
D:\\opencv\\build\\x64\\vc16\\libD:\\onnxruntime\\lib
2.2.4 附加依赖项
接着在链接器–》输入–》附加依赖项中将添加以下内容
opencv_world490d.libonnxruntime.libonnxruntime_providers_shared.lib
这三个lib文件在“D:\\onnxruntime\\lib”和“D:\\opencv\\build\\x64\\vc16\\lib”
也可以直接复制以下代码:注意,opencv_world490d.lib中的\"490\"表示OpenCV版本为4.9.0,请根据您的实际版本修改该数字;后缀\"d\"代表调试模式(Debug),若为发布模式(Release),请使用opencv_world490.lib。
2.2.5 生成解决方案文件
运行时一定将将模式设置为和所配置的项目属性一致,我这里就是debug、x64(否则会找不到对应的头文件)
到这里打开main.cpp文件,执行编译操作以生成解决方案文件。(都没有报错不用管,这一步旨在创建一个…\\x64\\Debug文件夹)
然后将“D:\\onnxruntime\\lib下的DLL文件全部复制到…\\x64\\Debug文件夹中
至此已经完成了项目属性的配置。掌握这一流程至关重要,因为在Visual Studio中配置其他库的步骤与此基本一致。
三、YOLOv8源代码修改
3.1 inference.cpp
inference.cpp:
第一行加上#define _CRT_SECURE_NO_WARNINGS 1(否则会出现strcpy报警告,程序无法运行!!!)
#define _CRT_SECURE_NO_WARNINGS 1
两行飘红的字符串前加上(char*):
Ret = (char*) “[YOLO_V8]:Your model path is error.Change your model path without chinese characters.”;(102行附近)
Ret = (char*)\"[YOLO_V8]:Your model path is error.Change your model path without chinese characters.\";
return (char*) “[YOLO_V8]:Create session failed.”;(166行附近)
return (char*)\"[YOLO_V8]:Create session failed.\";
3.2 opencv测试
在 main.cpp 文件中,通过编写测试代码来验证上述配置是否成功。测试代码只需在 int main() 函数中实现,无需修改其他部分。图片正常显示,说明配置成功;若异常,再检查一下自己的配置流程。
int main(){ //DetectTest(); //ClsTest(); cv::Mat image = cv::imread(\"D:\\\\test.png\");//输入测试图片 cv::imshow(\"test\", image); cv::waitKey(0); cv::destroyAllWindows(); return 0;}
3.3 main.cpp修改
由于本文是做目标检测任务,因此仅需调整与目标检测相关的函数。
- void Detector(YOLO_V8*& p)
- void DetectTest()
- int main()
void Detector(YOLO_V8*& p)
std::filesystem::path imgs_path:将其修改为待检测图片所在的文件夹;
std::string output_folder:检测后的图片输出位置;
在函数末尾添加保存图片的代码片段。
void Detector(YOLO_V8*& p) { /*std::filesystem::path current_path = std::filesystem::current_path();*/ std::filesystem::path imgs_path = \"D:/result/test\";//这里一定输入待检测图片所在的文件夹!!! std::string output_folder = \"D:/run\"; // 输出文件夹路径 std::filesystem::create_directories(output_folder); for (auto& i : std::filesystem::directory_iterator(imgs_path)) { if (i.path().extension() == \".jpg\" || i.path().extension() == \".png\" || i.path().extension() == \".jpeg\") { std::string img_path = i.path().string(); cv::Mat img = cv::imread(img_path); std::vector<DL_RESULT> res; p->RunSession(img, res); for (auto& re : res) { cv::RNG rng(cv::getTickCount()); cv::Scalar color(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256)); cv::rectangle(img, re.box, color, 3); float confidence = floor(100 * re.confidence) / 100; std::cout << std::fixed << std::setprecision(2); std::string label = p->classes[re.classId] + \" \" + std::to_string(confidence).substr(0, std::to_string(confidence).size() - 4); cv::rectangle( img, cv::Point(re.box.x, re.box.y - 25), cv::Point(re.box.x + label.length() * 15, re.box.y), color, cv::FILLED ); cv::putText( img, label, cv::Point(re.box.x, re.box.y - 5), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(0, 0, 0), 2 ); } std::cout << \"Press any key to exit\" << std::endl; cv::imshow(\"Result of Detection\", img); std::string save_path = output_folder + \"/\" + i.path().filename().string(); cv::imwrite(save_path, img); cv::waitKey(0); cv::destroyAllWindows(); img.release(); } }}
void DetectTest()
函数中的ReadCocoYaml(yoloDetector)注释掉,改为:
yoloDetector->classes = { “person”};
yoloDetector->classes = { \"person\"}; //可根据实际需求自定义类别设置,本文仅需进行人物识别以验证效果即可。
函数中params.modelPath修改为onnx模型的路径
函数末尾加上delete yoloDetector;
void DetectTest(){ YOLO_V8* yoloDetector = new YOLO_V8; /*ReadCocoYaml(yoloDetector);*/ yoloDetector->classes = { \"person\" }; DL_INIT_PARAM params; params.rectConfidenceThreshold = 0.1; params.iouThreshold = 0.5; params.modelPath = \"D:/result/yolov8n.onnx\"; params.imgSize = { 640, 640 };#ifdef USE_CUDA params.cudaEnable = true; // GPU FP32 inference params.modelType = YOLO_DETECT_V8; // GPU FP16 inference //Note: change fp16 onnx model //params.modelType = YOLO_DETECT_V8_HALF;#else // CPU inference params.modelType = YOLO_DETECT_V8; params.cudaEnable = false;#endif yoloDetector->CreateSession(params); Detector(yoloDetector); delete yoloDetector;}
int main()
int main(){ DetectTest(); return 0;}
main.cpp 文件中包含的以下函数与目标检测功能无关,删除后不会影响检测结果。
- void Classifier(YOLO_V8*& p)
- int ReadCocoYaml(YOLO_V8*& p)
- void ClsTest()
3.4 运行检测
完成代码修改后即可开始检测。
- 若使用CPU,直接编译运行即可。
- 如需在GPU上运行,请在main.cpp文件首行添加#define USE_CUDA指令。
检测结果将统一保存在指定目录下,默认路径为std::string output_folder = “D:/run”。
四、一些可能遇到的问题和解决方法
经过实际测试,按照上述步骤配置程序可以确保正常运行。然而,某些容易被忽视的细节仍可能导致程序出现错误。
4.1 filesystem飘红
文件系统类filesystem是从C++17开始引入的。VS2019默认标准是C++14,因此需要将标准修改为17及以上。见2.1节。
4.2 应用程序无法正常启动
编译的时候正常,但在运行时出现“应用程序无法正常启动”。出现这个问题基本是因为缺少DLL文件。检查是否将D:\\onnxruntime\\lib**下的DLL文件全部复制到…\\x64\\Debug文件夹中,见2.4节。这样问题基本就能解决。
4.3 Debug Error
Debug Error 的出现通常源于文件路径错误或包含中文字符。建议仔细核对文件路径的准确性,并将文件名统一改为英文格式。
4.4 头文件报错
检查运行时的模式(Debug,x64)是否和配置的项目属性一致,见第2节。