简介:面向水稻慈姑类杂草检测的221张图片数据集,为农业目标检测模型训练提供Pascal VOC与YOLO双格式标注。影像采集自田间水稻环境,标注对象包括sagittaria与sagittaria_flower两类,分别有744个和520个真实框,合计1264个边界框,可直接用于训练Faster R-CNN、YOLO系列等常用检测模型,适用于农业视觉识别、精准施药、杂草密度评估等研究与应用场景。 压缩包共665个文件,包含221张jpg原图、221个xml标注与221个txt标注,并附有类别说明等文本,整体大小约129.55MB。文件与标注一一对应,目录结构清晰,便于用户自行划分训练、验证与测试集。目前已有144人学习下载。 这份数据集省去了田间采集与人工标注的重复工作,提供可直接读取的规范数据;两类目标覆盖植株与花器官,可支撑模型在不同生长阶段下的识别效果验证,加速农业检测项目的迭代。
1. 水稻慈姑类杂草检测数据集:221张图能撑起什么场景
慈姑是长江中下游稻田里最难缠的杂草之一,箭形叶片贴着水面生长,苗期混进稻丛里人工分拣效率极低。想做精准施药或机械除草,第一道坎就是让机器在田间准确区分哪棵是稻、哪棵是慈姑。这个水稻慈姑类杂草检测数据集,一共提供221张田间图像,标注了2个检测类别——水稻和慈姑类杂草,属于典型的小样本目标检测数据集。它的直接用途是训练一个能区分稻苗和慈姑的检测模型,为田间巡检机器人或智能喷雾设备的视觉模块提供前级感知。221张图数量不大,但类别少、场景聚焦,适合做迁移学习练手,也适合刚接触目标检测的人把数据处理到训练评估的完整流程走一遍。
2. 慈姑类杂草检测的数据基础:标注格式、类别构成与221张样本的真实价值
2.1 数据集的类别设计与标注格式
拿到此类数据集,第一步不是急着开训,而是摸清标注文件的格式和标签质量。农田目标检测数据集常见的标注格式有两种:Pascal VOC格式和YOLO txt格式。VOC格式是每张图配一个同名xml文件,框的坐标用像素值写在bndbox节点里;YOLO格式则是每个框一行文本,记录类别id和归一化后的中心点坐标与宽高。如果是labelme标注产出的json,还需要先转成VOC或直接解析成YOLO格式,多数发布方为了便捷使用通常会给VOC或YOLO里的至少一种。
假设拿到的是VOC格式标注,单个xml文件大致长这样:
<annotation> <filename>IMG_0231.jpg</filename> <size> <width>1280</width> <height>720</height> </size> <object> <name>rice</name> <bndbox> <xmin>356</xmin> <ymin>188</ymin> <xmax>512</xmax> <ymax>342</ymax> </bndbox> </object> <object> <name>sagittaria</name> <bndbox> <xmin>720</xmin> <ymin>400</ymin> <xmax>910</xmax> <ymax>560</ymax> </bndbox> </object> </annotation>一个<object>块对应一个目标框,name是类别名,bndbox里的四个数字是左上角和右下角的像素坐标。这里最容易踩坑的位置是<size>里的宽高必须和jpg的物理尺寸严格一致。如果标注阶段对原图做过缩放而xml里没有同步更新,后面转YOLO格式时所有坐标都会整体偏移,这种错位不报错,但训练出来后框的位置会普遍偏上或偏左,排查起来非常隐蔽。
多数数据集发布方不会附带详细的类别分布文档,所以我会先写一个统计脚本,把xml扫一遍,看类别数量、每类框数、图像尺寸的一致性。这是数据侧最早的排雷步骤。
import os import xml.etree.ElementTree as ET ann_dir = "annotations" box_count = {} size_map = {} image_count = 0 for xml_file in os.listdir(ann_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(ann_dir, xml_file)) root = tree.getroot() image_count += 1 w = int(root.find("size/width").text) h = int(root.find("size/height").text) size_map[(w, h)] = size_map.get((w, h), 0) + 1 for obj in root.iter("object"): name = obj.find("name").text box_count[name] = box_count.get(name, 0) + 1 print("图像总数:", image_count) print("各类别目标框数:", box_count) print("图像尺寸分布:", size_map)这段脚本跑完,能看到两类关键信息:一是rice和sagittaria的框数量分别是多少,如果差距超过3倍,后面必须处理类别不平衡;二是图像尺寸是否存在多种分辨率,混用分辨率时转YOLO格式必须逐张读取实际宽高,不能写死。
2.2 221张样本的分布特点与训练可行性分析
221张图按最常见的8:1:1切分,训练集约176张,验证集22张,测试集23张。验证集22张意味着什么?一张图上漏掉一个关键的慈姑框,mAP可能直接掉2到3个点。同一套配置跑三次,结果波动3到5个点是常态,这不是模型不稳定,而是验证集样本太小,单张图的偶然因素会在指标里被放大。所以面对这类小样本目标检测数据集,头号原则是:评测时不迷信单次数值,要看多次实验的均值和方差。
我的习惯做法是:第一,所有实验固定同一个随机种子,保证数据划分和增强顺序可复现;第二,如果手头只有221张图,把训练和验证切成3到5折做交叉验证,每折留出20%左右的图像做验证,最终把各折mAP取平均,单次实验的运气成分会被抹平不少;第三,做对比实验至少跑三组,别一组定生死。
另外要关注图像分辨率和目标框面积。如果原图多为1920x1080,而水稻苗在图上只有40x40像素,这种小目标在训练尺寸设为640x640时会被压到13x13像素左右,特征非常微弱。我的习惯是先统计所有目标框的宽高,看面积中位数,再决定imgsz用640还是896还是1024。统计脚本就是在2.1那段脚本里加一个框面积列表,输出分位数即可,不复杂但非常管用。
3. 用YOLO在本地跑通水稻慈姑类杂草检测:VOC转YOLO格式与最小训练流程
3.1 VOC标注转YOLO格式:转换脚本与目录结构
YOLO系列训练时接受txt标注文件,每行格式是class_id x_center y_center width height,坐标都是归一化到0到1的浮点数。Ultralytics YOLO不能直接吃VOC xml,必须先转换。转换脚本的核心就一个:把xml里的像素坐标除以图像宽高变成相对坐标,再改写成txt。下面这个脚本我复制改一下类别列表就能用到多个数据集上:
import os import xml.etree.ElementTree as ET CLASSES = ["rice", "sagittaria"] def voc_to_yolo(xml_path, out_dir, img_width, img_height): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in CLASSES: continue cls_id = CLASSES.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) x_center = (xmin + xmax) / 2.0 / img_width y_center = (ymin + ymax) / 2.0 / img_height box_width = (xmax - xmin) / img_width box_height = (ymax - ymin) / img_height lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}") out_name = os.path.basename(xml_path).replace(".xml", ".txt") with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines)) xml_dir = "annotations" label_dir = "labels" os.makedirs(label_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) voc_to_yolo(os.path.join(xml_dir, xml_file), label_dir, w, h)逻辑说明:x_center和y_center由左右边界取平均值算出,box_width和box_height是框的实际宽高,全部除以图像宽高完成归一化。输出文件和xml同名,后缀改为.txt。最关键的是逐张读取图片宽高再转换,如果目录里混着多种分辨率的图,写死宽高必然出错。转完后用文本编辑器随便开一个txt看一眼,坐标值必须在0到1之间,类别id只能是0或1。
转换完的目录结构按YOLO惯例组织:
dataset/ ├── images/ │ ├── train/ # 约176张 │ ├── val/ # 约22张 │ └── test/ # 约23张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml里写清楚路径、类别数和类别名:
path: /绝对路径/dataset train: images/train val: images/val test: images/test nc: 2 names: 0: rice 1: sagittaria3.2 训练配置与超参数选择
小数据集不该上大模型。221张图用YOLOv8x这种大模型,即使有预训练权重也容易过拟合。我一般从yolov8s或yolov8n开始,n最轻量,s是速度和精度的平衡点。实际训练命令如下:
yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=640 \ batch=8 \ seed=42 \ device=0几个参数背后的选择逻辑:model=yolov8s.pt加载COCO预训练权重,这是小样本训练能否收敛的底座,随机初始化权重在221张图上基本跑不动;epochs=200对这个小数据集偏多,但配合早停没有问题,Ultralytics会在验证指标不再提升时自动停止;batch=8在单张消费级显卡上没问题,显存不够就降到4;seed=42保证每次实验的数据划分和增强顺序一致,方便对照。
如果前面统计发现目标框偏小,把imgsz改成896或1024,代价是训练时间翻倍,但小目标召回率通常会有明显提升。这里没有万能答案,我的经验是从目标框面积中位数出发:如果中位数边长小于原图宽度的5%,imgsz就值得往上调。
3.3 训练前必做的三件事
训练前花十分钟检查数据,能省掉后面两个小时的排错时间。我每次都会做以下三件事。
第一,检查有没有空的标签文件。部分标注过程会保存xml但没画框,转换出来就是0字节txt。训练时images里有图但labels里没有对应文件,YOLO会直接跳过这张图,浪费样本且不易察觉。
import os img_dir = "images/train" label_dir = "labels/train" img_names = {f.split(".")[0] for f in os.listdir(img_dir)} label_names = {f.split(".")[0] for f in os.listdir(label_dir)} missing = img_names - label_names empty = [f for f in os.listdir(label_dir) if os.path.getsize(os.path.join(label_dir, f)) == 0] print("有图无标注:", missing) print("空标签文件:", empty)第二,检查标注框是否越界。如果原始xml里的xmax比图像宽度还大,归一化后会出现大于1的坐标,训练和推理时会有边界错误。用脚本扫描所有txt,坐标全部限制在0到1之间才安全。
第三,检查类别id顺序。data.yaml里的names顺序必须和训练读取的类别id一致。最典型的错误是xml里类别名用的是中文"水稻"和"慈姑",而CLASSES列表写的是英文,顺序一错,模型就把水稻学到了id1、慈姑学到了id0,精度低还排查不出原因。跑训练前先打印一下任意一个txt文件内容确认。
4. 小样本数据集避坑指南:221张图最常见的5个翻车点
4.1 翻车点一:val loss掉头上升,模型过拟合
现象:训练loss一路下降,验证集loss在某个epoch后开始回升,mAP停在低位不再提升。
原因:221张图太少,模型在训练集上反复迭代,逐渐把背景纹理、光照色偏这些无关特征也学进去了,验证集环境一变就露馅。这是小样本目标检测数据集最典型的死法。
解决:先开早停,Ultralytics里patience参数设30到50,val loss连续多轮不降就停;其次是降低模型容量,yolov8s改成yolov8n;再就是加强数据增强,重点加光照和色偏扰动,让模型不能靠颜色捷径区分目标。如果这三板斧下去val loss还在升,就回到数据层面检查是不是训练集里有重复样本。
4.2 翻车点二:水稻和慈姑边界混淆
现象:预测结果里rice框里混着sagittaria,或者反过来,置信度高的框实际框住的是另一类。
原因:慈姑的箭形叶片在远距离拍摄时,轮廓特征和水稻的条形叶片区分度不高,苗期两种都是绿色,模型很容易把形状特征学混。这个小项目本身类别语义就接近,属于难分对,不是模型坏了。
解决:我的经验是给模型加料而不是换药。先按标注框把rice和sagittaria的局部图块裁出来,单独做图像分类的辅助训练,或者把这些图块做copy-paste增强贴回训练图,让模型多看到两类叶片的细部差异。推理阶段可以调低置信度阈值到0.15左右,宁可多出框,部署端再用目标位置和大小做规则过滤。如果换模型结构,优先试带注意力机制的变体,对细粒度差异有帮助。
4.3 翻车点三:验证集划分导致指标虚高
现象:训练时val mAP能到0.9,部署到新田块直接垮掉。
原因:最常见的错误是随机划分时,同一片田、同一时段拍摄的连拍图同时被分进训练集和验证集。验证集里全是见过场景,指标自然好看,一到新光照、新土质环境就原形毕露。这属于数据划分的结构性问题,代码层面查不出异常。
解决:划分前先按拍摄时间、田块编号或文件名前缀分组,确保同一场景的图像全部进train或全部进val。宁可验证集只有十几张也要场景隔离。这一步是田间小数据集的生死线,我在这上面栽过一次,后来所有农项目都先看文件名规律再切分。
4.4 翻车点四:类别不平衡,白训练一场
现象:两个类别AP差距悬殊,rice的AP有0.85,sagittaria只有0.5。
原因:田间水稻苗数量通常远多于慈姑,标注框数差距超过3倍时,模型天然偏向多数类。221张图里如果rice框数3000、sagittaria只有800,差距已经很危险。常见的数据集发布方式不会特意平衡两类框数,需要自己处理。
解决:先看统计脚本输出的框数量。两种常见处理:一是按框数量反比给少数类加loss权重,在Ultralytics中可以调整分类损失的类别权重;二是对少数类做重复增强,把含慈姑的图像多复制几份再配合随机裁剪,变相增加少数类样本量。注意copy-paste增强时贴进去的目标必须做轻微缩放和旋转,否则模型会记住固定形态和位置。
4.5 翻车点五:图像尺寸与训练尺寸不匹配,小目标全漏检
现象:大目标检测正常,小目标几乎全漏,召回率很低。
原因:原图1920x1080下40x40像素的稻苗,缩到640x640输入时只剩约13x13像素,特征图上一个格子都分不到,漏检是必然结果。这个项目里慈姑苗期个体小,特别容易踩这个坑。
解决:把imgsz调到896或1024,同时打开mosaic增强。mosaic会把四张图拼接,每个目标在新图里的相对尺寸更小,模型被迫学会在更小尺度上识别目标。显存不够时batch降到2也要保住imgsz,精度优先。训练后看验证集里小目标的recall变化,这一步的收益通常非常明显。
5. 数据增强与迁移学习:把221张图的价值放大到极限
5.1 针对性数据增强策略:光照、裁剪和拼接
水田环境的视觉干扰主要是光照变化和倒影。晴天正午和阴天早晨拍摄,同一种杂草的颜色特征差异非常大,所以数据增强里最值得加的是亮度扰动、对比度扰动和色调偏移。Ultralytics里这些augmentation参数可以直接在训练命令里控制:
yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=896 \ batch=8 \ hsv_h=0.02 \ hsv_s=0.6 \ hsv_v=0.5 \ degrees=15 \ fliplr=0.5 \ mosaic=1.0参数说明:hsv_h控制色调偏移幅度,0.02已经足够,太大容易让水稻叶片的绿色失真;hsv_s和hsv_v控制饱和度和明度扰动,0.6和0.5是田间场景常用的激进值,能有效抵抗日照变化;degrees=15允许图像旋转15度,田间拍摄角度并不总是水平;fliplr=0.5是水平翻转概率,水稻田场景左右对称性高,这个增强几乎没有副作用;mosaic=1.0让每个训练批都做拼接,对提升小目标鲁棒性帮助很大。
除了Ultralytics内置参数,针对这个数据集我还会额外做一步:把验证集里表现差的图像单独拿出来看,如果集中在某个亮度区间,就回去调hsv_v,而不是盲目堆增强。增强参数不是越大越好,过度增强会让模型学到失真纹理。
5.2 迁移学习的关键参数:预训练权重、冻结骨干层与学习率
221张图上从零训练几乎等于白做,预训练权重是唯一可靠起点。用COCO预训练权重时,模型底层已有边缘、纹理、颜色特征,需要学的只是水稻和慈姑的粗粒度形态差异,这种上层概念用少量数据就能学出来。
常见做法是冻结骨干网络的前10层,只训练检测头和后几层,收敛更快且有效降低过拟合风险。Ultralytics里用freeze参数控制:
yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=896 \ batch=8 \ freeze=10 \ lr0=0.005 \ lrf=0.01freeze=10表示冻结前10层参数,这些层学到的是通用视觉特征,没有必要让它们在221张图上重新调整;lr0从默认的0.01降到0.005,小数据集上学习率太大会把预训练特征在早期冲掉;lrf=0.01是最终学习率占初始学习率的比例,让后期收敛更稳。
冻结层数不是越多越好。如果冻到20层,相当于只训练检测头,模型学不到任何新的特征组合,遇到苗期形态差异反而表现更差。我会先从freeze=10试起,对比不冻结和冻结两种配置在验证集上的AP差,再决定。数据量越少,冻结的层数越应该靠后,这条经验在这个221张的项目里非常适用。
6. 验证与进阶:小样本也能做稳定评估,再从221张往外扩展
6.1 小样本下的稳定评估方法
验证集只有二十几张图,mAP方差大得惊人。我常用的做法是K折交叉验证:把221张图按场景分组后切成3折,每折轮流做验证,最终报告3折mAP的平均值和标准差。这样虽然要训练3次,但每一次都用到约200张训练图,信息利用率比8:1:1高得多,结论也更可信。评估时不只盯mAP,还要单独看rice和sagittaria各自AP和召回率。如果recall偏低,优先提高imgsz和mosaic强度;如果precision偏低,优先降低置信度阈值并加强后处理过滤。最后把验证集预测结果可视化,把误检图并排贴出来看,能发现很多指标上看不出的规律。
6.2 从221张走向更大规模的数据扩展方向
221张图作为起步够用,要部署到真实田间环境还需要扩大一个量级。扩展优先级如下:第一优先补不同生长阶段的图像,慈姑从幼苗期到分蘖期形态差异极大,只学一个阶段没法覆盖全年;第二补不同天气和时段,大逆光、阴天、雨后水反光、傍晚低照度,这些是部署时最容易翻车的场景;第三补不同地域的田块,不同土壤背景和种植密度会影响泛化;最后才是通过调整拍摄高度补充目标尺度多样性。收集新图像后沿用同一套标注标准和类别定义,与现有221张合并重新划分,再做一轮完整训练和评估。这个迭代流程的价值在于,每一轮数据扩展都能定位当前模型的失效模式,而不是盲目加数据。
说到底,小样本检测数据集拼的不是数量,是每一次训练前对数据的理解和对坑的预判。221张图完全可以跑通一个像样的检测管线,但前提是划分隔离、增强克制、迁移学习到位。我自己的习惯是每次拿到这类数据集,先跑统计脚本再看loss曲线,最后才信mAP。这套流程帮我避过不少坑,希望帮到你。
本文还有配套的精品资源,点击获取