拓冰建站拓冰建站
首页 / 资讯中心 / 正文

无人机目标检测数据集处理:VOC/COCO/YOLO格式转换与YOLO训练调优

简介面向无人机视觉、目标检测入门及YOLO系列算法实践者这份数据集包含5000张真实场景高质量图片覆盖多种环境与目标形态经LabelImg精细标注标注框质量高可直接支撑模型训练与效果验证。压缩包共2000个文件以xml标签文件为主1986个同时提供coco(json)、yolo(txt)格式转换结果方便不同框架直接读取另含5个txt说明、6个html教程和3个Python脚本覆盖环境搭建、训练案例、数据划分等关键环节。整包301.59MB已有433人学习。借助划分脚本可按需切分训练集、验证集、测试集配套Linux/Windows版本训练教程可帮助新手从零跑通YOLO完整流程。这份资料既能直接投入项目训练也可作为课程设计或毕业设计的扎实参考。1. 无人机视角下的目标检测为什么不拿通用数据集硬扛无人机拍到的画面和手机、监控视角完全不同百米高度俯视下去行人只占几十像素目标尺度变化又剧烈把 COCO 上训好的模型直接搬过来mAP 常常掉二十个点以上。这个标题把 5000 张航拍图、VOC/COCO/YOLO 三种标注格式、数据划分脚本和训练教程打包在一起解决的核心问题是“拿到数据后不需要再自己写转换脚本和处理划分逻辑”。VOC 的 XML 方便复查标注COCO 的 JSON 能直接接 mmdetection 这类框架YOLO 的 txt 喂给 YOLO 系列开箱即用。对准备在无人机场景上做检测验证的工程师来说这类资源省掉的是从原始标注到训练数据链路里最枯燥也最容易出错的几步。2. VOC、COCO、YOLO 三种标签格式的存储差异与相互转换2.1 VOC 格式XML 文件里的绝对像素坐标VOC 格式为每张图片生成一个同名 XML 文件标注框记录的是绝对像素坐标。一个典型的标注文件结构如下annotation folderdrone_images/folder filenameDJI_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object namecar/name truncated0/truncated difficult0/difficult bndbox xmin342/xmin ymin518/ymin xmax468/xmax ymax586/ymax /bndbox /object /annotation这里size节点里的宽高必须和原图实际尺寸一致后续做归一化转换时依赖的就是这两个值object可以有多个每个目标一套bndbox存的是左上角和右下角坐标。VOC 格式最大的优点是可以用 LabelImg 直接打开复查缺点是每个 XML 只对应一张图目录下上千个文件管理起来零散且坐标不能直接喂给 YOLO必须经过归一化。2.2 COCO 格式一个 JSON 管住所有标注COCO 格式把所有信息集中到一个 JSON 文件里核心是images、annotations、categories三个数组。images数组每项存图片的id、file_name、宽高annotations数组每项存一个目标实例的image_id、category_id、bboxcategories数组维护类别 id 和名称的对应关系。{ images: [ {id: 0, file_name: DJI_0001.jpg, width: 1920, height: 1080} ], annotations: [ { id: 0, image_id: 0, category_id: 1, bbox: [342, 518, 126, 68], area: 8568, iscrowd: 0 } ], categories: [ {id: 1, name: car, supercategory: vehicle} ] }这里最隐蔽的坑是bbox的坐标语义COCO 里是[x, y, width, height]即左上角坐标加宽高而不是右下角坐标。从 VOC 转换时如果直接拿xmax当 width 用所有框都会偏。另一个坑是category_id从 1 开始计数而 YOLO 的类别 id 从 0 开始转换时不做减一操作训练时类别会整体错位。2.3 YOLO 格式归一化坐标的纯文本YOLO 格式是三类中最精简的每张图片对应一个同名 txt 文件每行一个目标格式为class x_center y_center width height后四个值全部除以图片宽高做归一化取值范围 0 到 1。0 0.210938 0.511574 0.065625 0.062963 1 0.423958 0.389815 0.076042 0.055556训练时 YOLO 直接按行读取标签不需要解析 XML 或 JSON效率最高。缺点是无法直接用文本判断标注是否合理必须依赖可视化脚本画框检查。2.4 三种格式相互转换的参考实现最常见的需求是 VOC 转 YOLO。下面这段代码遍历 XML 文件把绝对像素坐标归一化后写入 txtimport xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) 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) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) out_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, out_name), w) as f: f.writelines(lines)class_names是外部传入的类别列表列表里元素的顺序就是 YOLO 类别 id。归一化除以的是 XML 里size的宽高而不是通过os.path重新读取图片尺寸因为标注时的原始尺寸以 XML 记录为准。输出保留 6 位小数平衡精度和文件体积。os.path.basename保证 txt 文件名与图片同名YOLO 训练时才能通过图片名匹配标签。转换完成后有一个几乎必做的检查随机挑几张图用 OpenCV 把 txt 里的坐标还原成像素框画出来确认框的位置和物体吻合。坐标转换的 bug 很难从 loss 上发现——模型会跟着错误的框一起收敛loss 照样下降直到验证阶段 mAP 上不去才开始排查这时往往已浪费了好几轮训练。3. 数据划分脚本怎么切无人机数据集才不虚高3.1 随机划分的三个隐性坑拿到 5000 张图后直接random.shuffle再按 8:1:1 切分对普通分类问题勉强能用对无人机数据集却会带来验证指标虚高。无人机数据最常见的来源是航拍视频抽帧同一架次前后相邻的帧画面几乎相同目标的大小、位置、光照高度接近。如果相邻帧被同时分到训练集和验证集模型在训练阶段已经见过验证集的内容验证 mAP 自然好看换到真实场景立刻打回原形。第二个坑是类别不平衡。无人机俯视场景里车辆往往占了大半标注行人次之骑行者可能只有零星几个。随机划分之后占比少的类别在验证集里可能只剩个位数评估出来的单类 AP 波动极大甚至某一类完全没出现在验证集中。第三个坑是序列相关。航拍抽帧按时间排列时前段可能是晴天、后段是阴天或者前段在城区、后段在郊区。随机打散后模型靠背景颜色和纹理就能猜出场景来源看似精度高实际学到的不是目标本身。3.2 按架次分组 类别平衡的划分实现正确的做法是先按架次分组同一个架次内部的帧整体落在同一个集合里再从每个架次内部按比例切片避免跨集合的数据泄漏。参考实现import random def group_divide(shots, ratio(0.8, 0.1, 0.1)): # shots: dict, key 为架次名, value 为该架次下的图片文件名列表 random.seed(42) train, val, test [], [], [] for shot, imgs in shots.items(): imgs list(imgs) n len(imgs) n_train int(n * ratio[0]) n_val int(n * ratio[1]) random.shuffle(imgs) train.extend(imgs[:n_train]) val.extend(imgs[n_train:n_train n_val]) test.extend(imgs[n_train n_val:]) return train, val, test核心逻辑是每个架次内部先 shuffle 再切片任意两个集合之间不会出现来自同一架次相邻帧的图片从源头上切断了序列相关性。random.seed(42)固定随机种子保证复现实验时数据划分一致——很多实验结果无法复现划分不固定是常见原因之一。ratio按 (train, val, test) 顺序传参默认 0.8、0.1、0.1。如果数据集规模小建议把测试集缩小到 0.05把省下的图片并入训练集验证集保持 0.1 不变。划分完成后务必打印每个集合的图片数和目标数确认没有类别被漏掉。但按架次分组仍解决不了类别分布问题如果某个类别只出现在一个架次该架次落到训练集验证集就完全没有这个类。所以划分脚本输出里必须带类别统计方便一眼看出问题。3.3 划分脚本输出应该包含什么一份可靠的划分脚本不只是输出三个文件列表还应该输出统计信息文件内容包括每个集合的图片数、目标总数、各类别目标数存成 JSON{ train: [DJI_0001.jpg, DJI_0002.jpg], val: [DJI_0010.jpg], test: [DJI_0020.jpg], stats: { train: {images: 4000, objects: 12000, classes: {car: 7200, person: 3600}}, val: {images: 500, objects: 1500, classes: {car: 900, person: 450}} } }这个文件同时承担两个作用训练时按train和val列表读取图片保证每次实验用的是同一份数据排查问题时对照stats里的类别明细能快速区分是数据切分的问题还是模型训练的问题。如果某个类别在验证集的目标数少于 30就该把这个类别所在架次的图片手动匀一部分到验证集或者考虑按类别分层抽样。4. 无人机数据集在 YOLO 上的训练流程与参数设置4.1 数据集目录结构与 YAML 配置YOLO 系列框架对数据集目录结构有固定要求拿到数据集后先按下面的结构摆好图片和标签drone_dataset/ ├── images/ │ ├── train/ │ │ └── DJI_0001.jpg │ └── val/ │ └── DJI_0010.jpg ├── labels/ │ ├── train/ │ │ └── DJI_0001.txt │ └── val/ │ └── DJI_0010.txt └── drone.yamllabels下的 txt 文件内容就是第二章讲过的归一化坐标一行一个目标。图片和标签必须同名且位于对应的 train/val 子目录下YOLO 不会做任何容错处理。drone.yaml内容很简单path: /path/to/drone_dataset train: images/train val: images/val names: 0: car 1: person 2: cyclist 3: truckpath是数据集根目录的绝对路径train和val写相对路径。names的顺序必须和 txt 标签第一列的类别 id 完全对应写反了模型预测结果会整体错位。拿到数据集后第一步建议扫描全部 txt确认实际出现的类别 id 范围for f in labels/train/*.txt labels/val/*.txt; do cat $f; done | awk {print $1} | sort | uniq -c这条命令把 train 和 val 所有标签文件的第一列输出、排序、统计输出形如3654 0、1200 1用于核对标签里到底有哪几类、每类多少目标再据此写names。如果标签里出现了 YAML 没写的类别 idYOLO 会跳过对应目标模型会莫名其妙漏检。4.2 训练命令与核心参数说明目录没问题后直接训练以 YOLOv8 为例yolo train taskdetect \ datadrone.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ workers8 \ patience20modelyolov8s.pt加载 COCO 预训练权重做迁移学习比从头训练收敛更快、精度更高这也是大部分无人机检测项目落地时采用的方式。imgsz640表示训练时统一缩放到 640×640这个值对无人机小目标影响很大后面细说。batch16是单卡批大小显存不足时降到 8patience20指连续 20 个 epoch 验证集指标不提升就早停。如果用的是 YOLOv5命令换成python train.py --data drone.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100参数含义一一对应。5000 张图在单张 RTX 3090 上训练 100 个 epoch大约需要 2.5 到 3 小时。epochs 不建议低于 50因为预训练模型的特征提取层已经学到通用特征但检测头需要足够多的迭代来适应无人机特有的目标尺度分布。4.3 训练过程中观察什么训练日志里重点看三个指标。第一个是box_loss代表预测框与真值框的回归误差。前 20 个 epoch 快速下降后趋于平缓属于正常收敛如果 loss 反复震荡先查学习率是否过大一般从默认 0.01 往下调。第二个是mAP50和mAP50-95无人机小目标数据集的 mAP50 通常明显高于 mAP50-95因为 IoU 阈值放宽后小目标只要框的位置大致对就算中。两者差距过大说明定位精度不足优先考虑加大 imgsz。第三个是recall和precision巡检场景漏检代价远大于误检调参时应优先保证 recall再靠conf_thres控制误报。现象可能原因调整方向loss 不降学习率过大、标签类别错位调低 lr、核对类别 idmAP50 高但 mAP50-95 低imgsz 过小、锚框不匹配增大 imgsz、聚簇锚框漏检严重小目标特征丢失加大输入尺寸、切片推理误检多增强过度、阈值过低降低 mosaic 概率、提高 conf提示训练前检查一下标签目录里有没有空文件。航拍数据中确实存在没有目标的背景图YOLO 会直接跳过空标签图片但每轮都在重复检查文件白白消耗 IO。空文件比例超过 10% 时建议把这些背景图挪出数据集目录。4.4 训练完成后怎么验证训练结束后runs/detect/train/weights/下会生成best.pt和last.ptbest.pt是验证集指标最优的权重。用它对单张图片推理yolo predict modelruns/detect/train/weights/best.pt \ sourcetest_imgs/DJI_1001.jpg \ conf0.25conf0.25是置信度阈值低于该值的检测结果被过滤。无人机巡检场景希望少漏检就降到 0.1用于自动报警希望少误报就调到 0.4 到 0.5 之间。推理结果会画出边界框和类别名肉眼检查框的位置和尺寸是否符合常识这一步比任何指标都直观。5. 针对无人机小目标的三个调优技巧5.1 训练时提高 imgsz推理时保持一致imgsz 是成本最低、见效最直接的参数。640 输入下一个 30×40 像素的行人缩到约 10×13 像素卷积下采样后特征几乎消失。把训练和推理的 imgsz 提高到 960目标像素占比提升约 1.5 倍mAP 通常能涨 3 到 8 个点。代价是显存占用非线性增长训练时间变长。建议先用 640 跑通整体流程确认数据链路没有 bug再用 960 做正式训练。5.2 推理时切片预测不改变模型训练完的模型输入尺寸固定但无人机原图往往是 4000×3000 甚至更大整体缩到 640 后小目标直接变成噪声。SAHI 是最常用的切片推理方案把大图切成若干 640×640 的小块每块独立推理再把结果映射回原图坐标重叠区域用 NMS 合并。切片之后的检测结果比整图缩放通常会好一截。pip install sahifrom sahi.model import Yolov8DetectionModel from sahi.predict import get_sliced_prediction model Yolov8DetectionModel( model_pathruns/detect/train/weights/best.pt, confidence_threshold0.25, devicecuda:0 ) result get_sliced_prediction( imageDJI_4000.jpg, detection_modelmodel, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2, ) result.export_visuals(export_diroutput/)slice_height和slice_width一般和训练时的 imgsz 保持一致统一为 640。overlap_height_ratio和overlap_width_ratio是相邻切片的重叠比例设为 0.2 而不是 0 的原因是目标刚好被切片边界切到两半时重叠区域能让目标至少完整出现在一个切片里。重叠越大推理越慢0.2 是精度和耗时的较好平衡点。5.3 手动聚类锚框适配俯视目标尺度无人机俯视视角下多数目标的宽高比集中在 0.5 到 2 之间和 COCO 预训练模型的锚框分布有明显差异。YOLO 训练时虽然会自动聚类但如果数据集本身的尺度区间和默认锚框差太多收敛会很慢。做法是用 K-Means 对所有训练集标签的宽高做一次聚类把聚出的锚框写回yolov8s.yaml的anchors字段再重新训练。改完观察前 20 个 epoch 的box_loss是否比默认锚框下降更快、更稳如果差异不明显说明当前瓶颈不在锚框把时间花在提高 imgsz 或补充小目标样本上更划算。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门