简介:这份资源面向遥感图像处理、计算机视觉与深度学习方向的研究者及工程人员,提供一套可直接用于目标检测训练的高分辨率船舶影像数据集,帮助解决海上目标识别任务中样本获取难、标注成本高的问题。压缩包共约2000个文件,包含7046个XML标注文件与7046个JPEG图像,二者一一对应,整体约945.16MB,采用VOC2007格式,可直接接入YOLO、Faster R-CNN等主流检测框架。影像来自高分2号与谷歌卫星,分辨率在0.5至0.8米之间,覆盖油轮、集装箱船、快艇、小型游艇、邮轮和杂货船共六类船舶,样本量大且类别丰富,适合科研实验与论文研究。目前已有678人学习下载,可作为船舶检测模型训练、算法对比与精度验证的可靠数据基础。
1. 高分遥感船舶检测数据集:7000 张 VOC 标注影像能直接跑通哪些模型
拿到一份遥感船舶检测数据集,第一反应通常不是看它有多少张图,而是先确认三件事:标注格式能不能直接喂给训练脚本、影像分辨率够不够区分小船、类别定义是否覆盖你的业务场景。这份基于高分辨率遥感影像的船舶目标检测数据集,正好在这三点上都给出了明确答案。它采用 VOC2007 格式,JPG 与 XML 一一对应,影像来源为高分2号和谷歌卫星,分辨率落在 0.5 到 0.8 米之间,训练图片七千多张,船舶类别细分为油轮、集装箱船、快艇、小型游艇、邮轮、杂货船六类。如果你正在做遥感影像船舶目标检测,或者需要一份能直接用于 YOLO、Faster RCNN 训练的数据集,这份资源省去了从原始影像切片到标注转换的大段前期工作。它适合科研论文实验、算法对比、以及需要快速验证检测模型在遥感场景下表现的从业者。
2. VOC2007 格式拆解:XML 里到底存了什么,怎么读进训练管线
2.1 单张 XML 的字段结构与船舶标注特点
VOC2007 的标注文件本质是一个结构化 XML,每张图对应一个同名 XML。以 ship_002822.xml 为例,根节点是 annotation,下面挂 folder、filename、size、segmented,以及若干个 object 节点。每个 object 包含 name、pose、truncated、difficult 和 bndbox。bndbox 里是 xmin、ymin、xmax、ymax 四个坐标,原点在左上角,单位是像素。
船舶检测和通用目标检测在标注上有两个明显差异。第一,遥感影像里的船体长宽比差异极大,快艇可能接近 1:1,而集装箱船能到 8:1 甚至更高,所以 bndbox 的宽高分布很分散,做 anchor 设计时不能照搬 COCO 的默认比例。第二,靠港船舶密集排列时,船与船之间的边界框容易重叠,XML 里 truncated 和 difficult 字段的取值会直接影响训练时正负样本的划分。我一般会先统计一遍 difficult 为 1 的样本占比,如果超过 10%,就要在 loss 里对这部分做降权,否则模型会偏向学那些容易的、孤立的船。
import xml.etree.ElementTree as ET import os def parse_voc_xml(xml_path): tree = ET.parse(xml_path) root = tree.getroot() # 读取图像尺寸,后续做归一化和 resize 要用 size = root.find('size') width = int(size.find('width').text) height = int(size.find('height').text) objects = [] for obj in root.findall('object'): name = obj.find('name').text difficult = int(obj.find('difficult').text) bndbox = obj.find('bndbox') xmin = int(bndbox.find('xmin').text) ymin = int(bndbox.find('ymin').text) xmax = int(bndbox.find('xmax').text) ymax = int(bndbox.find('ymax').text) objects.append({ 'class': name, 'difficult': difficult, 'bbox': [xmin, ymin, xmax, ymax] }) return width, height, objects # 批量扫描,统计六类船舶的分布 xml_dir = './Annotations' class_count = {} for f in os.listdir(xml_dir): if f.endswith('.xml'): _, _, objs = parse_voc_xml(os.path.join(xml_dir, f)) for o in objs: class_count[o['class']] = class_count.get(o['class'], 0) + 1 print(class_count)这段代码做了两件事:解析单张 XML 拿到图像宽高和所有目标框,然后批量遍历统计类别分布。参数上要注意,difficult 字段在 VOC 标准里表示该目标是否难以识别,训练时通常不直接丢弃,而是在评估阶段单独看。如果你用 YOLO 系列,转换脚本里一般会把 difficult=1 的框也保留,但可以在数据增强时对这类样本减少裁剪概率。
2.2 从 VOC 到 YOLO 格式的转换脚本与坐标归一化
YOLO 格式要求每张图对应一个 txt,每行是 class_id x_center y_center width height,全部归一化到 0 到 1。转换时最容易翻车的地方是类别名到 id 的映射顺序,一旦训练时和推理时的映射不一致,模型会把油轮认成集装箱船。我一般会把类别列表写死在脚本里,并且生成一份 classes.txt 随数据集一起放。
import xml.etree.ElementTree as ET import os # 六类船舶,顺序固定,不要随意改 CLASSES = ['Oil tanker', 'Container ship', 'Speedboat', 'Small yacht', 'Cruise ship', 'General Cargo Ship'] CLASS_TO_ID = {c: i for i, c in enumerate(CLASSES)} def voc_to_yolo(xml_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in CLASS_TO_ID: continue bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.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"{CLASS_TO_ID[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") return lines坐标归一化本身不复杂,但有两个边界情况要处理。一是 xmax 或 ymax 可能等于图像宽高,归一化后正好是 1.0,某些训练框架会认为越界,建议在转换时做一次 clip 到 0.999。二是如果 XML 里的 size 字段和实际 JPG 尺寸不一致,归一化就会整体偏移,所以转换前最好用 PIL 读一遍 JPG 的真实尺寸做校验,而不是直接信 XML 里的 width 和 height。
3. 训练管线搭建:用这份数据集跑 YOLOv8 和 Faster RCNN 的关键参数
3.1 YOLOv8 数据配置与 anchor 调整思路
YOLOv8 对 VOC 格式的兼容性一般,通常先转成 YOLO txt 再组织成 images 和 labels 两个目录。数据配置文件 data.yaml 里要写清楚 train、val 路径和 nc、names。这份数据集有七千多张训练图,按 8:1:1 切分后验证集大概七百张,足够做早停判断。
# data.yaml train: ./images/train val: ./images/val nc: 6 names: ['Oil tanker', 'Container ship', 'Speedboat', 'Small yacht', 'Cruise ship', 'General Cargo Ship']anchor 方面,YOLOv8 默认是 anchor-free 的,不需要手动设 anchor,但输入分辨率要留意。0.5 到 0.8 米分辨率的遥感影像里,小型游艇可能只有二三十个像素宽,如果训练时把 imgsz 设成 640,下采样到 32 倍后特征图上只剩一个点,召回率会很难看。我一般会把 imgsz 提到 1024 甚至 1280,同时把 batch 降到 8 或 4,显存不够就开梯度累积。学习率用默认的 0.01 配合余弦退火,前 3 个 epoch 做 warmup,让模型先适应船舶的长宽比分布。
3.2 Faster RCNN 的 backbone 选择与 RPN 参数
Faster RCNN 在遥感船舶检测上仍然有参考价值,尤其是做论文对比实验时。用 torchvision 的预训练权重,backbone 选 ResNet50-FPN,FPN 对多尺度船舶很关键,因为同一张图里可能同时有几十像素的快艇和几百像素的邮轮。RPN 的 anchor 尺寸要改,默认是 128、256、512,但遥感船舶在 1024 输入下,小目标可能只有 32 像素,所以我会加一组 32 和 64 的 anchor,比例设成 0.5、1.0、2.0、4.0,覆盖长条船体。
import torchvision from torchvision.models.detection.faster_rcnn import FastRCNNPredictor from torchvision.models.detection.anchor_utils import AnchorGenerator # 自定义 anchor,适配遥感船舶的尺度分布 anchor_sizes = ((32,), (64,), (128,), (256,), (512,)) aspect_ratios = ((0.5, 1.0, 2.0, 4.0),) * len(anchor_sizes) anchor_generator = AnchorGenerator(sizes=anchor_sizes, aspect_ratios=aspect_ratios) model = torchvision.models.detection.fasterrcnn_resnet50_fpn( weights='DEFAULT', rpn_anchor_generator=anchor_generator) # 替换分类头,6 类船舶加背景 num_classes = 7 in_features = model.roi_heads.box_predictor.cls_score.in_features model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)参数上,RPN 的 nms_thresh 默认 0.7,但船舶密集停靠时建议降到 0.5,否则相邻船的框会被互相抑制掉。训练时正负样本比例用默认的 0.5 就行,如果发现漏检多,可以调到 0.3 让更多正样本参与。学习率用 0.005 起步,每 5 个 epoch 降一半,七千张图大概跑 20 到 30 个 epoch 就能收敛。
4. 避坑与排查:标注、分辨率、类别不均衡的五个血泪经验
4.1 现象:训练 loss 正常下降但 mAP 极低
原因通常是类别 id 映射错位。VOC 的 name 字段是字符串,转 YOLO 时如果按 os.listdir 的顺序生成类别列表,不同机器上顺序可能不一样,导致训练和评估的类别对不上。解决方法是把类别列表硬编码在转换脚本和 data.yaml 里,转换完随机抽 20 张图用可视化脚本画框,确认框和类别文字对得上再开训。
4.2 现象:小目标召回率明显低于大目标
原因在于输入分辨率不足或 FPN 层数不够。0.5 米分辨率下,小型游艇可能只有 20 到 30 像素,640 输入下采样 32 倍后特征图只剩一个像素。解决办法是把 imgsz 提到 1024 以上,Faster RCNN 加 P2 层,YOLOv8 用 P2 检测头或者把 imgsz 设成 1280。代价是显存翻倍,batch 要相应减小。
4.3 现象:靠港密集船舶的框大量重叠,NMS 后只剩一个
原因是 NMS 阈值过高。默认 0.7 在通用检测里没问题,但船舶并排停靠时 IoU 很容易超过 0.5。解决办法是把 NMS 阈值降到 0.4 到 0.5,或者改用 soft-NMS。如果框架支持,还可以在训练时对密集区域做 copy-paste 增强,让模型学会区分相邻船体。
4.4 现象:XML 里的 size 和 JPG 实际尺寸不一致
原因可能是数据制作时用缩略图标注,或者后期做过 resize 但没同步改 XML。解决办法是在转换脚本里用 PIL 读 JPG 的真实宽高,和 XML 里的 size 做对比,不一致的样本直接剔除或重新映射坐标。我一般会写一个校验脚本,跑一遍全量数据,把不一致的文件名输出到 log 里。
4.5 现象:六类船舶里某些类别样本极少,模型几乎不预测
原因是类别不均衡。油轮和集装箱船通常占多数,小型游艇和快艇可能只有几百个实例。解决办法有三个方向:一是用重采样,对稀有类别过采样;二是在 loss 里给稀有类别更高权重;三是用 mosaic 和 mixup 增强时,优先从稀有类别里采样。如果稀有类别实例少于 500,建议先做二阶段训练,先训一个船舶/背景的二分类模型,再在船舶区域上做六分类。
5. 进阶技巧:用切片推理和类别权重把 mAP 再拉高几个点
遥感影像船舶检测有一个通用技巧叫切片推理,英文是 SAHI。原理是把大图切成有重叠的小图分别推理,再把结果拼回去。这份数据集分辨率 0.5 到 0.8 米,单张图可能覆盖几公里范围,直接 resize 到 1024 会丢失大量小目标细节。用 SAHI 切 512 的块,重叠 128 像素,小目标召回率通常能提升 5 到 10 个点。
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction # 加载训练好的 YOLOv8 模型 detection_model = AutoDetectionModel.from_pretrained( model_type='yolov8', model_path='best.pt', confidence_threshold=0.25, device='cuda:0' ) # 切片推理,切片 512,重叠 128 result = get_sliced_prediction( 'test_ship.jpg', detection_model, slice_height=512, slice_width=512, overlap_height_ratio=0.25, overlap_width_ratio=0.25 ) result.export_visuals(export_dir='./vis')参数上,slice_height 和 slice_width 一般设成训练输入的一半到三分之一,overlap 比例 0.2 到 0.3 之间比较稳。太小会漏掉跨切片的船,太大推理耗时翻倍。confidence_threshold 可以比训练时低一点,因为切片后每个目标的像素更多,模型更自信,但拼接时要做一次全局 NMS,避免同一艘船被多个切片重复检出。
另一个技巧是类别权重。六类船舶里,小型游艇和快艇的 AP 通常最低,可以在验证集上单独统计每类的 AP,然后对 AP 低于 0.5 的类别在 loss 里乘 1.5 到 2.0 的权重。注意权重不要设太大,否则模型会为了稀有类别牺牲多数类别的精度,整体 mAP 反而下降。我一般会跑三组权重做对比,选验证集上六类 AP 方差最小的那组。
从那以后我每次拿到新的遥感数据集,都会先跑一遍全量 XML 校验和类别分布统计,再决定输入分辨率和增强策略,而不是直接套默认配置开训。希望帮到你。
本文还有配套的精品资源,点击获取