opencv 使用
下載opencv 4.11 ,選擇版本下載,下載exe版本安裝即可
包含目錄添加E:\APP\opencv4.11\opencv\build\include\opencv2,E:\APP\opencv4.11\opencv\build\include
庫目錄添加E:\APP\opencv4.11\opencv\build\x64\vc16\lib
鏈接器-附加依賴項,輸入opencv_world4110.lib(對應release版本),opencv_world4110d.lib(對應debug版本),兩個只能放一個,對應版本,同時把這個.lib放到對應的debug和release文件夾下
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
在把lib文件放入附加依賴項
把三個.dll文件放到debug的有exe文件夾下,這樣啓動程序不會報錯
#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文件夾,沒有就創建
整體流程概覽
整個推理流程分為 8 個主要步驟:
|
步驟
|
內容
|
作用
|
|
1
|
初始化 ONNX Runtime 環境
|
創建推理引擎運行環境
|
|
2
|
設置 GPU / CPU 推理設備
|
決定模型運行在 CPU 還是 GPU 上
|
|
3
|
加載 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);
這部分作用是:
- 讀取圖片;
- 轉為 RGB;
- resize 到模型輸入大小;
- 轉 float、歸一化;
- 用 ImageNet 均值方差標準化;
- 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);
- 取出輸出張量數據;
- 用
cv::Mat包裝; - 計算 softmax → 轉為概率;
- 找出最大概率及對應類別;
- 打印或返回結果。
輸出:
✅ 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庫