☰
无人机视角马铃薯病害检测数据集:VOC+YOLO格式实操解析
2026/10/17 3:07:21 网站建设 项目流程

做智慧农业这行的朋友应该都有同感:想训练一套作物病害检测模型,最难的从来不是算法选哪个,而是数据从哪来、标注规不规范、能不能直接喂给模型。看到“无人机视角土豆马铃薯洋芋苗病害检测数据集VOC+YOLO格式1539张2类别”这个项目标题时,我第一反应是终于有人把无人机航拍视角的马铃薯病害数据整理成通用格式了。这里面的“土豆、马铃薯、洋芋”其实是同一作物在不同地区的叫法,北方习惯叫土豆,西南很多地方叫洋芋,学术和产业统一叫马铃薯,本质都是茄科作物。这篇内容我就以这个数据集项目为主线,把无人机病害检测的完整链路——数据格式、标注套路、训练配置、常见坑——按我自己的实操经验全部梳理一遍,争取让你拿到手就能直接开跑。

1. 项目定位:这份无人机病害检测数据集解决了什么问题

1.1 为什么非得用无人机视角拍马铃薯病害

传统的地面病害检测,一般是拿着手持相机或者手机在田间近距离拍摄叶片、茎秆,拍出来的目标很大、背景很干净,模型训练难度相对低。但真正到了大规模植保巡检环节,靠人工下地逐块拍照根本不现实,几百亩地走一圈下来人都要废掉。无人机巡田的优势在于效率高、覆盖广、能定时定点重复采集,十几分钟就能搞定一大片田。不过代价也很直接:视角从平视变成了俯视,高度上来了,目标缩小了,地面背景又杂又乱,杂草、土块、地膜、滴灌带全都会入镜,模型的判别难度成倍上升。

所以这个数据集的核心价值,不在于它有多少张图,而在于它把“无人机俯拍”这个真实作业场景下的病害识别问题,变成了一个可以直接训练和评估的标准任务。它面向的人群也很明确:做植保无人机航线规划的开发者、搞农业AI算法落地的工程师、研究作物表型分析的学生,甚至是想做田间智能巡检产品原型验证的个人开发者。

1.2 2类别设计的合理性和扩展思路

有人说才2个类别是不是太少了。我觉得对于打样的阶段,2类非常够用,而且往往是最好用的。常见的设计方案是“健康苗”和“病害苗”,病害苗可以指晚疫病、早疫病、枯萎病等任何一种表现出明显病斑或黄化的植株,只要标注规范,模型学到的是“正常vs异常”的判别边界,这个边界本身就是植保巡检最核心的需求——先发现问题,再针对问题做后续处理。

从扩展角度看,2类的底子也可以往多类别升级。无人机影像如果分辨率够高,完全可以在现有框里增加病害类型标签,比如把原来的“病害苗”细分成“晚疫病”“早疫病”“缺苗”“虫害苗”,只需要在标注阶段重新过一遍数据,训练脚本和评估流程基本不用大改。这也是当初选VOC+YOLO双格式的另一个原因:格式通用,后续不管加类别还是换模型,都不用重头再折腾数据。

1.3 1539张图像的数据规模分析

1539张图这个量级,我要说句公道话:不算大,但对于特定场景下的单类检测任务,训练出来已经够用。关键看怎么分配。我通常按约8:1:1划分训练集、验证集、测试集,也就是大约1200张训练、200张验证、150张测试。如果数据分布比较均衡,模型可能收敛不到SOTA精度,但达到可上线的水平完全有戏。另一种思路是做扩充,把1539张通过离线增强翻到5000张以上再来训练,后面我会细说增强怎么加才不破坏标注坐标。

2. VOC与YOLO双格式:数据结构逐项拆解

2.1 两种格式到底是什么

目标检测数据集的标注格式五花八门,但最通用的就是Pascal VOC和YOLO两种。VOC格式是早期Pascal VOC挑战赛确定的标注规范,每张图像对应一个同名XML文件,里面记录图像尺寸、目标类别和边界框坐标,坐标是绝对值,人类直接读XML就能看懂。YOLO格式是DarkNet框架推起来的,后来被Ultralytics YOLO系列继承,每张图对应一个TXT文件,每行记录类别ID和归一化后的中心点坐标、宽高,机器读取效率高,但人眼直接看数字会比较懵。

这个数据集同时给两种格式,最直接的收益就是省事。你想跑YOLO系模型,直接读TXT,不需要转换;你想跑Faster R-CNN、SSD或者其他依赖VOC风格的框架,直接用XML,也不需要自己写转换脚本。省下的时间看着不多,真正到了批量处理时你就知道有多值。

2.2 VOC XML标注文件长什么样

打开一张VOC标注文件,结构大概是这样的:

<annotation> <folder>images</folder> <filename>00123.jpg</filename> <size> <width>1280</width> <height>720</height> <depth>3</depth> </size> <object> <name>disease</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>143</xmin> <ymin>259</ymin> <xmax>686</xmax> <ymax>644</ymax> </bndbox> </object> </annotation>

几个字段要特别注意:width和height必须和真实图像尺寸一致,否则转YOLO算归一化数值时会出错;xmin/ymin是左上角坐标,xmax/ymax是右下角坐标,坐标系以左上角为原点;truncated和difficult很多标注工具默认填0,但如果你处理的是无人机俯拍影像,被叶子遮挡一半的植株很常见,要不要标成truncated是标注规范里需要提前定死的规则。

2.3 YOLO标注文件长什么样

YOLO格式的TXT文件就简单很多,每行代表一个目标:

1 0.5312 0.4478 0.3141 0.3556 0 0.1183 0.2214 0.2064 0.1832

第一个数字是类别ID,0代表健康苗,1代表病害苗(或者反过来,以数据集实际定义为准)。后面四个数字分别是归一化的中心点横坐标、中心点纵坐标、框宽、框高。计算方法很简单:中心点x除以图像宽、中心点y除以图像高、框宽除以图像宽、框高除以图像高,得到的都是0到1之间的小数。

这里有个很多人初次接触时会踩的坑:YOLO格式中x_center和width都除以图像宽度,y_center和height都除以图像高度,千万别搞成交互的。假设图像宽1280、高720,一个框的左上角在(320, 180),右下角在(700, 520),那么x_center = (320+700)/2 / 1280 = 0.3984,width = (700-320)/1280 = 0.2969,y_center = (180+520)/2 / 720 = 0.4861,height = (520-180)/720 = 0.4722。我见过一次系数除反的情况,模型训练起来Loss怎么都降不下去,最后排查半天才发现是标注转换脚本里的低级错误。

2.4 目录组织与数据划分建议

拿到数据集后,不管原始目录长什么样,我建议第一步统一成下面这种标准结构:

dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml

images存图,labels存YOLO格式TXT,annotations存VOC格式XML,yaml文件描述类别和路径。这样划分之后,不管是用Ultralytics YOLO还是其他框架,都能做到路径统一、脚本复用。如果原始数据本身就是分好文件夹的,那就直接确认文件命名是否一一对应。一个高频问题是图片是00123.jpg但标签文件叫00123.txt还是00123_w.txt,名字对不上就会导致训练时大量图像没有对应标签,被静默跳过。

3. 标注规范与无人机影像的预处理要点

3.1 标注工具选型

VOC和YOLO格式都有对应的标注工具,但实操中我基本只用两种方案:一是LabelImg,它的原生导出口径就是VOC XML,之后再写个十几行的Python脚本把XML转成TXT;二是Label Studio,适合多人协作,管理大批量图片时更舒服。至于到底用哪个,取决于团队规模和项目周期,自己单干或者小团队两三个人协作,LabelImg完全够用。

下面这段代码是我常用的XML转YOLO脚本,支持批量处理,只需要改一下类别映射表和路径就可以跑:

import os import xml.etree.ElementTree as ET classes = {"healthy": 0, "disease": 1} # 自己按数据集实际类别调整 def convert(xml_path, out_txt_path): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") width = int(size.find("width").text) height = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in classes: continue class_id = classes[name] box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) x_center = (xmin + xmax) / 2 / width y_center = (ymin + ymax) / 2 / height box_w = (xmax - xmin) / width box_h = (ymax - ymin) / height lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") with open(out_txt_path, "w") as f: f.write("\n".join(lines)) # 遍历annotations目录,批量转换 for xml_file in os.listdir("annotations/train"): if xml_file.endswith(".xml"): convert( os.path.join("annotations/train", xml_file), os.path.join("labels/train", xml_file.replace(".xml", ".txt")) )

转换完之后一定要做一次反向校验:随机挑几十张图,把TXT里的坐标画回图像上看,框的位置对不对、有没有出现上下颠倒、尺寸异常。这一步虽然土,但比跑一遍训练看结果来得快得多。

3.2 无人机视角的标注难点与避坑

无人机俯拍影像的标注,和地面拍摄有本质差别。首先是目标尺度小,植株在整张图里可能只占几十个像素,标起来容易误判,需要搭配原图放大窗口才能准确落框。其次是遮挡严重,植株与植株之间叶片交叉,从正上方往下看,很难分清一株的边界到底在哪里。我的建议是标注规范里明确写清楚:凡是叶片面积超过一半可见的植株,都要正常标注;被严重遮挡或者只露出一个角的不标,因为即使标了,模型学到的也是噪声。

另外,无人机影像经常出现图幅边缘的几何形变,特别是大疆这类广角镜头,边角区域的植株会倾斜、拉伸。如果直接标注原图,框的形状可能和实际目标形态差别很大。这个问题比较专业的处理方式是先把原始影像做畸变校正,或者按中心区域裁剪后再标注,但这个数据集如果是已经成图的正射影像或者拼接图,通常不存在这个问题,直接按原图标注问题不大。

标注时候还有一条非常关键的规则:边界框要贴着目标的外接矩形,不要预留太多背景。很多新手习惯把框画大一点,觉得把整个植株带一点周围土壤放进去更保险。这个习惯在目标检测里是大忌,因为框里面多余的背景会变成模型的特征噪声,训练时拉低精度。宁可我框挨着叶子边缘切,也不要画“宽松框”。

3.3 数据增强怎么加才不破坏坐标

无人机影像数量有限时,数据增强是提升模型泛化能力最直接的手段。但要记住,目标检测的数据增强和分类不一样,几何变换必须同步修改标注框坐标。好在YOLO训练框架里自带的增强已经处理了这个问题,你只需要在配置里开启对应选项就行。

以Ultralytics YOLO为例,常见增强参数包括:

# data.yaml同目录增强配置示例,按需调整 augment: true hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 fliplr: 0.5 flipud: 0.3 scale: 0.5 translate: 0.1 mosaic: 1.0 mixup: 0.2

这里要提醒一个很实际的经验:无人机俯拍图像里病害的表征通常是叶片颜色变化,比如黄化、褐色病斑。如果你把hsv_h开得太大,会把健康的绿色调成病态的黄褐色,模型就会学到错误关联,训练时看似精度不错,一到真实场景就露馅。所以我建议色相偏移控制在0.015以内,饱和度和亮度的扰动可以适当放大,让模型对光照变化更鲁棒。

另外,从1539张原始数据翻倍的角度,我建议优先用Mosaic增强。YOLOv8里的Mosaic会把四张图拼成一张,可以显著增加每张训练图里的目标数量,对小目标检测尤其有效。实测经验,Mosaic开启后模型的小目标召回率能提升3到5个点。

4. 基于该数据集的目标检测训练实操

4.1 环境准备与数据加载

先说训练环境。目标是跑通流程的话,一张入门级独显就能搞定,显存8GB起步,建议12GB以上。训练框架我推荐Ultralytics YOLOv8,生态成熟、文档全、遇到问题也更容易搜到答案。以下环境组合在我机器上跑得很稳定:Python 3.10以上,PyTorch 2.x,CUDA 11.8或12.1。

安装就一句话:

pip install ultralytics torch torchvision

装完之后先写data.yaml,这是训练入口文件,配置类别和路径。

# data.yaml path: /your_absolute_path/dataset # 改成实际路径 train: images/train val: images/val test: images/test nc: 2 names: ['healthy', 'disease']

nc是类别数,names按顺序对应TXT文件里的类别ID。这里最容易出的错是类别顺序不一致,如果标注TXT里0是病害苗但是names里0写成了健康苗,模型训练完了预测类别就是反的。我习惯在训练之前先把一个TXT文件打出来看一眼,确认一下ID范围是0到1,再对一下names顺序。

4.2 训练配置与参数选择

模型选择上,1539张图这个规模,我建议从yolov8s起步,体量适中,精度和速度的平衡比较舒服。如果跑起来显存吃紧,换成yolov8n;如果追求更高精度且时间充裕,可以试yolov8m。下面是一个我实测过的训练命令:

yolo detect train \ model=yolov8s.pt \ data=data.yaml \ epochs=100 \ imgsz=1280 \ batch=8 \ device=0 \ patience=15 \ project=potato_disease \ name=run1

几个参数的重点:

  • imgsz=1280:无人机影像分辨率普遍大于普通手机照片,如果输入尺寸太小,小目标会被压缩到几乎没有。1280和640相比,显存占用翻倍,但小而密的病害植株像素保留度完全不同。如果你的显存只有8G,可以降到960或者640试。
  • epochs=100:1539张图的底子不需要训太狠,100个epoch足够看到收敛趋势。配合patience=15自动早停,防止过拟合。
  • batch=8:可以根据显存调整,显存小就4,显存大就16。不建议为了省显存把图像尺寸压太低,宁可调小batch也不要过度缩小输入分辨率。

训练过程中要重点观察两个日志指标:box_loss和cls_loss。如果box_loss一路下降但cls_loss震荡明显,大概率是类别数据不均衡,可以尝试调整类别权重或者做样本均衡。

4.3 模型评估与结果解读

训练结束后,runs/detect/run1目录下会出现权重文件best.pt和last.pt,以及一系列验证结果图。重点看confusion_matrix.png和results.png以及val_batch0_pred.jpg。confusion_matrix.png能直接告诉你哪些误检最多:如果是健康苗被误判成病害苗,说明病害特征学得太激进;如果是病害苗被漏检,先检查病害样本是不是太少或者标注框是否过小。

评估指标我记得直接看验证集上的mAP50和mAP50-95就行。mAP50衡量的是IoU阈值0.5时平均精度,适合快速判断模型整体能不能用;mAP50-95是多个IoU阈值的平均,更严格。无人机小目标场景,mAP50达到0.75以上基本可以上线试飞,mAP50-95能到0.5就算很不错。如果mAP50很低,我不建议急着加数据,先检查标注质量问题,比如画框偏大、类别错标,这些对精度的影响比数据量大多了。

推理单张图验证一下:

yolo detect predict \ model=runs/detect/run1/weights/best.pt \ source=dataset/images/test/00001.jpg \ imgsz=1280 \ conf=0.35 \ save=True

conf=0.35的意思是置信度大于35%的输出,无人机场景下我一般设置在0.25到0.4之间,太高容易漏检,太低误判的框会变多。实际飞行时还会叠加一个面积过滤逻辑:无人机高度在5米以上时,小于一定像素宽度的框可以直接丢弃,因为这些小目标对应的很可能是远处杂草或者土壤颗粒,把这类噪声框提前滤掉能省很多算力。比如一张飞行高度8米拍摄的影像,单个病株框的宽高通常不会低于20像素,小于这个值的框基本可以判定为误检。

5. 实测中常见的问题排查与优化思路

5.1 类别不平衡怎么处理

我实际统计过不少病害检测数据集,健康苗样本数量远大于病害苗,有的甚至到5比1。如果直接训练,模型会偏向把目标预测为健康苗,因为从Loss角度看这样做更“稳”。解决思路有三个层面。第一是数据采样,如果原始数据里病害苗太少,就从全部图里多复制几遍含病害目标的图,或者对含病害目标图做局部裁剪增强。第二是Loss层面,Ultralytics里可以设置class_weights,给少数类更高的Loss权重。第三是后处理层面,推理时适当下调病害类的置信度阈值,让模型更“愿意”标出病害框。

5.2 无人机视角的小目标漏检问题

小目标漏检是无人机巡检最典型的问题。1539张图像如果很多标注框的宽高在32x32像素以下,就需要针对性处理。除了前面说的高输入分辨率、Mosaic增强之外,另一个有效思路是切图。把大图分成多个带重叠的切片,比如1280的图先缩放到1920后,切成四个960x960的patch,分别推理,再把结果合并回去。切图会显著增加推理时间,但查全率提升也很明显,适合离线巡检分析,不适合实时机载处理。

还有更进一步的思路:使用带Transformer检测头的模型或者小目标专门的检测器,比如YOLO系列中改进注意力机制的变体。不过对于1539张这个规模,我不建议一上来就叠模型复杂度,先把数据侧的分辨率、增强、切图手段用到位,通常已经能解决80%的问题。

5.3 模型训练过程中遇到的其他怪问题

训练过程中Loss出现NaN是个很头痛的事。这通常不是数据问题,而是学习率太大或者batch中有异常标注,比如某个框坐标超出了图像边界。要排查第一步确认有没有TXT里出现负数或者大于1的坐标值,可以用一句简单的Python检查:

import os for txt in os.listdir("labels/train"): with open(os.path.join("labels/train", txt)) as f: lines = f.readlines() for line in lines: parts = line.strip().split() if len(parts) != 5: print(f"{txt}: 格式错误 {line}") else: vals = list(map(float, parts[1:])) if any(v < 0.0 or v > 1.0 for v in vals): print(f"{txt}: 坐标越界 {line}")

如果确认坐标没问题,就把学习率从默认的0.01降到0.002,等Loss稳定后再调回去。还有一个容易被忽略的点:图像尺寸和标注框尺寸比。如果整张图里只有一个很小的病害苗框,模型会很难学到稳定特征,这时候要么通过切图放大目标,要么在标注阶段把单独一株苗和周围两三株一起合并成一个框,因为巡检场景只需要定位病区,不需要精确到单株。

5.4 模型迁移到其他地块的泛化问题

用这份数据集训练出来的模型,换一个地块、换一套无人机、换一个季节去测,效果往往会下降,这就是域差异。说白了,不同土壤颜色、光照方向、作物品种形态、飞行高度这些都是干扰项。应对办法是让数据更杂:把训练集里的图像做亮度扰动、加雾模拟、做空间色偏,模拟不同天气和相机参数。如果还有余力,采集一些不同地块的少量图片做在线增强,效果比闭门调参要好得多。

我在实测中发现一个很有意思的规律:模型对同一天同一块田的验证集精度往往虚高,真实新地块的测试精度会掉10到20个点。所以如果你要给客户交付模型,一定要拿一个“没见过的地块”做最终测试,而不是只拿同分布数据集里随机分的验证集。

写在最后的小提醒

这套数据处理和训练思路,不只是这个1539张的无人机马铃薯病害数据集能用,换成别的作物、别的病害,甚至是其他无人机巡检目标,流程基本都是通用的。回到这个数据集本身,我最想强调的还是那句老话:模型是锦上添花,数据是雪中送炭。VOC+YOLO双格式的便利性、无人机视角的真实性、1539张的起步规模,已经给了你一个不错的地基,剩下的靠标注质量、增强策略和反复调参来补。如果你正准备拿它跑第一个训练版本,我建议第一天不要追求精度,先把目录结构理清、格式转换脚本跑通、可视化校验做完,再启动训练。数据管道顺滑了,后面所有模型实验都省心。

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

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

立即咨询