c++ 使用onnx推理
·
假设你已经安装好onnx,并且成功配置。这章说的是加载onnx并推理的过程,输入的input已经是处理过后的。
下面举几个例子
图像分类推理
最常见的形式就是图像分类网络,输入是个图片mat(3x224x224),输出是个一维(1x7)向量。
int yolov5_onnx_model(Mat blob)
{
#ifdef _WIN32
const wchar_t* model_path = L"E://c++//mmpose//resnet18.onnx";
#else
const char* model_path = "E://c++//mmpose//resnet18.onnx";
#endif
//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
//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 = { "images" }; //自己打开onnx模型看下,名称不一样会报错
std::vector<const char*> output_node_names = { "output" };
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>();
// 得到最可能分类输出
vector<float> preds;
for (int i = 0; i < 7; i++)
{
preds.push_back(floatarr[i]);
}
// 得到分类最大id
int classid = 0;
for (int i = 0; i < preds.size(); i++) {
if (preds[i] > preds[classid]) {
classid = i;
}
}
return classid;
}
——————————————————————
vector输入mat输出
这里展示的是不同输入输出的改动,例如我输入的是176448的热图,输出是25200*7的矩阵。和之前不同的是,这里直接设置了输入输出的维度,没和之前一样读取。输出读取的时候因为输出不是一维的,也有点不一样。
这里示例的是输入三维,换成一维vector也一样,重点就是按顺序拉成一维的就行。
Mat resnet18_onnx_model(vector<vector<vector<float>>> heatmaps)
{
// onnx模型路径
#ifdef _WIN32
const wchar_t* model_path = L"E://c++//mmpose//yolov5.onnx";
#else
const char* model_path = "E://c++//mmpose//yolov5.onnx";
#endif
//这四行是设置onnx环境,尽量不要去动
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "OnnxModel"); // OnnxModel可以改成test其他东西,反正日志我是不去看的
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1); // 使用1个线程执行op,若想提升速度,增加线程数
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); // ORT_ENABLE_ALL: 启用所有可能的优化
//加载模型,开启session
Ort::Session session(env, model_path, session_options);
Ort::AllocatorWithDefaultOptions allocator;
//创建输入tensor,输入的维度,我是17*64*48维的特征,onnx模型的输入要拉成一维
std::vector<int64_t> input_node_dims = { 1, 17, 64, 48 };
size_t input_tensor_size = 1 * 17 * 64 * 48;
std::vector<float> input_tensor_values(input_tensor_size);
for (int i = 0; i < input_node_dims[1]; i++) {
for (int j = 0; j < input_node_dims[2]; j++) {
for (int k = 0; k < input_node_dims[3]; k++) {
input_tensor_values.push_back(heatmaps[i][j][k]);
}
}
}
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), input_node_dims.size());
assert(input_tensor.IsTensor());
/* 这部分不写也一样能运行,只要写input和output.name
// 创建输出tensor
std::vector<Ort::Value> ort_inputs;
ort_inputs.push_back(std::move(input_tensor));
std::vector<int64_t> output_node_dims = { 1, 7 };
size_t output_tensor_size = 1 * 7;
std::vector<double> output_tensor_values(output_tensor_size);
Ort::Value output_tensor = Ort::Value::CreateTensor<double>(memory_info, output_tensor_values.data(), output_tensor_size, output_node_dims.data(), output_node_dims.size());
*/
//推理(score model & input tensor, get back output tensor)
auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), ort_inputs.data(), ort_inputs.size(), output_node_names.data(), output_node_names.size());
//获取输出(Get pointer to output tensor float values)
Ort::Value& det_out = output_tensors.at(0);
// 得到最可能分类输出
Mat newarr = Mat_<float>(25200, 7); //定义一个矩阵,我onnx的输出是25200x7
for (int i = 0; i < newarr.rows; i++)
{
vector<float> out;
for (int j = 0; j < newarr.cols; j++) //矩阵列数循环
{
newarr.at<float>(i, j) = det_out.At<float>({ 0, i, j });
}
}
return newarr;
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)