简介:面向YOLO目标检测的高分辨率卫星图像标注数据集VHR-10,包含800张来自Google Earth和Vaihingen的遥感图像及对应标注文件,覆盖飞机、轮船、储罐、棒球场、网球场、篮球场、地面跑道、港口、桥梁和车辆10个类别,适合计算机、电子信息等专业学生完成课程设计、期末大作业或毕业设计。压缩包共2612个文件,主要有650张jpg图像、650个xml标注文件及1307个txt文本,标注信息可直接用于YOLO系列模型训练,整体大小约61.15MB,目录结构清晰,便于按需调用。目前已有321人学习使用。借助该数据集可省去手动标注的繁琐过程,直接开展遥感目标检测实验、算法对比与模型调优;配套的缓存文件也有助于快速搭建训练流程,是入门遥感图像目标检测的实用资源。
1. 卫星图像目标检测,绕不开的训练集与标注这道坎
YOLO 在车辆、船舶、建筑物等卫星图像目标检测上的落地,最难的不是模型结构,而是你根本拿不到标好的数据。这就是为什么一份 800 张已标注卫星图像集会被当成硬通货:它直接跳过了从 0 开始标框、写 XML、转 YOLO 格式的过程。拿到手的正确姿势是解压后先做三件事:核对标签格式、检查类别分布、按同源规则切分数据集。这比先跑通 train.py 更重要。适合读这篇文章的人有三类:用遥感图像做毕设或课题的学生、刚接到业务方“帮我识别图上有几栋楼”需求的算法工程师、以及想验证 YOLO 在小样本遥感场景下能不能用的 GIS 开发者。
2. 拿到已标注数据集:先核对 YOLO 标签格式与类别分布
2.1 已标注文件应该长什么样:YOLO 的 txt 格式
标题说“已标注文件可以直接使用”,但“可直接使用”在 YOLO 生态里有严格含义。YOLOv5/v8 要求每张图对应一个同名 txt 文件,里面每一行标注一个目标:类别序号、框中心点的 x 坐标、框中心点的 y 坐标、框宽、框高,其中四个坐标值都是 0 到 1 之间的相对值,即实际像素值除以图像宽高。解压 .rar 后先别急着训练,随便打开一张 txt 看一眼:
# 某一张卫星图的标注文件内容示例 0 0.3315 0.2412 0.0601 0.0339 1 0.6710 0.5513 0.1018 0.0715 1 0.7844 0.6078 0.0966 0.0675这段表示假设 0 代表 car、1 代表 ship,第一个框中心点落在图像横向 33.15%、纵向 24.12% 的位置,宽和高分别占整图宽高的 6.01% 和 3.39%。注意,无论原图多大,标注和图像之间都没有像素级依赖,所以任意缩放图像都不需要重新标注。
这里最容易出问题的是三个点。一是把 VOC 的 xml 或 labelme 的 json 当成“YOLO 格式就能用”,转换时把左上角右下角坐标算成了中心点,结果训练出来框全部偏移;二是类别序号没有和配置文件里的 names 列表对齐,原先标好的类别全乱了;三是 txt 文件里用了 tab 或者带了 BOM 头,训练时解析失败但报错不明显。从数据标注工具导出的文件尤其容易出现这类问题,建议把它当数据预处理的一部分来做,不要直接丢给训练脚本。
2.2 用脚本核查 800 张图的标注质量
800 张图看起来量不大,但靠人眼逐一抽查不可靠,正确的是写一个 Python 小脚本做四件事:统计每个类别的框数量、检查坐标越界、找出空标注文件、核对图像与标签文件名是否一一对应。
# check_label.py from pathlib import Path img_dir = Path("images") # 图像目录 label_dir = Path("labels") # YOLO txt 标签目录 cls_count = {} total_boxes = 0 bad_files = [] # 记录有问题的文件 # 第一步:两张 .txt 缺失检查,缺失 = 该图被当作背景处理 img_names = {p.stem for p in img_dir.glob("*.png")} label_names = {p.stem for p in label_dir.glob("*.txt")} missing_label = img_names - label_names if missing_label: print("缺标签的图像:", list(missing_label)[:10]) for label_file in sorted(label_dir.glob("*.txt")): if label_file.stat().st_size == 0: bad_files.append((label_file.name, "empty")) continue for line in label_file.read_text().strip().splitlines(): parts = line.split() if len(parts) != 5: bad_files.append((label_file.name, "format")) break cls, x_c, y_c, w, h = [float(v) for v in parts] if not (0 <= x_c <= 1 and 0 <= y_c <= 1 and 0 <= w <= 1 and 0 <= h <= 1): bad_files.append((label_file.name, "out of range")) break cls_count[int(cls)] = cls_count.get(int(cls), 0) + 1 total_boxes += 1 print("总框数:", total_boxes) print("每个类别的框数:", cls_count) print("问题文件:", bad_files[:10])脚本核心是逐行读 txt,拆成五个浮点数,再判断是否都在 0 到 1 之间。中心点必须落在图内,宽高理论上也不超过 1。越界不一定导致训练崩溃,很多训练流程会忽略越界框或裁剪它,但漏检率和指标可靠性会受影响。统计类别分布的用处是:如果某个类别只有五六十个框而其他类别有两三千个,那这个类很可能在训练后根本学不出来,要么补样本,要么在训练时单独配类别权重。
2.3 训练集/验证集划分与同源泄漏
800 张图按经验建议切成 7:2:1,即 560 张训练、160 张验证、80 张测试。但在卫星图场景有一个隐蔽的坑:如果这 800 张瓦片是从同一景原始影像切出来的,那随机划分会和常规自然图像截然不同。
| 划分方式 | 做法 | 对遥感场景的影响 |
|---|---|---|
| 不分组随机划分 | 整批 images 按比例乱序分给 train/val | 同一景影像的相邻瓦片可能同时出现在训练集和验证集,验证指标虚高,因为模型记住了同一区域的地物纹理 |
| 按原始影像分组 | 统计每张瓦片来源,同一来源的瓦片整体放入同一分桶 | 验证集和训练集地理不重叠,指标更接近新区域泛化的真实水平 |
如果压缩包里带有来源信息(比如文件名前缀相同),就按前缀分组再切分。没有的话,用一个简单的启发式:对文件名做哈希后取模分桶。这也能解释为什么你拿同一个数据集训练,别人说 mAP 有 0.8,你复现只有 0.65——很可能就是切分方式不同导致的评估偏差。另外再强调一次:始终保证验证集里没有伪造的“训练见过”的框,否则生产环境回退到新区域时,指标会暴跌。
3. 用 YOLOv8 在本地训练卫星目标检测模型
3.1 写 dataset.yaml 时容易错的三处
训练之前先建数据配置文件。路径写绝对路径还是相对路径,取决于你的工作目录,但建议第一次跑全部用绝对路径,把无关变量排除掉:
# dataset.yaml path: /home/user/remote_sense # 数据集的根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 test: images/test # 测试图片目录 nc: 2 # 类别数量 names: ['car', 'ship'] # 类别名列表三个常见错误。第一,path 写成相对路径但训练时换过当前工作目录,导致找不到图,YOLO 会直接报 FileNotFoundError;第二,train 和 val 写的是图片文件夹路径,而不是 txt 文件——YOLOv8 的 dataset.yaml 字段接受绝对路径,但要注意目录结尾不要带/,否则部分版本的路径拼接会重复出双斜杠;第三,names 列表顺序必须和标签 txt 里的数字序号一致,这一点最隐蔽,很多人在实验中途去调整了标签序号,结果训练出来的类别语义整体错乱,一个全天排错的时间就浪费在这里。
3.2 训练命令与参数表
对于 800 张这种规模的数据,我不会从随机初始化开始训练,而是用官方预训练权重微调。下面这个命令是 5GB 显存以上的机器都能跑的配置:
yolo detect train \ data=dataset.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=1280 \ batch=16 \ patience=25 \ device=0 \ workers=8- model=yolov8s.pt:加载 COCO 预训练权重。卫星图像里的目标和 COCO 天然不同,但低层特征(边缘、纹理、重复结构)是通用的,预训练带来明显的收敛加速。
- imgsz=1280 而不是默认的 640。卫星图像上的目标普遍偏小,如果你在 640 下检测车辆、小船舶,目标可能只有几个像素,模型很难学到有效特征。显存不够就退到 960 或 640,同时调小 batch。
- patience=25:验证集指标连续 25 轮不升就早停。卫星数据集小,过拟合来得快,早停务必开启。
- device=0 表示用第一块 GPU;没有 GPU 就改成 device=cpu,但 imgsz=1280 下 CPU 训练不现实,建议先用小模型跑 640 版本验证流程通畅,再上 GPU。
提示:imgsz=1280 的显存占用大约是 640 的三倍以上。遇到 OOM 先减小 batch,不要直接降图像尺寸,除非你确定目标在 640 下仍然大于 5 像素。
训练时还可以关注另一组超参数,它们对小样本遥感场景影响非常直接:
| 参数 | 取值建议 | 说明 |
|---|---|---|
| lr0 | 0.005 或 0.01 | 数据量小,lr0 调低一点能防止预训练权重被破坏 |
| mosaic | 1.0 或 0.5 | 对尺度跨度大的遥感目标有助益,但会破坏地理连续纹理,可以对比后取舍 |
| scale | 0.5 | 图像缩放增强范围,卫星目标密集时适当调大 |
| hsv_h / hsv_s | 默认 | 卫星影像色彩相对均匀,增强不必开太猛 |
3.3 训练过程看什么:loss 曲线与过拟合
YOLOv8 训练时终端会实时输出 box_loss、cls_loss、dfl_loss 以及 precision、recall。对小数据集,重点看 train 和 val 的 box_loss 曲线:train 一直降而 val 在某个 epoch 后回升,就是过拟合;val 指标上升太慢,八成是 imgsz 太小或标注本身有噪声。训练结束会在 weights 目录下产生 best.pt 和 last.pt 两个文件,best 按验证集指标自动挑选。生产上建议先拿 best.pt 做推理,若 best 与 last 的指标差距很大,说明训练波动明显,可以适当调大 lr0 或改用带 momentum 的优化器再跑一轮。
4. 预测验证:从 best.pt 到整景卫星图的输出
4.1 用 val 和 predict 验证模型效果
训练完之后不要直接在整张大图上跑推理,先用 val 看一遍指标:
yolo detect val model=runs/detect/train/weights/best.pt data=dataset.yaml输出会给出 mAP50、mAP50-95、precision、recall。卫星图像目标小、目标密度高,mAP50-95 偏低是正常的,更应该参考 mAP50 和 recall。recall 低说明漏检多,先做两件事:把推理 conf 阈值调低,训练时提高 imgsz。precision 和 recall 都低,就不是阈值问题了,回头查标注质量,空 txt 和类别不平衡是最常见原因。验证输出里的 confusion_matrix.png 也要看,它能直观告诉你哪些类别互相混淆,比如小型车辆和大型车辆在低分辨率下经常被混成一个类。
推理时除了单张图 predict,也能对目录批量推理:
yolo detect predict model=runs/detect/train/weights/best.pt \ source=test_images/ \ imgsz=1280 \ conf=0.25 \ iou=0.45 \ save=True \ save_txt=Truesave_txt=True 会把坐标以 YOLO 格式写进 txt,省去自己输出的步骤。注意预测输出的 txt 坐标是相对当前推理图像的宽高的,如果最后要换算成原始瓦片的绝对像素,需要乘回推理图尺寸。
4.2 遥感小目标的锚框与推理尺寸陷阱
YOLOv8 是 anchor-free 的,严格说没有固定锚框,但特征金字塔的浅层、中层、深层分别负责不同尺度。卫星影像上一个 30m 的船舶在 0.5m 分辨率下是 60 像素,在 10m 分辨率下只有 3 像素,这就是为什么同一个模型换个分辨率后,阈值哪怕不变,漏检情况也完全不同。常见做法是对原始大幅卫星图切瓦片,比如 10000×10000 的原图切成 1280×1280 的瓦片,相邻瓦片重叠 64 像素,再逐片推理。切瓦片时标签坐标要同步平移:
# 假设切图窗口的起始像素是 (x0, y0),新瓦片宽高是 w, h # 原标注框中心点像素坐标是 (cx_pix, cy_pix) new_cx = (cx_pix - x0) / w new_cy = (cy_pix - y0) / h new_w = bbox_w_pix / w new_h = bbox_h_pix / h核心是减掉偏移再做归一化。切图时目标在边界被一劈两半的情况很常见,经验做法是:框与瓦片相交面积占原框面积的比例大于 0.3 就保留该框,否则丢弃。这是遥感数据扩充和推理里最常踩的坑,很多人忽略它,模型会把两截车辆学成错误类别。
另外,如果你要检测的船舶或建筑物方向性很强、需要旋转矩形框,水平框方案的 YOLO 不够用,常见做法是转 DOTA 数据集格式后使用 mmrotate 训练。YOLO 系的水平框和 DOTA 的旋转框是两套坐标体系,不要混着用。
4.3 后处理:conf/iou 阈值与瓦片拼接去重
切瓦片推理的另一个问题是同一目标会被相邻瓦片重复检出,拼接前要做跨瓦片去重。YOLO 自带的 NMS 只是在单张瓦片内做,跨图不会去重。常见做法是把所有瓦片的预测框先还原为原图坐标,再跑一次全局 NMS 或 WBF(加权框融合)。WBF 对置信度中等但多个视角重复检出的目标往往效果好于简单 NMS,因为它融合的是几何位置证据而不是直接丢弃低置信度框。读回 save_txt 输出的文件后,用约 30 行的非极大值抑制代码就能完成拼接去重;先试 NMS,效果不够再看 WBF。这套流程对无人机正射影像同样成立,只要把瓦片来源从卫星图换成正射图即可。
5. 已标注数据不够时的保精度技巧
5.1 K 折交叉验证评估这 800 张图
样本少时单次 7:2:1 的评估方差大,更好的做法是 K 折交叉验证。对 K=4 或 K=5 拆,每折训练一次并记录指标,最后取均值,得到的结果能真实反映模型在数据层面的可复现性。折间必须按 2.3 节提到的同源规则切分,否则指标虚高没有参考价值。具体操作上可以用结果最好的那一折权重作为最终模型,其他折的指标只用来估计稳定性。如果训练环境是 CPU,K 折的时间成本会成倍增加,建议先单次训练验证流程,K 折留到有 GPU 时再跑。
5.2 用伪标签自动扩充卫星图样本
如果手头还有一批没有标注的同分布卫星图,一个提精度的手段是伪标签:用训练好的 best.pt 对未标注图做推理,把置信度超过阈值的框写成 YOLO 格式的 txt,与真实标注混合后增量训练。操作上就是把第 4.1 节 predict 命令的 save_txt 输出改名复制到待扩充数据集的 labels 目录里,命令行不需要额外改动。这里有两个约束:伪标签只做训练集,绝对不要混进验证集或测试集;伪标签的框需要经过人工抽查或按类别统计过滤后再用,否则错框会被模型当正确答案吸收。卫星图像上我会先从 0.75、0.8、0.85 三个置信度档位各做一次增量训练对比,选验证集指标最高的那档作为最终数据配方,这比盲目压低阈值收进来的大量错框更可靠。
本文还有配套的精品资源,点击获取