ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

无人机高空目标检测数据集解析:YOLO标注与训练实践

无人机高空目标检测数据集解析:YOLO标注与训练实践 简介这份数据集是面向无人机高空视角场景的多类目标检测训练集包含1421张验证图片及配套YOLO标注适合城市安防、港口机场巡检、智慧交通等方向的研究者与算法工程师使用。包体共2000个文件以576张jpg实拍图和1422个txt标注文件为主另有1个yaml配置文件与1份docx说明文档压缩后约92.32MB可直接导入YOLOv5/v7/v8等主流检测框架。数据覆盖港口、大型车辆、飞机、船舶、小型车辆、网球场6类目标兼顾海陆空场景标注采用标准化YOLO格式并经双重校验边界框平均定位误差小于1.5%且包含不同光照、尺度与遮挡情况适合多目标联合检测与真实环境适配。目前已有92人学习可用于无人机监控系统开发、地理空间分析、自动驾驶环境感知或军事侦察模拟等场景免去自行采集和标注数据的成本。1. 一份带 YOLO 标注的无人机数据集意味着什么很多做视觉的同行第一次拿到这套无人机高空视角多类目标检测数据集时第一反应是“才 1,421 张验证图加 1 张测试图够用吗”。但如果你真的拆过无人机项目就知道模型泛化能力差的根源往往不是图像数量而是标注是否干净、类别分布是否覆盖了“海陆空”三个维度的极端尺度。这份数据集的真正价值在于它提供了 6 类战略目标——harbor、large-vehicle、plane、ship、small-vehicle、tennis-court并且全部按照严格 YOLO 格式做成了.txt能直接喂给 YOLOv5/v8 或 Faster R-CNN。它适合那些不愿意再花两周时间人工画框、只想立刻验证算法在航拍视角下效果的团队也适合拿来做小目标检测的 baseline 测评。2. 从 Roboflow 文件命名到边界框坐标解算2.1 解析P0053__1__0___397_jpg.rf.*.jpg的命名约定如果你用ls看过解压后的目录会看到类似P0053__1__0___397_jpg.rf.a7fe89f524782ebb30a2183ee117d156.jpg这样的文件。这不是乱码而是 Roboflow 导出的标准痕迹。中间的__1__0___397是原始大图的切割坐标表示在左上角 x0y0右下角 x397 的位置截取了一块子图。这种命名方式在无人机数据集里非常常见因为单张高空原图往往分辨率极高直接训练会把显存撑爆所以要先切成 patch。我一般会写一段 Python 脚本先把文件名结构解析出来确认数据集来源和切割逻辑避免后续验证标签时对不上号import re from pathlib import Path img_dir Path(./dataset) pattern re.compile(r^(?Psource\w?)__\d?__(?Pleft\d?)___(?Pright\d?)_jpg\.rf\..?\.jpg$) for img_path in img_dir.glob(*.jpg): m pattern.match(img_path.name) if m: info m.groupdict() # 原始图源 P0053切割宽度为 right - left print(f源文件: {info[source]}, 左边界: {info[left]}, 右边界: {info[right]})这段代码的逻辑要点是源文件分组用于追溯它来自哪张无人机原始帧左边界和右边界用于计算这一块子图在原始大图中的位置。参数__1__是 Roboflow 内部的切图序号当同一张大图被切成多块时这个数字会递增。如果你发现某张图的左边界和右边界数值接近说明这是一块很小的局部图大概率属于 dense small object 区域训练时需要额外关注。2.2 从.jpg到.txt验证 YOLO 标注的格式边界YOLO 格式的标注核心是归一化的边界框坐标每一行代表一个目标class_id x_center y_center width height。这里的归一化指所有数值都除以图像宽高所以一个标注文件的每一行都应该在[0,1]区间内除了类别编号。如果出现大于 1 的坐标模型训练时 loss 直接变成 NaN。拿到数据集后我会先走一遍快速体检确认这份数据集是否真的“开箱即用”import numpy as np from pathlib import Path label_dir Path(./labels) invalid_files [] for label_file in label_dir.glob(*.txt): coords np.loadtxt(label_file, ndmin2) if coords.size 0: continue x_c coords[:, 1] y_c coords[:, 2] w coords[:, 3] h coords[:, 4] # 检查中心点是否在图像范围内宽度高度是否异常 if np.any((x_c 0) | (x_c 1)) or np.any((y_c 0) | (y_c 1)): invalid_files.append(label_file) if np.any((w 0) | (h 0)): invalid_files.append(label_file) print(f发现 {len(invalid_files)} 个异常标注文件) for f in invalid_files[:5]: print(f)体检脚本的核心是三个断言方向中心点坐标必须落在[0,1]闭区间内宽高必须严格大于 0宽高之和不能超过 1因为单个目标不可能超过画幅。如果看到w h 1的标注多半是切割子图时坐标映射错了这类标签会严重干扰 anchor 匹配需要直接用脚本过滤掉。2.3 六类目标的分布与长尾风险这份数据集的六类目标里small-vehicle和large-vehicle往往占绝对多数而tennis-court和harbor属于低频类别。如果直接把数据丢给 YOLOv8 训练模型大概率会对车辆过拟合而对网球场这类静态目标的 AP 值极低。用 sklearn 或者纯 Python 统计一下分布会更有数from collections import Counter class_names [harbor, large-vehicle, plane, ship, small-vehicle, tennis-court] class_counter Counter() for label_file in label_dir.glob(*.txt): for line in open(label_file): class_id line.split()[0] class_counter[int(class_id)] 1 total sum(class_counter.values()) for class_id in range(6): cnt class_counter.get(class_id, 0) print(f{class_names[class_id]:15s}: {cnt:6d} 个目标, 占比 {cnt/total*100:.2f}%)统计结果出来之后如果你的项目对tennis-court有硬性指标要求就一定要做类别重加权或者直接做复制粘贴增强否则 mAP 会被这个类别拖到很难看。3. 数据划分校验与定位误差复算3.1 训练 / 验证 / 测试划分陷阱这里要泼一盆冷水这套数据集的标注划分写的是验证集 1,421 张、测试集 1 张。这种划分非常反常规因为正常项目训练集要占 70% 以上验证集只留 20%。如果直接按原始划分跑训练等于你用验证集在训练最后 mAP 一定虚高。我拿到任何行业数据集后的第一件事都是重新划分。一种具备可复现性的做法是先用固定随机种子打乱文件名再按 80/10/10 拆成 train/val/testimport random from pathlib import Path random.seed(2024) all_images list(Path(./images).glob(*.jpg)) random.shuffle(all_images) n len(all_images) train_imgs all_images[:int(n*0.8)] val_imgs all_images[int(n*0.8):int(n*0.9)] test_imgs all_images[int(n*0.9):] def write_split(imgs, split_name): with open(f{split_name}.txt, w) as f: for img in imgs: # YOLO 训练时需要绝对路径或相对于项目根的路径 f.write(str(img.resolve()) \n) write_split(train_imgs, train) write_split(val_imgs, val) write_split(test_imgs, test) print(fTrain: {len(train_imgs)}, Val: {len(val_imgs)}, Test: {len(test_imgs)})这里涉及一个路径映射问题YOLO 训练时是根据图像路径去找同名的.txt标注所以train.txt里写什么路径就要确保.txt文件在相同路径下。3.2 计算边界框平均定位误差与 IoU 的关系摘要里提到“边界框平均定位误差 1.5%”这个指标通常不是指像素偏移而是指归一化的中心点偏移误差或者标注框与人工复标框之间的 IoU。为了验证这份数据集到底有没有认真做过二次校验我们可以自己算一遍预测框与真实框的 IoU 分布。假设你手上有一个已经跑过几轮的基础模型就可以把预测结果和这 1,421 张图的真值框做对比from ultralytics import YOLO model YOLO(yolov8s.pt) metrics model.val(datadata.yaml, splitval, plotsFalse) # 输出指标中包含每个类别的 AP 和 IoU 分布 print(fmAP50: {metrics.box.map50:.4f}) print(fmAP50-95: {metrics.box.map:.4f})这里的mAP50表示 IoU 阈值在 0.5 时的平均精确率mAP50-95是在 0.5 到 0.95 区间每隔 0.05 计算一次后取平均。如果数据标注误差真的小于 1.5%那么 mAP50-95 应该不会出现断崖式下跌尤其对于飞机和船舶这类边缘锐利的目标曲线会比较平滑。注意如果你发现某个类别的 mAP50 很高但 mAP50-95 极低说明标注框存在系统性偏移比如所有框都偏左上角这会让模型对框的精细位置不敏感。3.3 高空数据的尺度分布与 Slicing 策略无人机视角和手机拍摄的最大区别只有一个字小。在 100 米高度下一辆小型车辆在 1920x1080 的图片里可能只有 30x20 像素。拿 YOLOv8 默认的 640 输入尺寸去训练这类小目标的特征会直接在 5 次下采样后消失。所以处理这套数据集时建议先用脚本统计一遍所有真值框的面积分布目标类别像素面积范围示例是否属于小目标small-vehicle32x32 ~ 64x64是large-vehicle64x64 ~ 160x160否plane80x80 ~ 200x200否ship50x50 ~ 200x200视高度而定harbor200x200 以上否tennis-court120x120 ~ 300x300中等如果small-vehicle的面积普遍小于64x64你需要做两件事其一把输入分辨率从 640 提到 1280其二在训练前用滑窗把原图切成 640x640 的小块而不是直接把整图塞进网络。4. YOLOv8 与 Faster R-CNN 的训练配置差异4.1 为这套数据集编写data.yaml拿到数据集后的第一个正式动作永远是整理 YAML 文件。这份数据集的类别顺序是固定的千万不要自己改顺序否则标注里的 class_id 就全乱了# data.yaml path: /mnt/data/drone_dataset/ # 数据集根目录 train: train.txt # 训练集路径列表 val: val.txt # 验证集路径列表 nc: 6 names: 0: harbor 1: large-vehicle 2: plane 3: ship 4: small-vehicle 5: tennis-courtYOLO 会严格按照nc和names的定义去解析.txt标注文件。如果names列表写错顺序比如把harbor写在第二个位置而标注文件里class_id0实际代表 harbor模型训练的 loss 会震荡因为语义和梯度完全错位。配置完 YAML 后直接跑 YOLOv8 训练yolo train \ modelyolov8s.pt \ datadata.yaml \ imgsz1280 \ batch16 \ epochs100 \ lr00.01 \ optimizerAdamW这里参数的选择逻辑很明确imgsz1280是为了保住小目标的像素信息代价是显存占用会膨胀到 640 尺寸的 4 倍所以 batch 只能给到 16。lr00.01是 YOLOv8 默认的初始学习率配合 AdamW 优化器对中小规模的数据集收敛更稳。如果你的显卡是 24GB坚持用 1280 会非常吃力折中方案是开启rectTrue让 YOLO 自动对图像做等比缩放填充节省显存。4.2 锚框设计与重聚类YOLOv8 内部已经取消了手动指定 anchor 的逻辑改为训练时从数据中自动学习。但如果你转去用 YOLOv5或者准备用 Faster R-CNN就必须手动设计锚框。无人机视角下的目标宽高比很有特点飞机和船舶长宽比通常在 1:3 到 1:5网球场接近 1:2而车辆接近正方形。默认的 COCO 锚框在这里非常不适用。我用 k-means 在标注框上重新聚类过一版锚框效果比默认配置明显改善from pathlib import Path import numpy as np from sklearn.cluster import KMeans boxes [] for label_file in Path(./labels).glob(*.txt): for line in open(label_file): parts line.strip().split() if len(parts) 5: continue w float(parts[3]) h float(parts[4]) if w 0 and h 0: boxes.append([w, h]) boxes np.array(boxes) kmeans KMeans(n_clusters9, random_state0).fit(boxes) anchors kmeans.cluster_centers_ # 按面积排序输出适合直接写入模型配置的锚框 print(np.round(anchors[np.argsort(anchors[:, 0] * anchors[:, 1])] * 1280, 0))注意这里乘了 1280因为锚框的数值和输入分辨率密切相关。如果你决定用 640 训练就必须再除以 2否则锚框和真实目标尺度错配训练前期会出现大量低 IoU 的匹配导致模型收敛极其缓慢。4.3 Faster R-CNN 修改 RPN 参数如果你不愿意用 YOLO 系列想在 Faster R-CNN 上跑这个数据集torchvision 提供了一套现成的实现但默认锚框是为 COCO 的 800x1333 输入设计的。我一般会在代码里直接调大 RPN 的 anchor sizefrom torchvision.models.detection import fasterrcnn_resnet50_fpn from torchvision.models.detection.rpn import AnchorGenerator # 针对无人机高空视角的 6 类目标调整锚框尺寸与宽高比 anchor_generator AnchorGenerator( sizes((16, 32, 64),), aspect_ratios((0.5, 1.0, 2.0, 4.0),) ) model fasterrcnn_resnet50_fpn( weightsDEFAULT, num_classes7, # 6 类目标 背景 rpn_anchor_generatoranchor_generator, )这里sizes指的是 RPN 在特征图每个位置生成的锚框基础面积aspect_ratios是宽高比。因为无人机视角下船舶和飞机经常是很扁的长条所以宽高比里增加 4.0能让 RPN 更匹配长条形目标。如果你发现模型对网球场完全不敏感可以把sizes里加上 128 和 256应对中大型静态目标。5. 高空小目标的推理优化与切片检测5.1 测试时增强TTA的收益估算训练完模型后推理阶段不要直接调model.predict()建议先开 YOLO 自带的 TTA 看看指标上限在哪yolo predict modelbest.pt source./test_images imgsz1280 augmentTrueaugmentTrue会在推理时把原图做多尺度缩放、水平翻转和色域变换然后对预测框做加权融合。对小目标来说多尺度缩放尤其有用因为模型在 1280 尺寸下可能漏检的微小车辆在 1536 或 1920 尺寸下就能被召回。代价是单张推理时间会从 30ms 涨到 120ms 左右。如果项目对实时性要求不高比如战后离线分析TTA 是非常划算的提升手段。5.2 抛开 SAHI 谈切片推理都是空谈当输入图像达到几千像素宽时最有效的方案是切片推理SAHI。核心思路是把一张大图切成长宽 640 的 patch每个 patch 重叠 128 像素逐块送入模型做预测再用 NMS 把重叠区域的重复框合并掉。这里的重叠参数很关键如果两个 patch 之间的拼接处正好横切了一架飞机没有重叠的话这个目标就会被切碎导致漏检。pip install sahi ultralytics推理脚本from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeultralytics, model_pathbest.pt, confidence_threshold0.25, image_size1280, ) result get_sliced_prediction( imagetest_high_res.jpg, detection_modeldetection_model, slice_height640, slice_width640, overlap_height_ratio0.2, overlap_width_ratio0.2, )实战中我发现overlap_height_ratio0.2是一个比较均衡的配置既不会让重复框过多也不会把大目标切断。切片尺寸slice_height640直接决定单个目标的最小可检测尺寸如果目标是 20 像素的车辆640 patch 下的特征是最充分的但推理时间会很长。这份数据集里 harbor 和 tennis-court 属于大目标如果发现这种目标被 NMS 误删可以把 slice 尺寸提到 1024或者单独提高这两类的 confidence 阈值。5.3 数据卫生的最后一个建议最后说一个小坑在解压这份数据集的第一步建议先做一次unzip -t完整性检查不要急着改代码。航拍数据集动辄几个 GB传输过程很容易损坏如果解压时看到error read zip archive之类的报错后续跑训练的时候会发现某些图到某个 epoch 就突然 loss 为 0排查到最后发现是图像文件本身被截断。把解压、重命名、目录规划这层基础垒扎实你的实验复现流程能省下一大半时间。本文还有配套的精品资源点击获取
返回列表