资讯动态

Labelme格式路面缺陷数据集处理与YOLO训练避坑指南

发布时间:2026/10/21 20:18:52 来源:尧图企业网站定制
简介沥青路面缺陷目标检测数据集part1包含2000张标注图片对应2000个JSON标注文件采用Labelme工具制作压缩包整体大小为563.3MB7z格式。该子集针对道路养护领域数据稀缺和类别不平衡的痛点提供了裂缝、裂缝修补、坑洞、坑洞修补、井盖及其他共6类缺陷的精细化标注能够直接用于目标检测模型训练、验证与算法调优。适用对象包括智慧交通研究人员、道路巡检系统开发者、高校人工智能实验室及养护单位技术团队可用于构建路面缺陷识别基线、评估主流检测框架以及扩充自有数据集。资源文件与图片名称一一对应目录清晰便于按类别和场景检索配合全量6000张标注数据规划可支持多阶段训练和类别均衡策略研究。目前已有385人学习/下载对于希望快速获得高质量路面缺陷样本的从业者这是一份可直接落地到实际项目中的数据集资源。1. 6000张labelme格式的沥青路面缺陷数据集值不值得接先说一个反直觉的结论对路面缺陷目标检测这类任务模型结构能拉开的差距远没有标注质量能拉开的差距大。很多团队第一步就栽在数据上——检测车跑一天能采几百GB路面图像但最后让模型翻车的往往是标注格式不统一、裂缝目标太小、类别定义打架这类问题。这个沥青路面缺陷目标检测数据集提供的是labelme格式的多边形标注6000张的量级对目标检测来说不算大但足够检验一套训练链路是否健康。它面向的是做道路智能巡检、养护管理系统、无人机路面扫描数据处理的从业者核心诉求是把这批数据变成能稳定检出裂缝、坑槽、修补等缺陷的模型而不是在模型结构上反复试错。拿到数据后的第一件事不是训练而是做数据体检看目录结构、标注格式、类别分布和框尺寸分布。这四样东西直接决定了后续训练是顺利收敛还是反复返工。这篇笔记按我自己的处理路径展开先讲labelme格式里每个字段的用途再给类别设计建议然后给出格式转换脚本、训练集划分策略、5个高频坑最后说怎么用交叉验证判断这批数据真实质量。2. 拆开数据集目录结构、labelme标注格式与类别设计要点拿到手的第一步动作是先把目录树打出来看一眼不要急着写训练脚本。一个标准的labelme目标检测数据集通常长这样。tree 可以列出。目录里每一张jpg对应一个同名json文件json里存的是标注信息。如果json文件不在图片同目录或者文件名对不上后面转换时一定会出问题。2.1 labelme的json里到底存了什么labelme保存的标注信息是json格式结构并不复杂但很多人没仔细看过里面的坐标是绝对像素值而不是归一化值。这一点会在转YOLO格式时坑到你。# 直接看单个标注文件的字段 python -c import json with open(000001.json, r) as f: data json.load(f) print(data.keys()) print(data[shapes][0]) 输出会显示shapes是一个列表每个元素包含label、points、shape_type等关键字段。points是构成多边形的绝对像素坐标点列表shape_type多数是polygon也可能是rectangle或circle。label是缺陷类别名比如横向裂缝、坑槽这些。imagePath字段存的是对应图片文件名imageWidth和imageHeight存在data里但有些旧版本labelme生成的json不带这个字段后面写转换脚本时要兼容这种情况。# 缺失imageWidth/imageHeight时用PIL从原图读取宽高 python -c from PIL import Image img Image.open(000001.jpg) print(img.size) # (width, height) 逻辑说明转换脚本最怕json字段不完整。labelme版本不同输出的json字段有差异旧版本可能缺imageWidth和imageHeight新版本则正常。上面这段代码是为了确认字段情况如果缺宽高字段转换时就要用PIL去原图里读否则归一化坐标算不出来。参数说明imagePath在labelme保存时会自动写入但如果json文件被人为移动过目录imagePath可能失效校验时要注意这个字段的值是否和实际文件名一致。2.2 沥青路面缺陷怎么定类别才不打架6000张图要覆盖沥青路面的主要缺陷类别设计必须具体且互斥。拿常见的分类来举例横向裂缝和纵向裂缝必须分开网状裂缝单独一类坑槽、修补、松散各算一类。不建议把裂缝笼统地归成一个大类原因很直接横向裂缝和纵向裂缝在形态学上差异巨大训练出的模型关注的特征完全不同而网状裂缝是裂缝交织成网特征与单独的横纵裂缝差异又更大。# 类别清单建议按这个顺序建立类别映射 classes [ 横向裂缝, 纵向裂缝, 网状裂缝, 坑槽, 修补, 松散, 车辙 ]逻辑说明类别定义越细模型学到的特征越稳定。但类别也不是越多越好——6000张图如果分7个类某个类可能只有几百张样本不平衡问题会直接影响mAP。用上面这个清单前先统计每个类的标注数量如果某个类别低于200个实例建议合并到相近类别里等后续批次补充数据再单独开类。参数说明建议至少包含横向裂缝、纵向裂缝、网状裂缝、坑槽这四个核心类。修补、松散、车辙这类的语义边界需要标注人员严格按统一标准执行否则同一个缺陷不同人标出两个类模型直接学乱。2.3 用脚本盘点数据图片数量、标注密度、框尺寸分布训练前先做数据体检。统计每张图片的标注数量、每个类别的实例数量、每个目标框的宽高比这批数据质量到底如何靠这几组数字就能看出来。# 统计各类别数量与框尺寸分布 import json import glob from collections import Counter json_files glob.glob(labels/*.json) cat_counter Counter() box_sizes [] for jf in json_files: with open(jf, r, encodingutf-8) as f: data json.load(f) for shape in data[shapes]: cat_counter[shape[label]] 1 xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] w max(xs) - min(xs) h max(ys) - min(ys) box_sizes.append((w, h)) print(cat_counter) print(平均框宽高:, sum(w for w, h in box_sizes) / len(box_sizes), sum(h for w, h in box_sizes) / len(box_sizes))逻辑说明这个脚本会输出每个类别的实例总数以及所有目标框的平均宽高。如果某个类别只有几十个实例这个类基本等于废了模型学不出来。如果平均宽高比特别悬殊比如宽度均值500而高度均值只有20说明裂缝这类细长目标占绝大多数训练时要考虑用无anchor模型或调整anchor配置。参数说明在glob的路径里labels目录放的是json文件路径实际使用时改成数据所在目录即可。框尺寸统计不建议直接打印所有值数据多时print会很卡先看均值和分布再决定要不要逐条分析。2.4 标注质量抽检可视化叠加检查与边界定义统计脚本只能发现数量问题标注是否贴边还得靠人工抽检。把json里的多边形在图上画出来生成一批可视化文件快速浏览比对着数字判断直观得多。# 随机抽20张图叠加标注框 import random import json import glob from PIL import Image, ImageDraw json_files glob.glob(labels/*.json) random.seed(42) sample random.sample(json_files, 20) for jf in sample: with open(jf, r, encodingutf-8) as f: data json.load(f) img_path data[imagePath] img Image.open(img_path).convert(RGB) draw ImageDraw.Draw(img) for shape in data[shapes]: points [(p[0], p[1]) for p in shape[points]] draw.polygon(points, outline(255, 0, 0), width3) draw.text((points[0][0], points[0][1] - 10), shape[label], fill(0, 255, 0)) out_name check_ img_path.split(/)[-1].split(.)[0] .jpg img.save(out_name)逻辑说明抽检的核心不是看标注漂不漂亮而是看三个问题一是边界是否紧贴缺陷边缘二是类别语义是否统一三是有没有漏标。抽检过程要把json里的imagePath解析出来所以直接按data里的字段打开原图即可。参数说明代码里random.seed(42)固定了随机种子保证每次抽检的样本一致方便多人复核同一批结果。如果json里imagePath写的是相对路径且和当前工作目录不匹配img.open会报错处理方式是拼接json所在目录的路径前缀再打开。3. labelme标注的3个规范贴边、最小目标标定与语义隔离拿到外部数据集和自采数据跑流程有一个区别外部数据已经标好第一轮质检的精力应该花在校验标注边界和语义一致性上。标注规范看起来是给标注员看的实际上写转换脚本之前必须先用规则定义好合格标注的标准否则转换完了才发现问题返工成本更高。3.1 标签语义隔离把修补和松散分开沥青路面的缺陷经常混在一起比如坑槽修补后表面又出现松散。这种情况下标注是标修补还是标松散会影响模型学到的特征。规范做法是如果该区域的缺陷状态描述为修补过后的现状统一标修补如果坑槽边缘正在形成碎裂缺陷区域整体标坑槽碎裂细节不再单独标。这样能避免同一区域出现重叠的多个框。# 检测同一图像中同一位置是否存在重复标注 import json def has_overlap(shape_a, shape_b): # 简单判断两个框的中心点和面积重叠情况 def bbox(shape): xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] return min(xs), min(ys), max(xs), max(ys) ax1, ay1, ax2, ay2 bbox(shape_a) bx1, by1, bx2, by2 bbox(shape_b) ix1, iy1 max(ax1, bx1), max(ay1, by1) ix2, iy2 min(ax2, bx2), min(ay2, by2) if ix2 ix1 or iy2 iy1: return False inter (ix2 - ix1) * (iy2 - iy1) area_a (ax2 - ax1) * (ay2 - ay1) return inter 0.3 * area_a with open(某图像标注.json, r, encodingutf-8) as f: data json.load(f) for i in range(len(data[shapes])): for j in range(i 1, len(data[shapes])): if has_overlap(data[shapes][i], data[shapes][j]): print(发现重叠标注:, i, j)逻辑说明重叠区域的判断标准是交并面积大于较小框面积的30%。这样做能把阴影下裂缝与坑槽边界重叠严重的情况找出来标注人员复核后手动移除多余框。参数说明0.3这个阈值可调如果数据里细长裂缝特别多这个阈值可以放宽到0.2避免大量误报。另外这个脚本只作检查不自动修改避免误删有效标注。3.2 细长裂缝的目标框怎么画取外接矩形还是多边形labelme默认支持多边形标注很多标注员会把裂缝的轮廓画得特别精细一个裂缝能标几十个点。这对检测模型来说反而可能是负担较新的YOLO系模型输出的是矩形框多边形最终还是要转成外接矩形而除了带旋转框的检测头细长裂缝用多边形标注和矩形标注转出的目标框几乎一样精细化轮廓不会带来精度收益。因此规范建议直接给裂缝画最小外接矩形即标4个点形成矩形框紧贴缺陷边缘。# 把精细多边形转成外接矩形的示例逻辑 import json def polygon_to_bbox(points): xs [p[0] for p in points] ys [p[1] for p in points] return [min(xs), min(ys), max(xs), max(ys)] with open(标注示例.json, r, encodingutf-8) as f: data json.load(f) for shape in data[shapes]: if shape[shape_type] polygon: bbox polygon_to_bbox(shape[points]) print(shape[label], bbox)逻辑说明这段代码只是演示外接矩形的计算逻辑实际训练数据通常不需要你真的去改json。它是给转换脚本用的基础函数后面的YOLO格式转换会直接复用这个计算方式。参数说明外接矩形的坐标是图片上的绝对像素值转YOLO格式时需要进一步归一化这一步放在第4章的转换脚本里做。3.3 质检环节的脚本化重名标签、空标注、零尺寸框检测标注文件里最容易出现的三个问题同一个类写了两种写法比如横向裂缝和横向裂缝 带有空格或全角字符某张图一个标注都没有json里shapes为空某个框的宽或高为0即标注退化成了点。# 批量扫描json中的标注异常 import json import glob issues [] for jf in glob.glob(labels/*.json): with open(jf, r, encodingutf-8) as f: data json.load(f) if len(data[shapes]) 0: issues.append((jf, 空标注)) continue for shape in data[shapes]: xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] if max(xs) - min(xs) 1 or max(ys) - min(ys) 1: issues.append((jf, 零尺寸框, shape[label])) for item in issues: print(item)逻辑说明零尺寸框虽然少见一旦混进训练集标签归一化时可能出现除零或无效框问题导致训练loss出现nan。空标注图片需要单独处理可以保留但转换时跳过也可以选择删除。参数说明如果这是个多类别的json每个类别还存在一个名称归一化的问题例如横向裂缝与横向裂缝 这种不可见字符差异肉眼很容易忽略。建议在转换脚本里统一对类别名做strip后再做映射。4. 从labelme到训练集格式转换脚本与划分策略标注规范校验通过后进入最关键的格式转换环节。labelme的json毕竟不是主流检测框架直接消费的格式要把多边形转成YOLO格式的txt标签文件或者转成COCO格式的json二选一。4.1 批量生成标注转YOLO格式的最小脚本YOLO格式的标签文件是txt每行内容为类别id x_center y_center width height所有坐标值都是归一化到0到1的浮点数。类别id就是类别清单里的下标。转换过程中labelme的绝对坐标要除以图片宽高归一化还要把多边形转为外接矩形。import json import glob import os from PIL import Image classes [横向裂缝, 纵向裂缝, 网状裂缝, 坑槽, 修补, 松散, 车辙] class_to_id {name: i for i, name in enumerate(classes)} json_files glob.glob(labels/*.json) for jf in json_files: with open(jf, r, encodingutf-8) as f: data json.load(f) # 读取图片宽高json里没有就用PIL读取 if imageWidth in data and imageHeight in data: img_w, img_h data[imageWidth], data[imageHeight] else: img_path os.path.join(os.path.dirname(jf), data[imagePath]) try: with Image.open(img_path) as img: img_w, img_h img.size except Exception: print(跳过无法读取宽高的文件:, jf) continue out_lines [] for shape in data[shapes]: label shape[label].strip() if label not in class_to_id: print(未知类别:, label, 位于, jf) continue xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 边界裁剪防止框超出图像范围 x_min max(0, min(x_min, img_w)) y_min max(0, min(y_min, img_h)) x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / img_h box_w (x_max - x_min) / img_w box_h (y_max - y_min) / img_h # 过滤零尺寸框 if box_w 1e-6 or box_h 1e-6: continue line f{class_to_id[label]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f} out_lines.append(line) base os.path.basename(jf).replace(.json, ) out_path flabels_yolo/{base}.txt with open(out_path, w, encodingutf-8) as f: f.write(\n.join(out_lines) \n)逻辑说明这个脚本把labelme的绝对像素坐标转成了YOLO所需的归一化中心点坐标和宽高。核心有三处一是读取图片宽高二是多边形转外接矩形时需要把坐标限制在图像范围内三是空标注图片会写出一个空txt文件本身不报错但训练时需要对应配置允许空标签。参数说明类别清单classes的顺序决定了每个类别的数字id。如果与训练配置里的data.yaml不一致模型类别对应关系会全部错位。建议训练前先把classes写入data.yaml的names字段。边界裁剪用的是clip方式对个别超出图像边界十几个像素的坐标做拦截防止训练时读取标签崩溃。4.2 训练/验证/测试的划分按路段分组而不是随机分划分数据集时常见的错误是随机打乱图片后按比例分割。路面图像有一个特殊性相邻帧之间高度相似同一个路段的多张图片在被检测目标上几乎没有差异。随机划分会把同一路段的图像同时分到训练集和验证集验证指标虚高现场部署时效果立刻掉下来。正确做法是按采集批次或按路段分组把同一路段的图像全部分到同一个集合里。假设这批数据来自多个采集批次先从json文件名里提取采集批次信息再按批次划分。# 按采集批次划分数据集的示例 from collections import defaultdict import random random.seed(42) def parse_batch(filename): # 这里按你的命名规则改比如图片名前6位是批次号 return filename.split(_)[0] batches defaultdict(list) for img in all_images: b parse_batch(img) batches[b].append(img) batch_list list(batches.items()) random.shuffle(batch_list) train_batches batch_list[:int(len(batch_list) * 0.8)] val_batches batch_list[int(len(batch_list) * 0.8):int(len(batch_list) * 0.9)] test_batches batch_list[int(len(batch_list) * 0.9):]逻辑说明按批次划分的代价是单个集合内部的图像分布略有偏差但换来的是评估指标真实可靠这比好看的数字重要得多。没有批次信息的可以按文件名的连续段划分比如每连续200张图算一个路段取整个路段放入同一集合。参数说明划分比例建议8:1:16000张图拆下来训练集约4800张。如果发现训练集类别分布确实偏了可以通过调整批次的分组粒度再做平衡。4.3 数据增强策略Mosaic与细长目标的翻转裁剪6000张原始图对目标检测来说偏少。数据增强尤其关键但路面缺陷的黑匣子在于很多增强方式对普通目标有效对细长裂缝无效甚至有害。Mosaic增强会同时拼接4张图相当于变相增大batch size同时把细小目标缩小对裂缝这种本来就小的目标不利。一种折中是保留Mosaic的概率降低到0.2并让增强后的图尽量不缩小目标。左右翻转安全上下翻转在路面图像里也基本安全因为无人机或检测车的拍摄姿态通常是俯视的。随机色彩抖动要控制幅度沥青路面色调本身比较单一过于剧烈的色调变化会导致模型去学颜色特征而现场不同路段的路面色差很大颜色过拟合是常见问题。细长目标的裁剪增强值得做把宽高比大于5的目标单独做随机裁剪再放大到固定尺寸重新训练一个辅助检测头能显著提升裂缝召回率。5. 训练前必须排掉的5个坑我全部踩过一遍数据转换完成不代表可以放心的开始训练。以下5个问题是我在类似的数据集上反复踩过的按出现频率排序。5.1 裂缝目标太小默认anchor全部失效现象训练正常推进loss稳定下降但val mAP_50只有0.1几乎等于随机猜测。原因沥青路面裂缝宽度通常只有几个像素到十几个像素目标框面积占整张图的比例可能只有0.1%到0.5%。YOLO系模型默认的anchor尺寸是按COCO等常规数据集设计的最小的anchor也远大于裂缝框。解决换用anchor-free的检测头例如RTMDet、YOLOv8自带的anchor-free分支。如果只能用anchor-based模型就根据第2章统计的框尺寸分布聚类出适合的anchor。用下面的思路手动算一组anchor尺寸替代默认值。# 用k-means统计目标框聚类尺寸 import numpy as np from sklearn.cluster import KMeans boxes np.array(box_sizes) # 每行为[w, h] kmeans KMeans(n_clusters6, random_state42, n_init10).fit(boxes) print(kmeans.cluster_centers_)逻辑说明聚类的输出是6个anchor的宽度和高度。检查这些anchor是否包含大量宽高比大于10的框如果存在说明这类极端细长目标是主要分布建议用MOTA类的旋转框检测模型或者接受漏检一部分特别长的裂缝。参数说明n_clusters6是常见设置设置太大训练速度下降且增益很小设置太小对形状差异大的目标覆盖不足。生成anchor后要按面积从大到小排序再填进模型配置。5.2 标注框宽高比太极端loss不降反升现象训练2个epoch后loss出现周期性尖峰不容易收敛。原因裂缝类目标框宽度和高度差距可以达到50倍以上模型回归分支要同时预测这么大的数值差异梯度不稳定。回归loss中w和h的损失权重在极端宽高比下会失衡。解决用CIoU loss训练它对宽高比差异有一定的鲁棒性另外可以先把w和h做log变换再回归让模型预测log(w)和log(h)而不是直接预测宽高。在YOLOv8的配置里找到box_loss_weight对细长目标可以适当降低让分类分支权重更高。5.3 类别id错位训练时报IndexError现象训练刚开始就报IndexError提示索引超出类别数量。原因转换脚本里的classes顺序和模型训练配置data.yaml里的names顺序不一致。转换脚本里classes列表和data.yaml必须一字不差地对齐。解决检查两个文件的类别顺序尤其是出现二进制文件编码差异的场景。建议把类别清单单独存成一个文件比如class_names.txt转换脚本和data.yaml同时引用同一个文件从源头避免错位。5.4 同一路段图像泄漏到train和val指标虚高现象val mAP_50达到0.9以上部署到现场采集的新图后mAP直接掉到0.5。原因随机划分数据集导致同一路段的多帧图像同时出现在训练集和验证集。模型实际上记住了路段纹理而不是学会识别缺陷。解决按第4章的做法按路段划分为好。验证集和测试集个别指标差异大没关系关键是看现场新增图像的泛化表现。如果新增图像来自不同的采集设备这个差距只会更大。5.5 沥青纹理噪声大模型学到的是路面颗粒而不是裂缝现象训练得到的模型在验证集上表现尚可但仔细观察检测结果部分裂缝没有检出反而把细小石子阴影当成裂缝。原因沥青路面的粒状纹理在低对比度图像中和细小裂缝高度相似模型被高频纹理噪声带偏。解决这个坑训练前靠标注训练后靠验证——标注时要求框必须紧贴裂缝边缘不要包含大块背景另外在训练数据里混入一些负样本图像即完全没有缺陷的路面图可以有效压低误检率。如果数据集里没有负样本图从原图中裁出无缺陷区域单独做成负样本集。6. 怎样用这批数据做一次可靠的验证交叉验证与难样本复核模型迭代到这个阶段需要回到数据的角度再做一次全面体检这一步能确认6000张图够不够继续投入。一个简单可靠的做法使用已经切好的训练集和验证集做交叉验证再对模型漏检和误检的样本做二次人工复核。如果发现某个类别的误差主要来自标注问题说明这批数据还有提升空间如果误差已经纯粹是特征区分问题需要考虑补数据或者换更强的模型。6.1 按批次做5折交叉验证判断数据集稳定性前面按8:1:1划分完的集合这时可以重新按路段做5折交叉验证来评估整体稳定性。目标不是追求更好的指标而是看5次训练的平均值和方差。现象如果5折的mAP_50标准差超过0.1说明数据分布不均有的路段和缺陷类型模型根本没学会需要补采集或做难样本挖掘。如果标准差在0.03以内说明数据内部一致性可以接受。6.2 难样本二次复核的筛选算法把验证集上漏检和误检的目标取出来统计其位置、类别、框尺寸定位问题数据。筛选用两个维度一是模型置信度低于0.3且有对应标注目标二是目标框尺寸在所有样本里排在后20%的细小缺陷。# 筛选低置信度漏检样本的伪代码思路 for img, preds in results: for gt in img.gt_boxes: matched False for p in preds: if iou(p.box, gt.box) 0.5 and p.cls gt.cls: matched True break if not matched: print(img.path, gt.cls, gt.box, 漏检)逻辑说明把所有低于阈值的漏检样本重新用labelme打开并检查。如果多数漏检样本存在边界标注不贴合的问题把对应json修正后重新转换标签如果漏检样本的标注本身没问题说明该类别样本特征确实不足考虑补充该类别数据或做针对性增强。参数说明这里的IoU阈值0.5对应的是通常评估标准mAP_50如果你想严格一些可以换0.5:0.95的平均阈值但从抓问题的角度看0.5更容易暴露出明显的系统性漏检。6.3 用mAP_50、F1-score和漏检率三个指标做决策三个指标一起看mAP_50反映整体检测能力F1-score反映精度和召回的平衡点漏检率直接评估模型在养护巡检场景中的可用性。如果mAP_50大于0.6但漏检率高于20%说明模型存在大量细小目标漏检优先做难样本挖掘和数据增强。如果mAP_50偏低但漏检率不高误检是主要问题加入负样本集并把置信度阈值调高。最后说一个我自己的教训之前拿到一批标注数据没有做交叉验证就反复调参结果模型指标漂亮得不像话现场一测完全翻车。后来才发现是数据集划分泄漏导致的假象。现在无论数据量大小先老老实实做路段分组再进交叉验证。数据集的结论才会体型在真实场景上是能打的。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑