简介:菲律宾水稻褐飞虱成虫目标检测数据集来自真实稻田环境,共1,458张现场图片,训练集941张、验证集304张、测试集213张,面向农业AI开发者、植保科研人员与高校智慧农业专业学习者。标注采用YOLO格式,聚焦褐飞虱成虫这一主要水稻害虫类别,可直接投入YOLO等主流目标检测框架,用于构建虫害自动识别模型、物联网实时监测预警系统,为精准植保决策提供可靠数据支撑。资源包共2,000个文件:1,458个txt标注对应每张图片的边界框与类别信息,540张jpg原图,1个yaml配置便于快速适配训练环境,1个docx文档说明数据集背景与构成,整体zip压缩包仅56.36MB,轻便易取。已有196人学习使用。标注样本经农业专家校验,覆盖水稻不同生长阶段,田间真实光照变化与植株遮挡能有效提升模型泛化能力,既可支撑高校科研与毕业论文实验,也可接入无人机或田间物联网设备实现害虫实时监测预警,辅助减少农药滥用、助力粮食安全。
1. 为什么拿这份水稻虫害数据集做目标检测:1,458 张图能解决什么
做农业目标检测的人,最怕的不是模型选型,而是找不到能直接开训的数据集。这份菲律宾水稻褐飞虱成虫目标检测数据集一共 1,458 张田间实拍图,已经划分好训练集 941 张、验证集 304 张、测试集 213 张,标注统一为 YOLO 格式的单类目标。解压之后不用再花时间做格式转换,直接传给 YOLOv5、YOLOv8 就能开始训练,是典型的「yolov8 训练自己的数据集」入口级材料。
它能解决两件事:一是水稻虫害识别模型的快速验证,算法改版、超参对比都能先在这类小规模数据上跑出结论;二是给田间虫口监测系统提供一套带真实遮挡和光照变化的基准数据,而不是实验室白背景的“理想图”。褐飞虱成虫是水稻主要害虫,虫体只有几毫米,颜色又贴近稻株,这类图像恰好是目标检测里小目标检测的典型场景,比 COCO 那种通用数据集更贴农业现场。
适合三类人:拿 YOLO 练手、想完整走一遍数据到训练的初学者;做智慧农业、精准植保项目、需要先验证可行性的研发工程师;以及农业院校里带学生做病虫害识别实验的教师。下面就从标注格式讲起,把训练、避坑、部署和计数统计一条线走完。
2. 读懂 YOLO 标注:目录结构、标签 txt 和坐标换算
跑训练命令之前,先把数据集的「里子」看清楚。很多新手拿到压缩包就急着解压、急着训练,结果 loss 降不下去,最后发现是标签格式理解错了。YOLO 系列的数据处理流程高度依赖目录约定和 txt 坐标格式,这章把关键点拆开讲。
2.1 解压之后先看目录:train / valid / test 三套目录的约定
压缩包用 zip 打包,Linux 下直接解压,Windows 下用自带解压工具或 7-Zip 都行:
unzip 菲律宾水稻褐飞虱成虫目标检测数据集.zip解压后先不要急着删压缩包,确认目录结构再动手。这个数据集按训练、验证、测试三套目录组织,每套下面又有 images 和 labels 两个子目录:
rice_bph/ ├── train/ │ ├── images/ 941 张 jpg │ └── labels/ 941 个 txt ├── valid/ │ ├── images/ 304 张 jpg │ └── labels/ 304 个 txt └── test/ ├── images/ 213 张 jpg └── labels/ 213 个 txtimages 和 labels 是一一对应的,jpg 主名等于 txt 主名。比如 01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004.jpg 对过去一定是同名 txt。文件名里带 rf 是导出工具留下的命名痕迹,不影响训练,但写批处理脚本时不要按前缀过滤,直接按主名关联最稳。
训练和验证在 train / valid 上进行,test 集只做最终评估,不要拿来做任何调参。如果你要复现这份数据集的基准结果,train / valid / test 的划分比例就别动;如果只是练手,重排一次也没关系,但报告时要注明是自己重划分的。
| 目录 | 图片数 | 作用 |
|---|---|---|
| train/images + labels | 941 | 训练权重 |
| valid/images + labels | 304 | 选超参、早停、选 best.pt |
| test/images + labels | 213 | 最终效果评估,只碰一次 |
2.2 标签 txt 里那五个数字:归一化坐标怎么换算成像素框
打开任意一个 txt,每行是五个数:
0 0.4714 0.5382 0.0820 0.1176第一个数是类别 id,这份数据集只有一个类别「褐飞虱成虫」,所以固定为 0。后面四个数是归一化后的框中心 x、中心 y、宽度、高度,全部除以图片宽高以后得到 0~1 之间的浮点数。换算成像素坐标的公式是:
x_pixel = x_center * img_width y_pixel = y_center * img_height w_pixel = w * img_width h_pixel = h * img_height左上角坐标就是 x_pixel - w_pixel / 2 和 y_pixel - h_pixel / 2。拿 1280×720 的图举例:cx=0.4714 乘 1280 得 603,cy=0.5382 乘 720 得 387,bw=0.0820 乘 1280 得 105,bh=0.1176 乘 720 得 85。也就是说中心点大约在 (603, 387),框宽 105、高 85 像素。
这个 105×85 的框只占图面不到 1%,是标准的小目标尺寸。如果某张图里有多个目标,txt 里就有多行,一行一个框。发现某个值小于 0 或大于 1,说明标注导出异常;发现类别 id 不是 0,说明多类别混入,后面避坑章会专门展开。
2.3 用 Python 抽查标注:把框画出来再训练
不能只看 txt,我一般会抽 20 到 30 张图把框画出来,人眼扫一遍比任何指标都直观:
import cv2 import os img_dir = "rice_bph/train/images" label_dir = "rice_bph/train/labels" for name in ["01758_jpg.rf.e7169bd54d4777d1e5ab942ba0afc004", "00337_jpg.rf.b6074241442c5aecf427945e27b2bd68"]: img = cv2.imread(os.path.join(img_dir, name + ".jpg")) h, w = img.shape[:2] with open(os.path.join(label_dir, name + ".txt")) as f: for line in f: cid, cx, cy, bw, bh = map(float, line.split()) 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, 255, 0), 2) cv2.imwrite("check_" + name.split("_jpg")[0] + ".jpg", img)逻辑说明:先读图拿到宽高,再逐行解析 txt,五个数分别对应类别 id 和归一化坐标,乘上实际宽高得到像素坐标,最后用 OpenCV 画框。画完存成 check_ 开头的图片,直接肉眼看。
注意点:这份数据是单类,画框颜色不用区分;如果画出来的框偏到角落、比例畸形或明显只包住虫子的半边身体,说明标注坐标异常。缩略图一眼能看出漏标的话,直接放弃该图或重新标注,不要指望模型自己学回来。多目标图里如果出现大量重叠框,也要留意是不是把稻秆误包进了虫框。
2.4 标签统计:先搞清目标尺寸分布再定训练参数
画框只能看局部,全量统计目标尺寸分布才能决定训练策略:
import os import cv2 label_dir = "rice_bph/train/labels" img_dir = "rice_bph/train/images" areas = [] for f in os.listdir(label_dir): img = cv2.imread(os.path.join(img_dir, f.replace(".txt", ".jpg"))) h, w = img.shape[:2] with open(os.path.join(label_dir, f)) as fp: for line in fp: _, cx, cy, bw, bh = map(float, line.split()) areas.append(bw * w * bh * h) areas.sort() print("框数量:", len(areas)) print("最小 10% 框面积:", areas[:int(len(areas) * 0.1)]) print("中位数框面积:", areas[len(areas) // 2])逻辑说明:遍历目录里每个 txt,按主名找到对应图片,用图片宽高把归一化框还原成像素面积,收集起来排序。最小 10% 的框代表数据里最难检的那一批,中位数代表整体量级。
如果中位数框面积不到原图面积的 2%,可以按小目标检测方案处理:训练 imgsz 提到 640 以上,推理阶段用滑窗裁图。这个统计结果是第 3 章参数选择和第 6 章滑窗推理的直接依据,建议训练前先跑一遍。
3. 训练前把数据喂给 YOLOv8:数据集配置与三个关键参数
把 YOLOv8 能读懂的data.yaml配好,再把训练命令里的 imgsz、batch、epochs 三个参数调到符合这份数据的状态,训练基本就成功了一半。很多人死在数据路径和类别数对齐上,这一章专门讲清楚。
3.1 目录整理:用软链接而不是复制粘贴
YOLOv8 默认从数据集根目录往下找 images 和 labels,标准结构是train/images、train/labels这种。如果压缩包解压出来的目录层级不对,我习惯用软链接整理,不动原始数据:
mkdir -p rice_bph_dataset/train rice_bph_dataset/valid rice_bph_dataset/test ln -s /path/to/rice_bph/train/images rice_bph_dataset/train/images ln -s /path/to/rice_bph/train/labels rice_bph_dataset/train/labels ln -s /path/to/rice_bph/valid/images rice_bph_dataset/valid/images ln -s /path/to/rice_bph/valid/labels rice_bph_dataset/valid/labels ln -s /path/to/rice_bph/test/images rice_bph_dataset/test/images ln -s /path/to/rice_bph/test/labels rice_bph_dataset/test/labels逻辑说明:软链接只是快捷方式,不复制文件,省磁盘空间,也保证原始解压结果不被误改。Windows 用户可以用mklink /J做目录联接,或者直接把 images 和 labels 复制到目标目录,效果一样。
训练时框架会先在 images 里读图,再去对应 labels 目录找同名 txt。如果训练日志出现大量 label file not found,十有八九是目录相对位置被改过了,比如把 labels 全部塞进一个总文件夹。目录整理好以后,运行一次find . -name "*.txt" | wc -l,数量对得上预期再继续。
3.2 data.yaml:路径、类别数和类别名必须对齐
path: /path/to/rice_bph_dataset train: train/images val: valid/images test: test/images nc: 1 names: 0: brown_planthopper_adult说明:path 写数据集根目录,train / val / test 只写相对路径,框架会自动去对应 images 目录找图片,再按主名找 labels。nc=1 是类别数,names 从 0 开始编号。
类别名里不要带空格和连字符,用下划线连接。原始标注名如果是 brown planthopper - adult,写进 yaml 时建议统一成 brown_planthopper_adult,避免路径解析时空格被截断。三个最常见的报错是:path 写错导致 found 0 images;names 个数与 nc 不一致;images 和 labels 目录名拼错。检查顺序就是先ls -l确认目录,再打开 yaml 逐行对。
3.3 训练命令:imgsz、batch、epochs 三个参数定生死
代码:
yolo detect train \ data=/path/to/rice_bph_dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ project=rice_bph_runs \ name=exp1 \ device=0参数说明:
| 参数 | 含义 | 这份数据的建议 |
|---|---|---|
| model | 预训练权重 | yolov8n.pt 或 yolov8s.pt,不要从零训练 |
| imgsz | 输入尺寸 | 640 起步,显存够用再上 768 |
| batch | 每批图片数 | 16,显存不足降到 8 |
| epochs | 训练轮数 | 100,配合 patience 提前停 |
| patience | 早停耐心值 | 20,验证指标多轮不涨就停 |
| device | GPU 编号 | 0;没有 GPU 用 cpu,慢很多 |
针对这份单类小目标数据,模型选 yolov8n 最合理:1,458 张图只够学一个类,参数量大的 v8x 很容易过拟合。imgsz 是这张数据集的关键,虫体本来就只有几十像素,imgsz=320 会把目标压到几个像素,特征全丢;640 是兼顾显存和精度的起点。
早停参数 patience=20 的意思是验证集 mAP 连续 20 轮不提升就停止。训练日志每轮会打印 box_loss、cls_loss、dfl_loss,三项持续下降说明在正常学。如果 cls_loss 震荡不降,大概率是标签问题,回到第 2 章重新检查。
3.4 训练完看什么指标:不要只盯 mAP50
训练完成后重点看这四个数:
| 指标 | 含义 | 单类小目标参考 |
|---|---|---|
| precision | 检出的框里多少是真的 | 0.85 以上 |
| recall | 真实虫体找回多少 | 0.85 以上 |
| mAP50 | IoU=0.5 的平均精度 | 0.80 以上 |
| mAP50-95 | 更严格的 IoU 标准 | 0.50 以上 |
precision 和 recall 对虫害检测非常重要:recall 低了漏检多,后面做虫口计数会被系统性低估;precision 低了误检多,预警系统天天瞎报。mAP50-95 在小目标上天然吃亏,不用过于焦虑,重点看 mAP50 和 recall 有没有同时走高。
results.png 里还有一组曲线,val 的 loss 如果先降后升、train 还在降,就是过拟合信号,回到 3.3 加正则或减小 epochs。confusion_matrix.png 里如果背景那一格非零比例很高,说明误检多,检查 imgsz 是否太小、标注框是否太松,这正好接上第 4 章的避坑内容。
4. 避坑:田间小目标检测最常踩的五个坑
田间数据训练常见的坑,网上很多经验贴说得玄乎,本质就那么几个:标签脏、尺寸小、划分不均、显存不够硬调参。我把这五个最常遇到的按「现象 → 原因 → 解决」写清楚。
4.1 现象:模型把稻秆和背景误检成虫,precision 一片狼藉
原因:imgsz 设太小。褐飞虱成虫在原图上常只有 30 到 50 像素,imgsz=320 一缩放,虫体压到十个像素以内,纹理特征基本丢失,模型只能靠颜色和周围环境猜,误检自然爆表。
解决:imgsz 至少 640,更高分辨率配合 batch 折中使用。推理阶段把 confidence 阈值从默认 0.25 提到 0.3 到 0.35,能在 precision 和 recall 之间重新找平衡。如果 imgsz 已经 640 还是这样,检查标签框是否把稻秆大片包进去了。
4.2 现象:训练曲线正常,换到验证集 mAP 突然崩掉
原因:数据划分不随机或拍摄时段差异大。如果图片按日期或地块顺序排列,划分时可能 train 全是晴天顺光,valid 全是阴天逆光,田间光照差异直接变成域偏移,模型见过的那类光和没见过的光表现天差地别。
解决:划分前先把文件名打散,再按 7:2:1 或 8:2 切。固定划分的数据集想复现报告结果,用原始 train / valid 划分就行;想自己重训,重排一次更稳。跑完 val 如果 precision 和 recall 差距大,回看 valid 图片的直方图,确认不是整体曝光差异导致的假掉点。
4.3 现象:loss 正常下降,mAP 始终为零
原因:标签格式有问题。最常见三类:类别 id 越界、坐标值超出 0~1、txt 里出现 0 0 0 0 0 空框行。YOLO 会静默跳过坏标签不报错,模型在部分图上等于空跑,loss 看起来正常,精度永远起不来。
解决:训练前跑一遍 2.3 的脚本把所有 txt 打印出来人工抽查,再写循环检查每个 txt 的坐标范围。发现脏标签直接脚本清洗,不要手动改几百个文件。清洗完重新统计标签数量,和清洗前对比,差值就是被修掉的坏样本数。
4.4 现象:显存不够把 batch 调小,结果精度反而掉
原因:batch 降到 4 以后,BN 层统计量不稳定,尤其小数据集每个 batch 的分布差异大,模型收敛不稳。有人调参调到心态崩,以为是模型问题,其实是 batch 太小导致的训练不充分。
解决:优先降 imgsz(640 降到 512),保持 batch 16;或者开梯度累积,等效 batch 不变,只是多花一点时间。再不行换 yolov8n 这种轻量 backbone,不要一上来就用 v8x,1,458 张图根本喂不饱大模型。
4.5 现象:recall 高但 precision 低,框比虫多
原因:标注漏标或框太松。褐飞虱翅膀边缘和稻秆颜色接近,标注时容易把稻秆包进框,模型学到的框就偏大,推理时一个虫被重复框好几个,NMS 也压不干净。
解决:用 2.3 的可视化脚本随机抽 50 张训练图,重点看框有没有贴紧虫体。如果大量框明显偏大,后处理把 NMS 的 IoU 阈值从 0.5 调低到 0.4 可以缓解,但治本还是重新精修边缘样本。框太松的数据,就算 mAP 好看,用到计数场景也会系统性高估虫口密度。
5. 验证与部署:用推理结果反推数据质量,再导出边缘端模型
训练完不等于结束。先用验证命令把模型在 valid 上的表现完整压一遍,再根据误检漏检反推数据问题,最后导出 ONNX 给田间设备用。这套流程走完,你才敢把模型从电脑搬到现场。
5.1 用 val 命令批量验证:拿到每张图的置信度分布
yolo detect val \ data=/path/to/rice_bph_dataset/data.yaml \ model=/path/to/runs/detect/exp1/weights/best.pt \ conf=0.25 \ iou=0.5 \ max_det=300 \ save_json=True逻辑说明:conf 是检出置信度下限,低于这个值的框直接丢弃;iou 是 NMS 的 IoU 阈值,两个框重叠超过 50% 就合并;max_det 限制单张图最多输出几个框,田间密集场景要调大。save_json=True 会把每张图的预测框、置信度、类别写进 JSON,方便后面做漏检分析。
输出表格里重点看 per-class 的 precision、recall 和 mAP50。这份数据只有一个类,如果 mAP50 上了 0.8 但 recall 只有 0.7,说明漏检集中在部分难图,进入 5.4 做难例筛选。
5.2 阈值怎么调:conf 和 iou 在不同场景下的两套经验值
我的习惯是预警场景用高阈值,统计场景用低阈值:
| 场景 | conf | iou | max_det | 理由 |
|---|---|---|---|---|
| 虫害预警 | 0.35 | 0.5 | 100 | 宁缺毋滥,少制造误报 |
| 虫口普查 | 0.20 | 0.45 | 300 | 多召回,后处理过滤噪声 |
| 科研评估 | 0.25 | 0.5 | 300 | 与基线对齐 |
小目标框之间重叠本来就少,NMS 的 iou 不用调太高,主要靠 conf 卡误检。如果你发现同一只虫被重复框住,先把 iou 降到 0.4 试试,但优先查是不是标注框本身画太大,不然换了阈值也只是治标。
5.3 导出 ONNX:给无人机和边缘盒子用
yolo export \ model=/path/to/runs/detect/exp1/weights/best.pt \ format=onnx \ imgsz=640 \ simplify=True \ opset=12逻辑说明:ONNX 是边缘部署最常见的中间格式,可以再转到 TensorRT、OpenVINO 或 RKNN。opset=12 兼容大部分推理框架,simplify=True 会做计算图简化,去掉冗余算子。导出后用小脚本验证一次输入输出和 PyTorch 版本对齐:
import onnxruntime as ort import numpy as np sess = ort.InferenceSession("best.onnx") x = np.random.rand(1, 3, 640, 640).astype(np.float32) outs = sess.run(None, {sess.get_inputs()[0].name: x}) print([o.shape for o in outs])逻辑说明:随机生成一张假图跑一次推理,输出形状应该是 [1, 84, 8400] 这种,84 是 4 个坐标加 80 个类别再加 1 个置信度的组合。如果类别数对不上,回查 3.2 的 nc 设置。导完不验证,问题到运行时才暴露,排查成本高得多。
5.4 从 213 张测试集里挖难例,反推数据质量
验证完别只看 mAP 就收工。用 5.1 生成的 JSON 和真实标签对比,统计漏检率最高的前 20 张图,大概率是三类:强逆光下虫体颜色和稻秆混在一起、多虫重叠导致 NMS 吞框、叶片反光造成局部过曝。把这几类图单独抽到一个文件夹,回看它们的拍摄条件。
如果难例集中在某几个时段或某几块田,说明数据集在对应条件下有缺口,靠调参补不回来,只能补拍补标。这个分析过程本身就是数据质量报告的一部分,拿去跟甲方沟通时,比一个孤零零的 mAP 数字有说服力得多。
6. 进阶:从「框」到「数」,用滑窗推理把结果变成虫口密度
单张图推理只能回答「这张图里有几只虫」,农户真正关心的是「这块田现在的虫口密度是多少」。无人机拍出来的正射影像是几千像素的大图,直接整张缩放,小目标全丢;直接裁开,贴在边缘的虫体会被切成两半。滑窗推理是解决这个问题的常用方案,也是这份小目标数据集最能发挥价值的用法。
思路很简单:把大图按固定步长切成有重叠的小块,每块单独送进模型推理,再把框坐标映射回原图,最后合并去重:
import cv2 from ultralytics import YOLO model = YOLO("best.pt") img_big = cv2.imread("field_0425.jpg") H, W = img_big.shape[:2] tile_h, tile_w, overlap = 640, 640, 80 stride_h, stride_w = tile_h - overlap, tile_w - overlap all_boxes = [] for y in range(0, H, stride_h): for x in range(0, W, stride_w): x2 = min(x + tile_w, W) y2 = min(y + tile_h, H) tile = img_big[y:y2, x:x2] res = model(tile, conf=0.25, iou=0.5)[0] for box in res.boxes: cx, cy, bw, bh = box.xywh[0].tolist() all_boxes.append((x + cx, y + cy, bw, bh, float(box.conf[0])))逻辑说明:tile 尺寸对齐训练时的 imgsz,overlap 取 80 像素,保证贴边的虫体不会被裁切砍成两半。每块推理出的框中心坐标加上该块在原图上的偏移量,就还原成整图坐标。最后把所有框统一再做一次 NMS,去掉重叠区产生的重复框。参数上建议 conf 固定,tile 640,overlap 80;虫体特别密的时候 overlap 提到 128,推理时间大约增加三成。
拿到每张监测图的虫口数量以后,我的习惯是每隔几天同一块田拍一次,按同样的滑窗参数统计每平方米虫数,拉成一条折线。喷药后曲线断崖下跌说明施药时机正确;两三天后反弹说明虫源没切断,要复查周边田块。这个比单张图判断有没有虫更能回答「要不要打药、什么时候打药」。
说到这里必须讲一个教训:滑窗参数一旦定了就不要改,尤其 conf。有一次我把 conf 从 0.25 调到 0.15 想多召回一些,结果虫口密度基线直接翻了一倍,排查了很久才发现是阈值变动导致的,不是田里的虫变多了。从那以后,所有计数实验我都强制固定推理参数,同一批监测数据用完全相同的 conf、tile、overlap,前后才可比。希望这个坑你能避开,也希望这套从标注到部署的流程帮到你。
本文还有配套的精品资源,点击获取