首页 / 资讯中心 / 文章详情

路面缺陷检测数据集YOLOV5格式详解:从txt标注到训练避坑

路面缺陷检测数据集YOLOV5格式详解:从txt标注到训练避坑 ★ FEATURED ARTICLE
简介这是一份面向目标检测算法训练与路面缺陷识别场景的数据集资源适合计算机视觉学习者、工程检测人员及YOLO系列模型实践者使用。数据严格按YOLOV5目录格式组织分为训练集与验证集训练集含841张图片及对应标签验证集含231张图片及对应标签共覆盖纵向裂纹、横向裂纹、坑洞、不规则裂缝四个典型路面缺陷类别无需额外转换即可直接投入模型训练。资源总计2000个文件以jpg图像、txt标注文件和py可视化脚本为主压缩包大小74.55MB。其中txt文件为YOLO格式的边界框标签jpg为600×600分辨率的路面实拍图py脚本则用于随机抽取单张图片绘制检测框便于快速核查标注效果与数据质量。目前已吸引769人学习下载对入门目标检测数据准备或开展路面缺陷检测实验都有直接参考价值。1. 路面缺陷检测数据集4 类 YOLOV5 标注里真正决定训练成败的是 txt 而不是图片路面缺陷检测项目的第一个门槛不是模型而是数据集的 YOLOV5 目录格式。拿到一批裂缝、坑槽等缺陷的图片后需要先把它们整理成 images 与 labels 两块每一张 jpg 对应一个同名 txttxt 里每一行写一个目标框的类别和归一化中心点坐标。很多人拿到数据集先滚动看图图片看着没毛病训练时却报 “No labels found” 或 mAP 一直卡在 0 附近根源往往在 labels 的路径、文件名和字段顺序上。这篇文章把 4 类路面缺陷数据集从目录结构、标注规范、体检脚本一路铺到训练参数和避坑记录目标是让你拿着这份数据按步骤就能用 yolov5 训练自己的数据集并高质量完成验证。适合刚入门目标检测的从业者也适合想快速弄懂标注数据和训练流程之间关系的人。需要说明的是所谓 “4 类别”常见指裂缝、坑槽、修补、龟裂但具体哪一个 class ID 对应哪个名称永远以 labels 目录中的 txt 内容为准而不是数据集介绍里的文字。拿到手先打开 labels/train 下任意一个 txt看一眼数字后面所有脚本都会围绕这个真实格式展开。2. 拆开目录说话YOLOV5 的 images/labels 怎么装 4 类路面缺陷2.1 images 与 labels 目录约定一张 jpg 配一个同名 txtYOLOV5 的数据集目录约定比 COCO 那种“一个大 json 装全部标注”的方式更直接COCO2017 要把 json 转成 txt 才能训练而 YOLO 格式本身就是每张图一个标注文件目录结构长这样dataset/ ├── images/ │ ├── train/ │ │ ├── road_0001.jpg │ │ ├── road_0002.jpg │ │ └── ... │ └── val/ │ ├── road_0101.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── road_0001.txt │ │ ├── road_0002.txt │ │ └── ... │ └── val/ │ ├── road_0101.txt │ └── ... └── data.yamlimages/train 与 labels/train 是一一对应的同名文件不同扩展名。图片可以用 jpg、png标签一律是 txt。凡是对不上的要么这张图没有标注要么标注没有对应图train.py 在加载时会发出警告并跳过。训练时会出现“WARNING: corrupt JPEG”或者“No labels found”多数就是这么来的。这里有一个很容易忽略的细节labels 目录里不能出现多余的子目录或临时文件。有些人习惯把原始标注工具的导出文件放在 labels 边上结果 yolov5 在 glob 时把不该读的文件也读进去了报错信息还很抽象。常见做法是数据集根目录只留 images、labels 和 data.yaml 三样先用一个干净目录把格式立住。2.2 txt 标注逐行拆解类 ID、归一化坐标与最容易抄错的顺序打开任意一个 txt内容大概是这样0 0.516016 0.632812 0.075781 0.095703 2 0.741797 0.441406 0.117188 0.087891 1 0.322266 0.550781 0.047656 0.033203每一行代表一个目标框5 个字段之间用空格分隔顺序固定class_id x_center y_center width height。注意是中心点坐标不是左上角和右下角所有值都除以了图片宽高做了归一化范围在 0 到 1 之间。这个顺序一旦抄错模型也能训练loss 也能降但画出来的框和真实缺陷永远对不上。写一个解析函数能把 YOLO 行转回像素坐标方便人眼检查def parse_yolo_line(line, img_w, img_h): 把 YOLO txt 的一行文本解析成左上角/右下角的像素坐标 line: 0 0.516016 0.632812 0.075781 0.095703 parts line.strip().split() if len(parts) ! 5: raise ValueError(f标注字段数量不是 5: {line}) cls_id, x_c, y_c, w, h [float(v) for v in parts] # 中心点宽高格式先算左上角再算右下角 left int((x_c - w / 2) * img_w) top int((y_c - h / 2) * img_h) right int((x_c w / 2) * img_w) bottom int((y_c h / 2) * img_h) return int(cls_id), (left, top, right, bottom)参数说明x_c、y_c是目标中心点相对图片宽高的比例w、h是目标宽高相对图片宽高的比例。解析时先把line用空白字符拆成 5 段再逐段转 float。如果字段数不是 5直接抛异常方便批量检查时抓到坏行。常见错误有两种一是把x_c - w / 2理解成横坐标偏移量忘了乘以img_w和img_h导致框全部堆在左上角一小块二是把w和h当成像素值直接用导致框巨大。下面这张字段表可以贴在手边对照字段含义取值范围class_id类别序号0 到 nc-1本数据集为 0~3x_center框中心点横坐标 / 图片宽0.0 ~ 1.0y_center框中心点纵坐标 / 图片高0.0 ~ 1.0width框宽 / 图片宽0.0 ~ 1.0height框高 / 图片高0.0 ~ 1.0如果 txt 里出现了大于 3 的类 ID说明这份数据的类别定义和 yolov5 的 nc4 对不上训练不会崩但类别 loss 会一片混乱。正确做法是先看完所有 txt 再写 data.yaml而不是先写 names 再反过来猜 ID。3. 训练前先做数据体检统计类别分布与可视化标注框3.1 统计类别分布数清每个类的框数和坏文件拿到数据集先不要急着配环境而是写一个体检脚本把三类信息统计出来每个类别有多少个框、每个 txt 是否恰好 5 个字段、labels 与 images 是否一一对应。这个脚本 5 分钟就能写完却能在训练前解决一半以上的“玄学翻车”。import os from glob import glob label_dir dataset/labels/train class_counter {} bad_files [] txt_files sorted(glob(os.path.join(label_dir, *.txt))) for txt_path in txt_files: with open(txt_path, r, encodingutf-8) as f: lines f.readlines() for line in lines: line line.strip() if not line: continue parts line.split() if len(parts) ! 5: bad_files.append((txt_path, 字段数不为5: line)) continue cls_id parts[0] class_counter[cls_id] class_counter.get(cls_id, 0) 1 print(类别分布:, class_counter) print(坏文件数量:, len(bad_files)) for item in bad_files[:10]: print(item)逻辑说明先把所有 txt 路径收集起来排序再逐行解析。遇到空行直接跳过遇到字段数不对的行记进坏文件列表不影响整体统计。最后打印类别分布和坏文件前 10 条用来判断数据集质量。运行完看两个指标。第一class_counter里 4 个类是否都有数据如果某一类只有十几个框而其他类几千个框后面的 mAP 对这一类会非常难看。第二bad_files不为空时先修数据再训练。txt 里字段不足 5 个最常见的原因是标注工具导出时把类别名写成了字符串而不是 ID或者某一行坐标数据被截断。还要补一个 images 与 labels 的对应检查简单做法是img_files set(os.path.splitext(os.path.basename(p))[0] for p in glob(dataset/images/train/*.jpg)) label_files set(os.path.splitext(os.path.basename(p))[0] for p in glob(dataset/labels/train/*.txt)) print(有图无标注:, len(img_files - label_files)) print(有标注无图:, len(label_files - img_files))如果“有图无标注”数量很大先确认是本来就该有空标注文件还是文件名后缀大小写不一致比如.JPG和.jpg。YOLOV5 在部分系统上对大小写敏感.JPG的图可能找不到对应 txt训练时静默跳过。3.2 用可视化脚本把标注框画在原图上确认归一化坐标没写反统计只能说明“数量对”不能说明“位置对”。归一化坐标算错的情况下数字本身都是合法的类别分布也正常只有把框画到图上才能暴露问题。这里写一个最小可视化脚本from PIL import Image, ImageDraw import os def draw_boxes(img_path, txt_path, out_path): img Image.open(img_path).convert(RGB) draw ImageDraw.Draw(img) with open(txt_path, r, encodingutf-8) as f: lines f.readlines() img_w, img_h img.size for line in lines: cls_id, x_c, y_c, w, h [float(v) for v in line.strip().split()] x1 int((x_c - w / 2) * img_w) y1 int((y_c - h / 2) * img_h) x2 int((x_c w / 2) * img_w) y2 int((y_c h / 2) * img_h) color red if int(cls_id) 0 else yellow draw.rectangle([x1, y1, x2, y2], outlinecolor, width2) img.save(out_path) os.makedirs(vis, exist_okTrue) for name in os.listdir(dataset/images/train)[:50]: if not name.endswith(.jpg): continue stem os.path.splitext(name)[0] txt_path os.path.join(dataset/labels/train, stem .txt) if os.path.exists(txt_path): draw_boxes( os.path.join(dataset/images/train, name), txt_path, os.path.join(vis, name), ) print(可视化结果已保存到 vis/ 目录)逻辑说明draw_boxes复用了解析逻辑把每个 txt 里的目标框以矩形画到原图上按类别换颜色便于区分。这里故意用ImageDraw.rectangle而不是 OpenCV 画图因为 PIL 读 jpg 更省内存批量跑几百张也不会卡。vis目录下生成的前 50 张图抽查一圈就够了。画完之后重点看两类异常。一种是框的位置明显偏移比如裂缝明明在道路中央框却画到路边这说明x_center或y_center的归一化除错了分母。另一种是框的宽高比极端比如细长裂缝的框宽高比达到 10:1这本身合法但会影响 yolov5 的 anchor 匹配后面训练时要考虑提高输入分辨率。可视化脚本在数据集更新后可以反复跑它比看统计数据更能暴露标注质量问题。4. 用 yolov5 训练路面缺陷模型划分数据集、写 data.yaml、跑第一个 epoch4.1 按文件名划分 train/val不做随机抽样而是先打乱再切路面数据的特殊性在于同一路段拍摄的照片高度相似如果直接按文件顺序前 90% 做训练后 10% 做验证很可能训练集和验证集来自不同路段指标虚高或者虚低。常见做法是先打乱文件名再按比例切分保证每类缺陷在两边都出现。import os import random from glob import glob image_dir dataset/all_images label_dir dataset/all_labels train_ratio 0.9 random.seed(42) all_imgs sorted(glob(os.path.join(image_dir, *.jpg))) random.shuffle(all_imgs) split_idx int(len(all_imgs) * train_ratio) os.makedirs(dataset/images/train, exist_okTrue) os.makedirs(dataset/images/val, exist_okTrue) os.makedirs(dataset/labels/train, exist_okTrue) os.makedirs(dataset/labels/val, exist_okTrue) for img_path in all_imgs[:split_idx]: base os.path.basename(img_path) label_path os.path.join(label_dir, os.path.splitext(base)[0] .txt) os.rename(img_path, os.path.join(dataset/images/train, base)) if os.path.exists(label_path): os.rename(label_path, os.path.join(dataset/labels/train, os.path.basename(label_path)))逻辑说明先用sorted保证初始顺序确定然后用random.shuffle打乱random.seed(42)保证每次运行划分结果一致便于复现问题。split_idx是训练集和验证集的分界下标之前进 train之后进 val。循环里通过os.path.exists判断 txt 是否存在避免移动不存在的文件时报错。这里有两个容易踩的细节。一是训练集比例路面缺陷检测样本量通常不大9:1 比 8:2 更稳妥如果验证集只有几十张mAP 波动会很大看曲线时容易误判。二是移动而不是复制避免两个目录下出现同名文件导致 yolov5 读取时产生歧义。如果原始目录还要保留改成shutil.copy2即可。4.2 写 data.yamlnames 顺序必须和 txt 里的 class_id 一致data.yaml 是 yolov5 唯一需要你手工维护的数据配置文件内容很少但错一个字母训练就会跑偏train: ./dataset/images/train val: ./dataset/images/val nc: 4 names: 0: crack 1: pothole 2: repair 3: alligator这里面最容易出问题的不是 nc而是 names 的顺序。txt 里写的是0 0.516016 ...模型就会把第 0 类当成 crack 来学如果把 names 写成pothole在第 0 位模型训练的监督信号和实际标注就完全错位了而训练过程不会报任何错最后推理出来的框类别全是乱的。路径写法上train和val推荐用相对路径相对的是你执行python train.py时所在的目录。写绝对路径虽然第一次能用但数据集换目录或者换机器后必翻车。yolov5会把./dataset/images/train拼到当前工作目录下确保 train.py 运行时pwd在 yolov5 项目根目录即可。4.3 启动训练批次、分辨率和预训练权重的选择环境配置完成后训练命令写成这样python train.py --img 640 --batch 16 --epochs 100 \ --data dataset/data.yaml --weights yolov5s.pt \ --name road_defect参数含义和路面缺陷场景的适配建议如下--img 640是训练分辨率。裂缝是典型的细长小目标640 分辨率下一条几十像素宽的裂缝在特征图里可能只占几个格子。如果显存允许可以提到 800提分辨率比调任何超参数都更能直接提升小目标召回。--batch 16取决于显存大小batch 过小比如 4会让 BN 层的统计量不稳定损失曲线会出现明显毛刺8~16 是多数 8GB 显存卡的安全范围。--epochs 100对几千张到几万张的路面数据足够收敛数据量小可以加到 150配合早停机制防止过拟合。--weights yolov5s.pt是从 COCO 预训练权重开始迁移学习s 版本速度快、显存占用低适合第一轮跑通如果数据量超过 1 万张再换yolov5m.pt或yolov5l.pt。训练时的关键观察点不是看 loss 绝对值而是看三类日志第一是train box loss是否平稳下降第二是val mAP0.5是否在 30 个 epoch 内开始爬升第三是训练结束时Best权重是否出现在最后一个 epoch 附近。如果最佳权重始终出现在第 10 个 epoch 之前说明模型早就过拟合了此时要加数据增强或者降低训练轮数。第一轮训练不要动 hyp 超参数等跑完 100 个 epoch 再看哪些指标拖后腿。5. 避坑记录用这批 4 类数据跑 yolov5 时的 6 个常见问题5.1 标签全部丢失训练提示 No labels found现象训练刚开始几秒钟就打印出train: No labels found in /xxx/dataset/images/train后面直接退出或者训练时没有 loss。原因data.yaml 里的train路径写成了 images 目录但 yolov5 会根据同一个路径前缀去替换images为labels。如果目录名不叫 images 和 labels或者 data.yaml 里写的是绝对路径且换过机器标签就找不到。解决回到第二章的目录树把数据集根目录下的目录名严格改成images和labels并且确认 data.yaml 里写的是 images/train 而不是 images 本身。执行ls dataset/labels/train看看到底有没有 txt没有就先从原始标注里生成。5.2 中途卡住报 corrupt JPEG现象训练进度条长时间停在某一张图上终端出现UnidentifiedImageError: cannot identify image file或者OSError: image file is truncated。原因数据集从网盘或旧硬盘拷贝时某张 jpg 文件损坏PIL 打开读到一半就抛异常。这种情况在整个数据集中往往只有几张但会让训练中断。解决写一个删除坏图脚本打开失败就同时删掉 jpg 和对应的 txt不要只删图片否则 labels 目录会有残留文件导致新的对不齐。脚本可以用 PIL 的Image.open加load()强制读完全部数据捕获异常后os.remove。5.3 裂缝小目标漏检mAP 上不去现象loss 一直在降mAP0.5 停在 0.6 左右可视化结果里 pothole 和 repair 都能检测出来唯独裂缝经常漏掉或者同一个裂缝只检测到一半。原因裂缝是细长条目标在 640 分辨率下面积占比极小特征图下采样后可能只剩几个像素。另一个原因是标注框为了包住整条裂缝把宽高比拉得很极端而 yolov5 的默认 anchor 对极端宽高比覆盖不足。解决优先把--img调到 800 或 960输入端分辨率提高后小目标的像素占比直接变大。其次在训练日志里观察 autoanchor 打印的 k-means 结果如果新算出的 anchor 和默认 anchor 相差一倍以上说明默认 anchor 不匹配当前数据分布可以保留 autoanchor 自动适配不要开--noautoanchor。5.4 pothole 类别数据太少mAP 曲线抖动剧烈现象类别分布统计里 pothole 只有几百个框其他三类各有几千个训练时 pothole 的 AP 在 0.2 到 0.5 之间大幅波动。原因类别不平衡在路面缺陷数据里几乎是必然的坑槽本来就比裂缝罕见。yolov5 默认的 cls loss 系数对所有类一视同仁少数类学不到位。解决先做样本层面的过采样把 pothole 的图片在训练集里复制两到三份注意不要复制到验证集里否则指标虚高。然后在 yolov5 的 hyp 文件中调大cls系数从默认 0.5 提到 0.8 左右观察效果。最后一步才是换更大的模型数据不平衡靠模型容量解决是事倍功半。5.5 打开 cache 后第二次训练报错rebuild cache 也不行现象第一次训练加--cache正常后来删了几张坏图和对应标签第二次训练启动时报 cache 相关的解析错误删除 cache 目录后还是有残留问题。原因yolov5 会把标注信息缓存成.cache文件放在 labels 目录下。数据集变动后缓存没有自动失效训练时读到旧的缓存条目再去找对应的图片文件已经不存在了。解决把 labels/train 和 labels/val 下的*.cache文件全部删掉重新训练让它重建缓存。如果项目里用的是--cache ram删掉缓存文件的同时也要重启 Python 进程避免内存里还驻留着旧缓存对象。5.6 换一台机器训练标签路径全部失效现象代码在 A 机器上跑通git 同步到 B 机器后同样一份数据训练时找不到 labels或者加载的图片路径变成了 A 机器的用户名目录。原因data.yaml 里写了绝对路径例如/home/userA/dataset/images/train换机器和换用户名后路径前缀对不上。yolov5 在拼接路径时不会做存在性校验直接按字符串拼导致标签目录解析失败。解决把 data.yaml 的 train 和 val 改成相对路径执行 train.py 之前先cd到 yolov5 项目根目录保证相对路径的基准一致。如果需要多台机器共用同一份数据可以用环境变量把数据集根目录注入 data.yamltrain: ${DATASET_ROOT}/images/train val: ${DATASET_ROOT}/images/val然后在启动脚本里export DATASET_ROOT/path/to/dataset。这样切换机器只需要改环境变量不用改 yaml。6. 验证与进阶用 mAP 和可视化推理判断模型是否真正可用6.1 验证命令与判读指标训练结束后第一件事不是看 tensorboard 的截图而是用验证集跑一次完整的评估python val.py --data dataset/data.yaml \ --weights runs/train/road_defect/weights/best.pt \ --img 640输出结果里重点看每个类的 AP。四个类中 crack 的 AP 偏低是常态如果其他三类 AP 都在 0.8 以上而 crack 只有 0.5基本可以断定是裂缝标注框和实际裂缝轮廓偏差过大而不是模型能力不足。此时回头修标注比换模型有效得多。6.2 进阶把推理可视化当作最后的验收手段mAP 指标只能说明框位重合度不能说明检测结果对工程是否有用。路面缺陷检测的落地场景里维护人员更关心“裂缝在哪里”而不是“框有多准”。跑一下推理python detect.py --weights runs/train/road_defect/weights/best.pt \ --source test_imgs/ --img 640 --save-txt --save-conf把输出图片按类别分目录放好逐张看漏检和误检。裂缝漏检多就回到上一节调分辨率pothole 和 crack 互相混淆多半是标注框在类别边界上不清晰。我自己的习惯是每次新拿到一批数据第一件事永远是数类别、画框不做完这两步绝不碰 train.py。这套流程看着笨但能省下至少一个通宵的排查时间。希望这份数据格式和避坑记录能让你少踩几个已经替你踩过的坑。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站