掘金 人工智能 6小时前
C# OnnxRuntime Yolov8 纸箱检测
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了如何使用C#语言结合Microsoft.ML.OnnxRuntime库加载和运行YOLOv8目标检测模型。代码详细展示了图像的预处理,包括缩放、颜色空间转换,以及将图像数据转换为ONNX模型所需的Tensor格式。通过InferenceSession执行模型推理,并对输出结果进行后处理,包括置信度过滤、边界框计算和非极大值抑制(NMS),最终将识别到的目标及其置信度绘制在原图上,并显示推理耗时。该实现为在C#应用中集成先进的目标检测能力提供了完整的技术方案。

🎯 **模型加载与初始化**:代码通过`InferenceSession`类加载了Ultralytics YOLOv8模型(`.onnx`格式),并配置了CPU执行和日志级别。同时,初始化了输入Tensor和输入容器,为模型推理做好准备。

🖼️ **图像预处理**:在进行推理前,对输入的图像进行了必要的预处理。这包括将图像缩放到模型要求的尺寸(640x640),转换颜色空间(BGR2RGB),并将图像像素值归一化到0-1的浮点数范围,最后将其转换为适合ONNX Runtime的Tensor格式。

🚀 **模型推理与结果获取**:使用`onnx_session.Run()`方法执行模型推理,获取原始的输出Tensor。该Tensor包含了检测到的物体信息,如边界框坐标、置信度等。

📦 **结果后处理与可视化**:对模型输出的Tensor进行解析,提取出边界框、类别ID和置信度。应用了置信度阈值过滤和非极大值抑制(NMS)算法,以消除冗余的检测框。最后,将处理后的检测结果(边界框和类别标签)绘制在原始图像上,并显示在界面中。

💾 **结果保存功能**:提供了将带有检测结果的图像保存到多种常见图像格式(如JPG, PNG, BMP等)的功能,方便用户导出分析结果。

效果

模型信息

Model Properties-------------------------author:Ultralyticsversion:8.1.29task:detectlicense:AGPL-3.0 License (https://ultralytics.com/license)docs:https://docs.ultralytics.comstride:32batch:1imgsz:[640, 640]names:{0: 'carton'}---------------------------------------------------------------Inputs-------------------------name:imagestensor:Float[1, 3, 640, 640]---------------------------------------------------------------Outputs-------------------------name:output0tensor:Float[1, 5, 8400]---------------------------------------------------------------

项目

数据集

代码

using Microsoft.ML.OnnxRuntime;using Microsoft.ML.OnnxRuntime.Tensors;using OpenCvSharp;using OpenCvSharp.Dnn;using System;using System.Collections.Generic;using System.Drawing;using System.Drawing.Imaging;using System.Linq;using System.Text;using System.Windows.Forms;namespace Onnx_Yolov8_Demo{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        string fileFilter"*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";        string image_path"";        string startupPath;        string classer_path;        DateTime dt1 = DateTime.Now;        DateTime dt2 = DateTime.Now;        string model_path;        Mat image;        DetectionResult result_pro;        Mat result_image;        Result result;        SessionOptions options;        InferenceSession onnx_session;        Tensor<float> input_tensor;        List<NamedOnnxValue> input_container;        IDisposableReadOnlyCollection<DisposableNamedOnnxValue> result_infer;        DisposableNamedOnnxValue[] results_onnxvalue;        Tensor<float> result_tensors;        private void button1_Click(object sender, EventArgs e)        {            OpenFileDialog ofd = new OpenFileDialog();            ofd.Filter = fileFilter;            if (ofd.ShowDialog() != DialogResult.OK) return;            pictureBox1.Image = null;            image_path = ofd.FileName;            pictureBox1.Image = new Bitmap(image_path);            textBox1.Text"";            image = new Mat(image_path);            pictureBox2.Image = null;        }        private void button2_Click(object sender, EventArgs e)        {            if (image_path == "")            {                return;            }            float score_threshold = 0.5f;            float nms_threshold = 0.5f;            button2.Enabledfalse;            pictureBox2.Image = null;            textBox1.Text"";            Application.DoEvents();            //图片缩放            image = new Mat(image_path);            int max_image_length = image.Cols > image.Rows ? image.Cols : image.Rows;            Mat max_image = Mat.Zeros(new OpenCvSharp.Size(max_image_length, max_image_length), MatType.CV_8UC3);            Rect roi = new Rect(0, 0, image.Cols, image.Rows);            image.CopyTo(new Mat(max_image, roi));            float[] result_array = new float[8400 * 84];            float[] factors = new float[2];            factors[0] = factors[1] = (float)(max_image_length / 640.0);            // 将图片转为RGB通道            Mat image_rgb = new Mat();            Cv2.CvtColor(max_image, image_rgb, ColorConversionCodes.BGR2RGB);            Mat resize_image = new Mat();            Cv2.Resize(image_rgb, resize_image, new OpenCvSharp.Size(640, 640));            // 输入Tensor            for (int y = 0; y < resize_image.Height; y++)            {                for (int x = 0; x < resize_image.Width; x++)                {                    input_tensor[0, 0, y, x] = resize_image.At<Vec3b>(y, x)[0] / 255f;                    input_tensor[0, 1, y, x] = resize_image.At<Vec3b>(y, x)[1] / 255f;                    input_tensor[0, 2, y, x] = resize_image.At<Vec3b>(y, x)[2] / 255f;                }            }            //将 input_tensor 放入一个输入参数的容器,并指定名称            input_container.Add(NamedOnnxValue.CreateFromTensor("images", input_tensor));            dt1 = DateTime.Now;            //运行 Inference 并获取结果            result_infer = onnx_session.Run(input_container);            dt2 = DateTime.Now;            // 将输出结果转为DisposableNamedOnnxValue数组            results_onnxvalue = result_infer.ToArray();            // 读取第一个节点输出并转为Tensor数据            result_tensors = results_onnxvalue[0].AsTensor<float>();            result_array = result_tensors.ToArray();            resize_image.Dispose();            image_rgb.Dispose();            result_pro = new DetectionResult(classer_path, factors, score_threshold, nms_threshold);            result = result_pro.process_result(result_array);            result_image =  result_pro.draw_result(result, image.Clone());            StringBuilder sb = new StringBuilder();            if (!result_image.Empty())            {                pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());                //textBox1.Text"推理耗时:" + (dt2 - dt1).TotalMilliseconds + "ms";                sb.AppendLine("推理耗时:" + (dt2 - dt1).TotalMilliseconds + "ms");                sb.AppendLine("--------------------------------------------------");                sb.AppendLine("{lable}{scores}({X},{Y},{Width},{Height})");                sb.AppendLine("--------------------------------------------------");                // 识别结果                for (int i = 0; i < result.length; i++)                {                    //Scalar color= Scalar.RandomColor();                    Scalar color = new Scalar(0, 0, 255);                    string lable = string.Format("{0}\t{1}\t({2},{3},{4},{5})"                        , result.classes[i]                        , result.scores[i].ToString("P2")                        , result.rects[i].X                        , result.rects[i].Y                        , result.rects[i].Width                        , result.rects[i].Height                        )                    sb.AppendLine(lable);                    //Cv2.Rectangle(image, result.rects[i], color, 2, LineTypes.Link8);                    //Cv2.Rectangle(image                    //    , new Point(result.rects[i].TopLeft.X - 1, result.rects[i].TopLeft.Y - 20)                    //    , new Point(result.rects[i].TopLeft.X - 1 + lable.Length * 12, result.rects[i].TopLeft.Y)                    //    , color                    //    , -1);                    //Cv2.PutText(image, lable, new Point(result.rects[i].X, result.rects[i].Y - 4), HersheyFonts.HersheySimplex, 0.6, new Scalar(0, 0, 0), 1);                                                }                textBox1.Text = sb.ToString();            }            else            {                textBox1.Text"无信息";            }            button2.Enabledtrue;        }        private void Form1_Load(object sender, EventArgs e)        {            startupPath = System.Windows.Forms.Application.StartupPath;            model_path"model/carton.onnx";            classer_path"model/lable.txt";            // 创建输出会话,用于输出模型读取信息            options = new SessionOptions();            options.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;            options.AppendExecutionProvider_CPU(0);// 设置为CPU上运行            // 创建推理模型类,读取本地模型文件            onnx_session = new InferenceSession(model_path, options);//model_path 为onnx模型文件的路径            // 输入Tensor            input_tensor = new DenseTensor<float>(new[] { 1, 3, 640, 640 });            // 创建输入容器            input_container = new List<NamedOnnxValue>();            image_path"test_img/4.jpg";            pictureBox1.Image = new Bitmap(image_path);            image = new Mat(image_path);        }        private void pictureBox1_DoubleClick(object sender, EventArgs e)        {            Common.ShowNormalImg(pictureBox1.Image);        }        private void pictureBox2_DoubleClick(object sender, EventArgs e)        {            Common.ShowNormalImg(pictureBox2.Image);        }        SaveFileDialog sdf = new SaveFileDialog();        private void button3_Click(object sender, EventArgs e)        {            if (pictureBox2.Image == null)            {                return;            }            Bitmap output = new Bitmap(pictureBox2.Image);            sdf.Title"保存";            sdf.Filter"Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp|Images (*.emf)|*.emf|Images (*.exif)|*.exif|Images (*.gif)|*.gif|Images (*.ico)|*.ico|Images (*.tiff)|*.tiff|Images (*.wmf)|*.wmf";            if (sdf.ShowDialog() == DialogResult.OK)            {                switch (sdf.FilterIndex)                {                    case 1:                        {                            output.Save(sdf.FileName, ImageFormat.Jpeg);                            break;                        }                    case 2:                        {                            output.Save(sdf.FileName, ImageFormat.Png);                            break;                        }                    case 3:                        {                            output.Save(sdf.FileName, ImageFormat.Bmp);                            break;                        }                    case 4:                        {                            output.Save(sdf.FileName, ImageFormat.Emf);                            break;                        }                    case 5:                        {                            output.Save(sdf.FileName, ImageFormat.Exif);                            break;                        }                    case 6:                        {                            output.Save(sdf.FileName, ImageFormat.Gif);                            break;                        }                    case 7:                        {                            output.Save(sdf.FileName, ImageFormat.Icon);                            break;                        }                    case 8:                        {                            output.Save(sdf.FileName, ImageFormat.Tiff);                            break;                        }                    case 9:                        {                            output.Save(sdf.FileName, ImageFormat.Wmf);                            break;                        }                }                MessageBox.Show("保存成功,位置:" + sdf.FileName);            }        }    }    public class DetectionResult : ResultBase    {        /// <summary>        /// 结果处理类构造        /// </summary>        /// <param name="path">识别类别文件地址</param>        /// <param name="scales">缩放比例</param>        /// <param name="score_threshold">分数阈值</param>        /// <param name="nms_threshold">非极大值抑制阈值</param>        public DetectionResult(string path, float[] scales, float score_threshold = 0.25f, float nms_threshold = 0.5f)        {            read_class_names(path);            this.scales = scales;            this.score_threshold = score_threshold;            this.nms_threshold = nms_threshold;        }        /// <summary>        /// 结果处理        /// </summary>        /// <param name="result">模型预测输出</param>        /// <returns>模型识别结果</returns>        public Result process_result(float[] result)        {            Mat result_data = new Mat(4 + class_num, 8400, MatType.CV_32F, result);            result_data = result_data.T();            // 存放结果list            List<Rect> position_boxes = new List<Rect>();            List<int> class_ids = new List<int>();            List<float> confidences = new List<float>();            // 预处理输出结果            for (int i = 0; i < result_data.Rows; i++)            {                Mat classes_scores = result_data.Row(i).ColRange(4, 4 + class_num);                OpenCvSharp.Point max_classId_point, min_classId_point;                double max_score, min_score;                // 获取一组数据中最大值及其位置                Cv2.MinMaxLoc(classes_scores, out min_score, out max_score,                    out min_classId_point, out max_classId_point);                // 置信度 0~1之间                // 获取识别框信息                if (max_score > this.score_threshold)                {                    float cx = result_data.At<float>(i, 0);                    float cy = result_data.At<float>(i, 1);                    float ow = result_data.At<float>(i, 2);                    float oh = result_data.At<float>(i, 3);                    int x = (int)((cx - 0.5 * ow) * this.scales[0]);                    int y = (int)((cy - 0.5 * oh) * this.scales[1]);                    int width = (int)(ow * this.scales[0]);                    int height = (int)(oh * this.scales[1]);                    Rect box = new Rect();                    box.X = x;                    box.Y = y;                    box.Width = width;                    box.Height = height;                    position_boxes.Add(box);                    class_ids.Add(max_classId_point.X);                    confidences.Add((float)max_score);                }            }            // NMS非极大值抑制            int[] indexes = new int[position_boxes.Count];            CvDnn.NMSBoxes(position_boxes, confidences, this.score_threshold, this.nms_threshold, out indexes);            Result re_result = new Result();            // 将识别结果绘制到图片上            for (int i = 0; i < indexes.Length; i++)            {                int index = indexes[i];                int idx = class_ids[index];                re_result.add(confidences[index], position_boxes[index], this.class_names[class_ids[index]]);            }            return re_result;        }        /// <summary>        /// 结果绘制        /// </summary>        /// <param name="result">识别结果</param>        /// <param name="image">绘制图片</param>        /// <returns></returns>        public Mat draw_result(Result result, Mat image)        {            // 将识别结果绘制到图片上            for (int i = 0; i < result.length; i++)            {                //Scalar color= Scalar.RandomColor();                Scalar color = new Scalar(0, 0, 255);                string lable = result.classes[i] + "-" + result.scores[i].ToString("0.00");                Cv2.Rectangle(image, result.rects[i], color, 2, LineTypes.Link8);                               Cv2.Rectangle(image                    , new OpenCvSharp.Point(result.rects[i].TopLeft.X - 1, result.rects[i].TopLeft.Y - 20)                    , new OpenCvSharp.Point(result.rects[i].TopLeft.X - 1 + lable.Length * 12, result.rects[i].TopLeft.Y)                    , color                    , -1);                Cv2.PutText(image, lable, new OpenCvSharp.Point(result.rects[i].X, result.rects[i].Y - 4), HersheyFonts.HersheySimplex, 0.6, new Scalar(0, 0, 0), 1);            }            return image;        }    }}

下载

download.csdn.net/download/lw…

download.csdn.net/download/lw…

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

YOLOv8 ONNX Runtime 目标检测 C# 计算机视觉
相关文章