這是一個基于ONNX Runtime的YOLOv8目標檢測項目,支持CPU和GPU加速,使用Qt框架構建圖形化界面。
攝像頭實時畫面識別
視頻文件識別,能正常識別目標:紅綠燈,人,公交,巴士,摩托車 等
YOLOv8推理引擎?核心檢測算法實現
ONNX Runtime 1.20.1 - 支持CPU和GPU兩個版本
OpenCV 4.5.4 - 圖像處理和計算機視覺
tl-expected- 錯誤處理庫
QT += core gui widgets
CONFIG += c++17 consoleCONFIG += WIN_MSVC
#CONFIG += LINUX_X86CONFIG += CPU
#CONFIG += GPU##############WIN_MSVC###############
CONFIG(WIN_MSVC){
DESTDIR = ./bin_win/#opecv
INCLUDEPATH += $$PWD\ThirdParty\opencv454\include
CONFIG(debug,debug|release): LIBS += $$PWD\ThirdParty\opencv454\x64\vc16\lib\opencv_world454d.lib
CONFIG(release,debug|release): LIBS += $$PWD\ThirdParty\opencv454\x64\vc16\lib\opencv_world454.lib#onnxruntime
CONFIG(CPU){
DEFINES += CPU
INCLUDEPATH += $$PWD\ThirdParty\onnxruntime-win-x64-1.20.1\include
LIBS += $$PWD\ThirdParty\onnxruntime-win-x64-1.20.1\lib\onnxruntime.lib
}CONFIG(GPU){
DEFINES += GPU
INCLUDEPATH += $$PWD\ThirdParty\onnxruntime-win-x64-gpu-1.20.1\include
LIBS += $$PWD\ThirdParty\onnxruntime-win-x64-gpu-1.20.1\lib\onnxruntime.lib
}
}##############LINUX_X86###############
CONFIG(LINUX_X86){
DESTDIR = ./bin_linux/#opecv
INCLUDEPATH += $$PWD\ThirdParty\opencv454\include
LIBS += -lopencv_core -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc -lopencv_video -lopencv_videoio -lopencv_calib3d#onnxruntime
CONFIG(CPU){
DEFINES += CPU
INCLUDEPATH += $$PWD\ThirdParty\onnxruntime-win-x64-1.20.1\include
LIBS += -lonnxruntime
}CONFIG(GPU){
DEFINES += GPU
INCLUDEPATH += $$PWD\ThirdParty\onnxruntime-win-x64-gpu-1.20.1\include
LIBS += -lonnxruntime
}
}
######################################SOURCES += \inference.cpp \main.cpp \mainwindow.cppHEADERS += \inference.h \mainwindow.h \tl-expected.hppFORMS += \mainwindow.uiMOC_DIR = tmp/moc
RCC_DIR = tmp/rcc
UI_DIR = tmp/ui
OBJECTS_DIR = tmp/obj
#include "inference.h"
#include <filesystem>
#include <fstream>
#include <codecvt> // macos必須,否則提示String2WString函數報錯namespace fs = std::filesystem;std::vector<std::string> YoloV8::st_classes_;tl::expected<bool, std::string> YoloV8::create_session(const std::string &model_path)
{// 檢查模型文件是否存在if (!fs::exists(model_path)) return tl::unexpected("模型文件不存在!");// 創建Ort環境env_ = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "Yolo");Ort::SessionOptions session_options;// 添加CUDA執行提供程序
#ifdef GPUOrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);
#endif// 設置圖優化級別session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);// 設置線程數session_options.SetIntraOpNumThreads(5);// 設置日志級別session_options.SetLogSeverityLevel(ORT_LOGGING_LEVEL_WARNING);// 創建會話std::wstring model_path_w = String2WString(model_path);session_ = Ort::Session(env_, model_path_w.c_str(), session_options);Ort::AllocatorWithDefaultOptions allocator;// 獲取輸入節點名稱size_t input_count = session_.GetInputCount();for (size_t i = 0; i < input_count; i++)input_node_names_.emplace_back(session_.GetInputNameAllocated(i, allocator));// 獲取輸出節點名稱size_t output_count = session_.GetOutputCount();for (size_t i = 0; i < output_count; i++)output_node_names_.emplace_back(session_.GetOutputNameAllocated(i, allocator));// 創建運行選項options_ = Ort::RunOptions{nullptr};return true;
}// 對輸入的圖像進行預處理
tl::expected<cv::Mat, std::string> YoloV8::pre_process(cv::Mat &img, cv::Size img_size)
{// 如果輸入的圖像為空,則返回一個錯誤信息if (img.empty()){return tl::unexpected("圖片為空");}// 獲取輸入圖像的寬度和高度的最大值int max_side = std::max(img.cols, img.rows);// 計算輸入圖像的縮放比例resize_scales_ = max_side / static_cast<float>(img_size.width);// 創建一個與輸入圖像大小相同的空白圖像,填充值為114, 114, 114cv::Mat tmp(max_side, max_side, CV_8UC3, cv::Scalar(114, 114, 114));// 將輸入圖像復制到空白圖像中img.copyTo(tmp(cv::Rect(0, 0, img.cols, img.rows)));// 將空白圖像轉換為深度學習模型所需的輸入格式cv::Mat res = cv::dnn::blobFromImage(tmp, 1.0 / 255.0, img_size, cv::Scalar(0, 0, 0), true, false, CV_32F);// 返回轉換后的圖像return res;
}std::vector<DlResult> YoloV8::post_process(std::vector<Ort::Value> &outputs, float conf_threshold, float iou_threshold)
{// 定義一個存儲結果的向量std::vector<DlResult> vec_results;// 獲取輸出的類型信息Ort::TypeInfo type_info = outputs.front().GetTypeInfo();// 獲取輸出的張量類型和形狀信息auto tensor_info = type_info.GetTensorTypeAndShapeInfo();// 獲取輸出的形狀std::vector<int64_t> output_node_dims = tensor_info.GetShape();// 獲取輸出的數據auto output = outputs.front().GetTensorMutableData<float>(); // 8400// 輸出的形狀是 [1, 84, 8400]int stride_num = output_node_dims[2]; // 8400int signal_result_num = output_node_dims[1]; // 84// 定義存儲類別、置信度和框的向量std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;// 為存儲類別、置信度和框的向量預留空間class_ids.reserve(stride_num / 8);confidences.reserve(stride_num / 8);boxes.reserve(stride_num / 8);// 將輸出的數據轉換為矩陣cv::Mat raw_data = cv::Mat(signal_result_num, stride_num, CV_32F, output).t();// 獲取矩陣的指針float *data = raw_data.ptr<float>(0);// 遍歷每個輸出for (int i = 0; i < stride_num; ++i){// 找到置信度最高的類別auto max_it = std::max_element(data + 4, data + 80); // std::max_element返回指向最大元素的迭代器float max_class_socre = *max_it;int max_idx = std::distance(data + 4, max_it);// 如果置信度大于閾值,則存儲類別、置信度和框if (max_class_socre > conf_threshold){confidences.push_back(max_class_socre);class_ids.push_back(max_idx);float x = data[0];float y = data[1];float w = data[2];float h = data[3];// 計算框的位置和大小int left = int((x - 0.5 * w) * resize_scales_);int top = int((y - 0.5 * h) * resize_scales_);int width = int(w * resize_scales_);int height = int(h * resize_scales_);// 存儲框boxes.emplace_back(left, top, width, height);}// 移動到下一個輸出data += signal_result_num;}// 進行非極大值抑制std::vector<int> nms_result;cv::dnn::NMSBoxes(boxes, confidences, conf_threshold, iou_threshold, nms_result);// 將結果存儲到向量中for (int i = 0; i < nms_result.size(); ++i){int idx = nms_result[i];DlResult result;result.class_id = class_ids[idx];result.confidence = confidences[idx];result.box = boxes[idx];vec_results.push_back(result);}// 返回結果return vec_results;
}// 定義一個函數,用于將Ort::AllocatedStringPtr類型的向量轉換為const char*類型的向量
std::vector<const char *> YoloV8::get_name_data(std::vector<Ort::AllocatedStringPtr> &names)
{// 定義一個const char*類型的向量,用于存儲轉換后的數據std::vector<const char *> res;// 遍歷names向量中的每一個元素for (const auto &name : names){// 將每一個元素轉換為const char*類型,并添加到res向量中res.push_back(name.get());}// 返回轉換后的向量return res;
}tl::expected<std::vector<DlResult>, std::string> YoloV8::run_session(cv::Mat &img, cv::Size input_size, float confidence_threshold, float iou_threshold)
{// 預處理圖像auto ex_img = pre_process(img, input_size);// 如果預處理失敗,返回錯誤信息if (!ex_img){return tl::unexpected(ex_img.error());}// 定義輸入張量的形狀std::vector<int64_t> input_shape = {1, 3, input_size.height, input_size.width};// 創建輸入張量Ort::Value input_tensor = Ort::Value::CreateTensor<float>(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), ex_img.value().ptr<float>(0), 3 * input_size.height * input_size.width, input_shape.data(), input_shape.size());// 運行會話auto output_tensor = session_.Run(options_, get_name_data(input_node_names_).data(), &input_tensor, input_node_names_.size(), get_name_data(output_node_names_).data(),output_node_names_.size());// 后處理輸出張量auto outputs = post_process(output_tensor, confidence_threshold, iou_threshold);// 返回后處理結果return outputs;
}void YoloV8::draw_boxes(cv::Mat &img, std::vector<DlResult> &results)
{if (st_classes_.size() == 0)return;for (auto &re : results){cv::RNG rng(cv::getTickCount());cv::Scalar color(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));cv::rectangle(img, re.box, color, 3);float confidence = floor(100 * re.confidence) / 100;std::string label = st_classes_[re.class_id] + " " +std::to_string(confidence).substr(0, std::to_string(confidence).size() - 4);cv::rectangle(img,cv::Point(re.box.x, re.box.y - 25),cv::Point(re.box.x + label.length() * 15, re.box.y),color,cv::FILLED);cv::putText(img,label,cv::Point(re.box.x, re.box.y - 5),cv::FONT_HERSHEY_SIMPLEX,0.75,cv::Scalar(0, 0, 0),2);}
}
主要功能特性
- **CPU推理**: 使用標準ONNX Runtime
- **GPU推理**: 支持CUDA加速
2. 完整的檢測流水線
**預處理** (`pre_process`)
- 圖像尺寸調整到640x640
- Letterbox填充保持寬高比
- 像素值歸一化
**推理** (`run_session`)
- ONNX模型前向推理
- 批量處理支持
**后處理** (`post_process`)
- 非極大值抑制(NMS)
- 置信度過濾
- 邊界框坐標轉換
3. 可視化功能
- 檢測框繪制
- 類別標簽顯示
- 置信度分數展示
內存管理
- 使用ONNX Runtime的內存分配器
- 智能指針管理資源生命周期
- 避免內存泄漏
性能優化
- 支持GPU加速推理
- 批量處理能力
- 高效的圖像預處理pipeline
適用場景
這個項目特別適合:
- **實時目標檢測應用**
- **工業質檢系統**
- **監控分析系統** ?
- **教學和研究項目**
項目提供了完整的從模型加載到結果可視化的端到端解決方案,是學習和部署YOLOv8模型的優秀起點。