简介一批舰船卫星可见光成像目标检测数据集面向计算机视觉目标检测方向的研究者与算法学习者可用于舰船、航母载具等遥感目标的识别模型训练与验证。数据集中包含1000张RGB彩色卫星图尺寸统一为1024x1024覆盖两个类别hang-mu飞机载具/航母与jun-jian军舰已完成VOC格式XML标注方便直接接入主流检测框架。压缩包共2000个文件由1000个jpg图像、1000个xml标签文件及1个说明txt构成整体大小约397.63MB文件命名规律清晰便于按序检索与划分训练集。目前该数据集已有2320人学习使用属于分批发布中的第一批适合需要舰船遥感样本开展目标检测算法实验、对比不同检测器性能或扩充自有数据集的读者。1. 舰船卫星图目标检测数据集是第一个瓶颈卫星图里的舰船检测和日常照片里的目标检测完全是两回事。一张 1024×1024 的遥感图像里一艘货轮可能只占 40×20 像素甲板纹理、船头方向、尾迹形态全挤在这巴掌大的区域里港口场景还有码头、集装箱、岸桥这类外观高度接近的干扰物。很多人拿着 COCO 预训练权重直接跑mAP50 能到 0.6 以上但放到真实港口视频里一测误检率立刻失控。问题不在模型而在数据目标检测数据集的标注密度、类别定义、图像来源和分辨率直接决定了模型能不能把「船」和「长得像船的东西」分开。这个标题里的人工智能目标检测数据集舰船卫星图1对应的是遥感领域最常见的一类私有数据包从历史卫星图或公开遥感影像里裁剪出舰船样本按目标检测格式标注好供 YOLO、Faster R-CNN、DOTA 系旋转框模型训练使用。适合三类人做海事监管、渔业管理、港口调度相关项目的工程师高校里用遥感数据做人工智能大作业的学生以及想验证 YOLOv8 在自己数据上效果、又不愿意从零标注的算法岗新人。先说结论拿到这类数据集第一件事不是训练而是把标注格式、图像分辨率和类别定义摸清楚。舰船检测的绝大多数翻车现场都发生在数据预处理阶段。2. 舰船卫星图数据集的目录结构、标注格式与质量检查2.1 典型目录布局与图像规格公开渠道能拿到的舰船卫星图数据集目录结构通常长这样ship_satellite_v1/ ├── images/ │ ├── train/ # 训练图像JPG 或 PNG │ ├── val/ # 验证图像 │ └── test/ # 测试图像 ├── labels/ │ ├── train/ # 与图像同名的 txt 文件 │ ├── val/ │ └── test/ ├── classes.txt # 类别列表一行一个 ├── train.txt # 图像路径列表VOC 风格会用到 ├── val.txt └── data.yaml # YOLO 训练配置文件图像规格上舰船卫星图数据集和自然图像数据集有两个明显差异。一是单张图很大常见 1024×1024 到 4096×4096 不等直接缩放后小船会丢失细节后面第 4 章会讲切片策略。二是地面采样距离GSD不统一0.5 米分辨率的图像里一艘渔船有上百像素10 米分辨率的图像里同一条船只有十几个像素这决定了模型能学到的是「船的轮廓」还是「船的纹理」。拿到数据后第一件事是看 classes.txt。很多舰船数据集只有一个类别ship但也有把船细分为cargo、fishing、warship的版本。单一类别训练简单mAP 容易做高多类别则需要考虑类别间外观相似造成的混淆尤其是渔船和货轮在低分辨率下几乎无法区分。2.2 标注格式转换YOLO、VOC、COCO 之间怎么安全切换舰船卫星图数据集最常见的标注格式是 YOLO txt 格式每行一个目标class_id x_center y_center width height所有值都用图像宽度和高度归一化到 0~1 之间。比如0 0.5234 0.3102 0.0411 0.0289代表一个类别 0 的目标中心点在图像横向 52.34%、纵向 31.02% 的位置框宽占整张图的 4.11%高占 2.89%。YOLO 格式转 COCO 的代码很简单但转换时的坐标还原要小心import os import json from PIL import Image def yolo_to_coco(img_dir, label_dir, class_file, output_json): # 读取类别列表写入 COCO categories categories [] with open(class_file, r) as f: for i, line in enumerate(f.readlines()): categories.append({id: i, name: line.strip()}) images [] annotations [] ann_id 1 for img_id, label_file in enumerate(os.listdir(label_dir)): # 通过标签文件名找到对应图像获取宽高用于坐标还原 img_name label_file.replace(.txt, .jpg) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): # 有些数据集用 png 后缀没找到 jpg 再试 png img_path os.path.join(img_dir, label_file.replace(.txt, .png)) with Image.open(img_path) as img: width, height img.size images.append({ id: img_id, file_name: os.path.basename(img_path), width: width, height: height }) # yolo 格式一行: class cx cy w h都是归一化到 [0,1] 的浮点数 with open(os.path.join(label_dir, label_file), r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: continue # 跳过空行或格式异常的行 cid int(parts[0]) cx, cy, w, h map(float, parts[1:]) x (cx - w / 2) * width y (cy - h / 2) * height box_w w * width box_h h * height annotations.append({ id: ann_id, image_id: img_id, category_id: cid, bbox: [x, y, box_w, box_h], area: box_w * box_h, iscrowd: 0 }) ann_id 1 coco {images: images, annotations: annotations, categories: categories} with open(output_json, w) as f: json.dump(coco, f) if __name__ __main__: # 用法: python yolo2coco.py yolo_to_coco(images/train, labels/train, classes.txt, train_coco.json)这段代码的逻辑分三步读类别文件构建 COCO categories遍历标签目录拿到每个目标的归一化坐标最后乘图像宽高还原成 COCO 的 xywh 格式。需要注意两个坑一是 YOLO 格式的 width/height 是归一化的框宽高不是右下角坐标换算时一定要先算x cx - w / 2二是 COCO 的 bbox 要求是整数或浮点都可以但area必须匹配 bbox否则后续评估工具会报警告。反过来 VOC 格式转 YOLO 更常见因为很多遥感标注工具导出的是 PASCAL VOC 的 XML。核心就是把xmin, ymin, xmax, ymax换算成cx, cy, w, h再除以图像宽高x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height这个换算方向容易搞反写脚本时建议先用一张已知标注的图验证转换后把 bbox 画回原图叠上原标注对比肉眼确认一致再批量跑。舰船目标小、数量多一个错位的框混在几百个正确框里很难发现等训练完才发现就晚了。2.3 历史卫星图数据集的标注质量检查标题里强调「舰船卫星图1」很多数据集来自历史卫星图切片。这类数据的标注质量相比人工拍摄的无人机数据集要差一截主要体现在几个方面。一是漏标。一张港口图里码头边上停了十几条船标注员可能只标了视觉明显的几条小型渔船直接略过。训练时漏标的船会被当作背景模型学到「有船的地方也可以是背景」推理时就倾向漏检。二是目标框方向不一致。水平框标注下一条斜靠码头的船框里会包含大量水面模型被迫学习「船 水」的组合特征而不是船本身。旋转框数据集不存在这个问题但标注成本高。三是类别标签噪声。外卖平台上搜到的舰船数据集有的会把「船坞里的维修船」标成码头设施或者把两个紧邻的船标成一个框。训练前抽 200 张图用 OpenCV 或 labeling 工具逐张过一遍重点看标注框是不是贴合船身轮廓、有没有同一个目标被标两次。检查代码可以用一个简单的脚本统计异常标注——宽高比极端的框、面积小于 16 像素的目标、越界的框坐标import os label_dir labels/train anomalies 0 for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname), r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: print(f{fname}: 格式错误 - {line.strip()}) anomalies 1 continue _, cx, cy, w, h float(parts[0]), *map(float, parts[1:]) # 舰船宽高比通常不超过 8:1超过多半是标注错误 if w / h 8 or h / w 8: print(f{fname}: 宽高比异常 ({w:.3f}/{h:.3f})) anomalies 1 # 归一化坐标超出 [0,1] 说明标注越界 if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(f{fname}: 坐标越界 ({cx}, {cy}, {w}, {h})) anomalies 1 print(f共发现 {anomalies} 处异常标注)这个脚本本身不修复任何东西它的价值是告诉你数据的可信度。异常标注超过总目标数的 1%就别直接训练优先清洗。清洗方式很简单把异常框所在图像单独抽出来逐张手工修正通常一个几千张的数据集只需处理几十张异常图耗时半小时以内但训练稳定性能提升一个档次。3. 用 YOLOv8 在本地跑通舰船目标检测的最小流程3.1 数据划分与 YAML 配置拿到格式干净的数据集后先做数据划分。常见做法是训练集 70%、验证集 20%、测试集 10%但舰船数据集的特殊性在于同一场景的多张切片之间有大量重复目标——一张港口大图切成 16 个小块后相邻块边缘的船可能被切掉一半。划分时一定要按来源图分组别把同一张原始图的切片同时分进训练集和验证集否则验证集 mAP 虚高部署后立刻露馅。划分完成后写data.yamlpath: /data/ship_satellite_v1 train: images/train val: images/val test: images/test nc: 1 names: 0: ship这段配置的path是数据集根目录的绝对路径train和val是相对该路径的图像目录nc是类别数names是类别名映射。YOLOv8 会自动去同名 txt 目录找标签图像在images/train标签就在labels/train文件名一致、扩展名是.txt。如果标签目录结构不一样会报「label not found」或者直接跳过所有图像训练时 loss 不下降——这是最容易踩的第一个坑。3.2 最小训练命令与关键参数数据划分好之后命令行直接跑yolo detect train \ data/data/ship_satellite_v1/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz1280 \ batch16 \ workers4 \ device0 \ projectship_yolo \ nameexp1 \ pretrainedTrue这段命令的关键参数逐个说。modelyolov8s.pt表示基于 COCO 预训练的 Small 版本做迁移学习相比从零训练收敛更快卫星图这类与自然图像分布差异大的数据迁移学习通常能把收敛时间缩短一半以上。imgsz1280是输入分辨率舰船是小目标用默认 640 会丢掉大量纹理信息1280 是性价比比较高的起点显存不够就降到 960。batch16在 24GB 显存的显卡上配合 1280 输入刚好跑得动显存不够优先降 batch 而不是降分辨率。pretrainedTrue让 YOLOv8 加载 COCO 权重这对舰船检测是有利的——COCO 里有船这个类别模型已经学过船的底层特征轮廓、甲板、桅杆迁移过来只需要微调高层语义。训练过程中盯两个指标验证集的mAP50和mAP50-95。舰船检测场景里目标小、跟背景对比度低mAP50反映的是「大致找到船」的能力mAP50-95反映的是「框得多准」的能力。如果mAP50从 0.7 开始增长缓慢而mAP50-95一直贴着 0.3 左右不升说明框的定位精度不够优先考虑提升输入分辨率或者换旋转框方案而不是盲目加训练轮数。3.3 训练阶段的三个常见报错与规避第一个报错是「CUDA out of memory」。舰船数据集图像尺寸大YOLOv8 默认会做 Letterbox 缩放把 1024×1024 缩到 1280×1280 反而增大了显存占用。处理顺序先降 batch 到 8再降 imgsz 到 960最后关掉cacheTrue数据集缓存到内存会额外占显存之外的资源。如果 24GB 显存跑 1280 还 OOM检查是不是开了plotsTrue和频繁的验证评估把val频率从默认改成val20可以省去中间验证的显存峰值。第二个报错是训练 loss 正常但 mAP 始终为零。这种情况九成是数据划分出了交叉污染或者标签类别编号和data.yaml不一致。用训练集里随机抽 3 张图把标注框画出来人工检查一次python -c from ultralytics import YOLO model YOLO(yolov8s.pt) model.train(datadata.yaml, epochs1, imgsz1280, batch4, plotsTrue) 跑一个 epoch然后看runs/detect/exp/目录下的train_batch0.jpg图像上会画出标注框。如果框的位置明显偏离船体说明标签格式或坐标换算有问题如果完全没有框说明标签没被读进去。第三个问题是训练速度慢到无法接受。舰船数据集普遍图像数量少几千张但单张尺寸大YOLOv8 的rectTrue参数可以按宽高比分组批处理减少 Letterbox 带来的填充浪费训练速度提升 20% 到 30%。另外确认workers大于 0数据加载不吃满 CPU 时 GPU 只能空转。4. 卫星图舰船检测的模型选择与调参策略4.1 水平框还是旋转框MMRotate 在什么时候值得上舰船在卫星图里的朝向是任意的港口里并排停靠的船还会互相遮挡。水平框标注天然包含大量背景水面模型学到的特征被稀释尤其在船间距小、停靠密集的港口场景两个相邻的水平框 IoU 可能超过 0.5NMS 会把其中一条船直接压掉。这时就需要旋转框目标检测。旋转框方案里MMRotate 是目前社区最完整的工具链官方发布的模型大多基于 DOTA 数据集训练而 DOTA 数据集的舰船类别正好可以迁移到卫星图场景。用 MMRotate 训练自己的舰船旋转框数据集的典型配置# rotated_retinanet.py 关键配置 angle_version le90 # 角度范围 -90 到 90 model dict( typeRotatedRetinaNet, backbonedict(typeResNet, depth50, pretrainedtorchvision://resnet50), bbox_headdict( typeRotatedRetinaHead, num_classes1, angle_coderdict( typeDeltaXYWHAOBBoxCoder, target_means(0., 0., 0., 0., 0.), target_stds(1.0, 1.0, 1.0, 1.0, 1.0) ) ) ) train_pipeline [ dict(typeLoadImageFromFile), dict(typeLoadAnnotations, with_bboxTrue, box_typeqbox), dict(typeConvertBoxType, box_type_mappingdict(gt_bboxesrbox)), dict(typeRandomRotate, angle_range180, rotate_prob0.5), dict(typePackDetInputs) ]这里的angle_versionle90是旋转框的角度表示方式MMRotate 支持le90和oc两种DOTA 数据集官方用的是le90舰船这类没有方向语义的目标用le90就够了。RandomRotate增强旋转框模型极其重要因为旋转框标注本身依赖角度训练时加随机旋转可以让模型对任意朝向的船都鲁棒。旋转框不是免费的。标注成本高需要标角度、推理速度比水平框慢 30% 以上、NMS 实现更复杂。我的判断标准是如果场景是开阔海域、船之间间距大于船身长度水平框足够如果是密集港口、船只并排停靠直接上旋转框别在水平框上浪费时间调 NMS 参数。4.2 图像切片策略大图不缩放切片进模型舰船卫星图的典型痛点是整图分辨率高但放大到模型输入尺寸后船太小。常见做法是 SAHISlicing Aided Hyper Inference——推理和训练时都先把大图切成有重叠的切片分别检测再用 NMS 合并结果。切片参数有三个关键值切片大小、重叠率、模型输入尺寸。我常用的组合是 640×640 切片、20% 重叠率、模型输入 640。切片太大会导致边缘目标被截断太小则目标可能被切成两半、上下文丢失。重叠率越高边缘目标被完整捕获的概率越大但推理时间线性增长。用 SAHI 做舰船检测推理的代码from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction # 加载 YOLOv8 训练好的模型 detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathship_yolo/exp1/weights/best.pt, confidence_threshold0.3, image_size640, devicecuda:0 ) # 在大图上执行切片推理 result get_sliced_prediction( imagetest_imgs/harbor_001.png, detection_modeldetection_model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2, postprocess_typeNMS, postprocess_match_metricIOS, postprocess_confidence_threshold0.3, postprocess_match_threshold0.5, ) result.export_visuals(export_diroutput/)postprocess_match_metricIOS是切片推理合并时的关键参数。切片重叠区域里的同一个目标会被检测多次合并时需要判断两个框是否指向同一个目标。IOSIntersection over Smaller box比IoU更适合处理两个框一大一小的情况——当切片边缘只截到目标一半时小框是完全包含在大框里的IoU 会很小但 IOS 接近 1用 IOS 才能正确合并。切片推理的精度提升是实打实的。在 0.5 米分辨率卫星图上直接用 1280 输入检测的 mAP50 大约 0.72切成 640 切片检测后能到 0.81 以上——前提是训练时也用了同样的切片策略否则推理端切片、训练端整图尺度不一致模型反而会困惑。4.3 数据增强与类别不平衡舰船数据集的特殊处理在 YOLOv8 的增强参数里舰船检测和通用目标检测有几个明显不同的设置。第一个是degrees180舰船朝向任意水平翻转会破坏船只的左右对称性吗会但船在卫星图里本来就没有固定的「左」和「右」概念旋转增强 180 度让模型学到的是「任意朝向的船」对开阔海域场景帮助很大。第二个是fliplr0.5、flipud0.5可以保留默认但mosaic0.5调低——舰船数据集的图像切片之间本来就存在大量重复目标Mosaic 增强会把不同来源图的船拼在一起模型可能学到「船旁边必须有另一艘船」的错误关联。第三个是close_mosaic10最后 10 个 epoch 关闭 Mosaic让小目标不会被过度裁剪。类别不平衡在纯舰船数据集里不太常见因为通常只有一个类别。但如果数据集包含多种船型货轮样本量可能是渔船的几十倍这时要用class_weight或者简单的过采样。更常见的问题是难例不平衡一艘靠岸的货轮被码头遮挡了一半另一艘停在开阔水面的货轮完整可见前者才是真正影响部署效果的样本。针对这个问题训练后单独收集误检样本加入训练集做一轮增量训练yolo detect train ... resumeTrue不必直接加载best.pt继续训效果比调损失函数更直观。4.4 从单模态到多模态舰船检测的方向舰船检测的进阶方向是融合红外和可见光双模态数据。DMSD 这类船舶红外可见光双模态数据集的思路是同一场景下同时提供热红外和可见光两路图像可见光提供纹理细节红外提供温度特征两者互补。融合方式通常有早融合输入层拼接、晚融合特征层相加或注意力加权两种早期试验阶段用 late fusion 更容易调试——两个模态各有独立的检测分支最后用注意力模块加权合并而不是一开始就做像素级对齐。多模态需要的数据标注量翻倍对 GPU 显存的要求也更高如果是大作业或初探先把单模态做到极致再考虑扩展。5. 评估、推理加速与把检测结果落到经纬度上训练出的模型不能只看 mAP舰船检测的评估要单独验证三个维度小目标召回率、密集场景的漏检率、以及跨数据集的泛化能力。YOLOv8 的验证命令会输出完整指标yolo detect val \ modelship_yolo/exp1/weights/best.pt \ datadata.yaml \ imgsz1280 \ conf0.001 \ iou0.6conf0.001是评估时的关键参数推理时用 0.25 的置信度阈值会漏掉大量低置信度的真实小船评估时降到 0.001 才能看到模型的真实上限。看mAP50-95和验证集生成的一个confusion_matrix.png重点看船这一类别的漏检率false negative而不是只看 mAP 数值——舰船检测部署到港口监控里漏检比误检严重得多。推理加速上用 TensorRT 导出是部署前必做的一步yolo export modelship_yolo/exp1/weights/best.pt formatengine device0 imgsz1280 halfTrue导出的 engine 文件在相同 GPU 上推理速度比 PyTorch 版本快 2 到 3 倍显存占用降低约一半。halfTrue开启 FP16 精度卫星图舰船检测这类背景相对干净的任务不会有明显精度损失但如果发现小目标召回率下降改回 FP32。业务落地时舰船检测的最终输出往往不是像素框而是经纬度坐标。假设你的卫星图带地理信息文件把检测框中心从像素坐标转成经纬度方法很简单读取 GeoTIFF 的地理变换参数用仿射变换换算。from osgeo import gdal # 打开带地理信息的卫星图 ds gdal.Open(harbor_001.tif) gt ds.GetGeoTransform() # gt 的六个参数: 左上角x坐标, x方向像素分辨率, x方向旋转, 左上角y坐标, y方向旋转, y方向像素分辨率负值 def pixel_to_geo(pixel_x, pixel_y, gt): # 仿射变换地理坐标 左上角坐标 像素偏移 * 分辨率 geo_x gt[0] pixel_x * gt[1] pixel_y * gt[2] geo_y gt[3] pixel_x * gt[4] pixel_y * gt[5] return geo_x, geo_y # 假设检测框中心像素坐标为 (512, 384) lon, lat pixel_to_geo(512, 384, gt) print(f目标经纬度: {lat:.6f}, {lon:.6f})gt[5]在北半球是负值代表 y 轴向下、纬度递减很多人在这里踩坑——直接拿gt[3] pixel_y * gt[5]算出来纬度方向是对的但如果不取负值会得到反向结果。验证方法很简单把算出的经纬度放进任何地图工具里看是否落在港口陆域或近海水域如果落在陆地中央检查是不是 y 方向符号搞反了。最后提一个部署端常用但容易被忽略的技巧给检测结果加航向或尾迹信息。卫星图序列帧里同一艘船在相邻两帧的位置变化可以估算航速和航向这比单帧检测框更有业务价值。做法朴素——用 ByteTrack 或 DeepSORT 做跨帧关联船只目标外观差异小、容易跟丢关联时优先用位置预测而不是外观特征简单可靠。舰船卫星图检测的完整链路从数据清洗开始到经纬度输出结束每一步都有对应的验证方式。把调参精力集中在数据质量和部署场景的贴合度上比反复换模型结构更出效果。本文还有配套的精品资源点击获取