在 OpenCV 4.5.5 的 C++ 接口中,调用不同框架训练好的模型,核心是使用 cv::dnn::Net 对象和一系列 readNetFrom* 函数。流程非常统一:加载模型 -> 预处理图像 -> 设置输入 -> 执行推理 -> 处理输出

一、模型加载函数与文件格式

为了方便你快速查阅,我将四个框架的加载方式整理成了下面的表格。它们的核心思想都是一致的:使用 cv::dnn::Net 对象,并调用对应的 readNetFrom* 函数来加载模型

模型框架 OpenCV 加载函数 (C++) 所需文件 关键说明
Caffe cv::dnn::readNetFromCaffe() 配置文件 (.prototxt)
权重文件 (.caffemodel)
直接加载这两个文件即可,这是最经典的组合。
TensorFlow cv::dnn::readNetFromTensorflow() 模型文件 (.pb)
配置文件 (.pbtxt,可选)
主要支持冻结图(frozen graph)格式的 .pb 文件。可选的 .pbtxt 文件用于明确指定输入输出节点名称。
Darknet cv::dnn::readNetFromDarknet() 配置文件 (.cfg)
权重文件 (.weights)
直接加载 Darknet 框架训练出的模型,常见于 YOLO 系列目标检测模型。
PyTorch cv::dnn::readNetFromONNX() ONNX 模型文件 (.onnx) OpenCV 不直接支持 PyTorch 的 .pth 文件,需要先通过 torch.onnx.export() 将其转换为通用的 ONNX 格式。

此外,OpenCV 还支持一个通用的 cv::dnn::readNet() 函数,它会根据文件扩展名自动尝试推断模型类型并加载,可以让你的代码更简洁。

二、C++ 推理代码示例

一旦模型加载到 cv::dnn::Net 对象中,后续的图像预处理、推理和后处理步骤对所有框架来说都是完全一致的。下面是一个包含 Darknet 在内的完整 C++ 代码框架:

cpp

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

int main() {
    // --- 1. 加载模型 (选择对应你框架的函数) ---
    cv::dnn::Net net;

    // 方案 A: 加载 Caffe 模型
    // std::string prototxt = "path/to/deploy.prototxt";
    // std::string caffemodel = "path/to/model.caffemodel";
    // net = cv::dnn::readNetFromCaffe(prototxt, caffemodel);

    // 方案 B: 加载 TensorFlow 模型
    // std::string tf_pb = "path/to/frozen_graph.pb";
    // std::string tf_pbtxt = "path/to/graph.pbtxt"; // 可选
    // net = cv::dnn::readNetFromTensorflow(tf_pb, tf_pbtxt);

    // 方案 C: 加载 Darknet 模型 (例如 YOLOv4)
    std::string darknet_cfg = "path/to/yolov4.cfg";
    std::string darknet_weights = "path/to/yolov4.weights";
    net = cv::dnn::readNetFromDarknet(darknet_cfg, darknet_weights);

    // 方案 D: 加载 PyTorch 转换后的 ONNX 模型
    // std::string onnx_file = "path/to/model.onnx";
    // net = cv::dnn::readNetFromONNX(onnx_file);

    if (net.empty()) {
        std::cerr << "无法加载模型!请检查文件路径和格式。" << std::endl;
        return -1;
    }
    std::cout << "模型加载成功。" << std::endl;

    // 可选:设置计算后端以实现加速 (需OpenCV支持CUDA/OpenVINO)
    // net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
    // net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);

    // --- 2. 读取并预处理图像 ---
    std::string image_path = "path/to/your_image.jpg";
    cv::Mat img = cv::imread(image_path);
    if (img.empty()) {
        std::cerr << "无法读取图像!" << std::endl;
        return -1;
    }

    // 关键步骤:创建Blob。参数必须与模型训练时的预处理一致!
    // 这里以Darknet的YOLOv4为例 (输入尺寸416x416, 归一化因子1/255.0)
    cv::Mat blob = cv::dnn::blobFromImage(img,
                                           1.0 / 255.0,           // scalefactor: 像素值缩放到[0,1]
                                           cv::Size(416, 416),    // size: 模型输入尺寸
                                           cv::Scalar(0, 0, 0),    // mean: 此处为0,因为已在scalefactor归一化
                                           true,                   // swapRB: 将OpenCV的BGR转为模型所需的RGB
                                           false);                 // crop: 是否裁剪

    // --- 3. 设置网络输入并执行推理 ---
    net.setInput(blob);

    // 对于有多个输出的网络 (如YOLO),使用vector<Mat>来接收结果
    std::vector<cv::Mat> outputs;
    // 获取所有输出层的名称
    std::vector<cv::String> outNames = net.getUnconnectedOutLayersNames();
    net.forward(outputs, outNames); // 执行前向传播

    // --- 4. 处理输出结果 (此处以Darknet YOLO的检测结果为例) ---
    std::cout << "推理完成,共有 " << outputs.size() << " 个输出层。" << std::endl;
    // 后处理:遍历outputs,解析边界框、置信度和类别,并应用NMS等
    // ... (后处理代码因模型和任务而异,此处省略具体实现)

    return 0;
}

三、注意事项

  1. 数据预处理必须匹配训练:这是决定推理成败的关键。blobFromImage() 中的 scalefactormeanswapRB 等参数,必须和你训练模型时所用的预处理方式完全一致。例如,Darknet 的 YOLO 模型通常需要将像素值归一化到 0-1(即 scalefactor=1/255.0),并交换 R 和 B 通道。

  2. 处理多输出网络:像 YOLO 这样的目标检测网络通常有多个输出层。你需要使用 net.getUnconnectedOutLayersNames() 获取所有输出层的名称,并用 std::vector<cv::Mat> 来接收 forward 的返回值。

  3. TensorFlow 模型格式:请确保你准备的是冻结图(frozen graph)格式的 .pb 文件。如果你只有 TensorFlow 2 的 SavedModel,需要先用脚本将它转换。

  4. PyTorch 模型转换:将 PyTorch 的 .pth 文件转换为 .onnx 时,需要指定输入输出的名称和动态轴,以确保转换后的模型能被 OpenCV 正确解析。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐