公司动态

航拍屋顶检测数据集构建与YOLOv8模型训练全流程实战

📅 2026/9/2 6:07:05
航拍屋顶检测数据集构建与YOLOv8模型训练全流程实战
简介本资源是面向计算机视觉初学者与算法工程师的航拍屋顶检测专用数据集聚焦于遥感图像中屋顶目标的识别与分类任务特别适用于目标检测模型如YOLO系列、Faster R-CNN等的训练与验证。数据集共1376个文件包含458张高质量航拍JPG图像、458份Pascal VOC格式XML标注文件及458份YOLO格式TXT标注文件总大小132.59MB所有标注均由labelImg工具完成严格遵循矩形框标注规范覆盖roof、roof_asb、roof_asb_notsure三类目标总计2889个精确标注框。目前已有421人学习下载体现了该细分场景数据的稀缺性与实用价值。用户可直接用于模型训练、数据增强实验、格式转换实践或跨框架VOC/YOLO适配验证无需额外清洗目录结构规整命名统一开箱即用。1. 项目背景与数据集价值解析最近在做一个关于城市建筑屋顶状况的自动化巡检项目核心需求是通过无人机航拍图像快速识别屋顶的破损、异物堆积和植被覆盖情况。为了训练模型我花了不少时间在网上寻找合适的数据集但发现公开的、标注好的航拍屋顶数据非常稀缺。要么是场景不对比如都是卫星图分辨率太低要么是类别不全只标注了“屋顶”这个大类没有细分问题。没办法只能自己动手。我收集了458张不同季节、不同光照条件下的航拍屋顶图像并按照VOC和YOLO两种主流格式进行了精细标注最终整理成了这个“航拍屋顶检测数据集”。今天就把这个数据集以及我处理它的完整流程、踩过的坑和心得毫无保留地分享出来。这个数据集的核心价值在于它的“针对性”和“实用性”。它不是一个泛泛的通用目标检测集而是精准聚焦于“航拍视角下的屋顶”这一垂直场景。里面包含了三个精心定义的类别roof_damage屋顶破损、roof_debris屋顶杂物和roof_greenery屋顶植被。这三个类别基本覆盖了屋顶巡检中最常见、也最需要关注的问题。破损可能意味着漏水风险杂物如废弃的太阳能板架、堆积的垃圾可能影响结构安全或导致排水堵塞而过度茂盛的植被则会加速屋面材料的老化。对于从事智慧城市、物业管理、保险勘损或者相关学术研究的朋友来说这样一个高质量、可直接使用的数据集能省去大量的数据收集和标注成本让你快速跑通原型把精力集中在模型优化和业务逻辑上。2. 数据集内容深度拆解与标注实践2.1 图像数据来源与特点这458张图像并非来自单一的源头而是我通过多种渠道聚合而成的目的是为了保证数据的多样性和鲁棒性。大约60%的图像来自公开的无人机影像数据集如UC Merced Land Use Dataset的部分子集、自己从视频中截取的帧30%来自合作方提供的实际巡检照片剩下的10%则是在确保不涉及隐私的前提下于开源地图服务中筛选的航拍图。这种混合来源的策略虽然增加了前期处理的复杂度但极大地丰富了数据集的场景。这些图像具有几个鲜明的航拍特征视角固定基本都是正射或大倾角拍摄目标屋顶占据画面中心主要区域但尺寸变化很大从占据画面1/3的大型厂房屋顶到只占几十分之一的小型民居屋顶都有。背景复杂背景中不可避免地包含道路、车辆、树木、其他建筑等干扰物。这就要求模型不仅要学会识别屋顶还要能在复杂的背景下准确框出屋顶上的特定问题。光照与天气多样包含了晴天、阴天、黄昏等不同光照条件以及干燥、雨后湿润等不同表面状态。这对于模型的泛化能力是很好的考验。目标形态多变roof_damage可能表现为裂缝、瓦片缺失、锈蚀斑块形状极不规则roof_debris可能是条状、块状或点状roof_greenery则可能是一整片或零星分布。2.2 类别定义与标注规范在开始标注前明确定义每个类别的边界至关重要这直接决定了模型学习的效果和后续应用的准确性。我们团队内部制定了详细的标注规范文档roof_damage(屋顶破损)指屋顶材料本身出现的结构性或表面性损坏。包括但不限于明显的裂缝宽度通常大于2个像素、成片的瓦片碎裂或缺失、金属屋面的锈蚀穿孔、卷材屋面的起鼓或开裂。对于细微的划痕或正常的接缝则不进行标注。标注框应紧密包围破损区域如果一个大屋顶上有多个分散的破损点则分别标注。roof_debris(屋顶杂物)指非屋顶本身结构后期堆积或放置于屋顶上的物体。例如废弃的家具、建筑材料、落叶堆积形成的厚层、未正确安装的太阳能板支架、卫星天线等。标注的关键是判断该物体是否“不属于”屋顶常态且可能引发问题。一只偶尔停留的飞鸟则不标注。roof_greenery(屋顶植被)指在屋顶表面生长的植物如苔藓、杂草、灌木甚至小树。我们标注的是成片的植被区域对于零星几颗像素的苔藓点考虑到实际检测意义和标注成本予以忽略。标注框需包含植被的整体轮廓。注意标注中最容易产生歧义的是破损与污渍、杂物与固定设备。我们的原则是“是否影响屋顶功能或安全是否需要进行处置”例如一片水渍不标注但一片由渗漏导致的水渍伴生藻类则考虑标注为roof_damage或roof_greenery一个固定的、正常的烟囱不标注但一个倾倒的烟囱则标注为roof_debris。2.3 VOC与YOLO格式详解及转换要点数据集提供了VOCVisual Object Classes和YOLO两种格式这是为了适配不同的训练框架。很多新手会困惑到底该用哪种其实它们只是同一种信息的不同“包装”。VOC格式是最“直观”的XML描述文件。它为每张图片生成一个.xml文件里面用文本明确记录了图片尺寸、每个目标物体的类别名称以及其边界框的左上角和右下角绝对坐标以像素为单位。这种格式人类可读性强方便检查和调试。其目录结构通常如下VOCdevkit/ ├── VOC2007/ │ ├── Annotations/ # 存放所有 .xml 标注文件 │ ├── ImageSets/ │ │ └── Main/ # 存放 train.txt, val.txt 等文件列表 │ └── JPEGImages/ # 存放所有原始图片YOLO格式则是为了YOLO系列模型训练而设计的“高效”格式。它为每张图片生成一个同名的.txt文件。文件里每一行代表一个物体格式为class_id x_center y_center width height。这里的关键是后四个值不是像素值而是相对于图片宽度和高度的归一化比例值范围0-1。class_id是类别的索引从0开始。例如0 0.5 0.5 0.2 0.1表示类别0的目标其中心点位于图片正中央宽度占图宽的20%高度占图高的10%。格式转换是预处理中的关键一步。我从VOC格式开始标注因为手动审核方便然后写脚本批量转换为YOLO格式。转换的核心公式如下# 假设从VOC XML中解析出绝对坐标: xmin, ymin, xmax, ymax # 以及图片尺寸: img_width, img_height x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_width width (xmax - xmin) / img_width height (ymax - ymin) / img_height这里有个巨坑计算y_center和height时分母必须是img_height我一开始粗心全部用了img_width导致转换后的标注框全部错位训练时Loss直接不下降排查了好久。所以转换脚本写完后一定要用OpenCV或PIL写一个可视化脚本把转换后的YOLO标注框画回原图上看一看确保百分百对齐。3. 数据预处理、增强与划分策略拿到原始数据和标注后直接扔进模型训练效果往往不好。精心设计的数据预处理和增强流程是提升模型性能性价比最高的方法。3.1 预处理统一尺度与过滤脏数据我的预处理管道包括以下几步统一尺寸将所有图像缩放到一个统一的尺寸我选择了640x640。这是为了满足大多数检测网络输入尺寸固定的要求同时也能减少显存占用。缩放时采用cv2.INTER_LINEAR插值在速度和效果间取得平衡。同步更新标注框图像缩放后VOC格式的绝对坐标需要同步缩放。YOLO格式的归一化坐标则不需要改变因为它是比例值与绝对尺寸无关。这是YOLO格式的一个便利之处。脏数据清洗空标注文件检查并删除那些没有任何目标物体的图片及其标注。在这个数据集中有些图片屋顶完好无损没有三类问题这些就是“负样本”。对于检测任务负样本纯背景图并非必须但可以保留用于评估模型的假阳性率。我选择保留但在训练集中会控制其比例。标注错误检查通过可视化脚本发现了一些标注框明显错误如框到了背景树木上或类别标错的情况手动进行了修正。3.2 数据增强针对航拍场景的特化策略数据增强的目的是在不增加真实数据的情况下模拟出更多样的训练场景提高模型泛化能力。我使用albumentations这个强大的库来构建增强管道因为它能同时处理图像和其对应的边界框非常方便。我的增强策略分为“基础增强”和“场景化增强”基础增强每张图都做随机水平翻转p0.5。航拍图像左右翻转是合理的物理假设。随机亮度/对比度微调BrightnessContrast(p0.2)调整幅度较小模拟不同天气的光照变化。HSV颜色空间扰动轻微调整色相、饱和度和明度使模型对颜色不那么敏感。场景化增强以一定概率执行随机旋转小角度Rotate(limit10, p0.3)。无人机拍摄时会有轻微的角度偏移但大角度旋转如90度可能导致屋顶以非典型角度出现物理上不合理所以限制在10度以内。模拟云雾遮挡RandomFog(p0.1)或RandomRain(p0.1)。这是为了增强模型在恶劣天气下的鲁棒性。CutMix或Mosaic增强这是YOLOv5/v8等现代算法自带的增强在dataloader中实现。它能将多张图片拼接在一起极大地增加了每个batch内的上下文多样性对小目标检测尤其有效。对于屋顶上的小破损点或杂物这种增强效果显著。实操心得增强不是越多越好、越强越好。一开始我用了很强的色彩抖动和大幅度的旋转结果模型收敛变慢精度反而下降。后来我理解了增强应该模拟测试时可能遇到的合理变化。对于航拍屋顶光照颜色会变但屋顶不会突然变成紫色视角会有轻微倾斜但屋顶不会经常倒置。遵循这个“物理合理性”原则来调参增强才能真正发挥作用。3.3 数据集划分与类别平衡458张图片不算多所以划分策略要谨慎。我采用分层抽样的方式按7:2:1的比例划分为训练集320张、验证集92张、测试集46张。分层是为了确保每个子集中三个类别的分布比例与全集大致相同避免某个集合中缺少某一类样本。划分后我统计了每个集合的类别实例数类别训练集验证集测试集总计roof_damage58716885840roof_debris43212462618roof_greenery51014673729可以看到roof_damage的样本最多roof_debris相对较少但差距不算特别悬殊最多和最少的比例约1.36:1。在这种情况下我没有采用重采样oversampling或生成对抗网络GAN来造数据因为担心引入噪声或过拟合。而是选择在训练时使用加权损失函数给样本数较少的roof_debris类别稍微高一点的权重以缓解类别不平衡的影响。在YOLO中这通常可以通过调整loss中的cls_pw分类损失权重参数来实现。4. 基于YOLOv8的模型训练、调优与评估实战数据准备好了接下来就是模型训练。我选择YOLOv8作为基线模型因为它平衡了速度、精度和易用性。这里我以YOLOv8nnano版本为例展示从零开始的训练流程和调优细节。4.1 环境配置与数据准备首先创建一个干净的Python虚拟环境然后安装Ultralytics包pip install ultralytics数据准备非常简单得益于我们提供了YOLO格式。只需要创建一个dataset.yaml配置文件指向我们的数据即可# roof_dataset.yaml path: /path/to/your/dataset # 数据集根目录 train: images/train # 训练集图片路径相对path val: images/val # 验证集图片路径 test: images/test # 测试集图片路径可选 # 类别数量和名称 nc: 3 names: [roof_damage, roof_debris, roof_greenery]将图片和对应的.txt标注文件分别放入images/train/,images/val/,labels/train/,labels/val/等对应文件夹。Ultralytics库会自动根据图片文件找到同名的标签文件。4.2 基线模型训练与关键参数解析启动训练的命令很简单yolo taskdetect modetrain modelyolov8n.pt dataroof_dataset.yaml epochs100 imgsz640 batch16这条命令背后有几个关键参数直接影响训练效果和效率epochs100对于458张的小数据集100个epoch通常足够收敛甚至可以更少需要看验证集损失曲线。imgsz640与预处理时的缩放尺寸保持一致。batch16根据你的GPU显存调整。显存不足时可以减小batch同时等比例增大workers数据加载进程数来维持数据吞吐量。workers8数据加载的并行进程数设置为CPU核心数左右可以加快数据读取避免训练时GPU等待数据。训练开始后重点观察Ultralytics自动生成的日志和图表损失曲线train/box_loss,train/cls_loss,val/box_loss,val/cls_loss。理想情况是训练和验证损失都平稳下降且两者差距不大。如果验证损失很早就开始上升而训练损失持续下降这是典型的过拟合。性能指标主要看metrics/mAP50-95(B)即COCO标准的mAP。这是综合衡量模型精度和召回率的核心指标。mAP50IoU阈值为0.5时的mAP通常更高但mAP50-95更能说明模型定位的精确度。4.3 针对小目标与类别不平衡的调优技巧用默认参数跑完基线模型后mAP50-95可能只有0.4左右尤其是roof_debris这类小且少的物体检测效果较差。这就需要针对性调优。优化锚框AnchorYOLOv8默认使用自适应锚框计算但对于我们特定的数据集和固定的输入尺寸640x640可以尝试关闭自适应使用针对我们数据集聚类生成的锚框。我们可以用训练集的所有标注框进行K-means聚类K9因为YOLOv8默认3个检测头每个头3个anchor。# 使用ultralytics.utils.dataloaders模块中的函数可以方便地加载数据并聚类 from ultralytics.utils.dataloaders import LoadImagesAndLabels dataset LoadImagesAndLabels(..., rectFalse) anchors kmeans_anchors(dataset, n9, img_size640)将得到的9个anchor尺寸宽高更新到模型配置文件或直接通过参数传入。实测中这能带来约1-2%的mAP提升特别是对小目标。调整损失函数权重如前所述针对roof_debris样本少的问题可以调整分类损失权重。在YOLOv8中可以通过修改model.yaml文件中的cls_pw参数或者直接在训练命令中设置。例如将roof_debris的权重从1.0提高到1.5。但要注意权重不宜过高否则会破坏损失函数的平衡导致模型过于关注少数类而牺牲整体性能。启用更强的数据增强在roof_dataset.yaml中或训练命令里可以开启YOLOv8内置的更强增强。# roof_dataset.yaml 追加 hsv_h: 0.015 # 色相增强幅度 hsv_s: 0.7 # 饱和度增强幅度 (提高以模拟不同季节植被颜色) hsv_v: 0.4 # 明度增强幅度 degrees: 10.0 # 旋转角度范围 (与我们之前设置一致) translate: 0.2 # 平移幅度 scale: 0.9 # 缩放幅度 shear: 0.0 # 剪切幅度 (航拍中很少见设为0) perspective: 0.001 # 透视变换 (轻微模拟不同高度视角) flipud: 0.0 # 上下翻转概率 (航拍中物理不合理设为0) fliplr: 0.5 # 左右翻转概率 mosaic: 1.0 # Mosaic增强概率 (非常重要) mixup: 0.2 # Mixup增强概率 (可尝试但不宜过高)mosaic增强对提升小目标检测能力有奇效因为它把四张图拼成一张相当于让模型在单张图上看到更多、更密集、尺度更多变的目标。4.4 模型评估与错误分析训练完成后在独立的测试集上运行评估yolo taskdetect modeval modelruns/detect/train/weights/best.pt dataroof_dataset.yaml评估报告会给出详细的Precision,Recall,mAP50,mAP50-95等指标。但数字只是结果更重要的是进行错误分析。Ultralytics会自动生成一个混淆矩阵和一个F1-置信度曲线图。混淆矩阵主要看非对角线上的值。如果发现roof_debris有很多被误检为roof_damage说明这两个类别在特征上容易混淆可能需要回头检查标注或者考虑在数据增强时有针对性地增加这两类难例的样本例如使用Copy-Paste增强将debris物体粘贴到其他屋顶上生成新样本。F1-置信度曲线这条曲线显示了在不同置信度阈值下F1分数精确率和召回率的调和平均的变化。找到F1分数的峰值点对应的置信度阈值就是模型部署时最佳的默认阈值。通常这个值在0.25到0.5之间。如果曲线峰值很低说明模型区分前景和背景的能力不足。我还会手动查看测试集上的预测结果特别是那些置信度不高或预测错误的图片。常见的错误模式有背景误检将屋顶阴影、深色沥青路面误认为roof_damage。这说明模型对颜色和纹理的依赖过强可能需要增加更多包含类似背景但无目标的“困难负样本”到训练集中。小目标漏检很小的破损点或杂物被漏掉。除了使用mosaic增强还可以尝试修改模型结构使用更浅层、更高分辨率的特征图进行小目标检测如YOLOv8的P2层输出。在损失函数中给更小的目标框分配更高的权重YOLO本身有box_loss中的box增益可以微调。部分遮挡目标处理不佳被树木遮挡一部分的屋顶植被识别不全。这需要模型有更强的上下文推理能力可能更深的网络如YOLOv8m或YOLOv8l会表现更好但也要权衡速度。5. 模型部署与应用场景展望训练出一个满意的模型例如在测试集上mAP50-95达到0.55以上后下一步就是部署和应用。YOLOv8提供了极其便捷的导出功能可以将PyTorch模型转换为各种运行时格式。5.1 模型导出与优化最常用的导出格式是TorchScript、ONNX和TensorRT。# 导出为ONNX格式 (适用于多种推理引擎) yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640 # 导出为TensorRT格式 (用于NVIDIA GPU极致加速) yolo export modelruns/detect/train/weights/best.pt formatengine device0 imgsz640ONNX通用性好可以在CPU通过ONNX Runtime或GPU上运行是跨平台部署的首选。导出时建议开启dynamic选项以支持动态批处理和不同尺寸的输入虽然我们固定了640但为未来留有余地。TensorRT如果你有NVIDIA Jetson边缘设备或服务器TensorRT能提供最大的推理速度提升。导出时需要指定精度FP16或INT8。INT8量化能进一步提速并减少显存占用但可能会带来轻微的精度损失需要用小批量校准数据做后训练量化。5.2 构建端到端推理管道部署不仅仅是加载模型还需要构建一个完整的处理管道。以下是一个基于Python和ONNX Runtime的简单推理脚本框架import cv2 import numpy as np import onnxruntime as ort class RoofDetector: def __init__(self, onnx_path, conf_thresh0.4, iou_thresh0.45): self.session ort.InferenceSession(onnx_path) self.input_name self.session.get_inputs()[0].name self.conf_thresh conf_thresh # 从F1曲线确定的阈值 self.iou_thresh iou_thresh self.imgsz 640 def preprocess(self, image): # 保持长宽比resize并在边缘填充灰色 h, w image.shape[:2] scale min(self.imgsz / h, self.imgsz / w) new_h, new_w int(h * scale), int(w * scale) resized cv2.resize(image, (new_w, new_h), interpolationcv2.INTER_LINEAR) # 创建画布并填充 canvas np.full((self.imgsz, self.imgsz, 3), 114, dtypenp.uint8) canvas[:new_h, :new_w] resized # 转换通道和类型 blob canvas.transpose(2, 0, 1) # HWC to CHW blob np.ascontiguousarray(blob) blob blob.astype(np.float32) / 255.0 # 归一化 blob np.expand_dims(blob, axis0) # 添加batch维度 return blob, (scale, (new_w, new_h), (h, w)) def postprocess(self, outputs, preprocess_info): scale, (new_w, new_w), (orig_h, orig_w) preprocess_info # outputs[0] 形状为 [1, 8400, 8] (YOLOv8输出格式) predictions outputs[0].squeeze(0) # [8400, 8] # 过滤低置信度 conf_mask predictions[:, 4:7].max(axis1) self.conf_thresh predictions predictions[conf_mask] if predictions.shape[0] 0: return [] # 分离框、置信度、类别 boxes predictions[:, :4] # cx, cy, w, h (归一化相对于640x640画布) scores predictions[:, 4:7].max(axis1) class_ids predictions[:, 4:7].argmax(axis1) # 将框坐标转换回原始图像尺寸 boxes[:, [0, 2]] boxes[:, [0, 2]] * self.imgsz / scale boxes[:, [1, 3]] boxes[:, [1, 3]] * self.imgsz / scale boxes[:, 0] - (self.imgsz / scale - new_w) / 2 # 去除填充偏移 boxes[:, 1] - (self.imgsz / scale - new_h) / 2 # 转换为x1, y1, x2, y2 boxes[:, 0] - boxes[:, 2] / 2 boxes[:, 1] - boxes[:, 3] / 2 boxes[:, 2] boxes[:, 0] boxes[:, 3] boxes[:, 1] # 应用NMS indices cv2.dnn.NMSBoxes(boxes.tolist(), scores.tolist(), self.conf_thresh, self.iou_thresh) final_results [] if len(indices) 0: for i in indices.flatten(): x1, y1, x2, y2 boxes[i] # 确保坐标在图像范围内 x1, y1 max(0, int(x1)), max(0, int(y1)) x2, y2 min(orig_w, int(x2)), min(orig_h, int(y2)) final_results.append({ bbox: [x1, y1, x2, y2], score: float(scores[i]), class_id: int(class_ids[i]), class_name: self.class_names[int(class_ids[i])] }) return final_results def detect(self, image_path): image cv2.imread(image_path) blob, preprocess_info self.preprocess(image) outputs self.session.run(None, {self.input_name: blob}) results self.postprocess(outputs, preprocess_info) # 可视化结果... return results这个管道包含了保持长宽比的预处理、模型推理、基于置信度阈值和NMS的后处理以及坐标映射回原图。这是部署中最容易出错的环节务必仔细测试。5.3 应用场景与系统集成训练好的模型可以集成到多种实际应用中无人机自动巡检系统模型部署在无人机机载计算机如Jetson Nano或通过图传回传至地面站。无人机按预定航线飞行实时分析视频流自动标记疑似问题屋顶并生成包含位置坐标和问题类别的巡检报告。这极大地提升了巡检效率避免了人工查看海量图像的疲劳和疏漏。保险定损与风险评估保险公司在接到屋顶损坏理赔申请时可以要求客户或勘察员上传无人机拍摄的屋顶照片。系统自动分析照片快速初筛损坏情况辅助定损员判断甚至能对历史图像进行对比评估风险变化。智慧城市建筑管理市政部门可以对辖区内的建筑屋顶进行定期普查建立屋顶健康状况数据库。系统能自动统计各类问题的分布和变化趋势为城市更新、安全预警提供数据支持。光伏电站运维对于屋顶光伏电站杂物堆积和植被生长会影响发电效率甚至引发火灾。自动化检测可以及时发现问题触发清理工单。在系统集成时还需要考虑一些工程细节性能在Jetson Nano上YOLOv8nINT8量化后处理640x640图像可以达到30FPS满足实时性要求。误报处理在实际应用中可以设置一个高于开发阶段的置信度阈值来减少误报或者引入一个简单的后处理规则引擎。例如对于面积小于某个阈值的检测框可能是噪声直接过滤对于roof_greenery如果其检测框完全位于一个roof_debris框内则可能优先判定为杂物上的苔藓进行逻辑去重。持续学习系统上线后会收集到大量新的、可能带有标注人工复核后的数据。可以定期用新数据对模型进行微调fine-tuning让模型不断进化适应新的场景和变化。从458张标注图片开始到构建一个可运行的检测模型再到设想其在实际业务中的应用这个过程充满了挑战但也极具成就感。这份数据集和这些经验希望能为同样想在航拍视觉领域做些实事的你铺上一块小小的垫脚石。模型训练没有银弹最好的方法永远是理解你的数据、你的问题然后不断地实验、分析和迭代。本文还有配套的精品资源点击获取