ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

2238张遥感舰船数据集:VOC与YOLO双格式,YOLOv8目标检测直接训练

2238张遥感舰船数据集:VOC与YOLO双格式,YOLOv8目标检测直接训练 简介面向遥感目标检测与舰船识别场景这份数据集提供2238张卫星遥感舰船影像及对应标注同时采用Pascal VOC和YOLO两种格式覆盖17个类别包括航空母舰、驱逐舰、潜艇、油轮、货船、护卫舰等常见及细粒度舰船类型。资源共2000个文件xml标注为主辅以yolo格式txt与使用说明压缩包约109.32MB可直接用于YOLO系列、Faster R-CNN等主流检测框架省去格式转换环节。目前已有835人学习适用于高校遥感实验、算法竞赛与工程化舰船检测任务。图片与标注一一对应文件按统一编号命名便于划分训练集与验证集配套博文详解了数据集构建流程和类别分布能帮助研究者快速评估模型在多类别舰船识别上的表现。整体上这份数据集标注规范、类别覆盖广既适合刚接触目标检测的学习者快速上手也能满足进阶研究中的多类别细粒度识别需求是遥感视觉方向不可多得的实战资源。1. 卫星遥感舰船检测数据集2238张图、17类标注VOC和YOLO格式一次给齐做遥感舰船检测最烦的不是模型写不出来而是数据集拿到手要先花两三天写转换脚本。这份资源直接把这一步省掉了2238张jpg图片、2238个VOC格式xml、2238个YOLO格式txt全部配对好17个类别覆盖航母、驱逐舰、潜艇、油轮、滚装船这些常见舰船解压之后就能开始训练。适合两类人一是刚接触yolov8训练自己的数据集、想找一份干净数据跑通全流程的初学者二是做卫星遥感目标检测、需要真实标注来验证预处理和推理策略的熟手。它解决的是「下载完就能直接干活」的问题而不是让你先被格式折腾一周。2. 为什么同时给 VOC 和 YOLO 两套标注格式差异与选型逻辑2.1 VOC 的 XML 到底存了什么对象级标注与归一化坐标的关系Pascal VOC 格式的标注文件是 XML每个 xml 对应一张 jpg。打开 firc_ship_2222.xml 能看到典型的 VOC 结构folder记录目录名filename记录图片名size里有 width、height、depth 三个值接下来是多个object节点每个节点代表图上的一艘船。annotation foldersatellite/folder filenamefirc_ship_2222.jpg/filename size width1024/width height768/height depth3/depth /size object nameAircraft Carrier/name bndbox xmin312/xmin ymin258/ymin xmax687/xmax ymax419/ymax /bndbox difficult0/difficult /object /annotationxml 里存的是像素坐标xmin/xmax/ymin/ymax 都是原始图片上的绝对位置。这套坐标的好处是直观坏处是不同分辨率图片之间没法直接对比训练框架内部还是得做归一化。所以很多框架的 DataLoader 在读 VOC 时第一件事就是把像素坐标除以 width 和 height 转成 0~1 的相对值。这里有一个常见误区xml 的name标签必须和类别清单严格一致哪怕多一个空格、改了一个大小写训练时类别映射就会错位。2.2 YOLO 格式的 txt 为什么更适合训练从归一化坐标看选型YOLO 格式把每个标注目标写成一行类别索引、中心点 x、中心点 y、宽度、高度全部是 0~1 的归一化值。比如 firc_ship_2222.txt 里有一行0 0.485 0.315 0.326 0.167对应上面 xml 里那艘 Aircraft Carrier类别索引 0 指的是类别清单里第一个名字。YOLO 训练时从 txt 读入坐标不需要再做任何除法直接和特征图上的 anchor 计算 IoU省了一步转换也能少踩一个数据精度坑。之所以推荐这份数据集的 YOLO 格式做训练是因为它的 txt 已经按类别清单排好了索引。训练前你要做的就是把 17 个类别名按顺序写进 data.yaml不能改顺序。比如列表里第一个是 Aircraft Carrier那 txt 里的索引 0 就必须是 Aircraft Carrier如果你把 names 改成字母序索引 0 变成了 Auxiliary Ships训练不会报错mAP 会直接废掉。这种错误最常见也最隐蔽loss 曲线看起来很正常但混淆矩阵总合不唯一就是类别名和索引对不上。2.3 数据集的目录组织从压缩包结构看使用方式先看下面的文件组织这是这类数据集的通用解压结构原因下文会说。拿到 .7z 包之后先看结构再动手别一上来就复制文件。目录/文件数量说明images/2238原始 jpg 图片xmls/ 或 annotations/2238VOC 格式标注labels/ 或 yolo_txt/2238YOLO 格式标注使用前必读.txt1命名规则与标注约定这个数据集不包含 train.txt / val.txt 这种「分割路径索引」文件摘要里写得很清楚只有 jpg、对应的 VOC xml、对应的 YOLO txt。这意味着训练集和验证集要自己划分别在 data.yaml 里直接填 txt 文件路径。我一般会建议先做一次文件名一致性检查——三个目录下的文件名前缀必须完全一致比如 firc_ship_2222.jpg、firc_ship_2222.xml、firc_ship_2222.txt 同时存在。这一步用脚本跑掉之后再划分能省下后面排查数据加载报错的几个小时。3. 把数据集跑起来目录检查到 YOLOv8 训练的第一步3.1 解压后的目录核对与文件一致性校验解压之后第一步不是急着训练而是校验文件的配对关系。遥感数据集经常出现某张图没有标注、或者 xml 和 txt 名字对不上的情况训练的时候报 FileNotFoundError 还算好最怕的是 yolov8 训练自己的数据集时静默跳过这些坏样本指标上去了但实际没学到东西。我一般会先跑一段配对检查脚本# check_pair.py 检查三个目录下文件名是否一一对应 import os from collections import Counter image_dir images xml_dir xmls txt_dir labels def get_stems(dir_path): stems set() for name in os.listdir(dir_path): if name.lower().endswith((.jpg, .jpeg, .png)): stems.add(os.path.splitext(name)[0]) elif name.lower().endswith((.xml, .txt)): stems.add(os.path.splitext(name)[0]) return stems img_stems get_stems(image_dir) xml_stems get_stems(xml_dir) txt_stems get_stems(txt_dir) missing_xml img_stems - xml_stems missing_txt img_stems - txt_stems extra_xml xml_stems - img_stems print(jpg 数量:, len(img_stems)) print(xml 缺少:, len(missing_xml), sorted(missing_xml)[:10]) print(txt 缺少:, len(missing_txt), sorted(missing_txt)[:10]) print(多余 xml:, len(extra_xml), sorted(extra_xml)[:10])这段脚本分别读取三个目录的文件主名不含扩展名然后用集合取差集。正常情况三个数量都是 2238差集为空。如果 txt 比 jpg 少说明某张图只有 VOC 标注没有 YOLO 标注可以用 xml 转 txt 补回去反过来说明有冗余文件训练时容易索引错乱。这里注意我用了 set 而不是 list因为文件名数量大set 的去重和差集运算更快输出前 10 个就够定位问题了。3.2 划分训练集和验证集按类别分布采样而不是纯随机确认文件配对没问题之后划分数据集是第二个关键动作。纯随机划分在遥感数据上经常翻车某个小类别比如 Submarine 或者 Yacht总量就几十个框随机一划分可能全掉进验证集训练集里一个都见不到。yolov8 在 data.yaml 里有个参数叫val它可以是一个目录但目录里的图片你得自己放好。我习惯写成独立脚本固定随机种子保证每次跑出来的划分结果可复现。# split_dataset.py 按类别分布做分层划分 import os import random import shutil from collections import defaultdict random.seed(42) val_ratio 0.2 image_dir images label_dir labels train_img_dir dataset/images/train val_img_dir dataset/images/val train_lbl_dir dataset/labels/train val_lbl_dir dataset/labels/val os.makedirs(train_img_dir, exist_okTrue) os.makedirs(val_img_dir, exist_okTrue) os.makedirs(train_lbl_dir, exist_okTrue) os.makedirs(val_lbl_dir, exist_okTrue) # 统计每张图里出现过的类别集合 img_classes defaultdict(set) for txt_name in os.listdir(label_dir): if not txt_name.endswith(.txt): continue stem os.path.splitext(txt_name)[0] with open(os.path.join(label_dir, txt_name)) as f: for line in f: parts line.strip().split() if len(parts) 1: img_classes[stem].add(int(parts[0])) # 按类别分层抽样每个类别至少保证一定比例进 train all_stems list(img_classes.keys()) val_stems set() for cls_id in range(17): stems_with_cls [s for s, cls_set in img_classes.items() if cls_id in cls_set] val_count max(1, int(len(stems_with_cls) * val_ratio)) val_stems.update(random.sample(stems_with_cls, val_count)) for stem in all_stems: img_src os.path.join(image_dir, stem .jpg) lbl_src os.path.join(label_dir, stem .txt) if stem in val_stems: shutil.copy(img_src, os.path.join(val_img_dir, stem .jpg)) shutil.copy(lbl_src, os.path.join(val_lbl_dir, stem .txt)) else: shutil.copy(img_src, os.path.join(train_img_dir, stem .jpg)) shutil.copy(lbl_src, os.path.join(train_lbl_dir, stem .txt)) print(train 图片:, len(os.listdir(train_img_dir))) print(val 图片:, len(os.listdir(val_img_dir)))核心是先把每张图包含的类别索引收集起来然后遍历 17 个类别每个类别单独抽出 20% 进验证集用 set 合并保证同一张图不会又进训练又进验证。这里有两个参数可以调val_ratio 默认 0.2小数据集可以降到 0.15max(1, ...)保证小类别即使只有 1~2 张图也会至少留一张进验证集。复制用 shutil.copy 而不是 os.rename是为了保留原目录不动后续想重划分不用重新解压。3.3 写一个 labels 可视化脚本把 txt 坐标画回图上看划分完成后我强烈建议把训练集里随机抽几张图把 YOLO txt 坐标画回原图检查一遍。这一步看着简单但能一次性发现坐标归一化方向反了、x/y 写反、目标框超出图片边界这类问题。绕开这一步直接训练轻则 loss 不收敛模型学了错误的框重则训练时 batch 里出现空标注BN 崩溃。# visualize_labels.py 将 yolo txt 坐标画回 jpg import cv2 import os import random label_dir dataset/labels/train image_dir dataset/images/train class_names [ Aircraft Carrier, Auxiliary Ships, Cargo, Commander, Container Ship, Cruiser, Destroyer, Dock, Frigate, Hovercraft, Landing, Oil Tanker, Other Ship, Other Warship, RoRo, Submarine, Yacht ] random.seed(1) sample_names random.sample(os.listdir(label_dir), 5) for txt_name in sample_names: stem os.path.splitext(txt_name)[0] img_path os.path.join(image_dir, stem .jpg) img cv2.imread(img_path) if img is None: print(图片读取失败:, img_path) continue h, w img.shape[:2] with open(os.path.join(label_dir, txt_name)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(非法标注行:, txt_name, line) continue cls_id, xc, yc, bw, bh map(float, parts) # yolo 的 xc,yc,bw,bh 都是 0~1 归一化值 x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) # 检查坐标是否越界 if x1 0 or y1 0 or x2 w or y2 h: print(越界框:, txt_name, line) color (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[int(cls_id)], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) out_path check_ stem .jpg cv2.imwrite(out_path, img) print(已输出:, out_path)这段脚本把第 5 个字段拆开cls_id 是类别索引后四个是中心点和宽高的归一化值。转像素坐标时有个易错点先算中心再加减宽高一半而不是直接乘 w 和 h。画框的同时做了越界检查一旦发现 x1 小于 0 或者 x2 大于 w就说明 txt 里某个目标归一化坐标超出图片实际范围多半是标注时用了错误的图片尺寸。输出文件名带 check_ 前缀和源图分开存放方便直接在文件夹里扫一遍看有没有画歪的框。4. 类别不平衡与标注噪声遥感舰船检测训练前的数据体检4.1 17 类标注的统计口径与类别分布遥感舰船检测和普通目标检测最大的不同在于类别分布极不均衡。常见民用船Cargo、Container Ship、Oil Tanker、Other Ship数量会明显偏多而 Submarine、Hovercraft、Dock 这类目标数量少、出现频率低甚至某些类别在整个数据集里只有几十个框。这个数据集 17 个类别训练前先跑一遍统计心里有底再决定要不要做类别重加权。# stats_classes.py 统计每类目标数量和每类图片数量 import os from collections import Counter label_dir dataset/labels/train cls_counter Counter() img_counter Counter() for txt_name in os.listdir(label_dir): with open(os.path.join(label_dir, txt_name)) as f: seen set() for line in f: parts line.strip().split() if not parts: continue cls_id int(parts[0]) cls_counter[cls_id] 1 seen.add(cls_id) for cls_id in seen: img_counter[cls_id] 1 class_names [ Aircraft Carrier, Auxiliary Ships, Cargo, Commander, Container Ship, Cruiser, Destroyer, Dock, Frigate, Hovercraft, Landing, Oil Tanker, Other Ship, Other Warship, RoRo, Submarine, Yacht ] for cls_id in range(17): print(f{cls_id:2d} {class_names[cls_id]:20s} f框数:{cls_counter[cls_id]:5d} 图片数:{img_counter[cls_id]:4d})统计两个维度目标框总数和包含该类别的图片数两者差距大的类别说明单张图里密集出现比如 Dock 场景里可能有多个目标挤在一起。跑完脚本对照 17 个类别名如果 Submarine 只有 100 个框左右训练时给它的 loss 权重就要适当调高否则模型会只顾着学 Cargo 这类大样本验证集上 Submarine 的 recall 趋近于 0。这里我一般用的策略是给样本少的类别在 loss 函数里加 class weight权重取总框数 / 该类别框数的平方根不用线性值不然头部类别被压得太狠。4.2 小目标问题2238 张图里的舰船到底有多小遥感舰船检测的典型痛点是目标小。一张 1024×768 的卫星图一艘驱逐舰可能只占 40×20 像素按 COCO 的标准属于小目标面积小于 32×32 像素。这个数据集的图片分辨率没有统一训练时如果直接 resize 到 640×640原本 40 像素的船就缩到 16 像素模型很难学到有效特征。所以训练前需要统计目标尺寸分布决定要不要用多尺度训练。# analyze_sizes.py 按像素面积统计目标尺寸分布 import os label_dir dataset/labels/train size_bins {tiny(1024px): 0, small(3024px): 0, medium(9216px): 0, large(9216px): 0} for txt_name in os.listdir(label_dir): with open(os.path.join(label_dir, txt_name)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue bw float(parts[3]) bh float(parts[4]) # 先假设图片 1024x1024算出近似像素面积再分桶 area_px (bw * 1024) * (bh * 1024) if area_px 32 * 32: size_bins[tiny(1024px)] 1 elif area_px 96 * 96: size_bins[small(3024px)] 1 elif area_px 96 * 96 * 1.2: size_bins[medium(9216px)] 1 else: size_bins[large(9216px)] 1 for k, v in size_bins.items(): print(k, v)注意这里做了简化用 1024×1024 作为假想尺寸估算像素面积因为 txt 里只有归一化宽高。实际使用时应该读每张 jpg 的真实宽度和高度把归一化宽高乘回去得到准确像素面积再分桶。分桶阈值我沿用了 COCO 的划分逻辑小于 32×32 算小目标小于 96×96 算中等目标。如果统计结果 tiny 和 small 占比超过一半训练时 imgsz 建议调到 960 或 1280开启多尺度yolov8 的 scale 参数从 0.5 调到 0.3~1.2推理时再做多尺度测试细节在最后一章展开。4.3 标注噪声的典型位置边界框贴合度与遮挡处理遥感数据集里标注噪声是逃不掉的现实这份数据集的 xml 和 txt 是二次转换过的转换过程本身可能引入两类典型问题。第一类是边界框贴合度不够有些船只有部分可见标注员习惯把整条船的完整轮廓框进去导致框里包含了大量海面背景模型学到的特征被背景稀释。第二类是难例标注不一致Hovercraft 和气垫船、Other Ship 与 Cargo 之间的界限模糊不同标注员可能给出不同类别。对于贴合度问题我一般不会手动重新标而是检查有多少框的宽高比异常。舰船目标平躺时宽高比在 2~6 之间如果出现宽高比接近 1 甚至小于 1 的框大概率是标注框切了码头或者其他建筑物。处理方式是训练时对这类样本做 cutout 增强模拟遮挡强迫模型关注可见部分。对于类别混淆问题最快的手段是看验证集混淆矩阵总合是否唯一如果模型的 Cargo 和 Other Ship 互相误判率很高说明这两个类在原始标注里边界就不清晰可以考虑在损失函数里把二者合并在输出层用软标签让模型自己学差异。5. 避坑记录三周跑完这份数据集踩过的五个问题5.1 现象训练 loss 正常但 mAP 始终上不去跑 yolov8 训练自己的数据集loss 从 2.0 正常降到 0.8但验证集 mAP50 卡在 0.3 左右不上涨。查了网络结构、学习率、anchor 都没问题。 原因data.yaml 里的 names 顺序和 txt 里的类别索引不一致。原始类别清单是「Aircraft Carrier, Auxiliary Ships, Cargo, Commander,...」我自作聪明按字母序排了一遍导致 txt 里索引 0 对应的 Aircraft Carrier 被模型当成 Auxiliary Ships 学。 解决txt 的类别索引从 0 到 16 已经固定data.yaml 的 names 必须按原顺序填一个位置都不能动。改完 names 重新训练mAP50 直接从 0.3 跳到 0.61。5.2 现象验证集指标虚高实际推理效果差一截训练时验证集 mAP 有 0.65但把模型放到没见过的卫星图上检测效果明显跟不上漏检一堆小目标。 原因我用纯随机比例划分训练集和验证集同一颗卫星拍摄的连续相邻图被分到了两边。验证集里大量图的地物背景和目标尺度和训练集几乎一样等于开卷考试。 解决划分前按文件名前缀做场景聚类firc_ship_2222 和 firc_ship_2223 这种连续编号大概率来自同一场景按编号区间整体划分而不是单张随机抽。从那以后我先给文件名加场景前缀再划分验证指标才可信。5.3 现象图片尺寸不统一导致画框错位、标注越界检查可视化结果时发现部分图框的位置明显偏移有的框超出了图像边界。 原因jpg 里带 EXIF 方向信息手机或某些卫星图处理后 cv2.imread 读出来是旋转后的图而 xml 和 txt 是基于原始方向的坐标另外部分图片实际尺寸和标注时的参考尺寸不一致。 解决第一个问题统一用 cv2.imread 读取并忽略 EXIF 方向或者训练前全部转成同一方向第二个问题在预处理脚本里读每张图的真实宽高和 xml 的width/height做比对不一致的直接打出来人工确认。坐标越界的框我后来加了裁剪逻辑越界超过 10% 的删掉该目标轻微越界的做 clamp。5.4 现象yolov8 训练时提示 labels 文件报错或目标数为空训练日志里出现 Warning: label format 不对或者某个 batch 的 gt 数量为 0。 原因txt 里有空行、多余空格或者类别索引值超过 16。这类问题大多是 xml 转 txt 时处理不当留下的尾巴视觉上看不出来训练中途才会爆。 解决写了个清洗脚本遍历所有 txt过滤空行、检查每行字段数是否为 5、检查 cls_id 是否在 0~16 范围。清洗之后训练日志干干净净没有警告弹出来BN 也不再震荡。5.5 现象显存不够、loss 波动剧烈、模型不收敛遥感大图直接按原始分辨率训练7G 显存的卡 batch 拉不上去一开 mosaic 增强显存直接爆。强行调 480 分辨率跑loss 波动得像心跳图。 原因图片里的小目标在低分辨率下变成了 2~3 个像素yolov8 的 anchor 机制找不到足够多的正样本loss 回传不稳定。加上遥感图背景复杂batch 太小 BN 统计量抖动严重。 解决先把 imgsz 固定到 960关闭 mosaic 只留基本翻转增强确认 loss 能稳住之后再逐步开 mosaic显存不够就改 AMP 混合精度训练pyTorch 的 autocast 能省近一半显存速度也提 30%。我这边最终参数是 imgsz960、batch8、AMP 开启loss 曲线平滑收敛。6. 多尺度推理与切片检测遥感舰船小目标的验证技巧6.1 多尺度测试把原图缩放成三级金字塔再合并结果验证阶段建议不要只在单一分辨率上跑推理而是把每张测试图缩放到原图、0.75 倍、0.5 倍三个尺度分别推理后再把预测框映射回原图坐标做 NMS 合并。这个操作在普通数据集上收益不大但对舰船这种小目标密集的遥感场景能把 recall 拉高 5~8 个点。映射时要注意缩放系数框坐标除以对应的 scale 再送回原图坐标系。6.2 TTA 的三个常用参数组合yolov8 的 predict 里可以直接开 TTAmodel.predict(sourcetest, imgsz960, augmentTrue, scale0.8, fliplr0.5)。三个参数的意义分别是scale 控制多尺度推理的范围0.8 表示在 0.8 倍尺度上下浮动采样fliplr 是水平翻转增强率batch 里一半图翻转后推理再融合augment 总开关打开后模型内部会自动做多次增强推理取平均。参数没固定标准遥感大图我习惯 scale 开到 0.7小图数据集 scale 保留默认 0.5 即可翻转发在这个数据集上收益不大因为舰船方向性明显翻了之后反而容易误判左右舷。6.3 切片检测的实际收益另一种思路是不缩放整图而是把大图切成分块分别检测后合并。用 512×512 窗口、overlap 0.2 滑窗检测完把每个窗口的预测框坐标加上窗口偏移量映射回大图坐标再做 NMS。切片检测可以把原本 30 像素的小目标放大到 150 像素模型能提取到更多纹理特征。多次跑下来切片后 recall 比不切片平均高 8%~12%但推理时间也翻倍适合离线分析和监控场景不适合实时推理。我现在的习惯是先跑整图推理拿基线再对高置信度区域做局部切片二次确认兼顾速度和精度。从那以后这份数据集每次被我用到新项目我都强制先走一遍第 3 章的配对检查和第 4 章的尺寸统计再训练鱼没上岸之前不急着下锅。希望帮到你。本文还有配套的精品资源点击获取
返回列表