
crnn的水表刻度识别
数据集包含1类别
收集整理标注好的数据共1500张
包含yolo格式数据集+ppocr识别格式数据集+训练好的模型
支持数据转换voc



一、数据集概述
面向水务智能抄表、园区远程水表读数自动化采集、工业仪表视觉读取场景研发,整套方案采用两级串联识别架构:先用YOLOv8定位水表表盘区域,裁剪表盘后送入CRNN卷积循环神经网络完成数字刻度序列识别;数据集共计1500张现场实拍水表图像,单类别表盘目标,同步搭载YOLO检测标注、PP-OCR文本标注两套标签,附带VOC格式转换脚本、训练权重、完整工程代码,适配水务智能化课题研发与落地部署。
二、核心信息概览
| 维度 | 详细参数 |
|---|---|
| 识别架构 | YOLOv8表盘定位 + CRNN数字序列文字识别(两级流水线) |
| 目标类别 | 1类:water_meter(水表表盘) |
| 图像总量 | 1500张实拍水表图像 |
| 标注体系 | YOLO txt检测标注 + PP-OCR json文本标注,附带VOC转换工具 |
| 适配分辨率 | 统一640×640 |
| 配套资源 | 数据集划分脚本、YOLOv8训练推理代码、CRNN网络完整源码、预训练权重、临时文件清理工具 |
| 适配场景 | 水务远程抄表、园区仪表巡检、工业数显仪表字符识别 |
三、数据集核心亮点
- ✅ 双任务配套标注:检测框+刻度文本一一对应,完美适配先定位后识别流水线方案
- ✅ 实拍复杂工况数据:涵盖强光、阴影、轻微污渍等真实水表使用环境,模型泛化性更强
- ✅ 一站式完整链路:数据集划分→表盘检测→裁剪→CRNN序列识别全流程代码无需二次开发
- ✅ 多格式兼容:原生YOLO、PP-OCR标注,附赠VOC转换脚本,兼容Faster R-CNN等两阶段算法
- ✅ 轻量化部署友好:YOLOv8n+小型CRNN组合,可移植嵌入式设备做现场抄表识别
- ✅ 附带训练好模型权重,可直接开箱推理测试效果
四、适用场景
- 水务公司远程自动抄表系统、小区物业水表集中巡检识别
- 工业数显仪表、流量计、压力表字符序列视觉读取课题研究
- YOLO目标检测+CRNN文本识别串联流水线算法实验
- 嵌入式边缘端水表识别项目开发、机器视觉课程实训
- 水务智能化、物联网视觉采集相关科研课题
五、整体项目目录结构
water_meter_recognition/
├── datasets/
│ └── water_meter_dataset/
│ ├── images/ # 原始水表图片
│ ├── labels/ # YOLO检测txt标注
│ ├── ocr_labels/ # PP-OCR json文本标注
│ ├── train/ # 划分后训练集
│ │ ├── images/
│ │ ├── labels/
│ │ └── ocr_labels/
│ └── val/ # 划分后验证集
│ ├── images/
│ ├── labels/
│ └── ocr_labels/
├── runs/detect/ # YOLO训练日志与权重
├── weights/
│ ├── best.pt # YOLO表盘检测最优权重
│ └── crnn_water_meter.pth # CRNN数字识别权重
├── data.yaml # YOLO数据集配置文件
└── main.py # 整套流程整合脚本
六、分步完整实操流程
步骤1:数据集划分(训练集8:验证集2)
import os
import random
from pathlib import Path
import shutil
def split_dataset(data_dir, train_ratio=0.8):
images = list(Path(data_dir).glob('*.jpg'))
random.seed(42)
random.shuffle(images)
num_train = int(len(images) * train_ratio)
train_images = images[:num_train]
val_images = images[num_train:]
train_dir = Path(data_dir).parent / 'train'
val_dir = Path(data_dir).parent / 'val'
train_img_dir = train_dir / 'images'
train_label_dir = train_dir / 'labels'
train_ocr_label_dir = train_dir / 'ocr_labels'
val_img_dir = val_dir / 'images'
val_label_dir = val_dir / 'labels'
val_ocr_label_dir = val_dir / 'ocr_labels'
for folder in [train_img_dir,train_label_dir,train_ocr_label_dir,val_img_dir,val_label_dir,val_ocr_label_dir]:
folder.mkdir(parents=True, exist_ok=True)
for img in train_images:
label_path = img.with_suffix('.txt')
ocr_label_path = img.with_name(img.stem + '.json')
shutil.copy(img, train_img_dir / img.name)
shutil.copy(label_path, train_label_dir / label_path.name)
shutil.copy(ocr_label_path, train_ocr_label_dir / ocr_label_path.name)
for img in val_images:
label_path = img.with_suffix('.txt')
ocr_label_path = img.with_name(img.stem + '.json')
shutil.copy(img, val_img_dir / img.name)
shutil.copy(label_path, val_label_dir / label_path.name)
shutil.copy(ocr_label_path, val_ocr_label_dir / ocr_label_path.name)
split_dataset('./datasets/water_meter_dataset/images')
步骤2:YOLO数据集配置 data.yaml
train: ./datasets/water_meter_dataset/train/images
val: ./datasets/water_meter_dataset/val/images
nc: 1
names: ['water_meter']
步骤3:YOLOv8表盘区域检测训练+评估+推理
import torch
from ultralytics import YOLO
torch.manual_seed(42)
model = YOLO('yolov8n.pt')
# 训练
results = model.train(
data="data.yaml",
epochs=100,
imgsz=640,
batch=16,
name='water_meter_detection',
project='runs/detect'
)
# 验证集评估
metrics = model.val()
best_weights = 'runs/detect/water_meter_detection/weights/best.pt'
print("最优检测权重保存路径:", best_weights)
步骤4:CRNN卷积循环网络数字刻度识别训练
CRNN核心逻辑:灰度表盘图像经卷积提取空间特征,LSTM时序建模数字前后序列依赖,完成水表连续读数识别
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms
from torch.utils.data import DataLoader, Dataset
import json
import cv2
import numpy as np
class WaterMeterOCRDataset(Dataset):
def __init__(self, root_dir, transform=None):
self.root = root_dir
self.transform = transform
self.img_list = sorted(os.listdir(os.path.join(root_dir,"images")))
self.ocr_list = sorted(os.listdir(os.path.join(root_dir,"ocr_labels")))
def __len__(self):
return len(self.img_list)
def __getitem__(self, idx):
img_path = os.path.join(self.root,"images",self.img_list[idx])
json_path = os.path.join(self.root,"ocr_labels",self.ocr_list[idx])
img = cv2.imread(img_path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = np.expand_dims(img, -1) / 255.0
with open(json_path,"r",encoding="utf-8") as f:
text = json.load(f)["text"]
if self.transform:
img = self.transform(img)
return img, text
# CRNN网络结构
class CRNN(nn.Module):
def __init__(self, in_feat=160, hidden=128, out=10, layers=2):
super().__init__()
self.conv = nn.Sequential(
nn.Conv2d(1,32,3,padding=1),nn.ReLU(),nn.MaxPool2d(2,2),
nn.Conv2d(32,64,3,padding=1),nn.ReLU(),nn.MaxPool2d(2,2)
)
self.lstm = nn.LSTM(in_feat, hidden, layers, bidirectional=True)
self.fc = nn.Linear(hidden*2, out)
def forward(self, x):
x = self.conv(x)
x = x.squeeze(2).permute(2,0,1)
out,_ = self.lstm(x)
return self.fc(out[-1])
# 训练参数
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
trans = transforms.Compose([transforms.ToTensor(),transforms.Resize((64,160))])
train_set = WaterMeterOCRDataset("./datasets/water_meter_dataset/train", trans)
val_set = WaterMeterOCRDataset("./datasets/water_meter_dataset/val", trans)
train_loader = DataLoader(train_set,16,shuffle=True)
val_loader = DataLoader(val_set,16,False)
crnn = CRNN().to(device)
opt = optim.Adam(crnn.parameters(),lr=1e-3)
loss_fn = nn.CrossEntropyLoss()
# 迭代训练
for epoch in range(10):
crnn.train()
total_loss = 0
for imgs, texts in train_loader:
imgs = imgs.to(device)
targets = torch.tensor([int(t) for t in texts]).to(device)
opt.zero_grad()
pred = crnn(imgs)
loss = loss_fn(pred, targets)
loss.backward()
opt.step()
total_loss += loss.item()
print(f"Epoch{epoch+1} Loss:{total_loss/len(train_loader):.4f}")
torch.save(crnn.state_dict(),"weights/crnn_water_meter.pth")
# 验证集准确率评估
crnn.eval()
correct, total = 0,0
with torch.no_grad():
for imgs, texts in val_loader:
imgs = imgs.to(device)
targets = torch.tensor([int(t) for t in texts]).to(device)
pred = crnn(imgs)
_,res = torch.max(pred,1)
total += targets.size(0)
correct += (res==targets).sum().item()
print(f"验证集识别准确率:{100*correct/total:.2f}%")
步骤5:整套流水线联合推理(检测表盘+裁剪+CRNN读数)
import cv2
from PIL import Image,ImageDraw
from ultralytics import YOLO
det_model = YOLO("weights/best.pt")
ocr_model = CRNN().to(device)
ocr_model.load_state_dict(torch.load("weights/crnn_water_meter.pth"))
ocr_model.eval()
def meter_full_infer(img_path):
# 1. YOLO检测表盘
res = det_model(img_path,conf=0.5)[0]
box = res.boxes.xyxy[0].cpu().numpy().astype(int)
x1,y1,x2,y2 = box
# 2. 裁剪表盘区域
raw_img = cv2.imread(img_path)
crop_img = raw_img[y1:y2,x1:x2]
# 3. CRNN识别数字
gray = cv2.cvtColor(crop_img,cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray,(160,64))
tensor = trans(np.expand_dims(gray,-1)).unsqueeze(0).to(device)
with torch.no_grad():
out = ocr_model(tensor)
num = torch.argmax(out).item()
# 4. 绘制结果
pil_img = Image.open(img_path)
draw = ImageDraw.Draw(pil_img)
draw.rectangle([x1,y1,x2,y2],outline="green",width=3)
draw.text((x1,y1-25),f"水表读数:{num}",fill="red")
pil_img.save("meter_result.jpg")
pil_img.show()
return num
meter_full_infer("test.jpg")
步骤6:临时缓存文件清理
import shutil,os
def clean_cache(path):
for p in [f"{path}/wandb",f"{path}/cache"]:
if os.path.exists(p):
shutil.rmtree(p)
clean_cache("runs/detect/water_meter_detection")
七、重要使用注意事项
- 文件路径禁止包含中文、空格,避免标注读取、训练报错;
- 水表污渍、反光会降低识别精度,可增加随机亮度、对比度离线数据增强优化;
- CRNN适配连续数字序列,若水表为多位小数读数,可调整字符字典与输出维度;
- 硬件显存不足可下调batch尺寸,嵌入式部署推荐选用yolov8n极小模型;
- 内置VOC转换工具,可将YOLO标注转为xml格式适配传统检测算法。
数据集标签
#水表刻度识别数据集 #CRNN文字识别 #YOLO水表检测 #水务智能抄表 #工业仪表OCR #PPOCR标注数据集 #YOLO+CRNN串联识别 #工业视觉数据集 #深度学习水表读数识别 #物联网仪表视觉采集数据集
691



被折叠的 条评论
为什么被折叠?



