Onnx Model inference by C++

[TOC]

0. onnx模型准备以及测试图

1. c++使用onnxruntime进行推理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/dnn.hpp>
#include <iostream>
#include <onnxruntime_cxx_api.h>
#include <assert.h>
#include <vector>
#include <fstream>


using namespace cv; //当定义这一行后,cv::imread可以直接写成imread
using namespace std;
using namespace Ort;
using namespace cv::dnn;

String labels_txt_file = "F:\\Pycharm\\PyCharm_Study\\Others\\c++_learning\\C++_Master\\Onnx\\classification\\classification_classes_ILSVRC2012.txt";
vector<String> readClassNames(); // string对象作为vector对象

// 图像处理 标准化处理
void PreProcess(const Mat& image, Mat& image_blob)
{
Mat input;
image.copyTo(input);


//数据处理 标准化
std::vector<Mat> channels, channel_p;
split(input, channels);
Mat R, G, B;
B = channels.at(0);
G = channels.at(1);
R = channels.at(2);

B = (B / 255. - 0.406) / 0.225;
G = (G / 255. - 0.456) / 0.224;
R = (R / 255. - 0.485) / 0.229;

channel_p.push_back(R);
channel_p.push_back(G);
channel_p.push_back(B);

Mat outt;
merge(channel_p, outt);
image_blob = outt;
}


// 读取txt文件
std::vector<String> readClassNames()
{
std::vector<String> classNames;

std::ifstream fp(labels_txt_file);
if (!fp.is_open())
{
printf("could not open file...\n");
exit(-1);
}
std::string name;
while (!fp.eof())
{
std::getline(fp, name);
if (name.length())
classNames.push_back(name);
}
fp.close();
return classNames;
}



int main() // 返回值为整型带参的main函数. 函数体内使用或不使用argc和argv都可
{

//environment (设置为VERBOSE(ORT_LOGGING_LEVEL_VERBOSE)时,方便控制台输出时看到是使用了cpu还是gpu执行)
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "OnnxModel");
Ort::SessionOptions session_options;
// 使用1个线程执行op,若想提升速度,增加线程数
session_options.SetIntraOpNumThreads(1);
CUDA加速开启(由于onnxruntime的版本太高,无cuda_provider_factory.h的头文件,加速可以使用onnxruntime V1.8的版本)
//OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);
// ORT_ENABLE_ALL: 启用所有可能的优化
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);

//load model and creat session

#ifdef _WIN32
const wchar_t* model_path = L"F:\\Pycharm\\PyCharm_Study\\Others\\c++_learning\\C++_Master\\Onnx\\classification\\vgg16.onnx";
#else
const char* model_path = "F:\\Pycharm\\PyCharm_Study\\Others\\c++_learning\\C++_Master\\Onnx\\classification\\vgg16.onnx";
#endif

printf("Using Onnxruntime C++ API\n");
Ort::Session session(env, model_path, session_options);
// print model input layer (node names, types, shape etc.)
Ort::AllocatorWithDefaultOptions allocator;


//model info
// 获得模型又多少个输入和输出,一般是指对应网络层的数目
// 一般输入只有图像的话input_nodes为1
size_t num_input_nodes = session.GetInputCount();
// 如果是多输出网络,就会是对应输出的数目
size_t num_output_nodes = session.GetOutputCount();
printf("Number of inputs = %zu\n", num_input_nodes);
printf("Number of output = %zu\n", num_output_nodes);
//获取输入name
const char* input_name = session.GetInputName(0, allocator);
std::cout << "input_name:" << input_name << std::endl;
//获取输出name
const char* output_name = session.GetOutputName(0, allocator);
std::cout << "output_name: " << output_name << std::endl;
// 自动获取维度数量
auto input_dims = session.GetInputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();
auto output_dims = session.GetOutputTypeInfo(0).GetTensorTypeAndShapeInfo().GetShape();
std::cout << "input_dims:" << input_dims[0] << std::endl;
std::cout << "output_dims:" << output_dims[0] << std::endl;
std::vector<const char*> input_names{ input_name };
std::vector<const char*> output_names = { output_name };
std::vector<const char*> input_node_names = { "input.1" };
std::vector<const char*> output_node_names = { "70"};


//加载图片
Mat img = imread("F:\\Pycharm\\PyCharm_Study\\Others\\c++_learning\\C++_Master\\Onnx\\classification\\dog.jpg");
Mat det1, det2;
resize(img, det1, Size(256, 256), INTER_AREA);
det1.convertTo(det1, CV_32FC3);
PreProcess(det1, det2); //标准化处理
Mat blob = dnn::blobFromImage(det2, 1., Size(224, 224), Scalar(0, 0, 0), false, true);
printf("Load success!\n");

clock_t startTime, endTime;
//创建输入tensor
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);
std::vector<Ort::Value> input_tensors;
input_tensors.emplace_back(Ort::Value::CreateTensor<float>(memory_info, blob.ptr<float>(), blob.total(), input_dims.data(), input_dims.size()));
/*cout << int(input_dims.size()) << endl;*/
startTime = clock();

推理(score model & input tensor, get back output tensor)
auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), input_tensors.data(), input_names.size(), output_node_names.data(), output_node_names.size());
endTime = clock();
assert(output_tensors.size() == 1 && output_tensors.front().IsTensor());
//除了第一个节点外,其他参数与原网络对应不上程序就会无法执行
//第二个参数代表输入节点的名称集合
//第四个参数1代表输入层的数目
//第五个参数代表输出节点的名称集合
//最后一个参数代表输出节点的数目
获取输出(Get pointer to output tensor float values)
float* floatarr = output_tensors[0].GetTensorMutableData<float>(); // 也可以使用output_tensors.front(); 获取list中的第一个元素变量 list.pop_front(); 删除list中的第一个位置的元素
// 得到最可能分类输出
Mat newarr = Mat_<double>(1, 1000); //定义一个1*1000的矩阵
for (int i = 0; i < newarr.rows; i++)
{
for (int j = 0; j < newarr.cols; j++) //矩阵列数循环
{
newarr.at<double>(i, j) = floatarr[j];
}
}
/*cout << newarr.size() << endl;*/

vector<String> labels = readClassNames();
for (int n = 0; n < newarr.rows; n++) {
Point classNumber;
double classProb;
Mat probMat = newarr(Rect(0, n, 1000, 1)).clone();
Mat result = probMat.reshape(1, 1);
minMaxLoc(result, NULL, &classProb, NULL, &classNumber);
int classidx = classNumber.x;
printf("\n current image classification : %s, possible : %.2f\n", labels.at(classidx).c_str(), classProb);

// 显示文本
putText(img, labels.at(classidx), Point(10, 20), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 0, 255), 1, 1);
imshow("Image Classification", img);
waitKey(0);
}

计算运行时间
std::cout << "The run time is:" << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << std::endl;
printf("Done!\n");
system("pause");
return 0;
}

参考资料

Install ONNX Runtime (ORT)

C++使用onnxruntime/opencv对onnx模型进行推理(附代码)

在C++上利用onnxruntime (CUDA)和 opencv 部署模型onnx