ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLO轮胎字符检测实战:1741张带标签图像训练与避坑指南

YOLO轮胎字符检测实战:1741张带标签图像训练与避坑指南 简介本资源为面向YOLO系列目标检测算法的轮胎字符数据集适用于yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流框架可直接用于模型训练与验证测试适合从事工业质检、字符识别方向的研究者与开发者快速搭建实验基线。压缩包共2000个文件约69.89MB其中1219个xml文件为VOC格式标注781个txt文件为YOLO格式标注两种格式分别存放便于按需选用同时附带data.yaml配置文件已划分好训练与验证集开箱即可投入训练。YOLO格式采用归一化的中心点坐标与宽高比例类别索引从0开始符合标准解析规范。目前已有65人学习下载资源结构清晰、标注完整能帮助读者省去数据采集与清洗环节将精力集中于模型调优与字符检测效果验证。1. 轮胎字符数据集与 YOLO 检测1741 张带标签图像能跑出什么结果拿到一个「yolo算法-轮胎字符数据集-1741张图像带标签-.zip」这样的压缩包多数人的第一反应是解压、看目录、找 data.yaml然后直接yolo train。但真正决定这个数据集能不能落地的不是模型选型而是这 1741 张图像里轮胎字符的标注质量、字符类别分布和拍摄工况。轮胎字符检测的典型场景是产线轮胎侧壁 DOT 码、规格型号、生产周次的自动识别字符往往存在弧形排布、低对比度、油污遮挡、反光等问题和通用 OCR 数据集完全不是一个难度量级。这个数据集适合两类人一是做轮胎制造 MES 追溯、质检工位的视觉工程师二是想用真实工业小样本数据集练手 YOLO 检测到识别全链路的人。1741 张不算多但带标签意味着可以直接进入训练省掉最耗时的标注环节这也是它最大的价值点。下面按「先看清数据、再跑通训练、最后落到字符识别」的顺序拆开讲。2. 先摸清 1741 张轮胎字符图像的底细标注格式、类别分布与工况判断2.1 解压后先做的三件事目录结构、标签格式、图像尺寸拿到压缩包不要急着训练先花十分钟把数据摸清楚。轮胎字符数据集常见的标签格式有两种YOLO 的 txt每行class x_center y_center w h归一化到 0-1和 VOC 的 xml。先确认是哪一种直接决定后面要不要转换。# 解压后先看目录层级判断是 images/labels 分离还是混放 unzip yolo算法-轮胎字符数据集-1741张图像带标签-.zip -d tire_char_dataset cd tire_char_dataset find . -maxdepth 3 -type d | head -30 # 统计图像数量和格式分布 find . -type f \( -iname *.jpg -o -iname *.png -o -iname *.bmp \) | wc -l find . -type f -iname *.jpg | wc -l find . -type f -iname *.png | wc -l # 看标签文件长什么样判断是 YOLO txt 还是 VOC xml find . -name *.txt | head -3 | xargs -I{} sh -c echo {} ; head -5 {} find . -name *.xml | head -2这三条命令分别解决三个问题目录结构决定 data.yaml 里 train/val 路径怎么写图像格式分布决定要不要统一转 jpgbmp 体积大、png 无损但训练时解码慢标签文件内容决定是直接用还是先转换。如果 txt 里每行是 5 个 0-1 之间的小数就是标准 YOLO 格式如果是xmin ymin xmax ymax的整数那是 VOC 转过来的中间态需要归一化。2.2 用脚本统计类别分布和框尺寸判断数据集是否偏斜轮胎字符的类别通常是数字 0-9 加字母但实际标注里可能把整个 DOT 码框成一个类也可能逐字符标注。这两种标注策略对训练目标影响完全不同必须先统计。import os, glob from collections import Counter label_dir tire_char_dataset/labels # 按实际路径改 class_counter Counter() box_w, box_h [], [] for txt in glob.glob(os.path.join(label_dir, *.txt)): with open(txt) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue c, x, y, w, h int(parts[0]), *map(float, parts[1:]) class_counter[c] 1 box_w.append(w) box_h.append(h) print(类别分布:, dict(sorted(class_counter.items()))) print(总框数:, sum(class_counter.values())) if box_w: print(f框宽 min/mean/max: {min(box_w):.3f}/{sum(box_w)/len(box_w):.3f}/{max(box_w):.3f}) print(f框高 min/mean/max: {min(box_h):.3f}/{sum(box_h)/len(box_h):.3f}/{max(box_h):.3f})这段脚本输出两个关键信息。类别分布如果某一类占比超过 60%说明数据集偏斜训练时要么过采样少数类要么在 loss 里给少数类加权。框尺寸的 min 值尤其重要如果最小框宽小于 0.01即原图上不到 10 像素YOLO 默认的 640 输入下这个字符会被缩到 6 像素以下特征几乎消失必须考虑提高输入分辨率到 960 或 1280或者用切片推理。轮胎字符里 DOT 码末尾的小数字经常是这种小目标这是后面翻车的高发区。2.3 划分训练集验证集1741 张怎么分才不浪费1741 张按 8:1:1 分验证集只有 174 张如果某些字符类别本身样本就少验证集里可能一个都没有导致评估指标失真。轮胎字符这种类别不均衡的数据集我一般用分层抽样保证每个类别在验证集里至少出现几次。import os, glob, random, shutil from collections import defaultdict random.seed(42) img_dir, lbl_dir tire_char_dataset/images, tire_char_dataset/labels out_root dataset_split for sub in [train, val]: os.makedirs(f{out_root}/{sub}/images, exist_okTrue) os.makedirs(f{out_root}/{sub}/labels, exist_okTrue) # 按主类别分组简单分层 groups defaultdict(list) for img in glob.glob(os.path.join(img_dir, *)): stem os.path.splitext(os.path.basename(img))[0] lbl os.path.join(lbl_dir, stem .txt) if not os.path.exists(lbl): continue with open(lbl) as f: classes [line.split()[0] for line in f if line.strip()] key classes[0] if classes else empty groups[key].append((img, lbl)) for key, items in groups.items(): random.shuffle(items) n_val max(1, int(len(items) * 0.15)) for i, (img, lbl) in enumerate(items): sub val if i n_val else train shutil.copy(img, f{out_root}/{sub}/images/{os.path.basename(img)}) shutil.copy(lbl, f{out_root}/{sub}/labels/{os.path.basename(lbl)}) print(train:, len(os.listdir(f{out_root}/train/images))) print(val:, len(os.listdir(f{out_root}/val/images)))分层抽样的逻辑是按每张图的主类别分组组内随机划分这样少数类不会全被分到训练集。n_val用max(1, ...)兜底防止某个类别只有一两张时验证集为空。划分完打印两个数字确认train 加 val 应该等于有效图像总数去掉没有标签的图。这一步做完data.yaml 就可以写了路径指向dataset_split下的 train 和 val。提示如果解压后发现标签是 VOC xml先用转换脚本转成 YOLO txt 再走上面的流程转换时注意 xml 里的 size 字段要和实际图像尺寸一致不一致会导致框偏移。3. 用 YOLOv8 在轮胎字符数据集上跑通训练配置、命令与参数3.1 环境准备与 data.yaml 的正确写法轮胎字符检测不需要多复杂的依赖Ultralytics 的 YOLOv8 是目前上手最快、文档最全的选择。环境用 Python 3.9 以上装 ultralytics 和 opencv 就够。conda create -n tire_yolo python3.10 -y conda activate tire_yolo pip install ultralytics opencv-python pyyaml yolo checks # 确认环境和 GPU 可用yolo checks会打印 CUDA 是否可用、显存大小、版本号。如果显示 CPU only训练会慢到无法接受先解决驱动和 torch 的 CUDA 版本匹配问题。data.yaml 的写法直接决定训练能不能启动# tire_char.yaml path: /abs/path/to/dataset_split train: train/images val: val/images nc: 10 names: [0,1,2,3,4,5,6,7,8,9]path必须是绝对路径相对路径在不同工作目录下会找不到。nc和names要和前面统计的类别数严格一致多一个少一个都会在训练启动时报错。如果数据集是把整个 DOT 码当一个类那 nc 就是 1names 写[dot_code]。names 的顺序必须和标签里的 class id 对应这个对应关系错了训练出来的模型会把字符认成别的类而且 loss 下降得很正常属于最隐蔽的翻车。3.2 训练命令与关键参数轮胎字符小目标该调什么yolo detect train \ datatire_char.yaml \ modelyolov8s.pt \ epochs150 \ imgsz960 \ batch16 \ lr00.01 \ lrf0.01 \ patience30 \ mosaic1.0 \ close_mosaic15 \ degrees5 \ translate0.1 \ scale0.3 \ fliplr0.0 \ hsv_v0.4 \ hsv_s0.3 \ projectruns/tire_char \ nameexp_v8s_960逐个说为什么这么设。imgsz960而不是默认 640是因为轮胎字符里的小数字在 640 下会缩得太小960 能在显存和精度之间取平衡如果显存够直接上 1280。batch16是 960 分辨率下 8G 显存的保守值显存不够就降到 8 并开ampTrue默认开。patience30是早停轮胎字符数据集小150 epoch 里如果 30 轮验证指标不涨就停省时间。close_mosaic15表示最后 15 轮关闭 mosaic 增强让模型在真实分布上收敛这个对小数据集尤其重要mosaic 开到最后会导致验证指标虚高但实际推理框不准。fliplr0.0关掉水平翻转因为轮胎字符是有方向性的翻转后字符变成镜像标签语义就错了这是轮胎字符和通用目标检测最大的差异之一。degrees5只做小角度旋转模拟轮胎侧壁的轻微倾斜不要开大大了字符会转出框。3.3 训练过程看什么loss 曲线、mAP 与验证集可视化训练启动后不要只盯着终端刷屏重点看三个东西。第一是box_loss和cls_loss是否稳定下降如果 cls_loss 震荡剧烈多半是学习率太大或类别不均衡。第二是每轮结束打印的mAP50和mAP50-95轮胎字符这种小目标mAP50 能到 0.9 以上算正常mAP50-95 通常低不少因为框的定位精度要求高。第三是runs/tire_char/exp_v8s_960/下的val_batch*.jpg这些是验证集的可视化结果直接看模型有没有漏检小字符、有没有把相邻字符框成一个。# 训练完在验证集上单独评估输出每类指标 yolo detect val \ modelruns/tire_char/exp_v8s_960/weights/best.pt \ datatire_char.yaml \ imgsz960 \ plotsTrueplotsTrue会生成混淆矩阵和 PR 曲线。混淆矩阵能看出哪些字符容易被互相认错轮胎字符里 0 和 O、1 和 I、6 和 8 是高频混淆对如果混淆严重要么增加这些类的样本要么在推理后处理里加规则约束比如 DOT 码格式固定可以用正则校验。PR 曲线看每个类的 AP哪个类拖后腿一目了然。注意训练完的 best.pt 是在验证集上选出来的如果验证集划分不合理比如某类没进验证集best.pt 可能过拟合训练集。所以第 2 章的分层抽样不能省。4. 轮胎字符检测的避坑与排查5 个真实翻车记录4.1 现象训练 loss 正常下降但推理框全偏原因标签归一化基准错了这是最经典的翻车。标签 txt 里的 x_center 是按图像宽度归一化的但如果标注时用的基准尺寸和实际图像尺寸不一致比如标注工具里图像被缩放过框就会整体偏移。表现是训练时 loss 能降到很低因为模型学的是一套自洽的偏移标签但推理时框的位置和字符对不上。解决办法是写个脚本把标签框画回原图肉眼抽查 20 张。import cv2, os img_path dataset_split/train/images/xxx.jpg lbl_path dataset_split/train/labels/xxx.txt img cv2.imread(img_path) H, W img.shape[:2] with open(lbl_path) as f: for line in f: c, x, y, w, h line.split() x, y, w, h map(float, (x, y, w, h)) x1, y1 int((x - w/2) * W), int((y - h/2) * H) x2, y2 int((x w/2) * W), int((y h/2) * H) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_vis.jpg, img)画出来一看就知道框准不准。如果整体偏移说明归一化基准错了需要重新按实际图像尺寸归一化标签。4.2 现象小字符漏检严重原因输入分辨率不够或 anchor 不匹配轮胎 DOT 码末尾的小数字在原图上可能只有 15 像素宽缩到 640 输入后不到 8 像素卷积下采样几次就没了。解决有三条路提高 imgsz 到 960 或 1280用切片推理SAHI把大图切小块分别检测再合并或者在数据增强里加copy_paste把小字符复制粘贴到更多位置增加样本。我一般先试提高分辨率成本最低显存不够再考虑切片。4.3 现象验证 mAP 很高但实际产线图检测差原因训练集工况太单一1741 张如果都来自同一批轮胎、同一光照条件模型会过拟合到这个工况。产线上换个批次、光照变了、轮胎脏了检测就崩。这是工业视觉数据集最常见的坑。解决办法是在训练增强里加大 HSV 扰动hsv_v0.4, hsv_s0.3、加高斯噪声和模糊如果条件允许尽量补充不同工况的样本。评估时不要只看验证集留一批不同时间拍的图做测试集。4.4 现象相邻字符被框成一个大框原因NMS 阈值或标注本身粘连轮胎字符排列紧密如果标注时两个字符的框有重叠模型学到的就是粘连框。先检查标注如果标注本身是分开的但推理粘连调低 NMS 的iou阈值默认 0.7可降到 0.5让重叠框更容易被保留。如果标注本身就粘连只能重新标注没有捷径。4.5 现象训练到一半显存爆了原因mosaic 增强的随机拼接放大了输入mosaic 会把 4 张图拼成一张如果原图分辨率不一致拼接后的图可能远大于 imgsz导致显存峰值飙升。解决办法是训练前统一所有图像尺寸或者在 dataloader 里强制 resize。另外close_mosaic15之后显存会降下来如果爆在前期先把 batch 降到 8。提示这 5 条里标签归一化错误和工况单一是最难排查的因为它们不报错只是指标虚高。养成训练前可视化抽查、训练后换工况测试的习惯能省掉大量返工。5. 从检测框到字符识别轮胎 DOT 码端到端落地的进阶技巧检测只是第一步轮胎字符的最终目标是读出 DOT 码内容。检测框出来之后接一个轻量 OCR 或者字符分类头就能闭环。我一般不在检测模型上直接做识别而是检测框裁剪 字符分类这样两个模型可以独立迭代检测模型换分辨率不影响识别模型。具体做法用训练好的 YOLO 检测出每个字符框按 x 坐标排序轮胎字符通常水平或弧形排列弧形的话先做透视校正裁剪出每个字符的小图送进一个简单的 CNN 分类器比如 5 层卷积 全连接输入 32x32 灰度图。分类器的训练数据就是检测数据集里的框裁剪出来的标签用文件名或标注里的类别。这个分类器在 1741 张数据集上每张图平均 10 个字符能裁出约 1.7 万个小图足够训练一个字符分类器。import cv2, os, glob import numpy as np def crop_chars(img_path, lbl_path, out_dir, size32): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) H, W img.shape os.makedirs(out_dir, exist_okTrue) with open(lbl_path) as f: boxes [] for line in f: c, x, y, w, h line.split() boxes.append((float(x), int(c), float(x)-float(w)/2, float(y)-float(h)/2, float(x)float(w)/2, float(y)float(h)/2)) boxes.sort(keylambda b: b[0]) # 按 x 排序还原字符顺序 for i, (_, c, x1, y1, x2, y2) in enumerate(boxes): crop img[int(y1*H):int(y2*H), int(x1*W):int(x2*W)] if crop.size 0: continue crop cv2.resize(crop, (size, size)) cv2.imwrite(f{out_dir}/{c}_{os.path.basename(img_path)}_{i}.png, crop) for img in glob.glob(dataset_split/train/images/*.jpg): lbl img.replace(images, labels).replace(.jpg, .txt) if os.path.exists(lbl): crop_chars(img, lbl, char_crops/train)这段脚本的关键在boxes.sort(keylambda b: b[0])按 x 中心排序还原字符从左到右的顺序DOT 码的语义依赖顺序排错了读出来就是乱码。裁剪出来的小图按类别_原图名_序号.png命名方便后续训练分类器时直接解析标签。分类器训练就是标准的图像分类流程交叉熵损失Adam 优化器20 个 epoch 左右就能收敛因为字符类别少、图像简单。端到端串联时检测和识别之间加一个后处理规则DOT 码有固定格式比如DOT XXXX YYYY用正则校验识别结果不符合格式的丢弃或告警。这个规则能挡掉大部分误检比单纯调模型阈值有效得多。验证方法上我习惯留 50 张完全不参与训练的产线图做端到端测试统计整串 DOT 码的完全正确率而不是单字符准确率因为实际业务只关心整串对不对。最后说个血泪经验轮胎字符数据集这类工业小样本模型结构的选择远不如数据质量和工况覆盖重要。我见过用 yolov8n 在清洗好的数据上跑出比 yolov8x 在脏数据上更好的结果。所以拿到 1741 张带标签图像先花时间摸清数据、修标注、补工况再谈调参和换模型。希望帮到你。本文还有配套的精品资源点击获取
返回列表