简介:这是一份面向桥梁健康监测与工业缺陷检测方向的目标检测数据集,适合从事YOLO系列模型训练、算法验证及工程落地的开发者与研究人员使用,可解决桥梁表面病害样本稀缺、标注不规范的问题。压缩包共2000个文件,以1999个txt标签文件和1个Python脚本为主,整体约123.37MB,标签已统一处理为YOLO格式,可直接接入YOLO各系列网络训练。数据集涵盖腐蚀、裂纹、白石灰、泄漏、剥落等5类典型桥梁缺陷,包含超过3千张图像及对应labelme标注信息,并附有class类别文本文件便于类别映射。配套的show脚本可将预测框绘制到图像上,方便快速检查标注质量与可视化效果。目前已有419人学习下载,适合需要构建缺陷检测基线、开展数据增强实验或进行模型对比的读者参考使用。
1. 桥梁缺陷检测数据集:从零构建一个能跑通的目标检测数据管线
桥梁缺陷检测这件事,真正卡住大多数团队的从来不是模型结构,而是数据。你手里可能有一堆巡检拍回来的照片,裂缝、露筋、剥落、渗水混在一起,标注口径不统一,正负样本极度失衡,直接丢进 YOLOv8 训练,mAP 看着还行,一上真实桥面就集体翻车。桥梁缺陷检测数据集要解决的核心问题,是把「现场采集的原始影像」变成「模型能稳定收敛、且能迁移到新桥的标注样本」。它适合三类人:做交通基础设施巡检的算法工程师、想把目标检测落到工业缺陷场景的学生、以及需要给桥梁养护系统补一套视觉能力的后端开发者。这一章先把数据集的边界讲清楚——它检测的是可见表观缺陷,不是结构内部损伤;它的难点在长尾类别、极小目标和跨桥域偏移,而不是类别数量本身。后面几章会从采集、标注、格式转换、训练参数一路讲到验证和避坑,全部按可复现的路径来写。
2. 桥梁缺陷检测数据集怎么采、怎么定类别:先想清楚再按快门
2.1 缺陷类别体系与标注口径的确定
桥梁表观缺陷的类别划分,直接决定后面标注成本和模型上限。行业里常见的做法是参考公路桥梁技术状况评定标准里的缺陷描述,但那份标准是给人看的,颗粒度不适合直接当检测类别。我一般会把它压缩成 5 到 7 类,兼顾可标注性和业务价值:
| 类别名 | 英文标签 | 判定要点 | 标注难点 |
|---|---|---|---|
| 裂缝 | crack | 线性、有走向、宽度可辨 | 细长目标,框容易画歪 |
| 露筋 | exposed_rebar | 混凝土剥落后钢筋外露 | 与剥落边界重叠 |
| 剥落 | spalling | 混凝土块状脱落 | 与渗水区域混淆 |
| 渗水 | seepage | 水渍、泛碱、变色 | 边界模糊,框大小主观 |
| 蜂窝麻面 | honeycomb | 表面密集孔洞 | 小目标密集,易漏标 |
| 锈蚀 | corrosion | 钢构件表面锈斑 | 与渗水颜色接近 |
类别定完之后,必须写一份标注口径文档,明确「一个缺陷一个框」还是「一片区域一个框」。裂缝如果分叉,是整体一个框还是分段标注,这个不统一,后面模型学出来的框会非常玄学。我的习惯是:裂缝按连通域整体标注,分叉算同一个实例;渗水按可见变色区域外接矩形标注,不追求像素级边界。
2.2 采集设备、角度与光照的覆盖策略
数据采集阶段最容易犯的错,是只在一个天气、一个时间段、一个焦段下拍。桥梁缺陷检测模型上线后面对的是全年全时段影像,训练集如果只有晴天正午的顺光照片,模型一到逆光或阴影区就废了。
采集时我会强制覆盖这几个维度:
- 光照:晴天顺光、晴天逆光、阴天散射光、桥下阴影、黄昏弱光,每类至少占 10%。
- 距离:远景(整跨概览)、中景(构件级)、近景(缺陷特写),比例大约 2:5:3。
- 角度:正射、斜射 30 度到 60 度、贴近表面掠射,掠射对裂缝最友好。
- 设备:手机、单反、无人机各拍一部分,避免模型只认某一种成像风格。
采集数量上,如果只做单桥验证,2000 到 3000 张原始图能撑起一个可用的基线;要做跨桥泛化,建议至少 5 座桥、每座 800 张以上,且桥梁类型要有差异(梁桥、拱桥、斜拉桥各来一点)。
提示:采集时同步记录每张图的桥梁编号、构件类型、拍摄距离和光照条件,这些元数据后面做分层抽样和误差分析时非常值钱,事后补是补不回来的。
2.3 从原始影像到可训练样本的清洗流程
拍回来的图不能直接标。先跑一遍清洗,把废片、重复片、严重模糊片剔掉。下面这段脚本做的是基础去重和模糊检测,用的是感知哈希加拉普拉斯方差,依赖 opencv 和 imagehash:
import cv2 import os import imagehash from PIL import Image def is_blurry(img_path, threshold=80): # 拉普拉斯方差低于阈值判为模糊,阈值需按分辨率调整 img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) if img is None: return True return cv2.Laplacian(img, cv2.CV_64F).var() < threshold def dedup_by_hash(img_dir, hash_size=8, max_dist=5): # 感知哈希去重,汉明距离小于 max_dist 视为重复 seen = {} keep, drop = [], [] for name in sorted(os.listdir(img_dir)): path = os.path.join(img_dir, name) try: h = imagehash.phash(Image.open(path), hash_size=hash_size) except Exception: drop.append(path) continue dup = False for k, v in seen.items(): if h - v <= max_dist: dup = True break if dup: drop.append(path) else: seen[name] = h keep.append(path) return keep, drop if __name__ == "__main__": src = "./raw_images" keep, drop = dedup_by_hash(src) print(f"保留 {len(keep)} 张,剔除 {len(drop)} 张")逻辑说明:is_blurry用拉普拉斯算子响应方差衡量清晰度,方差越小越模糊,阈值 80 是针对 1080p 以上图像的经验值,手机小图要往下调。dedup_by_hash用 phash 把图像压成 8x8 指纹,汉明距离小于 5 认为是同一场景的重复拍摄。参数hash_size越大越敏感,max_dist越大去重越激进,建议先设 5 跑一遍看剔除比例,超过 30% 就说明阈值太松,要收紧。
清洗完之后做分层抽样,保证每个光照、距离、桥梁类别在训练集和验证集里都有代表。不要随机切分,随机切分会让同一座桥的相似图同时进训练和验证,指标虚高,这是血泪经验。
3. 标注、格式转换与增强:把数据喂进 YOLO 的完整链路
3.1 标注工具选择与标注质量控制
标注工具上,LabelImg 适合小规模矩形框,CVAT 适合多人协作和视频抽帧,Labelme 适合以后要转分割。桥梁缺陷检测用矩形框就够,我一般用 CVAT 起项目,因为它的快捷键和属性配置能强制标注员填构件类型和缺陷严重度。
质量控制靠三件事:预标注、交叉复核、抽样验收。预标注用一个先在通用数据上训过的检测模型跑一遍,标注员只做修正,效率能提 2 到 3 倍。交叉复核是每 10 张抽 1 张让第二个人重标,算 IoU 一致性,低于 0.7 的批次打回。抽样验收是训练前随机抽 50 张可视化检查,重点看小目标和边界模糊的渗水。
标注格式统一用 YOLO 的 txt:每行class_id cx cy w h,全部归一化到 0 到 1。这里有个容易翻车的点,cx cy 是框中心点,不是左上角,很多人第一次转格式就栽在这。
3.2 VOC 与 COCO 转 YOLO 格式的脚本与边界处理
现场标注工具导出的往往是 VOC XML 或 COCO JSON,需要转成 YOLO txt。下面这段脚本处理 VOC 转 YOLO,同时处理越界框和零面积框:
import os import xml.etree.ElementTree as ET CLASSES = ["crack", "exposed_rebar", "spalling", "seepage", "honeycomb", "corrosion"] CLASS_MAP = {c: i for i, c in enumerate(CLASSES)} def voc_to_yolo(xml_path, img_w, img_h, out_path): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in CLASS_MAP: continue # 未定义类别直接跳过,避免训练时报错 bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 裁剪到图像边界,防止越界框导致归一化后为负 xmin = max(0, min(xmin, img_w)) xmax = max(0, min(xmax, img_w)) ymin = max(0, min(ymin, img_h)) ymax = max(0, min(ymax, img_h)) w = xmax - xmin h = ymax - ymin if w <= 1 or h <= 1: continue # 零面积或极小框丢弃 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h nw = w / img_w nh = h / img_h lines.append(f"{CLASS_MAP[name]} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines))逻辑说明:CLASS_MAP把类别名映射成从 0 开始的整数,顺序必须和训练时的data.yaml完全一致,错一位模型就全乱。边界裁剪那两行是后悔药,标注员手抖画出图像外的框很常见,不裁归一化后会出现负数,训练直接报错。w <= 1 or h <= 1过滤掉退化的框,这种框在损失计算里会贡献异常梯度。
参数上,img_w和img_h必须读原图真实尺寸,不能写死。如果图像被预处理过(比如统一缩放到 640),标注框也要同步缩放,否则坐标全错。常见做法是转换前先统一图像尺寸,再按统一尺寸转换标注。
3.3 针对小目标和长尾类别的增强参数
桥梁缺陷里裂缝和蜂窝麻面属于小目标,渗水和锈蚀属于长尾。增强策略要分开对待。YOLO 训练时常用的增强有 mosaic、mixup、copy-paste、随机缩放和 HSV 抖动。
对小目标,mosaic 和随机缩放最有效,mosaic 把 4 张图拼成 1 张,等于变相增加小目标密度。但 mosaic 对小目标也有副作用,拼接边缘会切断裂缝,所以 mosaic 概率我一般设 0.5 到 0.7,不拉满。copy-paste 适合长尾类别,把稀少的渗水区域抠出来贴到其他图上,能显著提升召回,但贴的时候要注意光照一致性,否则模型学到的是「贴上去的渗水」而不是真实渗水。
HSV 抖动里,色调 h 的幅度要小,0.015 左右,因为缺陷颜色是有物理意义的,色调大改会让锈蚀和渗水混淆。饱和度和明度可以给到 0.7 和 0.4,模拟不同光照。
注意:增强只对训练集做,验证集和测试集保持原始分布,否则指标不可信。很多人把增强写进 dataloader 全局生效,验证指标虚高,上线就露馅。
4. 训练参数与评估:桥梁缺陷检测模型的调参与验收
4.1 YOLOv8 训练配置与关键参数含义
数据管线通了之后,训练本身反而简单。以 YOLOv8 为例,data.yaml长这样:
path: ./bridge_defect train: images/train val: images/val nc: 6 names: ["crack", "exposed_rebar", "spalling", "seepage", "honeycomb", "corrosion"]训练命令:
yolo detect train \ data=./bridge_defect/data.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=1024 \ batch=8 \ lr0=0.01 \ lrf=0.01 \ mosaic=0.6 \ mixup=0.1 \ patience=30 \ device=0参数说明:imgsz=1024是关键,桥梁缺陷小目标多,640 会丢细节,1024 显存吃得消就上 1024,吃不消用 896 折中。batch=8配合 1024 输入,8G 显存大概能跑,显存不够就降 batch 并开梯度累积。lr0=0.01是初始学习率,lrf=0.01是最终学习率系数,余弦退火到初始的 1%。patience=30是早停,30 轮验证指标不升就停,省时间。mosaic=0.6和mixup=0.1按上一节说的,不拉满。
模型规模上,yolov8s是性价比之选,yolov8n太快但小目标召回差,yolov8m以上精度提升有限但推理慢,边缘部署不划算。如果做跨桥泛化,建议从yolov8s起步,数据量上万再考虑yolov8m。
4.2 mAP、召回与分桥验证:指标怎么看才不骗自己
训练日志里会打一堆指标,重点看三个:mAP50、mAP50-95、每类召回。mAP50 是 IoU 0.5 下的平均精度,反映「框得差不多就算对」的能力;mAP50-95 更严格,反映框的精准度。桥梁缺陷检测里,裂缝的 mAP50-95 通常明显低于 mAP50,因为细长目标框的 IoU 很难做高,这是正常的,不用慌。
真正要警惕的是整体指标高但分桥指标崩。做法是把验证集按桥梁编号分组,分别算每座桥的 mAP。如果 A 桥 0.85、B 桥 0.4,说明模型过拟合到 A 桥的成像风格,跨桥泛化不行。这时候要么补 B 桥数据,要么做域增强(颜色抖动、风格迁移)。
还有一个坑是类别不平衡导致的指标幻觉。渗水样本少,模型干脆不预测渗水,mAP 里渗水那一类算 0,但整体 mAP 被裂缝拉高,看着还行。所以必须逐类看召回,长尾类召回低于 0.3 就要专门处理,加样本或调类别权重。
4.3 推理部署时的输入尺寸与置信度阈值
训练完导出模型,部署时输入尺寸要和训练一致,训练 1024 推理也 1024,改成 640 精度会掉。置信度阈值conf默认 0.25,桥梁缺陷场景建议调到 0.3 到 0.4,因为误检一个裂缝的代价比漏检一个渗水高,巡检报告里假阳性太多会失去信任。NMS 的 IoU 阈值默认 0.45,密集小目标(蜂窝麻面)可以调到 0.5 到 0.6,避免相邻缺陷被合并。
from ultralytics import YOLO model = YOLO("best.pt") results = model.predict( source="./test_images", imgsz=1024, conf=0.35, iou=0.55, save=True )逻辑说明:conf=0.35过滤低置信度框,iou=0.55控制 NMS 合并力度。这两个值没有万能解,要在验证集上画 PR 曲线,按业务对误检和漏检的容忍度选点。我一般会跑一组 conf 从 0.2 到 0.5 的对比,看召回下降的拐点在哪,拐点前一点就是合适值。
5. 避坑与排查:桥梁缺陷检测数据集最常见的 5 个翻车现场
5.1 现象:训练 loss 正常下降,但验证 mAP 一直 0.1 以下
原因:九成是标注格式或类别映射错了。常见的是data.yaml里names顺序和 txt 里的class_id对不上,或者 cx cy 写成了左上角坐标。还有一种隐蔽情况是图像和标注文件名不匹配,YOLO 按文件名找标注,找不到就当负样本,模型学了个寂寞。
解决:先拿 5 张图做可视化,把标注框画回原图上看位置对不对。再检查data.yaml的names和转换脚本里的CLASSES是否逐字一致。文件名不匹配的,写个脚本比对 images 和 labels 目录的文件名集合,差集就是问题所在。
5.2 现象:裂缝检测召回很低,框总是断成几截
原因:裂缝是细长目标,标注时如果按连通域整体标,框的长宽比极大,YOLO 的 anchor 和损失对这种极端长宽比不友好。加上 mosaic 增强会切断裂缝,模型学到的裂缝是碎片化的。
解决:两个方向。一是标注时对超长裂缝分段,每段单独一个框,降低长宽比;二是训练时降低 mosaic 概率到 0.3 以下,并开启scale增强让小目标尺度更丰富。如果还不行,考虑用旋转框(OBB)任务,YOLOv8 支持 OBB,对细长目标更友好,但标注成本会上升。
5.3 现象:模型在训练集上很好,换一座桥就崩
原因:跨桥域偏移。不同桥的混凝土颜色、光照条件、拍摄设备差异很大,模型学到了「这座桥的裂缝长这样」而不是「裂缝长这样」。数据采集时如果只覆盖一两座桥,这个问题必然出现。
解决:补数据是最直接的,每座目标桥至少 500 张。补不了就做域增强,随机调整亮度、对比度、色调,加高斯噪声和运动模糊,模拟不同成像条件。进阶做法是用风格迁移把 A 桥的图转成 B 桥的风格,但工程量大,一般项目用域增强就够。
5.4 现象:渗水类别几乎检不出来,召回接近 0
原因:长尾类别样本太少,且渗水边界模糊,标注一致性差。模型在损失里被裂缝等大类主导,渗水的梯度被淹没。
解决:先确认渗水样本数量,低于总样本 5% 就要补。补不了就用 copy-paste 增强,把渗水区域抠出来贴到其他图上。训练时给渗水类别加权,YOLO 不直接支持类别权重,但可以通过过采样含渗水的图来间接实现。另外检查标注,渗水框如果画得太大或太小,模型学不到稳定特征,要统一口径。
5.5 现象:推理速度远低于预期,边缘设备跑不动
原因:输入尺寸 1024 加上yolov8m以上的模型,在边缘设备上推理一帧要几百毫秒。很多人训练时用大模型大尺寸,部署时才发现算力不够。
解决:训练和部署要一起规划。边缘部署优先yolov8n或yolov8s,输入 640 或 768。如果精度不够,用知识蒸馏把大模型的能力迁到小模型,或者用 TensorRT 量化加速。量化会掉一点精度,要在验证集上确认掉幅可接受再上线。
6. 让数据集越用越值钱:主动学习与版本管理的一个具体习惯
数据集不是标完就完事,它应该随着项目推进持续增值。我自己的习惯是每上线一轮模型,就把线上误检和漏检的图回收,人工确认后补进训练集,下一轮再训。这套流程本质是主动学习,重点在「挑哪些图来标」——不是随机挑,而是挑模型最不确定的。
具体做法是推理时输出每个框的置信度,把置信度落在 0.3 到 0.6 之间的图挑出来,这些是模型拿不准的样本,标注价值最高。低于 0.3 的可能是纯背景,高于 0.6 的模型已经会了,都不用优先标。下面这段脚本做不确定性筛选:
from ultralytics import YOLO import os model = YOLO("best.pt") uncertain = [] for name in os.listdir("./unlabeled"): path = os.path.join("./unlabeled", name) results = model.predict(path, imgsz=1024, conf=0.1, verbose=False) boxes = results[0].boxes if boxes is None or len(boxes) == 0: uncertain.append((path, 0.0)) # 完全没检出,也可能是难样本 continue confs = boxes.conf.cpu().numpy() # 取置信度落在不确定区间的框比例 mid = ((confs > 0.3) & (confs < 0.6)).sum() / len(confs) uncertain.append((path, mid)) uncertain.sort(key=lambda x: -x[1]) print("优先标注:", [u[0] for u in uncertain[:20]])逻辑说明:conf=0.1放低阈值,尽量多检出候选框,再看置信度分布。mid是不确定框占比,占比越高说明模型对这张图越没把握,优先送标。完全没检出的图单独标记,可能是新出现的缺陷类型,价值也高。参数上,0.3 和 0.6 这两个边界可以按业务调,误检代价高的场景把下界提到 0.4。
版本管理上,数据集要像代码一样打 tag。每次补数据、改标注口径、调类别,都记一个版本号,训练配置里写清楚用的哪个版本。这样模型效果变化时能回溯是数据变了还是参数变了。我吃过亏,改了一版标注没记录,后来模型掉点查了两天才发现是标注口径变了。
这套流程跑顺之后,数据集会从「一次性成本」变成「持续资产」,每轮迭代模型都更强,标注量却不用线性增长。希望帮到你。
本文还有配套的精品资源,点击获取