简介本资源为面向目标检测初学者与算法工程师的飞机单类数据集采用Pascal VOC与YOLO双格式标注可直接用于训练与验证飞机检测模型适合课程设计、算法复现及小样本实验等场景。压缩包共2000个文件以1999个xml标注文件和1个说明txt为主整体约107.37MB图片与标注一一对应省去格式转换的繁琐步骤。数据集包含7931张jpg图片对应7931个VOC格式xml与7931个YOLO格式txt文件标注类别仅airplane一类共23568个矩形框全部由labelImg工具人工绘制标注准确合理。目前已有201人学习下载读者可快速获得一份结构清晰、开箱即用的单类检测数据用于模型训练、精度对比与数据增强实验也可作为自建数据集的标注参考范例。1. 飞机数据集落地7931 张图、23568 个框VOC 与 YOLO 双格式到底怎么用手上有个目标检测项目类别就一个airplane。找了一圈公开数据要么类别杂、要么标注质量参差最后翻到这份飞机数据集——7931 张 jpg配 7931 个 VOC 格式 xml 和 7931 个 YOLO 格式 txt单类别 airplane总框数 23568标注工具是 labelImg规则就是画矩形框。这个量级对单类别检测来说够用了平均每张图约 2.97 个框说明不少图里有多架飞机不是那种一张图一个目标的“玩具集”。它解决的核心问题是你不用自己从零标数据也不用写脚本在 VOC 和 YOLO 之间来回转。压缩包里两套标注同时给齐xml 给需要解析原始标注、做统计分析或转其他格式的人txt 给直接喂 YOLO 系列训练的人。适合谁做遥感飞机检测、机场场面监控、航拍目标识别的从业者以及想拿一个干净单类别集跑通 YOLOv8/v11 训练流程的新手。下面按“先看清结构 → 再动手转换和训练 → 最后避坑”的顺序拆。2. 拆包先看目录VOC 与 YOLO 双格式的文件对应关系2.1 压缩包里到底有什么从项目正文的文件列表能看到xml 命名是xyxr_plane_数字.xml这种形式比如xyxr_plane_7336.xml、xyxr_plane_6759.xml、xyxr_plane_362.xml。数字部分就是图片的唯一标识对应的 jpg 和 txt 应该同名。这是很典型的“一套图、两套标注”组织方式。先别急着训练第一步是把压缩包解开确认三件事jpg 数量、xml 数量、txt 数量是否都是 7931以及三者文件名去掉扩展名能不能一一对上。# 解压后进入目录分别统计三类文件数量 unzip 【目标检测数据集】飞机数据集7930张VOCYOLO格式.zip -d plane_dataset cd plane_dataset echo jpg 数量:; ls *.jpg 2/dev/null | wc -l echo xml 数量:; ls *.xml 2/dev/null | wc -l echo txt 数量:; ls *.txt 2/dev/null | wc -l # 找出只有图片没有标注的“孤儿文件” for f in *.jpg; do base${f%.jpg} [ -f $base.xml ] || echo 缺 xml: $base [ -f $base.txt ] || echo 缺 txt: $base done逻辑说明ls | wc -l是最快的计数方式比打开文件管理器数靠谱。第三个循环是血泪经验——数据集号称 7931 对实际交付时经常有个别图片漏标或标注文件丢失训练前不查训练时 dataloader 报错或者静默跳过你都不知道少训了多少张。参数上没什么可调的就是把*.jpg换成你实际的扩展名有些集是.jpeg或.png。2.2 VOC xml 里存了什么VOC 格式的 xml 是“人类可读”的标注打开一个看结构annotation folderplane/folder filenamexyxr_plane_7336.jpg/filename size width1024/width height768/height depth3/depth /size object nameairplane/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin212/xmin ymin98/ymin xmax540/xmax ymax330/ymax /bndbox /object /annotation关键字段size里的宽高是原图尺寸bndbox是左上角和右下角的绝对像素坐标。name全是airplane单类别所以不用做类别映射表。truncated和difficult这两个标志位很多转换脚本会忽略但如果你后面要做困难样本挖掘或者评估时排除截断目标就得把它们读出来。这份集标注规则是“对类别画矩形框”没有多边形、没有分割路径所以 xml 里不会出现segmentation节点转换时也不用处理掩码。2.3 YOLO txt 的归一化坐标YOLO 格式的 txt 每行一个目标格式是class_id x_center y_center width height全部归一化到 0~1。单类别时 class_id 恒为 0。拿上面那个框举例假设图是 1024×7680 0.3672 0.2786 0.3203 0.3021计算方式x_center (212540)/2/1024 ≈ 0.3672y_center (98330)/2/768 ≈ 0.2786width (540-212)/1024 ≈ 0.3203height (330-98)/768 ≈ 0.3021。这份集已经帮你转好了 txt所以正常情况直接用就行。但如果你要自己校验或重转就得注意归一化用的是每张图各自的宽高不是统一尺寸——这是新手最容易翻车的地方后面避坑章会细说。3. 从 VOC 到 YOLO转换脚本、目录划分与 data.yaml 配置3.1 自己写一遍转换脚本校验用虽然集里给了 txt但强烈建议自己写一遍转换脚本做交叉校验确认给的 txt 和 xml 一致。常见做法是用xml.etree.ElementTree解析逐框算归一化坐标import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, img_w, img_h, class_map): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_map: continue bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 归一化注意用每张图自己的宽高 xc (xmin xmax) / 2.0 / img_w yc (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 裁剪到 [0,1]防止标注越界 xc, yc min(max(xc, 0), 1), min(max(yc, 0), 1) w, h min(max(w, 0), 1), min(max(h, 0), 1) lines.append(f{class_map[name]} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) return lines class_map {airplane: 0}逻辑说明class_map把类别名映射成 id单类别就是{airplane: 0}。归一化前先取size里的宽高不要用固定值。最后那两行裁剪是后悔药——有些标注框会超出图片边界xmax 大于 width不裁的话 YOLO 训练时可能报坐标越界或产生异常梯度。参数上:.6f保留六位小数足够YOLO 官方也是这个精度。3.2 划分 train/val 并生成 data.yamlYOLO 训练需要按images/train、images/val、labels/train、labels/val组织。7931 张按 8:2 分验证集约 1586 张import os, random, shutil random.seed(42) # 固定种子保证可复现 src_img, src_lbl plane_dataset, plane_dataset dst plane_yolo for sub in [images/train,images/val,labels/train,labels/val]: os.makedirs(os.path.join(dst, sub), exist_okTrue) files [f[:-4] for f in os.listdir(src_img) if f.endswith(.jpg)] random.shuffle(files) split int(len(files) * 0.8) for i, base in enumerate(files): phase train if i split else val shutil.copy(f{src_img}/{base}.jpg, f{dst}/images/{phase}/{base}.jpg) shutil.copy(f{src_lbl}/{base}.txt, f{dst}/labels/{phase}/{base}.txt)逻辑说明random.seed(42)是必须的否则每次划分不同实验没法对比。split取 80% 做训练。复制而不是移动保留原始文件方便后面重新划分。对应的data.yamlpath: ./plane_yolo train: images/train val: images/val nc: 1 names: 0: airplanenc是类别数单类别填 1names用字典或列表都行但 id 必须从 0 开始且和 txt 里的 class_id 对上。这份集只有 airplane 一类所以nc: 1别写成 0。3.3 起训命令与关键参数yolo detect train \ dataplane_yolo/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ workers4 \ projectruns/plane \ nameexp1逻辑说明model用预训练权重起步单类别小数据从零训容易不收敛。imgsz640是通用起点如果原图分辨率普遍在 1024 以上且飞机目标偏小可以提到 960 或 1280但显存和速度要权衡。batch按显存调16 是 8G 显存的稳妥值。workers是 dataloader 进程数Windows 下设太高容易卡死4 比较稳。训练完看runs/plane/exp1下的results.csv和confusion_matrix.png单类别重点看 mAP50 和召回别只盯 loss。4. 避坑与排查标注越界、类别错位、划分泄漏这几件事4.1 现象训练报 “normalized coordinates outside [0,1]”原因xml 里存在 xmax 大于图片宽度、或 ymax 大于高度的框转换时没裁剪归一化后超过 1。解决在转换脚本里对 xc/yc/w/h 做min(max(v,0),1)裁剪或者直接过滤掉越界比例过大的框比如宽高超过 1.5 的。我一般会先跑一遍统计把越界框数量打出来超过几十个就说明标注源头有问题得回看原图。4.2 现象训练不报错但 mAP 一直很低原因类别 id 错位。比如 data.yaml 里names: {1: airplane}但 txt 里写的是 0模型学的是“背景”。解决确认nc和names的 id 从 0 开始且和 txt 第一列完全一致。单类别集里这个错特别隐蔽因为只有一个类错了也不报错就是学不动。4.3 现象验证集指标虚高实际推理一塌糊涂原因划分时同一张图的不同副本或高度相似帧被分到了 train 和 val造成数据泄漏。解决如果图片来自视频抽帧按视频段划分而不是随机按图划分随机划分时至少固定种子并检查 train/val 里有没有文件名高度相似的。这份集是独立图片随机划分问题不大但如果你自己扩充了数据这条要盯紧。4.4 现象dataloader 报找不到 label 文件原因图片和 txt 文件名大小写不一致或者扩展名不统一.JPGvs.jpg。解决用 2.1 的孤儿文件检查脚本先扫一遍统一重命名。Linux 下大小写敏感Windows 下不敏感跨平台迁移时这个坑必踩。4.5 现象显存够但训练速度异常慢原因workers设太大导致进程争抢或者图片分辨率远大于imgsz且没开缓存。解决先把workers降到 2~4 试再考虑cacheram内存够的话或cachedisk。另外确认图片不是超大尺寸必要时预处理缩放到接近imgsz再存一份。5. 进阶技巧用 23568 个框做分布分析反推标注质量与训练策略拿到一个数据集别急着开训先花十分钟做框分布分析能提前发现很多问题。这份集总框数 23568图 7931 张平均 2.97 框/图但平均值会骗人——得看分布。下面这段脚本统计每张图的框数、框的宽高比和面积占比import os, glob import numpy as np box_per_img, ratios, areas [], [], [] for txt in glob.glob(plane_dataset/*.txt): with open(txt) as f: lines [l for l in f if l.strip()] box_per_img.append(len(lines)) for l in lines: _, xc, yc, w, h map(float, l.split()) ratios.append(w / h if h 0 else 0) areas.append(w * h) box_per_img np.array(box_per_img) ratios np.array(ratios) areas np.array(areas) print(f总框数: {len(ratios)}) print(f每图框数: min{box_per_img.min()} max{box_per_img.max()} fmean{box_per_img.mean():.2f} 中位数{np.median(box_per_img)}) print(f宽高比: 中位数{np.median(ratios):.2f} f5%{np.percentile(ratios,5):.2f} 95%{np.percentile(ratios,95):.2f}) print(f面积占比: 中位数{np.median(areas):.4f} f小于0.01的比例{np.mean(areas0.01):.2%})逻辑说明box_per_img看每图目标数如果中位数远小于均值说明少数图目标极多、多数图目标很少训练时 batch 内正样本不均衡。ratios看飞机框的宽高比飞机在不同视角下比例差异大如果 5% 和 95% 分位跨度很宽说明视角多样anchor 或模型要能覆盖。areas看小目标占比areas0.01就是面积不到图 1% 的框如果比例超过 30%imgsz640可能不够得往上提。我一般会拿这几个数做决策小目标多就提分辨率或加 P2 检测层每图框数中位数低就适当增大 batch 里的图数保证正样本量宽高比跨度大就确认用的是 anchor-free 头YOLOv8 之后默认是。这套分析跑下来比盲目调参有用得多。从那以后我每次拿到新数据集都强制先跑一遍数量核对、孤儿文件检查和框分布统计再动训练脚本。这三步花不了十五分钟但能省掉后面几小时的玄学排查。希望帮到你。本文还有配套的精品资源点击获取