☰
植物萌芽检测实战:基于YOLOv12的数据集标注与训练全流程
2026/10/1 2:45:12 网站建设 项目流程

简介:植物萌芽检测数据集专注植物萌发阶段的视觉识别,面向精准农业监测、智能温室管理、植物表型研究及农业科研教学等场景,可支持构建萌芽期识别模型,用于作物生长阶段自动化监测与生长周期预测。数据集共2000个文件,涵盖1484个txt格式边界框标注、514张jpg原始图像、1个yaml配置与1个docx说明文档,压缩包整体约133.71MB;txt标注遵循中心坐标加宽高的YOLO规范,yaml可直接套用训练配置,docx补充数据构建与使用建议。数据划分包含1177张训练图、302张验证图及5张测试图,覆盖多种视角并包含旋转、裁剪、噪声处理等增强版本,有助于提升模型鲁棒性。目前已有101人学习,适合目标检测研究者与农业AI开发者快速开展萌芽期检测实验,亦可作为农林院校算法教学实践数据集。

1. 植物萌芽检测数据集:从标准 YOLO 标注图到能落地的出芽识别模型

温室育苗车间里,一张苗盘几百个穴孔,旺季时工人要逐个弓腰核对萌芽率,一个班下来脖子和眼睛都僵得难受。植物萌芽检测数据集解决的正是这种重复枯燥的视觉劳动:它把苗期图像整理成标准 YOLO 标注格式,拿回来就能直接丢给 YOLOv12 这类目标检测模型训练,模型输出每个芽的坐标框与置信度,后面接统计报表还是补苗机械臂都顺理成章。适合三类人:做农业自动化方案的工程团队、要在嵌入式设备上跑植物视觉识别的开发者,以及研究工业农业医学领域目标检测数据集落地的从业者。省下的不只是标注时间,还有从零梳理训练管线的过程。

2. 先看懂数据集的目录与标注格式:训练前的一小时自检

2.1 目录结构长什么样

这份资源解压之后,内部目录是按目标检测训练的标准习惯组织的。我拿到任何一份数据集,第一步不是急着训练,而是先把目录树摸清楚,确认 images 和 labels 是否一一对应。常见结构如下:

plant_sprout/ ├── images/ │ ├── train/ │ │ ├── sprout_0001.jpg │ │ ├── sprout_0002.jpg │ │ └── ... │ └── val/ │ ├── sprout_0351.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── sprout_0001.txt │ │ ├── sprout_0002.txt │ │ └── ... │ └── val/ │ └── ... └── plant_sprout.yaml

train 与 val 的比例通常接近 8:2 或 9:1,这个数据集按经验也是这种划分。图像多为俯拍或斜俯拍的苗盘/苗床照片,分辨率跨度比较大,有手机拍的,也有工业相机拍的,恰好能模拟现场设备参差不齐的真实状况。

每个 jpg 对应一个同名 txt,txt 里存的就是这张图上所有目标框的标注。yam 文件是给 Ultralytics 系列训练框架用的数据集描述文件,里面写明了类别数量和类别名称,训练时直接以data=plant_sprout.yaml的方式引用。

在动手训练之前,建议先做一件事:用tree或find把文件数量统计出来,确认两个目录的文件数一致,避免后面训练时出现“部分图像没有标签”的隐蔽问题。

find images/train -name "*.jpg" | wc -l find labels/train -name "*.txt" | wc -l

如果两边数量对不上,说明存在漏标注的图像,后面训练出的模型在没标签的那些图上大概率会被当作负样本,直接干扰损失函数。我的习惯是先把这一步走完,再谈参数。

2.2 YOLO 标注格式里的数字到底代表什么

YOLO 格式的每一行是五个数字,依次是类别编号、目标中心点的 x 坐标、y 坐标、目标宽度、目标高度。坐标全部做了归一化处理,取值都在 0 到 1 之间,分母是图像的宽和高,不是像素绝对值。我拿一份典型的标注拆开说明:

1 0.513462 0.288741 0.034615 0.040727

这一行表示:这张图里有一个类别编号为 1 的目标,它的中心点位于图像水平方向 51.35%、垂直方向 28.87% 的位置;目标本身宽度约占整张图宽度的 3.46%,高度约占整张图高度的 4.07%。换算成像素值,只需要把前两个数乘以图像宽高、后两个数乘以图像宽高,就能得到真实的像素框。

萌芽期的小苗在整张图中往往只占很小一块面积,所以 w、h 这类数字普遍落在 0.02 到 0.08 之间,属于典型的小目标标注。做这类数据集训练最忌讳的就是把 w、h 的顺序记反,还有把中心点坐标误当成左上角坐标。我见过不止一个同事用 LabelImg 导出时选了 PASCAL VOC 格式,然后又手动转 YOLO,转完忘了除宽度,训练出来的框全部偏移到图像角落。

这份数据集里常见类别有两种组织方式:一类是只标“萌芽”一个类别,适合单纯的出芽计数;另一类会把“未出芽的空穴”和“已出芽”区分开,甚至把子叶展开的苗单独列一类。后者在实际补苗场景中更有用,因为自动补苗需要同时知道哪里空着、哪里已经长出来了。拿到数据集后先打开 yaml 文件,确认 names 列表的内容,不要按我的项目场景强行套你的业务逻辑。

2.3 用一段脚本把数据集的健康状况查一遍

训练之前,我强烈建议把下面这段校验脚本跑一遍。它做的事很简单:遍历每张训练图,检查同名标签是否存在、每行是不是 5 个字段、坐标是否都落在 0 到 1 范围内。这段脚本几乎可以套用在任何 YOLO 格式数据集上,不止这一份资源。

from pathlib import Path root = Path("plant_sprout") bad_count = 0 for split in ["train", "val"]: img_dir = root / "images" / split lbl_dir = root / "labels" / split for img_p in sorted(img_dir.glob("*.jpg")): lbl_p = lbl_dir / (img_p.stem + ".txt") if not lbl_p.exists(): print(f"[标签缺失] {img_p.name}") bad_count += 1 continue for line in lbl_p.read_text().splitlines(): parts = line.split() if len(parts) != 5: print(f"[格式错误] {img_p.name}: {line}") bad_count += 1 continue cls = parts[0] try: cx, cy, w, h = map(float, parts[1:]) except ValueError: print(f"[数值错误] {img_p.name}: {line}") bad_count += 1 continue if not all(0 <= v <= 1 for v in (cx, cy, w, h)): print(f"[坐标越界] {img_p.name}: {line}") bad_count += 1 print(f"检查完成,异常数量: {bad_count}")

逻辑并不复杂:先用glob拿到所有 jpg 文件名,用stem属性去掉扩展名后拼出对应 txt 路径;再逐行解析,先查字段数量,再查数值范围。坐标越界是最常见的问题之一,比如手工标注时不小心把框拉出了画布边缘,归一化后出现大于 1 的值。

如果异常数量为 0,说明这份数据集在结构层面是干净的,可以进入训练环节。如果报出一堆标签缺失,建议直接补一份空 txt 或者删除对应图像,不要让模型把无标签区域当负样本学。这条规则对所有计算机视觉数据集都适用,尤其是做行业数据集复用时,前人的标注习惯未必严谨,自检这一步不能省。

3. 用 YOLOv12 把萌芽检测模型跑通:环境、参数与验证

3.1 训练环境与数据集划分

YOLOv12 的训练流程和整个 YOLO 系列基本一致,主流做法还是基于 Ultralytics 框架。先装好依赖:

pip install ultralytics albumentations

如果你的 Ultralytics 版本还没有收录 YOLOv12 的预训练权重,可以先换成 yolov11n 或 yolov8n 验证整套流程,数据集本身不绑定特定模型版本,标注格式是所有 YOLO 家族通用的。常见做法是先跑通小模型,确认数据和标签没有问题,再切换到大模型。

数据集划分前要注意一个容易被忽略的点:如果原始图像里存在同一苗盘的连拍序列,直接随机打散划分 train/val 会造成图片泄漏,验证指标虚高。按拍摄批次或时间先后切分,例如前 80% 时间段的图像进 train、后 20% 进 val,才是模拟真实新场景的做法。这一点在工业农业医学行业目标检测数据集里尤其重要,因为现场采集往往是一次性拍几百张连图。

我会在数据目录外新建一份数据集描述文件,内容如下:

# plant_sprout.yaml path: ./plant_sprout train: images/train val: images/val nc: 2 names: ['empty_hole', 'sprout']

path写数据集根目录的相对路径,train、val相对于path填写,nc是类别总数,names按类别顺序列出。如果你的数据集是单类别,把nc改成 1,names只保留一个即可。

3.2 训练参数模板与说明

下面是我平时训练萌芽检测时用的参数模板。YOLOv12 训练入口与 YOLOv8/YOLOv11 风格一致,下面这段按 Ultralytics 通用接口写成:

from ultralytics import YOLO model = YOLO("yolov12n.pt") model.train( data="plant_sprout.yaml", epochs=120, imgsz=640, batch=16, lr0=0.01, lrf=0.01, optimizer="SGD", patience=20, augment=False, seed=42, )

逐项解释我的选择理由:epochs=120是考虑到数据集规模不大,给足迭代次数,让小目标框的 anchor 充分调整;imgsz=640兼顾显存占用和大多数手机/工业相机图片的分辨率比例,如果图像本身就是 1920×1080 的航拍图,建议直接提到 960 再训练,否则芽的像素面积会被压缩到十几像素;batch=16是 24GB 显存的典型配置,显存小的机器降到 8;lr0=0.01是 SGD 优化器的常用初值,配合patience=20早停在验证指标不再上升时自动保存最好权重;augment=False是因为我习惯先跑一版不带增强的基线,确认数据和标签正确,再开增强。

先解释imgsz对小目标的影响。imgsz=640意味着输入图像会被缩放到 640×640,原本 4000×3000 的大图里一个 40×40 像素的芽,缩放后只剩约 6×6 像素,几乎在主干网特征图上消失。第一版训练如果发现漏检严重,优先把imgsz提到 960 或 1280。

再解释augment=False的用意。Ultralytics 默认开启 Mosaic、HSV 扰动等增强,数据集本身存在标注偏差时很难判断是增强过度还是模型欠拟合。先关闭增强跑一版干净的,再增开,是我处理所有行业数据集的一贯顺序。

推理验证的代码也贴出来,方便直接复现:

from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict( source="test_images/", imgsz=640, conf=0.3, save=True, save_txt=True, )

conf=0.3是出芽检测场景比较合理的下限。芽体小、边缘模糊,置信度普遍比大目标低一截,卡在 0.5 会漏掉大量真值;save_txt=True会把检测结果存成 YOLO 格式文本,方便后续统计每张图的芽数。

3.3 验证指标怎么读

训练结束后,Ultralytics 会输出一张 results.csv,里面逐行记录了每个 epoch 的 mAP50、mAP50-95、precision、recall。萌芽检测这类小目标任务,我更关注 recall 而不是 mAP。因为补苗场景里漏掉一个芽,意味着空穴被当成有苗,后期不会有人再补种,损失更不可逆;误报多一个芽,顶多补苗时多放一粒种子,成本低得多。

如果 recall 低于 0.85,建议按第 4 章的方法加大输入尺寸、调整增强策略。如果 precision 远低于 recall,再去排查是不是标注框偏大,导致模型把背景也包了进来。我通常会从 results.csv 里抽最后 10 行的数据做判断,避免只看最佳权重那一个点。

4. 数据增强与小目标优化:让模型对现场光照更鲁棒

4.1 针对萌芽图像的增强参数

萌芽检测的图像大多在温室或苗床拍摄,光照条件受天气、补光灯、遮阳网影响很大,同一批苗在不同时段拍出来的色温、亮度完全不同。模型需要对这些变化不敏感,才能在现场稳定工作。开启增强时,我比较关注下面这几项:

model.train( data="plant_sprout.yaml", epochs=120, imgsz=960, batch=8, hsv_h=0.015, hsv_s=0.6, hsv_v=0.5, degrees=15, translate=0.1, scale=0.4, fliplr=0.5, flipud=0.1, mosaic=0.8, )

hsv_v=0.5允许亮度在正负一半的区间内随机扰动,温室补光灯开启和关闭时的亮度差异基本能被覆盖;hsv_h=0.015只做轻微色相扰动,避免把芽的绿色和土壤的黄色混到一起;degrees=15模拟相机安装角度偏移,苗盘在视野里稍微歪一点也能被识别。flipud=0.1只给 10% 概率,因为俯拍角度下图像上下翻转虽然物理上合理,但如果现场苗盘有方向性纹理,翻转过多反而引入噪声。

Mosaic 增强会把四张图拼成一张,放大图像中的上下文信息。对小目标检测来说,Mosaic 能显著提升模型对“芽周围土壤环境”的建模能力,但也要注意,如果标注框本身有偏差,拼图后偏差会被放大。我习惯把mosaic从默认的 1.0 降到 0.8,给正常图像留一些训练比例。

4.2 输入尺寸、类别不平衡与困难样本挖掘

萌芽数据集普遍存在一个反直觉的现象:类别不平衡不在“萌芽 vs 背景”,而在“容易检测的萌芽 vs 难检测的萌芽”。刚破土而出的芽只有两片子叶甚至只有一条缝,面积小、对比度低,和土壤颜色接近,模型倾向于只学那些已经展开子叶、边界清晰的大芽。针对这种困难样本,有两个常见做法我经常组合使用。

第一,把输入尺寸从 640 提到 960。萌芽框宽度占整图的比例通常在 0.03 到 0.05 之间,640 输入下对应 19 到 32 像素,属于小目标但还不算极小;960 输入下能到 29 到 48 像素,特征图上的响应明显增强。显存不够时,用batch=8或者开启梯度累积也要上大图尺寸,这一步对小目标检测的收益往往比调任何超参数都明显。

第二,对低置信度样本做二次标注加入训练集。训练完第一版模型后,把验证集里 recall 为 0 的图像抽出来,人工检查是没标还是标错。常见情况是原数据集漏标了那些特别小的芽,模型学到了正确特征但 label 里没有对应框,损失函数把正确的预测也当负样本惩罚。把这些漏标补上再训练一轮,recall 通常能提升三到五个百分点。

类别不均衡的处理则要看 yaml 里的 names 定义。如果你的数据集区分了empty_hole和sprout两个类别,且空穴数量远多于芽数,可以在采样时对sprout类别的图像做上采样复制,让每个 epoch 里两类目标出现的频率接近。Ultralytics 没有直接提供 per-class 采样参数,常见做法是提前在数据层面复制少数类图像,或者直接用外部脚本做重采样。

5. 避坑:五个翻车现场与排查记录

5.1 损失正常下降,验证 mAP 却一直是 0

现象:训练日志里 box loss 从 0.08 慢慢降到 0.03,看起来很健康,但每个 epoch 结束后的 mAP50 和 recall 都停在 0 附近,整张验证集一个框都预测不出来。

原因:标签文件路径写错了。plant_sprout.yaml里train和val写的是images/train和images/val,但 Ultralytics 会默认去labels/train找同名 txt。如果 labels 目录名大小写不一致,或者 txt 文件压根没放进去,训练时所有图像都成了“无标签”状态,模型学不到任何监督信号,损失下降只是背景知识的正常收敛。

解决:回到 2.3 的校验脚本,先统计 labels/train 下的 txt 数量,确认和 images 数量一致。如果 txt 文件存在但训练仍显示 0 标签,打开其中一份看内容是否为空文件。空文件比缺失文件更隐蔽,因为文件名对得上,但没有任何标注行,模型依然拿不到监督信号。

5.2 预测框整体偏到芽旁边的土壤上

现象:置信度挺高,框也方方正正,但框的中心点不在芽体上,而是落在芽基部旁边的土块或基质上,尤其是小芽特别明显。

原因:标注框不够紧。很多标注工具导出的框是矩形外接框,如果标的时候图省事把边框划大了几像素,芽体本身又小于 10 像素,框中心点就会偏离目标区域。模型训练过程中会学着预测这个“偏了的中心”,最后输出的预测自然也是偏的。

解决:我一般会抽查 50 张训练图,把标注框画回原图看贴合度。如果固定偏移方向,比如都是偏右下,说明标注时统一出现了系统性偏差,用脚本把所有标注按像素偏移修正再训练。如果是随机偏移,就把这类边界模糊的样本找出来重新标注,重点修那些面积小于 20×20 像素的框。

5.3 刚破土的芽频繁漏检,已展开子叶的芽都能检出

现象:分类别统计时,sprout类的 recall 明明有 0.92,但实际看可视化推理结果,漏掉的全是最小的那批芽,很多只有三五个像素宽。

原因:这是典型的小目标在多尺度特征图中响应不足。YOLO 系列的主干网络下采样倍数决定了最小检测尺寸,输入 640 时,P3 特征图的 stride 是 8,对应感受野大约 8 像素,小于 8 像素的目标基本只能靠上下文推测,难以形成稳定的特征响应。

解决:两个手段组合,一是按 4.2 把imgsz提到 960 或 1280,让目标在输入图像里的像素面积变大;二是用切图推理。我常用的工具是 SAHI(Slicing Aided Hyper Inference),把大图切成 640×640 的重叠切片分别推理再合并结果,能在不改训练配置的情况下把 recall 拉高五到十个点。对萌芽检测这种目标小、分布密的任务,切图推理几乎是现场落地的保底方案。

5.4 显存不够,batch 调小后损失曲线剧烈震荡

现象:24GB 显卡跑batch=16没问题,换到 8GB 显卡只能batch=4,训练损失从 0.05 到 0.11 来回跳,验证指标也不稳定,早停机制频繁误判。

原因:batch 太小的情况下,BN(Batch Normalization)层的统计量计算不稳定,每批数据的均值和方差波动大,梯度方向的噪声也随之放大。这在目标检测的早期训练阶段尤其明显,因为前期 anchor 分配还在剧烈变化。

解决:优先做梯度累积,单卡上模拟出更大的 batch。Ultralytics 支持在训练参数里通过batch和accumulate配合,或者手动在优化器层面累积梯度。其次把优化器从 SGD 换成 AdamW,它对小 batch 的适应性更好一些。如果两者都不方便,就强制冻结 backbone 前 10 层训练,只更新检测头,能显著降低显存占用和梯度波动。

5.5 验证指标很好,一到现场新场景就翻车

现象:训练集和验证集都是从同一次拍摄里随机划分的,mAP50 到了 0.95,结果到现场换了一批苗盘拍摄,检测率直接掉到 0.6 以下。

原因:随机划分造成数据泄漏。同一苗盘、同一时段拍摄的图像之间高度相似,模型记忆了这些图像的纹理特征,而不是“芽”本身的通用特征,验证集和训练集太像导致评估结果虚高。

解决:所有行业数据集都应该按时间或场景做分组划分,而不是随机打散。具体到这个数据集,我会把所有图像先按拍摄日期排序,取前 80% 的图像做训练,后 20% 做验证。如果资源里没带时间信息,就按文件名序号的大段切分,保证同一个批次的连拍不会同时出现在训练和验证里。这个改动看起来小,但对现场泛化能力的影响是决定性的。

6. 训练完成后,我建议你在交付前强制走一遍的验证流程

模型训练完不代表能用,每年因为这个翻车的人不少。我自己的习惯是留出一个叫final_check的小目录,里面放 20 到 30 张与训练集完全不同场景的图片,比如不同育苗盘的、不同光照下的,甚至手机拍摄角度与工业相机不同的。每次训练完,不管验证集指标多好看,都要把这个目录跑一遍推理,肉眼检查输出结果。

检查时重点看两类错误:一类是漏检,模型对某个明显的芽没输出任何框;另一类是错检,把土壤里的石子、基质颗粒或刻度线误判成芽。前者说明泛化不足,后者说明特征学习有偏差。漏检多的回到第 4 章调输入尺寸和增强;错检多的往往是标注时把背景物体框了进去,需要回查训练集里有没有类似的脏标签。

我还习惯把每张测试图的置信度分布打印出来,统计conf < 0.3的检测框数量占比。如果一张 640×640 的图上模型输出了上百个低置信度框,说明模型对背景产生了过度敏感,需要在后处理里把conf阈值往上提到 0.35 或 0.4,同时检查是不是训练集里空穴类别样本太少。

最后分享一个我自己的血泪教训:有一次做实时的苗盘巡检设备,训练阶段只跑通了 YOLOv8 的默认配置,没有做小目标优化,结果拿到现场后漏检率高出预期好几倍。从那以后我每次训练前都强制走一遍数据校验、按场景划分训练集、开大输入尺寸这三个步骤,虽然多花一两个小时,但再也没有出现过交付现场翻车的情况。这份植物萌芽检测数据集本身标注得算规范,但任何数据集到了自己手里都值得按这个流程过一次手,希望我的经验能帮你少走一段弯路。

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

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

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

立即咨询