ARTICLE DETAIL

资讯详情

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

水稻虫害检测:VOC与YOLO格式转换及YOLOv8训练全攻略

水稻虫害检测:VOC与YOLO格式转换及YOLOv8训练全攻略 简介面向农业信息化与计算机视觉研究者的水稻虫害检测数据集说明文档系统梳理了VOCYOLO双格式标注的5212张高分辨率图片与6类害虫的识别标注规范帮助读者快速掌握数据组织方式、标注工具使用和类别分布为基于该数据集训练目标检测模型提供直接参考。文档详细列出了褐飞虱、绿叶蝉、稻纵卷叶螟、稻水象甲、茎螟、螺纹虫六类害虫的框数分布合计8104个标注框这部分信息可辅助判断样本均衡性并规划数据增强策略。文档共1个docx文件压缩包仅2.08MB内容涉及Pascal VOC与YOLO格式说明、labelImg标注规则、图片与标注文件夹结构等细节适合农业科研人员、算法工程师及高校学生作为开展水稻虫害智能检测研究的入口资料。目前已有118人学习/下载。1. 水稻虫害检测数据集VOCYOLO格式5212张6类别一个可以绕过采集地狱的起步盘做植保无人机或者智能虫情测报灯落地项目的人都会遇到同一个坎算法不难选难的是数据。大田作物病害虫害有强季节性一种害虫一年能拍到的时间窗口就两三个月从一个省跨到另一个省背景、光照、品种全变模型立刻掉点。这种场景下一份整理好的VOCYOLO双格式数据集把常见的稻纵卷叶螟、二化螟、褐飞虱这类虫害框选工作提前做完直接省掉大半年的采集和打标周期。5212张、6个类别的规模在目标检测里属于“小而有价值”的档位不够支撑从零训练一个检测头但足够在预训练权重上迁移学习把路线验证、指标评估、硬件部署这三件事全部跑通。下文不假设你手上有这个数据集而是按这种数据集最常见的工程路径把坐标格式换算、质量核查、训练命令、预测排错完整过一遍适合刚接手农业视觉项目的算法工程师也适合在毕业设计里需要快速出baseline的硕士生。2. VOC与YOLO格式的核心差异坐标归一化与XML到TXT的换算2.1 两种格式的字段到底差在哪很多初学者拿到“VOCYOLO格式数据集”时以为只是两种文件后缀的区别实际差异在坐标体系和元信息存储方式。VOC格式是一个XML文件对应一张图典型特征是“自描述”型。XML里用folder、filename、size记录文件名和图像宽高每个object块里写一个目标name是类别名bndbox里的xmin,ymin,xmax,ymax是绝对像素坐标下的整数图像尺寸变了标注框的位置数值就得跟着变。YOLO格式则是一张图对应一个同名TXT文件每行记录一个目标格式固定为class_id x_center y_center width height前一个是类别下标后四个是归一化浮点数数值范围在0到1之间表示相对图片宽和高的比例。这个设计让标注和图像的实际分辨率解耦模型训练时统一在网格上做预测推理时再按输入尺寸映射回像素坐标。两者互相转换的公式并不复杂但工程里最常见的报错源头不是公式而是图像宽高被读错、类别顺序错位、转换后坐标越界这三件事。2.2 VOC字段与YOLO字段的对照表维度VOC XML标注YOLO TXT标注文件名映射XML内filename字段写图名图名同名TXT如a.jpg对应a.txt坐标类型像素绝对坐标整数归一化浮点数0到1之间目标描述object块中name为类别名class_id为类别在列表中的下标宽高信息size显式写width/heightTXT里没有只能读图片头获取类别语义字符串“褐飞虱”直接可读必须依赖classes.txt或data.yaml的names顺序一个容易忽视的点是YOLO格式的class_id本身没有语义单独看一个TXT文件用户无法知道第一列数字代表哪个类别。因此双格式数据集里几乎一定附带一份classes.txt它的行顺序就是类别的编号顺序转换脚本和训练配置都以此为准这个顺序一旦在中间环节被改乱模型会在训练表现正常的情况下输出错位标签。2.3 用Python脚本完成VOC到YOLO的批量转换拿到双格式数据集第一件事不是训练而是自己跑一遍格式互转确认坐标转换没有偏差。下面这段脚本不需要额外依赖只用标准库xml.etree.ElementTree和cv2读图验证宽高适用于大部分VOC目录结构。import cv2 import xml.etree.ElementTree as ET from pathlib import Path voc_img_dir Path(VOC/images) voc_xml_dir Path(VOC/annotations) yolo_label_dir Path(yolo/labels) yolo_label_dir.mkdir(parentsTrue, exist_okTrue) # 类别顺序必须与训练时 data.yaml 的 names 完全一致 class_names [二化螟, 稻纵卷叶螟, 褐飞虱, 白背飞虱, 稻蝗, 黏虫] for xml_path in sorted(voc_xml_dir.glob(*.xml)): tree ET.parse(xml_path) root tree.getroot() # 先从 XML 的 size 节点读取宽高 size root.find(size) xml_w int(size.find(width).text) xml_h int(size.find(height).text) # 再用 cv2 读真实图片宽高防止XML与实际图不符 img_path voc_img_dir / root.find(filename).text img_bgr cv2.imread(str(img_path)) real_h, real_w img_bgr.shape[:2] if xml_w ! real_w or xml_h ! real_h: img_w, img_h real_w, real_h else: img_w, img_h xml_w, xml_h lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_names: print(f跳过未定义类别: {name} in {xml_path.name}) continue cls_id class_names.index(name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化到 [0,1]保留6位小数 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path yolo_label_dir / (xml_path.stem .txt) out_path.write_text(\n.join(lines), encodingutf-8)这段脚本的逻辑并不复杂但几个细节决定了转换的可靠性。第一类别名在比对前做了strip()避免XML里带空格或换行导致匹配失败第二宽高优先采用cv2.imread读出的真实值因为很多数据集经过截图、压缩软件二次处理后XML里的size很可能和实际图片不一致第三坐标保留6位小数在640x640的训练尺度下误差远小于一个像素漂移可以忽略。class_names的顺序是整条链路的核心训练YAML里的names、转回VOC时的名称映射、最终部署时的标签字典都要依赖这个列表。2.4 转换时最容易出错的三处第一空XML会产生空TXT文件。YOLO训练管线遇到空标签文件时通常会把它当作纯净背景图处理如果这类图占比过高模型会倾向于把所有目标漏掉。转换完成后应统计空TXT数量超过总数3%就要回到原始标注检查是不是漏标。第二坐标越界。许多标注工具允许标注框超出图像边界xmin可能为负数xmax可能大于图宽归一化后出现大于1或小于0的浮点数训练到Mosaic增强阶段会直接报错。常见的兜底做法是转换后对所有坐标做clip把坐标约束回[0,1]。第三类别字典不一致。二化螟和二化蜢这种因输入法产生的同音错字在XML里是合法的不同字符串但不在类别表里转换脚本会跳过它导致图像里大量真实目标被丢弃。建议脚本里加一行集合差输出把“XML里有、类别表里没有”的标签名列出来而不是静默忽略。3. 5212张6类别的数据规模意味着什么不只是个数3.1 图片数量和实例数要分开看5212张图这个数字在YOLO生态里属于“够用但不足以自由发挥”的量级真正决定模型上限的不是图片张数而是目标实例总数。如果每张图平均只有1到2个目标六个类别分下来每类不过一千出头的正样本类别不均衡会立刻显现如果每张图平均有5个以上目标整个数据集能提供两三万个学习样本足够让检测头在固定场景下收敛到一个可用的局部最优。水稻虫害场景还有自身的尺度问题。稻飞虱、稻蓟马这类小型害虫单框可能只有几十个像素而稻蝗、二化螟成虫的框可以占到图像面积的十几分之一。大小目标混合在一个数据集里模型会自然偏向学大目标的特征因为大目标贡献的梯度更稳定。所以拿到数据后先看框面积分布不要一上来就把imgsz设成416或320去赶训练速度小目标的特征在小分辨率下会被直接压没AP曲线会非常难看。3.2 用一段统计代码摸清数据集底细准备data.yaml之前应该对YOLO格式的标签目录做一次整体统计搞清楚每个类别的实例数、每张图的目标数、框面积占比和坐标合法性。下面这段代码会输出四类关键指标。from pathlib import Path import numpy as np label_dir Path(yolo/labels) cls_count np.zeros(6, dtypeint) obj_per_img [] areas [] for txt in sorted(label_dir.glob(*.txt)): lines [line.strip() for line in txt.read_text().strip().splitlines() if line.strip()] obj_per_img.append(len(lines)) for line in lines: parts line.split() cls_id int(parts[0]) cx, cy, w, h map(float, parts[1:]) cls_count[cls_id] 1 if w 0 or h 0 or cx 0 or cy 0 or cx 1 or cy 1: print(f非法框: {txt.name} - {line}) areas.append(w * h) print(类别实例数:, cls_count.tolist()) print(每图目标数 平均:%.2f 中位:%.0f 最大:%d % ( np.mean(obj_per_img), np.median(obj_per_img), max(obj_per_img))) print(框面积占比分位:, np.percentile(areas, [50, 90, 99]).round(4).tolist())这段统计能把三类问题暴露出来。一是空TXT如果对应图片存在但里的TXT字节数为空在YOLO训练里这类图会被当作背景占比过高会让模型倾向输出“无目标”的默认结果二是坐标非法出现负数或大于1的框说明上一步转换有遗漏的越界情况三是面积分位如果90分位和50分位相差两个数量级说明小目标和大目标混得非常厉害需要为小目标单独设计训练策略。检查项统计方式异常阈值对应处理空标注文件文件大小或行数为0超过总图数3%补齐标注或移入背景集类别实例数按class_id累加最少类与最多类比值低于1:10对少数类做copy-paste增强框面积占比计算w*h50分位小于0.01提高imgsz或使用SAHI切片推理坐标合法性检测是否在[0,1]出现任一越界值回退到VOC脚本重新clip3.3 训练集和验证集划分的干活原则5212张的规模不支持直接随机划分。大田虫害数据通常来自连续视频抽帧或同一批田块的连拍相邻帧高度相似随机划分会把几乎一摸一样的图同时放进训练集和验证集训练时的mAP虚高一旦换到新的田块立刻原形毕露。常见做法是提前按文件名前缀或拍摄目录把图片分组再以组为单位划分保证同一场景的所有画面只存在于其中一个集合。推荐比例是8:2或9:1。如果后续要调超参可以把验证集的一半单独留下来做测试集但前提是训练、验证、测试三者的类别分布保持一致。一个简单的约束是每个类别在任一集合中的占比与全局占比的偏差不超过5个百分点最稀有的类别在验证集里至少有30个实例否则它的AP置信区间会宽到没有参考意义。有了这个层面的数据认识才谈得上把数据集引入YOLO训练流程。4. 用5212张双格式数据集处理成YOLOv8可训练的最小工程4.1 一个能直接跑起来的目录结构ultralytics的YOLO生态已经统一了训练和推理入口处理数据集用于yolov8训练时只需要把图片和标签按固定目录放好。规范的目录结构如下。yolo_dataset/ ├── data.yaml ├── images/ │ ├── train/ (4169张) │ └── val/ (1043张) └── labels/ ├── train/ (与images/train同名) └── val/ (与images/val同名)labels目录里每个txt的文件名必须和对应图片完全一致包括后缀的差别都要规避比如a.jpg对应a.txt不能出现a.jpg.txt这类衍生文件名。训练过程中YOLO按文件名前缀查找标注找不到就把这张图当作背景图处理不会报错但会静默影响精度。下面是一份适用于本数据集的data.yaml类别顺序必须与之前转换脚本里的class_names保持一致。path: /absolute/path/to/yolo_dataset train: images/train val: images/val nc: 6 names: 0: 二化螟 1: 稻纵卷叶螟 2: 褐飞虱 3: 白背飞虱 4: 稻蝗 5: 黏虫path字段推荐写绝对路径相对路径会受当前工作目录影响很多人在这里踩坑报错信息是train dataset not found。nc必须和names列表的实际长度一致如果写成了5训练会在数据加载阶段报出标签索引越界的错误这个错误提示比较隐晦常见于类别数量被手动改过的情况。4.2 yolov8训练自己的数据集命令最少要控制的参数安装ultralytics之后训练自己的数据集只需要一条命令。以下是我在6类别小目标数据集上常用的起点命令兼顾收敛速度和显存占用。pip install ultralytics yolo train modelyolov8s.pt \ data/absolute/path/to/yolo_dataset/data.yaml \ epochs120 \ imgsz640 \ batch16 \ optimizerAdamW \ lr00.001 \ patience20 \ cacheTruemodelyolov8s.pt会从COCO预训练权重起步对5212张的小数据量来说迁移学习的效果远好于从随机初始化开始训练。imgsz640保持默认目的是保留小虫目标的像素细节不要为了提速降到416。batch16是8GB显存下的安全值如果训练中报CUDA out of memory先降batch再考虑降imgsz。optimizerAdamW配合lr00.001在小数据集上比默认SGD收敛更快也更容易处理类别不均衡带来的损失震荡。参数建议值说明modelyolov8s.pt小目标场景选s规格速度和精度平衡epochs120配合patience早停防止过拟合imgsz640低于512会丢失稻飞虱类小目标batch16/8/4按显存减半调整optimizerAdamW小数据集收敛稳定lr00.001微调预训练权重时不要用默认0.01cacheTrue图片预加载到内存提速明显patience20连续20轮验证集不提升则早停注意modelyolov8s.pt会自动联网下载权重离线环境会卡在下载步骤。提前把权重文件放到当前目录或注释掉自动下载逻辑即可。4.3 训练日志里的yolo损失函数看什么训练过程中终端会打印三类损失box_loss、cls_loss、dfl_loss它们的数值不能横向比较只能看各自趋势。box_loss衡量预测框和真实框的回归误差小目标多的数据集上它收敛慢是正常的重点看是否持续下降而不震荡。cls_loss衡量分类错误如果某一类长期压在数值高位基本能定位到那类标签噪声大。dfl_loss负责框边界分布的学习它剧烈抖动通常会伴随预测框偏移常见处理是降低fl_gamma或检查标注框边界是否有系统性的偏移。训练结束后runs/detect/train/weights/目录下会生成best.pt和last.pt两个权重best.pt是验证集mAP最优的权重部署和预测都优先选它。验证指标里重点看每个类别的单独AP对虫害检测来说mAP50权重高于mAP50-95因为小目标的框很难达到0.75的IoU阈值mAP50-95被小目标拉低是正常现象不代表模型不可用。4.4 测试图片时调整置信度门限的正确姿势训练结束后立刻跑一次预测闭环用验证集或真实田块照片检验模型表现。最基本的预测命令如下。yolo predict modelruns/detect/train/weights/best.pt \ sourcetest_images/ \ conf0.25 \ iou0.5 \ saveTrue \ save_txtTrueconf0.25表示只保留置信度高于0.25的预测框对稻飞虱这类密集小目标0.25的门限往往偏高适当降到0.1能捞回更多真目标如果误检多把conf提高到0.4到0.5。iou0.5是NMS去重的阈值目标密集时建议提高到0.6避免同一只虫被输出两个重叠框。save_txtTrue会把预测结果以YOLO格式落到runs/detect/predict/labels/方便回到标注工具复查。5. 预测后回灌VOC用双格式优势做标注修正闭环5.1 把预测TXT转回VOC直接在LabelImg里复查模型预测出的TXT是YOLO格式而人工复查最顺手的工具仍然是LabelImg这类以VOC为默认工作格式的标注器。双格式数据集的优势在这里体现出来预测结果转回XML修正完再转回TXT全程不改变原始数据集的结构。转换思路和第2章相反从归一化坐标乘回像素宽高。import cv2 from pathlib import Path import xml.etree.ElementTree as ET src_dir Path(runs/detect/predict/labels) img_dir Path(test_images) out_dir Path(review_xml) out_dir.mkdir(exist_okTrue) names [二化螟, 稻纵卷叶螟, 褐飞虱, 白背飞虱, 稻蝗, 黏虫] for txt in src_dir.glob(*.txt): img_path img_dir / (txt.stem .jpg) h, w cv2.imread(str(img_path)).shape[:2] root ET.Element(annotation) ET.SubElement(root, filename).text txt.stem .jpg size ET.SubElement(root, size) ET.SubElement(size, width).text str(w) ET.SubElement(size, height).text str(h) for line in txt.read_text().strip().splitlines(): cid, cx, cy, bw, bh map(float, line.split()) # 还原像素坐标并裁剪到有效图像范围内 xmin int(max(0, (cx - bw / 2) * w)) ymin int(max(0, (cy - bh / 2) * h)) xmax int(min(w - 1, (cx bw / 2) * w)) ymax int(min(h - 1, (cy bh / 2) * h)) obj ET.SubElement(root, object) ET.SubElement(obj, name).text names[int(cid)] box ET.SubElement(obj, bndbox) ET.SubElement(box, xmin).text str(xmin) ET.SubElement(box, ymin).text str(ymin) ET.SubElement(box, xmax).text str(xmax) ET.SubElement(box, ymax).text str(ymax) ET.ElementTree(root).write(out_dir / (txt.stem .xml), encodingutf-8)转换时xmin和ymin必须做下限裁剪xmax和ymax必须做上限裁剪模型预测出的边界框偶尔会略超出图像范围不裁剪的XML会让LabelImg直接报错打不开。这个细节在人工复核流里几乎一定会遇到。5.2 修正后重新训练前要核对三个文件人工把漏检目标补上、把偏移框拖正之后再走一遍TXT转换但不要急着直接重复训练。先核对三个文件classes.txt的类别顺序有没有变化、data.yaml的names列表是否与之一致、本次修正的图片有没有被错误地同时放进训练集和验证集。如果只是补漏标建议沿用原来的9:1划分只把修正图放回对应集合保持历史baseline可比。5.3 一个可复用的验证技巧在不同置信度下画门限曲线部署时推荐用一个简单有效的门限扫描代替拍脑袋定conf0.25。从0.05到0.5分别预测统计每个门限下的召回率和精确率两个指标交叉点就是较优门限。for conf in 0.05 0.1 0.15 0.2 0.25 0.3 0.4 0.5; do yolo predict modelruns/detect/train/weights/best.pt \ sourcetest_images/ conf$conf iou0.5 save_txtTrue \ projectconf_sweep nameconf_$conf /dev/null 21 done跑完循环后把每个conf_*目录下的预测TXT与真实标注做逐类对比记录精确率和召回率选取两者差距最小的门限值。对虫害密集的田块图像门限通常落在0.1到0.2之间而不是默认的0.25。把conf扫描脚本里的对比维度改成单独统计每个类别的AP还能找出长尾类别在当前权重下的薄弱程度下一轮补标就优先补它。本文还有配套的精品资源点击获取
返回列表