简介:面向遥感地质灾害监测与计算机视觉目标检测的航拍滑坡检测数据集,适合研究人员、算法工程师和深度学习者用于滑坡识别模型训练、数据格式切换与检测精度验证。资源包为ZIP压缩格式,大小约200.98MB,共2000个文件,其中以xml标注文件为主并附带说明txt,覆盖4315张512×512分辨率的航拍滑坡图像,提供Pascal VOC和YOLO两种标注格式,滑坡目标框总计11315个,类别为单一的landslide,标注工具为labelImg。数据集经过增强处理,但未预设训练、验证、测试划分,用户可按需自行切分,有利于练习数据拆分与模型调优。当前已有34人学习浏览,可作为滑坡识别、遥感图像理解等方向的实验数据,帮助快速搭建目标检测流程并验证算法在不同场景下的泛化能力。
1. 航拍滑坡数据集是干嘛的:一个行业痛点与这份数据的解法
搞地质灾害遥感的人大概都有这个体验:网上公开的滑坡数据不是 DEM 栅格就是灾害点坐标,真正能直接喂进 YOLO 训练管线、带框标注的航拍影像集少得可怜。这个「航拍滑坡数据集 4315 张 VOC+YOLO 格式.zip」正好补上这块短板——4315 张航拍影像,每张都有对应的目标框标注,VOC 和 YOLO 两种格式给你打包好,解压就能用。它适合三类人:想跑通「用自己的数据训 YOLO」全流程的初学者,做应急巡检或地质灾害监测方案预研的一线工程师,以及需要一份非公开风格样本做对比实验的研究生。数据本身不神秘,但围绕它展开的解压、格式核对、转换、训练、避坑、验证,是一条值得完整走一遍的技术链路。顺着这个标题,我把每一步怎么落地和会在哪里翻车讲清楚。
2. 先弄清包里装了什么:VOC 与 YOLO 两种标注格式的分工
2.1 VOC 侧的三件套:JPEGImages、Annotations、ImageSets
PASCAL VOC 格式是这个 zip 里最直观的一半。解压后你会看到典型的 VOC 目录骨架:JPEGImages放航拍原图,Annotations放同名 XML 标注文件,ImageSets/Main里是 train.txt、val.txt 这类划分列表。VOC 标注用像素坐标记录目标位置,XML 里<size>给出图像宽高,每个<object>里有<name>表示类别,<bndbox>给出左上角和右下角的像素坐标。
<annotation> <folder>JPEGImages</folder> <filename>landslide_001.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>landslide</name> <difficult>0</difficult> <bndbox> <xmin>320</xmin> <ymin>240</ymin> <xmax>1280</xmax> <ymax>860</ymax> </bndbox> </object> </annotation>像素坐标的好处是直观,用 labelImg 或 CVAT 标注完导出就是这个结构,人眼能直接和影像对得上。VOC 格式除了喂检测模型,还被很多语义分割工具兼容——你拿同一份 XML 做滑坡影响范围分析,也能把<bndbox>当成裁剪范围用。这类航拍数据集常见的一个特征是目标占画面比例偏大,像上面这种从 320 到 1280 的框说明滑坡体本身就铺了一大片,这会影响后续选 imgsz 的策略。
2.2 YOLO 侧的具体形态:images/ 与 labels/ 下的 txt 对应关系
YOLO 侧就好认多了:一个images目录放图片,一个labels目录放同名 TXT。每行格式固定为class cx cy w h,其中 cx、cy 是目标中心点坐标,w、h 是目标宽高,全部做了归一化,取值 0 到 1。比如同一张landslide_001.jpg,对应landslide_001.txt内容是:
0 0.4167 0.5093 0.5000 0.5741这行数字的意思是:类别 ID 是 0,目标中心在图像 41.67% 宽度、50.93% 高度处,占宽 50%、高 57.4%。YOLO 训练时直接读这种 TXT,不再需要边训练边解析 XML,路径匹配靠的是同名前缀。
两种格式同时下发不是脱裤子放屁。最实际的用途是交叉校验:训练前写三行脚本,把 YOLO 的归一化坐标乘回图像宽高,和 VOC 的 bndbox 对比,误差超过一个像素就说明标注有损。VOC 到 YOLO 的转换是常规操作,但反过来从 YOLO 转 VOC 经常有人少了四舍五入,导致框偏了几个像素,在滑坡这种边界模糊的目标上无伤大雅,在小裂缝目标上可能直接让 IoU 从 0.75 掉到 0.68。所以我的习惯是收到数据包先做一次一致性校验,确认两个格式是同一批标注导出的,而不是各自标了一遍。
2.3 为什么同时发两种格式:降低交接成本与两轮踩坑铺垫
从工程交接的角度看,发双格式的核心收益是让不同工位的同事不用等转换。做检测的拿 YOLO 版直接进训练,做前处理的拿 VOC 版做裁剪和可视化,做语义分割的拿 XML 做掩码生成。省掉一次全量转换,也就省掉了转换脚本维护和坐标丢失排查的时间。尤其是团队里有人用老版本 detectron2、有人用 YOLOv5、还有人用自家框架,统一成双格式是最省事的交接形态。这类「XX 数据集 VOC+YOLO 格式.zip」的发布方式,在红外电力巡检、烟草病虫害这些垂直领域已经成了事实标准,因为发布者清楚下游消费方用什么工具不可控。你拿到手要做的不是质疑为什么给两份,而是先确认两份标注数据对不对得上,再做自己的训练计划。
3. 从 VOC 转到 YOLO:自写转换脚本与三个必调的坑
3.1 开工前的检查:先数文件再动手
标题写着 4315 张,解压后第一步不是写脚本,而是核对数量。我一般先跑一个命令,确认三个数字:图像数量、XML 数量、TXT 数量。
ls JPEGImages | wc -l ls Annotations | wc -l ls labels | wc -l正常情况三个数都是 4315。如果 Annotations 少了十几份,多半是标注员漏存;如果 labels 比 JPEGImages 多,说明存在无对象标注的空文件,这两个情况在训练前必须处理。空 TXT 在 YOLO 训练里不会报错,但会让这张图永远没有正样本,白白浪费训练时间,还会在验证时拉低召回率。检查完数量再看一眼类别名,不同数据包可能叫landslide、slump、crack,或者干脆全部归成一类。标题没写类别数,那就在Annotations里抓一次全量name字段:
grep -rh "<name>" Annotations | sort | uniq -c这一步输出的类别名和数量就是后续配置 YAML 的依据,不要自己猜。
3.2 转换脚本:xml 解析到 txt 的完整套路
确认没问题后,按需写转换脚本。虽然包里已经给了 YOLO 格式,但如果你后面自己补充标注、合并别的数据集,这步躲不掉。我用 Python 的标准库写,不引额外依赖,跑起来最省事:
import os import xml.etree.ElementTree as ET from pathlib import Path class_names = ["landslide"] # 以实际类别名为准 def voc_to_yolo(xml_file, out_txt, class_names): tree = ET.parse(xml_file) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_names: continue # 过滤掉不需要的类 cls_id = class_names.index(name) # 兼容没有 difficult 字段的标注 diff = obj.find("difficult") difficult = int(diff.text) if diff is not None else 0 if difficult == 1: continue # 难样本要不要保留看任务,检测大滑坡体时可跳过 box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) # 像素坐标 -> 归一化中心点与宽高 cx = (xmin + xmax) / 2.0 / img_w cy = (ymin + ymax) / 2.0 / img_h bw = (xmax - xmin) / img_w bh = (ymax - ymin) / img_h # 越界裁剪,后面细讲 cx = min(max(cx, 0.0), 1.0) cy = min(max(cy, 0.0), 1.0) bw = min(bw, 1.0) bh = min(bh, 1.0) lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") with open(out_txt, "w", encoding="utf-8") as f: f.write("\n".join(lines)) xml_dir = Path("Annotations") out_dir = Path("labels") out_dir.mkdir(exist_ok=True) for xml_path in xml_dir.glob("*.xml"): out_path = out_dir / (xml_path.stem + ".txt") voc_to_yolo(str(xml_path), str(out_path), class_names)这段转换最基本的输入输出:XML 解析出宽高和每个目标框,归一化后填六个小数精度,逐行写 TXT。精度取六位已经足够,因为 1920 宽的图像一像素对应 0.0005 左右,六位小数远小于一个像素误差。
3.3 三个必调参数:越界框、difficult 与类别起始序号
第一个必调是越界框。航拍影像尤其是无人机拼接图,边缘经常出现目标被截断,XML 里 xmax 可能写成图像宽度加几十像素。直接转会产生大于 1 的归一化值,YOLO 训练时 loss 出现 NaN 是很常见的翻车现场。解决方式就是代码里的 clip 操作,把 cx、cy、w、h 全部限制在 0 到 1 之间。
第二个必调是 difficult 字段。VOC 规范里 difficult=1 代表难样本,通常指目标极小或严重遮挡。检测常规目标时可以跳过,但滑坡检测里难样本往往是最需要学习的那一批——滑坡初期的小裂缝、被植被遮挡一半的滑体。我的实际建议是训练集把 difficult 也包含进去,但分配更低的样本权重,或者干脆单列一个困难样本验证集来观察模型尾部表现。要不要过滤,取决于你的业务对「漏报」的容忍度。
第三个必调是类别起始序号。VOC 里类名是字符串,YOLO 里类别必须从 0 开始编号,很多人转换后从 1 开始,训练时模型输出的 class_id 和标注错位一位,mAP 曲线看起来正常但实际推理时全部类别漂移。转换脚本里用class_names.index(name)来编号,能保证始终从 0 开始对应。加新类时记得只改 class_names 列表的顺序,不要自己在循环里维护编号变量,那是在制造问题。每转完一批,随机挑三张图,把 TXT 里的坐标可视化画回原图检查一遍,这一步能挡住九成低级错误。
4. 拿这份数据跑通 YOLOv8:最小训练命令与必调参数
4.1 一张表列好先决条件:到哪一步才算环境就绪
常见做法是用 ultralytics 的 YOLOv8 跑这份数据,Python 生态最省事。先对照这张表自查环境,避免训练到一半发现算力不够:
| 项目 | 建议配置 | 说明 |
|---|---|---|
| 操作系统 | Windows 10/11 或 Ubuntu 20.04+ | Windows 下注意路径全英文 |
| Python | 3.8 到 3.11 | 3.12 某些 CUDA 版本适配有问题 |
| 显卡 | NVIDIA 显卡,显存 8G 起 | 8G 跑 v8s 的默认 batch 刚够 |
| CUDA | 11.8 或 12.1 | 与 PyTorch 版本对应 |
| PyTorch | 2.0 以上 | 低版本兼容性差,新功能缺失 |
| 存储 | 至少 10G 空闲 | 原图加训练日志加权重 |
环境配置是这门手艺里玄学最多的一环,CUDA 版本不匹配导致的报错能占初学者排错时间的一半。我的做法是直接创建独立 conda 环境,装好 ultralytics 和对应 torch 版本,不碰系统全局 Python。先跑一个yolo detect predict拿官方权重做验证,能出图说明环境闭环了,再上这份数据集。
4.2 配置数据集 yaml:路径、类别数、train/val 划分
YOLOv8 的训练入口是数据集 YAML,它告诉框架去哪找图片和标签,以及类别信息。这份数据包按上一章检查完,类别名假设为landslide,那 YAML 这样写:
path: D:/datasets/landslide # 数据集根目录,绝对路径,不要用相对路径 train: images/train # 训练图片目录,相对于 path val: images/val # 验证图片目录,相对于 path nc: 1 names: 0: landslide这里先手动把 labels 和 images 按比例切成train、val两个子目录。切分时要额外注意一个隐藏点:航拍影像常来自同一个大场景的滑窗切片,相邻切片的标注高度相关,如果随机分,验证集会混入大量与训练图几乎相同的场景,验证 mAP 虚高。正确的划分是按影像来源分桶,同一个原始场景的全部切片只能进 train 或 val 一边。标题没给出每个切片来源,稳妥做法是先看文件名前缀,按前缀分组再划分。
import os import shutil import random from collections import defaultdict random.seed(42) src_img = "images" src_lbl = "labels" train_img, val_img = "images/train", "images/val" train_lbl, val_lbl = "labels/train", "labels/val" # 按文件名前缀分组,模拟按场景划分 files = [f for f in os.listdir(src_img) if f.endswith(".jpg")] groups = defaultdict(list) for f in files: prefix = f.split("_")[0] # 例如 scene1_001.jpg -> scene1 groups[prefix].append(f) train_files, val_files = [], [] for prefix, flist in groups.items(): random.shuffle(flist) split = int(len(flist) * 0.8) train_files += flist[:split] val_files += flist[split:] def copy_set(files, img_src, lbl_src, img_dst, lbl_dst): for f in files: shutil.copy(os.path.join(img_src, f), os.path.join(img_dst, f)) lbl = f.replace(".jpg", ".txt") shutil.copy(os.path.join(lbl_src, lbl), os.path.join(lbl_dst, lbl)) os.makedirs(train_img, exist_ok=True) os.makedirs(train_lbl, exist_ok=True) os.makedirs(val_img, exist_ok=True) os.makedirs(val_lbl, exist_ok=True) copy_set(train_files, src_img, src_lbl, train_img, train_lbl) copy_set(val_files, src_img, src_lbl, val_img, val_lbl) print(f"train {len(train_files)} val {len(val_files)}")场景前缀分组的意义在于防止数据串扰。如果全随机分,同一滑坡场景的两个相邻切片一个进训练一个进验证,模型其实已经见过验证图的内容了,测试分数根本没有参考价值。
4.3 训练命令与关键超参:imgsz、batch、epochs、预训练权重
YAML 就位后,训练命令本身很短:
yolo detect train \ data=landslide.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ workers=4model=yolov8s.pt是官方预训练权重,框架会自动下载。预训练模型下载这一步很多人卡住,内网环境建议提前手动下载放到项目目录下,再通过model=./yolov8s.pt指定本地路径。预训练权重对这个航拍滑坡数据集的价值是让特征提取器从自然图像域起步,FG 收敛比随机初始化快得多,最终 mAP 也普遍高出几个点。显存只有 8G 的机器把 batch 降到 8,workers在 Windows 上建议保持 4,太高容易踩 DataLoader 的坑。
imgsz=640不是拍脑袋定的。滑坡体目标通常比行人、车辆大得多,很多框占图像宽高的 30% 以上,640 的分辨率不会把目标压没。但如果你的任务里还有细小的裂缝、碎屑坡脚这类小目标,建议单独跑一组imgsz=1280对比,代价是训练时间约为 4 倍、显存占用大幅上升,可以用后续的滑窗推理来缓解。这里有一个关键信号要看:训练时的box_loss曲线,如果 loss 降不下去但cls_loss正常,普遍是目标框大小分布太宽,模型对小框回归能力不足,优先调 imgsz 而不是盲目加层。
遥感影像的数据增强有个特殊坑:HSV 颜色增强默认开启,但航拍滑坡影像的颜色分布和地物纹理高度相关,植被、裸土、水体的色调被大幅扰动后,模型很容易学到错误的颜色特征。常见做法是训练时把hsv_h、hsv_s、hsv_v显式调低,或者直接设为 0。
yolo detect train \ data=landslide.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ hsv_h=0.01 \ hsv_s=0.2 \ hsv_v=0.1 \ mosaic=1.0 \ fliplr=0.5 \ flipud=0.0fliud=0.0是航拍数据处理里常被忽略的一条。航拍图方向通常和飞行航向强相关,上下翻转会制造出大量现实中不存在的排列,且滑坡的光照阴影方向会被破坏。左右翻转则相对安全,mosaic 拼接保留 1.0 是因为它能让模型在小目标上积累更多上下文。这几个参数比学习率和优化器选择更能决定这份数据集的最终表现。
5. 避坑清单:这份滑坡数据集最容易翻车的五个现场
5.1 解压路径的隐形炸弹:中文目录、多级嵌套、伪加密
现象:数据集解压到本地后,训练时提示AssertionError或图片路径不存在,但明明目录里能看到文件。
原因:zip 包内如果带着套一层的顶层目录,解压后实际图片在航拍滑坡数据集/images下一层,YAML 里的路径就会指错;更隐蔽的是 Windows 中文路径下,部分 OpenCV 版本读取文件会直接失败,报错还不是「找不到文件」,而是莫名其妙的cv2.error。另一个我遇到过的情况是 zip 设置了伪加密——压缩包把每个文件标记成需要密码,但实际上不输入密码也能解压,有些解压工具会弹窗卡住,让人误以为文件坏了。
解决:目录一律改名为纯英文短路径(如D:\data\landslide),先打开压缩包看第一层目录结构,确认没有多余嵌套;用 7-Zip 打开后直接拖出文件而不是双击解压,能绕过大多数伪加密弹窗。解压完成后跑一遍图片数量校验,再继续往下做。
5.2 标注框越界导致训练 loss 异常
现象:训练前几个 epoch 的box_loss在正常下降,突然某个 epoch 跳出断崖式峰值,甚至出现 NaN,之后 mAP 波动巨大。
原因:上一章提到过,XML 里标注框超出图像边界,转换后的归一化坐标大于 1 或小于 0,YOLO 在计算 IoU 和损失时遇到非法的框坐标,反向传播就会炸。很多标注工具允许标注时把框拖到画布外,航拍大图被裁剪成切片时尤其容易产生这类数据。
解决:训练前写个脚本扫描全部 TXT,统计超出 0 到 1 范围的坐标值,一旦发现就检查对应原图裁剪逻辑。修复是个体力活——正确的做法是重新执行带 clip 的转换脚本,而不是手工改 TXT,因为 clip 之后框的面积和中心点可能受影响的,手工逐个改容易漏。批量 clip 后再跑一次训练,loss 曲线就正常了。
5.3 类别不均衡:滑坡主体多、裂缝少
现象:训练曲线看起来收敛了,但验证集上裂缝类别的召回率只有 20%,滑坡主体却有 85%,整体 mAP 被平均分拉得还行,一拆类目就露馅。
原因:4315 张影像里滑坡主体框可能占了九成,裂缝、碎屑流等附属类别只有零星几百个框,模型被大类别主导,小类别几乎学不到。标题没列出类别清单,拿到数据后先看uniq -c的结果,如果符合这个分布,就是典型的长尾问题。
解决:先按类别统计框数量,对样本量少的类别做复制粘贴增强,或者对包含该类别的图片提高采样权重。Ultralytics 框架里可以用weights参数指定每类的损失权重,我把少数类权重调到 3 到 5 倍之后,召回率通常能拉回 55% 以上。注意权重不要一次拉太高,否则模型会出现大量把大目标误判成小类别的过拟合式输出,反而不利于整体 mAP。
5.4 随机划分数据导致验证虚高,部署后现原形
现象:训练时验证 mAP 高达 0.9,部署到一段新的航拍视频上,识别效果惨不忍睹,漏检一大堆。
原因:这是航拍类数据集最容易踩的坑,我在 4.2 节已经说到了场景串扰。如果划分训练集和验证集时是纯随机抽的,同一场景的相邻切片会同时出现在两边。模型在训练时已经见过验证切片的内容,分数自然漂亮,但遇到没见过的场景就失去泛化能力。滑坡检测的应用场景几乎都是新地区、新地形,验证集若不能反映这一点,训练就是自欺欺人。
解决:严格按文件名前缀或日期进行分组划分,保证同一个原始场景的所有切片只落在训练集或验证集一侧。把划分前后两组实验的 mAP 对比一下,你往往会看到同一个模型从 0.9 掉到 0.7 左右,这个 0.7 才是真实水平。把这次血泪经验记录下来,以后做任何遥感数据集,划分代码里第一件事就是按场景分桶。
5.5 显存不足和训练中断:小卡救急三板斧
现象:12G 显存跑imgsz=640, batch=16中途CUDA out of memory,训练了几个小时直接白干;或者训练中断后一切重来,费时费力。
原因:YOLOv8 默认的 batch 对显存不友好,航拍影像纹理丰富,即使分辨率不高,特征图的内存占用也比普通数据集更大。初学者往往直接照抄别人的训练命令,没考虑自己的显卡实际余量。
解决:第一板斧,把 batch 降到 8,workers降到 2;第二板斧,把imgsz降到 480,代价是损失部分小目标精度,适合先跑通流程;第三板斧,开启梯度累积,Ultralytics 框架可以通过设置batch=8搭配增大训练迭代来达到等效大 batch 的效果。另外养成训练前先跑 5 个 epoch 试水的好习惯,确认不爆显存、loss 能降,再跑正式的长训练。YOLO 训练中断后没有内置保存「后悔药」——只有设好save_period定期保存 checkpoint,万一中断了也能从断点续跑。所有超参数实验前都先问自己一句:如果这个模型明天要部署在新航线上,还能不能有现在的效果。
6. 从 mAP 到能用的决策阈值:用验证集反向压出你真正要的模型
训练收敛只是第一步,航拍滑坡检测落地时,核心不是 mAP 而是「在固定的误报预算下,能抓到多少真滑坡」。我常用的一个方法是把验证集当成压测集,用训练好的best.pt跑推理,扫描置信度门限从 0.1 到 0.9 的 F1 分数,找到业务最舒服的那个点。
from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") best_thresh, best_f1 = 0.5, 0.0 for thresh in [x / 10 for x in range(1, 10)]: metrics = model.val(conf=thresh, iou=0.5, verbose=False) f1 = metrics.box.f1[0] # 取第一类的 F1 if f1 > best_f1: best_f1, best_thresh = f1, thresh print(f"best threshold {best_thresh}, f1 {best_f1}")这事说起来像玄学,但实际效果很直接:有些模型在默认 0.25 置信度下误报一堆裸土和道路,把门限抬到 0.5 后误报减半、召回只掉一点;反过来如果业务要求漏报率极低,门限降到 0.15 多抓几个假阳性也能接受。适应这个环节的唯一办法是把验证集的错误预测框全部可视化看一眼,归类一下哪些是地形阴影、哪些是道路路基,你就知道这个模型真正在什么条件下会翻车。
另一个更贴近航拍场景的技巧是大图滑窗推理。航拍原始分辨率往往不止 640,直接把整张图喂给模型会把小目标压没,我的做法是切成 50% 重叠的 640 切片,逐块推理后再按坐标合并结果。合并时同一个目标会被多个切片重复检测到,用 NMS 的 IoU 阈值收敛一下即可。配合同样的滑窗做训练数据裁剪,模型对小裂缝的召回率有明显提升。
我自己养成的习惯是每次训练结束,固定验证集,把所有 FP 样本打印出来当图册翻一遍。滑坡检测真正的落地瓶颈通常不是网络结构,而是「阴影被当滑坡」「道路被当裂缝」这类相似地物的区分度。先看模型错在哪,再决定是加数据还是调阈值,比盲目堆参数省时间得多。希望这份航拍滑坡数据集的完整折腾路径能帮到你,也祝你训出来的模型第一次上真实航线就不掉链子。
本文还有配套的精品资源,点击获取