一个 WinForms YOLOv8 目标检测小工具

  1. 点按钮 加载模型

  2. 点按钮 选图 → 检测 → 画框 → 把结果直接显示在主窗体的 PictureBox 里

  3. 类别只有"1"、"2"两个;4. 全程用 System.Drawing + SixLabors.ImageSharp 混合绘图,已解决所有命名冲突。

【功能拆解】

模块 作用
字段 yolov8(可空引用) + mylabel = {"1","2"}
button2_Click 手动加载模型;路径取 best1.onnx(与 exe 同目录)。
button1_Click 打开图片:弹出 OpenFileDialog,把文件路径交给 showimg
ToImageSharp System.Drawing.ImageImageSharp.Image<Rgb24>
showimg 核心流程
资源清理 Dispose 旧图,避免句柄泄漏。

安装的NuGet 包:

完整代码:

cs 复制代码
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
using Yolov8Net;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace NetOnnx08
{
    public partial class Form1 : Form
    {
        public IPredictor yolov8;
        string[] mylabel = { "1", "2" };//模型分类标签

        public Form1()
        {
            InitializeComponent();
            
        }

        //public void Form1_Load(object sender, EventArgs e)
        //{


        //    //加载模型
        //    try
        //    {
        //        yolov8 = YoloV8Predictor.Create("best1.onnx", mylabel);
        //        if (yolov8 != null)
        //        {
        //            toolStripStatusLabel1.Text = ("模型加载成功\r\n");
        //        }
        //    }
        //    catch (System.Exception ex)
        //    {
        //        MessageBox.Show(ex.Message);
        //    }
        //}

        public void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            //dialog.Multiselect = true;//该值确定是否可以选择多个文件
            dialog.Title = "请选择文件";
            dialog.Filter = "图像文件(*.jpg;*.jpeg)|*.jpg;*.jpeg";
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                string file = dialog.FileName;
                toolStripStatusLabel1.Text = file;
                toolStripStatusLabel2.Text = "检测成功!";
                showimg(dialog);
            }
        }

        public static SixLabors.ImageSharp.Image<SixLabors.ImageSharp.PixelFormats.Rgb24>
            ToImageSharp(System.Drawing.Image source)
        {
            if (source == null) throw new ArgumentNullException(nameof(source));

            using var ms = new MemoryStream();
            source.Save(ms, ImageFormat.Png);
            ms.Position = 0;

            return SixLabors.ImageSharp.Image.Load<SixLabors.ImageSharp.PixelFormats.Rgb24>(ms);
        }


        public void showimg(OpenFileDialog dialog)
        {
            string imgpath = dialog.FileName;

            Image image = System.Drawing.Image.FromFile(imgpath);
            Form form = new Form();
            form.Text = dialog.SafeFileName + " " + image.Width + "x" + image.Height;

            PictureBox pictureBox = new PictureBox();
            pictureBox.Parent = form;
            //格式转换
            SixLabors.ImageSharp.Image img = ToImageSharp(image);
            //图像预测
            var predictions = yolov8.Predict(img);
            // Draw your boxes
            //using var graphics = Graphics.FromImage(image);
            foreach (var pred in predictions)
            {
                var originalImageHeight = image.Height;
                var originalImageWidth = image.Width;

                var x = Math.Max(pred.Rectangle.X, 0);
                var y = Math.Max(pred.Rectangle.Y, 0);
                var width = Math.Min(originalImageWidth - x, pred.Rectangle.Width);
                var height = Math.Min(originalImageHeight - y, pred.Rectangle.Height);

                ////////////////////////////////////////////////////////////////////////////////////////////
                // *** Note that the output is already scaled to the original image height and width. ***
                ////////////////////////////////////////////////////////////////////////////////////////////

                // Bounding Box Text
                string text = $"{pred.Label.Name} [{pred.Score}]";

                using (Graphics graphics = Graphics.FromImage(image))
                {
                    graphics.CompositingQuality = CompositingQuality.HighQuality;
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

                    // Define Text Options
                    Font drawFont = new Font("consolas", 11, FontStyle.Regular);
                    SizeF size = graphics.MeasureString(text, drawFont);
                    SolidBrush fontBrush = new SolidBrush(Color.Black);
                    Point atPoint = new Point((int)x, (int)y - (int)size.Height - 1);

                    // Define BoundingBox options
                    Pen pen = new Pen(Color.Yellow, 2.0f);
                    SolidBrush colorBrush = new SolidBrush(Color.Yellow);

                    // Draw text on image 
                    graphics.FillRectangle(colorBrush, (int)x, (int)(y - size.Height - 1), (int)size.Width, (int)size.Height);
                    graphics.DrawString(text, drawFont, fontBrush, atPoint);

                    // Draw bounding box on image
                    graphics.DrawRectangle(pen, x, y, width, height);
                }
            }
            ////用pictureBox弹窗来显示
            //pictureBox.Width = image.Width;
            //pictureBox.Height = image.Height;
            //pictureBox.Image = (Bitmap)image.Clone();

            //form.AutoSize = true;
            //form.Show();
            //image.Dispose();
            // 1. 清空旧图
            if (pictureBox1.Image != null)
            {
                pictureBox1.Image.Dispose();
                pictureBox1.Image = null;
            }

            // 3. 直接赋给 PictureBox
            //pictureBox1.Image = new System.Drawing.Bitmap(ms);
            pictureBox1.Image = (Bitmap)image.Clone();
            // 4. 自适应/原图大小任选其一
            pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;   // 自适应
                                                              // pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize; // 原图大小

        }

        public void button2_Click(object sender, EventArgs e)
        {
            try
            {
                string modelPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "best1.onnx");
                if (!File.Exists(modelPath))
                {
                    MessageBox.Show($"模型文件不存在:{modelPath}");
                    return;
                }

                // 25 个类别示例
                string[] labels = { "1", "2"};

                yolov8 = YoloV8Predictor.Create(modelPath, labels);
                if (yolov8 != null)
                {
                    toolStripStatusLabel2.Text = "模型加载成功";
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("模型加载失败: " + ex.Message);
            }
        }
    }
}

【优点】

  • 不弹额外窗口,所有结果在主界面实时可见。

  • 已显式区分 System.Drawing.ImageSixLabors.ImageSharp.Image,彻底消除"不明确的引用"。

  • 支持任意 jpg/jpeg/bmp 图片。


【可改进的小提示】

  1. pictureBox1.Image 的创建/释放封装成方法,减少重复代码。

  2. 如果类别多,可把 string[] labels 改成读取外部 txt,避免硬编码。

  3. 若图片非常大,可把 pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize 并放在 Panel.AutoScroll 里实现滚动查看。