☰
坦克检测数据集实战:1521张图单类标注,YOLO训练避坑指南
2026/10/5 10:48:36 网站建设 项目流程

简介:这是一份面向目标检测初学者与算法工程师的坦克检测数据集,采用Pascal VOC与YOLO双格式标注,可直接用于YOLO系列模型的训练与验证,适合军事目标识别、遥感图像分析等场景的入门实践与算法调优。压缩包共2000个文件,包含1521张jpg图片、1521个VOC格式xml标注文件及479个YOLO格式txt标注文件,整体约103.15MB,xml与txt分别对应两种主流框架的读取需求,无需额外转换即可投入训练。数据集仅含tank一个类别,共标注2220个矩形框,使用labelImg人工绘制,标注准确度较高。目前已有655人学习下载,可作为单类别检测任务的轻量级训练样本,帮助读者快速验证模型结构、调试数据增强与评估指标,节省自行采集与标注的时间成本。

1. 坦克检测数据集:1521 张图、2220 个框,单类标注能不能直接开训

如果你正在做目标检测数据集的选型,尤其是想找一个单类别、标注干净、拿来就能跑 YOLO 的小型数据集验证训练链路,这份坦克检测数据集值得先看一眼。它的核心参数很直白:1521 张 jpg 图片,对应 1521 个 VOC 格式 xml 和 1521 个 YOLO 格式 txt,标注类别只有一类tank,总框数 2220,标注工具是 labelImg,标注方式是矩形框。也就是说,平均每张图约 1.46 个目标,属于典型的小目标数量、单类别场景。

它解决的不是“我要训一个通用检测器”的问题,而是“我想快速验证数据管线、损失函数收敛、anchor 匹配是否正常”的问题。适合两类人:一是刚入门 YOLO、想拿真实标注数据跑通训练和推理的新手;二是手里有坦克/装甲类检测需求,想先拿一份标注质量尚可的数据做 baseline 的从业者。需要提前说清楚:这是个人标注数据,准确度高是相对而言,不代表零漏标零误标,后面会讲怎么自查。

2. 拆包先看目录:VOC 与 YOLO 双格式到底怎么对应

2.1 文件结构与命名规则

拿到压缩包后,第一件事不是急着写训练脚本,而是把目录结构摸清楚。从项目正文给出的文件列表看,根目录下有说明.txt和一批tank_xyxr_*.txt,这些 txt 是 YOLO 格式的标注文件,命名规则是tank_xyxr_序号.txt。对应的 jpg 图片和 VOC 格式 xml 应该与这些 txt 同名,只是扩展名不同。常见做法是解压后得到三个平行目录:JPEGImages放 jpg,Annotations放 xml,labels放 txt。如果压缩包里没有分目录,而是全部平铺,那就需要自己按扩展名归类。

先执行一遍清点,确认数量对得上:

# 统计三类文件数量,确认是否都是 1521 find . -name "*.jpg" | wc -l find . -name "*.xml" | wc -l find . -name "*.txt" | wc -l # 检查是否有图片没有对应标注,或标注没有对应图片 for f in *.jpg; do base="${f%.jpg}" [ -f "${base}.xml" ] || echo "缺 xml: $base" [ -f "${base}.txt" ] || echo "缺 txt: $base" done

这段脚本的逻辑很直接:先按扩展名计数,三个数字都应该是 1521;再做交叉检查,任何一张图缺 xml 或 txt 都会被打印出来。参数上唯一要注意的是工作目录,必须在文件所在目录执行,否则for f in *.jpg匹配不到。如果输出一堆“缺 xml”,说明压缩包内部分目录,需要先find . -name "*.jpg" -exec mv {} ./JPEGImages/ \;这类操作归拢,但归拢前先备份,避免同名覆盖。

2.2 VOC xml 与 YOLO txt 的字段映射

VOC 的 xml 里,关键字段是filename、size下的width/height/depth,以及每个object下的name和bndbox的xmin/ymin/xmax/ymax。YOLO 的 txt 每行是class_id x_center y_center width height,全部归一化到 0~1。这份数据类别只有tank一类,所以 class_id 恒为 0。转换关系是:

x_center = (xmin + xmax) / 2 / img_width y_center = (ymin + ymax) / 2 / img_height width = (xmax - xmin) / img_width height = (ymax - ymin) / img_height

常见坑是 xml 里的size字段和实际图片尺寸不一致。labelImg 正常保存时不会出这个问题,但个人标注数据里偶尔会有图片被替换而 xml 没更新。验证方法是用 PIL 读一遍图片尺寸,和 xml 里的size对比:

import os import xml.etree.ElementTree as ET from PIL import Image def check_size(ann_dir, img_dir): mismatches = [] for xml_file in os.listdir(ann_dir): if not xml_file.endswith('.xml'): continue tree = ET.parse(os.path.join(ann_dir, xml_file)) root = tree.getroot() w = int(root.find('size/width').text) h = int(root.find('size/height').text) img_name = root.find('filename').text img_path = os.path.join(img_dir, img_name) if not os.path.exists(img_path): mismatches.append((xml_file, '图片不存在')) continue iw, ih = Image.open(img_path).size if (w, h) != (iw, ih): mismatches.append((xml_file, f'xml={w}x{h}, img={iw}x{ih}')) return mismatches for item in check_size('Annotations', 'JPEGImages'): print(item)

逻辑说明:遍历所有 xml,读出标注尺寸和实际图片尺寸,不一致就记录。参数上ann_dir和img_dir按实际目录名改。如果输出为空,说明尺寸一致,可以放心用 YOLO txt;如果有不一致,优先以实际图片尺寸为准重新生成 txt,而不是改 xml,因为训练时读的是图片。

3. 从 VOC 到 YOLO:转换脚本、划分训练集与 data.yaml

3.1 自己写一遍转换脚本,别直接信现成 txt

虽然压缩包里已经带了 YOLO 格式 txt,但我一般会自己再转一遍,目的是验证已有 txt 和 xml 是否一致。如果直接拿现成 txt 训练,万一某几行坐标越界或类别写错,loss 会异常但不报错,排查起来很费时间。下面这个脚本把 VOC 转成 YOLO,并顺带做坐标合法性检查:

import os import xml.etree.ElementTree as ET CLASSES = ['tank'] def voc_to_yolo(xml_path, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.iter('object'): cls_name = obj.find('name').text.strip() if cls_name not in CLASSES: continue cls_id = CLASSES.index(cls_name) 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) # 裁剪到图片范围内,防止越界 xmin = max(0, min(xmin, img_w)) xmax = max(0, min(xmax, img_w)) ymin = max(0, min(ymin, img_h)) ymax = max(0, min(ymax, img_h)) if xmax <= xmin or ymax <= ymin: continue xc = (xmin + xmax) / 2 / img_w yc = (ymin + ymax) / 2 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") return lines

逻辑说明:CLASSES只放tank,所以 class_id 恒为 0;坐标先裁剪再判断有效性,避免负宽高;输出保留 6 位小数,和 YOLO 官方格式一致。参数上img_w/img_h必须从实际图片读取,不能从 xml 的size读,原因上一节讲过。转换完可以和自己手里的 txt 做逐行对比,差异超过阈值的样本单独看一眼。

3.2 划分训练集、验证集与 data.yaml

1521 张图不算多,按 8:1:1 划分,训练集约 1216 张,验证集和测试集各约 152 张。划分时要注意同一场景的连续帧不要跨集,否则验证集精度会虚高。常见做法是按文件名排序后间隔采样,而不是随机打乱:

import os import random random.seed(42) imgs = sorted([f for f in os.listdir('JPEGImages') if f.endswith('.jpg')]) random.shuffle(imgs) n = len(imgs) train = imgs[:int(n*0.8)] val = imgs[int(n*0.8):int(n*0.9)] test = imgs[int(n*0.9):] for split, items in [('train', train), ('val', val), ('test', test)]: with open(f'{split}.txt', 'w') as f: for name in items: f.write(f'./images/{name}\n')

逻辑说明:固定随机种子保证可复现;输出的是图片路径列表,YOLO 训练时通过替换扩展名找 labels。参数上0.8/0.9是划分比例,数据量小的时候可以改成 7:2:1,验证集大一点更能反映真实水平。data.yaml写法如下:

path: ./tank_dataset train: train.txt val: val.txt test: test.txt nc: 1 names: ['tank']

nc是类别数,这里必须写 1;names顺序要和转换脚本里的CLASSES一致,否则类别索引错位,训练能跑但预测全错。

4. 训练参数怎么设:单类别小数据集的避坑配置

4.1 模型选型与输入尺寸

单类别、2200 多个框,不建议一上来就上大模型。常见做法是从 YOLOv8n 或 YOLOv5s 起步,参数量小、收敛快,单卡 8G 显存就能跑。输入尺寸设 640 是默认值,但如果你的坦克目标在图中占比很小,比如远距离拍摄,可以提到 960 或 1280,代价是显存和训练时间上升。判断依据是统计一下 YOLO txt 里 width 和 height 的分布:

import os import numpy as np ws, hs = [], [] for f in os.listdir('labels'): if not f.endswith('.txt'): continue for line in open(os.path.join('labels', f)): parts = line.strip().split() if len(parts) == 5: ws.append(float(parts[3])) hs.append(float(parts[4])) print('width 中位数:', np.median(ws), '90分位:', np.percentile(ws, 90)) print('height 中位数:', np.median(hs), '90分位:', np.percentile(hs, 90))

如果中位数小于 0.05,说明目标偏小,640 下可能只剩几十像素,建议提高输入尺寸或改用带 P2 小目标层的模型。参数上np.percentile(ws, 90)看的是 90% 的框有多小,比平均值更能反映长尾。

4.2 训练命令与关键超参

以 YOLOv8 为例,训练命令如下:

yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ warmup_epochs=3 \ patience=20 \ cache=True

逻辑说明:epochs=100配合patience=20,验证集 20 轮不提升就早停,避免过拟合;lr0=0.01是初始学习率,小数据集可以降到 0.005 更稳;cache=True把图片缓存到内存,1521 张图完全放得下,能明显加快训练。参数上batch=16是 8G 显存的保守值,显存够可以加到 32。如果 loss 在前几轮就 nan,优先检查 txt 里有没有坐标大于 1 或负值,而不是调学习率。

4.3 训练过程看什么指标

单类别检测,重点看三个:box_loss是否稳定下降、mAP50是否在 30 轮后进入平台、precision和recall是否严重失衡。如果 recall 远低于 precision,说明漏检多,可能是目标太小或 anchor 不匹配;如果 precision 低,说明误检多,可能是背景类样本不足。这份数据只有 tank 一类,误检通常来自类似坦克的车辆或建筑,可以在验证集里把误检样本挑出来看。

5. 避坑与排查:个人标注数据最容易翻车的五件事

5.1 现象:训练 loss 正常但 mAP 始终为 0

原因:data.yaml里names顺序和 txt 里的 class_id 对不上,或者 txt 里 class_id 不是 0。这份数据只有一类,正常情况 class_id 全是 0,但如果转换脚本里CLASSES写成了别的顺序,就会错位。解决:用awk '{print $1}' labels/*.txt | sort -u看唯一类别值,应该只有 0;再核对data.yaml的names长度等于nc。

5.2 现象:部分图片训练时报“坐标越界”

原因:xml 里的 bbox 超出图片边界,或者图片尺寸和 xml 不一致。个人标注时手滑把框拉到图外很常见。解决:在转换脚本里加裁剪逻辑,如 3.1 节所示;同时跑一遍 2.2 节的尺寸检查,把不一致的样本单独处理。

5.3 现象:验证集精度很高,但实际推理漏检严重

原因:训练集和验证集划分时,同一场景的连续帧被分到了两边,导致验证集和训练集高度相似。解决:按文件名或时间戳分组划分,而不是逐张随机。如果文件名里有序号,可以按序号区间切分,保证同一段视频的帧只出现在一个集合里。

5.4 现象:训练到一半显存爆了

原因:cache=True把图片全部读进内存,如果图片分辨率很高,内存先爆;或者batch设太大。解决:先看图片平均尺寸,超过 1920 宽就把cache关掉,改用cache='disk';batch从 8 开始试,逐步加。

5.5 现象:推理时框的位置整体偏移

原因:训练时用了 letterbox 填充,推理时没有做同样的预处理,或者输入尺寸和训练不一致。解决:推理时保持imgsz和训练一致,用官方predict接口会自动处理 letterbox;如果自己写前处理,记得把填充后的坐标映射回原图。

6. 进阶验证:用混淆矩阵和单图推理确认数据可用性

训练完之后,别只看 mAP 数字。我一般会做两件事:一是跑混淆矩阵,二是挑几张有代表性的图做单图推理,肉眼确认框的位置和置信度。混淆矩阵在 YOLO 训练结束时会自动生成,单类别情况下主要看背景被误判为 tank 的比例。如果背景误检高,说明负样本不够,可以从验证集里挑一些没有坦克的图加进训练集,作为背景样本。

单图推理命令:

yolo detect predict \ model=runs/detect/train/weights/best.pt \ source=test_images/ \ conf=0.25 \ iou=0.5 \ save=True

逻辑说明:conf=0.25是置信度阈值,单类别场景可以降到 0.2 看召回;iou=0.5是 NMS 的 IoU 阈值,如果坦克密集排列,可以降到 0.4 减少框合并。参数上source可以指向单张图或目录,输出默认在runs/detect/predict。

还有一个容易被忽略的验证点:把 YOLO txt 反算回 VOC 坐标,和原始 xml 对比,看最大偏差。如果偏差超过 1 像素,说明转换或取整有问题。这个检查能兜住大部分标注格式的玄学问题。从那以后我每次拿到新数据集,都强制走一遍“计数→尺寸检查→类别检查→反算对比”这四步,再开始训练。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询