舰船卫星图目标检测数据集:格式转换与YOLOv8训练避坑指南
发布时间:2026/10/12 17:15:12 锦皓数字建站

简介这是一批面向目标检测算法研究的舰船卫星可见光成像数据集适合从事遥感图像识别、军事目标检测及深度学习模型训练的研究者与开发者使用。数据集共1000张1024×1024的RGB彩色图像涵盖hang-mu飞机载具与船只jun-jian两个类别所有图片均已按VOC格式完成标注可直接用于YOLO、Faster R-CNN等主流检测框架的训练与验证。资源包共2000个文件包含1000个jpg图像、1000个xml标注文件及1个txt说明文件压缩包大小约397.63MB目录结构清晰便于按类别检索与批量读取。目前已有2327人学习下载可作为舰船遥感目标检测研究的可靠数据基础。该数据集分批次逐步发布本批为第一批后续还将持续扩充适合需要长期积累样本、开展对比实验或构建基线模型的研究者持续关注与使用。1. 舰船卫星图数据集从一张 0.5 米分辨率切片说起去年帮一个做港口监测的朋友看模型他手里有一批卫星图想把里面的舰船框出来做进出港统计。模型训了两周mAP 卡在 0.4 上不去可视化一看近岸的船和码头、集装箱混在一起小目标漏得一塌糊涂。问题不在网络结构在数据——他用的那批标注框是斜的转成水平框之后船体被码头吃掉一半。这就是「人工智能目标检测数据集舰船卫星图1」这类数据集真正要解决的事不是给你一堆图就完事而是要让标注格式、目标尺度、背景复杂度都对得上你的检测任务。舰船卫星图数据集本质是面向遥感场景的目标检测数据集合图像来源多为光学卫星或航拍标注对象是各类水面舰船常见标注形式包括水平框HBB和旋转框OBB。它服务的人群很明确做遥感目标检测的研究生、做港口/海域监测的工程团队、以及拿它当人工智能大作业或毕设选题的学生。和 COCO、VOC 那种通用数据集不同舰船卫星图的难点集中在三点——目标小、方向任意、背景干扰强。你拿通用预训练权重直接 finetune不改任何东西大概率会翻车。下面按「数据集长什么样 → 怎么转格式 → 怎么训 → 坑在哪」这条线走一遍。2. 舰船卫星图数据集的结构与标注格式拆解2.1 图像来源、分辨率与目标尺度分布舰船卫星图数据集通常由若干景卫星影像切片组成单张尺寸从 512×512 到 1024×1024 不等地面采样距离GSD常见在 0.5m 到 2m 之间。GSD 直接决定船体像素数一艘 50 米长的护卫舰在 0.5m GSD 下约 100 像素长在 2m GSD 下只剩 25 像素。这就是为什么同一套模型在不同数据集上表现差异巨大——目标尺度变了anchor 和特征层都得跟着调。标注对象以水面舰船为主部分数据集会细分军舰、民船、渔船等类别也有只标一个「ship」类的。类别数少是好事但别高兴太早单类检测的难点全在召回上。目标尺度分布一般呈长尾大量小目标32 像素加少量中大目标。我一般会先跑一遍统计脚本把宽高分布画出来再决定用哪几层特征做检测。提示拿到数据集先别急着训用脚本统计一下目标宽高和数量分布比盲目调参省几天时间。2.2 水平框与旋转框标注格式的选型理由舰船在卫星图里是任意朝向的水平框HBB会把大量背景框进来尤其是近岸和并排停靠的场景。旋转框OBB用中心点加宽高加角度表示贴合船体但代价是标注成本高、格式转换麻烦、部分检测框架支持有限。选型逻辑很简单如果你的下游任务是计数、存在性判断HBB 够用如果要做精细定位、方向估计、或者船体长宽比分析必须上 OBB。常见做法是先用 HBB 快速跑通 baseline再换 OBB 对比提升。下面这张表是我整理的两类格式在舰船场景下的对比供选型参考。维度水平框 HBB旋转框 OBB标注成本低高背景引入多近岸尤甚少框架支持全mmrotate、部分 YOLO 变体适合任务计数、分类定位、方向、长宽比小目标表现一般较好2.3 标注文件组织与类别命名规范数据集一般按 images 和 labels 分目录标注格式可能是 VOC XML、COCO JSON 或 YOLO txt。舰船数据集里常见的一个坑是类别名不统一比如同一批数据里出现 ship、vessel、warship 混用训练时要么报类别越界要么把同一类学成多类。我一般会先写个脚本扫一遍所有标注文件把类别名去重打印出来确认后再统一映射。import os, glob, xml.etree.ElementTree as ET # 扫描 VOC 格式标注统计类别名出现情况 label_dir annotations class_counter {} for xml_path in glob.glob(os.path.join(label_dir, *.xml)): tree ET.parse(xml_path) for obj in tree.findall(object): name obj.find(name).text.strip() class_counter[name] class_counter.get(name, 0) 1 # 打印类别名和数量人工确认是否需要合并 for k, v in sorted(class_counter.items(), keylambda x: -x[1]): print(f{k}: {v})这段脚本的作用是把所有标注里的类别名和出现次数列出来。参数上没什么可调的重点在输出结果——如果看到 ship 和 Ship 同时出现说明大小写不一致如果 vessel 和 ship 数量接近大概率是同一类被标了两次。确认后写一个映射字典统一替换别等到训练时报错才回头查。3. 把舰船标注转成 YOLO 可训练格式脚本与四个边界坑3.1 VOC 转 YOLO txt 的完整脚本YOLO 系列训练需要每张图对应一个 txt每行是class_id cx cy w h全部归一化到 0-1。舰船数据集如果给的是 VOC XML转换时最容易出问题的不是公式是边界情况。下面这个脚本我用了很多次处理了越界裁剪和空标注。import os, glob, xml.etree.ElementTree as ET # 类别映射按你的数据集实际类别改 classes [ship] img_w, img_h 1024, 1024 # 按实际图像尺寸改或从 XML 的 size 节点读 def convert(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() # 优先从 XML 读取图像尺寸避免硬编码出错 size root.find(size) if size is not None: w int(size.find(width).text) h int(size.find(height).text) else: w, h img_w, img_h lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in classes: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 边界裁剪防止标注越界导致归一化后为负或大于1 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) if xmax xmin or ymax ymin: continue # 跳过无效框 cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{classes.index(name)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) # 空标注也要生成空文件否则训练时可能报找不到 label out_path os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines)) for p in glob.glob(annotations/*.xml): convert(p, labels)逻辑上分三步读 XML 拿图像尺寸和框坐标、裁剪越界、归一化写 txt。参数上唯一要改的是 classes 列表和图像尺寸来源。注意空标注也要写空文件YOLO 训练时如果某张图没有对应 txt部分版本会直接跳过或报错行为不一致。3.2 旋转框转 YOLO OBB 格式的角度处理如果你用的是 OBB 标注转 YOLO OBB 格式时每行是class_id cx cy w h angle角度单位是弧度范围通常约定在 [-pi/2, 0) 或 [0, pi/2)。不同工具导出的角度定义不一样有的用长边定义有的用短边直接混用会导致框旋转 90 度。我一般会先可视化几张确认角度方向再批量转。import math def poly_to_obb(points, img_w, img_h): # points 为四个角点 [(x1,y1),...]按顺时针或逆时针 xs [p[0] for p in points] ys [p[1] for p in points] cx sum(xs) / 4.0 / img_w cy sum(ys) / 4.0 / img_h # 用相邻边计算宽高和角度 edge1 math.hypot(xs[1]-xs[0], ys[1]-ys[0]) edge2 math.hypot(xs[2]-xs[1], ys[2]-ys[1]) w max(edge1, edge2) / img_w h min(edge1, edge2) / img_h angle math.atan2(ys[1]-ys[0], xs[1]-xs[0]) # 归一化到 [-pi/2, 0) if angle 0: angle - math.pi / 2 return cx, cy, w, h, angle关键在角度归一化那两行。不同框架对角度范围要求不同mmrotate 和 ultralytics 的 OBB 定义就有差异转完一定要拿几张图叠加可视化确认框贴合船体再批量跑。3.3 数据集划分与 data.yaml 配置转完格式后按 8:1:1 或 7:2:1 划分 train/val/test写一个 data.yaml 给 YOLO 用。舰船数据集样本量通常不大划分时注意别让某一类只出现在训练集里。path: ./ship_dataset train: images/train val: images/val test: images/test nc: 1 names: [ship]nc 是类别数names 顺序必须和转换脚本里的 classes 一致错一位整个训练就废了。这个文件看着简单但我见过至少三次因为 names 和实际 id 对不上导致模型全预测成背景的情况。4. 用 YOLOv8 在舰船卫星图上跑通第一个 baseline4.1 环境配置与预训练权重选择环境这块ultralytics 的 YOLOv8 是目前上手最快的选择pip 装完就能跑。预训练权重建议从 COCO 的 yolov8s 或 yolov8m 起步n 太小对遥感小目标欠拟合l 以上在小数据集上容易过拟合。舰船数据集样本量一般几千张s 或 m 是比较稳的档位。pip install ultralytics # 用 COCO 预训练权重起步s 档在精度和速度间比较平衡 yolo detect train dataship_dataset/data.yaml modelyolov8s.pt epochs100 imgsz1024 batch8imgsz 设 1024 是因为舰船目标小下采样太狠会丢细节。batch 根据显存调8G 显存跑 1024 大概能到 8。epochs 先跑 100 看曲线舰船数据集通常 100 到 200 轮收敛。4.2 针对小目标和任意朝向的参数调整默认 anchor 和损失权重是按 COCO 调的舰船场景要动几个地方。第一关闭马赛克增强或者降低概率马赛克会把小目标拼得更小第二调小目标检测层的权重第三如果目标普遍小于 32 像素考虑加一层 P2 特征。yolo detect train dataship_dataset/data.yaml modelyolov8s.pt epochs150 imgsz1024 batch8 \ mosaic0.5 close_mosaic20 scale0.3 degrees0.0mosaic0.5 是降低拼接概率close_mosaic20 是最后 20 轮关闭马赛克让模型适应真实分布。degrees0.0 是因为舰船朝向已经任意再做随机旋转意义不大反而增加学习负担。scale0.3 控制缩放幅度别让船变得太小。4.3 训练日志里该盯的三个指标跑起来之后别只看 mAP训练日志里三个东西最值得盯一是 cls_loss 和 box_loss 的下降曲线如果 cls_loss 早早躺平说明类别太单一或者学习率不对二是 val 的 mAP50 和 mAP50-95 差距差距大说明框定位不准可能是角度或尺度问题三是每类的 AP单类数据集就看整体但如果有子类要确认没有某一类 AP 为 0。注意如果 mAP50 高但 mAP50-95 很低八成是框贴合度不够优先检查标注质量和角度定义别急着换模型。5. 舰船检测避坑五条血泪排查记录5.1 现象mAP 卡在 0.3 不涨loss 也不降原因标注格式转换时归一化用错了图像尺寸比如 XML 里写的是 1024 但实际图是 512导致所有框坐标偏大一倍。解决写脚本随机抽 20 张图把转换后的框画回原图叠加肉眼确认贴合。这一步花十分钟能省两天调参。5.2 现象近岸船只大量漏检原因近岸背景复杂码头、集装箱和船体纹理接近模型把船当背景。解决在数据层面补充近岸负样本或者用 copy-paste 增强把船贴到复杂背景上。训练时提高近岸样本的采样权重也有帮助。5.3 现象旋转框训练 loss 震荡不收敛原因角度定义和框架要求不一致比如标注是 [0, pi] 但框架要 [-pi/2, 0)导致同一艘船的角度在训练中跳变。解决统一角度范围后再训转换脚本里加一行归一化别指望模型自己学。5.4 现象验证集 mAP 正常但测试集崩了原因划分时没有按场景分层训练集全是开阔海域测试集全是港口。解决划分前先按背景复杂度或地理位置聚类再分层抽样保证三个集合分布一致。5.5 现象推理时框重叠严重NMS 压不掉原因舰船并排停靠时框高度重叠默认 NMS 阈值 0.7 不够。解决调低 NMS 的 iou 阈值到 0.5 左右或者换 soft-NMS。但注意别调太低否则密集小目标会被误删。6. 从能跑到好用舰船检测的验证技巧与迭代习惯baseline 跑通之后真正决定模型能不能上线的是验证环节。我一般会做三件事。第一按目标尺度分桶统计 AP把目标分成小32、中32-96、大96三档看模型到底弱在哪一档。舰船数据集通常小目标 AP 最低如果小目标 AP 只有大目标的一半说明特征层或 imgsz 还得调。第二按场景分桶把验证集分成开阔海域、近岸、港口三类分别算 AP找出模型的实际短板场景。第三做一轮错误可视化把 FP 和 FN 最多的图挑出来看往往能发现标注错误或者系统性偏差。# 按目标面积分桶统计需要推理结果和 GT 对齐后计算 import numpy as np def bucket_ap(gt_areas, pred_matches, thresholds(32**2, 96**2)): small [i for i, a in enumerate(gt_areas) if a thresholds[0]] medium [i for i, a in enumerate(gt_areas) if thresholds[0] a thresholds[1]] large [i for i, a in enumerate(gt_areas) if a thresholds[1]] for name, idxs in [(small, small), (medium, medium), (large, large)]: if not idxs: continue matched sum(pred_matches[i] for i in idxs) print(f{name}: {matched}/{len(idxs)} {matched/len(idxs):.3f})这段代码的核心是把 GT 按面积分桶再统计每桶的匹配率。参数 thresholds 按你的 GSD 调0.5m 分辨率下 32 像素约等于 16 米差不多是一艘小艇的长度。跑完你会清楚知道模型是「整体不行」还是「只在小目标上不行」对应的优化方向完全不同。迭代习惯上我自己的做法是每次只改一个变量改完记录 mAP 和分桶 AP攒够五次对比再决定下一步。舰船检测这方向数据质量比模型结构重要得多标注框贴合、角度一致、场景均衡这三件事做到位YOLOv8s 就能跑出能用的结果。反过来标注一塌糊涂换什么大模型都是白搭。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。