自学内容网 自学内容网

C++ 通过 OpenCV 调用 YOLOv8 ONNX 模型进行图像缺陷检测

在现代计算机视觉领域,YOLO(You Only Look Once)是一种高效的物体检测方法,广泛应用于实时目标检测。YOLOv8 采用更为高效的架构和算法,提供更精确的检测结果。在本文中,我们将介绍如何使用 OpenCV 和 C++ 通过 ONNX 模型进行 YOLOv8 目标检测。

1. 下载 OpenCV

  • 访问 OpenCV 官网:https://opencv.org/releases/。
  • 下载适用于 Windows 的 OpenCV 版本。推荐下载的是 .exe 安装包,通常是 opencv-4.x.x-vc15.exe(对于 Visual Studio 2015+ 的版本)。
  • 安装并解压 OpenCV 至指定目录(例如 C:\opencv)。

2. 在 Visual Studio 中配置 OpenCV

安装 OpenCV 后,配置 Visual Studio:

附加包含目录:添加 OpenCV 的 include 目录,例如:

附加库目录:添加 OpenCV 的 lib 目录,例如:

链接 OpenCV 库

  • 选择 链接器 -> 输入 -> 附加依赖项,并添加 OpenCV 的库文件(例如 opencv_world410d.libopencv_world410.lib,取决于 Debug 或 Release 模式)。
  • 对于 Debug 模式,通常需要使用带有 d 后缀的版本(例如 opencv_world410d.lib)。

使用 C++17 标准

3. 编写 C++ 代码使用 OpenCV 加载和测试模型

假设你已经下载了一个预训练的深度学习模型(如 .onnx 格式的模型),下面是一个使用 OpenCV dnn 模块来加载模型并进行图片检测的示例代码。

以下示例代码展示了如何加载一个 .onnx 模型,并用它对图片进行推理。

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
#include <ctime>

namespace fs = std::filesystem;

int main() {
    // CUDA检查
    std::cout << "OpenCV Version: " << CV_VERSION << std::endl;
    std::cout << "CUDA Available: " << cv::cuda::getCudaEnabledDeviceCount() << std::endl;

    // 输入和输出路径
    std::string input_folder = "D:/BaiduNetdiskDownload/TEST";  // 替换为你的输入文件夹路径
    std::string output_folder = "D:/BaiduNetdiskDownload/TEST_RESULTS";  // 替换为你的输出文件夹路径

    // 创建输出文件夹
    fs::create_directories(output_folder);

    // 加载YOLOv8 ONNX模型
    std::string modelPath = "D:/yolov8n.onnx";  // 替换为你的YOLOv8 ONNX模型路径
    cv::dnn::Net net = cv::dnn::readNetFromONNX(modelPath);
    if (net.empty()) {
        std::cerr << "Failed to load the model!" << std::endl;
        return -1;
    }

    // 设置置信度阈值
    float confThreshold = 0.25f;

    // 遍历文件夹中的图片
    for (const auto& entry : fs::directory_iterator(input_folder)) {
        if (entry.is_regular_file() && entry.path().extension() == ".jpg") {
            std::string input_path = entry.path().string();

            // 开始计时
            clock_t start_time = clock();
            
            // 读取图像
            cv::Mat img = cv::imread(input_path);
            if (img.empty()) {
                std::cerr << "无法加载图片: " << input_path << std::endl;
                continue;
            }

            // 图像预处理:将图片调整为640x640并转换为blob
            cv::Mat blob;
            cv::dnn::blobFromImage(img, blob, 1.0, cv::Size(640, 640), cv::Scalar(0, 0, 0), true, false);
            net.setInput(blob);

            // 获取模型输出
            std::vector<cv::Mat> outputs;
            net.forward(outputs, net.getUnconnectedOutLayersNames());

            // 检测结果处理
            bool has_defect = false;  // 是否检测到缺陷

            for (size_t i = 0; i < outputs.size(); ++i) {
                cv::Mat& output = outputs[i];
                for (int j = 0; j < output.rows; ++j) {
                    float* data = (float*)output.data + j * output.cols;

                    // 置信度
                    float confidence = data[4];
                    if (confidence > confThreshold) {
                        // 类别ID
                        int classId = -1;
                        float* classScores = data + 5;
                        cv::Point classIdPoint;
                        double classConfidence;
                        cv::minMaxLoc(cv::Mat(1, output.cols - 5, CV_32F, classScores), nullptr, &classConfidence, nullptr, &classIdPoint);
                        classId = classIdPoint.x;

                        // 边界框坐标
                        int x1 = (int)(data[0] * img.cols);
                        int y1 = (int)(data[1] * img.rows);
                        int x2 = (int)(data[2] * img.cols);
                        int y2 = (int)(data[3] * img.rows);

                        // 保证边界框在图片范围内
                        x1 = std::max(0, x1);
                        y1 = std::max(0, y1);
                        x2 = std::min(img.cols, x2);
                        y2 = std::min(img.rows, y2);

                        // 绘制矩形框
                        cv::rectangle(img, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 0, 255), 2);
                        std::string label = "Class: " + std::to_string(classId) + " Conf: " + std::to_string(confidence);
                        cv::putText(img, label, cv::Point(x1, y1 - 10), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 0, 0), 2);
                        
                        has_defect = true;  // 标记为有缺陷
                    }
                }
            }

            // 仅保存有缺陷的图片
            if (has_defect) {
                std::string save_path = output_folder + "/" + entry.path().filename().string();
                cv::imwrite(save_path, img);
                clock_t end_time = clock();
                double elapsed_time_ms = (double)(end_time - start_time) / CLOCKS_PER_SEC * 1000;
                std::cout << "图片: " << entry.path().filename() << " | 耗时: " << elapsed_time_ms << " 毫秒 | 已保存检测结果。" << std::endl;
            } else {
                std::cout << "图片: " << entry.path().filename() << " 未检测到缺陷,跳过保存。" << std::endl;
            }

            // 释放内存
            img.release();
            blob.release();
        }
    }

    std::cout << "检测完成!检测结果已保存到: " << output_folder << std::endl;

    return 0;
}

代码说明

  1. 加载 YOLOv8 模型:使用 cv::dnn::readNetFromONNX() 函数加载 YOLOv8 的 ONNX 模型。如果模型加载失败,程序会输出错误信息并退出。

  2. 图像预处理:通过 cv::dnn::blobFromImage() 将输入图像转换为适合 YOLO 模型的输入格式(大小调整为 640x640,且进行归一化)。然后使用 net.setInput() 将图像输入到神经网络中。

  3. 推理与输出:使用 net.forward() 获取模型的输出(即检测结果)。每个输出包含了预测的边界框、置信度和类别。

  4. 检测与标注:遍历每个输出,提取出置信度大于设定阈值的检测框,并绘制边界框和类别标签。

  5. 保存结果:如果检测到缺陷,程序会保存处理后的图像到指定输出目录。保存的文件名与输入文件名相同。

  6. 释放内存:在每次处理完图像后,显式调用 img.release()blob.release() 释放内存,避免内存泄漏。

性能优化

  1. CUDA 加速:如果你的计算机支持 CUDA,并且 OpenCV 已经配置了 CUDA 支持,你可以通过 CUDA 加速 DNN 推理过程。可以通过 cv::cuda::getCudaEnabledDeviceCount() 检查可用的 GPU 数量。

  2. 图像和内存管理:通过显式调用 release() 释放图像和 blob 对象的内存,确保内存不会泄漏,特别是在处理大量图像时尤为重要。

总结

通过 OpenCV 和 C++ 调用 YOLOv8 ONNX 模型,你可以快速对一批图像进行目标检测,并保存检测到缺陷的图像。通过合理的内存管理和性能优化,本程序能够高效地处理大量图像,并输出检测结果。


原文地址:https://blog.csdn.net/m0_58648890/article/details/144193186

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!