news 2026/6/10 2:00:32

[C#][winform]基于yolov8的水表读数检测与识别系统C#源码+onnx模型+评估指标曲线+精美GUI界面

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[C#][winform]基于yolov8的水表读数检测与识别系统C#源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】

智慧水务中的水表读数检测与识别系统,基于先进的YOLOv8算法,为城市供水管理的智能化升级提供了坚实的技术支撑。该系统通过集成YOLOv8的深度学习框架,实现了对水表数字及关键标识的实时、精准识别,覆盖["0","1","2","3","4","5","6","7","8","9","counter","liter"]等核心类别。

YOLOv8以其卓越的检测速度与高精度特性,能够快速解析水表图像中的数字信息,并精准定位计数器区域("counter")与单位标识("liter")。通过YOLOv8算法自动提取特征信息,完成数字识别与状态判断。此外,该系统支持多场景适配与动态追踪功能,可同时监测不同型号水表的读数变化,并兼容低光照、反光等复杂环境,如果增强数据集进行训练可以进一步提升了水务管理的精细化水平。

综上所述,基于YOLOv8的水表读数检测与识别系统,为智慧水务建设注入了创新动能,有效保障了供水计量的准确性,推动了城市水资源管理的数字化转型。

【效果展示】

注意:系统采用yolov8x大模型进行训练,所以单张图片预测1.1秒左右,如果采用yolov8n训练可以达到50ms左右。由于水表方向不确定为了能够正常读取完整水表数字需要正向放置,代码采用位置从左到右提取完整数字,最终显示在软件左上角区域。水表只读取整数部分,小数部分只检测没有识别,因为这个主要是依靠目标检测进行定位识别。

【训练数据集介绍】

注意数据集中有很多增强图片,主要是旋转增强图片

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

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

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

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

标注类别数:12

所在github仓库:firc-dataset

标注类别名称(注意yolo格式类别顺序不和这个对应,而以labels文件夹classes.txt为准):["0","1","2","3","4","5","6","7","8","9","counter","liter"]

每个类别标注的框数:

0 框数 = 3865

1 框数 = 1768

2 框数 = 1390

3 框数 = 1295

4 框数 = 1198

5 框数 = 1074

6 框数 = 1019

7 框数 = 827

8 框数 = 908

9 框数 = 864

counter 框数 = 3552

liter 框数 = 3551

总框数:21311

图片分辨率:640x640

使用标注工具:labelImg

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

重要说明:暂无

特别声明:本数据集不对训练的模型或者权重文件精度作任何保证

图片预览:

标注例子:

【测试环境】

windows10 x64系统
VS2019
netframework4.7.2
opencvsharp4.9.0
onnxruntime1.22.0

【训练信息】

参数
训练集图片数3072
验证集图片数480
训练map98.6%
训练精度(Precision)96.5%
训练召回率(Recall)96.6%

【界面设计代码】

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 = "模型加载已完成!"; } } }

【使用步骤】

使用步骤:
(1)首先根据官方框架ultralytics安装教程安装好yolov8环境,并根据官方export命令将自己pt模型转成onnx模型,然后去github.com/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#源码
yolov8x.onnx模型(不提供pytorch模型)
训练的map,P,R曲线图(在weights\results.png)
测试图片(在test_img文件夹下面)

训练数据集

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 0:47:51

为什么开发者都在用Anything-LLM做私有化文档分析?

为什么开发者都在用 Anything-LLM 做私有化文档分析&#xff1f; 在企业知识管理的战场上&#xff0c;一个看似简单却长期无解的问题正在被重新定义&#xff1a;那些散落在硬盘、邮件和共享文件夹里的 PDF、Word 和 PPT 文档&#xff0c;如何才能真正“活”起来&#xff1f;不是…

作者头像 李华
网站建设 2026/6/9 13:22:25

macOS菜单栏终极优化指南:Ice完整使用教程

macOS菜单栏终极优化指南&#xff1a;Ice完整使用教程 【免费下载链接】Ice Powerful menu bar manager for macOS 项目地址: https://gitcode.com/GitHub_Trending/ice/Ice 你的Mac屏幕顶部是不是挤满了各种应用图标&#xff1f;Wi-Fi、电池、时间、音量、日历...再加上…

作者头像 李华
网站建设 2026/6/9 21:15:13

Synology Photos人脸识别补丁:解锁无GPU设备的AI照片管理能力

Synology Photos人脸识别补丁&#xff1a;解锁无GPU设备的AI照片管理能力 【免费下载链接】Synology_Photos_Face_Patch Synology Photos Facial Recognition Patch 项目地址: https://gitcode.com/gh_mirrors/sy/Synology_Photos_Face_Patch 许多Synology设备用户发现&…

作者头像 李华
网站建设 2026/6/9 7:45:37

桌面待办工具My-TODOs:告别拖延症的高效利器

桌面待办工具My-TODOs&#xff1a;告别拖延症的高效利器 【免费下载链接】My-TODOs A cross-platform desktop To-Do list. 跨平台桌面待办小工具 项目地址: https://gitcode.com/gh_mirrors/my/My-TODOs 还在为繁杂的待办事项而烦恼吗&#xff1f;每天打开电脑总是一片…

作者头像 李华
网站建设 2026/6/9 18:35:11

Synology Photos AI识别补丁:让老设备焕发新生的终极解决方案

Synology Photos AI识别补丁&#xff1a;让老设备焕发新生的终极解决方案 【免费下载链接】Synology_Photos_Face_Patch Synology Photos Facial Recognition Patch 项目地址: https://gitcode.com/gh_mirrors/sy/Synology_Photos_Face_Patch 还在为Synology Photos无法…

作者头像 李华
网站建设 2026/6/9 17:23:49

终极指南:如何快速将B站m4s视频转换为mp4格式

终极指南&#xff1a;如何快速将B站m4s视频转换为mp4格式 【免费下载链接】m4s-converter 将bilibili缓存的m4s转成mp4(读PC端缓存目录) 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否遇到过B站视频突然下架&#xff0c;或者缓存视频无法播放的困扰…

作者头像 李华