【C++】使用opencv+onnxruntime推理YOLOv8目标检测

通过C/C++语言能够实现深度学习模型的高效部署。在众多部署框架中,本文采用OpenCV结合ONNX Runtime的方案来部署YOLOv8目标检测模型。其中,ONNX Runtime作为核心推理引擎,而OpenCV则主要负责图像读取等辅助功能。


前言

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 的目标检测流程主要包含以下步骤:

  1. 图像加载与预处理:通过 OpenCV 读取目标图像,并根据模型需求进行尺寸调整、归一化等预处理操作。
  2. 模型加载:使用 ONNX Runtime 加载预训练的 ONNX 格式目标检测模型,为推理做好准备。
  3. 模型推理:将预处理后的图像数据输入模型,利用 ONNX Runtime 进行推理计算,获取初步检测结果。
  4. 结果后处理:借助 OpenCV 对检测结果进行可视化处理,包括绘制目标边界框、添加类别标签等操作。

一、环境准备

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:\opencv
D:\onnxruntime

下载好opencv和onnxruntime后,将其加入到环境变量中。右键/设置 此电脑–》属性–》高级系统设置–》高级–》环境变量,将onnxruntime文件夹里include/lib以及opencv文件夹下…\build\x64\vc16添加进环境变量。

D:\onnxruntime\include
D:\onnxruntime\lib
D:\opencv\build\x64\vc16\bin
D:\opencv\build\x64\vc16\lib

在这里插入图片描述

二、项目属性

2.1 新建项目

打开VS2019创建一个空项目,命名为“yolov8”。将上面下载的yolov8中推理源码main.cppinference.cppinference.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\include
D:\opencv\build\include\opencv2
D:\opencv\build\include

2.2.3 附加库目录

接着在链接器–》常规–》附加库目录中将opencv和onnxruntime的‘“lib”文件夹添加进去

D:\opencv\build\x64\vc16\lib
D:\onnxruntime\lib

2.2.4 附加依赖项

接着在链接器–》输入–》附加依赖项中将添加以下内容

opencv_world490d.lib
onnxruntime.lib
onnxruntime_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节。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值