opencv调用Caffe、TensorFlow、Darknet、Torch训练好的模型
在 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;
}
三、注意事项
-
数据预处理必须匹配训练:这是决定推理成败的关键。
blobFromImage()中的scalefactor、mean、swapRB等参数,必须和你训练模型时所用的预处理方式完全一致。例如,Darknet 的 YOLO 模型通常需要将像素值归一化到0-1(即scalefactor=1/255.0),并交换 R 和 B 通道。 -
处理多输出网络:像 YOLO 这样的目标检测网络通常有多个输出层。你需要使用
net.getUnconnectedOutLayersNames()获取所有输出层的名称,并用std::vector<cv::Mat>来接收forward的返回值。 -
TensorFlow 模型格式:请确保你准备的是冻结图(frozen graph)格式的
.pb文件。如果你只有 TensorFlow 2 的SavedModel,需要先用脚本将它转换。 -
PyTorch 模型转换:将 PyTorch 的
.pth文件转换为.onnx时,需要指定输入输出的名称和动态轴,以确保转换后的模型能被 OpenCV 正确解析。
更多推荐



所有评论(0)