沥青路面缺陷目标检测实战:6000张Labelme标注数据集转换与训练指南
发布时间:2026/9/26 7:16:06来源:尧图网络
简介面向道路养护与智慧交通场景的沥青路面缺陷目标检测数据集其中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 训练经验来说单类别目标检测大概需要 15003000 张有效标注图才能训练出一个能用的模型多类别且目标形态差异大比如裂缝和坑槽时建议每类至少 1000 张以上总样本量 5000 张起步是合理的。6000 张意味着如果没有严重标注质量问题足够支撑 35 类病害的模型初版训练。但要注意「有效量」和「文件数」的区别——如果里面有大量近乎重复的帧比如视频抽帧或者标注——错位、漏标严重那有效量会大打折扣。我在项目里遇到过号称 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 labelme5.1.1 PyQt55.15.10Linux 上我习惯在 conda 环境里装conda create -n labelme python3.9 -y conda activate labelme conda install pyqt5 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——转换脚本要兼容两种类型而且模型学习时对目标形状的假设也不一样。实际操作时注意这几点。第一裂缝标注时沿裂缝可见边缘取点拍照清晰的图控制在 1530 个点内没必要把像素级边缘全部描出来点太多模型也不会学到更多。第二对于一条裂缝断开成几段的情况要不要分别标成多个目标这取决于你的缺陷判定标准——如果下游只关心「这条路段有没有裂缝」分开标没问题如果关心「裂缝长度」指标就得用 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坐标是绝对像素值直接填进pointsshape_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坐标都归一化到 01。下面这个脚本是常见的 JSON 转 YOLO 做法按 labelme 目录组织输入输出import json import os def labelme_to_yolo(json_path, out_dir, class_map, shape_typerectangle): with open(json_path, r, encodingutf-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, encodingutf-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 张数据集的潜力才算真正挖完。希望这篇能帮你在路面缺陷检测的数据准备和标注环节少走几条弯路让后续训练出门即正分。本文还有配套的精品资源点击获取
网站建设高端定制企业官网