onnxruntime-gpu + windows + vs2019 cuda加速推理C++样例超详细

一、环境配置
全是windows 下的版本
cuda:11.1 11.4 11.7 三个版本都试过,都是ok的
cudnn:8.5.0
onnxruntime:1.12.1 relase版本

onnxruntime-gpu下载完后可以看到里面的头文件和静态库动态库,onnxruntime不需要安装,下载完之后需要把头文 件和库文件配置到工程中,下面有具体方法
PS D:\tools\onnxruntime-win-x64-gpu-1.12.1> tree /f
D:.
│ CodeSignSummary-e54fd8c5-34c1-462b-a8b2-0761efa3159d.md
│ GIT_COMMIT_ID
│ LICENSE
│ Privacy.md
│ README.md
│ ThirdPartyNotices.txt
│ VERSION_NUMBER

├─include
│ cpu_provider_factory.h
│ onnxruntime_cxx_api.h
│ onnxruntime_cxx_inline.h
│ onnxruntime_c_api.h
│ onnxruntime_run_options_config_keys.h
│ onnxruntime_session_options_config_keys.h
│ provider_options.h
│ tensorrt_provider_factory.h

└─lib
onnxruntime.dll
onnxruntime.lib
onnxruntime.pdb
onnxruntime_providers_cuda.dll
onnxruntime_providers_cuda.lib
onnxruntime_providers_cuda.pdb
onnxruntime_providers_shared.dll
onnxruntime_providers_shared.lib
onnxruntime_providers_shared.pdb
onnxruntime_providers_tensorrt.dll
onnxruntime_providers_tensorrt.lib
onnxruntime_providers_tensorrt.pdb

二、使用vs2019创建cmake工程或者解决方案工程,都行,这里我用的是cmake工程
1、创建vs2019创建cmake工程会自动创建build文件夹,就不需要单独创建了,创建完结果如下:

在这里插入图片描述
2、配置项目文件下的CMkaeLists.txt
将onnxruntime和opencv的静态库和头文件配置到CMakeLists.txt中,opencv的安装方式网上一大把,这里不在多说
在这里插入图片描述

此外将onnxruntime.dll、 onnxruntime_providers_cuda.dll、onnxruntime_providers_shared.dll、 onnxruntime_providers_tensorrt.dll放到C:\windows\system32中或者放到程序执行目录下,也就是.exe所在目录下

在这里插入图片描述
3、工程下的CMakeList.txt配置就很简单了
在这里插入图片描述
三、代码
模型文件,测试图片,标签获取请参考
https://blog.csdn.net/qq_44747572/article/details/120820964

头文件
CMaketest.h

// CMaketest.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。

#pragma once

#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>
#include <numeric>

源文件
CMaketest.cpp

#include "CMaketest.h"
bool CheckStatus(const OrtApi* g_ort, OrtStatus* status) {
	if (status != nullptr) {
		const char* msg = g_ort->GetErrorMessage(status);
		std::cerr << msg << std::endl;
		g_ort->ReleaseStatus(status);
		throw Ort::Exception(msg, OrtErrorCode::ORT_EP_FAIL);
	}
	return true;
}

using namespace cv;     
using namespace std;
using namespace Ort;
using namespace cv::dnn;

// 图像处理  标准化处理
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;
}


int main()         
{
	const OrtApi* g_ort = NULL;
	const OrtApiBase* ptr_api_base = OrtGetApiBase();
	g_ort = ptr_api_base->GetApi(ORT_API_VERSION);
	OrtEnv* env;
	CheckStatus(g_ort, g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "test", &env));
	OrtSessionOptions* session_options;
	CheckStatus(g_ort, g_ort->CreateSessionOptions(&session_options));
	CheckStatus(g_ort, g_ort->SetIntraOpNumThreads(session_options, 1));
	CheckStatus(g_ort, g_ort->SetSessionGraphOptimizationLevel(session_options, ORT_ENABLE_BASIC));
	//CUDA option set
	OrtCUDAProviderOptions cuda_option;
	cuda_option.device_id = 0;
	cuda_option.arena_extend_strategy = 0;
	cuda_option.cudnn_conv_algo_search = OrtCudnnConvAlgoSearchExhaustive;
	cuda_option.gpu_mem_limit = SIZE_MAX;
	cuda_option.do_copy_in_default_stream = 1;
	//CUDA 加速
	CheckStatus(g_ort, g_ort->SessionOptionsAppendExecutionProvider_CUDA(session_options, &cuda_option));
	
	//load  model and creat session
	//模型文件路径
	const wchar_t* model_path = L"D:\\xx\\onnxruntimetest\\vgg16.onnx";
	printf("Using Onnxruntime C++ API\n");
	OrtSession* session;
	CheckStatus(g_ort, g_ort->CreateSession(env, model_path, session_options, &session));
	OrtAllocator* allocator;
	CheckStatus(g_ort, g_ort->GetAllocatorWithDefaultOptions(&allocator));

	
	//**********输入信息**********//
	size_t num_input_nodes; //输入节点的数量
	CheckStatus(g_ort, g_ort->SessionGetInputCount(session, &num_input_nodes));
	std::vector<const char*> input_node_names; //输入节点的名称
	std::vector<std::vector<int64_t>> input_node_dims; //输入节点的维度
	std::vector<ONNXTensorElementDataType> input_types; //输入节点的类型
	std::vector<OrtValue*> input_tensors; //输入节点的tensor

	input_node_names.resize(num_input_nodes);
	input_node_dims.resize(num_input_nodes);
	input_types.resize(num_input_nodes);
	input_tensors.resize(num_input_nodes);

	for (size_t i = 0; i < num_input_nodes; i++) {
		// Get input node names
		char* input_name;
		CheckStatus(g_ort, g_ort->SessionGetInputName(session, i, allocator, &input_name));
		input_node_names[i] = input_name;

		// Get input node types
		OrtTypeInfo* typeinfo;
		CheckStatus(g_ort, g_ort->SessionGetInputTypeInfo(session, i, &typeinfo));
		const OrtTensorTypeAndShapeInfo* tensor_info;
		CheckStatus(g_ort, g_ort->CastTypeInfoToTensorInfo(typeinfo, &tensor_info));
		ONNXTensorElementDataType type;
		CheckStatus(g_ort, g_ort->GetTensorElementType(tensor_info, &type));
		input_types[i] = type;

		// Get input shapes/dims
		size_t num_dims;
		CheckStatus(g_ort, g_ort->GetDimensionsCount(tensor_info, &num_dims));
		input_node_dims[i].resize(num_dims);
		CheckStatus(g_ort, g_ort->GetDimensions(tensor_info, input_node_dims[i].data(), num_dims));

		size_t tensor_size;
		CheckStatus(g_ort, g_ort->GetTensorShapeElementCount(tensor_info, &tensor_size));

		if (typeinfo) g_ort->ReleaseTypeInfo(typeinfo);
	}
	//---------------------------------------------------//

	//***********输出信息****************//

	size_t num_output_nodes;
	std::vector<const char*> output_node_names;
	std::vector<std::vector<int64_t>> output_node_dims;
	std::vector<OrtValue*> output_tensors;
	CheckStatus(g_ort, g_ort->SessionGetOutputCount(session, &num_output_nodes));
	output_node_names.resize(num_output_nodes);
	output_node_dims.resize(num_output_nodes);
	output_tensors.resize(num_output_nodes);

	for (size_t i = 0; i < num_output_nodes; i++) {
		// Get output node names
		char* output_name;
		CheckStatus(g_ort, g_ort->SessionGetOutputName(session, i, allocator, &output_name));
		output_node_names[i] = output_name;

		OrtTypeInfo* typeinfo;
		CheckStatus(g_ort, g_ort->SessionGetOutputTypeInfo(session, i, &typeinfo));
		const OrtTensorTypeAndShapeInfo* tensor_info;
		CheckStatus(g_ort, g_ort->CastTypeInfoToTensorInfo(typeinfo, &tensor_info));

		// Get output shapes/dims
		size_t num_dims;
		CheckStatus(g_ort, g_ort->GetDimensionsCount(tensor_info, &num_dims));
		output_node_dims[i].resize(num_dims);
		CheckStatus(g_ort, g_ort->GetDimensions(tensor_info, (int64_t*)output_node_dims[i].data(), num_dims));

		size_t tensor_size;
		CheckStatus(g_ort, g_ort->GetTensorShapeElementCount(tensor_info, &tensor_size));

		if (typeinfo) g_ort->ReleaseTypeInfo(typeinfo);
	}
	//---------------------------------------------------//

	printf("Number of inputs = %zu\n", num_input_nodes);
	printf("Number of output = %zu\n", num_output_nodes);
	std::cout << "input_name:" << input_node_names[0] << std::endl;
	std::cout << "output_name: " << output_node_names[0] << std::endl;

	//加载图片
	Mat img = imread("D:\\xx\\onnxruntimetest\\dog.jpg");
	Mat det1, det2, det3;
	resize(img, det1, Size(224, 224), INTER_AREA);
	det1.convertTo(det1, CV_32FC3);
	PreProcess(det1, det2);         //标准化处理
	//cvtColor(det2, det3, CV_BGR2RGB);
	//size_t input_data_length0 = det2.step * det2.rows;
	Mat blob = dnn::blobFromImage(det2, 1., Size(224, 224), Scalar(0, 0, 0), false, true);
	size_t input_data_length = blob.total() * blob.elemSize();
	printf("Load success!\n");

	clock_t startTime, endTime;
	//创建输入tensor
	OrtMemoryInfo* memory_info;

	CheckStatus(g_ort, g_ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info));
	CheckStatus(g_ort, g_ort->CreateTensorWithDataAsOrtValue(
						         memory_info, reinterpret_cast<void*>(blob.data), input_data_length,
						         input_node_dims[0].data(), input_node_dims[0].size(), input_types[0], &input_tensors[0]));

	g_ort->ReleaseMemoryInfo(memory_info);
    //循环推理看效果
	for (int i = 0; i < 10; i++)
	{
		startTime = clock();
		CheckStatus(g_ort, g_ort->Run(session, nullptr, input_node_names.data(), (const OrtValue* const*)input_tensors.data(),
					input_tensors.size(), output_node_names.data(), output_node_names.size(),
					output_tensors.data()));
		endTime = clock();
		cout << i << ": " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
	}

	void* output_buffer;
	CheckStatus(g_ort, g_ort->GetTensorMutableData(output_tensors[0], &output_buffer));
	float* float_buffer = reinterpret_cast<float*>(output_buffer);
	size_t output_data_size = output_node_dims[0][1];
	auto max = std::max_element(float_buffer, float_buffer + output_data_size);

	std::vector<float> optu(float_buffer, float_buffer + output_data_size);
	int max_index = static_cast<int>(std::distance(float_buffer, max));
	

	//标签文件加载
	std::fstream label_file("D:\\xx\\onnxruntimetest\\classification_classes_ILSVRC2012.txt", std::ios::in);
	std::unordered_map<int, std::string> label_table;
	label_table.reserve(output_data_size);
	int i = 0;
	std::string line;
	while (std::getline(label_file, line)) {
		label_table.emplace(i++, line);
	}

	printf("%d, %f, %s \n", max_index, *max, label_table[max_index].c_str());
	printf("Done!\n");
	return 0;
}

四、结果
第一次推理时间很长,原因推测GPU唤醒需要时间,也跟模型有关
在这里插入图片描述

五、说一下遇到的坑
1、onnxruntime的动态库其实需要onnxruntime.dll、 onnxruntime_providers_cuda.dll、onnxruntime_providers_shared.dll这3个,而且一定要放对位置,否则会出各种内存异常的问题,可以看一下debug模式下,加载所需的dll,上文也已经说过也可以放在windows\system32下,优先从cmake可执行文件目录里面找dll。

在这里插入图片描述

2、提示找不到zlibwapi.dll文件,导致gpu推理失败
nvidia官网提供下载链接
https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#prerequisites-windows

  • 13
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 30
    评论
部署 ONNX Runtime GPU 版本的 C API 可以分为以下几个步骤: 1. 下载 ONNX Runtime GPU 版本的 C API 库文件和模型文件。 2. 编写 C 代码,调用 ONNX Runtime C API 加载模型文件,并使用 ONNX Runtime 进行推理。 3. 配置环境变量,设置 LD_LIBRARY_PATH 为 ONNX Runtime C API 库文件所在的路径。 4. 编译 C 代码,链接 ONNX Runtime C API 库文件,生成可执行文件。 以下是一个简单的示例代码,使用 ONNX Runtime C API 加载模型文件并进行推理: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include "onnxruntime_c_api.h" int main() { // 加载模型文件 OrtEnv* env; OrtCreateEnv(ORT_LOGGING_LEVEL_WARNING, "test", &env); OrtSession* session; OrtSessionOptions* session_options; OrtCreateSessionOptions(&session_options); OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0); OrtCreateSession(env, "model.onnx", session_options, &session); // 准备输入数据 float input_data[1][3][224][224]; // ... 填充输入数据 // 创建输入张量 size_t input_shape[] = {1, 3, 224, 224}; OrtMemoryInfo* memory_info; OrtCreateCpuMemoryInfo(OrtDeviceAllocator, OrtMemTypeCPU, 0, OrtMemAllocatorTypeDefault, &memory_info); OrtValue* input_tensor; OrtCreateTensorWithDataAsOrtValue(memory_info, input_data, sizeof(input_data), input_shape, 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, &input_tensor); // 创建输出张量 OrtValue* output_tensor; size_t output_shape[] = {1, 1000}; OrtCreateTensorAsOrtValue(memory_info, output_shape, 2, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, &output_tensor); // 进行推理 OrtRunOptions* run_options; OrtCreateRunOptions(&run_options); OrtRun(session, run_options, NULL, &input_tensor, 1, &output_tensor, 1); // 获取输出数据 float* output_data = (float*)OrtGetTensorMutableData(output_tensor); // ... 处理输出数据 // 释放资源 OrtReleaseValue(output_tensor); OrtReleaseValue(input_tensor); OrtReleaseMemoryInfo(memory_info); OrtReleaseSession(session); OrtReleaseSessionOptions(session_options); OrtReleaseEnv(env); return 0; } ``` 编译命令如下: ```bash gcc -I /path/to/onnxruntime/include -L /path/to/onnxruntime/lib -lonnxruntime -lonnxruntime_providers_cuda -o test test.c ``` 其中 /path/to/onnxruntimeONNX Runtime C API 和 CUDA Execution Provider 库文件所在的路径。
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

idealmu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值