欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > C# yolo10使用onnx推理

C# yolo10使用onnx推理

2025/9/17 9:35:28 来源:https://blog.csdn.net/AP1005834/article/details/143386833  浏览:    关键词:C# yolo10使用onnx推理

一、前言

    本篇总结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启动时间比较慢,但是之后的每次推理就很快了。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词