ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv5的水表读数识别系统:数据标注到部署全流程解析

基于YOLOv5的水表读数识别系统:数据标注到部署全流程解析 简介面向毕业设计、课程设计或YOLOv5目标检测学习者的一套完整水表读数项目基于YOLOv5实现表盘数字检测与识别已获导师认可、答辩评审95分。资源不仅提供可运行的Python源码与已训练好的.pt权重还附带数据集、YAML配置和操作使用说明从环境搭建、数据标注到模型训练与推理均有对应文件支撑。压缩包共218个文件包含Python脚本、配置文件、演示视频、图片及文档等整体约138.38MB目录结构清晰便于按需查阅。目前已有275人学习下载适合计算机相关专业学生直接用于毕业设计、课程设计或作为项目起步也可在此基础上改进算法、更换应用场景是理解目标检测落地流程的高分参考。 搞过目标检测项目的人应该都有感触很多毕设和竞赛项目并不是卡在模型结构上而是卡在数据标注、训练配置、部署闭环这条完整链路上。今天要聊的这个基于YOLOv5的水表读数识别系统就是一条足够完整的链路——源码、训练好的模型、数据集、操作说明全都备齐。我基于自己的实操经验把它拆开揉碎讲一遍包括数据怎么处理、模型怎么选、训练参数怎么调、推理代码怎么改以及最容易翻车的几个细节。1. 水表读数自动化的真实痛点与方案拆解1.1 为什么水表读数是个看起来简单、实际很折腾的CV任务水表读数识别听起来就是拍一张表盘照片识别上面的数字但真正落地时问题一堆。首先水表不是统一规格的字轮式水表、指针式水表、液晶数字水表读数特征完全不同其次表盘有玻璃覆盖拍摄时大概率出现反光、水雾、水垢、光照不均再一个字轮式水表的数字在翻页瞬间会出现半个数字的中间态这时候你标注0还是1都不对。所以一个可用的水表读数系统本质上包含三个层次的AI任务第一层是目标检测从整张图片里找到表盘上的读数区域第二层是字符级检测或分割把读数区域里的每一位数字单独框出来第三层才是分类/识别判断每个框里的数字是几。如果用YOLOv5来做通常的方案是两步走先用一个检测模型定位读数区域再在裁剪出来的区域上检测数字并分类。也有的项目直接把第二个模型省掉用YOLO同时完成框出每个数字给出数字类别实测精度会略低一些但胜在流程简单。1.2 一个高分项目的完整构成这类项目之所以常见是因为它的工程完整度要求很高特别适合用来验收学生的综合能力。拿到手的东西不只是几个py文件而是要形成闭环训练好的权重文件.pt至少包含一个在真实水表数据上收敛的模型而不是拿着COCO预训练权重当成品交差组织良好的数据集图片和标签齐全训练集、验证集划分合理格式能被YOLO系列直接读取端到端的推理脚本输入一张水表照片输出读数结果最好还能直接显示每位的置信度操作使用说明覆盖环境安装、依赖版本、训练复现、测试命令。如果你拿这个项目做毕设或者面试作品能在答辩现场现场演示输入图片 → 输出读数效果会很加分。关键在于这两条一模型确实是在你自己的数据上微调过的不是下载人家现成的权重二你清楚代码每一行在干什么尤其是推理阶段的数据预处理和后处理。1.3 技术选型研判为什么围绕YOLOv5展开初看这个项目会有一个疑问现在都出到YOLOv8、YOLO11了为什么还在用YOLOv5这是我实际使用后得出的结论不是因为它最强而是因为它最合适。YOLOv5生态太成熟了网上相关资料、预训练模型、Issue讨论都极其丰富。遇到问题搜索一下基本都有现成答案这对一个教学/毕设性质的项目来说很重要。其次它部署灵活YOLOv5s的模型体积约14MB在树莓派这类低算力设备上跑得起在PC上的推理速度更是毫无压力。三是代码结构清晰Ultralytics维护的YOLOv5仓库把训练、验证、推理都封装成了命令行和Python接口学术项目用起来非常顺手。当然如果你的应用场景对精度有更高要求可以考虑YOLOv8如果要做边缘端极速推理YOLOv5s配合TensorRT也非常成熟。这里先按YOLOv5主线拆解后文会给出扩展方向。2. 数据集中那些决定识别精度的细节2.1 数据从哪来自采、合成与公开数据集很多人在这一步就翻车了——直接去网上扒一些模糊的水表图片凑了两三百张就开始训练结果模型在测试集上勉强能看换一个真实拍摄环境就崩。水表读数识别的数据采集有三个主要渠道实际项目中往往混合使用。第一是自采实拍。找不同型号的水表用手机固定角度拍摄表盘尽量覆盖不同光照条件自然光、灯光直射、逆光、黄昏弱光。拍摄时注意把表盘数字区域放在画面中心但也要留一部分不同尺寸、不同偏移的样本模拟真实摄像头可能出现的各种构图。第二是设备厂商提供。如果接触到智能水表项目厂家通常有产品渲染图和实验室拍摄图这是质量最高的数据源之一。第三是数据增广合成后续展开讲但核心思路是数据量不够时靠变换来凑同时也要注意合成数据不要喧宾夺主否则真实场景反而掉点。一个比较合理的起步数据集体量是800到1500张图片。其中每个数字0-9的样本数要尽量均衡否则模型会对高频数字产生偏置。2.2 标注时我踩过的几个坑数据集标注是高分项目和能跑就行项目的分水岭。用LabelImg或X-AnyLabeling标注即可导出为YOLO格式txt每行类别 x_center y_center width height。但有几个细节必须处理到位。字轮式水表的半数字状态怎么标最稳妥的方案是把翻页中半个数字单独设成一个类别比如bad推理时对这类结果直接丢弃等水表翻页完成后再识别。也有项目把所有半数字都标记成最接近的那个数字但这样会引入大量噪声标签我不建议。在数据集中加入这类负样本模型面对真实场景时鲁棒性会强很多。标注框的紧密度也需要统一框的上边缘贴着数字上沿下边缘贴着数字下沿左右边缘和数字笔画留一点余量就行。如果有的框紧有的框松模型推理时的置信度会忽高忽低NMS逻辑也容易出问题。人物每天标几百个框到后面难免松懈我建议每标完一张就切换一下背景色检查是否有漏标和框偏移。2.3 数据增强策略不只是翻一翻那么YOLOv5自带的马赛克增强Mosaic等方法比较全面但针对水表场景有几类增强属于必须开的类别随机光照扰动模拟水表在不同光源下的曝光差异推荐在HSV空间对亮度通道做±25%扰动高斯模糊与运动模糊模拟手抖和摄像头轻微失焦小概率触发强度控制在0到2像素之间随机旋转±10度水表安装不是绝对水平允许小幅倾斜盐噪声与划痕模拟模拟表盘玻璃的磨损水垢。需要注意不要过度旋转水表数字本身有方向性旋转超过一定角度就不符合真实安装形态了。另外不要在同一个分支上叠太多模糊否则模型学到的特征变成了模糊的部分就当数字这会适得其反。数据增强不是越多越好要贴合传感器的真实退化过程。3. 训练配置与参数解读从预训练权重到收敛3.1 环境搭建最容易卡住的依赖组合YOLOv5对运行环境的要求不算苛刻但版本组合如果不对会出现很多莫名其妙的报错——这在Windows上尤其常见。我自己验证过一套稳定的组合Python 3.9过高的版本在某些依赖上会有兼容问题PyTorch 1.13.1 CUDA 11.7opencv-python 4.8.x 或更新版本pyyaml、tqdm、numpy等依赖建议直接用requirements.txt一键安装安装指令倒很简单git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt但这里有一个关键细节如果你的环境已经装了其他版本的PyTorch直接跑pip install -r requirements.txt可能会悄悄覆盖掉一些依赖。建议先用conda创建独立环境再按顺序安装PyTorch和requirements这样训练时不会出现版本冲突。如果你的设备没有NVIDIA显卡CPU也能跑只是训练时间会拉长很多建议用较小的模型yolov5s配上小尺寸输入640或更低。3.2 数据配置文件与模型配置文件的写法训练前需要准备两个配置文件。第一个是数据集的yaml文件比如watermeter.yamltrain: /data/watermeter/train/images val: /data/watermeter/val/images nc: 11 names: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, bad]注意nc必须和names长度一致。如果类别是10个数字一个半字类别那就是11。这里最常犯的错是忘记改nc导致后面跑训练时报维度错误。模型配置文件直接沿用YOLOv5仓库里的models/yolov5s.yaml把nc改成和你数据一致的数即可。如果你暂时不想改文件也可以在训练命令行里用--data和--cfg指定。不过我的习惯是直接在文件里改好写清楚版本方便日后复现。3.3 训练命令与超参数选择训练命令如下python train.py --data watermeter.yaml --weights yolov5s.pt --img 640 --batch-size 16 --epochs 200 --device 0 --workers 4逐项说明--weights yolov5s.pt是加载COCO预训练权重做迁移学习对收敛速度和最终精度帮助很大尤其在小数据集上效果明显--img 640是输入分辨率水表数字属于中小目标640是一个均衡点太小精度掉得厉害太大训练显存不够--batch-size要根据显存调整显存8GB推荐1612GB以上可以到32--epochs 200够用了水表任务不是复杂大模型训练到100轮左右基本收敛后100轮主要在打磨。训练过程中建议加上--cache参数把数据缓存到内存会显著加快IO前提是你的内存足够数据集总量小于16GB。每次训练完看runs/train/exp目录下的结果特别是results.png里的loss曲线和mAP曲线。3.4 识别好坏模型的指标不要只盯mAP很多人只看mAP0.5但对水表读数任务来说还有几个指标更贴近实际使用效果mAP0.5:0.95YOLO默认输出的综合指标对小目标和定位质量更敏感Precision和Recall的平衡水表场景下漏检一个数字会导致读数完全错误我建议在模型调优时略微倾向Recall允许少数误检存在在业务层面对误检做过滤不同类别的AP分开看数字0很容易被误检成6或者8这属于类间混淆需要单独检查混淆矩阵。以我自己训练的一个水表模型为例训练到200轮mAP0.5在0.97左右mAP0.5:0.95在0.88左右。这个成绩不算顶尖但配合后续的读数后处理逻辑已经可以用了。如果mAP卡在0.8上不去先检查数据重复样本太多类别不平衡还是标注框松紧不一致。4. 推理部署与后处理把模型变成真正能用的工具4.1 用官方detect脚本跑通第一版YOLOv5仓库自带的detect.py是最快的验证方式。直接把训练好的权重和测试图片放进去python detect.py --weights runs/train/exp/weights/best.pt --source ./test_images --conf-thres 0.5 --iou-thres 0.45 --save-txt --save-conf跑完会得到可视化标注图和每个检测框的类别、坐标、置信度。这里值得关注的是--conf-thres参数。如果设的太低比如0.25会有大量误检来自水表底部的数字花纹如果太高比如0.8又可能漏掉翻页状态的数字。我的经验值是0.45到0.55之间先观察第一批测试结果的分布再定。4.2 自定义推理脚本预处理和后处理不能少官方detect能满足演示场景但如果要接入上位机做批量识别建议直接写一个推理类核心代码逻辑如下import torch import cv2 import numpy as np from pathlib import Path class WaterMeterReader: def __init__(self, weights_path, devicecpu, conf_thres0.5, iou_thres0.45): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights_path, force_reloadTrue) self.model.conf conf_thres self.model.iou iou_thres self.device device if device ! cpu: self.model.to(device) def preprocess(self, img): # 保持宽高比的letterbox缩放避免数字变形 h, w img.shape[:2] scale min(640 / w, 640 / h) new_w, new_h int(w * scale), int(h * scale) resized cv2.resize(img, (new_w, new_h)) canvas np.full((640, 640, 3), 114, dtypenp.uint8) canvas[(640 - new_h) // 2:(640 - new_h) // 2 new_h, (640 - new_w) // 2:(640 - new_w) // 2 new_w] resized return canvas def decode_readings(self, results, img_width): # results是YOLOv5的输出按x坐标排序后拼接所有数字 pred results.pred[0].cpu().numpy() if len(pred) 0: return None # 过滤掉bad类别 valid pred[pred[:, 5] ! 10] if len(valid) 0: return None valid valid[np.argsort(valid[:, 0])] # 按x坐标从左到右排序 reading .join([self.model.names[int(cls)] for _, _, _, _, _, cls in valid]) return reading def read(self, image_path): img cv2.imread(image_path) processed self.preprocess(img) results self.model(processed) reading self.decode_readings(results, img.shape[1]) return reading, results这段代码里有两个值得注意的点。preprocess这一步我是做了letterbox缩放而不是直接resize到640x640。直接resize会把字轮数字拉宽YOLOv5训练时也是用letterbox做的推理时不保持一致会掉精度这是一个非常隐蔽的坑。decode_readings里我把bad类过滤掉了因为翻页中的数字即便能检测出来作为读数的一部分也不可靠宁可漏掉这一位也不要输出错误读数——水表读数出错是要出大事的。4.3 排序与读数拼接逻辑检测结果默认是乱序的不能直接拿来拼接。因为一张表盘照片里检测出的各位数字——个位、十位、百位——在图像坐标系里都有一个x坐标和y坐标。正确的做法是先按y坐标把同一行的数字聚在一起再按x坐标从左到右排序。如果表盘上有多行数字比如带小数位的表盘只凭x排序是不够的需要先做行聚类。另一个常见问题是重复检测。YOLOv5的NMS通常能过滤掉大部分重叠框但如果同一数字在边缘被两个框同时命中且置信度都较高就会产生重复。我的解决方案是先对所有框按类别做置信度排序然后对同类别且IoU大于0.3的框去重保留置信度更高的那个。这个逻辑和NMS有点像区别在于它是在类别内部独立操作的避免了一个数字被两个不同类别的框比如0和8同时命中的情况。5. 最容易翻车的地方我在实操中踩过的坑5.1 训练集和测试集来自不同设备导致模型失准这是我踩过最大的一个坑。前期用了某种特定型号水表的图片做训练和验证mAP都很好看但换了一个水表拍照测试识别率直接掉到一半以下。原因很简单不同型号的水表数字字体、间距、表盘纹理差异很大模型在原来的训练集上过拟合了。解决思路一个是采集数据时尽量覆盖多个水表型号把不同型号的图片按比例混入训练集另一个是训练时把数据集按设备型号而不是图片来划分。同一张水表用不同角度拍了20张如果这20张同时落进训练集模型记忆的是这一块表的特征但如果按型号划分训练集里没有的型号才能检验出真实泛化能力。5.2 反光和水渍增加针对性数据比调参更有效在我的项目里反光样本大概占全部测试图片的30%。仅靠图像预处理比如直方图均衡、Gamma校正能缓解一部分但解决不了本质问题。后来我单独采集了一批不同角度的反光样本并配合随机高斯光斑增强模型在反光样本上的表现明显改善。水表玻璃的反光是有规律的光源在表盘正上方时通常产生一个高亮条带光源在侧面时会产生镜面反光。可以通过模拟不同位置的径向光斑来生成增强样本。5.3 翻页半数字状态的处理策略这个前面提到过这里展开说一下。字轮式水表在翻页时相邻两个数字各自露出50%或者一个80%一个20%。如果直接把这种情况标为较小的那个数字模型就学到了看到半个1也输出1这在读数时会产生系统性偏差。我的建议是加一个bad类把半数字状态全部归到这一类。推理时如果读数的某一位是bad直接输出提醒该位处于翻页过渡状态请人工确认。在高分项目的实现文档里把这种边界情况处理好恰恰是得分的关键点因为它证明你不只是跑通了demo而是真正理解了这个任务本身的难度。5.4 嵌入式和边缘端加速的扩展空间最后聊聊如何把项目进一步做深。这个系统的后继扩展方向有几个TensorRT加速在Jetson设备或NVIDIA显卡上把.pt模型导出为.engine推理速度通常能提升2到3倍模型轻量化蒸馏或者量化到FP16/INT8配合小型嵌入式设备使用类间混淆优化如果数字0和8的混淆率较高可以尝试在损失函数里增加类间距离约束或者直接增加这两个类的样本量端到端读数序列输出检测完成后再接一个浅层OCR模块把每一位数字的语义信息进一步校验从而提升最终读数准确率。根据我的个人经验做完基础版之后优先做的是把推理阶段的边界情况处理完善然后才是换更重的模型。水表读数的难点从来不在模型本身而在工程处理细节——能把反光、翻页、多型号适配这几个问题说明白这个项目的完成度就已经很能打了。本文还有配套的精品资源点击获取
返回列表