混凝土缺陷检测数据集:7513张VOC+YOLO双格式与YOLOv8实战

发布时间:2026/10/10 0:13:21
混凝土缺陷检测数据集:7513张VOC+YOLO双格式与YOLOv8实战
简介本资源为混凝土缺陷检测数据集面向从事建筑结构健康监测、工业视觉检测及深度学习目标检测的开发者与研究人员可用于训练和验证裂缝、剥落、锈迹等病害的自动识别模型。数据集同时提供Pascal VOC与YOLO两种标注格式包含7513张jpg图片及一一对应的xml与txt标注文件标注类别共7类涵盖可见裂斑、分层、风化、缝隙、剥落、脱落、锈迹总标注框数达40324个均使用labelImg以矩形框方式完成标注。压缩包为7z格式整体约397.27MB内含2000个文件其中1999个xml标注文件与1个说明用txt文件目录结构清晰便于直接接入主流检测框架进行训练与评估。目前已有716人学习下载适合需要高质量多类别混凝土缺陷样本的算法训练、模型对比与课程实践场景。1. 混凝土缺陷检测数据集7513 张 VOCYOLO 双格式7 类缺陷一次说清拿到一个标注好的混凝土缺陷数据集最怕的不是数据量不够而是格式对不上、类别定义模糊、训练时才发现漏标错标一大堆。这次拆的这个包7513 张图VOC 和 YOLO 两种标注格式都给了7 个类别覆盖了混凝土结构表面最常见的缺陷类型。说白了它就是给做结构健康监测、桥梁隧道巡检、建筑外墙检测这类任务的人准备的——你不需要从零标注直接拿来做训练或微调都行。适合谁用一是做工程质检方向的研究生需要快速跑通 baseline 发论文二是做工业视觉的工程师手头有巡检业务想验证算法可行性三是参加检测类竞赛的选手想找个真实场景的数据集练手。不适合谁如果你要做的是裂缝分割而不是检测或者需要像素级标注这个包满足不了它给的是边界框标注。先说清楚一个事混凝土缺陷检测和通用目标检测不一样。缺陷的类间差异小、类内差异大同一类裂缝在不同光照、不同粗糙度表面下长得完全不一样而且小目标特别多。所以拿到这个数据集第一件事不是急着训模型而是先把格式转换、类别映射、数据分布这三件事理清楚。2. VOC 与 YOLO 双格式拆解目录结构、标注差异与转换逻辑2.1 两种格式到底差在哪VOC 格式的核心是每张图对应一个 XML 文件里面用object标签逐个记录目标的类别和边界框坐标坐标是绝对像素值原点在左上角。YOLO 格式则是每张图对应一个 txt 文件每行一个目标格式是类别索引 中心x 中心y 宽 高全部归一化到 0~1 之间。这两种格式没有谁优谁劣取决于你用什么框架。YOLOv5/v8/v11 系列直接吃 YOLO 格式Faster R-CNN、SSD 的很多实现吃 VOC 格式。这个包两种都给了省去了自己写转换脚本的麻烦。但要注意两种格式的类别索引映射必须一致否则训练出来的模型类别全是乱的。常见做法是先用 VOC 的 XML 做数据清洗和校验因为 XML 可读性好能直接看到类别名和坐标确认没问题后再转成 YOLO 格式喂给训练框架。这个包已经帮你转好了但你还是得自己验证一遍转换是否正确。2.2 目录结构长什么样解压后典型的目录结构是这样的不同打包方式可能略有差异以实际为准dataset/ ├── VOC/ │ ├── JPEGImages/ # 原始图片jpg 格式 │ ├── Annotations/ # VOC 标注 XML │ └── ImageSets/ │ └── Main/ # 训练/验证/测试划分文件 ├── YOLO/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── dataset.yaml # YOLO 训练配置文件 └── classes.txt # 类别名称列表拿到手先做一件事确认classes.txt里的类别顺序和 YOLO labels 里的类别索引是否一一对应。我见过太多人栽在这个地方——classes.txt 写的是[crack, spalling, exposed_rebar, ...]但标注文件里的索引 0 对应的是spalling训练完模型检测结果全错位。2.3 类别映射与校验脚本下面这段脚本用来校验 VOC XML 和 YOLO txt 的类别一致性同时统计每个类别的样本数import os import xml.etree.ElementTree as ET from collections import Counter # 配置路径 VOC_ANN_DIR dataset/VOC/Annotations YOLO_LABEL_DIR dataset/YOLO/labels/train CLASSES_FILE dataset/classes.txt # 读取类别列表 with open(CLASSES_FILE, r) as f: classes [line.strip() for line in f.readlines() if line.strip()] print(f类别列表: {classes}) print(f类别数量: {len(classes)}) # 统计 VOC 中各类别出现次数 voc_counter Counter() for xml_file in os.listdir(VOC_ANN_DIR): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(VOC_ANN_DIR, xml_file)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text voc_counter[name] 1 print(\nVOC 类别分布:) for cls_name, count in voc_counter.most_common(): print(f {cls_name}: {count}) # 统计 YOLO 中各类别出现次数 yolo_counter Counter() for txt_file in os.listdir(YOLO_LABEL_DIR): if not txt_file.endswith(.txt): continue with open(os.path.join(YOLO_LABEL_DIR, txt_file), r) as f: for line in f: parts line.strip().split() if len(parts) 5: cls_id int(parts[0]) yolo_counter[cls_id] 1 print(\nYOLO 类别分布 (索引: 数量):) for cls_id, count in sorted(yolo_counter.items()): cls_name classes[cls_id] if cls_id len(classes) else 未知 print(f {cls_id} ({cls_name}): {count}) # 交叉校验VOC 和 YOLO 的类别总数是否一致 voc_total sum(voc_counter.values()) yolo_total sum(yolo_counter.values()) print(f\nVOC 目标总数: {voc_total}) print(fYOLO 目标总数: {yolo_total}) if voc_total ! yolo_total: print(警告两种格式的目标总数不一致可能存在转换遗漏) else: print(校验通过两种格式目标总数一致。)这段脚本的逻辑很直接分别遍历 XML 和 txt统计每个类别的出现次数最后对比总数。参数方面VOC_ANN_DIR和YOLO_LABEL_DIR按实际解压路径改CLASSES_FILE是类别列表文件。如果总数对不上大概率是转换时漏了某些文件或者有些图片没有对应的标注文件。提示如果 VOC 和 YOLO 的目标总数差异在 5% 以内可能是转换时的浮点精度问题超过 5% 就必须逐文件排查了。3. 用 YOLOv8 跑通训练配置文件、参数设置与首轮结果分析3.1 环境准备与数据配置文件假设你已经装好了 ultralytics 包没装的直接pip install ultralytics。然后需要写一个 YOLO 的数据配置文件通常叫dataset.yaml# dataset.yaml path: ./dataset/YOLO # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 # 类别数量与名称 nc: 7 names: 0: crack 1: spalling 2: exposed_rebar 3: honeycomb 4: efflorescence 5: void 6: delamination这里的nc必须和实际类别数一致names的顺序必须和标注文件里的索引严格对应。我一般会先跑一遍校验脚本确认再写这个文件。路径用相对路径还是绝对路径都行但建议用相对路径方便整个项目打包迁移。3.2 训练命令与关键参数YOLOv8 的训练命令很简洁yolo detect train \ datadataset.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ device0 \ projectruns/concrete \ nameexp1逐个说参数含义。modelyolov8s.pt用的是 small 版本预训练权重如果显存不够换成yolov8n.pt精度要求高换yolov8m.pt。imgsz640是输入分辨率混凝土缺陷很多是小目标如果显存允许建议拉到 1024对小裂缝的召回率提升明显。batch16根据显存调8G 显存跑 640 分辨率大概能到 16跑 1024 就只能给到 4 或 8。lr00.01是初始学习率用预训练权重的话这个值比较稳从零训练要降到 0.001。patience20是早停耐心值20 个 epoch 验证集指标不提升就停。3.3 首轮训练结果怎么看训练完成后重点看几个东西。一是results.png里的损失曲线box_loss 和 cls_loss 是否稳定下降如果震荡剧烈说明学习率偏大或者 batch 太小。二是混淆矩阵confusion_matrix.png看哪些类别之间容易混。混凝土缺陷里最常见的混淆是 crack 和 delamination因为细长的剥离有时候看起来就像裂缝。三是各类别的 mAP 值。7 个类别里通常 exposed_rebar 和 honeycomb 的 AP 会比较高因为特征明显crack 和 efflorescence 的 AP 偏低前者因为太细后者因为和背景对比度低。如果某个类别 AP 低于 0.3先别急着调模型回去看这个类别的标注质量——大概率是漏标或者框不准。注意第一次训练不要一上来就调各种超参先跑一轮 baseline把数据问题暴露出来。数据问题不解决调参就是玄学。4. 数据增强与类别不平衡处理7 类缺陷的针对性策略4.1 先看分布再决定增强策略7513 张图、7 个类别平均下来每类大概 1000 个目标左右但实际分布往往不均匀。跑完第 2 章的统计脚本后你大概率会发现某些类别样本特别少。比如 void空洞和 delamination剥离通常比 crack 少很多因为这两类缺陷在自然场景中出现频率就低。YOLOv8 默认开启的增强包括 mosaic、随机缩放、随机裁剪、HSV 色彩抖动、水平翻转。这些对混凝土缺陷检测基本都适用但有几个需要调整。4.2 针对小目标的增强参数混凝土裂缝在 640 分辨率下可能只有十几个像素宽属于典型小目标。YOLOv8 的mosaic1.0默认开启它把四张图拼成一张变相增加了小目标的出现频率这个保留。scale0.5控制随机缩放幅度对小目标建议调到 0.9让模型见到更多尺度变化。copy_paste是分割任务用的检测任务不用管。如果某个类别样本太少可以用过采样。简单做法是在训练配置文件里给每个类别设权重但 YOLOv8 不直接支持类别权重。替代方案是手动复制少样本类别的图片和标注文件让它们在训练集中出现多次。注意只复制训练集验证集不能动否则评估结果虚高。import os import shutil import random # 对少样本类别进行过采样仅限训练集 TRAIN_IMG_DIR dataset/YOLO/images/train TRAIN_LBL_DIR dataset/YOLO/labels/train RARE_CLASS_IDS [5, 6] # void 和 delamination 的索引 OVERSAMPLE_RATIO 3 # 复制 3 倍 # 找出包含少样本类别的图片 rare_images [] for lbl_file in os.listdir(TRAIN_LBL_DIR): if not lbl_file.endswith(.txt): continue with open(os.path.join(TRAIN_LBL_DIR, lbl_file), r) as f: for line in f: cls_id int(line.strip().split()[0]) if cls_id in RARE_CLASS_IDS: rare_images.append(lbl_file.replace(.txt, .jpg)) break print(f包含少样本类别的图片数: {len(rare_images)}) # 复制到训练集加后缀避免覆盖 for img_name in rare_images: base img_name.replace(.jpg, ) for i in range(OVERSAMPLE_RATIO - 1): new_base f{base}_os{i} # 复制图片 shutil.copy( os.path.join(TRAIN_IMG_DIR, img_name), os.path.join(TRAIN_IMG_DIR, f{new_base}.jpg) ) # 复制标注 shutil.copy( os.path.join(TRAIN_LBL_DIR, f{base}.txt), os.path.join(TRAIN_LBL_DIR, f{new_base}.txt) ) print(过采样完成。)这段脚本的逻辑是扫描训练集标注找出包含指定少样本类别的图片然后复制指定倍数。参数RARE_CLASS_IDS根据你的实际分布改OVERSAMPLE_RATIO控制复制倍数一般 2~3 倍就够了太多会导致过拟合。4.3 增强后的验证方法做完过采样后重新跑一轮训练对比过采样前后的各类别 AP。重点看少样本类别的 AP 是否提升同时关注多样本类别的 AP 是否下降。如果多样本类别 AP 掉了超过 3 个点说明过采样比例过大模型被少样本类别带偏了。另一个验证方法是看验证集上的混淆矩阵。过采样前少样本类别可能大量被误判为背景或相似类别过采样后这些类别的召回率应该明显上升。如果召回率上去了但精确率掉得厉害说明模型开始过度预测这些类别需要降低过采样比例。5. 避坑与排查标注质量、格式转换和训练崩溃的 5 个血泪教训5.1 标注框越界导致训练报错现象训练启动后报AssertionError: Label class x is out of bounds或者坐标超出 0~1 范围。原因VOC 转 YOLO 时如果原始 XML 里的坐标超出了图片实际尺寸归一化后就会大于 1 或小于 0。这种情况在人工标注里很常见标注员手抖把框拉出去了。解决写个脚本遍历所有 YOLO 标注文件把越界的坐标裁剪到 [0, 1] 范围内同时检查宽高是否为正数。如果越界严重比如坐标是 1.5直接删掉这个标注因为框的位置已经不可信了。5.2 图片和标注文件不匹配现象训练时提示找不到某些图片的标注文件或者标注文件数量比图片多。原因打包时可能混入了没有标注的图片或者标注文件命名和图片命名不一致比如图片是.jpg标注是.JPG.txt。解决跑一个配对检查脚本列出所有没有对应标注的图片和没有对应图片的标注文件。对于没有标注的图片如果确认是负样本可以保留YOLO 支持空标注文件否则直接删除。对于没有图片的标注文件直接删除。5.3 类别索引错位现象训练完模型检测结果类别全乱明明是裂缝却识别成空洞。原因dataset.yaml里的names顺序和标注文件里的类别索引不一致。比如标注时 0 是 crack但 yaml 里写成了 0 是 spalling。解决用第 2 章的校验脚本打印出每个索引对应的类别名和classes.txt逐一对比。确认无误后再写 yaml。这个坑我踩过不止一次后来养成了习惯每次写 yaml 之前先跑校验脚本把类别映射打印出来贴在屏幕边上。5.4 小目标漏标严重现象模型训练 loss 正常下降但验证集上小裂缝的召回率极低大量漏检。原因原始标注时标注员对小目标不够敏感很多细小的裂缝没有标出来。模型学到的是“这些区域是背景”推理时自然不检测。解决抽样检查一批图片把模型漏检的区域和原始标注对比。如果确认是漏标要么补标要么在训练时降低这些小目标区域的损失权重。更彻底的做法是重新标注一批小目标密集的图片加入训练集。5.5 显存溢出导致训练中断现象训练到一半报CUDA out of memory进程被 kill。原因imgsz或batch设得太大或者 mosaic 增强时拼了四张高分辨率图显存峰值超标。解决先把batch减半如果还不行就把imgsz从 1024 降到 640。另一个技巧是开启ampTrue自动混合精度能省不少显存。如果这些都不行用yolov8n.pt替代yolov8s.pt模型小了显存占用自然低。6. 从 7513 张到可用模型验证集划分技巧与推理部署要点6.1 验证集划分不能随机很多人拿到数据集直接random.shuffle然后按 8:2 切分这在混凝土缺陷检测里是个大坑。因为同一个结构部位可能拍了多张照片随机切分会导致同一部位的图片同时出现在训练集和验证集里验证指标虚高实际部署时性能暴跌。正确做法是按结构部位或拍摄批次划分。如果数据集里没有部位信息至少按图片文件名前缀分组同一组的图片要么全在训练集要么全在验证集。这个包如果自带了ImageSets/Main划分文件直接用它的划分不要自己重新切。import os import hashlib # 按文件名前缀分组划分验证集 IMG_DIR dataset/YOLO/images/train VAL_RATIO 0.2 # 提取文件名前缀假设前缀格式为 部位ID_序号 prefix_groups {} for img_name in os.listdir(IMG_DIR): if not img_name.endswith(.jpg): continue prefix img_name.split(_)[0] # 按实际命名规则调整 if prefix not in prefix_groups: prefix_groups[prefix] [] prefix_groups[prefix].append(img_name) # 按组划分 groups list(prefix_groups.keys()) random.seed(42) random.shuffle(groups) val_group_count int(len(groups) * VAL_RATIO) val_groups set(groups[:val_group_count]) val_images [] for g in val_groups: val_images.extend(prefix_groups[g]) print(f验证集图片数: {len(val_images)}) print(f验证集组数: {len(val_groups)} / 总组数: {len(groups)})这段脚本的核心思路是先按前缀分组再按组划分保证同一组的图片不会跨集。参数VAL_RATIO控制验证集比例prefix的提取规则根据实际文件名调整。6.2 推理部署时的预处理对齐训练时用了 HSV 增强、mosaic 等推理时不需要这些但有几个必须对齐输入分辨率、归一化方式、通道顺序。YOLOv8 推理时默认把图片 resize 到imgsz大小保持长宽比填充灰边。如果你训练时用了rectTrue推理时也要保持一致否则检测框位置会有偏移。部署到边缘设备时常见做法是把模型导出成 ONNX 或 TensorRT。导出命令yolo export modelruns/concrete/exp1/weights/best.pt formatonnx imgsz640导出后一定要用同一张测试图对比 PyTorch 和 ONNX 的推理结果确保输出一致。我遇到过导出后类别顺序变了的情况原因是导出时的names字典顺序和训练时不一致。从那以后我每次导出后都强制跑一遍对比测试确认无误才部署。6.3 一个实用的推理后处理技巧混凝土缺陷检测有个特点同一个缺陷可能被模型用多个框检测出来尤其是大面积的剥落区域。标准 NMS 的 IoU 阈值默认 0.45对这个场景偏小会导致大量重叠框被保留。我一般会把 IoU 阈值调到 0.5~0.6同时开启agnostic_nms让不同类别的框也参与 NMS避免同一区域被多个类别重复检测。另外对于裂缝这种细长目标标准 NMS 可能把一条长裂缝切成多个短框。如果业务上需要完整裂缝可以在后处理时把 IoU 大于 0.1 且类别相同的框做合并用最小外接矩形替代原来的多个框。这个逻辑不复杂但能显著提升裂缝检测的实用性。希望帮到你。本文还有配套的精品资源点击获取