简介:一套面向人工智能目标检测方向的飞机卫星图数据集,聚焦遥感场景下的单类别目标检测,包含1000张1024x1024高分辨率彩色图像和1000个XML标注文件,标注了飞机边界框坐标,可直接用于训练YOLO、SSD、Faster R-CNN等主流检测模型。压缩包共有2000个文件,除图像与标注外还附info.txt元数据,总大小约291.76MB,便于下载与本地部署。当前已有634人学习使用,覆盖遥感图、卫星图、飞机识别等应用方向。由于卫星图像存在阴影、遮挡、反射等复杂背景,该数据能有效锻炼模型对目标与环境的区分能力,为遥感图像分析、智能监控、无人驾驶等实际场景提供高质量训练素材。
1. 飞机卫星图目标检测数据集:先弄懂数据再谈训练才不白费力气
做遥感目标检测的人,拿到一个叫“飞机卫星图3”的人工智能目标检测数据集时,第一反应大多是赶紧配环境、跑训练。但版本号里带个“3”,往往意味着这是第三版迭代,常见改动包括类别合并、删除错误标注、补充新机场场景。卫星图里的飞机目标通常只有几十甚至十几个像素,机场背景又杂乱,数据质量直接决定模型上限。这个标题背后是一整套标准做法:先拆目录和标注,再转格式、配 YAML、调参数,最后用旋转框和切片推理收尾。适合正在做遥感目标检测、移动小目标检测,或者刚拿到自定义数据集想用 YOLO 训练自己的数据集的人。接下来按落地顺序把每一步讲清楚。
2. 拆开飞机卫星图数据集:目录规范、标注格式与可视化校验
2.1 目录结构长什么样,VOC 与 YOLO 两种组织方式怎么选
飞机卫星图数据集最常见的两种组织方式,一种是 VOC 风格,JPEGImages放原图、Annotations放 XML;另一种是 YOLO 风格,images/train和labels/train一一对应,标签是同名.txt。第三版迭代的数据集通常已经整理成 YOLO 风格,因为现在本地跑训练的主流流程是 Ultralytics YOLO,直接吃这种结构。拿到手先别急着改文件,第一步是用一行命令看全貌:
find aircraft_satellite_v3 -maxdepth 3 -type d | sort常见输出会包含train、val、test三个划分,每个划分下再分images和labels。我一般会确认三件事:train/val/test 比例是否合理、是否每个 images 下的文件都有对应的 txt、test 划分是不是真的不带标签。第二个问题尤其关键,因为很多人会把 test 也标注了,这在训练时会造成信息泄露,评估指标虚高。
VOC 和 YOLO 怎么选,看你要接什么框架。如果打算用 Ultralytics YOLO 或者 SAHI,直接保留 YOLO 格式最省事;如果想跑 mmrotate 做旋转框检测,VOC 的 XML 反而更好转 DOTA 四点格式。第三版数据集常见的情况是两种格式都给,但以 YOLO 为主。不管哪种,建议训练前统一整理成下面这种标准结构,避免脚本里到处写死绝对路径:
datasets/ └── aircraft_v3/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── aircraft_v3.yaml2.2 标注格式逐字段拆解:从 XML 到 YOLO 的换算脚本
如果拿到的是 VOC XML,就要先转成 YOLO 的归一化 txt。这里有一个从业者普遍认同的约定:YOLO 格式每行是class x_center y_center width height,四个坐标全部除以原图宽高,归一到 0~1。转格式时最容易翻车的点有两个:一是坐标越界不处理,训练时出现无效框;二是过小的框不过滤,一个目标只有四五个像素,模型学不到任何东西。
下面这个脚本是常见做法,可以直接套用:
import xml.etree.ElementTree as ET from pathlib import Path def xml_to_yolo(xml_path, out_dir, class_map): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_map: 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) # 标注工具偶尔会写出界坐标,先裁回图像边界 xmin = max(0, min(xmin, w - 1)) xmax = max(0, min(xmax, w - 1)) ymin = max(0, min(ymin, h - 1)) ymax = max(0, min(ymax, h - 1)) # 过滤面积过小的标注,常见阈值是两个像素 if xmax - xmin < 2 or ymax - ymin < 2: continue cx = (xmin + xmax) / 2.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") out_txt = Path(out_dir) / (Path(xml_path).stem + '.txt') out_txt.write_text('\n'.join(lines) + '\n') class_map = {'airplane': 0} # 按自己数据集的类别顺序改 xml_to_yolo('000001.xml', 'labels', class_map)这段代码的逻辑是:先读 XML 里的图像宽高,再遍历每个 object,取 bndbox 坐标。裁剪边界是为了防止后续训练时报 “box out of bounds” 或者出现负数 anchor。按像素面积过滤是为了清理垃圾标注。最后把像素坐标归一化。参数里最值得改的是class_map,如果数据集里区分了民航机、战斗机、停场飞机,就要把类别名和 id 对应好;卫星图数据集常见的是只标注airplane一类,因为从高空分辨机型本身就很难,强行划多类只会拉低精度。
2.3 把标注画回原图:可视化脚本与三种异常结果
格式转完不等于数据干净。我见过太多人数据集一解压就训练,跑完发现 mAP 不低但实际推理惨不忍睹,回头一看标注框有的框在飞机头、有的框进半个机库。所以训练前必须做一步可视化校验,把标签框画回原图人工抽检。
import cv2 from pathlib import Path def draw_yolo_boxes(img_path, label_path, class_names, out_path): img = cv2.imread(str(img_path)) h, w = img.shape[:2] with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue cls = int(parts[0]) cx, cy, bw, bh = map(float, parts[1:]) xmin = int((cx - bw / 2) * w) ymin = int((cy - bh / 2) * h) xmax = int((cx + bw / 2) * w) ymax = int((cy + bh / 2) * h) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) cv2.putText( img, class_names[cls], (xmin, max(ymin - 5, 0)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2 ) cv2.imwrite(str(out_path), img) draw_yolo_boxes( 'images/train/000001.jpg', 'labels/train/000001.txt', ['airplane'], 'check_000001.jpg' )这段脚本本身不难,重点是看三类异常。第一种是“框偏”,框只罩住机身一半,通常是标注工具在机头方向判断上有偏差,这类样本多了会拉低定位精度。第二种是“漏标”,图里有十架飞机只标了三架,模型训练时会把没标的部分当背景学,推理时漏检就在所难免。第三种是“整图空标”,某些原图没有任何 txt 内容,留着只会稀释正样本。抽检时每类划分至少看 50 张图,尤其是 val 和 test,因为它们直接影响你对模型真实水平的判断。这套可视化脚本你也可以直接用于训练结束后的预测可视化,一鱼两吃。
2.4 什么算“干净”的飞机检测数据集
数据清洗这件事没有统一标准,但对飞机卫星图数据,有几个从业者共识:第一,图像分辨率要一致或者至少在同一量级,卫星图常见的是几百到几千像素的切片,混入太低分辨率的图会干扰模型对目标尺寸的预期。第二,样本要覆盖多个机场、多种光照、多个季节,如果全部来自同一场景,模型容易过拟合到背景纹理上。第三,类别分布要记录清楚,第三版数据集常见改动就是合并类别,因为旧版把“飞行中飞机”和“停机坪飞机”分两类,很多标注员分不清,模型也分不清,最后合成一类反而能提升召回。
第四,也是容易被忽略的,是备份原始标注文件。转格式、清洗、过滤都会覆盖原始数据,跑完一轮发现处理逻辑有误时没有后悔药。我一般的做法是把原始数据集目录权限设成只读,所有转换产物输出到另一个目录,这样每个版本都有据可查。数据集的干净程度直接决定后面训练要调多少参数,前面这部分多花半小时,后面能少熬两夜。
3. 用 Ultralytics 把数据集喂进 YOLO:环境、目录与首条训练命令
3.1 环境配置:一条命令装完,报错却常常在这两处
跑 YOLO 训练,目前最顺手的工具链是 Ultralytics。环境配置本身不复杂,但“适合0基础纯小白”的教程最容易卡在两个地方:一是 pip 源慢,二是装完发现 OpenCV 起不来。常规做法是直接用清华源装:
pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple python -c "import ultralytics; print(ultralytics.__version__)"能打印版本号说明核心装好了。如果 import 报错,第一优先排查是不是缺了系统库,最常见的是libGL.so.1错误,这是 OpenCV 依赖的图形库,在轻量容器里经常没有:
apt-get update && apt-get install -y libgl1 libglib2.0-0第二个高频问题是 CUDA 不可用。yolo命令能跑但速度奇慢,多半是 torch 装成了 CPU 版。确认方法是用nvidia-smi看驱动支持的 CUDA 版本,再用python -c "import torch; print(torch.cuda.is_available())"确认 PyTorch 能否调用 GPU。常见做法是先装对应 CUDA 版本的 PyTorch,再装 ultralytics,顺序反了会重复下载大文件。我的建议是不要为了求新装最新版,选当前稳定版本,训练脚本能少踩一半的坑。
3.2 数据集 YAML 写法和目录整理脚本
环境就绪后,把上一章整理的目录写成一个 YAML。这个 YAML 是数据集与训练器的接口,写错一个字段,训练器就找不到图。
# aircraft_v3.yaml path: datasets/aircraft_v3 # 相对于当前工作目录,别写绝对路径 train: images/train val: images/val test: images/test names: 0: airplane这里有几个参数值得说清楚。path是数据集根目录,我习惯写相对路径,方便换机器后不用改配置;train和val必须存在,test可选,有就留着做最终验证;names的 id 必须和标注 txt 里的第一个数字完全一致,标注文件里是 0,YAML 里就别写成 1,否则类别错位。有些数据集标注里类别顺序是乱的,比如 txt 里用了 2,YAML 里没写第 2 类,训练时就会直接报 “class index out of range”。
目录整理这个活儿,手动拖文件夹最容易漏文件。我一般用一段小脚本把分散的原图和标签统一拷贝到标准结构:
import shutil from pathlib import Path src = Path('aircraft_satellite_v3') dst = Path('datasets/aircraft_v3') for split in ['train', 'val', 'test']: (dst / 'images' / split).mkdir(parents=True, exist_ok=True) (dst / 'labels' / split).mkdir(parents=True, exist_ok=True) # 原图可能是 jpg/png/tif,卫星图尤其留意 tif for img in (src / split).glob('*'): if img.suffix.lower() in ['.jpg', '.jpeg', '.png', '.tif', '.tiff']: shutil.copy(img, dst / 'images' / split / img.name) txt = src / split / (img.stem + '.txt') if txt.exists(): shutil.copy(txt, dst / 'labels' / split / txt.name) else: print(f'missing label: {txt}')逻辑说明:先建目录,再按扩展名过滤图像,复制图像的同时查找同名 txt,找不到就打印出来。打印出来的这些缺失标签文件,如果是在 train 里,建议直接删掉对应图像;如果是在 test 里,反而没关系,test 本来就不需要标签。参数上唯一要注意的是glob('*')会抓所有文件,所以必须靠suffix白名单过滤,别偷懒直接复制所有文件。
3.3 第一个训练命令:参数逐项说清
目录和 YAML 就绪后,第一个训练命令可以跑起来了。这一步的目标不是刷最高精度,而是把流程跑通,所以参数偏保守:
yolo detect train \ data=datasets/aircraft_v3.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ cache=True \ device=0逐项说明:data指向刚才写的 YAML;model填预训练权重,官方模型仓库里也有不同规模的选择,想尝鲜可以直接换成同系列更高版本,但第一次跑建议用 s 这个尺寸,速度和精度平衡;epochs=100是训练轮数,遥感数据不像 ImageNet 那么海量,100 轮足够看到收敛趋势;batch=16是批大小,取决于显存,8GB 显存就减到 8;patience=20的意思是验证集指标连续 20 轮不涨就早停,可以有效避免无效的跑轮次;cache=True是把图像提前加载进内存,小数据集能明显提速,如果内存不够就改成cache=False。
这条命令跑完,runs/detect/train/下会生成权重文件和验证结果图。第一次跑的重点是确认三点:训练 loss 在下降、验证集 mAP 在上升、显存没有被撑爆。这三个都正常,再谈后面的调参。如果一上来就丢指令跑几十小时,跑完发现数据有问题,浪费的时间才是最贵的成本。
4. 训练参数这样调:小目标、背景占比与评估指标读法
4.1 主干网络与图片尺寸:小目标的第一个拐点
飞机卫星图的目标检测,核心矛盾是“目标太小”。一张 1024×1024 的卫星图,一架停机坪上的飞机可能只有 20×30 像素,经过 YOLO 的 32 倍下采样后,在特征图上只剩不到一个像素,检测器自然看不见。所以 “移动小目标检测” 场景里,第一优先级往往不是换更强的 backbone,而是调整输入分辨率。
常见做法是把imgsz从 640 提到 1024 或 1280。这个过程带来的收益通常比换大一号模型更明显。但要注意:imgsz=1280时显存占用大约是 640 的四倍,batch 必须同步降下来,否则直接 OOM。我一般是这样权衡的:如果训练集里大多数目标宽度小于 32 像素,imgsz=1024起步;如果目标都在 50 像素以上,640 就够。yolov8s这类小模型在 1024 下的推理速度仍然可接受,yolov8m以上建议配合切片推理使用,否则单图耗时翻倍。
另一个容易被忽视的方向是数据增强里的尺度扰动。默认的scale=0.5意味着一半概率对图像做缩小,这对小目标来说等于雪上加霜。我的习惯是把scale调到 0.3 甚至更低,避免训练时频繁把本就很小飞机压得更小。目标尺度分布决定了这些参数怎么改,花十分钟统计一下标注框的宽高分布,比盲调十个参数都管用。
4.2 训练策略参数:epoch、batch、mosaic 与热启动
在数据集跑通的基础上,可以把训练参数调整到更适合卫星图的状态。下面这组参数是我在类似数据集上常用的起步配置:
yolo detect train \ data=datasets/aircraft_v3.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=1024 \ batch=8 \ mosaic=0.5 \ close_mosaic=10 \ fliplr=0.25 \ lr0=0.005 \ warmup_epochs=3 \ cache=True \ device=0参数含义逐一说。mosaic=0.5是把四张图拼成一张训练,本质是增加图片里的目标数量,对小目标数据很有帮助,但问题也明显:拼接会让飞机被切碎。如果目标极小,建议从 0.5 开始,观察训练 loss 异常就降到 0.2。close_mosaic=10表示最后 10 轮关闭 mosaic,让模型在接近真实分布的图片上微调,这个参数默认就有,但很多人不知道它的存在,导致最后几轮 loss 波动。fliplr=0.25是水平翻转概率,卫星图不存在“文字方向”问题,翻转是安全增强。lr0=0.005比默认值低一些,预训练权重下用更低学习率不容易破坏已有特征。
epochs=150配合patience早停可以接受,早期用yolov8s这种小模型多跑几轮成本不高。batch=8是因为imgsz=1024显存压力大,如果你的显卡有 24GB,可以试 16。特别提醒:在卫星图数据上,mosaic不是越高越好。当目标本身只有十几个像素时,四图拼接后单个目标进一步缩小,很多时候模型学到的不是“飞机”而是“噪声纹理”。这也是卫星图训练和自然图像训练最大的差别之一。
4.3 评估指标读法:mAP 背后的漏检定位
训练完先别急着部署,用验证集跑一次标准评估:
yolo detect val \ model=runs/detect/train/weights/best.pt \ data=datasets/aircraft_v3.yaml输出里最关键的是mAP50和mAP50-95。mAP50是 IoU 阈值 0.5 下的平均精度,衡量检测器“能不能找到目标”;mAP50-95是 0.5 到 0.95 每 0.05 步进一次再取平均,衡量“框得准不准”。对飞机这类小目标,常见现象是 mAP50 尚可但 mAP50-95 偏低,说明框的定位误差偏大,这时候优先检查是不是输入分辨率不够,而不是盲目堆模型参数。
除了看数字,还要看runs/detect/val/下生成的混淆矩阵和预测图。混淆矩阵找漏检:看 airplane 类的 Recall 是多少,如果只有 0.6,说明四成飞机没检出来。预测图找误检:看有没有把建筑物屋顶、地面标志线当成飞机。这两个问题的解决路径完全不同,漏检要提高召回,误检要增强负样本。另外,很多人只看 best.pt,我习惯同时看last.pt的指标,如果 last 比 best 差很多,说明训练后期过拟合了,可以提前结束或加强增强;如果两者接近,说明模型收敛得比较稳定。
5. 飞机卫星图数据集的 5 个经典坑与排查方法
5.1 小飞机漏检:mAP 看着不低,一到推理就丢目标
现象:验证集 mAP50 有 0.8 以上,但用小图切片做推理时,图像里的小飞机一个都没框出来。原因:训练时imgsz用了 640,小目标在特征图上只占一个点,模型根本没机会学习。解决:把imgsz提到 1024 以上,并用第 6 章的切片推理跑大图。另外一个常见原因是训练数据里小目标占比本身太低,可统计标注框宽高分布确认。
5.2 坐标越界:训练日志里出现 invalid box
现象:训练刚开始没几步,日志里出现 “Invalid box” 或 loss 突然变成 NaN。原因:XML 转 YOLO 时没有做边界裁剪,某些框的 xmax 大于原图宽度,归一化后出现大于 1 的坐标。解决:在转换脚本里加上max(0, min(x, w - 1))这类裁剪逻辑,同时过滤掉面积小于 2 像素的框。前面第 2.2 节的脚本已经把这步写进去了,直接套用就不会踩这个坑。
5.3 背景占比过高:损失降不下去,误检满天飞
现象:训练的 box loss 一直在 1.5 左右,验证集上把机场跑道标志、建筑物屋顶都框成飞机。原因:卫星图里飞机占整图面积常常不到 1%,负样本压倒性占优。解决:一方面在数据层面增加飞机密集场景的图,另一方面调低scale增强,减少小目标的进一步缩小。还有个细节是检查有没有一整张图都没有标注的“空图片”,这类图会强行拉高背景权重,训练时可以直接剔除。
5.4 labels.cache 缓存污染:改了标签后训练结果没变化
现象:明明重新标注了数据,训练跑完 mAP 和上一次一模一样。原因:Ultralytics 第一次加载数据时会把标签信息缓存到labels.cache文件,后续直接读缓存,不会重新解析 txt。解决:每次替换标签文件后,删除数据集目录下的labels/*.cache,或者在训练命令里加cache=False绕过。这个坑很隐蔽,改数据后模型没变化,很多时候不是训练问题而是缓存问题。
5.5 显存不足:imgsz=1280 后训练直接 OOM
现象:把imgsz调到 1280 后,显存 8GB 的卡直接报 CUDA out of memory。原因:输入分辨率增大后特征图内存占用约是 640 的四倍,加上cache=True又把图像数据驻留在内存里,双倍压力。解决:先把batch降到 4,cache=False,再打开 AMP(默认开启)。如果还 OOM,检查是不是后台有其他进程占显存,用nvidia-smi看一眼即可。实在不行就退回 1024,并用切片推理补足小目标检测能力,这比硬撑高分辨率更划算。
6. 进阶:旋转框检测和切片推理,把漏检率再压一截
6.1 旋转框检测:飞机不是横平竖直的目标
水平框对飞机这种长条目标很不友好,机头朝向任意时,水平框会圈进大量背景。这时候旋转框检测就派上用场,mmrotate 是遥感目标检测方向常用的工具,训练 DOTA 格式数据是它的典型用法。旋转框需要标注框的四个角点坐标,格式为x1 y1 x2 y2 x3 y3 x4 y4 class。如果你手里只有水平 YOLO 标注,想要转换成旋转框标注,需要先计算每个水平框的最小外接旋转矩形,再取四个角点。这个过程有专门的脚本,但要注意:停场飞机和飞行中飞机的朝向分布完全不同,转换后务必可视化检查角点顺序,角点顺序错乱会导致训练 loss 不收敛。
6.2 切片推理:大图上用小窗滑一遍
如果暂时不想引入旋转框,切片推理是立竿见影的补救手段。SAHI 是现在比较常用的切片推理工具,思路是把大图切成有重叠的小块,分别送入检测模型,再用 NMS 合并结果。对大尺寸卫星图来说,切片后的小目标相当于被放大了,检测率能显著提升。常见做法是:
sahi predict \ --model_type ultralytics \ --model_path runs/detect/train/weights/best.pt \ --source test_images/ \ --slice_size 640 \ --overlap_ratio 0.2 \ --postprocess_type NMS \ --postprocess_class_agnostic True参数含义:slice_size=640是切片尺寸,必须和你训练时的imgsz一致或略小,否则目标尺寸分布会变;overlap_ratio=0.2是相邻切片的重叠比例,切到边框的目标在重叠区能被完整检测,太小容易漏掉边缘目标,太大则推理耗时增加;postprocess_class_agnostic=True表示跨类别做 NMS。实操中,切片尺寸越小,小目标检出的概率越高,代价是推理时间成倍增长。
我在实际项目中吃过一次亏:训练时用 1024,切片推理用 320,结果模型大量漏检。原因是目标在训练时被缩放到某个尺度,推理切片尺寸变了,尺度不一致,模型当然认不出来。后来我固定训练和推理的imgsz一致,漏检率立刻降了下来。工具本身不复杂,复杂的是让训练和推理的尺度分布对齐。这个习惯我一直保留到现在——改任何推理参数前,先回头看一眼训练配置,希望帮到你。
本文还有配套的精品资源,点击获取