ARTICLE DETAIL

资讯详情

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

农作物病害实例分割数据集:从COCO标注转换到YOLOv8训练落地全流程

农作物病害实例分割数据集:从COCO标注转换到YOLOv8训练落地全流程 简介这份农作物病害实例分割数据集面向农业AI诊断、精准农业监测及植物病理学交叉研究场景适合从事YOLO实例分割任务开发与农业智能检测的算法工程师、科研人员及农业院校师生使用。资源包共582个文件以290张jpg图像与290个txt标注文件为主另含1个yaml配置文件与1份docx说明文档压缩包约58.81MB目录结构清晰便于按训练、验证、测试划分直接调用。数据集总计290张图片其中训练集258张、验证集23张、测试集9张覆盖细菌性枯萎病、褐条病、花叶病三类典型病害标注采用YOLO多边形格式精确勾勒病害区域边界可直接适配主流实例分割框架。已有89人学习关注。读者可借助该数据快速搭建病害识别与分割模型用于早期诊断、物联网实时监测及教学实训为作物健康管理与产量提升提供可靠的数据支撑。1. 农作物病害实例分割数据集从标注格式到训练落地的完整拆解拿到一个名为农作物病害实例分割数据集_20251117_001317.zip的压缩包很多人的第一反应是解压、翻目录、找图片然后卡在标注文件上——COCO JSON 还是 YOLO txt类别 id 从 0 还是从 1 开始mask 是 polygon 还是 RLE这些问题不搞清楚后面yolov8训练自己的数据集时 loss 不降、mAP 为零你连从哪查都不知道。农作物病害的实例分割和通用目标检测不一样病斑边界模糊、同类病斑大小差异极大、叶片遮挡严重标注质量直接决定模型上限。这篇笔记按「数据集结构 → 格式转换 → 训练配置 → 避坑排查 → 进阶技巧」的顺序把从解压到跑通第一条验证曲线的路径讲清楚适合手里已经拿到这个包、准备用 YOLO 系列做实例分割的从业者。2. 先看清数据集结构农作物病害实例分割的标注长什么样2.1 实例分割与语义分割、目标检测的本质区别农作物病害场景里三种标注方式经常被混用。目标检测只给病斑一个矩形框框内大量健康组织被算进正样本语义分割给每个像素一个类别但同一张叶片上两个相邻病斑会被合并成一个区域实例分割要求每个病斑单独一个 mask既能算像素级面积又能数出病斑个数。这就是为什么做病害严重度分级、病斑计数、单株发病率统计时实例分割是绕不开的。从标注成本看实例分割最贵。一张 1920×1080 的叶片图熟练标注员画 20 个病斑 polygon 大约需要 8 到 15 分钟。所以拿到一个现成的农作物病害实例分割数据集第一件事不是急着训练而是评估标注密度和一致性——这决定了你后面要不要做数据清洗。常见做法是先用脚本统计每张图的实例数分布。如果某张图实例数超过 200大概率是标注员把整片叶子的坏死区域一次性框了这种样本在训练时会主导梯度需要单独处理。2.2 解压后先跑一遍目录与标注统计假设压缩包解压到crop_disease_seg/典型结构是images/加annotations/标注可能是 COCO 格式的单个 JSON也可能是每张图一个 txt。先写一个统计脚本把图片数、标注文件数、类别分布、实例数分布、图片尺寸分布全部打出来。import json, os from collections import Counter from PIL import Image root crop_disease_seg ann_file os.path.join(root, annotations, instances.json) with open(ann_file, r, encodingutf-8) as f: coco json.load(f) # 类别分布 cat_map {c[id]: c[name] for c in coco[categories]} cat_counter Counter() for ann in coco[annotations]: cat_counter[cat_map[ann[category_id]]] 1 # 每图实例数 img_inst Counter() for ann in coco[annotations]: img_inst[ann[image_id]] 1 # 图片尺寸分布 size_counter Counter() for img in coco[images]: size_counter[(img[width], img[height])] 1 print(类别分布:, cat_counter) print(实例数 Top10:, img_inst.most_common(10)) print(尺寸分布:, size_counter.most_common(5)) print(图片总数:, len(coco[images]), 标注总数:, len(coco[annotations]))这段脚本输出三个关键信息。类别分布告诉你有没有长尾类别——如果某个病害只有几十个实例训练时基本学不动要么合并类别要么做过采样。实例数 Top10 帮你定位异常样本超过 150 个实例的图建议单独看。尺寸分布决定你训练时的imgsz参数如果原图普遍是 4000×3000直接 resize 到 640 会丢失大量病斑细节。参数说明cat_map把 category_id 映射成可读名称注意 COCO 的 id 不保证连续img_inst用 image_id 聚合能快速发现标注密度异常。如果标注是 YOLO txt 格式把读取逻辑换成逐行解析class x_center y_center w h加 polygon 点即可统计思路一致。2.3 判断标注是 polygon 还是 RLE决定后续转换路径COCO 的segmentation字段有两种polygon 是点列表的列表RLE 是{counts: ..., size: ...}的字典。农作物病害数据集里 polygon 占多数因为标注工具labelme、CVAT默认导出 polygon。但如果你看到 RLE说明标注方用了 mask 转 RLE 的流程转换到 YOLO 格式时需要先解码成二值 mask 再提取轮廓。from pycocotools import mask as mask_utils import numpy as np def seg_to_mask(seg, h, w): if isinstance(seg, list): # polygon rles mask_utils.frPyObjects(seg, h, w) rle mask_utils.merge(rles) else: # RLE rle seg return mask_utils.decode(rle) # 返回 h×w 的 0/1 数组 # 示例取第一条标注 ann coco[annotations][0] img_info next(i for i in coco[images] if i[id] ann[image_id]) m seg_to_mask(ann[segmentation], img_info[height], img_info[width]) print(mask 形状:, m.shape, 前景像素:, int(m.sum()))逻辑说明frPyObjects把 polygon 转成 RLEmerge合并同一实例的多个 polygon一个病斑可能被标成多个不连续区域decode输出二值数组。参数上注意h, w必须和原图一致否则 mask 会错位。这一步是后面转 YOLO 格式的基础先确认能正确解码再往下走。3. 把 COCO 标注转成 YOLO 实例分割格式脚本与四个边界坑3.1 YOLO 分割标签的格式约定YOLO 系列v5/v8/v11的实例分割标签是每张图一个 txt每行一个实例格式为class_id x1 y1 x2 y2 ... xn yn所有坐标是归一化到 0 到 1 的相对值class_id 从 0 开始连续。注意这里没有宽高只有多边形点序列点数不固定。和检测格式class x_center y_center w h的区别就在这——分割标签直接存轮廓点。转换的核心步骤读 COCO JSON → 按 image_id 分组 → 对每条标注取 segmentation → 解码成 mask 或直接用 polygon 点 → 归一化坐标 → 写入对应 txt。听起来简单但边界情况很多。3.2 完整转换脚本import json, os import numpy as np from pycocotools import mask as mask_utils from PIL import Image root crop_disease_seg out_dir crop_disease_seg/labels os.makedirs(out_dir, exist_okTrue) with open(os.path.join(root, annotations, instances.json), r, encodingutf-8) as f: coco json.load(f) # 类别 id 重映射为 0 起始连续 cats sorted(coco[categories], keylambda x: x[id]) cat_id_map {c[id]: idx for idx, c in enumerate(cats)} img_map {img[id]: img for img in coco[images]} ann_by_img {} for ann in coco[annotations]: ann_by_img.setdefault(ann[image_id], []).append(ann) def polygon_to_yolo(seg, w, h): polygon 点列表 - 归一化点序列 pts [] for poly in seg: for i in range(0, len(poly), 2): x min(max(poly[i] / w, 0.0), 1.0) y min(max(poly[i 1] / h, 0.0), 1.0) pts.append((x, y)) return pts def rle_to_polygon(seg, h, w): RLE - 轮廓点用 pycocotools 解码后取边界 m mask_utils.decode(seg) contours mask_utils.encode(np.asfortranarray(m)) # 简化处理直接用 decode 后的 mask 提取外轮廓点 from skimage.measure import find_contours cs find_contours(m, 0.5) if not cs: return [] c max(cs, keylen) return [(min(max(x / w, 0.0), 1.0), min(max(y / h, 0.0), 1.0)) for y, x in c] for img_id, anns in ann_by_img.items(): info img_map[img_id] w, h info[width], info[height] lines [] for ann in anns: cid cat_id_map[ann[category_id]] seg ann[segmentation] if isinstance(seg, list): pts polygon_to_yolo(seg, w, h) else: pts rle_to_polygon(seg, h, w) if len(pts) 3: continue # 少于 3 个点无法构成多边形 coord_str .join(f{x:.6f} {y:.6f} for x, y in pts) lines.append(f{cid} {coord_str}) # 文件名与图片同名扩展名换 txt stem os.path.splitext(info[file_name])[0] with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines))逻辑说明cat_id_map把原始类别 id 重映射为 0 起始这是 YOLO 的硬性要求不重映射会导致类别索引越界或错位。polygon_to_yolo里做了 clamp 到 0 到 1因为有些标注点会略微超出图片边界。rle_to_polygon用find_contours提取外轮廓注意返回的坐标是(y, x)顺序写的时候要反过来。len(pts) 3的过滤是必要的退化多边形会让训练报错。参数说明坐标保留 6 位小数足够YOLO 内部会再处理如果原图很大超过 4000 像素归一化精度不受影响但训练时 resize 会损失细节后面会讲怎么处理。3.3 四个边界坑空标注、重叠实例、类别不连续、文件名冲突第一个坑是空标注图片。COCO 里有些图片没有任何 annotation转换后会生成空 txt。YOLO 训练时遇到空 txt 会当作负样本如果负样本比例过高模型会偏向预测背景。解决方法是统计空 txt 比例超过 10% 就考虑剔除或降低采样权重。第二个坑是重叠实例。同一片叶子上两个病斑如果标注时重叠转成 polygon 后两个实例的 mask 会互相覆盖。YOLO 分割训练时每个实例独立计算 loss重叠区域会被两个实例同时认领导致边界模糊。常见做法是保留重叠但在评估时用 mask IoU 而不是 box IoU能部分缓解。第三个坑是类别不连续。COCO 的 category_id 可能是 1、3、7、12 这种跳号直接当 YOLO 的 class_id 用会报错。必须像脚本里那样重映射为 0、1、2、3。重映射后要同步更新data.yaml里的names列表顺序。第四个坑是文件名冲突。不同子目录下可能有同名图片比如IMG_001.jpg出现在两个批次里转换后 txt 会互相覆盖。解决方法是转换时保留相对路径或用 image_id 作为文件名再在data.yaml里对应好。提示转换完成后随机抽 5 张图用可视化脚本把 mask 画出来和原图叠加确认坐标没偏移、类别没错位。这一步花 10 分钟能省后面几小时的排查。4. 用 YOLOv8 训练农作物病害实例分割配置、参数与首轮验证4.1 数据集配置文件 data.yaml 怎么写YOLOv8 分割训练需要一个 yaml 描述数据路径和类别。典型内容path: /data/crop_disease_seg train: images/train val: images/val test: images/test names: 0: early_blight 1: late_blight 2: leaf_rust 3: bacterial_spotpath是根目录train/val/test是相对路径下的图片目录YOLO 会自动在同级找labels/下的同名 txt。names的顺序必须和转换脚本里的cat_id_map一致错一个位置整个类别就乱了。如果类别名有中文建议用英文或拼音避免编码问题。4.2 训练命令与关键参数yolo segment train \ data/data/crop_disease_seg/data.yaml \ modelyolov8s-seg.pt \ epochs150 \ imgsz1024 \ batch8 \ lr00.01 \ lrf0.01 \ patience30 \ mosaic1.0 \ copy_paste0.3 \ device0 \ projectruns/seg \ namecrop_disease_v1参数逐个说。modelyolov8s-seg.pt是分割预训练权重s 是 small如果显存够可以换 m 或 l。imgsz1024是关键——农作物病斑普遍偏小640 下很多病斑只剩几个像素mask 质量会很差。1024 是精度和显存的平衡点实测比 640 的 mask mAP 高 5 到 8 个点。batch8配合 1024 输入24G 显存基本吃满不够就降到 4 或用梯度累积。lr00.01是初始学习率分割任务比检测对学习率更敏感太大 mask 会崩。patience30是早停验证集 30 轮不涨就停。mosaic1.0开启马赛克增强对小目标有效但病害分割里要注意——马赛克拼接后病斑可能被截断如果发现验证集 mask 质量下降可以降到 0.5。copy_paste0.3是分割特有的增强把实例 mask 复制粘贴到其他图上对增加病斑样本很有效但粘贴比例太高会导致背景不自然。4.3 首轮训练该看哪些曲线训练启动后重点盯三个指标train/seg_loss、metrics/mAP50(M)、metrics/mAP50-95(M)。M 后缀表示 mask 指标。正常情况下 seg_loss 在前 10 轮快速下降mAP50(M) 在 20 轮左右开始爬升。如果 seg_loss 不降先查标签格式——最常见的是坐标没归一化或 class_id 越界。如果 mAP50(M) 卡在很低的值不动用验证脚本单独跑几张图看预测 mask。YOLO 自带yolo segment predict可以输出可视化结果。常见问题是模型把所有病斑预测成一个大 mask这通常是标注里实例边界不清导致的需要回头清洗标注。注意农作物病害数据集如果类别不平衡严重验证集上小类别 mAP 会很低但整体 mAP 看起来还行。建议单独看每个类别的指标别被平均值骗了。5. 避坑与排查训练农作物病害分割模型时最容易翻车的五件事5.1 现象mask mAP 始终为 0box mAP 正常原因标签格式错误。YOLO 分割要求每行至少 3 个点6 个坐标值如果转换时某些实例只剩 1 到 2 个点被写入训练时这些行会被跳过或报错导致 mask 分支学不到东西。另一个可能是data.yaml里names数量和实际 class_id 不匹配。解决用脚本检查所有 txt统计每行的点数分布过滤掉少于 3 个点的行。同时确认names列表长度等于最大 class_id 加 1。5.2 现象训练 loss 震荡剧烈mask 边界锯齿严重原因学习率过大或 batch 太小。分割任务对 batch 敏感batch 小于 4 时 BN 层统计不稳定loss 会跳。另外imgsz设太小比如 320时病斑 mask 被压缩到几个像素边界自然锯齿。解决把lr0降到 0.005 或 0.001batch 至少 8imgsz不低于 640病斑小的场景直接上 1024。如果显存不够用accumulate做梯度累积等效增大 batch。5.3 现象验证集表现好但实际田间图片预测很差原因数据集里的图片可能是实验室拍摄或特定角度背景单一、光照均匀。田间图片有复杂背景、阴影、水滴、虫咬痕迹分布差异大。这是农作物病害数据集的通病。解决训练时加强增强特别是hsv_h、hsv_s、hsv_v调大模拟不同光照加degrees旋转和translate平移如果条件允许混入少量田间图片做微调。评估时不要只看验证集单独留一批田间图做测试。5.4 现象某些类别实例被合并成一个大 mask原因标注时相邻病斑边界没分清或者转换时多个 polygon 被 merge 成一个。COCO 的merge操作会把同一实例的多个 polygon 合并但如果两个不同实例的 polygon 被误标为同一 instance_id就会合并。解决回到原始标注检查 instance_id 是否唯一。如果是标注问题只能重新标注或接受这个误差。训练时可以调低overlap_mask参数YOLO 默认 True让重叠区域只归一个实例。5.5 现象训练到后期 mask loss 突然飙升原因过拟合。农作物病害数据集通常规模不大几千张图训练 150 轮以上很容易过拟合。表现为训练 loss 持续降但验证 loss 反弹。解决开早停patience30加weight_decay0.0005加dropout如果模型支持减少epochs。另外检查验证集和训练集有没有图片重复——同一张图出现在两边会让验证指标虚高掩盖过拟合。6. 进阶用 mask 面积做病害严重度分级与模型验证训练出分割模型只是第一步农作物病害场景真正有价值的是从 mask 算出可量化的指标。我一般会在预测后加一个后处理脚本统计每张图的病斑总面积占比、病斑个数、最大病斑面积然后按阈值分级。import numpy as np from ultralytics import YOLO model YOLO(runs/seg/crop_disease_v1/weights/best.pt) results model.predict(test_field.jpg, imgsz1024, conf0.4, iou0.5, retina_masksTrue) r results[0] h, w r.orig_shape total_area h * w disease_area 0 count 0 max_area 0 if r.masks is not None: for m in r.masks.data.cpu().numpy(): area int(m.sum()) disease_area area count 1 max_area max(max_area, area) ratio disease_area / total_area if ratio 0.05: level 健康 elif ratio 0.15: level 轻度 elif ratio 0.30: level 中度 else: level 重度 print(f病斑数: {count}, 面积占比: {ratio:.2%}, 最大病斑: {max_area}, 等级: {level})逻辑说明retina_masksTrue让 mask 分辨率对齐原图否则 mask 是低分辨率的面积统计会偏小。r.masks.data是每个实例的二值 masksum()得到像素数。面积占比分级阈值可以根据具体作物调整这里给的是通用参考。参数说明conf0.4是置信度阈值病害分割里建议不要设太低否则健康组织上的纹理会被误检成病斑。iou0.5是 NMS 阈值病斑密集时可以降到 0.3 减少重叠。验证模型是否可靠我习惯做两件事。一是拿一批人工标注过的图用模型预测后算 mask IoU低于 0.6 的样本单独看通常是标注边界模糊或模型漏检。二是把同一张图在不同光照、不同角度下拍多次看预测的面积占比波动波动超过 10% 说明模型对光照敏感需要补数据。血泪经验是别在验证集指标好看就急着上线。农作物病害的田间环境比数据集复杂得多我见过验证集 mAP50(M) 到 0.85 的模型在真实大棚里漏检率超过 40%。后来固定了一个习惯——每训完一版拿手机去田里拍 20 张不同条件下的图跑一遍看效果这比任何指标都直接。希望帮到你。本文还有配套的精品资源点击获取
返回列表