新版OpenCV5.0在ONNX模型的推理应用

1、OpenCV5.0的新特性

OpenCV5 是架构级大改版,彻底清理老旧 C 接口、重写 DNN 推理引擎、原生支持视觉大模型、硬件加速体系重构,最低编译标准升级至 C++17。

1.1 ONNX应用新特性

DNN 深度学习引擎是OpenCV5.0版本的最大升级。主要新特性如下:

1)全新自研推理引擎双引擎共存

新引擎默认自动启用,兼容动态输入 shape、复杂子图、现代 ONNX 算子;旧经典引擎可手动降级兼容老模型

ONNX算子覆盖率从 4.x 的22% 暴涨至 80%+,YOLO、SAM、LLaVA、ResNet、Diffusion 等主流模型直接导入无压力

移除老旧 Caffe、Darknet 模型解析器,全面转向 ONNX 标准格式

2)原生支持 VLM 视觉语言大模型

内置 Tokenizer、Attention、KV 缓存、解码模块,可直接部署看图问答、图像描述类多模态模型 (LLaVA 等),不用额外套推理框架

3)推理加速扩展

可对接 ONNX Runtime 后端;CPU 矢量优化大幅提速,GPU/CUDA 支持后续迭代补齐;支持量化 INT8/FP16 轻量化推理。

1.2 语言与编译底层大清理

  1. 彻底废弃 C 语言 API

所有旧版cvCreateImage、cvMat等 C 接口全部移除,仅保留现代 C++、Python 绑定,代码体积大幅精简

2)C++ 最低标准强制 C++17

全面使用 std 智能指针、 constexpr、结构化绑定等现代语法,内存管理更安全,消除大量兼容宏代码

3)Python 绑定全面现代化

支持命名参数传参,不用死记函数参数顺序

类型对齐 NumPy,无隐式转换 bug;异常报错信息精准易懂

pip 一键分包:opencv-python基础、opencv-contrib-python扩展模块,版本严格锁死 5.0.0

4)新增多语言绑定:Swift、Julia 官方支持完善.

和OpenCV4.x比较如下表所示:

维度 OpenCV 4.x OpenCV 5.0
C 接口 兼容保留 彻底删除
C++ 标准 C++11 起步 强制 C++17
ONNX 覆盖 ~22% >80%
大模型 仅 CNN 检测 VLM 多模态原生支持
硬件加速 碎片化 ifdef 统一 HAL 插件架构
FLANN 独立模块 内置 Annoy 替代

2 下载及安装

OpenCV5.0下载地址为:https://github.com/opencv/opencv/releases

下载页面如下图所示:

按上图下载opencv-5.0.0-windows.exe并解压

3.Qt5.12环境下读取图像并显示

我用的Qt5.12环境采用的编译器为VS2017,QT的pro文件配置主要要设置成C++ 17,注意包含的头文件和库文件。如下图所示:

bash 复制代码
#CONFIG += c++11
CONFIG += c++17

SOURCES += \
        main.cpp \
        mainwindow.cpp

HEADERS += \
        mainwindow.h

FORMS += \
        mainwindow.ui

#Added By Lzy
# OpenCV5.0 路径,改成你本地实际路径
#OPENCV_PATH = D:/opencv480/build
OPENCV_PATH = D:/opencv500/build

INCLUDEPATH += $${OPENCV_PATH}/include
LIBS += -L$${OPENCV_PATH}/x64/vc16/lib \
    #-lopencv_world480
    -lopencv_world500

部署推理的代码如下图所示

cpp 复制代码
// 定义检测结果结构体
struct Detection {
    int class_id;
    float confidence;
    cv::Rect box;
};
// 1. 参数配置
const std::string model_path = "Onnx/best.onnx";
const std::string image_path = "./11.jpg";
const float conf_threshold = 0.25f;
const float nms_threshold = 0.45f;
const cv::Size target_size(640, 640);
std::vector<std::string> class_names = {"Node"};

cv::dnn::Net net;
// 图像等比例缩放平铺(Letterbox)预处理,使检测更精准
cv::Mat letterbox(const cv::Mat& source, cv::Size target_size, cv::Scalar bg_color = cv::Scalar(114, 114, 114)) {
    int col = source.cols;
    int row = source.rows;
    float _max = std::max(col, row);
    cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);
    result.setTo(bg_color);
    source.copyTo(result(cv::Rect(0, 0, col, row)));
    cv::Mat resized;
    cv::resize(result, resized, target_size);
    return resized;
}

    //
    static bool bFlag=true;
    // 2. 加载模型 (OpenCV 5.0.0 深度优化了 readNet 引擎)
    if(bFlag)
    {
        net = cv::dnn::readNet(model_path);
        bFlag=false;
    }
    
    // 如果有 NVIDIA CUDA 环境,取消注释以下两行以开启硬件加速
    // net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
    // net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
    
    // 3. 读取并预处理图像
    cv::Mat frame = cv::imread(image_path);
    if (frame.empty()) {
        std::cerr << "无法读取图像: " << image_path << std::endl;
        return ;
    }
    
    // 缩放到 640x640
    cv::Mat letterbox_img = letterbox(frame, target_size);
    
    // 计算原始尺寸到缩放尺寸的比例,用于恢复坐标边界框
    float scale = std::max(frame.cols, frame.rows) / 640.0f;
    
    // 创建模型的输入 Blob (YOLO 标准:像素值除以 255.0,交换 R 与 B 通道)
    cv::Mat blob;
    cv::dnn::blobFromImage(letterbox_img, blob, 1.0 / 255.0, target_size, cv::Scalar(), true, false);
    net.setInput(blob);
    
    // 4. 前向推理
    std::vector<cv::Mat> outputs;
    net.forward(outputs, net.getUnconnectedOutLayersNames());
    
    // 5. 解析输出 (YOLOv11 输出形状为 [1, 84, 8400])
    cv::Mat raw_data = outputs[0];
    raw_data = raw_data.reshape(1, raw_data.size[1]); // 变更为 84 x 8400 的 2D 矩阵
    cv::transpose(raw_data, raw_data);              // 转置为 8400 x 84
    
    float* data = (float*)raw_data.data;
    int rows = raw_data.rows;       // 8400 个候选框
    int dimensions = raw_data.cols; // 84 (4个坐标 + 80个类别置信度)
    int num_classes = dimensions - 4;
    
    std::vector<int> class_ids;
    std::vector<float> confidences;
    std::vector<cv::Rect> boxes;
    
    for (int i = 0; i < rows; ++i) {
        // 指向当前候选框的置信度起始位置
        float* classes_scores = data + 4;
        
        // 寻找最大置信度的类别
        cv::Mat scores(1, num_classes, CV_32FC1, classes_scores);
        cv::Point class_id_point;
        double max_class_socre;
        cv::minMaxLoc(scores, 0, &max_class_socre, 0, &class_id_point);
        
        if (max_class_socre > conf_threshold) {
            // 提取中心坐标及宽高 (x_center, y_center, width, height)
            float cx = data[0];
            float cy = data[1];
            float w = data[2];
            float h = data[3];
            
            // 转换成左上角坐标,并映射回原图尺寸
            int left = static_cast<int>((cx - 0.5 * w) * scale);
            int top = static_cast<int>((cy - 0.5 * h) * scale);
            int width = static_cast<int>(w * scale);
            int height = static_cast<int>(h * scale);
            
            class_ids.push_back(class_id_point.x);
            confidences.push_back(static_cast<float>(max_class_socre));
            boxes.push_back(cv::Rect(left, top, width, height));
        }
        data += dimensions; // 跳到下一个候选框
    }
    
    // 6. 执行非极大值抑制 (NMS) 过滤重叠框
    std::vector<int> nms_indices;
    cv::dnn::NMSBoxes(boxes, confidences, conf_threshold, nms_threshold, nms_indices);
    
    // 7. 绘制检测框
    for (int idx : nms_indices) {
        cv::Rect box = boxes[idx];
        float conf = confidences[idx];
        int class_id = class_ids[idx];
        
        // 边界限制,防止框超出图片范围
        box.x = std::max(0, box.x);
        box.y = std::max(0, box.y);
        box.width = std::min(box.width, frame.cols - box.x);
        box.height = std::min(box.height, frame.rows - box.y);
        
        // 绘制目标框
        cv::rectangle(frame, box, cv::Scalar(0, 255, 0), 2);
        
        // 绘制置信度标签
        std::string label =/* "Class " + std::to_string(class_id)*/ class_names[class_id]+ ": " + cv::format("%.2f", conf);
        int baseLine;
        cv::Size label_size = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
        cv::rectangle(frame, cv::Point(box.x, box.y - label_size.height), cv::Point(box.x + label_size.width, box.y + baseLine), cv::Scalar(0, 255, 0), cv::FILLED);
        cv::putText(frame, label, cv::Point(box.x, box.y), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0), 1);
    }
    
    // 8. 展示与保存结果
    //cv::imshow("YOLOv11 OpenCV5.0 C++ Detection", frame);
    //cv::imwrite("result.jpg", frame);
    //cv::waitKey(0);
    
    QImage qImg = cvMat2QImage(frame);
    QPixmap pix = QPixmap::fromImage(qImg);
    // 自适应缩放,保持比例
    pix = pix.scaled(ui->labelShow->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    ui->labelShow->setPixmap(pix);

运行效果如下:

切换不同的OpenCV4.8.0和OpenCV5.0.0库编译及推理测试结果如下:

OpenCV4.8.0平均推理耗时421ms

OpenCV5.0.0平均推理耗时265ms

可见OpenCV5.0对推理速度进行了优化。

相关推荐
影寂ldy1 小时前
C# 三大内置委托(Action / Func / Predicate)+ Lambda
c++·算法·c#
机器学习之心2 小时前
小龙虾优化算法(COA)驱动的CNN-LSTM多输出回归模型及其SHAP可解释性分析
算法·cnn·lstm·小龙虾优化算法·cnn-lstm多输出回归·shap可解释性分析
阿正的梦工坊2 小时前
【Rust】13-Trait 系统、动态分发与对象安全
算法·安全·rust
言存2 小时前
力扣热题283 移动零
数据结构·算法·leetcode
字节高级特工2 小时前
智能指针原理与使用场景全解析
开发语言·c++·算法
珊瑚里的鱼2 小时前
【动态规划】买卖股票的最佳时机Ⅲ
算法·动态规划
逻辑星辰2 小时前
x-ds-pow-response逆向分析
开发语言·人工智能·python·深度学习·算法
CQU_JIAKE2 小时前
6.9【aAAA]
算法
Lewiis3 小时前
白话桶排序
数据结构·算法·golang·排序算法