C# yolo10使用onnx推理

ops/2024/11/17 22:43:36/

一、前言

    本篇总结C#端使用yolo10的onnx文件做模型推理,主要使用Microsoft.ML.OnnxRuntime.Gpu这个库。需要注意的是Microsoft.ML.OnnxRuntime 和 Microsoft.ML.OnnxRuntime.Gpu 这2库只装1个就行,CPU就装前者,反之后者。然后需要注意系统安装的CUDA版本、CUDNN、OnnxRuntime这3者的版本一致,可在这里查询  NVIDIA - CUDA | onnxruntime

    这里使用的是 Microsoft.ML.OnnxRuntime.Gpu 版本 1.15.1版本

CUDA 11.8 和 Cudnn 8.5.0

二、代码

        使用vs2022平台 debug x64模式,注意需要将图片进行 letterbox居中填充预处理,以及将Mat转为Tensor,数据排布需要转换,详看 letterBox 和 matToTensor,yolo10输出不需要使用nms,输出矩阵(300,6),直接置信度筛除

    //Form.cs    void inferDemo() {  string model_path = @"model/yolov10x.onnx";string device = "GPU";  //CPU  GPU  float conf_thr = 0.15f;DefectYolo yoloDet = new DefectYolo(model_path = model_path, conf_thr = conf_thr, device = device);  //只需一次初始化string img_path = @"model/demo.png";bool show_draw = true;yoloDet.inferImg(img_path, show_draw = show_draw);}

//DefectYolo.csusing System;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;using OpenCvSharp;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using static OpenCvSharp.LineIterator;
using System.Collections;
using System.Web.Compilation;
using System.IO;
using System.Security.Claims;namespace yolo10onnx
{class DefectYolo{private int  model_height=0, model_width=0;private int ch = 3;private float conf_thr = 0.15f;float[] floatArray;private InferenceSession session;IDisposableReadOnlyCollection<DisposableNamedOnnxValue> result_infer;public DefectYolo(string model_path,  float conf_thr, string device = "GPU" ){//初始化模型var sessionOptions = new SessionOptions();if (device.Contains("GPU")){try{sessionOptions.AppendExecutionProvider_CUDA();  //只需要安装 Microsoft.ML.OnnxRuntime.GPU , 然后 onnxruntime 版本和 CUDA cudnn版本都要对好}catch (Exception ex){MessageBox.Show("模型初始化失败!GPU调用发生错误:" + ex.Message);}}else{sessionOptions.AppendExecutionProvider_CPU();}//根据onnx文件路径实例化一个推理对象session = new InferenceSession(model_path, sessionOptions);//session.Run 第一次很慢,先预热var inputMeta = session.InputMetadata;foreach (var input in inputMeta){int[] model_shape = input.Value.Dimensions;model_height = model_shape[2];model_width = model_shape[3];}DenseTensor<float> zeroT = getRandomTensor();List<NamedOnnxValue> input_ontainer = new List<NamedOnnxValue>(); ;//将 input_tensor 放入一个输入参数的容器,并指定名称input_ontainer.Add(NamedOnnxValue.CreateFromTensor("images", zeroT ));result_infer = session.Run(input_ontainer);floatArray = new float[model_height * model_width * ch];}private DenseTensor<float> getRandomTensor(){int[] shape = new int[] { 1, 3, model_height, model_width };float[] values = new float[1 * 3 * model_height * model_width]; // 根据需要填充数据Array.Clear(values, 0, values.Length);  DenseTensor<float> tensor = new DenseTensor<float>(values, shape);return tensor;}private Mat letterBox(Mat img, ref Tuple<float, float> ratio, ref float dw, ref float dh,Tuple<int, int> newShape = null, bool auto = false, bool scaleFill = false,  bool scaleup = true, bool center = true, int stride = 32){if (newShape == null){newShape = new Tuple<int, int>(640, 640); // Default shape (640, 640)}Size shape = img.Size();  // current shape [height, width]// Scale ratio (new / old)float r = Math.Min(newShape.Item1 / (float)shape.Height, newShape.Item2 / (float)shape.Width);if (!scaleup)  // only scale down, do not scale up (for better val mAP){r = Math.Min(r, 1.0f);}// Compute paddingratio = new Tuple<float, float>(r, r);   // width, height ratiosSize newUnpad = new Size((int)Math.Round(shape.Width * r), (int)Math.Round(shape.Height * r));dw = newShape.Item2 - newUnpad.Width;dh = newShape.Item1 - newUnpad.Height;  // wh paddingif (auto)  // minimum rectangle{dw = dw % stride;dh = dh % stride;}else if (scaleFill)  // stretch{dw = 0.0f;dh = 0.0f;newUnpad = new Size(newShape.Item2, newShape.Item1);ratio = new Tuple<float, float>(newShape.Item2 / (float)shape.Width, newShape.Item1 / (float)shape.Height);  // width, height ratios}if (center){dw /= 2;  // divide padding into 2 sidesdh /= 2;}if (!shape.Equals(newUnpad))  // resize{img = img.Resize(newUnpad, interpolation: InterpolationFlags.Linear);}int top = (int)Math.Round(dh - 0.1f);int bottom = (int)Math.Round(dh + 0.1f);int left = (int)Math.Round(dw - 0.1f);int right = (int)Math.Round(dw + 0.1f);// Add border (padding)Scalar borderColor = new Scalar(114, 114, 114); // Color for the padding (similar to [114, 114, 114])img = img.CopyMakeBorder(top, bottom, left, right, BorderTypes.Constant, borderColor);return img;}private List<float[]> filterResult(float[] pred_array, Tuple<float, float> ratio , float x_offset, float y_offset){List<float[]>  pred_l = new List<float[]>();int inter = 6;for (int i = 0; i < pred_array.Length; i += inter){float conf_v = pred_array[i + 4];if (conf_v > conf_thr){   float xmin = (pred_array[i] - x_offset) / ratio.Item1;float ymin = (pred_array[i + 1] - y_offset) / ratio.Item2;float xmax = (pred_array[i + 2] - x_offset) / ratio.Item1;float ymax = (pred_array[i + 3] - y_offset) / ratio.Item2;pred_l.Add( new float[] { xmin, ymin, xmax, ymax, conf_v, pred_array[i + 5] }  );}}return pred_l;}public void boxLabel(Mat im, float[] pred_arr, Scalar color = default(Scalar), Scalar txtColor = default(Scalar), string label = ""   ){int lw = 1;if (color == default(Scalar))color = new Scalar(0, 255, 255); // Default color (yellow)if (txtColor == default(Scalar))txtColor = new Scalar(0, 0, 0); // Default text color (black)// Convert float box coordinates to integerPoint p1 = new Point((int)pred_arr[0], (int)pred_arr[1] );Point p2 = new Point((int)pred_arr[2], (int)pred_arr[3] );// Draw the rectangleCv2.Rectangle(im, p1, p2, color, lw, LineTypes.AntiAlias);if (!string.IsNullOrEmpty(label)){// Font thickness and size calculationint tf = Math.Max(lw - 1, 1); // Font thicknessSize textSize = Cv2.GetTextSize(label, HersheyFonts.HersheySimplex, lw / 3.0, tf, out _);int textWidth = textSize.Width;int textHeight = textSize.Height;// Check if the label can fit outside the rectanglebool outside = p1.Y - textHeight -3 >= 0;Point labelPos;Rect labelRect;if (outside){// Label fits outside the boxlabelPos = new Point(p1.X, p1.Y - textHeight-3 );labelRect = new Rect(p1.X, labelPos.Y, textWidth, textHeight + 3);}else{// Label fits inside the boxlabelPos = new Point(p1.X, p1.Y + textHeight+3 );labelRect = new Rect(p1.X, labelPos.Y- textHeight, textWidth, textHeight+3  );}// Draw the background rectangle for the labelCv2.Rectangle(im, labelRect, color, -1, LineTypes.AntiAlias);// Draw the label text\if (outside){Cv2.PutText(im, label, new Point(p1.X, labelPos.Y + textHeight + 1), HersheyFonts.HersheySimplex, lw / 3.0, txtColor, tf, LineTypes.AntiAlias);}else{Cv2.PutText(im, label, new Point(p1.X, labelPos.Y - 1), HersheyFonts.HersheySimplex, lw / 3.0, txtColor, tf, LineTypes.AntiAlias);}}}private void  visResult(Mat img , List<float[]> pred_list ,bool show_draw ){if (show_draw){for (int i=0; i< pred_list.Count ;i++){float[] pred_target = pred_list[i];float conf  = pred_target[4];int cls_id = (int)pred_target[5];string label_str = string.Format("{0}-{1:F2}", cls_id, conf);boxLabel(img , pred_target, new Scalar(),new Scalar()  ,label_str);}Cv2.ImShow("img", img);Cv2.WaitKey();Cv2.DestroyAllWindows();}}public   void  inferImg(string image_path , bool show_draw=false){DateTime t0 = DateTime.Now;Mat src = Cv2.ImRead(image_path);   // Mat mat_image = new Mat();Tuple<float, float> ratio = new Tuple<float, float>(0.0f,0.0f) ;float dw=0.0f;float dh=0.0f;mat_image = letterBox(src,ref ratio , ref dw , ref dh);Tensor<float> tensor = matToTensor(mat_image);  //new DenseTensor<float>(input_image, new[] { 1, 3, 896, 896 });List<NamedOnnxValue> input_ontainer = new List<NamedOnnxValue>(); ;//将 input_tensor 放入一个输入参数的容器,并指定名称input_ontainer.Add(NamedOnnxValue.CreateFromTensor("images", (DenseTensor<float>)tensor ));// tensor));result_infer = session.Run(input_ontainer);// 将输出结果转为DisposableNamedOnnxValue数组DisposableNamedOnnxValue[] results_onnxvalue = result_infer.ToArray();Tensor<float> result_tensors = results_onnxvalue[0].AsTensor<float>();float[] det_result_array = result_tensors.ToArray();List<float[]> pred_list = filterResult(det_result_array , ratio, dw , dh);DateTime t1 = DateTime.Now;MessageBox.Show(  "推理耗时:" + (t1 - t0).TotalMilliseconds + "ms"); //200ms左右visResult(  src , pred_list , show_draw);}private  Tensor<float> matToTensor(Mat mat_image){for (int y = 0; y < model_height; y++){for (int x = 0; x < model_width; x++){// 获取当前像素的 BGR 值Vec3b pixel = mat_image.At<Vec3b>(y, x);// 获取当前像素的 BGR 值byte b = pixel.Item0; // 蓝色通道byte g = pixel.Item1; // 绿色通道byte r = pixel.Item2; // 红色通道// 计算在 result 数组中的索引int index = y * model_width + x;// 按照要求的顺序排列floatArray[index] = r/ 255.0f;                 // R通道floatArray[index + model_height * model_width] = g / 255.0f;    // G通道floatArray[index + 2 * model_height * model_width] = b / 255.0f; // B通道}}Tensor<float> tensor = new DenseTensor<float>(floatArray, new[] { 1, ch, model_height, model_width });return tensor;}}}

三、效果

        GPU的 onnx runtime启动时间比较慢,但是之后的每次推理就很快了。


http://www.ppmy.cn/ops/134556.html

相关文章

大数据治理:从概念到实践的旅程

大数据治理&#xff1a;从概念到实践的旅程 在这个数字化飞速发展的时代&#xff0c;数据如同石油一样成为了推动社会进步的重要资源。大数据治理&#xff0c;作为管理这一宝贵资源的关键实践&#xff0c;其重要性日益凸显。它不仅关乎数据的准确性、一致性和可靠性&#xff0…

Ollama—87.4k star 的开源大模型服务框架!!

这一年来&#xff0c;AI 发展的越来越快&#xff0c;大模型使用的门槛也越来越低&#xff0c;每个人都可以在自己的本地运行大模型。今天再给大家介绍一个最厉害的开源大模型服务框架——ollama。 项目介绍 Ollama 是一个开源的大语言模型&#xff08;LLM&#xff09;服务工具…

【H3C华三 】VRRP与BFD、Track联动配置案例

原创 厦门微思网络 组网需求 如图1所示&#xff0c;区域A和区域B用户所在网络的出口处部署了两台汇聚层设备&#xff08;Device A和Device B&#xff09;。 现要求使用VRRP与BFD、Track联动功能&#xff0c;实现以下需求&#xff1a; • 在Device A和Device B上分别配置两个…

【机器学习】特征工程、降维与超参数调优:提升机器学习模型表现的三大核心技术

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

用指针遍历数组

#include<stdio.h> int main() {//定义一个二维数组int arr[3][4] {{1,2,3,4},{2,3,4,5},{3,4,5,6},};//获取二维数组的指针int (*p)[4] arr;//二维数组里存的是一维数组int[4]for (int i 0; i < 3; i){//遍历一维数组for (int j 0; j <4; j){printf("%d &…

elementui el-table中给表头 el-table-column 加一个鼠标移入提示说明

前言 在使用el-table 表格中有些表格的表头需要加入一些提示&#xff0c;鼠标移入则出现提示&#xff0c;非常实用&#xff0c;我是通过el-table中的el-tooltip实现的&#xff0c;以下的效果预览 代码实现 <el-table ref"multipleTable" :data"data"…

SRIO RapidIO 笔记

RapidIO 基础与底层包类型 1.RapidIO 是基于数据包交换的互联体系结构 类似 Mac 层使用以太网的计算机网络&#xff08;IEEE802.3&#xff09;&#xff1f; 首先 RapidIO 是一个互联协议&#xff08;类似计算机网络 IEEE802.3&#xff09;&#xff0c;包含硬件与软件的定义&…

Eureka、Zookeeper 与 Nacos:服务注册与发现功能大比拼

一、引言 在当今分布式系统和微服务架构蓬勃发展的时代&#xff0c;服务注册与发现机制已然成为了构建可靠、灵活且易于扩展的系统的关键要素。它就像是分布式系统中的 “通讯录”&#xff0c;让各个微服务能够轻松找到彼此&#xff0c;协同完成复杂的业务流程。Eureka、Zookee…