这是一个基于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模型的优秀起点。