
简介本资源为航拍孢子目标检测YOLO数据集面向农业病虫害监测、生物学研究、环境监测及智慧林业等方向的算法开发者与科研人员提供标准化的孢子颗粒检测数据可用于训练与验证目标检测模型。数据集共1010张图片划分为训练集708张、验证集202张、测试集100张标注类别为spores孢子采用YOLO格式归一化边界框与类别标签适配主流检测框架。压缩包共2000个文件以txt标注文件、jpg图像为主另含1个yaml配置文件与1份docx说明文档整体约10.82MB轻量级规模便于中小团队快速验证原型。单图最高含11个孢子实例支持密集目标统计场景标注规范严格可直接用于训练与部署流水线。目前已有55人学习下载适合农业院校实训、病害预警系统开发及孢子传播模式研究等场景使用。1. 航拍孢子目标检测到底难在哪从一张 4K 图里找几十个像素的孢子航拍孢子目标检测说白了就是把无人机或高塔拍下来的作物冠层图像喂给 YOLO让它把画面里那些肉眼几乎看不清的孢子囊、孢子堆一个个框出来。这件事的难点不在模型结构而在数据本身一张 4K 航拍图里孢子目标往往只有 20 到 60 个像素背景是纹理极其复杂的叶片、茎秆和土壤光照还随飞行高度和角度剧烈变化。你拿 COCO 预训练权重直接推理大概率一个框都出不来因为 COCO 里根本没有这种尺度的密集小目标。这个方向适合两类人一类是做植物病理、农业遥感的研究生和工程师需要把孢子扩散趋势量化成可统计的检测结果另一类是手里已经有航拍图、想用 YOLO 跑通小目标检测的算法从业者。孢子检测是一个极端的“小目标 密集 类内差异大”场景把它啃下来你再回头看车辆检测、行人检测会觉得轻松很多。数据集是这条链路里最贵的一环标注一张 4K 图的成本远高于训练本身所以拿到一份可用的航拍孢子 YOLO 数据集等于省掉了最痛的前期投入。2. 拆开航拍孢子 YOLO 数据集目录结构、标注格式与类别定义2.1 一份能直接训练的孢子数据集长什么样航拍孢子数据集通常按 YOLO 标准格式组织根目录下分 images 和 labels 两个文件夹各自再分 train、val、test 三个子集。images 里是原始航拍图格式多为 JPG 或 PNG分辨率从 1920×1080 到 5472×3648 不等labels 里是同名的 .txt 文件每行一个目标格式是class_id x_center y_center width height坐标全部归一化到 0 到 1 之间。这里有个容易翻车的点航拍图如果做过裁剪或缩放标注坐标必须跟着同步变换否则框会整体偏移。我见过有人用 PIL 把图缩到 640 宽label 却没动训练 loss 直接飙到 nan。正确的做法是缩放和坐标变换写在同一个脚本里保证像素级对齐。类别定义方面孢子检测一般不会只分“孢子”一类。常见做法是按孢子形态或病害类型分 2 到 5 类比如“健康孢子囊”“破裂孢子囊”“孢子堆”“疑似目标”。类别数直接决定 YOLO 检测头最后一层的输出通道改类别必须同步改 data.yaml 里的 nc 和 names少改一个就会报维度不匹配。2.2 data.yaml 的五个必填字段与路径坑YOLO 训练入口认的是 data.yaml这个文件写错后面全白搭。一个能跑通的孢子数据集配置大概长这样# data.yaml path: /data/spore_dataset # 数据集根目录绝对路径最稳 train: images/train # 相对 path 的训练图目录 val: images/val # 相对 path 的验证图目录 test: images/test # 可选推理评估用 nc: 3 # 类别数必须和 label 里的 class_id 最大值1 一致 names: # 类别名顺序对应 class_id 0: healthy_sporangium 1: ruptured_sporangium 2: spore_cluster逻辑说明path是基准目录train/val/test都相对于它解析这样换机器只需改一行。nc和names必须严格对应标注文件里的 class_id如果 label 里出现了 3 而 nc 只写到 2训练时 dataloader 会直接抛索引越界。参数说明path建议用绝对路径相对路径在不同工作目录下启动训练会解析到不同位置这是新手最常踩的坑。names的键从 0 开始连续不要跳号。如果你的孢子数据集只有一类nc 写 1names 只写 0 那一行。2.3 用脚本检查标注完整性三行代码排除 90% 的低级错误拿到数据集先别急着训练跑一遍完整性检查。下面这段脚本遍历所有 label检查坐标是否越界、类别是否超范围、是否有空文件import os import glob LABEL_DIR /data/spore_dataset/labels/train NC 3 # 与 data.yaml 保持一致 bad_files [] for txt in glob.glob(os.path.join(LABEL_DIR, *.txt)): with open(txt) as f: lines [l.strip() for l in f if l.strip()] if not lines: bad_files.append((txt, empty label)) continue for line in lines: parts line.split() if len(parts) ! 5: bad_files.append((txt, ffield count {len(parts)})) break cls, x, y, w, h int(parts[0]), *map(float, parts[1:]) if cls NC: bad_files.append((txt, fclass {cls} out of range)) break if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): bad_files.append((txt, fcoord out of range: {x},{y},{w},{h})) break print(fchecked, bad files: {len(bad_files)}) for p, reason in bad_files[:20]: print(p, reason)逻辑说明逐文件读取先排除空 label再检查每行字段数和类别范围最后校验归一化坐标是否落在 0 到 1 之间。w和h必须大于 0等于 0 的框在训练时会产生无效梯度。参数说明LABEL_DIR换成你的实际路径NC必须和 data.yaml 的 nc 一致。输出里如果 bad files 超过总数的 5%说明标注质量有问题先修数据再训练否则模型学到的全是噪声。3. 用 YOLOv8 在孢子数据集上跑通第一个 baseline3.1 环境准备与最小训练命令孢子检测对显存的要求取决于输入分辨率。航拍图目标小很多人第一反应是拉高 imgsz但 4K 图直接喂进去显存扛不住。常见做法是先离线切图把大图切成 1024×1024 的瓦片再按瓦片训练推理时把瓦片结果拼回原图。这样既保留了小目标像素又控制了显存。环境上Python 3.9 以上PyTorch 2.0 以上ultralytics 装最新稳定版即可。最小训练命令# 单卡训练孢子数据集输入 1024 yolo detect train \ data/data/spore_dataset/data.yaml \ modelyolov8s.pt \ imgsz1024 \ epochs150 \ batch8 \ device0 \ projectruns/spore \ namebaseline逻辑说明modelyolov8s.pt用官方预训练权重做迁移学习孢子数据量通常不大从零训容易过拟合。imgsz1024是切图后的瓦片尺寸不是原图尺寸。batch8是 1024 分辨率下 8G 显存能稳住的保守值。参数说明epochs设 150 到 300孢子这种小目标收敛慢早停 patience 默认 50 够用。batch如果显存够可以往上加但要注意 YOLOv8 的自动 batch 有时会给出偏大的值导致 OOM建议手动指定。device0指定第一块 GPU多卡用device0,1。3.2 孢子小目标的三个关键训练参数第一个是imgsz。孢子目标只有几十像素输入分辨率低于 640 时特征图上的响应会消失。我一般从 1024 起步显存允许就上 1280。但注意 imgsz 必须是 32 的倍数YOLO 的下采样 stride 最大是 32不整除会在拼接时出错。第二个是mosaic和scale。Mosaic 增强把四张图拼成一张对小目标检测有正面作用因为它增加了目标出现的上下文多样性。但孢子场景里 mosaic 可能把不同光照、不同地块的图拼在一起引入不真实的边界。我的经验是前 100 epoch 开 mosaic后 50 epoch 关掉让模型在真实分布上收尾。scale控制在 0.5 左右缩放太狠会让孢子变得更小。第三个是lr0和lrf。初始学习率 0.01 是 YOLOv8 默认值孢子数据集如果只有几千张图建议降到 0.005避免早期震荡。最终学习率因子lrf默认 0.01保持即可。3.3 训练日志怎么看定位不收敛的三个信号训练启动后重点盯三列box_loss、cls_loss、mAP50。孢子检测最常见的问题是 box_loss 下降但 mAP 不涨这通常意味着模型在学框的位置但分不清类别检查类别标注是否一致。第二个信号是 cls_loss 震荡不降多半是学习率偏大或 batch 太小先把 lr0 减半试。第三个信号是 mAP50 在 0.1 附近卡住说明模型根本没学到小目标特征这时候要回头检查 imgsz 是否够大、切图是否把孢子切没了。验证集 mAP 和训练集 mAP 差距超过 0.3就是过拟合加数据增强或减模型容量。孢子数据集样本量普遍偏小过拟合是常态早停和权重衰减要开。4. 孢子检测避坑实录从标注到推理的五个翻车现场4.1 现象训练 loss 正常但推理全是空框原因标注坐标没有归一化或者归一化时用了错误的图像尺寸。航拍图经常有 EXIF 旋转信息PIL 读出来的尺寸和实际显示尺寸不一致导致坐标整体偏移。解决统一用 OpenCV 读图cv2.imread会忽略 EXIF 旋转保证标注和训练用的是同一套像素坐标。如果原图有旋转先在标注前把图转正再标。4.2 现象小孢子目标全部漏检大目标正常原因YOLO 的 P3 特征图 stride 是 81024 输入下 P3 是 128×128对应原图 8 像素的步长。孢子如果只有 20 像素在 P3 上只有 2 到 3 个格子的响应很容易被 NMS 滤掉。解决把 imgsz 提到 1280 或 1536让孢子在特征图上占更多格子。另一个办法是改 anchor 或换用带 P2 检测头的模型结构P2 的 stride 是 4对小目标更友好。如果不想改结构降低推理时的conf阈值到 0.1先看召回能不能上来再决定要不要调结构。4.3 现象验证集 mAP 很高换一块地推理就崩原因孢子数据集如果只来自一个地块、一个时间段模型学到的是那块地的背景纹理不是孢子本身。换地块后光照、土壤颜色、叶片形态全变模型直接失效。解决训练集必须覆盖多个地块、多个光照条件。如果数据有限用强颜色增强HSV 空间的 H、S、V 都扰动和随机裁剪来模拟分布变化。验证集要留一个完全没出现在训练里的地块这样 mAP 才有参考价值。4.4 现象训练到一半 loss 突然变 nan原因标注里有宽高为 0 的框或者坐标超出 0 到 1 范围。YOLO 在计算 CIoU loss 时会对宽高取对数0 宽高直接产生 inf。解决训练前跑一遍 2.3 节的完整性检查脚本把所有非法框修掉或删除。另外检查 data.yaml 的 nc 是否和 label 里的最大 class_id 一致不一致也会在 one-hot 编码时出错。4.5 现象推理速度远低于预期达不到实时原因imgsz 拉到 1536 后单张推理时间可能超过 100ms加上预处理和后处理整条链路更慢。孢子检测如果要做实时监测分辨率和速度必须权衡。解决先确认是否真的需要实时。孢子扩散是小时级过程分钟级推理完全够用。如果非要提速用 TensorRT 导出 FP16 或 INT8 引擎1024 分辨率下 T4 上大概能到 30 到 50 FPS。导出命令yolo export modelruns/spore/baseline/weights/best.pt \ formatengine \ imgsz1024 \ halfTrue \ device0逻辑说明formatengine导出 TensorRT 引擎halfTrue启用 FP16精度损失通常在 1% mAP 以内速度提升接近一倍。device0指定导出时用的 GPU导出的引擎和该 GPU 架构绑定换卡要重新导出。参数说明INT8 量化需要校准集孢子数据集可以直接用验证集做校准但要注意校准集分布要和实际推理一致。FP16 不需要校准是更稳妥的选择。5. 把孢子检测推到可用切图推理、结果拼接与阈值调优5.1 大图切图推理的完整流程航拍原图动辄 5000 像素宽直接推理显存不够必须切图。切图有两个参数tile_size和overlap。tile_size和训练时的 imgsz 保持一致比如 1024。overlap一般设 128 到 256 像素防止目标正好落在切缝上被切掉。import cv2 import numpy as np from ultralytics import YOLO model YOLO(runs/spore/baseline/weights/best.pt) img cv2.imread(/data/raw_flight/plot_01.jpg) H, W img.shape[:2] TILE, OVERLAP 1024, 200 STRIDE TILE - OVERLAP all_boxes [] for y in range(0, H, STRIDE): for x in range(0, W, STRIDE): tile img[y:yTILE, x:xTILE] if tile.shape[0] TILE or tile.shape[1] TILE: tile cv2.copyMakeBorder(tile, 0, TILE-tile.shape[0], 0, TILE-tile.shape[1], cv2.BORDER_CONSTANT, value(114,114,114)) res model.predict(tile, imgszTILE, conf0.15, iou0.5, verboseFalse)[0] for box in res.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() all_boxes.append([x1x, y1y, x2x, y2y, box.conf.item(), int(box.cls.item())]) # 跨 tile 的重复框用 NMS 合并 def nms(boxes, iou_thr0.5): boxes sorted(boxes, keylambda b: b[4], reverseTrue) keep [] while boxes: best boxes.pop(0) keep.append(best) boxes [b for b in boxes if iou(best, b) iou_thr] return keep def iou(a, b): xx1, yy1 max(a[0], b[0]), max(a[1], b[1]) xx2, yy2 min(a[2], b[2]), min(a[3], b[3]) inter max(0, xx2-xx1) * max(0, yy2-yy1) area_a (a[2]-a[0]) * (a[3]-a[1]) area_b (b[2]-b[0]) * (b[3]-b[1]) return inter / (area_a area_b - inter 1e-6) final nms(all_boxes, 0.5) print(ftotal detections after merge: {len(final)})逻辑说明按 stride 滑动切图边缘不足 TILE 的用灰色填充保证每块都是 TILE×TILE。每块推理后把框的坐标加回原图偏移最后用全局 NMS 合并跨 tile 的重复检测。conf0.15是孢子检测的常用低阈值宁可多检再靠 NMS 和后续规则过滤。参数说明OVERLAP越大切缝漏检越少但推理次数增加。200 像素在 1024 tile 下大约增加 25% 的计算量是可以接受的折中。iou_thr设 0.5孢子密集时如果发现框被过度合并可以降到 0.4。5.2 置信度阈值和 NMS IoU 的联合调优孢子检测的 conf 阈值不能照搬 COCO 的 0.25。孢子目标小、特征弱模型输出的置信度普遍偏低0.25 会漏掉大量真实目标。我的做法是在验证集上画 conf 从 0.05 到 0.5 的 P-R 曲线找 F1 最大的点。通常这个点在 0.1 到 0.2 之间。NMS IoU 阈值影响密集孢子的合并程度。孢子堆里目标挨得很近IoU 设 0.5 可能把两个真实目标合并成一个。如果标注里孢子堆是分开标的把 NMS IoU 降到 0.3 到 0.4保留更多相邻框。代价是可能引入重复检测需要靠后续的面积过滤或聚类去重。5.3 用验证集做一次端到端评估训练时的 mAP 是在切图瓦片上算的和整图推理的指标有差距。部署前必须用整图切图推理跑一遍验证集对比两个数字。如果整图 mAP 比瓦片 mAP 低超过 5 个点说明切图或拼接环节有问题重点查 overlap 是否够、边缘填充是否正确。评估脚本可以直接用 ultralytics 的 val 接口但要把数据换成整图推理的结果。更简单的办法是手动统计对每张验证图把预测框和 GT 框做匹配算 precision、recall、F1。孢子检测最终看的是 recall漏检一个孢子堆可能意味着错过一次病害爆发点precision 低一点可以靠人工复核补。我自己的习惯是每次改完切图参数或阈值都固定跑同一批 50 张验证图把 recall 和误检数记在表格里。改了三版之后回头看哪组参数是真的有用一目了然。孢子检测没有银弹参数都是一轮轮试出来的希望帮到你。本文还有配套的精品资源点击获取