☰
航拍屋顶识别数据集实战:YOLOv8/v11训练避坑与切片推理
2026/10/27 23:45:48 网站建设 项目流程

简介:这份航拍屋顶识别数据集面向从事目标检测的深度学习开发者与研究者,尤其适合需要快速验证YOLO系列、Faster R-CNN、SSD等模型训练效果的场景。数据以航拍视角采集,聚焦屋顶目标识别任务,可支撑遥感影像分析、城市建筑普查等应用方向。资源包共2000个文件,以txt标签文件为主,另含1个yaml类别配置文件,压缩包约170.36MB,txt标签已按训练集、验证集、测试集完成划分,yaml文件则指定了类别信息,便于直接接入YOLOv5至YOLOv10等主流框架。图片总量达3516张,同时提供VOC格式的xml标签,兼顾不同算法对标注格式的需求。目前已有367人学习下载,读者可借此省去数据采集与标注环节,将精力集中于模型结构调优与训练策略验证,快速搭建可复现的屋顶识别实验流程。

1. 航拍屋顶识别数据集:从“能跑通”到“能落地”的第一道坎

拿到一份航拍屋顶识别数据集,很多人第一反应是直接丢进 YOLO 训练脚本里跑一遍,看 mAP 能不能上 0.8。但真正做过遥感目标检测的人都知道,航拍屋顶识别和常规的 COCO 数据集完全不是一回事——屋顶目标密集、尺度差异大、背景干扰强,而且标注框的贴合度直接决定模型能不能在实际业务里用。这份航拍屋顶识别数据集面向的就是这类场景:从无人机或卫星影像中定位建筑物屋顶区域,输出边界框,服务于城市规划、光伏潜力评估、违建排查等下游任务。它适合两类人:一是刚接触遥感目标检测、想找一个真实场景练手的新手;二是已经跑过 YOLOv5/v8/v11,但手头缺一份标注质量可控、类别定义清晰的航拍数据集的从业者。数据集本身不复杂,复杂的是怎么把它用对。

2. 拆开这份航拍屋顶识别数据集:目录结构、标注格式与选型逻辑

2.1 数据集里到底有什么

一份标准的航拍屋顶识别数据集,通常按以下结构组织:

roof_dataset/ ├── images/ │ ├── train/ # 训练集影像,.jpg 或 .png │ ├── val/ # 验证集影像 │ └── test/ # 测试集影像(可选) ├── labels/ │ ├── train/ # 对应训练集的 YOLO 格式标注 │ ├── val/ │ └── test/ ├── data.yaml # 数据集配置文件 └── README.md # 类别说明与采集信息

影像来源一般是无人机正射影像或公开卫星图切片,分辨率从 512×512 到 2048×2048 不等。屋顶目标在影像中占比通常不大,单张图里可能有几十到上百个实例,密集场景下标注框之间会有明显重叠。标注格式以 YOLO 的.txt为主,每行一个目标,格式为class_id x_center y_center width height,坐标全部归一化到 0~1。

2.2 为什么选 YOLO 格式而不是 COCO 或 VOC

这不是随便定的。YOLO 格式的标注文件是纯文本,一行一个目标,解析成本极低,训练时直接按行读入即可,不需要像 COCO 那样先加载 JSON 再建索引。对于航拍屋顶这种单类别或少数几类的任务,YOLO 格式的简洁性优势非常明显。另外,Ultralytics 的 YOLOv8/v11 系列对 YOLO 格式的支持是一等公民,data.yaml里写清楚train、val、nc、names四个字段就能直接开训,不需要额外写 Dataset 类。

如果你手头的数据集是 VOC 的 XML 或 COCO 的 JSON,转换脚本也不复杂。常见做法是写一个 Python 脚本遍历标注文件,把绝对坐标转成归一化中心点坐标。下面是一个 VOC 转 YOLO 的示例:

import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, img_w, img_h, class_map): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall('object'): cls_name = obj.find('name').text if cls_name not in class_map: continue cls_id = class_map[cls_name] 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) # 归一化中心点坐标和宽高 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return lines

这段代码的关键点在于:class_map必须和data.yaml里的names顺序一致,否则类别 ID 会错位;归一化时除法要用浮点数,Python 3 里/默认就是浮点除法,但如果你从其他语言转过来,容易在这里翻车。另外,VOC 的坐标是 1-based,YOLO 是 0-based 归一化,边界框的xmax和ymax不需要减 1,直接除就行。

2.3 data.yaml 的写法与参数含义

data.yaml是训练入口,写错了后面全白搭。一个典型的航拍屋顶识别配置如下:

path: ./roof_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: roof

path是数据集根目录,train/val/test是相对路径。nc是类别数,屋顶识别通常就是 1 类。如果你的数据集区分了“平顶”“坡顶”“光伏板”等子类,nc和names要对应改。注意names的键必须从 0 开始连续,不能跳号。Ultralytics 在启动训练时会检查data.yaml里的路径是否存在,如果val路径写错,会在第一个 epoch 结束后报错,而不是一开始就提示,这个坑后面会细说。

3. 用 YOLOv8/v11 跑通航拍屋顶识别:环境、训练与推理全流程

3.1 环境配置:别在 CUDA 版本上浪费一整天

航拍屋顶识别数据集本身不大,但训练时显存占用和影像分辨率直接相关。如果你用的是 1024×1024 的切片,batch size 设 16 可能需要 12GB 以上显存。环境配置的常见做法是:

conda create -n roof_det python=3.10 -y conda activate roof_det pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118

这里的关键是 PyTorch 的 CUDA 版本要和驱动匹配。很多人装完ultralytics之后直接pip install torch,结果拉下来的是 CPU 版本,训练时 GPU 利用率为 0,速度慢几十倍。验证方法很简单:

import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))

如果第一行输出False,说明 CUDA 没配好,别急着跑训练,先把驱动和 CUDA 版本对齐。另一个常见问题是ultralytics自动安装的torch版本和手动装的不一致,导致import时报 DLL 错误。我一般会先装torch,再装ultralytics,并且用pip install ultralytics --no-deps跳过依赖自动安装,手动补齐opencv-python、numpy、matplotlib等包。

3.2 训练脚本与关键参数

Ultralytics 的训练接口非常简洁,但参数设不对,mAP 能差 10 个点以上。下面是一个针对航拍屋顶识别的训练脚本:

from ultralytics import YOLO model = YOLO("yolov8s.pt") # 从预训练权重开始 results = model.train( data="roof_dataset/data.yaml", epochs=100, imgsz=1024, # 航拍影像分辨率高,用 1024 比 640 更合适 batch=8, # 显存不够就降到 4 device=0, workers=4, optimizer="AdamW", lr0=0.001, lrf=0.01, patience=20, # 20 个 epoch 没提升就早停 augment=True, # 开启默认增强 mosaic=1.0, # 马赛克增强对密集小目标有效 mixup=0.1, copy_paste=0.1, # 屋顶目标复制粘贴增强,提升召回 save_period=10, project="roof_runs", name="exp1" )

imgsz是航拍任务里最关键的参数之一。屋顶目标在原始影像里可能只占几十个像素,如果缩到 640 训练,小目标信息损失严重,mAP 会明显偏低。常见做法是保持 1024 或 1280,但显存不够时可以先降到 768 试试。mosaic和copy_paste对密集场景的召回提升比较明显,但mixup别开太大,航拍影像的语义信息本来就弱,混叠过度会让模型学不到稳定的屋顶纹理。

3.3 推理与结果验证

训练完成后,用model.predict做推理:

from ultralytics import YOLO model = YOLO("roof_runs/exp1/weights/best.pt") results = model.predict( source="roof_dataset/images/test", conf=0.25, iou=0.5, imgsz=1024, save=True, save_txt=True )

conf是置信度阈值,航拍屋顶识别里建议从 0.25 开始调。如果漏检多,降到 0.15;如果误检多,升到 0.4。iou是 NMS 的 IoU 阈值,密集屋顶场景下建议设 0.5~0.6,太低会把相邻屋顶合并成一个框。推理结果会保存到runs/detect/下,save_txt=True会输出 YOLO 格式的预测结果,方便和标注做对比。

验证 mAP 时,Ultralytics 会在训练结束后自动跑val,输出mAP50和mAP50-95。航拍屋顶识别任务里,mAP50能到 0.85 以上算不错,mAP50-95通常在 0.5~0.65 之间。如果mAP50高但mAP50-95低,说明框的位置不够准,可能是标注框贴合度问题,或者imgsz设小了。

4. 避坑与排查:航拍屋顶识别数据集训练中的五个血泪教训

4.1 现象:训练 loss 正常下降,但 mAP 一直是 0

原因:data.yaml里的val路径写错,或者labels/val目录为空。Ultralytics 在第一个 epoch 结束后才跑验证,如果验证集为空,mAP 直接为 0,但训练 loss 照常下降,很容易误以为模型在学。

解决:训练前手动检查labels/val下的.txt文件数量是否和images/val下的图片数量一致。不一致就说明标注缺失或路径映射错了。

4.2 现象:显存溢出,batch size 降到 2 还是 OOM

原因:imgsz设得太大,或者workers太多导致数据加载占用过多内存。航拍影像本身分辨率高,1024×1024 的图在mosaic增强后会拼成 2048×2048,显存占用翻倍。

解决:先把imgsz降到 768,batch设 4,workers设 2。如果还 OOM,检查是否有其他进程占用显存,用nvidia-smi确认。另外,mosaic=1.0会显著增加显存占用,可以临时设mosaic=0.5。

4.3 现象:模型把相邻屋顶合并成一个框

原因:NMS 的iou阈值设得太低,或者标注框本身重叠严重。航拍屋顶密集区域,两个屋顶之间的间隙可能只有几个像素,模型很难区分。

解决:推理时把iou调到 0.6 甚至 0.7,训练时检查标注框是否有过度重叠。如果标注框本身就把两个屋顶框在一起,模型学到的就是合并框,这时候需要回到标注环节修正。

4.4 现象:验证集 mAP 很高,但实际推理漏检严重

原因:验证集和测试集的分布不一致。常见情况是验证集影像来自同一区域、同一光照条件,而实际推理的影像来自不同季节或不同飞行高度。

解决:划分数据集时按区域或时间划分,不要随机划分。如果数据集本身已经划分好,检查val和test的影像来源是否和train有重叠。另外,推理时的imgsz要和训练时一致,训练用 1024、推理用 640 会导致小目标漏检。

4.5 现象:训练到一半 loss 突然变成 NaN

原因:学习率太大,或者标注文件里有非法值(如坐标超出 0~1 范围)。航拍数据集的标注如果是从其他格式转换来的,容易出现坐标未归一化或归一化错误。

解决:先检查标注文件,用脚本遍历所有.txt,确认每行的x_center、y_center、width、height都在 0~1 之间。如果有超出范围的,定位到对应图片重新标注。学习率方面,AdamW的lr0建议从 0.001 开始,不要超过 0.01。

5. 进阶技巧:用切片推理和类别平衡把航拍屋顶识别推到可用水平

5.1 大图切片推理:解决小目标漏检的最后一公里

航拍影像动辄 4000×3000 以上,直接缩到 1024 推理,小屋顶可能只剩几个像素。常见做法是切片推理:把大图切成有重叠的小块,分别推理后再合并。Ultralytics 本身不直接支持切片推理,但可以用sahi库或者自己写一个滑动窗口脚本。

import cv2 import numpy as np from ultralytics import YOLO def sliced_predict(model, img_path, slice_size=1024, overlap=128, conf=0.25): img = cv2.imread(img_path) h, w = img.shape[:2] all_boxes = [] step = slice_size - overlap for y in range(0, h, step): for x in range(0, w, step): x2 = min(x + slice_size, w) y2 = min(y + slice_size, h) x1 = max(0, x2 - slice_size) y1 = max(0, y2 - slice_size) patch = img[y1:y2, x1:x2] results = model.predict(patch, conf=conf, imgsz=slice_size, verbose=False) for r in results: for box in r.boxes: bx1, by1, bx2, by2 = box.xyxy[0].tolist() all_boxes.append([bx1 + x1, by1 + y1, bx2 + x1, by2 + y1, box.conf[0].item()]) # 合并重叠框,用 NMS 去重 return all_boxes

这段代码的核心是overlap参数,设 128 或 256 都可以,太小会导致边缘目标被切断,太大则推理次数增加。合并时需要用 NMS 把重叠区域的重复框去掉,iou阈值设 0.5 左右。切片推理的代价是速度慢,但小目标召回能提升 10~20 个点,对于屋顶识别这种密集小目标场景,值得。

5.2 类别平衡与难例挖掘

如果数据集里某些屋顶类型(如光伏屋顶、彩钢瓦屋顶)样本很少,模型会偏向多数类。常见做法是在data.yaml里给少数类加权,或者用copy_paste增强少数类。Ultralytics 的copy_paste参数就是干这个的,它会从同一张图里复制少数类目标粘贴到其他位置,增加少数类的出现频率。

另一个技巧是难例挖掘:训练完一轮后,用best.pt在训练集上推理,找出漏检和误检的样本,手动检查这些样本的标注是否有问题。如果标注没问题,就把这些样本复制多份加入训练集,重新训练。这个流程跑两三轮,mAP 通常能再涨 3~5 个点。

5.3 验证方法:别只看 mAP,看 PR 曲线和混淆矩阵

Ultralytics 训练结束后会在runs/detect/exp1/下生成PR_curve.png和confusion_matrix.png。PR 曲线能看出模型在不同置信度下的精确率和召回率平衡点,混淆矩阵能看出类别之间的误判情况。航拍屋顶识别如果只有一类,混淆矩阵主要看背景误检——即把非屋顶区域框成屋顶。如果背景误检多,说明负样本不够,需要在训练集里加入一些不含屋顶的影像作为背景样本。

从那以后我每次拿到新的航拍数据集,都强制走一遍“检查标注范围 → 确认 val 路径 → 小 batch 试跑 → 看 PR 曲线”的流程,不再直接开大训练。希望帮到你。

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

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

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

立即咨询