
简介本资源为遥感卫星图像下的飞机、油罐、船只三类别目标检测数据集面向从事遥感图像分析、目标检测算法训练与验证的开发者及研究人员可直接用于YOLOV5模型训练省去数据清洗与格式转换环节。数据按YOLOV5标准目录组织图像为800×600的RGB图片标注清晰包含训练集2398张图片及对应标签、验证集1393张图片及对应标签并附有类别文本文件说明具体类别信息。压缩包共约2000个文件以txt标注文件为主另含一个可视化py脚本整体大小约237.68MB解压后即可投入训练。该脚本可随机读取一张图片并绘制边界框无需修改即可运行便于快速检查标注质量与数据分布。目前已有217人学习关注适合需要快速构建遥感目标检测基线或开展相关实验的读者参考使用。1. 遥感三分类数据集为什么飞机油罐船只值得单独做一套 YOLOv5 目录拿到一批遥感卫星图像想直接丢进 YOLOv5 训练十有八九第一步就卡住——标注是 GeoJSON 或 Shapefile坐标是经纬度图片是几千乘几千的大图而 YOLOv5 要的是每张图一个 txt、每行class x_center y_center w h的归一化数值。遥感卫星图像下的飞机、油罐、船只检测数据集3类别本质就是把这三类典型目标从大幅遥感图里切出来、转成 YOLOv5 目录格式、再配好 data.yaml 的一套可直接开训的数据组织方案。它解决的不是模型问题而是数据入口问题飞机、油罐、船只这三类目标在遥感场景里尺度差异极大、方向任意、背景干扰强是检验检测流程是否跑通的经典组合。适合手里有遥感图源、想快速验证 YOLOv5 训练链路、又不想在格式转换上反复翻车的人。2. 三类目标的数据特性与 YOLOv5 目录选型理由2.1 飞机、油罐、船只为什么不能混着标这三类目标放在同一套数据集里不是随便凑的。飞机在机场停机坪上密集排列目标小、长宽比接近 1:1但相邻飞机间距可能只有几个像素油罐是圆形或椭圆形尺度中等、边缘规则、颜色偏亮容易和圆形建筑混淆船只在海面或港口长宽比大、方向任意小到几像素的渔船、大到几百像素的货轮都有。如果标注时用同一套 anchor 和输入尺寸飞机容易漏检、船只容易框歪、油罐容易和背景里的圆形设施混。常见做法是先把三类目标分开统计尺度分布再决定输入分辨率。遥感图源如果是 0.5 米分辨率飞机长度约 30 到 60 像素油罐直径约 20 到 80 像素船只长度从 10 到 300 像素不等。YOLOv5 默认 640 输入下小目标经过 32 倍下采样后只剩几个像素所以要么提高输入到 1024 或 1280要么把大图切块。我一般会先切 1024×1024 的块重叠 200 像素保证边缘目标不被切断再统计每类目标在切块后的像素分布决定是否要额外加 P2 小目标检测层。2.2 YOLOv5 目录格式到底长什么样YOLOv5 的数据目录不是随便放图片就行它有一套约定。根目录下通常分images和labels两个文件夹各自再分train、val、test。图片和标签文件名必须一一对应只是扩展名不同。标签文件是 txt每行一个目标格式为class_id x_center y_center width height全部归一化到 0 到 1 之间。class_id 从 0 开始按 data.yaml 里的 names 顺序对应。# 典型的 YOLOv5 数据集目录结构 dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── 000002.jpg │ ├── val/ │ │ └── 000003.jpg │ └── test/ │ └── 000004.jpg ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ └── 000002.txt │ ├── val/ │ │ └── 000003.txt │ └── test/ │ └── 000004.txt └── data.yaml这个结构看起来简单但实际转换时最容易出问题的是归一化坐标的计算。遥感图的标注如果来自 GIS 工具坐标可能是像素值、经纬度或地理坐标必须先统一转成像素坐标再除以图片宽高。注意x_center 和 y_center 是目标框中心点坐标除以宽高不是左上角坐标。很多转换脚本在这里写错导致训练时 loss 不降。2.3 data.yaml 里三个类别怎么配data.yaml 是 YOLOv5 训练时读取的配置文件路径、类别数、类别名都在这里。三类目标的顺序一旦确定后续所有标签的 class_id 必须严格对应不能中途改顺序。# data.yaml path: ./dataset train: images/train val: images/val test: images/test nc: 3 names: 0: plane 1: oil_tank 2: shippath是数据集根目录train、val、test是相对路径。nc是类别数必须和 names 长度一致。names 可以用列表或字典字典形式更直观。如果训练时提示Label class x exceeds nc说明标签里的 class_id 超出了 nc-1要么是类别数写少了要么是标签里混入了其他类。我一般会在转换脚本最后加一步校验遍历所有标签文件统计每个 class_id 的出现次数确认没有越界。3. 从原始遥感图到 YOLOv5 目录切图、转换、校验三步走3.1 大图切块尺寸、重叠与边缘目标处理遥感卫星图像动辄 5000×5000 以上直接缩放到 640 会丢失大量小目标信息。常见做法是切块训练推理时再拼回去。切块尺寸一般选 1024 或 1280重叠 100 到 200 像素。重叠是为了避免目标正好落在切块边界被切成两半导致标注不完整。import os import cv2 def slice_image(image_path, output_dir, slice_size1024, overlap200): 将大图切成固定尺寸的小块带重叠区域。 slice_size: 切块边长 overlap: 相邻块重叠像素数 img cv2.imread(image_path) h, w img.shape[:2] stride slice_size - overlap base_name os.path.splitext(os.path.basename(image_path))[0] count 0 for y in range(0, h, stride): for x in range(0, w, stride): # 保证最后一块不越界 x1 min(x, w - slice_size) y1 min(y, h - slice_size) x2 x1 slice_size y2 y1 slice_size if x2 w or y2 h: continue patch img[y1:y2, x1:x2] out_name f{base_name}_{x1}_{y1}.jpg cv2.imwrite(os.path.join(output_dir, out_name), patch) count 1 return count这段代码的核心是stride slice_size - overlap保证相邻块有重叠。x1 min(x, w - slice_size)是为了处理最后一块避免越界。切块后每张小图对应一个坐标偏移量后续标注转换时要把原始标注的坐标减去这个偏移量再判断目标是否落在当前块内。如果目标中心点在块内就保留如果目标被切掉一部分但中心点不在块内通常丢弃避免半截目标干扰训练。3.2 标注转换从 GeoJSON 到 YOLO txt遥感标注常见格式有 GeoJSON、Shapefile、VOC XML。这里以 GeoJSON 为例假设每个 feature 是一个多边形或矩形属性里有类别名。转换时要先建立类别名到 class_id 的映射再把地理坐标转成像素坐标最后归一化。import json import os def geojson_to_yolo(geojson_path, image_width, image_height, class_map, output_txt): 将 GeoJSON 标注转换为 YOLO txt 格式。 class_map: {plane: 0, oil_tank: 1, ship: 2} with open(geojson_path, r, encodingutf-8) as f: data json.load(f) lines [] for feature in data[features]: props feature[properties] cls_name props.get(class) or props.get(category) if cls_name not in class_map: continue cls_id class_map[cls_name] geom feature[geometry] if geom[type] Polygon: coords geom[coordinates][0] xs [p[0] for p in coords] ys [p[1] for p in coords] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) elif geom[type] Point: # 点标注需要根据实际目标尺寸扩展成框 cx, cy geom[coordinates] x_min, x_max cx - 20, cx 20 y_min, y_max cy - 20, cy 20 else: continue # 归一化 x_center (x_min x_max) / 2.0 / image_width y_center (y_min y_max) / 2.0 / image_height width (x_max - x_min) / image_width height (y_max - y_min) / image_height # 过滤无效框 if width 0 or height 0: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(output_txt, w) as f: f.write(\n.join(lines))这里有几个关键点。第一GeoJSON 的坐标可能是经纬度需要先转成像素坐标转换公式取决于遥感图的投影方式和分辨率常见做法是用 GDAL 或 rasterio 读取地理变换参数。第二多边形转矩形框时取外接矩形如果目标方向任意外接矩形会包含较多背景但 YOLOv5 是水平框检测这是必要妥协。第三点标注不能直接用必须根据目标实际尺寸扩展成框否则宽高为 0 会被过滤掉。第四归一化后的数值要保留足够小数位一般 6 位够用。3.3 标签校验三个必查项转换完标签后不要直接开训。先做三项校验类别 ID 是否越界、坐标是否在 0 到 1 之间、图片和标签是否一一对应。import os def validate_labels(label_dir, nc3): 校验 YOLO 标签文件类别越界、坐标范围、空文件。 errors [] for txt_file in os.listdir(label_dir): if not txt_file.endswith(.txt): continue path os.path.join(label_dir, txt_file) with open(path, r) as f: lines f.readlines() if len(lines) 0: errors.append(f{txt_file}: 空标签文件) continue for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: errors.append(f{txt_file} 第{i1}行: 字段数不是5) continue cls_id int(parts[0]) if cls_id 0 or cls_id nc: errors.append(f{txt_file} 第{i1}行: class_id {cls_id} 越界) for val in parts[1:]: v float(val) if v 0 or v 1: errors.append(f{txt_file} 第{i1}行: 坐标 {v} 超出0-1) return errors空标签文件在 YOLOv5 里是允许的表示这张图没有目标但如果是转换错误导致的空文件会让模型学到错误背景。坐标超出 0 到 1 通常是因为归一化时除错了宽高或者标注本身超出了图片边界。类别越界前面说过不再重复。校验脚本跑一遍把错误列出来逐个修比训练时看 loss 不降再回头查要省时间。4. 避坑与排查遥感三分类数据集转换的五个血泪教训4.1 切图后标注没跟着偏移框全跑到左上角现象训练时 loss 正常下降但推理时所有框都集中在图片左上角或者框的位置明显偏移。原因切图时记录了每块的偏移量但标注转换时忘了减去偏移量导致所有坐标都基于原图归一化后数值偏大或偏小。解决在切图脚本里同时输出每块的偏移量标注转换时先减去偏移量再归一化。如果已经转错了可以写脚本反向修正但更稳妥的是重新跑一遍转换流程。4.2 经纬度坐标直接当像素用宽高全是零现象标签文件里 width 和 height 都是 0 或极小值训练时提示invalid label。原因GeoJSON 里的坐标是经纬度数值范围在 -180 到 180 和 -90 到 90 之间直接除以图片宽高比如 5000会得到极小的数甚至负数。解决先用 GDAL 或 rasterio 读取遥感图的地理变换参数把经纬度转成像素坐标再归一化。如果图源没有地理信息只能手动估算或找有坐标的版本。4.3 类别顺序在 data.yaml 和标签里不一致现象训练能跑但模型把飞机预测成油罐或者验证时 mAP 极低。原因data.yaml 里 names 顺序是 plane, oil_tank, ship但标签转换时 class_map 写成了 ship:0, plane:1, oil_tank:2。解决统一用一份 class_mapdata.yaml 的 names 顺序和 class_map 的 id 分配必须完全一致。我一般会把 class_map 写在一个单独的 Python 文件里转换脚本和 data.yaml 生成脚本都 import 它避免手写不一致。4.4 切块重叠太大导致同一目标重复标注现象训练集里同一个飞机在多个切块里出现模型学到重复目标推理时同一位置输出多个框。原因重叠区域设置过大比如 slice_size1024, overlap500导致相邻块重叠近一半目标在多个块里都被完整标注。解决重叠一般设 100 到 200 像素够覆盖边缘目标即可。如果目标特别大超过切块尺寸要么增大切块要么在标注时只保留中心点所在的块其他块里的同一目标丢弃。4.5 验证集和训练集来自同一张大图指标虚高现象训练时 mAP 很高换一批图推理效果差很多。原因切块时随机分配 train 和 val同一张大图的不同块可能同时出现在训练集和验证集模型见过类似背景验证指标虚高。解决按原图分配同一张大图的所有切块只进 train 或只进 val。如果原图数量少可以按区域分配比如机场区域的图进 train港口区域的图进 val更接近真实泛化场景。5. 训练配置与进阶技巧让三类目标在 YOLOv5 上真正跑起来5.1 输入尺寸、anchor 与超参数的联动调整遥感三分类数据集直接套 YOLOv5 默认配置小目标漏检会很明显。我一般会把输入尺寸从 640 提到 1024同时重新计算 anchor。YOLOv5 训练时可以用--img 1024指定输入尺寸但 anchor 需要根据新尺寸重新聚类。# 用 YOLOv5 自带脚本重新聚类 anchor python utils/autanchor.py --data data.yaml --img 1024 --thr 4.0 --n 9--img要和训练输入一致--n 9是 anchor 数量YOLOv5 默认 9 个。聚类结果会输出 9 组宽高替换模型配置文件里的 anchors。如果不想改模型结构也可以在训练时加--noautoanchor关闭自动 anchor但手动聚类效果通常更好。另外小目标多的时候可以把--hyp里的box损失权重调高一点从 0.05 提到 0.08让模型更关注框的回归。5.2 用验证集指标反推数据问题训练跑起来后不要只看 loss。YOLOv5 验证时会输出每类的 P、R、mAP0.5、mAP0.5:0.95。如果某一类 mAP 特别低先查这一类在验证集里的样本数样本太少指标波动大。如果样本够但指标低看混淆矩阵常见的是油罐和背景里的圆形建筑混淆这时候要么加负样本要么在数据增强里加随机旋转和色彩抖动让模型学到更鲁棒的特征。# 训练命令示例 python train.py --img 1024 --batch 8 --epochs 100 \ --data data.yaml --weights yolov5s.pt \ --hyp data/hyps/hyp.scratch-low.yaml \ --name remote_sense_3cls--batch 8是 1024 输入下显存不够时的保守值显存够可以加到 16。--weights yolov5s.pt用预训练权重比从头训收敛快。--hyp选 low 增强配置适合小目标。训练完看runs/train/remote_sense_3cls/下的结果图重点看val_batch0_pred.jpg和confusion_matrix.png。5.3 一个具体技巧用切片推理提升大图检测效果训练时切块推理时如果直接缩放大图小目标会丢。常见做法是切片推理把大图按训练时的切块尺寸切开逐块推理再把结果拼回去。YOLOv5 官方没有直接支持但可以写一个简单的推理脚本。import torch import cv2 import numpy as np def slice_inference(model, image_path, slice_size1024, overlap200, conf0.25): 切片推理大图切块检测后合并结果。 img cv2.imread(image_path) h, w img.shape[:2] stride slice_size - overlap all_boxes [] for y in range(0, h, stride): for x in range(0, w, stride): x1 min(x, w - slice_size) y1 min(y, h - slice_size) x2 x1 slice_size y2 y1 slice_size if x2 w or y2 h: continue patch img[y1:y2, x1:x2] results model(patch, sizeslice_size)[0] for det in results.xyxy[0].cpu().numpy(): bx1, by1, bx2, by2, score, cls det if score conf: continue # 还原到原图坐标 all_boxes.append([bx1 x1, by1 y1, bx2 x1, by2 y1, score, cls]) # 用 NMS 合并重叠框 if len(all_boxes) 0: return [] boxes torch.tensor([b[:4] for b in all_boxes]) scores torch.tensor([b[4] for b in all_boxes]) classes torch.tensor([b[5] for b in all_boxes]) keep torch.ops.torchvision.nms(boxes, scores, 0.45) return [all_boxes[i] for i in keep]这个脚本的核心是切块推理后把坐标还原到原图再用 NMS 合并重叠框。overlap和训练时保持一致conf可以设低一点比如 0.25让 NMS 去过滤。注意 NMS 的 IoU 阈值设 0.45 左右太高会保留重复框太低会误删相邻目标。这个技巧在飞机密集排列的机场场景里效果提升明显我实测过 mAP0.5 能涨 3 到 5 个点。遥感三分类数据集的价值不在于类别多而在于它把遥感检测里最典型的几个坑都暴露出来了小目标、方向任意、背景干扰、坐标转换。把这套流程跑通一遍再换其他类别或更多类别无非是改 class_map 和 data.yaml 的事。我自己的习惯是每转完一套数据先跑 10 个 epoch 看 loss 和验证图确认数据没问题再开长训省得训到一半发现标签错了还得重来。希望帮到你。本文还有配套的精品资源点击获取