☰
沥青路面缺陷目标检测实战:6000张Labelme标注数据集转换与训练指南
2026/9/26 7:16:04 网站建设 项目流程

简介:面向道路养护与智慧交通场景的沥青路面缺陷目标检测数据集,其中part1包含2000张图片对应的Labelme标注数据,压缩包内共2000个json文件,总大小为563.3MB。针对当前缺陷检测中高质量标注稀缺、类别不平衡等痛点,数据覆盖裂缝、裂缝修补、坑洞、坑洞修补、井盖及其他6个关键类别,场景涵盖城市道路与高速公路等常见路面环境,可用于监控式巡检、无人机巡检等不同采集方式下的模型训练与评估。目前已有385人学习下载,适合道路病害识别算法研发的工程师、科研人员以及相关专业学生作为基准数据集使用。json文件内含精细的多边形轮廓标注与类别标签,既可在Labelme中直接查看和编辑,也可按需转换为COCO或YOLO格式,为后续扩展至全部6000张图像的研究提供了标准化的标注基础,对解决路面缺陷样本稀缺和类别不平衡问题有实用价值。

1. 沥青路面缺陷检测的 6000 张标注数据:先看清这个数据集能做什么

道路养护巡检里,沥青路面缺陷检测是个典型的「目标检测落地场景」:病害种类多、边界模糊、光照和阴影干扰大,而且一个缺陷从出现到恶化可能只有几个月,人工巡检根本排不过来。所以不管是做车载相机巡检、无人机巡航还是手持设备拍照上报,第一步都得先有一批「标注干净、类别明确」的缺陷样本。这里说的数据集-part1-沥青路面缺陷目标检测数据集-labelme-6000,指的就是一套用 labelme 工具标注完成的沥青路面缺陷检测数据集,规模 6000 张,格式上保留了 labelme 的 JSON 标注产物,可以直接转成 YOLO 系或其它检测框架需要的格式。对想训练路面缺陷检测模型的工程师来说,这套数据的价值在于:它帮你跳过了「到处找原始路面照片 + 重新标注」这个最耗时间的阶段,但拿到手之后还有不少活要自己干——数据清洗、格式转换、类别核对、训练验证集划分。这篇文章就顺着「数据集里有什么 → 怎么用 labelme 标注自己的补充数据 → 怎么转成训练格式 → 哪里容易翻车」这条线,把这套数据真正用起来。

2. 路面缺陷目标检测任务与 labelme 数据集的构成:先搞清楚手里有什么牌

2.1 路面缺陷检测检测的到底是什么:从任务定义反推数据需求

沥青路面缺陷检测在工程上归为「路面状况评价」的核心环节,常见病害类型包括横向裂缝、纵向裂缝、网状裂缝、龟裂、坑槽、修补区域、车辙、松散、泛油等。但在目标检测任务里,并不是所有病害都适合用「框」或者「多边形」去标。以我接触过的路面检测项目为例,横向裂缝和纵向裂缝这类线状目标,用多边形标注比用矩形框更贴合实际,因为裂缝往往细长且走向不规则,矩形框会把大量背景算进正样本,导致模型训练时背景干扰过大。而坑槽、修补这类块状目标,用矩形框或者多边形都能接受,关键看后续模型输入要求。

这就引出数据集评估的第一个动作:打开标注文件,统计每个类别的样本量和标注类型。拿到一个标注好的数据集,不要急着训练,先做三件事:第一,把类别标签列出来,和你的实际业务场景对照一遍,看看有没有缺失的类别、有没有多余的类别;第二,看标注形式是矩形框(rectangle)还是多边形(polygon),这决定了你后续转换脚本怎么写;第三,看单张图的标注数量分布,如果大量图片只有一两个目标,而个别图片有十几个目标,训练时要考虑样本不均衡。

那这套 6000 张的数据集在规模上是什么水平?拿 YOLOv5 / YOLOv8 训练经验来说,单类别目标检测大概需要 1500~3000 张有效标注图才能训练出一个能用的模型;多类别且目标形态差异大(比如裂缝和坑槽)时,建议每类至少 1000 张以上,总样本量 5000 张起步是合理的。6000 张意味着如果没有严重标注质量问题,足够支撑 3~5 类病害的模型初版训练。但要注意「有效量」和「文件数」的区别——如果里面有大量近乎重复的帧(比如视频抽帧),或者标注——错位、漏标严重,那有效量会大打折扣。我在项目里遇到过号称 2 万张的公共数据集,清洗完有效样本只剩 8000 张,这种事情很常见。

2.2 labelme 标注产物长什么样:JSON 结构、目录组织与坐标体系

labelme 是实验室和中小团队用得最多的图像标注工具之一,它默认把每一张图片的标注结果保存为一个独立的 JSON 文件,和图片文件放在同级目录或子目录里。一个典型的 labelme JSON 文件内部结构大致是:

{ "version": "5.2.1", "flags": {}, "shapes": [ { "label": "crack", "points": [[123.4, 567.8], [456.7, 890.1]], "group_id": null, "shape_type": "rectangle", "flags": {} } ], "imagePath": "img_0001.jpg", "imageData": null, "imageHeight": 1080, "imageWidth": 1920 }

这里的shapes数组就是核心:每个元素对应一个标注目标,label是类别名,points是坐标点列表,shape_type表示标注类型——rectangle只有左上和右下两个点,polygon是闭合多边形的顶点序列。imagePath记录图片文件名,imageWidth和imageHeight是图片尺寸。

看这份 JSON 时要注意几点。第一,imageData字段在大多数情况下是null,因为 labelme 默认不把图片转成 base64 冗余进 JSON;如果你的数据集里每份 JSON 都有大段 base64 图片数据,文件体积会膨胀好几倍,转换脚本要额外处理。第二,shape_type直接决定了后续坐标归一化方式:矩形框取两个点作为x1, y1, x2, y2,多边形则要计算外接矩形或按多边形语义处理。第三,point坐标是原始像素坐标,没有做任何归一化,转 YOLO 格式时要用imageWidth和imageHeight做除法。

从目录组织的角度看,比较规范的 labelme 数据集目录通常长这样:

dataset/ ├── images/ │ ├── img_0001.jpg │ ├── img_0002.jpg │ └── ... ├── labels/ │ ├── img_0001.json │ ├── img_0002.json │ └── ...

但很多人拿到的数据集直接把 JSON 和图片混放在同一个目录里,甚至 JSON 名字和图片名字不对应(标号错位)。无论哪种形式,第一步都建议写个脚本扫一遍全目录,确认「图片-标注」一一对应,把孤儿 JSON 和没标注的图片都挑出来,这步提前做能省后面几天的事。

2.3 类别体系核查:多标签还是单标签,背景干扰阈值设多少

路面缺陷检测数据集在类别设置上有个容易忽略的问题:病害类别之间并不是互斥的。比如一张图里既有横向裂缝又有修补痕迹,或者网状裂缝和龟裂在视觉上界限模糊,标crack_net还是alligator_crack完全看标注规范。所以拿到数据集第一步就是统计类别集合,然后问自己三个问题。

第一,这些类别在你的业务下游里是否需要区分?如果下游只是做「有没有病害」的粗筛,把裂缝细分出五六类反而是负担,合并成「crack」一个类别即可。第二,类别之间的混淆程度高不高?像泛油(bleeding)和修补区域(patching)在某些光照下外观很像,如果数据集中存在大量这类易混淆样本,训练时要在损失函数里加大类别权重,或者干脆合并类别。第三,是否有「背景类」干扰?路面照片里的污渍、水渍、落叶、轮胎印很容易被标注人员误标成病害,这类标注错误是检测模型出现「高置信度误检」的最常见原因。

我一般拿到数据集后会做一次类别频次直方图,看长尾分布。每类样本量差异超过 5 倍时,训练要启用mosaic增强、或者对少样本类别做过采样。这是把数据集用好的前提——很多人上来就训练,结果发现模型对样本量大的类别过拟合,少样本类别几乎学不到特征,这锅其实应该由数据集评估环节来背。

3. 用 labelme 标出自己的路面缺陷样本:从环境搭建到标注规范落地

3.1 labelme 安装:Windows / Linux 两条路径与 pyqt5 依赖坑

虽然手上有 6000 张标注好的数据,但实际项目里几乎一定会遇到「需要补标」的情况——模型在特定路段上误检率高,你得采集一批新图,用 labelme 标完再混合训练。所以 labelme 环境是绕不开的。这里讲两个常见安装路径和它们各自容易翻车的地方。

Windows 上最简单的方式是直接用 pip 装:

python -m venv labelme_env labelme_env\Scripts\activate pip install labelme

如果pip install labelme卡在 pyqt5 或 pyqt5-sip 上,大概率是 Python 版本和 PyQt5 的构建版本没对齐。常见解法是先把 pyqt5 相关包清干净再装:

pip uninstall pyqt5 pyqt5-sip pyqt5-qt5 labelme -y pip install labelme==5.1.1 PyQt5==5.15.10

Linux 上我习惯在 conda 环境里装:

conda create -n labelme python=3.9 -y conda activate labelme conda install pyqt=5 pip install labelme

这里有个关键约束:labelme 5.x 版本在 Python 3.10 / 3.11 上配合最新 PyQt5 时,偶尔出现菜单栏不显示、保存按钮失效这类「黑匣子」问题,很难排查,直接降级 Python 到 3.9 或固定 PyQt5 版本基本能消停。如果你拿到的数据集配套的 labelme 版本标注文件里带了"version": "4.6.0"这类字段,建议用对应相近版本打开,避免新版 labelme 在读写某些旧版 JSON 时字段兼容出问题。

3.2 路面缺陷的标注规范:多边形优先、边界贴合与 group_id 使用

标注规范决定数据集质量的上限。针对路面缺陷数据集,我总结的标注优先级是:线状病害(裂缝)用 polygon 精细勾边缘,块状病害(坑槽、修补)用 rectangle 或 polygon 都行,但同一数据集里同一病害不要混用两种 shape_type——转换脚本要兼容两种类型,而且模型学习时对目标形状的假设也不一样。

实际操作时注意这几点。第一,裂缝标注时沿裂缝可见边缘取点,拍照清晰的图控制在 15~30 个点内,没必要把像素级边缘全部描出来,点太多模型也不会学到更多。第二,对于一条裂缝断开成几段的情况,要不要分别标成多个目标?这取决于你的缺陷判定标准——如果下游只关心「这条路段有没有裂缝」,分开标没问题;如果关心「裂缝长度」指标,就得用 group_id 把属于同一条裂缝的多个 polygon 关联起来,后续通过 group_id 合并计算长度。第三,对于坑槽这类有明确边界的病害,标注时应包含完整的下陷区域,但要避开堆积的碎石和松散颗粒——标注员很容易把边界外的碎石也框进去,这部分背景会教坏模型。

标注界面的操作路径比较统一:打开 labelme 后拖入图片或打开图片目录,左侧工具栏选Create Polygons或Create Rectangle,闭合形状会弹窗要求填标签名。填标签时不要手打,建立一份固定的标签列表(label.txt),在 labelme 的设置里指定Labels文件路径,这样能避免「crack」「crack_」「Crack」这种大小写和多余字符导致类别数量爆炸的问题。6000 张数据集的标注工程里,这类标签不统一的情况几乎一定存在。

3.3 用现有 6000 张数据做半自动预标注:为什么推荐做这一步

有一个提升补标效率的套路值得说:如果你现有 6000 张数据已经训出一个粗模型,不要直接用它去标新图,而是先用这个模型以「预标注」方式跑一批新图,然后人工在 labelme 里修正。具体做法是把模型推理出的框或掩膜转成 labelme JSON——在 YOLO 模型推理结果里,xyxy坐标是绝对像素值,直接填进points,shape_type按需要设为rectangle,写一个一次性脚本就能批量生成。然后打开 labelme 逐个确认修正,工作量至少可以降到纯人工标注的 40%~60%。这是当前做目标检测项目里很实用的增效手段,没理由不用。

半自动标注的修正重点放在三类错误:模型把水渍/污渍误检成缺陷(删掉)、漏检了细小裂缝(补画)、边界框过大导致含大量背景(收边)。人工修正完的 JSON 再合并回数据集,和原始 6000 张一起混合、打乱、重新划分训练集与验证集,注意不要让同一路段的图片同时出现在训练集和验证集里——路面病害图像往往帧间高度相似,不按「路段维度」划分会带来严重的数据泄漏,评估指标虚高,工程上一旦上线就会露馅。

4. 把 labelme JSON 转成 YOLO 训练格式:转换脚本与标签映射的细节

4.1 转换脚本:JSON 到 YOLO txt,坐标归一化与类别映射

YOLOv5、YOLOv8、YOLOv6(即yolov26热搜里常指的那类版本)系列训练时读取的标签格式是:每张图对应一个同名 txt,每行代表一个目标,格式为class x_center y_center width height,坐标都归一化到 0~1。下面这个脚本是常见的 JSON 转 YOLO 做法,按 labelme 目录组织输入输出:

import json import os def labelme_to_yolo(json_path, out_dir, class_map, shape_type="rectangle"): with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) img_w, img_h = data['imageWidth'], data['imageHeight'] lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_map: continue cls_id = class_map[label] if shape['shape_type'] == 'rectangle': (x1, y1), (x2, y2) = shape['points'] elif shape['shape_type'] == 'polygon': xs = [p[0] for p in shape['points']] ys = [p[1] for p in shape['points']] x1, y1, x2, y2 = min(xs), min(ys), max(xs), max(ys) else: continue x1 = max(0, min(x1, img_w)) y1 = max(0, min(y1, img_h)) x2 = max(0, min(x2, img_w)) y2 = max(0, min(y2, img_h)) if x2 <= x1 or y2 <= y1: continue cx = ((x1 + x2) / 2) / img_w cy = ((y1 + y2) / 2) / img_h bw = (x2 - x1) / img_w bh = (y2 - y1) / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") out_path = os.path.join(out_dir, os.path.basename(json_path).replace('.json', '.txt')) with open(out_path, 'w', encoding='utf-8') as f: f.write('\n'.join(lines)) class_map = { "transverse_crack": 0, "longitudinal_crack": 1, "net_crack": 2, "pothole": 3, "patching": 4, "bleeding": 5, } # 使用示例 labelme_to_yolo("labels/img_0001.json", "yolo_labels/", class_map)

这段代码的核心逻辑是:读取 JSON 里的shapes,取出每个标注的类别名和坐标点,归一化后拼成一行文本。参数说明:class_map是标签名到数字 ID 的映射表,必须和训练时用的data.yaml保持一致;shape_type参数决定了是按矩形直接转换,还是先算多边形外接矩形——对于裂缝这类细长目标,外接矩形的长宽比很大,YOLO 的 anchor 机制对极端长宽比不友好,所以你也可以考虑保留 polygon 语义用实例分割模型(YOLOv8-seg)来训,这时转换目标要改成 polygon 点序列而不是 bbox,坐标同样归一化。

转换之后一定要抽查几个 txt 文件,重点确认点序是否正确:YOLO 的 bbox 格式要求x_center y_center width height,很多人写脚本时把x1 y1 x2 y2直接原样输出,训练时 loss 直接炸掉。另外一个常见的坑是浮点精度不足,坐标归一化后小于 1e-6 的目标在训练时会被过滤掉,所以上面代码用了 6 位小数。

4.2 训练验证集划分:按路段分组而非随机打散

划分训练集和验证集时,路面缺陷数据集有一个特殊风险——同一路段连续拍摄的照片背景高度相似,随机打散会导致训练集和验证集里出现「同一段路面的不同帧」,验证指标虚高。正确做法是先给图片按路段(或者按拍摄时间连续段)分组,再以组为单位划分。如果你的数据集没有提供路段信息,可以按文件名前缀、拍摄文件夹名或者图片拍摄时间戳来分组。一个实用的划分比例是训练:验证:测试 = 7:2:1,但如果数据总量偏少,可以调整到 8:2 并关闭验证集上的早停。

写划分脚本时建议在输出目录里维护一个train.txt / val.txt的图片路径列表,YOLO 系框架既支持这种列表模式,也支持目录扫描模式。我的习惯是显式维护列表文件,这样清洗和回滚都方便——数据集迭代时,只改列表不挪文件,成本低很多。

4.3 要不要做类别合并:从 6000 张的标注粒度反推模型设计

如果拿到的数据集中裂缝被细分成横向、纵向、网状三类,而你的业务只需要「有没有裂缝」,建议先分别训练看看各类别的单独 AP,再决定是否合并。这里有一个容易忽略的点:网络裂缝(net crack)和龟裂(alligator crack)在外观上是递进关系——同一块路面,初期是网状裂缝,恶化后变成龟裂。标注人员的时间节点不同,同一块区域可能被标成不同类别,这属于标注一致性风险。处理办法是:要么合并成一个crack_net类别,要么在数据集文档里明确判定边界(比如「裂缝网格边长大于 10cm 标为 net,小于 10cm 标为 alligator」)。

类别的增减决策会影响data.yaml:

# dataset.yaml train: data/train.txt val: data/val.txt nc: 5 names: ['transverse_crack', 'longitudinal_crack', 'net_crack', 'pothole', 'patching']

这里有个细节:如果 JSON 里的某些类别你没有映射进class_map,脚本运行时会直接跳过该目标而不是报错,结果会把漏标问题悄悄带进行训练。写转换脚本时建议做一个反向统计,打印每个类别在转换前后的目标数量,确保没有被静默丢数据。

5. labelme 标注数据集常见问题与排查:5 条血泪经验

5.1 JSON 里 imagePath 变成绝对路径,换电脑后批量损坏

现象:数据集从一台机器拷贝到另一台机器,用转换脚本时发现大量图片找不到,打开 JSON 一看imagePath被写成了/Users/xxx/Desktop/images/xxx.jpg这样的绝对路径。

原因:labelme 默认会把打开时图片的路径写入 JSON。如果标注人员在不同电脑上操作,或者数据从网盘同步后位置变化,相对路径和绝对路径就会混在一起。

解决:写一个脚本,遍历所有 JSON,不信任imagePath字段,直接按「JSON 文件名对应图片文件名」的规则重新生成 imagePath,同时做一次图片存在性校验。补齐规则很简单:new_path = os.path.join(img_dir, os.path.basename(data['imagePath'])),确保imagePath只保留文件名,路径由后续数据加载逻辑自己拼。

5.2 标注时 polygon 反向点序或自交,转成 mask 后面积异常

现象:用 YOLOv8-seg 训练时 loss 正常下降但 mAP 一直很低,可视化结果发现 mask 面积和实际目标完全对不上,最严重的会出现 mask 覆盖到图片外的负坐标。

原因:labelme 允许标注者画任意多边形,但如果是自交多边形或者点序混乱(顺时针/逆时针混合),转换算法计算外接矩形尚可,一旦转成 mask 再做仿射变换就出问题。

解决:在转换脚本里加一个多边形合法性检查,利用shapely.geometry.Polygon判断is_valid,对于不自交的多边形统一做规范化(检查面积、移除重复点)。不推荐人工逐条修,直接在批量转换时过滤非法多边形,记录到日志里再回头处理问题样本。道路缺陷标注中常见的情况是标注者在一个局部反复描边,产生大量近距离重复点,简化点序列能改善训练稳定性。

5.3 裂缝标注太细碎:一条裂缝被拆成 5 个小目标,混淆学习信号

现象:模型在验证集上召回率正常,但视觉上同一区域出现多个交叠的检测框,并集覆盖率很低,且置信度都在 0.5 左右波动。

原因:标注规范没有约定「裂缝断开多少算多个目标」。团队里有人把一条整裂缝按可见性断断续续标了五段,另一个人合并成一段,模型学到的目标定义本身就是混乱的。

解决:在标注规范里明确写「同一连续裂缝,无论可见度如何,只要纵向连贯,就标为一个 polygon;明显断开超过 50cm 才允许拆成两个独立目标」。然后对已有数据做一次 NMS 合并后处理——把 IoU 高于 0.7 且类别相同的目标合并,缓解标注碎片化对训练的干扰。不过要记住,合并处理是事后补救,最好的方案是标注阶段用 labelme 的group_id把断开的段绑定为同一目标,训练时按 group_id 聚合。

5.4 类别标签字符串有空格和大小写差异,导致类别数量莫名膨胀

现象:转换脚本运行完,统计发现目标类别有 9 个,但标签规范只定了 5 个。names列表里出现了'crack'和'crack '两个类。

原因:labelme 弹窗填写标签时,手打容易带空格、全角符号,或者不同标注者用了大小写不同写法。

解决:在标注环境里强制使用标签列表文件。做法是在 labelme 启动时指定--labels参数指向一个labels.txt,每行一个规范标签名。如果数据集已经标完,就在转换脚本里加一层标签清洗:

def clean_label(label): label = label.strip().lower() return label.replace(" ", "_").replace(",", ",")

把清洗函数接在class_map查找之前,宁可报错也不静默跳过,这样才能尽早发现脏标签。这一步成本极低,但对训练稳定性的贡献非常大。

5.5 验证时发现矩形框与目标严重偏离:病根在 labelme 自动保存的旧 JSON

现象:某些图片的标注框整体偏移几十像素,尤其在图像边缘区域偏移最严重,且总是同一批文件出问题,新标注的图片没有问题。

原因:labelme 在标注时如果图片被拖拽过或者窗口缩放比例不是 100%,坐标写入时可能出现偏差;但更常见的原因是这批 JSON 是由某一次批量操作(比如脚本生成预标注)产生的,当时生成时用的图片尺寸和现在目录里图片尺寸不一致——比如标注时用的是 1920×1080,后来有人把图片缩小成 1280×720 但没重新生成 JSON。

解决:批量转换前写一个校验脚本,遍历所有 JSON 的imageWidth / imageHeight和图片实际尺寸做比对,不一致的单独输出列表。要么回退到原尺寸图片,要么用缩放比例重新计算坐标。这种「尺寸不匹配」问题在线性缩放下不会触发报错,只会表现为模型效果变差,属于最容易漏掉的数据集「暗病」,值得在开工前排查。

6. 训练前的最后一道关卡:数据校验、抽样回看与复训闭环

在把 6000 张数据喂进 YOLO 之前,值得花半小时做一个数据校验和抽样回看,这步能把训练返工率降一半。校验脚本做三件事:一是统计全部目标的类别分布,输出每类目标数量、包含该类的图片数,确认长尾情况;二是检查每个目标的宽高比分布,如果某个类别目标的宽高比集中在 0.05 以下,说明 line-like 目标占比过高,训练时可以考虑在数据增强里关闭随机旋转,避免旋转后裂缝目标的 bbox 面积剧烈变化;三是把图片和标注框画出来,按类别抽样各 30 张生成可视化图。

可视化抽样的代码逻辑不复杂,但很实用:

import cv2 def draw_yolo_boxes(img_path, label_path, class_names): img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path, 'r') as f: for line in f: parts = line.strip().split() cls_id, cx, cy, bw, bh = int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) return img

把这批可视化图过一遍,你能快速发现三类低级错误:类别挂错(裂缝标成坑槽)、边界框过大(把半条路都框进去)、漏检(明显的大裂缝没有框)。我自己的习惯是每次拿到新数据集都跑一轮这个流程,肉眼回看不比写复杂指标差,很多模型问题在数据上看一眼就知道答案了。

训练完成之后也别急着交差。把模型对新采集的路面照片做一次推理,专门挑那些模型高置信度误检的图片回来反向分析——如果误检集中在阴影覆盖区域,可以考虑在训练集增强里加入亮度抖动和HSV扰动;如果误检集中在路面污渍,则说明标注里没把这类「难背景」作为负样本专门清洗。这套「往返迭代」做完,6000 张数据集的潜力才算真正挖完。希望这篇能帮你在路面缺陷检测的数据准备和标注环节少走几条弯路,让后续训练出门即正分。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询