深度神经网络——转换 PyTorch 分类模型并用 OpenCV C++ 启动 OpenCV v4.8.0

上一个教程转换 PyTorch 分类模型并使用 OpenCV Python 发布

原作者Anastasia Murzova
兼容性OpenCV >= 4.5

目标

在本教程中,您将学习如何

  • 将 PyTorch 分类模型转换为 ONNX 格式
  • 使用 OpenCV C/C++ API 运行转换后的 PyTorch 模型
  • 提供模型推理

我们将以 ResNet-50 架构为例,探讨上述要点。

简介

让我们简要回顾一下使用 OpenCV API 转换 PyTorch 模型的流程中涉及的关键概念。将 PyTorch 模型转换为 cv::dnn::Net 的第一步是将模型转换为 ONNX 格式。ONNX 旨在实现神经网络在不同框架间的互换。PyTorch 中有一个用于转换 ONNX 的内置函数:torch.onnx.export。然后,得到的 .onnx 模型会被传入 cv::dnn::readNetFromONNXcv::dnn::readNet

要求

要使用下面的代码进行实验,您需要安装一组库。为此,我们将使用带有 python3.7+ 的虚拟环境:

virtualenv -p /usr/bin/python3.7 <env_dir_path>
source <env_dir_path>/bin/activate

要从源代码构建 OpenCV-Python,请遵循 **OpenCV 简介**中的相应说明。

在开始安装库之前,可以自定义 requirements.txt,排除或包含(例如,opencv-python)某些依赖项。下面一行将启动需求安装到先前激活的虚拟环境中:

pip install -r requirements.txt

实践

在这一部分,我们将介绍以下几点:

  1. 创建分类模型转换管道
  2. 提供推理,处理预测结果

模型转换管道

本分章中的代码位于 samples/dnn/dnn_model_runner 模块中,可通过以下命令行执行:

python -m dnn_model_runner.dnn_conversion.pytorch.classification.py_to_py_resnet50_onnx

以下代码包含下列步骤的说明:

  1. 实例化 PyTorch 模型
  2. 将 PyTorch 模型转换为 .onnx
# 初始化 PyTorch ResNet-50 模型
original_model = models.resnet50(pretrained=True)
# 获取转换为 ONNX PyTorch 模型的路径
full_model_path = get_pytorch_onnx_model(original_model)
print("PyTorch ResNet-50 模型转换成功:", full_model_path)

get_pytorch_onnx_model(original_model) 函数基于 torch.onnx.export(...) 调用:

# 为进一步转换的模型定义保存目录
onnx_model_path = "models" # 为进一步转换的模型定义名称
# 为进一步转换的模型定义名称
onnx_model_name = "resnet50.onnx" # 为进一步转换的模型创建目录
# 为进一步转换的模型创建目录
os.makedirs(onnx_model_path, exist_ok=True)
# 获取转换后模型的完整路径
full_model_path = os.path.join(onnx_model_path, onnx_model_name)
# 生成模型输入
generated_input = Variable(
    torch.randn(1, 3, 224, 224)
)
# 将模型导出为 ONNX 格式
torch.onnx.export(
    original_model、
    generated_input,
    full_model_path、
    verbose=True、
    input_names=["input"]、
    output_names=["output"]、
    opset_version=11
)

成功执行上述代码后,我们将得到以下输出:

PyTorch ResNet-50 模型已成功转换:models/resnet50.onnx

通过 dnn/samples 模块 dnn_model_runner,我们可以为以下 PyTorch 分类模型重现上述转换步骤:

  • alexnet
  • vgg11
  • vgg13
  • vgg16
  • vgg19
  • resnet18
  • resnet34
  • resnet50
  • resnet101
  • resnet152
  • squeezenet1_0
  • squeezenet1_1
  • resnext50_32x4d
  • resnext101_32x8d
  • wide_resnet50_2
  • wide_resnet101_2

要获得转换后的模型,应执行以下一行:

python -m dnn_model_runner.dnn_conversion.pytorch.classification.py_to_py_cls --model_name <pytorch_cls_model_name> --evaluate False

对于 ResNet-50 情况,应运行以下一行:

python -m dnn_model_runner.dnn_conversion.pytorch.classification.py_to_py_cls --model_name resnet50 --evaluate False

转换后模型存储的默认根目录在 CommonConfig 模块中定义:

@dataclass
class CommonConfig:
    output_data_root_dir: str = "dnn_model_runner/dnn_conversion" (输出数据根目录)

因此,转换后的 ResNet-50 将保存在 dnn_model_runner/dnn_conversion/models 中。

推理管道

现在,我们可以使用 models/resnet50.onnx 通过 OpenCV C/C++ API 进行推理。实现的管道可在 samples/dnn/classification.cpp 中找到。构建样本(BUILD_EXAMPLES 标志值应为 ON)后,将提供相应的 example_dnn_classification 可执行文件。

为了提供模型推理,我们将使用下面与 ImageNet 类 ID 335 相对应的松鼠照片(采用 CC0 许可):

狐松鼠,东部狐松鼠,Sciurus niger

在这里插入图片描述

分类模型输入图像

为了对预测结果进行标签解码,我们还需要 imagenet_classes.txt 文件,其中包含 ImageNet 类别的完整列表。

在本教程中,我们将运行构建 (samples/build) 目录中转换的 PyTorch ResNet-50 模型的推理过程:

./dnn/example_dnn_classification --model=../dnn/models/resnet50.onnx --input=../data/squirrel_cls.jpg --width=224 --height=224 --rgb=true --scale="0.003921569" --mean="123. 675 116.28 103.53" --std="0.229 0.224 0.225" --crop=true --initial_width=256 --initial_height=256 --classes=../data/dnn/classification_classes_ILSVRC2012.txt

让我们逐步探索 classification.cpp 的关键点:

  1. 使用 cv::dnn::readNet 读取模型,初始化网络:
net = readNet(model,config,framework)

模型参数值取自 --model key。在我们的例子中,它是 resnet50.onnx

- 预处理输入图像:
if (rszWidth != 0 && rszHeight != 0)
{
    resize(frame, frame, Size(rszWidth, rszHeight))}
// 从帧创建 4D blob
blobFromImage(frame, blob, scale, Size(inpWidth, inpHeight), mean, swapRB, crop)// 检查 std 值。
if (std.val[0] != 0.0 && std.val[1] != 0.0 && std.val[2] != 0.0)
{
    // 将 blob 除以 std。
    divide(blob, std, blob)}

在这一步中,我们使用 cv::dnn::blobFromImage 函数准备模型输入。我们设置了 Size(rszWidth,rszHeight),其中 --initial_width=256 --initial_height=256 用于调整初始图像大小,正如 PyTorch ResNet 推理管道 中所描述的那样。

需要注意的是,首先要减去 cv::dnn::blobFromImage 中的平均值,然后才将像素值乘以比例。因此,我们使用 --mean="123.675 116.28 103.53",相当于 [0.485, 0.456, 0.406] 乘以 255.0,以重现 PyTorch 分类模型的原始图像预处理顺序:

img /= 255.0
img -= [0.485, 0.456, 0.406]
img /= [0.229, 0.224, 0.225]
  • 进行前传:
net.setInput(blob);
Mat prob = net.forward()
  • 处理预测结果:
Point classIdPoint;
double confidence;
minMaxLoc(prob.reshape(1, 1), 0, &confidence, 0, &classIdPoint)int classId = classIdPoint.x;
  • 在这里,我们选择最有可能的对象类别。本例的 classId 结果为 335 - 狐松鼠,东部狐松鼠,Sciurus niger:

在这里插入图片描述

ResNet50 OpenCV C++ 推理输出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值