opencv 使用

下載opencv 4.11 ,選擇版本下載,下載exe版本安裝即可

包含目錄添加E:\APP\opencv4.11\opencv\build\include\opencv2,E:\APP\opencv4.11\opencv\build\include

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_#qt

庫目錄添加E:\APP\opencv4.11\opencv\build\x64\vc16\lib

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_CUDA_02

鏈接器-附加依賴項,輸入opencv_world4110.lib(對應release版本),opencv_world4110d.lib(對應debug版本),兩個只能放一個,對應版本,同時把這個.lib放到對應的debug和release文件夾下

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_#開發語言_03

onnxRuntime 使用

cuda11.2+ onnxRuntime1.8.1

訓練好的python模型,轉換為onnx,即可使用onnxRuntime運行,相比於dnn更加快速方便,兼容性更強.並且不像opencvdnn需要編譯cuda版本,這個直接下載gpu版本配置就能夠使用gpu.

下載和安裝,打開cmd輸入nvcc -V查看cuda版本根據對應的下載版本對比地址,下載地址

包含目錄添加E:\APP\onnxruntime-win-gpu-x64-1.8.1\include

庫目錄添加E:\APP\onnxruntime-win-gpu-x64-1.8.1\lib

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_#qt_04

在把lib文件放入附加依賴項

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_CUDA_05

把三個.dll文件放到debug的有exe文件夾下,這樣啓動程序不會報錯

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_#分類_06

#include <onnxruntime_cxx_api.h>
#include <onnxruntime_c_api.h>   // ✅ 必須包含這個頭文件,聲明 CUDA Provider API
#include <cuda_provider_factory.h>  // ✅ 1.8 GPU 版本額外需要這個頭

QString opencvManger::getClassificaitonResult(const string & imgPath)
{
	//答應cuda環境
	std::cout << "Available providers:\n";
	auto providers = Ort::GetAvailableProviders();
	for (auto &p : providers)
		std::cout << "  - " << p << std::endl;

	// ==================== 1. 初始化 ONNX Runtime 環境 ====================
	// 創建一個全局環境對象,記錄日誌等級和模型名稱
	Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ConvNext");

	// 創建會話選項對象
	Ort::SessionOptions session_options;

	// 設置線程數,控制 CPU 並行計算
	session_options.SetIntraOpNumThreads(4);

	// 設置圖優化等級,可以提高推理速度
	session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

	// ==================== 2. 設置 GPU / CPU 推理設備 ====================
	// 如果你在編譯時定義了 USE_CUDA,則使用 GPU,否則默認 CPU
	OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);

	// ==================== 3. 加載 ONNX 模型 ====================
	// ONNX 模型文件路徑
	std::string model_path = "./convnext.onnx";

	// 將 std::string 轉為 std::wstring(Windows 文件路徑需要)
	std::wstring w_model_path(model_path.begin(), model_path.end());

	// 使用 env 和 session_options 創建 ONNX Runtime 會話,完成模型加載
	Ort::Session session(env, w_model_path.c_str(), session_options);

	// ==================== 4. 獲取模型輸入輸出信息 ====================
	// 獲取默認分配器,用於分配和釋放字符串等資源
	OrtAllocator* allocator;
	Ort::GetApi().GetAllocatorWithDefaultOptions(&allocator);

	// 獲取模型的輸入節點數量和輸出節點數量
	size_t num_input_nodes = session.GetInputCount();
	size_t num_output_nodes = session.GetOutputCount();

	// 存儲輸入輸出節點名稱
	std::vector<std::string> input_node_names;
	std::vector<std::string> output_node_names;

	// 輸入圖像尺寸
	int input_h = 0, input_w = 0;

	// ---- 獲取輸入信息 ----
	for (size_t i = 0; i < num_input_nodes; i++) {
		// 獲取輸入節點名稱
		char* input_name = session.GetInputName(i, allocator);
		input_node_names.push_back(input_name);
		allocator->Free(allocator, input_name); // 釋放臨時分配的內存

		// 獲取輸入節點的形狀 [N, C, H, W]
		auto input_shape = session.GetInputTypeInfo(i)
			.GetTensorTypeAndShapeInfo()
			.GetShape();

		int ch = input_shape[1];
		input_h = static_cast<int>(input_shape[2]);
		input_w = static_cast<int>(input_shape[3]);

		std::cout << "Input shape: " << ch << "x" << input_h << "x" << input_w << std::endl;
	}

	// ---- 獲取輸出信息 ----
	int num = 0, nc = 0;
	for (size_t i = 0; i < num_output_nodes; i++) {
		// 獲取輸出節點名稱
		char* output_name = session.GetOutputName(i, allocator);
		output_node_names.push_back(output_name);
		allocator->Free(allocator, output_name);

		// 獲取輸出節點的形狀
		auto out_shape = session.GetOutputTypeInfo(i)
			.GetTensorTypeAndShapeInfo()
			.GetShape();

		num = static_cast<int>(out_shape[0]); // 批大小
		nc = static_cast<int>(out_shape[1]);  // 類別數
		std::cout << "Output shape: " << num << "x" << nc << std::endl;
	}

	// ==================== 5. 圖像預處理 ====================
	// 讀取圖像
	cv::Mat image = cv::imread(imgPath);
	if (image.empty()) {
		std::cerr << "❌ Failed to read image: " << imgPath << std::endl;
		return -1;
	}

	// BGR 轉 RGB
	cv::Mat rgb, blob;
	cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);

	// 調整圖像大小到模型輸入尺寸
	cv::resize(rgb, blob, cv::Size(input_w, input_h));

	// 轉為 float 並歸一化到 [0,1]
	blob.convertTo(blob, CV_32F, 1.0 / 255.0);

	// 減均值
	cv::subtract(blob, cv::Scalar(0.485, 0.456, 0.406), blob);

	// 除以標準差
	cv::divide(blob, cv::Scalar(0.229, 0.224, 0.225), blob);

	// HWC -> CHW 並增加 batch 維度 [1,3,H,W]
	cv::Mat input_blob = cv::dnn::blobFromImage(blob);

	// ==================== 6. 構造輸入張量 ====================
	// 定義輸入張量形狀
	std::array<int64_t, 4> input_shape = { 1, 3, input_h, input_w };

	// 張量元素個數
	size_t tensor_size = 3 * input_h * input_w;

	// 分配 CPU 內存信息
	Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(
		OrtArenaAllocator, OrtMemTypeDefault);

	// 創建輸入張量並綁定內存
	Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
		memory_info, input_blob.ptr<float>(), tensor_size,
		input_shape.data(), input_shape.size());

	// ==================== 7. 推理 ====================
	const char* input_names[] = { input_node_names[0].c_str() };
	const char* output_names[] = { output_node_names[0].c_str() };

	// 執行推理
	std::vector<Ort::Value> output_tensors = session.Run(
		Ort::RunOptions{ nullptr }, input_names, &input_tensor, 1, output_names, 1);

	// ==================== 8. 後處理推理結果 ====================
	// 獲取輸出數據指針
	const float* pdata = output_tensors[0].GetTensorMutableData<float>();

	// 包裝成 cv::Mat 方便後處理
	cv::Mat logits(1, nc, CV_32F, (float*)pdata);

	// softmax 計算概率
	cv::Mat expScores;
	cv::exp(logits, expScores);
	float sumExp = static_cast<float>(cv::sum(expScores)[0]);
	cv::Mat softmax = expScores / sumExp;

	// 找到最大類別及其概率
	cv::Point classIdPoint;
	double confidence;
	cv::minMaxLoc(softmax, nullptr, &confidence, nullptr, &classIdPoint);
	int classId = classIdPoint.x;

	// 打印預測結果
	std::vector<std::string> labels = { "A", "B", "C" };
	std::cout << "✅ Predicted class: " << labels[classId]
		<< " (id=" << classId << ", conf=" << confidence << ")" << std::endl;

	// 轉為 QString 返回
	QString result = QString("✅ Predicted class: %1, conf=%2")
		.arg(QString::fromStdString(labels[classId]))
		.arg(confidence);


	return result;

}

同時需要把provider_options.h移動到core/framework文件夾,沒有就創建

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_#開發語言_07

整體流程概覽

整個推理流程分為 8 個主要步驟:

步驟

內容

作用

1

初始化 ONNX Runtime 環境

創建推理引擎運行環境

2

設置 GPU / CPU 推理設備

決定模型運行在 CPU 還是 GPU 上

3

加載 ONNX 模型

.onnx 模型文件載入內存

4

讀取輸入輸出信息

獲取模型的輸入維度、輸出維度等元信息

5

圖像預處理

把原始圖片轉成模型需要的張量格式

6

構造輸入張量

把預處理好的圖像綁定為 ONNX 輸入

7

執行推理

調用 session 運行模型前向推理

8

後處理結果

softmax 計算類別概率,輸出預測結果


🧩 詳細講解每個部分

🔹 1. 初始化 ONNX Runtime 環境

Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ConvNext");
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(4);
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
  • Ort::Env:全局環境對象,負責管理日誌、錯誤、資源等。
  • SessionOptions:控制會話行為,比如多線程、優化等級等。
  • SetIntraOpNumThreads(4):設置在 CPU 上同時運行的線程數。
  • SetGraphOptimizationLevel(...):啓用圖優化,加速推理。

🔹 2. 設置 GPU / CPU 推理設備

OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);
  • 如果編譯時啓用了 USE_CUDA,就使用 GPU 加速
  • 否則會自動回退到 CPU 模式

注意:這句只有在你的項目鏈接了 CUDA 版本的 ONNX Runtime 才生效。


🔹 3. 加載 ONNX 模型

std::string model_path = "./convnext.onnx";
std::wstring w_model_path(model_path.begin(), model_path.end());
Ort::Session session(env, w_model_path.c_str(), session_options);
  • ONNX 模型路徑從字符串轉為 wstring(Windows 路徑要求)。
  • Ort::Session 創建推理會話,模型被加載進內存。

🔹 4. 獲取模型輸入輸出信息

size_t num_input_nodes = session.GetInputCount();
size_t num_output_nodes = session.GetOutputCount();

然後依次獲取:

  • 輸入名(input_node_names)
  • 輸出名(output_node_names)
  • 輸入尺寸 [N, C, H, W]
  • 輸出尺寸 [N, num_classes]

輸出:

Input shape: 3x384x384
Output shape: 1x3

🔹 5. 圖像預處理

cv::Mat image = cv::imread(imgPath);
cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);
cv::resize(rgb, blob, cv::Size(input_w, input_h));
blob.convertTo(blob, CV_32F, 1.0 / 255.0);
cv::subtract(blob, cv::Scalar(0.485, 0.456, 0.406), blob);
cv::divide(blob, cv::Scalar(0.229, 0.224, 0.225), blob);
cv::Mat input_blob = cv::dnn::blobFromImage(blob);

這部分作用是:

  1. 讀取圖片;
  2. 轉為 RGB;
  3. resize 到模型輸入大小;
  4. 轉 float、歸一化;
  5. 用 ImageNet 均值方差標準化;
  6. HWC → CHW,並加 batch 維度(變成 [1,3,H,W])。

🔹 6. 構造輸入張量

std::array<int64_t, 4> input_shape = { 1, 3, input_h, input_w };
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
    memory_info, input_blob.ptr<float>(), tensor_size,
    input_shape.data(), input_shape.size());
  • 創建一個 Tensor 對象,綁定預處理後的圖像數據;
  • 這相當於把圖像數據送入 ONNX Runtime 的輸入節點。

🔹 7. 執行推理

const char* input_names[] = { input_node_names[0].c_str() };
const char* output_names[] = { output_node_names[0].c_str() };

std::vector<Ort::Value> output_tensors = session.Run(
    Ort::RunOptions{ nullptr }, input_names, &input_tensor, 1, output_names, 1);
  • session.Run() 執行一次前向推理;
  • 返回一個 output_tensors 列表;
  • 其中第一個元素即模型輸出。

🔹 8. 後處理推理結果

const float* pdata = output_tensors[0].GetTensorMutableData<float>();
cv::Mat logits(1, nc, CV_32F, (float*)pdata);
cv::exp(logits, expScores);
float sumExp = static_cast<float>(cv::sum(expScores)[0]);
cv::Mat softmax = expScores / sumExp;
cv::minMaxLoc(softmax, nullptr, &confidence, nullptr, &classIdPoint);
  1. 取出輸出張量數據;
  2. cv::Mat 包裝;
  3. 計算 softmax → 轉為概率;
  4. 找出最大概率及對應類別;
  5. 打印或返回結果。

輸出:

✅ Predicted class: B (id=1, conf=0.932)

✅ 總結:執行順序圖

[開始]
   ↓
初始化 ONNX 環境
   ↓
配置 GPU/CPU
   ↓
加載 convnext.onnx 模型
   ↓
讀取輸入輸出形狀
   ↓
預處理圖像 (resize+normalize)
   ↓
構建輸入張量
   ↓
執行推理 session.Run()
   ↓
後處理結果 (softmax + argmax)
   ↓
輸出預測類別與置信度
[結束]

onnx模型Release版本

把onnxRuntime的三個dll複製到release文件夾,opencvword.dll,把模型複製

再導出qt庫

C#使用OnnxRuntime進行Resnet50分類(支持GPU)_c# onnxruntime_CUDA_08