[C#][winform]基于yolov11的鱼病害检测系统C#源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】

基于YOLOv11的鱼病害检测系统是一种先进的解决方案,利用深度学习算法实现高效准确的目标检测。该系统采用YOLOv11目标检测算法训练数据集,专门用于检测与识别鱼类病害。

YOLOv11作为Ultralytics公司开发的最新一代算法模型,具有速度更快、准确率更高的优势。其全新的网络结构,包括优化的骨干网络、Anchor-Free检测头和新的损失函数,使得模型在各种硬件平台上都能表现出色。

在鱼病害检测系统中,用户可以上传图片、视频或通过摄像头实时输入数据进行检测。系统会对输入数据进行处理,利用已训练的YOLOv11模型对鱼类病害进行识别,并将检测结果可视化展示。用户可以根据需要调整检测置信分和IOU(Intersection over Union)阈值,以过滤掉低于某个阈值的检测结果。

此外,该系统还支持检测结果的导出功能,用户可以将检测后的图片或视频保存到本地。系统还会记录每次检测的前向推理时间,为评估模型性能和优化系统提供重要参考。

总之,基于YOLOv11的鱼病害检测系统为鱼类病害的快速识别和诊断提供了一种高效、准确的解决方案。该系统具有用户友好的界面和强大的功能,适用于水产养殖、科研机构和渔业监管部门等多种应用场景。

【效果展示】

【测试环境】

windows10 x64系统

VS2019

netframework4.7.2

opencvsharp4.9.0

onnxruntime1.22.0

【模型可以检测出类别】

kuiyang (溃疡)

lansai (烂腮)

qisunshang (鳍损伤)

yanbing (眼病)

【训练数据集】

重要说明:数据集有2/3是增强图片,请认真观看图片预览,确认符合要求在下载

数据集格式:Pascal VOC格式+YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)

图片数量(jpg文件个数):8474

标注数量(xml文件个数):8474

标注数量(txt文件个数):8474

标注类别数:4

标注类别名称:["kuiyang","lansai","qisunshang","yanbing"]

每个类别标注的框数:

kuiyang (溃疡)框数 = 7085

lansai (烂腮)框数 = 2016

qisunshang (鳍损伤)框数 = 2380

yanbing (眼病) 框数 = 2147

总框数:13628

使用标注工具:labelImg

标注规则:对类别进行画矩形框

特别声明:本数据集不对训练的模型或者权重文件精度作任何保证,数据集只提供准确且合理标注

图片预览:

标注例子:

【训练信息】

|-----------------|-------|
| 参数 | 值 |
| 训练集图片数 | 7626 |
| 验证集图片数 | 848 |
| 训练map | 99.5% |
| 训练精度(Precision) | 98.8% |
| 训练召回率(Recall) | 98.2% |

【验证集精度统计】

|------------|--------|-----------|-------|-------|-------|----------|
| Class | Images | Instances | P | R | mAP50 | mAP50-95 |
| all | 848 | 1386 | 0.988 | 0.982 | 0.995 | 0.895 |
| kuiyang | 446 | 722 | 0.985 | 0.974 | 0.994 | 0.889 |
| lansai | 201 | 202 | 0.99 | 0.981 | 0.995 | 0.895 |
| qisunshang | 229 | 268 | 0.997 | 0.985 | 0.995 | 0.923 |
| yanbing | 181 | 194 | 0.98 | 0.987 | 0.994 | 0.873 |

【界面设计】

复制代码
using DeploySharp.Data;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FIRC
{
    public partial class Form1 : Form
    {

        public bool videoStart = false;//视频停止标志
        string weightsPath = Application.StartupPath + "\\weights";//模型目录
        YoloDetector detetor = new YoloDetector();//推理引擎
        public Form1()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;//线程更新控件不报错
        }
        private void LoadWeightsFromDir()
        {
            var di = new DirectoryInfo(weightsPath);
            foreach(var fi in di.GetFiles("*.onnx"))
            {
                comboBox1.Items.Add(fi.Name);
            }
            if(comboBox1.Items.Count>0)
            {
                comboBox1.SelectedIndex = 0;
            }
            else
            {
                tssl_show.Text = "未找到模型,请关闭程序,放入模型到weights文件夹!";
                tsb_pic.Enabled = false;
                tsb_video.Enabled = false;
                tsb_camera.Enabled = false;
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            LoadWeightsFromDir();//从目录加载模型
                               
        }
        public string GetResultString(DetResult[] result)
        {
            Dictionary<string, int> resultDict = new Dictionary<string, int>();
            for (int i = 0; i < result.Length; i++)
            {
                if(resultDict.ContainsKey( result[i].Category) )
                {
                    resultDict[result[i].Category]++;
                }
                else
                {
                    resultDict[result[i].Category] =1;
                }
            }

            var resultStr = "";
            foreach(var item in resultDict)
            {
                resultStr += string.Format("{0}:{1}\r\n",item.Key,item.Value);
            }
            return resultStr;
        }
        private void tsb_pic_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
            if (ofd.ShowDialog() != DialogResult.OK) return;
            tssl_show.Text = "正在检测中...";
            Task.Run(() => {
                var sw = new Stopwatch();
                sw.Start();
                Mat image = Cv2.ImRead(ofd.FileName);
                detetor.SetParams(Convert.ToSingle(numericUpDown1.Value), Convert.ToSingle(numericUpDown2.Value));
                var results=detetor.Inference(image);
                
                var resultImage = detetor.DrawImage(image, results);
    
                sw.Stop();
                pb_show.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImage);
                tb_res.Text = GetResultString(results);
                tssl_show.Text = "检测已完成!总计耗时"+sw.Elapsed.TotalSeconds+"秒";
            });
           


        }

        public void VideoProcess(string videoPath)
        {
            Task.Run(() => {

                detetor.SetParams(Convert.ToSingle(numericUpDown1.Value), Convert.ToSingle(numericUpDown2.Value));
                VideoCapture capture = new VideoCapture(videoPath);
                if (!capture.IsOpened())
                {
                    tssl_show.Text="视频打开失败!";
                    return;
                }
                Mat frame = new Mat();
                var sw = new Stopwatch();
                int fps = 0;
                while (videoStart)
                {

                    capture.Read(frame);
                    if (frame.Empty())
                    {
                        Console.WriteLine("data is empty!");
                        break;
                    }
                    sw.Start();
                    var results = detetor.Inference(frame);
                    var resultImg = detetor.DrawImage(frame,results);
                    sw.Stop();
                    fps = Convert.ToInt32(1 / sw.Elapsed.TotalSeconds);
                    sw.Reset();
                    Cv2.PutText(resultImg, "FPS=" + fps, new OpenCvSharp.Point(30, 30), HersheyFonts.HersheyComplex, 1.0, new Scalar(255, 0, 0), 3);
                    //显示结果
                    pb_show.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImg);
                    tb_res.Text = GetResultString(results);
                    Thread.Sleep(5);


                }

                capture.Release();

                pb_show.Image = null;
                tssl_show.Text = "视频已停止!";
                tsb_video.Text = "选择视频";

            });
        }
        public void CameraProcess(int cameraIndex=0)
        {
            Task.Run(() => {

                detetor.SetParams(Convert.ToSingle(numericUpDown1.Value), Convert.ToSingle(numericUpDown2.Value));
                VideoCapture capture = new VideoCapture(cameraIndex);
                if (!capture.IsOpened())
                {
                    tssl_show.Text = "摄像头打开失败!";
                    return;
                }
                Mat frame = new Mat();
                var sw = new Stopwatch();
                int fps = 0;
                while (videoStart)
                {

                    capture.Read(frame);
                    if (frame.Empty())
                    {
                        Console.WriteLine("data is empty!");
                        break;
                    }
                    sw.Start();
                    var results = detetor.Inference(frame);
                    var resultImg = detetor.DrawImage(frame, results);
                    sw.Stop();
                    fps = Convert.ToInt32(1 / sw.Elapsed.TotalSeconds);
                    sw.Reset();
                    Cv2.PutText(resultImg, "FPS=" + fps, new OpenCvSharp.Point(30, 30), HersheyFonts.HersheyComplex, 1.0, new Scalar(255, 0, 0), 3);
                    //显示结果
                    pb_show.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(resultImg);
                    tb_res.Text = GetResultString(results);
                    Thread.Sleep(5);


                }

                capture.Release();

                pb_show.Image = null;
                tssl_show.Text = "摄像头已停止!";
                tsb_camera.Text = "打开摄像头";

            });
        }
        private void tsb_video_Click(object sender, EventArgs e)
        {
            if(tsb_video.Text=="选择视频")
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Filter = "视频文件(*.*)|*.mp4;*.avi";
                if (ofd.ShowDialog() != DialogResult.OK) return;
                videoStart = true;
                VideoProcess(ofd.FileName);
                tsb_video.Text = "停止";
                tssl_show.Text = "视频正在检测中...";

            }
            else
            {
                videoStart = false;
               
            }
        }

        private void tsb_camera_Click(object sender, EventArgs e)
        {
            if (tsb_camera.Text == "打开摄像头")
            {
                videoStart = true;
                CameraProcess(0);
                tsb_camera.Text = "停止";
                tssl_show.Text = "摄像头正在检测中...";

            }
            else
            {
                videoStart = false;

            }
        }

        private void tsb_exit_Click(object sender, EventArgs e)
        {
            videoStart = false;
            this.Close();
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            numericUpDown1.Value = Convert.ToDecimal(trackBar1.Value / 100.0f);
        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            numericUpDown2.Value = Convert.ToDecimal(trackBar2.Value / 100.0f);
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            trackBar1.Value = (int)(Convert.ToSingle(numericUpDown1.Value) * 100);
        }

        private void numericUpDown2_ValueChanged(object sender, EventArgs e)
        {
            trackBar2.Value = (int)(Convert.ToSingle(numericUpDown2.Value) * 100);
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            tssl_show.Text="加载模型:"+comboBox1.Text;
            detetor.LoadWeights(weightsPath+"\\"+comboBox1.Text);
            tssl_show.Text = "模型加载已完成!";
        }
    }
}

【常用评估参数介绍】

在目标检测任务中,评估模型的性能是至关重要的。你提到的几个术语是评估模型性能的常用指标。下面是对这些术语的详细解释:

Class:

这通常指的是模型被设计用来检测的目标类别。例如,一个模型可能被训练来检测车辆、行人或动物等不同类别的对象。

Images:

表示验证集中的图片数量。验证集是用来评估模型性能的数据集,与训练集分开,以确保评估结果的公正性。

Instances:

在所有图片中目标对象的总数。这包括了所有类别对象的总和,例如,如果验证集包含100张图片,每张图片平均有5个目标对象,则Instances为500。

P(精确度Precision):

精确度是模型预测为正样本的实例中,真正为正样本的比例。计算公式为:Precision = TP / (TP + FP),其中TP表示真正例(True Positives),FP表示假正例(False Positives)。

R(召回率Recall):

召回率是所有真正的正样本中被模型正确预测为正样本的比例。计算公式为:Recall = TP / (TP + FN),其中FN表示假负例(False Negatives)。

mAP50:

表示在IoU(交并比)阈值为0.5时的平均精度(mean Average Precision)。IoU是衡量预测框和真实框重叠程度的指标。mAP是一个综合指标,考虑了精确度和召回率,用于评估模型在不同召回率水平上的性能。在IoU=0.5时,如果预测框与真实框的重叠程度达到或超过50%,则认为该预测是正确的。

mAP50-95:

表示在IoU从0.5到0.95(间隔0.05)的范围内,模型的平均精度。这是一个更严格的评估标准,要求预测框与真实框的重叠程度更高。在目标检测任务中,更高的IoU阈值意味着模型需要更准确地定位目标对象。mAP50-95的计算考虑了从宽松到严格的多个IoU阈值,因此能够更全面地评估模型的性能。

这些指标共同构成了评估目标检测模型性能的重要框架。通过比较不同模型在这些指标上的表现,可以判断哪个模型在实际应用中可能更有效。

【使用步骤】

使用步骤:

(1)首先根据官方框架ultralytics安装教程安装好yolov8环境,并根据官方export命令将自己pt模型转成onnx模型,然后去github仓库futureflsl/firc-csharp-projects找到源码

(2)使用vs2019打开sln项目,选择x64 release并且修改一些必要的参数,比如输入shape等,点击运行即可查看最后效果

特别注意如果运行报错了,请参考我的博文进行重新引用我源码的DLL:[C#]opencvsharp报错System.Memory,Version=4.0.1.2,Culture=neutral,PublicKeyToken=cc7b13fcd2ddd51"版本高于所引_未能加载文件或程序集"system.memory, version=4.0.1.2, culture-CSDN博客

【提供文件】

C#源码

yolo11n.onnx模型(提供pytorch模型)

训练的map,P,R曲线图(在weights\results.png)

测试图片(在test_img文件夹下面)

特别注意这里提供训练数据集

相关推荐
JicasdC123asd12 小时前
密集残差瓶颈网络改进YOLOv26特征复用与梯度传播双重优化
网络·yolo·目标跟踪
JicasdC123asd16 小时前
密集连接瓶颈模块改进YOLOv26特征复用与梯度流动双重优化
人工智能·yolo·目标跟踪
duyinbi751717 小时前
局部特征提取改进YOLOv26空间移位卷积与轻量化设计双重突破
人工智能·yolo·目标跟踪
张道宁18 小时前
基于Spring Boot与Docker的YOLOv8检测服务实战
spring boot·yolo·docker
duyinbi751721 小时前
大核瓶颈架构改进YOLOv26扩大感受野与多尺度特征提取双重突破
yolo·架构
孤狼warrior21 小时前
YOLO技术架构发展详解(从v1到v8)近万字底层实现逻辑解析
yolo
张张123y21 小时前
机器学习与深度学习:从基础概念到YOLOv8全解析
深度学习·yolo·机器学习
hans汉斯2 天前
基于区块链和语义增强的科研诚信智能管控平台
人工智能·算法·yolo·数据挖掘·区块链·汉斯出版社
Dev7z2 天前
斑点叉尾鮰鱼损伤检测数据集(YOLO格式)
yolo·斑点叉尾鮰鱼
duyinbi75172 天前
多尺度空洞卷积分支模块改进YOLOv26感受野扩展与特征提取能力双重突破
深度学习·yolo·目标跟踪