使用onnxruntime加载YOLOv8生成的onnx文件进行目标检测

2024-06-02 07:52

本文主要是介绍使用onnxruntime加载YOLOv8生成的onnx文件进行目标检测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

      在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集,使用 LabelMe  工具进行标注,然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件,并自动生成YOLOv8支持的目录结构,包括melon.yaml文件,其内容如下:

path: ../datasets/melon # dataset root dir
train: images/train # train images (relative to 'path')
val: images/val  # val images (relative to 'path')
test: # test images (optional)# Classes
names:0: watermelon1: wintermelon

      使用以下python脚本进行训练生成onnx文件

import argparse
import colorama
from ultralytics import YOLOdef parse_args():parser = argparse.ArgumentParser(description="YOLOv8 train")parser.add_argument("--yaml", required=True, type=str, help="yaml file")parser.add_argument("--epochs", required=True, type=int, help="number of training")parser.add_argument("--task", required=True, type=str, choices=["detect", "segment"], help="specify what kind of task")args = parser.parse_args()return argsdef train(task, yaml, epochs):if task == "detect":model = YOLO("yolov8n.pt") # load a pretrained modelelif task == "segment":model = YOLO("yolov8n-seg.pt") # load a pretrained modelelse:print(colorama.Fore.RED + "Error: unsupported task:", task)raiseresults = model.train(data=yaml, epochs=epochs, imgsz=640) # train the modelmetrics = model.val() # It'll automatically evaluate the data you trained, no arguments needed, dataset and settings rememberedmodel.export(format="onnx") #, dynamic=True) # export the model, cannot specify dynamic=True, opencv does not support# model.export(format="onnx", opset=12, simplify=True, dynamic=False, imgsz=640)model.export(format="torchscript") # libtorchif __name__ == "__main__":colorama.init()args = parse_args()train(args.task, args.yaml, args.epochs)print(colorama.Fore.GREEN + "====== execution completed ======")

      以下是使用onnxruntime接口加载onnx文件进行目标检测的实现代码:

namespace {constexpr bool cuda_enabled{ false };
constexpr int image_size[2]{ 640, 640 }; // {height,width}, input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 6, 8400)
constexpr float model_score_threshold{ 0.45 }; // confidence threshold
constexpr float model_nms_threshold{ 0.50 }; // iou threshold#ifdef _MSC_VER
constexpr char* onnx_file{ "../../../data/best.onnx" };
constexpr char* torchscript_file{ "../../../data/best.torchscript" };
constexpr char* images_dir{ "../../../data/images/predict" };
constexpr char* result_dir{ "../../../data/result" };
constexpr char* classes_file{ "../../../data/images/labels.txt" };
#else
constexpr char* onnx_file{ "data/best.onnx" };
constexpr char* torchscript_file{ "data/best.torchscript" };
constexpr char* images_dir{ "data/images/predict" };
constexpr char* result_dir{ "data/result" };
constexpr char* classes_file{ "data/images/labels.txt" };
#endifstd::vector<std::string> parse_classes_file(const char* name)
{std::vector<std::string> classes;std::ifstream file(name);if (!file.is_open()) {std::cerr << "Error: fail to open classes file: " << name << std::endl;return classes;}std::string line;while (std::getline(file, line)) {auto pos = line.find_first_of(" ");classes.emplace_back(line.substr(0, pos));}file.close();return classes;
}auto get_dir_images(const char* name)
{std::map<std::string, std::string> images; // image name, image path + image namefor (auto const& dir_entry : std::filesystem::directory_iterator(name)) {if (dir_entry.is_regular_file())images[dir_entry.path().filename().string()] = dir_entry.path().string();}return images;
}void draw_boxes(const std::vector<std::string>& classes, const std::vector<int>& ids, const std::vector<float>& confidences,const std::vector<cv::Rect>& boxes, const std::string& name, cv::Mat& frame)
{if (ids.size() != confidences.size() || ids.size() != boxes.size() || confidences.size() != boxes.size()) {std::cerr << "Error: their lengths are inconsistent: " << ids.size() << ", " << confidences.size() << ", " << boxes.size() << std::endl;return;}std::cout << "image name: " << name << ", number of detections: " << ids.size() << std::endl;std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<int> dis(100, 255);for (auto i = 0; i < ids.size(); ++i) {auto color = cv::Scalar(dis(gen), dis(gen), dis(gen));cv::rectangle(frame, boxes[i], color, 2);std::string class_string = classes[ids[i]] + ' ' + std::to_string(confidences[i]).substr(0, 4);cv::Size text_size = cv::getTextSize(class_string, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect text_box(boxes[i].x, boxes[i].y - 40, text_size.width + 10, text_size.height + 20);cv::rectangle(frame, text_box, color, cv::FILLED);cv::putText(frame, class_string, cv::Point(boxes[i].x + 5, boxes[i].y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}//cv::imshow("Inference", frame);//cv::waitKey(-1);std::string path(result_dir);path += "/" + name;cv::imwrite(path, frame);
}std::wstring ctow(const char* str)
{constexpr size_t len{ 128 };wchar_t wch[len];swprintf(wch, len, L"%hs", str);return std::wstring(wch);
}float image_preprocess(const cv::Mat& src, cv::Mat& dst)
{cv::cvtColor(src, dst, cv::COLOR_BGR2RGB);float resize_scales{ 1. };if (src.cols >= src.rows) {resize_scales = src.cols * 1.f / image_size[1];cv::resize(dst, dst, cv::Size(image_size[1], static_cast<int>(src.rows / resize_scales)));} else {resize_scales = src.rows * 1.f / image_size[0];cv::resize(dst, dst, cv::Size(static_cast<int>(src.cols / resize_scales), image_size[0]));}cv::Mat tmp = cv::Mat::zeros(image_size[0], image_size[1], CV_8UC3);dst.copyTo(tmp(cv::Rect(0, 0, dst.cols, dst.rows)));dst = tmp;return resize_scales;
}template<typename T>
void image_to_blob(const cv::Mat& src, T* blob)
{for (auto c = 0; c < 3; ++c) {for (auto h = 0; h < src.rows; ++h) {for (auto w = 0; w < src.cols; ++w) {blob[c * src.rows * src.cols + h * src.cols + w] = (src.at<cv::Vec3b>(h, w)[c]) / 255.f;}}}
}void post_process(const float* data, int rows, int stride, float xfactor, float yfactor, const std::vector<std::string>& classes,cv::Mat& frame, const std::string& name)
{std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;for (auto i = 0; i < rows; ++i) {const float* classes_scores = data + 4;cv::Mat scores(1, classes.size(), CV_32FC1, (float*)classes_scores);cv::Point class_id;double max_class_score;cv::minMaxLoc(scores, 0, &max_class_score, 0, &class_id);if (max_class_score > model_score_threshold) {confidences.push_back(max_class_score);class_ids.push_back(class_id.x);float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * xfactor);int top = int((y - 0.5 * h) * yfactor);int width = int(w * xfactor);int height = int(h * yfactor);boxes.push_back(cv::Rect(left, top, width, height));}data += stride;}std::vector<int> nms_result;cv::dnn::NMSBoxes(boxes, confidences, model_score_threshold, model_nms_threshold, nms_result);std::vector<int> ids;std::vector<float> confs;std::vector<cv::Rect> rects;for (size_t i = 0; i < nms_result.size(); ++i) {ids.emplace_back(class_ids[nms_result[i]]);confs.emplace_back(confidences[nms_result[i]]);rects.emplace_back(boxes[nms_result[i]]);}draw_boxes(classes, ids, confs, rects, name, frame);
}} // namespaceint test_yolov8_detect_onnxruntime()
{// reference: ultralytics/examples/YOLOv8-ONNXRuntime-CPPtry {Ort::Env env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "Yolo");Ort::SessionOptions session_option;if (cuda_enabled) {OrtCUDAProviderOptions cuda_option;cuda_option.device_id = 0;session_option.AppendExecutionProvider_CUDA(cuda_option);}session_option.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);session_option.SetIntraOpNumThreads(1);session_option.SetLogSeverityLevel(3);Ort::Session session(env, ctow(onnx_file).c_str(), session_option);Ort::AllocatorWithDefaultOptions allocator;std::vector<const char*> input_node_names, output_node_names;std::vector<std::string> input_node_names_, output_node_names_;for (auto i = 0; i < session.GetInputCount(); ++i) {Ort::AllocatedStringPtr input_node_name = session.GetInputNameAllocated(i, allocator);input_node_names_.emplace_back(input_node_name.get());}for (auto i = 0; i < session.GetOutputCount(); ++i) {Ort::AllocatedStringPtr output_node_name = session.GetOutputNameAllocated(i, allocator);output_node_names_.emplace_back(output_node_name.get());}for (auto i = 0; i < input_node_names_.size(); ++i)input_node_names.emplace_back(input_node_names_[i].c_str());for (auto i = 0; i < output_node_names_.size(); ++i)output_node_names.emplace_back(output_node_names_[i].c_str());Ort::RunOptions options(nullptr);std::unique_ptr<float[]> blob(new float[image_size[0] * image_size[1] * 3]);std::vector<int64_t> input_node_dims{ 1, 3, image_size[1], image_size[0] };auto classes = parse_classes_file(classes_file);if (classes.size() == 0) {std::cerr << "Error: fail to parse classes file: " << classes_file << std::endl;return -1;}for (const auto& [key, val] : get_dir_images(images_dir)) {cv::Mat frame = cv::imread(val, cv::IMREAD_COLOR);if (frame.empty()) {std::cerr << "Warning: unable to load image: " << val << std::endl;continue;}auto tstart = std::chrono::high_resolution_clock::now();cv::Mat rgb;auto resize_scales = image_preprocess(frame, rgb);image_to_blob(rgb, blob.get());Ort::Value input_tensor = Ort::Value::CreateTensor<float>(Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU), blob.get(), 3 * image_size[1] * image_size[0], input_node_dims.data(), input_node_dims.size());auto output_tensors = session.Run(options, input_node_names.data(), &input_tensor, 1, output_node_names.data(), output_node_names.size());Ort::TypeInfo type_info = output_tensors.front().GetTypeInfo();auto tensor_info = type_info.GetTensorTypeAndShapeInfo();std::vector<int64_t> output_node_dims = tensor_info.GetShape();auto output = output_tensors.front().GetTensorMutableData<float>();int stride_num = output_node_dims[1];int signal_result_num = output_node_dims[2];cv::Mat raw_data = cv::Mat(stride_num, signal_result_num, CV_32F, output);raw_data = raw_data.t();float* data = (float*)raw_data.data;auto tend = std::chrono::high_resolution_clock::now();std::cout << "elapsed millisenconds: " << std::chrono::duration_cast<std::chrono::milliseconds>(tend - tstart).count() << " ms" << std::endl;post_process(data, signal_result_num, stride_num, resize_scales, resize_scales, classes, frame, key);}}catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;return -1;}return 0;
}

      labels.txt文件内容如下:仅2类

watermelon 0
wintermelon 1

      说明

      1.这里使用的onnxruntime版本为1.18.0;

      2.windows下,onnxruntime库在debug和release为同一套库,在debug和release下均可执行;

      3.通过指定变量cuda_enabled判断走cpu还是gpu流程 ;

      4.windows下,onnxruntime中有些接口参数为wchar_t*,而linux下为char*,因此在windows下需要单独做转换,这里通过ctow函数实现从char*到wchar_t的转换;

      5.yolov8中提供的sample有问题,需要作调整。

      执行结果如下图所示:同样的预测图像集,与opencv dnn结果相似,它们具有相同的后处理流程;下面显示的耗时是在cpu下,gpu下仅20毫秒左右

      其中一幅图像的检测结果如下图所示:

      GitHub:https://github.com/fengbingchun/NN_Test

这篇关于使用onnxruntime加载YOLOv8生成的onnx文件进行目标检测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023382

相关文章

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(