资讯动态

风筝检测数据集全解析:VOC/YOLO双格式与YOLOv8训练避坑指南

发布时间:2026/9/23 6:11:10 来源:尧图企业网站定制
简介面向计算机视觉目标检测入门、风筝识别相关项目及需要现成标准标注数据的算法实验这份数据集提供Pascal VOC与YOLO两种主流标注格式可直接用于训练检测模型或进行格式迁移测试。压缩包共2000个文件主要文件类型为1998个xml标注文件另附2个txt说明文件含使用前必读、版权声明整体大小约268MB。数据集描述显示共2260张图片标注类别仅kite一种矩形框总数达8790个标注规则为统一矩形框标记目标样本量与框密度对微调、轻量级模型对比及数据增强实验较为友好。包内文件按统一前缀命名便于脚本批量处理与训练集划分随包说明文件可帮助快速确认数据规范与版权边界。目前已有75人学习下载适合需要现成风筝检测数据开展实验的算法学习者与项目开发者。1. 风筝检测数据集值不值得下先弄清 2260 张、8790 框是什么做无人机巡检误报排查时我发现一个反直觉的现象通用目标检测模型在风筝识别上普遍翻车。风筝是软体目标受风形变大俯瞰视角里轮廓常与背景黏在一起要么漏检要么置信度太低没法用。因此一份格式统一的风筝检测数据集就很有价值。这份数据共 2260 张 JPEG 原图配套 2260 个 Pascal VOC 格式 xml 和 2260 个 YOLO 格式 txt 标注只有一个 kite 类别标注框总数 8790全部经 labelImg 画矩形框生成。图片和标注一一对应免掉了自己抽帧、打标、导出的整个前期过程。下载前要明确边界它只保证标注文件与图片对应完整不保证任何训练后的模型精度。划分、调参、评估都在你自己手上。后面章节我按解压检查、标签格式拆解、训练复现、避坑排查到进阶验证的顺序把这份数据完整跑通一遍。2. VOC 与 YOLO 双格式标注xml 与 txt 字段逐段拆解2.1 xml 里到底存了什么关键字段与两个常被忽略的开关先别急着训练把压缩包解压后随便挑一个 xml 文件打开。文件名大致是firc_kite_278.xml这种结构前缀firc对应仓库 firc-dataset数字是图片编号。Pascal VOC 格式的特征就是一个标注文件对应一张图后缀和 jpg 完全同名所有检测信息都写在 xml 节点里不依赖额外索引。一个常见的 xml 内容如下节点顺序可能因工具版本有差异但关键节点一定存在annotation folderfirc_kite/folder filenamefirc_kite_278.jpg/filename path/data/firc-dataset/firc_kite/firc_kite_278.jpg/path source databasefirc-dataset/database /source size width1280/width height720/height depth3/depth /size segmented0/segmented object namekite/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin418.55/xmin ymin202.13/ymin xmax883.10/xmax ymax511.63/ymax /bndbox /object /annotationfolder 是图片所在文件夹名filename 是图片文件名path 是标注机器上的绝对路径size 里三个字段是原图宽、高、通道数。真正的检测语义集中在 object 节点name 表示类别名bndbox 四元组是左上角和右下角的像素坐标。这份数据里 name 只可能是 kite因为整份数据只有这一个类别。我留意到一个细节bndbox 坐标保留到两位小数不是整数。浮点框对边缘回归更友好目标检测框架在计算回归损失时能拟合出更精细的边界这也侧面说明这批数据不是脚本批量生成的而是有人用 labelImg 手工逐步标完的。这里有两个常被忽略的字段truncated 和 difficult。truncated1 表示目标在图像边缘被截断difficult1 表示目标难以辨认。如果你训练时遇到“漏检集中在图像边缘区域”的问题回来检查这两个字段大概率全是默认 0说明标注时没有区分困难样本。模型对这类遮挡目标天然不敏感需要额外补数据或做难例挖掘仅靠这份原始标注解决不了。2.2 yolo 的 txt 里只有五列归一化坐标怎么还原成像素打开与上面同名的 txt 文件这是 YOLO 训练时真正被加载的标注格式每一行代表一个目标0 0.50842 0.49571 0.36301 0.42986 0 0.12476 0.60207 0.18945 0.34422第一列是类别的数字编号。因为整份数据只有 kite 一类所以所有行的第一个数都是 0。后面四个浮点数依次是框中心点的 x 归一化坐标、y 归一化坐标、框宽 w 归一化值、框高 h 归一化值。所谓归一化就是把实际像素除以图像宽度 W 或高度 H。拿第一行的 0.36301 举例0.36301 表示框宽占图像总宽度的 36.301%乘回宽度 1280 后得到实际约 464 像素。同时中心点 x 为 0.50842乘 1280 得到约 651 像素配合高度方向的 0.49571 乘 720 得到约 357 像素就能算出框左上角和右下角的精确像素坐标。下面这段脚本可以把 txt 里的框还原到原图上做可视化用来快速检查标注质量import cv2 img cv2.imread(firc_kite_278.jpg) h, w img.shape[:2] with open(firc_kite_278.txt, r, encodingutf-8) as f: lines [line.strip() for line in f if line.strip()] for line in lines: parts list(map(float, line.split())) cls_id, cx, cy, bw, bh int(parts[0]), parts[1], parts[2], parts[3], parts[4] x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, fkite_{cls_id}, (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(check_boxes.jpg, img) print(f图像 {w}x{h}检测到 {len(lines)} 个框)这个脚本把归一化坐标按原图宽高还原成像素再用 OpenCV 画出矩形框。跑完后打开 check_boxes.jpg重点看两类异常一类是框明显超出图像边界说明标注时的坐标和输出图片尺寸没对上另一类是多个框重叠但内容不是同一只风筝常见于密集编队场景下人眼漏标。建议一次随机抽 50 张图做人工复核比直接训练再排错效率高得多。2.3 双格式不是白送的VOC 和 YOLO 互相转换的三个易错点文件清单里同时给了 xml 和 txt相当于免去了 VOC 坐标换算成 YOLO 归一化坐标的脚本工作。但如果后续要把这份数据合并进其他项目或者做自定义增强仍然会碰到格式转换。VOC 到 YOLO 的数学关系很直接cx (xmin xmax) / 2 / W cy (ymin ymax) / 2 / H bw (xmax - xmin) / W bh (ymax - ymin) / H反过来 YOLO 到 VOC则是把四个浮点数乘回原图宽高再还原出 xmin、ymin、xmax、ymax。换算本身很简单实际使用中我踩过三个不大不小的坑。第一个坑是类别编号对齐。YOLO txt 里只存数字0 对应哪个类别由训练配置里的 names 决定。单独跑这份数据names 写0: kite就行不会出问题。但网上的通用转换脚本有些默认按字母序给类别编号加第二个类别时就会错位。第二个坑是 xml 里多个 object 的遍历。有相当一部分图片中有多只风筝txt 里有多行。转换脚本如果用 find 而不是 findall会丢掉同一张图后面的目标最后损失的是召回率。第三个坑是坐标取整。很多转换脚本习惯用 int() 截断小数导致框的边界坐标丢失像素。0.5 像素的误差人眼分辨不出但对小目标在特征金字塔上聚合特征有影响。我一般统一用 round() 保留到像素不用 int() 强转。这份数据在格式层面的价值可以浓缩成一句话xml 便于人眼审查txt 便于框架直接加载两者并存意味着两种格式可以互相验证不用依赖一个不可见的黑匣子。3. 标注复盘labelImg 参数、8790 框的规则与标签自检脚本3.1 labelImg 安装与模式切换VOC 和 YOLO 不能同时保存数据集的说明里明确标注工具是 labelImg基于 PyQt5 的图形化标注软件。它同时支持 VOC 和 YOLO 两种输出但界面上的切换需要手动完成不会同时生成两份文件。安装方式有两种。# 方式一pip 直接安装适合快速体验 pip install labelImg # 方式二源码运行适合改类别表或二次开发 git clone https://github.com/HumanSignal/labelImg.git cd labelImg pyrcc5 -o libs/resources.py resources.qrc python labelImg.pypip 版本安装后直接在终端输入labelImg启动。源码方式从 GitHub 拉下来后需要先用 pyrcc5 把 resources.qrc 编译成资源文件这个步骤容易出问题如果报找不到 pyrcc5先pip install pyqt5-tools补上开发工具。打开软件后建议先设置保存目录。点击左侧 “Change Save Dir” 选择输出文件夹然后用快捷键 W 画框CtrlS 保存A 和 D 切换上一张下一张。要注意的是labelImg 默认输出 VOC 格式 xml按 Y 键能在 xml 和 yolo txt 之间切换。如果用默认设置标注完再切换已标注的文件不会自动转格式只有切换后新保存的才对。3.2 单类别标注规则没有想象中简单矩形框怎么贴合变形的风筝8790 个框平均到 2260 张图每张约 3.9 个框说明多目标图占比不低。kite 在画面里往往是斜着飞的矩形框不带旋转角唯一的自由度就是松紧。框紧了会把飘带或翼尖切掉框松了会带进大量天空背景模型在特征提取阶段就分不清哪些像素属于风筝。结合这组数据的命名和标注数量我复盘时会把框的下边界收在线绳与风筝连接处上边界贴近风筝顶端左右边界靠近翼尖。线绳是不能进框的线绳在图像里通常是一条细线圈进去后模型会把线绳附近的边缘误当成风筝特征推理时对细长线状目标的响应会异常高。同样道理人物手拿风筝的场景手部如果在线绳旁边也要严格排除在框外。另一个常见误区是重叠目标。多只风筝叠飞时人眼容易只看到前面一只后一只漏掉或者干脆把两只画进同一个框。模型会把两个重叠目标学成单个目标推理时遇到类似叠飞场景只能输出一个低置信度框。多目标场景下宁可多画也不能并框只要两只风筝主体在画面中都可见就必须分别画框。3.3 训练前必跑的自检脚本数量、空标注、坐标边界一次查完拿到数据先别急着进训练用十几行脚本做全量自检。训练中后期 loss 降不下去再回头找数据问题就太被动提前把明显错误挡在门外才是工程上最省时间的做法。import xml.etree.ElementTree as ET import glob import os xmls sorted(glob.glob(Annotations/*.xml)) total_boxes 0 empty_files [] coord_errors [] image_sizes set() class_counts {} for xml_path in xmls: tree ET.parse(xml_path) root tree.getroot() objs root.findall(object) if not objs: empty_files.append(os.path.basename(xml_path)) continue total_boxes len(objs) size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) image_sizes.add((img_w, img_h)) for obj in objs: name obj.find(name).text class_counts[name] class_counts.get(name, 0) 1 bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) if x2 x1 or y2 y1 or x2 img_w or y2 img_h: coord_errors.append(os.path.basename(xml_path)) print(fxml 文件数: {len(xmls)}) print(f总框数: {total_boxes}) print(f空标注文件: {empty_files}) print(f坐标异常文件: {coord_errors}) print(f图像尺寸集合: {image_sizes}) print(f类别分布: {class_counts})如果数据完整这个脚本输出应该满足几条硬指标xml 总数 2260总框数 8790空标注列表和坐标异常列表为空。图像尺寸集合如果只有一个分辨率说明素材来源统一如果出现 1920x1080 和 1280x720 混用训练时 yaml 里 imgsz 的选值就要往小分辨率靠。另一个值得做的检查是框的宽高比分布。风筝在空中的长宽比相对固定绝大多数框的宽高比应该在 0.5 到 2.0 之间。如果出现大量宽高比超过 5 的框先怀疑是否把电线杆或者细长建筑误标成了类。这个检查我习惯用 numpy 排序后直接看宽高比两端极值比肉眼逐一翻图高效。4. 用这份数据训练 YOLOv8目录重建、yaml 配置与显存匹配策略4.1 目录结构重建扁平目录怎么划分成 train/val下载解压后是一整层平铺文件jpg、xml、txt 全部混在一起。YOLOv8 不认这种结构它期望 images 目录下按 train 和 val 分文件夹labels 目录下用同样结构组织。第一步就是重建目录。mkdir -p firc-dataset/images/train firc-dataset/images/val mkdir -p firc-dataset/labels/train firc-dataset/labels/val划分比例建议 9:1约 2034 张训练图、226 张验证图。这对 2260 张的数量级比较稳妥验证集不会因为太小导致指标忽高忽低。import random import os from shutil import copy2 random.seed(42) imgs [f for f in os.listdir(.) if f.endswith(.jpg)] random.shuffle(imgs) split int(len(imgs) * 0.9) for i, img in enumerate(imgs): name os.path.splitext(img)[0] tag train if i split else val try: copy2(img, ffirc-dataset/images/{tag}/{img}) copy2(f{name}.txt, ffirc-dataset/labels/{tag}/{name}.txt) except FileNotFoundError: print(f文件缺失: {name}) print(ftrain {split} 张, val {len(imgs) - split} 张)脚本做了两件事按固定随机种子洗牌保证每次划分结果可复现复制时同时带上 jpg 和同名 txt。如果遇到缺 txt 的图会输出文件名那这就是不可继续训练的信号。xml 没有复制到新结构里因为 YOLOv8 训练链路完全不读 xml只读 txt。xml 单独留档一份放 Annotations 目录就行。4.2 firc.yaml 与训练命令显卡只有 8G 怎么选型号YOLOv8 训练入口是 ultralytics 框架先安装依赖。pip install ultralytics在项目根目录写一个 firc.yamlpath: /data/firc-dataset train: images/train val: images/val nc: 1 names: 0: kitepath 是数据集根目录的绝对路径train 和 val 是相对 path 的图片目录。标签文件会由框架自动匹配 labels 前缀不需要在 yaml 里显式声明。nc 为类别数 1names 映射表必须和 txt 第一列保持一致kite 对应 0。这里一旦写错训练不会直接报错只是模型学到的东西没有任何意义。接着启动训练yolo detect train \ modelyolov8s.pt \ datafirc.yaml \ imgsz640 \ epochs100 \ batch-1 \ lr00.01 \ patience15 \ projectruns/detect \ namekite_s \ seed42batch-1 让框架根据显存自动匹配最大 batch。8G 显存下用 s 规格模型大约能跑到 batch 16。显存只有 6G就把 model 换成 yolov8n.pt精度略微下降但训练速度提升明显。epochs100 配合 patience15 早停大多数情况下到 60 到 80 轮就收敛了不用硬等 100 轮。imgsz640 是速度与精度的平衡点。这份数据里多数风筝框占全图比例不小属于中大型目标640 完全够用。如果你审视数据后发现大量框面积小于 32 乘 32再考虑 960 或 1280否则只是白白增加显存占用。调大输入分辨率时 batch 要减半避免 OOM。4.3 训练结果怎么读mAP50、精确率与召回率的三层判断训练结束后输出在 runs/detect/kite_s 下核心文件是 weights/best.pt 和 weights/last.pt。先用验证集跑一遍指标yolo detect val \ modelruns/detect/kite_s/weights/best.pt \ datafirc.yaml \ conf0.25 \ iou0.5输出表格里最关键的是 mAP50(B)。单类别风筝检测标注质量正常的情况下mAP50 应该在 0.7 以上。低于 0.6 优先怀疑训练集里的脏数据多比如越界框、错误框。如果 mAP50 很高但 mAP50-95 低说明模型框得准但贴合度不够这时候更值得去调置信度门限而不是叠训练轮数。精确率和召回率对风筝检测场景的意义不同。做防撞预警漏报一只风筝可能直接出事故所以召回率优先置信度门限要往低调。做图像检索误检会把云层、树叶都捞出来那精确率优先门限往高调。不管哪种诉求一个门限打不了天下必须在 val 集上多跑几个 conf 值对比。我一般把 conf 从 0.05 到 0.5 每 0.05 跑一遍把 P/R 曲线画出来再决策。5. 避坑手册解压失败、绝对路径与类别编号错位的五个高频坑5.1 7z 解压失败密码正确却一直报错现象在 Linux 下执行7z x xxx.7z提示密码错误但密码明明是对的或者解压到一半中断报 CRC 校验失败。原因大部分服务器自带的 p7zip 版本过旧对新版 7-Zip 的压缩头和 AES-256 加密支持不完整。另一种场景是压缩包路径带中文系统 locale 不是 UTF-8 环境解压器找不到正确路径。解决先升级 p7zip 再解压。sudo apt install p7zip-full 7z x 风筝检测数据集VOCYOLO格式2260张1类别.7z -o./firc_kite解压完立即做三件事统计 jpg、xml、txt 各 2260 个抽查一个 xml 的编码正确性确认文件夹名没有乱码。解压阶段节省的时间会在后面数据清洗阶段成倍还回来。5.2 VOC 路径是绝对路径换机器后读不到现象如果把 xml 里的 path 字段直接喂给某个 VOC 兼容训练脚本会报一堆图片不存在错误但图片明明就在本地。原因VOC 的 path 记录的是标注机器上的绝对路径例如/home/labeler/firc-dataset/firc_kite_278.jpg换台机器自然失效。YOLOv8 不读 path所以用 yaml 配置时不会踩这个坑。解决不要依赖 xml 里的 path统一以 yaml 的 path 字段为基准。如果你运行的数据加载器强制读 xml path那就要批量改写 path 字段。import glob import xml.etree.ElementTree as ET import os for xml_path in glob.glob(Annotations/*.xml): tree ET.parse(xml_path) root tree.getroot() filename root.find(filename) if filename is None: continue path_node root.find(path) if path_node is None: path_node ET.SubElement(root, path) abs_path os.path.abspath(os.path.join(JPEGImages, filename.text)) path_node.text abs_path tree.write(xml_path, encodingutf-8, xml_declarationTrue)5.3 xml 与 txt 数量对不上或文件名大小写不一致现象统计 jpg 有 2260 张但只有 2258 个 xml 或 2259 个 txt。训练启动时不一定报错因为 YOLOv8 只看 txt缺一个 txt 就少学一张图指标悄悄往下掉。原因大量小文件在复制或下载过程中被截断也可能是 Windows 下扩展名大小写混用例如 JPG 和 jpg 并存导致 glob 匹配漏掉。解决写一段配对校验把三类文件的主文件名分别取出来做差集。import glob import os jpg_names {os.path.splitext(os.path.basename(f))[0] for f in glob.glob(**/*.jpg, recursiveTrue)} xml_names {os.path.splitext(os.path.basename(f))[0] for f in glob.glob(**/*.xml, recursiveTrue)} txt_names {os.path.splitext(os.path.basename(f))[0] for f in glob.glob(**/*.txt, recursiveTrue)} print(缺 xml:, jpg_names - xml_names) print(缺 txt:, jpg_names - txt_names) print(多 xml:, xml_names - jpg_names) print(多 txt:, txt_names - jpg_names) print(jpg 总数:, len(jpg_names))任何一行的输出非空都要先补齐或去重再训练。这个检查耗时不到一分钟却能避免整轮训练白跑。5.4 合并训练时类别编号错位0 不再意味着 kite现象把这份风筝数据和 person、car 等其他类别数据放在同一份 yaml 里训练训练能跑完但推理结果中风筝被识别成第 0 号类别对应的目标。原因YOLO txt 第一列的 0 是相对类别表 names 而言的编号。这份数据单独存在时 names 是{0: kite}0 等于 kite。合并后如果 names 改成{0: person, 1: kite}旧 txt 里的 0 就会让模型把风筝框当成 person 学习。解决合并前统一重写 txt 第一列。先把合并后的 names 定清楚比如 kite 排在第 1 位那把所有风筝 txt 里的 0 改成 1。import glob NEW_ID 1 # 合并后 kite 的编号 for txt_path in glob.glob(labels/**/*.txt, recursiveTrue): lines [] with open(txt_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue parts[0] str(NEW_ID) lines.append( .join(parts)) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines) (\n if lines else ))重写之后随机抽 50 个框画出来人眼确认类别和框内容一致再训。这个步骤是合并数据集的底线。5.5 整包直接训练验证指标忽高忽低现象训练 loss 看起来正常但 val mAP 在前 20 轮剧烈抖动甚至同配置跑两次结果差 10 个点。原因数据没有提前划分验证集训练框架内部的验证抽样带随机性。或者划分时没做分层风筝密度大的图全部落到验证集把 mAP 拉低。解决固定随机种子按 9:1 划分。分完以后单独统计验证集的框数别只看图数量。如果 val 里有 10 张图每张都挤了 10 个以上的风筝说明划分极不平衡重新洗牌或者手动把部分密集图调回训练集。我一般还会做一次快速的抽检随机抽 20 张 val 图打开画框图翻一遍确认每个框至少覆盖目标主体 80% 的区域。这个检查不需要脚本十分钟就能完成却能把最后一轮的脏数据风险降到最低。6. 进阶数据增强、置信度调优与推理验证的完整闭环6.1 只开四种增强mosaic 不是越多越好风筝场景和通用目标检测有一个明显区别目标背景多是天空天空纹理细腻、噪声少。如果像 COCO 那样把 mosaic、copy-paste、随机擦除全开模型会把天空背景的补丁当成增强的一部分训练集被改得面目全非。从工程经验看我只保留水平翻转、正负 15 度旋转、亮度和对比度抖动、少量高斯噪声这四种增强。增强幅度不用太大mosaic 要在训练后期关闭比如第 80 轮之后关掉再跑 20 轮纯正常样本给模型一段稳定期。6.2 批预测脚本找到漏检集中在大图还是小图训练结束后用 226 张验证图做一次批量推理。from ultralytics import YOLO model YOLO(runs/detect/kite_s/weights/best.pt) results model.predict( sourcefirc-dataset/images/val, conf0.15, saveFalse, verboseFalse ) total len(results) detected sum(len(r.boxes) 0 for r in results) print(fval 共 {total} 张图检到至少一个框的有 {detected} 张检出率 {detected/total:.2%})把 conf 从 0.15 提到 0.35 再跑一遍对比两次检出率。如果门限小幅提升检出率大幅下降说明模型在低置信度区域堆了一堆近乎随机的框部署时要做尺寸过滤和后处理不能无脑降低门限。6.3 部署前的最后一道检查导出 ONNX 并加载验证训练完成后的常用导出命令yolo export modelruns/detect/kite_s/weights/best.pt formatonnx opset12导出后用 onnxruntime 加载跑一遍 val 前 10 张图确认输出 shape 和置信度合法。风筝检测通常要接无人机云台视频流后面还有编解码、跟踪和报警联动每一步都可能引入新坑但模型本身稳住后面排错就有依据。那次误报事故之后我养成了一个习惯拿到任何数据集先做路径校验和统计自检再进训练流程。这套动作帮我躲过了不少翻车现场。希望这份风筝数据也能帮你省下那几周标注时间。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价