☰
农作物病害实例分割数据集:从标注格式到YOLOv8训练落地
2026/10/8 13:45:09 网站建设 项目流程

简介:这份农作物病害实例分割数据集面向农业AI诊断、精准农业监测及植物病理学交叉研究场景,适合从事YOLO实例分割任务开发与农业图像算法验证的工程师、科研人员及农业院校师生使用。资源包共582个文件,以290张jpg图像与290个txt标注文件为主体,另含1个yaml配置和1个docx说明文档,压缩包约58.81MB,标注采用YOLO多边形格式,精确勾勒病害区域边界。数据集总计290张图片,按训练集258张、验证集23张、测试集9张划分,覆盖细菌性枯萎病、褐条病与花叶病三类典型病害,兼顾细菌性与病毒性样本,类别代表性较强。已有89人学习关注。读者可借助其直接开展实例分割模型训练与部署,用于病害区域自动识别、早期诊断系统搭建及农业物联网实时监测,也可作为教学与科研中的标注范例和实验基线,降低数据采集与标注成本。

1. 农作物病害实例分割数据集:从标注格式到 YOLOv8 训练落地的完整路径

拿到一个名为农作物病害实例分割数据集_20251117_001317.zip的压缩包,第一反应不应该是直接解压丢进模型,而是先搞清楚它到底以什么格式标注、类别怎么定义、掩码是多边形还是 RLE。农作物病害识别这件事,分类模型只能告诉你「这片叶子有病」,目标检测能框出病斑位置,但只有实例分割才能精确到每一个病斑的像素边界——这在计算病害面积占比、评估严重程度、做精准施药决策时是刚需。这个数据集面向的就是这类场景:叶片上的多个独立病斑需要被逐个分割出来,而不是笼统地标一个框。适合已经跑通过 YOLOv8 检测任务、想往分割方向升级的工程师,也适合农业 AI 方向需要快速验证实例分割方案可行性的团队。下面从数据集结构拆解开始,一步步走到训练、评估和部署。

2. 拆开压缩包:农作物病害实例分割数据集的目录结构与标注格式判定

2.1 先看目录树再决定怎么处理

解压之后别急着写训练脚本,先花五分钟把目录结构摸清楚。常见的实例分割数据集组织方式有两种:COCO 风格的annotations/+images/分离结构,以及 YOLO 风格的images/+labels/平行结构。前者标注文件是单个 JSON,后者是每张图对应一个 TXT。

# 先看压缩包内文件列表,不解压 unzip -l 农作物病害实例分割数据集_20251117_001317.zip | head -50 # 解压到指定目录 mkdir -p ./agri_disease_seg unzip 农作物病害实例分割数据集_20251117_001317.zip -d ./agri_disease_seg # 查看目录树,只看两层 find ./agri_disease_seg -maxdepth 2 -type d | sort

逻辑说明:unzip -l先窥探内容,避免解压出一堆无关文件。find -maxdepth 2控制输出深度,快速判断是 COCO 还是 YOLO 结构。如果看到annotations/instances.json就是 COCO 风格;如果看到labels/下全是.txt就是 YOLO 风格。

参数说明:-d指定解压目标目录,建议单独建目录避免污染当前工作区。-maxdepth根据实际嵌套深度调整,一般 2 到 3 层足够判断结构。

2.2 判定标注格式:多边形、RLE 还是 YOLO 分割行

实例分割的标注格式直接决定后续转换脚本怎么写。COCO 格式的segmentation字段可能是多边形点列表([[x1,y1,x2,y2,...]])或 RLE(Run-Length Encoding)。YOLO 分割格式则是每行class_id x1 y1 x2 y2 ...,坐标是归一化后的多边形点。

import json import os # 如果是 COCO 格式,检查 segmentation 字段类型 ann_path = "./agri_disease_seg/annotations/instances.json" if os.path.exists(ann_path): with open(ann_path, "r") as f: coco = json.load(f) # 看第一條标注的 segmentation 类型 seg = coco["annotations"][0]["segmentation"] if isinstance(seg, list): print("多边形格式,点数:", len(seg[0]) // 2) elif isinstance(seg, dict): print("RLE 格式,size:", seg.get("size")) # 统计类别 for cat in coco["categories"]: print(cat["id"], cat["name"]) else: # 检查 YOLO 格式 label_dir = "./agri_disease_seg/labels" if os.path.exists(label_dir): sample = os.listdir(label_dir)[0] with open(os.path.join(label_dir, sample)) as f: line = f.readline().strip() parts = line.split() print("类别ID:", parts[0], "多边形点数:", (len(parts) - 1) // 2)

逻辑说明:COCO 的segmentation如果是 list 就是多边形,每个子列表是一串x,y交替的点;如果是 dict 就是 RLE,需要pycocotools解码。YOLO 格式直接读一行就能判断:第一个数是类别 ID,后面全是归一化坐标。

参数说明:len(seg[0]) // 2得到多边形顶点数,顶点太少(比如少于 3 个)说明标注质量可能有问题。类别统计用来确认病害种类数量,常见的有早疫病、晚疫病、叶斑病等。

2.3 类别分布与样本均衡性检查

在动手训练之前,必须知道每个类别有多少个实例。农作物病害数据集经常出现长尾分布——健康叶片样本多,某种罕见病害只有几十个实例。不查清楚就训练,模型对少数类基本没反应。

from collections import Counter # 假设已加载 COCO 格式 cat_counter = Counter() for ann in coco["annotations"]: cat_counter[ann["category_id"]] += 1 id2name = {c["id"]: c["name"] for c in coco["categories"]} for cid, cnt in cat_counter.most_common(): print(f"{id2name[cid]}: {cnt} 个实例") # 同时统计每张图的实例数,判断是否有多病斑同图的情况 img_ann_count = Counter() for ann in coco["annotations"]: img_ann_count[ann["image_id"]] += 1 multi = sum(1 for v in img_ann_count.values() if v > 1) print(f"含多个病斑的图像数: {multi} / {len(coco['images'])}")

逻辑说明:Counter统计每个类别的实例总数,most_common()按数量降序排列。第二个统计看有多少张图包含多个病斑——这个比例高说明实例分割确实有必要,检测框会重叠。

参数说明:如果某个类别实例数少于 100,训练时需要过采样或加类别权重。多病斑图像占比超过 30% 就值得做实例分割而不是检测。

3. 从 COCO 到 YOLO 分割格式:转换脚本与四个边界坑

3.1 转换脚本:多边形坐标归一化与类别重映射

YOLOv8 的分割训练需要 YOLO 格式的标注:每张图一个 TXT,每行class_id x1 y1 x2 y2 ...,坐标是相对于图像宽高的归一化值。COCO 的多边形坐标是绝对像素值,需要除以宽高。

import json import os from PIL import Image def coco_to_yolo_seg(coco_json, image_dir, output_label_dir): with open(coco_json, "r") as f: coco = json.load(f) # 建立 image_id -> (file_name, width, height) 映射 img_info = {} for img in coco["images"]: img_info[img["id"]] = (img["file_name"], img["width"], img["height"]) # 类别 ID 重映射为 0 起始连续整数 cat_ids = sorted([c["id"] for c in coco["categories"]]) cat_id_map = {old: new for new, old in enumerate(cat_ids)} # 按 image_id 聚合标注 from collections import defaultdict img_anns = defaultdict(list) for ann in coco["annotations"]: img_anns[ann["image_id"]].append(ann) os.makedirs(output_label_dir, exist_ok=True) for img_id, anns in img_anns.items(): fname, w, h = img_info[img_id] label_name = os.path.splitext(fname)[0] + ".txt" lines = [] for ann in anns: seg = ann["segmentation"] if not isinstance(seg, list): continue # 跳过 RLE,需要单独处理 cls_id = cat_id_map[ann["category_id"]] for poly in seg: # 归一化并保留 6 位小数 coords = [] 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) coords.append(f"{x:.6f}") coords.append(f"{y:.6f}") if len(coords) >= 6: # 至少 3 个点 lines.append(f"{cls_id} " + " ".join(coords)) with open(os.path.join(output_label_dir, label_name), "w") as f: f.write("\n".join(lines)) print(f"转换完成,类别映射: {cat_id_map}") coco_to_yolo_seg( "./agri_disease_seg/annotations/instances.json", "./agri_disease_seg/images", "./agri_disease_seg/labels" )

逻辑说明:先建立图像 ID 到文件名和尺寸的映射,再把类别 ID 重映射为从 0 开始的连续整数——YOLO 要求类别 ID 必须连续。按图像聚合标注后逐条转换,坐标裁剪到[0,1]防止越界。

参数说明::.6f保留 6 位小数,精度足够且文件不会太大。len(coords) >= 6确保至少 3 个点,退化的多边形直接丢弃。如果遇到 RLE 格式的segmentation,需要用pycocotools的annToMask转成二值掩码再提取轮廓。

3.2 坑一:坐标越界与退化多边形

现象:转换后的 TXT 里有坐标小于 0 或大于 1,训练时报NaN loss或直接崩溃。

原因:COCO 标注中偶尔有多边形顶点超出图像边界,或者标注工具导出的坐标有浮点误差。

解决:转换时强制裁剪到[0,1],如上面脚本中的min(max(...))。同时过滤掉面积过小的多边形——计算多边形面积,小于图像面积 0.1% 的直接丢弃。

3.3 坑二:类别 ID 不连续导致训练报错

现象:YOLOv8 训练启动时报AssertionError: class id must be in range [0, nc)。

原因:COCO 的类别 ID 可能是 1, 3, 7 这种不连续的,而 YOLO 要求从 0 开始连续编号。

解决:转换时做重映射,如脚本中的cat_id_map。同时生成data.yaml时names列表的顺序必须和重映射后的 ID 一致。

3.4 坑三:图像与标签文件名不匹配

现象:训练时提示No labels found,但labels/目录下明明有文件。

原因:图像是.jpg,标签是.txt,但文件名前缀不一致——比如图像叫IMG_001.jpg,标签叫img_001.txt,大小写或前缀不同。

解决:转换脚本中用os.path.splitext(fname)[0]统一取文件名主干,确保一一对应。转换后跑一遍校验脚本,检查每个图像是否有对应标签。

4. YOLOv8 实例分割训练:data.yaml 配置与关键超参设置

4.1 data.yaml 的五个必填字段

YOLOv8 分割训练的配置文件比检测多一个task字段,且names必须与类别数严格对应。

# data.yaml path: ./agri_disease_seg # 数据集根目录 train: images/train # 训练集图像相对路径 val: images/val # 验证集图像相对路径 test: images/test # 测试集(可选) task: segment # 关键:指定分割任务 nc: 5 # 类别数,根据实际修改 names: # 类别名称,顺序必须与训练标签中的 ID 一致 0: 早疫病 1: 晚疫病 2: 叶斑病 3: 锈病 4: 健康

逻辑说明:path是根目录,train和val是相对于根目录的路径。task: segment告诉 YOLOv8 加载分割头而不是检测头。names用字典或列表都行,但顺序不能错。

参数说明:nc必须等于names的长度。如果类别数超过 10,建议用列表形式写names更紧凑。test字段可选,不写就不做测试集评估。

4.2 训练命令与 batch size 的显存估算

# 单卡训练,imgsz 根据图像实际尺寸调整 yolo segment train \ data=./data.yaml \ model=yolov8n-seg.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ device=0 \ project=agri_seg \ name=exp1

逻辑说明:model=yolov8n-seg.pt加载预训练的分割模型,n 是最小版本,显存不够就换 n,够就换 s 或 m。imgsz=640是输入尺寸,如果原始图像是 1024 以上,可以设 1024 但显存翻倍。patience=20表示 20 轮验证指标不提升就早停。

参数说明:batch=16在 8GB 显存上跑 640 尺寸的 n 模型基本够用。如果 OOM,先降 batch 到 8,再降 imgsz 到 512。lr0=0.01是初始学习率,分割任务通常比检测稍小,0.005 到 0.01 之间比较稳。

4.3 训练过程监控:看哪些指标、什么时候该停

训练启动后,终端会输出每轮的box_loss、seg_loss、cls_loss和mAP50。分割任务重点看seg_loss和mask mAP50。

# 训练完成后查看结果 ls agri_seg/exp1/ # 关键文件:results.csv(每轮指标)、weights/best.pt(最佳权重)、 # confusion_matrix.png(混淆矩阵)、val_batch0_pred.jpg(验证集预测可视化)

逻辑说明:results.csv可以用 pandas 读取画曲线,重点看metrics/mAP50(M)是否还在涨。如果seg_loss持续下降但mAP50不涨,说明过拟合,需要加数据增强或减模型复杂度。

参数说明:mAP50(M)是掩码 IoU 阈值 0.5 时的平均精度,分割任务的核心指标。mAP50-95(M)更严格,但农作物病害场景下 0.5 就够用。

5. 避坑与排查:农作物病害分割训练中最容易翻车的五个地方

5.1 坑一:验证集 mAP 很高但实际预测全是背景

现象:训练日志里mAP50(M)到了 0.8,但拿新图预测时模型输出全是背景,没有任何掩码。

原因:验证集和训练集来自同一批图像,分布太相似。模型学会了「猜」验证集的类别分布,但没有真正学到病斑的视觉特征。

解决:手动划分验证集时确保不同批次、不同光照条件的图像分开。如果数据集本身多样性不足,用albumentations做离线增强扩充训练集。预测时降低置信度阈值试试:yolo segment predict conf=0.1。

5.2 坑二:小病斑被完全忽略

现象:大块病斑分割得很准,但直径小于 20 像素的早期病斑一个都检不出来。

原因:YOLOv8 的 P3 特征图 stride 是 8,对应 640 输入下最小感受野约 8 像素。小于这个尺寸的目标在浅层特征中信息太少。

解决:把imgsz提到 1024 或 1280,让 P3 特征图覆盖更多像素。或者在数据增强中加copy_paste把小病斑复制到其他位置增加样本。如果显存不够,用yolov8s-seg换yolov8n-seg,小模型的特征提取能力更弱。

5.3 坑三:掩码边缘锯齿严重

现象:预测出的病斑掩码边缘呈锯齿状,和真实边界差距大。

原因:YOLOv8 分割头输出的掩码原型分辨率是 160x160(输入 640 时),上采样到原图尺寸时细节丢失。

解决:训练时设overlap_mask=True(默认开启),推理时用retina_masks=True获得更高分辨率的掩码。如果还不够,换yolov8x-seg或yolov8l-seg,大模型的原型掩码通道更多。

5.4 坑四:类别不平衡导致少数类完全学不到

现象:早疫病和晚疫病样本多,mAP 正常;锈病只有几十个实例,mAP 接近 0。

解决:在data.yaml同级目录建一个hyp.yaml,设置cls_pw=1.0开启类别权重。或者用WeightedRandomSampler做数据重采样。最直接的办法是离线过采样:把少数类的图像复制多份,文件名加后缀区分。

5.5 坑五:训练 loss 震荡不收敛

现象:seg_loss在 0.5 到 2.0 之间反复跳,始终不下降。

原因:学习率太大,或者 batch size 太小导致梯度噪声大。

解决:先把lr0降到 0.001 试 10 轮。如果还震荡,把batch翻倍(显存允许的话)。另外检查数据标注是否有大量错误——用yolo segment train的--verbose模式看每批数据的损失分布,异常高的批次对应的图像大概率标注有问题。

6. 掩码后处理与面积计算:把分割结果变成可用的病害评估指标

6.1 从预测掩码到病斑面积占比

训练完模型只是第一步,实际业务要的是「这片叶子上病斑占了多少面积」。YOLOv8 预测返回的masks是二值掩码,直接算像素占比就行。

from ultralytics import YOLO import numpy as np import cv2 model = YOLO("agri_seg/exp1/weights/best.pt") results = model.predict("test_leaf.jpg", conf=0.25, retina_masks=True) for r in results: if r.masks is None: print("未检测到病斑") continue # masks.data 形状 [N, H, W],N 是检测到的实例数 masks = r.masks.data.cpu().numpy() img_h, img_w = r.orig_shape total_pixels = img_h * img_w for i, mask in enumerate(masks): # 调整掩码到原图尺寸 mask_resized = cv2.resize(mask, (img_w, img_h), interpolation=cv2.INTER_NEAREST) area_ratio = mask_resized.sum() / total_pixels cls_name = r.names[int(r.boxes.cls[i])] print(f"病斑 {i}: 类别={cls_name}, 面积占比={area_ratio:.4f}")

逻辑说明:retina_masks=True让预测返回原图分辨率的掩码,避免手动上采样。r.masks.data是[N, H, W]的张量,每个通道对应一个实例的二值掩码。cv2.resize用最近邻插值保持二值性。

参数说明:conf=0.25是置信度阈值,农作物病害场景可以降到 0.15 提高召回。area_ratio就是病斑面积占比,超过 0.3 通常意味着严重感染。

6.2 多病斑合并与严重度分级

一张叶子上可能有多个同类病斑,业务上需要合并计算总感染面积。

# 按类别合并掩码 from collections import defaultdict cls_masks = defaultdict(list) for i, mask in enumerate(masks): cls_name = r.names[int(r.boxes.cls[i])] cls_masks[cls_name].append(mask) for cls_name, mask_list in cls_masks.items(): # 逻辑或合并同类掩码 merged = np.zeros_like(mask_list[0]) for m in mask_list: merged = np.logical_or(merged, m > 0.5) ratio = merged.sum() / total_pixels # 分级 if ratio < 0.05: level = "轻微" elif ratio < 0.15: level = "中度" elif ratio < 0.30: level = "重度" else: level = "极重" print(f"{cls_name}: 总面积占比={ratio:.4f}, 严重度={level}")

逻辑说明:np.logical_or合并同类掩码,避免重叠区域重复计算。分级阈值根据实际业务调整,这里给的是通用参考值。

参数说明:m > 0.5把概率掩码二值化。分级阈值需要和农学专家确认,不同作物的耐受度不同。

6.3 一个容易忽略的细节:掩码面积计算时的图像预处理还原

如果推理时用了imgsz=640但原图是 2048x2048,YOLOv8 内部会缩放。retina_masks=True会自动还原到原图尺寸,但如果你手动处理masks.data,必须自己 resize 回去。我一般直接在predict里加retina_masks=True,省掉这一步。另外注意r.orig_shape返回的是(height, width)顺序,别搞反了。

这套流程跑下来,从拿到压缩包到算出病斑面积占比,顺利的话半天能跑通。最耗时间的往往是标注格式转换和验证集划分——这两步做扎实,后面训练基本不会出大问题。我自己的习惯是转换完先拿 10 张图可视化检查一遍,确认掩码和原图对齐了再启动训练,能省下大量排查时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询