简介:面向人工智能目标检测研究者的战车卫星图数据集,聚焦装甲车辆在复杂遥感场景下的识别与定位,适合计算机视觉、军事遥感和自动驾驶等领域的中高级开发者使用。压缩包内包含图像文件夹、标注文件、说明文档及重命名脚本,整体约343.63MB。数据集约含1000张1024×1024彩色卫星图,每图均可能包含战车目标,配套标注信息提供边界框等位置坐标,可直接用于训练YOLO、Faster R-CNN等检测框架;说明文档与预处理脚本便于理解数据集结构和批量整理。目前已有2902人学习下载,适合需要实际遥感目标检测数据的算法验证与模型调优场景。读者可获得完整的数据集目录结构、标注格式说明与预处理脚本,有助于缩短数据准备周期,评估模型在光照变化、遮挡和远距离条件下的检测性能。
1. 战车卫星图2:为什么遥感小目标检测比你想的更依赖数据集
把一张 5120×5120 的卫星大图直接丢给目标检测模型,第一轮验证集 mAP50 做到 0.78,看着挺像样;等把它切成 1024 瓦片再验证,mAP50 直接掉到 0.33。这种“大图能中、切片就废”的反差,几乎是每个接触人工智能目标检测数据集(战车卫星图2)的人都会撞上的第一堵墙。战车卫星图2 这类遥感数据集解决的核心问题,不是教会模型认坦克,而是让模型在俯视视角、低分辨率、复杂背景里找到只有几十像素的小目标。适合用它的人有两类:一类是做军事目标识别、遥感图像分析落地项目的从业者,另一类是把公开自然图像数据集刷吐了、想找一批更有挑战性的小目标数据来验证检测框架极限的工程师。
2. 先拆数据集再谈训练:目录结构、标注格式与统计口径
2.1 三种常见目录布局与标注格式识别
拿到战车卫星图2 的第一件事不是急着训练,而是把数据目录完整看一遍。常见做法是压缩包解压后呈现三种布局之一:纯 VOC 风格、纯 YOLO 风格、混合风格。VOC 风格通常长这样:
dataset/ ├── JPEGImages/ # 大图和切片图混放 │ ├── img_0001.jpg │ └── img_0002.jpg ├── Annotations/ # VOC 格式 XML │ ├── img_0001.xml │ └── img_0002.xml └── ImageSets/ └── Main/ ├── train.txt └── val.txtYOLO 风格则是把标签与图片分开,每张图对应一个同名 txt,每行是“类别ID cx cy w h”,全部为归一化坐标。还有一种常见变体,把大图放在origin/,切片放在tiles/,标注按切片单独存。我一般会先用一条命令把所有标注文件后缀统计一遍,判断格式再决定后续要不要转换:
find dataset -type f \( -name "*.xml" -o -name "*.txt" -o -name "*.json" \) | awk -F'.' '{print $NF}' | sort | uniq -c拿到统计结果后,一个容易被忽略的点是:标注格式只有 xml/txt/json 不代表内容就是统一规范的。有的 xml 里<name>写的是中文类别名,有的写拼音缩写,有的 box 坐标是整数、有的带小数,这些都要在转换前统一。另一个常见情况是train.txt和val.txt里只有文件名不带路径,写数据加载器时要先补齐前缀。
三种标注格式的适用差异很直接:VOC 适合做细致类别标注和手工检查,但训练时大部分检测框架不能直接吃;YOLO txt 是多数现代框架的默认输入,节省一步转换;COCO json 适合做实例分割和多类别评估,但小目标数据集里用 json 查看单张图非常费劲。建议优先转成 YOLO txt,因为训练前的可视化、数据增强、模型输入都能直接用一套工具链。下表是三种格式的字段对比:
| 格式 | 坐标基准 | 典型字段 | 训练框架适用性 |
|---|---|---|---|
| VOC XML | 左上右下像素坐标 | xmin, ymin, xmax, ymax | 需转换 |
| YOLO txt | 归一化中心宽高 | cx, cy, w, h | 原生支持 |
| COCO json | 左上宽高像素坐标 | x, y, w, h | 转 YOLO 更常见 |
2.2 类别映射与样本统计:看清楚再动手
战车卫星图2 的类别通常不会只有一类“战车”,更常见的是坦克、装甲输送车、自行火炮、卡车、指挥车等多个细分类。类别映射表是后续一切工作的基础,我习惯先写成明确的映射清单,而不是在转换脚本里散落数字。比如tank: 0, apc: 1, self-propelled-gun: 2, truck: 3,类别 ID 从 0 开始与 YOLO 系列框架保持一致,避免训练时因为索引偏一导致所有框都错位。
统计脚本要输出三类信息:每个类别的目标框数量、每张图的平均目标数、图片尺寸分布。下面这个脚本能快速摸清数据家底:
import os import xml.etree.ElementTree as ET annotations_dir = "Annotations" class_count = {} img_objects = [] for xml_file in os.listdir(annotations_dir): tree = ET.parse(os.path.join(annotations_dir, xml_file)) root = tree.getroot() objs = root.findall("object") img_objects.append(len(objs)) for obj in objs: name = obj.find("name").text class_count[name] = class_count.get(name, 0) + 1 print("类别统计:", class_count) print("平均每图目标数:", sum(img_objects) / len(img_objects))这段代码的逻辑就是把每个 XML 文件里的<object>节点数出来,按类别名累加。如果代码跑出来的class_count里有某些类别数量只有个位数,就要警惕类别不平衡:模型可能会把这些类全部学成背景。平均每图目标数低于 1.5 是一个常见信号,说明切片图里大量是负样本,训练时要把负样本比例控制住,否则正样本的梯度会被负样本淹没。
图片尺寸分布这一步容易被跳过,但恰恰很关键。用 Python 的PIL循环读图片取size,统计出哪些图是 512×512、哪些是 1024×1024。如果同一个数据集中混了多种尺寸,后面做 batch 训练时要么强制 resize 导致小目标进一步缩小,要么做 padding 补齐。我看到的多数遥感数据集最终都会统一到一个尺寸,但开箱即用的数据很少完全干净,这步统计能提前暴露问题。
3. 把标注转成 YOLO 格式:坐标归一化与转换脚本
3.1 最小可用的 VOC 转 YOLO 脚本
拿到 XML 标注后,转成 YOLO txt 是训练的第一道工序。最小脚本不需要依赖第三方库,用标准库xml.etree.ElementTree就能完成,核心是几何换算:VOC 的xmin, ymin, xmax, ymax是像素坐标,YOLO 需要的是归一化到 [0,1] 区间的中心点坐标和宽高。换算公式是:
cx = (xmin + xmax) / 2 / image_widthcy = (ymin + ymax) / 2 / image_heightw = (xmax - xmin) / image_widthh = (ymax - ymin) / image_height
下面是一段经过验证的转换脚本核心逻辑:
import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, img_width, img_height, class_map, out_path): 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_map: continue # 跳过不在映射表中的类别 box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) cx = (xmin + xmax) / 2.0 / img_width cy = (ymin + ymax) / 2.0 / img_height w = (xmax - xmin) / img_width h = (ymax - ymin) / img_height lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines))这段代码逐字段取值,按公式归一化,最后组装成 YOLO txt 的一行。需要注意img_width和img_height必须来自实际图片的真实尺寸,而不是 XML 里<size>节点的值——虽然多数情况下两者一致,但有少量数据集的<size>写的是原大图尺寸,图片却是切片后的尺寸,直接用会出大问题。
参数确定性是这个脚本最大的价值。class_map字典建议显式传入而不是在脚本里硬编码,例如{"tank": 0, "apc": 1, "truck": 2},这样后续增删类别时不用改转换逻辑。归一化到 6 位小数足够,再长没有实际意义,反而会让 txt 文件变大。
3.2 坐标越界与空标签:两个第一时间要处理的边界
转换后必须做一次越界清理。卫星图上人工标注的框经常出现贴着图边缘划的情况,计算后cx或cy落在 [0,1] 之外,或者w、h为负值。YOLO 系训练器遇到这些数值轻则 warning,重则 loss 变成 nan。处理方式有两种:直接丢弃越界框;或者把框裁剪到图像范围再重新归一化。我一般选择裁剪,因为边缘目标本身也是有效样本。
def clip_normalized(cx, cy, w, h): # 还原到像素坐标再裁剪,避免归一化坐标裁剪产生误差 x1 = max(0.0, (cx - w / 2)) y1 = max(0.0, (cy - h / 2)) x2 = min(1.0, (cx + w / 2)) y2 = min(1.0, (cy + h / 2)) if x2 - x1 < 0.01 or y2 - y1 < 0.01: return None # 裁剪后目标过小,直接丢弃 new_cx = (x1 + x2) / 2 new_cy = (y1 + y2) / 2 new_w = x2 - x1 new_h = y2 - y1 return new_cx, new_cy, new_w, new_h越界问题的表现很隐蔽:训练时 loss 曲线正常下降,验证集 mAP 却始终上不去。原因是越界框的锚点分配错乱,让正样本匹配到了错误的特征层。另一个高频坑是转换后某些图片没有产生任何标注行,生成空的 txt 文件。空文件本身没问题,表示负样本,但如果对应的图片在训练集里属于正样本却被转换脚本过滤掉了,就造成“标注丢失”。排查方法是转换后按文件大小排序,把所有字节数为 0 的 txt 文件找出来,核对对应图片是否真的没有目标。
4. 训练前必须做的准备:切片策略、数据增强与模型选型
4.1 大图切片:重叠瓦片与标签重映射
战车卫星图2 里的原始图像往往不是直接能拿去训练的尺寸,常见做法是把大图切成瓦片再进入训练流程。切片策略直接影响两个矛盾:切得越小,目标相对尺寸越大,检测越容易,但一张大图产生的瓦片数越多,训练冗余越大;切得越大,目标越小,模型需要更强的下采样特征来感知,难度指数级上升。
常见做法是采用重叠切片,步长小于切片尺寸,保证跨切片的目标至少有一个完整副本。如果切片尺寸是 1024×1024,步长取 800,重叠区域为 224 像素。目标框重新映射到切片坐标系时,有一个容易被忽略的规则:只有目标框与切片的交集面积占原框面积的比例大于阈值时,这个框才被保留并重算坐标;否则直接丢弃,避免一个目标被十几个切片各记录一次,导致训练时正样本重复度过高。
def slice_image_with_boxes(img, boxes, tile_size=1024, stride=800): tiles = [] tile_boxes = [] h, w = img.shape[:2] for y in range(0, h - tile_size + 1, stride): for x in range(0, w - tile_size + 1, stride): tile = img[y:y + tile_size, x:x + tile_size] new_boxes = [] for box in boxes: x1, y1, x2, y2, cls = box inter_x1 = max(x1, x) inter_y1 = max(y1, y) inter_x2 = min(x2, x + tile_size) inter_y2 = min(y2, y + tile_size) if inter_x2 - inter_x1 <= 0 or inter_y2 - inter_y1 <= 0: continue inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1) orig_area = (x2 - x1) * (y2 - y1) if inter_area / orig_area < 0.7: continue # 保留完整度高于 70% 的目标 new_boxes.append((inter_x1 - x, inter_y1 - y, inter_x2 - x, inter_y2 - y, cls)) tiles.append(tile) tile_boxes.append(new_boxes) return tiles, tile_boxes切片代码的逻辑核心是二维循环按步长遍历大图,每次裁出tile_size的块,然后对每个原目标框判断它与切片的交集占比。为什么阈值取 0.7 而不是更高?根据经验,阈值太高会丢掉大量边缘目标,尤其是目标本身只有 30 像素时,只要被切片边界切掉一点,交集占比就会跌破 0.8;阈值太低则会让同一个目标在多个切片中重复出现,模型被迫学习“目标永远完整居中”的错误先验。这一步做完后,建议把切片结果可视化一遍,随机叠加标注框看是否有目标被切成只剩一条边的情况。深色背景下的战车目标被切片边界切断后,肉眼很难察觉,一定要看图确认。
4.2 数据增强配置:哪些有效、哪些和遥感场景冲突
遥感目标检测的数据增强和自然图像有很大差异。随机裁剪、随机缩放这类在 COCO 上常用的增强,在战车卫星图2 这种场景下要谨慎。原因是目标太小,随机缩放可能让本来就只有 30 像素的目标再缩小一半,直接退化成噪点。我常用的增强组合是:
| 增强方式 | 参数建议 | 遥感场景下的理由 |
|---|---|---|
| 随机旋转 90 度/180 度/270 度 | 概率 0.5 | 卫星图没有“正立”概念,旋转不改变语义 |
| 水平/垂直翻转 | 概率 0.5 | 等价于卫星不同过境方向,对检测无害 |
| HSV 微调 | h:0.015, s:0.5, v:0.4 | 模拟不同光照和地表反射差异 |
| 高斯噪声 | 概率 0.2,sigma=5 | 增强对传感器噪声的鲁棒性 |
| 随机遮挡 | 概率 0.1,遮挡边长 16~32 像素 | 模拟云层、阴影遮挡 |
特别注意不要用“随机擦除整块大区域”的强增强。卫星图中目标与环境纹理耦合紧密,大面积擦除会让模型学会“看到深色块就猜是战车”,而不是真正学习目标本身的轮廓特征。另一个值得注意的点是翻转增强会让训练数据的框分布偏向图片中心,如果后续要部署到拼接大图的全图检测场景,推理时仍然要考边缘目标。
4.3 模型选型:为什么小目标场景把框架差距拉得很大
战车卫星图2 上的模型选型,本质是在“精度上限”和“工程效率”之间做取舍。单阶段检测器里,我一般会优先尝试带多尺度检测头的版本,因为小目标需要在高分辨率特征图上检测,模型的 P3 层(步长 8)比 P5 层(步长 32)重要得多。两阶段检测器在遥感小目标上有更高的理论上限,但训练速度慢一个量级,数据增强和调参周期也长,对“先看模型在数据上能不能学出来”这个目标来说性价比低。
选型时的硬指标有两条:第一,框架是否原生支持小目标评估指标,比如 COCO 的AP_small区间统计,没有这个指标就无法量化模型在小目标上的真实表现;第二,框架是否支持滑窗推理的分块预测逻辑,因为遥感图部署时几乎不可能整图一次性推理。如果用蒸馏或半监督方案,还要考虑教师模型在切块上的推理一致性,这块后文会展开。
5. 战车卫星图检测的 5 个高频坑:从标注偏移到小目标漏检
5.1 标注偏移 2 像素,小目标 mAP 直接腰斩
现象:训练集和验证集都是自己人标的,训练时 loss 收敛正常,但验证集 mAP50 只有 0.3 左右,排查了很久找不出原因。后来把预测框和标注框同时画出来,发现很多框有 2 到 3 像素的错位。
原因:卫星图上的人工标注框普遍存在亚像素级误差。目标是 24×24 像素的坦克时,标注偏移 2 像素意味着框的重合度显著下降。IoU 计算对这个小偏移极其敏感,导致正样本预测被判定为错检。
解决:对标注框做小幅度外扩,把框向外扩展 2 像素再训练,相当于给模型一个容错空间。扩展不是无脑做,要在转换脚本里对每个框执行expand_ratio = 0.08的缩放操作,让模型预测的目标范围比实际真值略大,验证时 mAP 会稳定不少。这个操作虽然会让框稍不精确,但在小目标场景里“框偏大常常比框偏小好”。
5.2 切片把目标从中间切断,模型学到半截特征
现象:某个类别(比如自行火炮)在训练集上 mAP 很高,但在验证集上几乎检不出来。仔细检查切片图发现,大量该类别目标在切片时被切掉一半,保留下的框交集占比刚好超过阈值,但视觉上半截车身被切片边缘截断。
原因:切片重叠率不足和阈值设置不合理叠加导致。当步长过大,目标恰好落在两块切片的缝隙附近时,没有一张切片包含完整目标。
解决:增加切片重叠,步长从 800 改为 640,同时把保留阈值从 0.7 下调到 0.5。更稳妥的做法是对切片边缘的目标做二次采样:如果一个目标在切片中靠近边缘且完整度低于 0.85,就把它所在的更大邻域额外裁剪一张。这套逻辑会让训练数据增多约 15%,但效果立竿见影。
5.3 整图推理显存不足,半推半卡
现象:训练完模型后,把验证集大图直接送进模型推理,报显存不足,或者推理速度慢到不可接受。原因是遥感大图分辨率远超常规训练输入尺寸,模型在特征图上产生大量中间张量。
原因:模型输入尺寸固定(比如 640×640),大图直接 resize 后目标缩小到不可见,不 resize 则超过显存上限。本质上是一个“检测精度”和“工程资源”的单选题。
解决:采用滑窗推理,把大图按训练时的切片尺寸分块推理,再把所有预测框映射回大图坐标,最后用 NMS 合并重叠框。由于训练时使用了重叠切片,推理时同一目标会被多个窗口检到,NMS 的 IoU 阈值要放宽到 0.5 而不是常规的 0.45,避免把同一目标的不同视角误删。
5.4 坦克和卡车俯视特征几乎一样,类间混淆严重
现象:目标检测能框出所有地面车辆,但把卡车误判成装甲车,或者把装甲车误判成坦克。混淆矩阵热力图上,颜色集中在坦克和卡车的交叉区域。
原因:卫星俯视图只有顶部轮廓和纹理,坦克与重型卡车的长宽比、颜色、几何外形高度相似。深度学习模型默认学习最显著的外观特征,而这两类目标的外观特征重叠度太高。
解决:训练时引入难例挖掘策略。从训练集里筛出那些和坦克外观最接近的卡车图,把这些图直接复制到训练列表中并增加采样权重。有些框架支持hard_example_mining参数,打开后模型会在线筛选难例。另一种做法是修正类别定义,如果两类在俯视图像上确实不可分,就合并为一个“战车”类,换一个更稳定的标签层次。
5.5 验证集 mAP 虚高,实地新数据一测就翻车
现象:验证集来自同一批次卫星图,模型表现很好,mAP50 超过 0.8。换一批不同季节或不同传感器的卫星图测试,mAP 骤降到 0.35。
原因:数据划分时没有考虑图像来源。同一张大图切出来的训练和验证瓦片,背景纹理、光照条件高度相关,模型学到的部分“捷径特征”恰好在验证集上也存在,造成性能虚高。这个现象在遥感数据里特别严重,因为卫星图的成像条件受传感器、季节、天气影响极大。
解决:划分数据集时要按大图分组,而不是按切片随机划分。把同一张大图的所有切片放进同一集合,保证训练集和验证集来自不同的大图区域。更进一步,如果数据有多批次来源,按批次做留一验证,把模型的泛化能力测到真实水平。我用这个方式重新切分后,验证集 mAP 从 0.78 降到 0.52,但这个数字才是真实可信的。
6. 滑窗推理与小目标 mAP:验证模型真正实力的两个方法
训练结束后,真正决定战车卫星图2 能否落地的不是训练集 mAP,而是推理时对任意尺寸大图的处理效果。我习惯做两件事:第一,验证模型在小目标上的表现,看AP_small而不是整体 mAP;第二,用滑窗推理跑完整张大图,观察边界目标的漏检情况。AP_small一般定义为目标像素面积小于 32×32 的检测结果,恰好贴合卫星图战车目标的真实尺寸。如果模型整体 mAP 高但AP_small低,说明模型只学会了检测大目标,应用时依然无能为力。
滑窗推理的另一个价值是测试时增强。推理时把每块瓦片做水平翻转,得到两组预测框,映射回原图坐标后做加权融合。这个操作在小目标上能提升 1 到 3 个点的 mAP,代价是推理时间翻倍。如果部署环境对延迟不敏感,我一般会保留这个策略,因为它不需要重新训练。
还有一个值得养成的验证习惯:把推理结果可视化时,不要只画验证集里那些很清晰的目标,而是专门挑出位于切片边缘的目标。这些目标往往被漏检,原因不是模型能力不够,而是训练时切片边缘的目标框被过滤太多,模型对“不完整目标”的响应天然偏弱。验证完这三点之后,才敢说这个模型真正可以上手。
有一次我因为在转换脚本里少写了一个类别映射,导致一整个类别的标注被静默过滤,训练出来的模型在验证时对该类目标毫无反应。排查了整整两天,最后是逐行比对 txt 文件才发现的。自那以后,我每次做数据集转换都先把统计脚本跑一遍,对比转换前后的类别数量是否一致。这不是玄学,是实打实的血泪经验。希望帮到你。
本文还有配套的精品资源,点击获取