基于opencv for unity部署追踪特征提取的onnx模型
在进行图像运算操作以及显示时,Mat的形式基本都为二维矩阵对应图片的size,图片的通道数用元素值得type来代替,而我们的目标是将其转换为onnx模型的输入格式BCHW形式。在这里首先查看我们的Mat格式,由于在设置摄像机时已经对输出格式进行了设置,这里Mat的内容格式为CV_8UC3,及已经进行了矫正,若没有在开始进行设定,默认格式为CV_8UC4及多一个透明度的通道,需要额外进行转换。我们要
上一章我们已经完成了onnx的特征提取模块的图像前处理模型,接下来我们将部署第一个onnx模型。在通入onnx模型前,首先要对输入图像Mat做基本变换。在进行图像运算操作以及显示时,Mat的形式基本都为二维矩阵对应图片的size,图片的通道数用元素值得type来代替,而我们的目标是将其转换为onnx模型的输入格式BCHW形式。
在这里首先查看我们的Mat格式,由于在设置摄像机时已经对输出格式进行了设置,这里Mat的内容格式为CV_8UC3,及已经进行了矫正,若没有在开始进行设定,默认格式为CV_8UC4及多一个透明度的通道,需要额外进行转换
Imgproc.cvtColor(resizedresion, resizedresion, Imgproc.COLOR_RGBA2RGB);
这里转换后每个位置的值为unit8类型,及0到255。我们要将其转换为onnx需要的输入形式,查看onnx文件,可以看到onnx的输入要求为float32。

在opencv for unity中提供了可以调用onnx模型的接口,详情可以去看第一张。同时,opencv提供了从图像到输入转换的函数 blobFromImage,可以将图片mat直接转换为输入的BCHW形式。
这里有两点需要注意,第一个是通常模型的输入需要经过正则化将尺度缩放到(0,1)之间,这里blobFromImage函数友好的提供了scalefactor参数,我们将其设定为1/255即可。第二点需要注意的是,很多模型为了均衡现实图像的各个通道,通常需要对图像的均值和方差进行调整。比如我模型中需要对初始图像的输入进行矫正。
def _get_default_transform(img_size):
pipeline = albu.Compose(
[
albu.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
]
)
在这里对均值进行矫正也可以通过该函数的mean参数进行配置,但方差需要而外配置。swapRB参数控制是否交换RB通道,最后ddepth来决定数据的类型。结合需要我们如下配置
Mat blob = Dnn.blobFromImage(resizedresion, 1.0 / 255.0, new Size(256, 256), new Scalar(0.485, 0.456, 0.406), false, true, CvType.CV_32F);
Core.divide(blob, new Scalar(0.229, 0.224, 0.225), blob);
这样均值和方差都配置好了。这里我们可以来检验一下blob的尺寸。注意不可以直接用blob.size()来看尺寸,因为这时的mat超过了2维,需要使用blob.size(n)来准确查看。

可以看到输出满足要求。下面是调用onnx模型对输入进行处理。Dnn库在读取模型文件时可以指定文件类型也可以直接读取,在这里我们直接读取
getfeatures = Dnn.readNet(onnx_path_getfeatures);
首先是设置输入,Dnn提供了setInput函数,对只有一个输入的情况,直接设置即可
getfeatures.setInput(blob);
在进行模型推理时需要注意一些细节,onnx模型推理将直接输出推理结果或层结果,可以通过阅读onnx文件灵活的读取各个层的输出,这里不详细解释。在这里我们的输出名为”output“,因此可以如下配置。
Mat result = getfeatures.forward("output");
按理说这样就应该顺利的结束了,但我在这里踩了不少坑,这里分享给大家。
首先我们在使用onnx导出模型时通常会给定一个输入,该输入在batchsize维度上的值通常为1,转为onnx后,会自动将该维度设置为batchsize
onnx_model = resnet18
inp_batch = torch.randn(1, 3, 224, 224)
onnx_model.eval()
# 导出模型到 ONNX 格式
torch.onnx.export(onnx_model,
inp_batch,
"resnet18.onnx",
export_params=True,
opset_version=10,
do_constant_folding=True, # 是否执行常量折叠优化
input_names=['input'], # 输入节点名称
output_names=['output'], # 输出节点名称
)

当然老版本的一些onnx导出的模型可能还是1而不会变成batch_size,这是无所谓的,模型都可以正常读取。但还有一种情况,比如我们的网络为全卷积网络,那么其对输入就没有尺寸要求 ,如果使用动态尺寸模式进行导出,就会出现以下显示

及没有具体的height和width,这时如果进行推理则会报错 numAxes==inputs().size()
后来我指定了输入尺寸在进行测试就没有问题了。我在测试的时候发现,即使输入图像的尺寸不满足输入尺寸,模型也是可以正常的推理的,因此我推测Dnn会读取onnx的输入尺寸,依据该尺寸对输入图像进行resize,因此如果没有指定onnx输入尺寸,Dnn则无法完成初始化。
接下来就没有问题了,对输出检测一切正常。

更多推荐
所有评论(0)