简介:草莓成熟度目标检测数据集,面向计算机视觉、智慧农业及自动化采摘等领域的开发者和研究者,可直接用于YOLO全系列网络训练。数据已按YOLO格式整理,包含训练集、验证集与测试集,分别约1900张、100张和20张图像,标注类别有成熟与未熟两个等级,class文件内附有具体类别名称,方便直接调用。资源图片涵盖温室栽培、不同品种与生长阶段等多样场景,可提升模型在实际农业环境中的泛化能力。资源文件总数为2000个,其中1999个txt文件是每张图像的标签说明,1个Python脚本(show.py)能将检测框可视化到原图上,便于快速核查标注质量、调试模型输出;整个压缩包仅55.48MB,轻量易传输。目前已有392人学习下载,特别适合需要构建草莓成熟度识别方案、或希望用真实农业场景数据进行目标检测入门与进阶练习的学习者,也可作为课题实验、竞赛备战的参考数据集。
1. 目标检测数据集为什么会以草莓成熟度为题
成熟度识别在水果分拣场景里是最容易翻车的一环。颜色接近的品种、光照不均的果面、遮挡严重的花果,单靠传统的颜色阈值根本无法稳定分出“青果—转色—成熟—过熟”的边界。目标检测数据集以草莓成熟度为题,意味着这套数据的核心不是单纯框出草莓在哪,而是把成熟度作为类别标签放进检测任务里:每个实例既要有位置框,也要有明确的成熟阶段标签。超过2k张图片这个量级也很有意思,它高于模型验证阶段的“随手跑跑”,又远低于动辄几十万张的工业数据集,恰好对应一个中小规模项目从标注到训练再到部署的完整闭环。
这里推荐用 YOLO 系列来做落地。原因在于 YOLO 训练流程对标注文件格式要求简单(每行一个实例),社区资料密集,虽然是“目标检测数据集”,但最终形态几乎必然是一个 YOLO 格式的目录结构。2k 张图的规模也适合在单卡 GPU 上完成训练,不需要分布式方案。文章的主要受众是两类人:一类是算法工程师,拿着这批数据想快速评估标签质量和训练基线;另一类是农业信息化方向的技术人员,需要理解成熟度标注怎么设计才不至于把模型练偏。下面所有操作都以这份数据集存在为前提,但步骤和参数同样适用于自己标注的其他成熟度数据集。
2. 标签格式与草莓成熟度类别体系的对应关系
2.1 成熟度划分的类别数量决定标签文件的第一列
一份目标检测数据集的标签文件,第一列永远是类别 ID。对于草莓成熟度识别,类别划分有几档常见做法:二分类(可采/不可采)、三分类(青/转色/红)、四分类(青/转色/红/过熟)。这份数据集标注了超过2k张图片,大概率采用三分类或四分类,因为只分两类的数据集对算法工程师的参考价值很有限——现实分拣线更需要知道“什么时候摘”而不是“能不能摘”。
标注类别的具体方式直接决定data.yaml的写法。以三分类为例,YOLO 格式的data.yaml应该写成:
# data.yaml path: ./strawberry_dataset train: images/train val: images/val names: 0: unripe 1: turning 2: ripe这里把unripe放在 ID 0 是刻意为之:数据集里任何未知背景都会被模型当作背景,把最常见的负样本类放在靠前位置,预热阶段收敛更稳。而names的排序要和标注文件里的整数 ID 严格对齐,这个映射关系一旦错位,训练出来的模型在推理时会输出张冠李戴的成熟度。
提示:如果标注工具(LabelImg、X-AnyLabeling 或 Roboflow)导出的类别顺序和这里不一致,先在脚本里把类别 ID 做一次重映射,不要在
data.yaml里硬改顺序。
2.2 标签文件内容与常见坐标陷阱
YOLO 格式的标签文件是.txt,文件名与图片名保持一致(如IMG_2043.jpg对应IMG_2043.txt),内容每行五个数值,顺序是:类别ID、归一化中心x、归一化中心y、归一化宽度、归一化高度。下面的代码可以快速体检一份标签文件是否合格:
# inspect_labels.py import os label_dir = "strawberry_dataset/labels/train" bad_files = [] for fname in os.listdir(label_dir): if not fname.endswith(".txt"): continue with open(os.path.join(label_dir, fname), "r") as f: lines = f.readlines() for idx, line in enumerate(lines): parts = line.strip().split() if len(parts) != 5: bad_files.append((fname, idx, "列数错误")) continue cls_id = int(parts[0]) x, y, w, h = map(float, parts[1:]) if cls_id not in (0, 1, 2): bad_files.append((fname, idx, f"类别ID非法: {cls_id}")) if not (0 < x < 1 and 0 < y < 1): bad_files.append((fname, idx, "中心点越界")) if not (0 < w <= 1 and 0 < h <= 1): bad_files.append((fname, idx, "宽高越界")) print(f"检查 {len(os.listdir(label_dir))} 个标签文件") if bad_files: print(f"发现 {len(bad_files)} 处问题, 前5个: {bad_files[:5]}") else: print("全部标签格式合法")脚本的核心检查点是归一化坐标是否落在 (0,1) 区间内。这个脚本检查出来问题后,一个需要注意的坑是:label 里 x、y 是中心点而非左上角,很多从 COCO 格式转过来的数据集容易在这里栽跟头,转格式时的坐标换算公式是x_center = (x_min + x_max) / 2 / img_width。
3. 用 YOLOv8 在 2k 张草莓成熟度数据集上完成一次完整训练
3.1 数据集目录结构与 train/val 划分标准
拿到数据集后第一步不是直接训练,而是把目录结构整理到 YOLOv8 认识的形态。标准结构如下:
strawberry_dataset/ ├── data.yaml ├── images/ │ ├── train/ # ~1600张 │ └── val/ # ~400张 └── labels/ ├── train/ # 与images/train同名对应的txt └── val/ # 与images/val同名对应的txt训练集与验证集的比例建议 8:2 或 8.5:1.5。2k 张图片的规模下,验证集至少留 300 张,否则成熟度相邻类别的 mAP 波动会非常大,不好判断模型是否真的收敛。划分时还要注意同一颗草莓的不同视角图片不能同时落在训练集和验证集里,否则会造成数据泄漏,mAP 虚高,但到了真实分拣线上被打回原形。
官方推荐用ultralytics库自带的划分逻辑,但更可控的做法是写一个按“果实实例分组”的划分脚本:
# 在项目根目录执行, 先装依赖 pip install ultralytics scikit-learn # 划分数据集(按图片文件名前缀分组) python - <<'EOF' import os, shutil from sklearn.model_selection import train_test_split img_dir = "strawberry_dataset/images/all" imgs = [f for f in os.listdir(img_dir) if f.endswith(".jpg")] train_imgs, val_imgs = train_test_split(imgs, test_size=0.2, random_state=42) os.makedirs("strawberry_dataset/images/train", exist_ok=True) os.makedirs("strawberry_dataset/images/val", exist_ok=True) os.makedirs("strawberry_dataset/labels/train", exist_ok=True) os.makedirs("strawberry_dataset/labels/val", exist_ok=True) for split, fnames in [("train", train_imgs), ("val", val_imgs)]: for fname in fnames: stem = os.path.splitext(fname)[0] shutil.copy(os.path.join(img_dir, fname), f"strawberry_dataset/images/{split}/{fname}") lbl = f"strawberry_dataset/labels/all/{stem}.txt" if os.path.exists(lbl): shutil.copy(lbl, f"strawberry_dataset/labels/{split}/{stem}.txt") else: print(f"警告: {stem} 缺少标签文件") print(f"训练集 {len(train_imgs)} 张, 验证集 {len(val_imgs)} 张") EOF分组划分时不要只按文件名顺序切一刀,要保证成熟度类别在训练集和验证集中的分布比例一致,否则会出现“训练集全是青果、验证集全是熟果”的极端情况,mAP 看起来还行但实际检测不可用。划分后可以打印每个类别的数量分布来复核。
3.2 yolo 目标检测流程中的关键训练参数设置
YOLOv8 训练命令是 yolo 目标检测流程里最常见的执行入口。针对 2k 张草莓图,imgsz=640是默认值,不需要为了“看得更细”调到 1280,因为草莓成熟度的识别主要依赖颜色和表面纹理分布,不是依赖小目标的细节像素——调大分辨率只会让小 GPU 的显存爆炸,收益微乎其微。
# 单卡训练, 以 yolov8s 为预训练权重 yolo train \ model=yolov8s.pt \ data=strawberry_dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ patience=15 \ project=runs/strawberry \ name=maturity_v1 \ workers=4参数说明:
model=yolov8s.pt:从 COCO 预训练的 small 版本开始训练,迁移学习能大幅缩短收敛时间,比yolov8n精度高,比yolov8m训练快,是 2k 数据量级下性价比较高的选择patience=15:验证集 loss 连续 15 个 epoch 不下降就提前停止。草莓成熟度数据集类别之间差异相对明显,通常 60 个 epoch 左右就能收敛,这个值可以有效防止过拟合workers=4:数据加载进程数,Windows 上如果报错可改为 0,Linux 上保持 4 能明显提升 GPU 利用率
训练过程中重点关注终端输出的mAP50-95和mAP50两个指标。对成熟度识别来说,mAP50的意义远大于mAP50-95——因为草莓果实不算小目标,定位精度不需要像素级苛刻,成熟度判断的核心是框住果实、再判类别。如果mAP50能到 0.9 以上而mAP50-95只有 0.6,这是正常的,不必焦虑。
3.3 训练完成后推理验证最小命令
训练结束后的推理命令同样用 yolo 入口,指向验证集图片,输出带成熟度标签的标注图:
# 用最优权重对验证集推理并保存可视化结果 yolo predict \ model=runs/strawberry/maturity_v1/weights/best.pt \ source=strawberry_dataset/images/val \ save=True \ conf=0.25 \ project=runs/strawberry_predict \ name=val_pred其中conf=0.25表示置信度阈值设为 0.25,低于这个值的检测框会被丢弃。成熟度识别场景下建议把阈值设置在 0.2~0.3 之间——草莓转色阶段的果实颜色介于青红之间,模型容易给出相对较低的置信度,把阈值设太高会把“转色”这一类几乎全部滤掉,直接导致成熟度分布统计失真。预测结果保存在runs/strawberry_predict/val_pred/下,可以直接用图片查看器浏览,肉眼快速检查边界情况。
4. 成熟度识别比普通目标检测多出来的三类决策逻辑
4.1 类别定义的连续性与相邻类别的边界处理
普通目标检测的类别之间通常是离散的(猫不是狗,车不是人),但草莓成熟度是连续变化的过程——青果和转色之间、转色和成熟之间没有一条干净的分界线。这是草莓成熟度目标检测数据集与通用目标检测数据集最本质的区别。
处理这个问题的推荐做法是训练时引入“软边界”策略:在标注转色阶段时,只把颜色明显处于中间态的草莓标为 turning,把稍有泛红但整体仍以青绿色为主的标为 unripe,把九成熟以上但还没有完全变红的标为 ripe——宁可在边界处“错标”到相邻类别,也尽量不要引入一个模糊的“半转色”类别。四分类或五分类的标签体系看着更精细,但对于 2k 张图的数据集,样本被稀释后每个类别的特征都学不充分,边界反而更混乱。
一个直接的验证方法是训练后查看混淆矩阵。YOLOv8 训练会在runs/strawberry/maturity_v1/confusion_matrix.png生成混淆矩阵图,重点观察unripe与turning、turning与ripe这两组相邻类的互混比例。如果互混超过 20%,优先检查标注是否存在系统性偏差,用下面代码统计类别分布:
# count_class_distribution.py import glob from collections import Counter cnt = Counter() for lbl_file in glob.glob("strawberry_dataset/labels/train/*.txt"): with open(lbl_file) as f: for line in f: cls_id = int(line.split()[0]) cnt[cls_id] += 1 total = sum(cnt.values()) for cls_id in range(3): print(f"类别 {cls_id} 实例占比: {cnt[cls_id]/total:.2%}")4.2 从“框住果实”到“匹配成熟度”的推理逻辑
用训练好的权重做推理,输出格式不止是画框。实际分拣场景中,经常需要把同一画面里的多个草莓按成熟度分组计数。YOLOv8 的 Python API 可以拿到结构化结果:
# run_inference.py from ultralytics import YOLO model = YOLO("runs/strawberry/maturity_v1/weights/best.pt") results = model("test_images/row1.jpg", conf=0.25) for r in results: boxes = r.boxes cls_names = [model.names[int(c)] for c in boxes.cls.tolist()] confs = boxes.conf.tolist() counts = {name: cls_names.count(name) for name in set(cls_names)} print(f"检测框数量: {len(boxes)}") print(f"成熟度分布: {counts}") for name, conf in zip(cls_names, confs): print(f" - {name}, 置信度: {conf:.2f}")这段代码的用途是直接对接后续的分析模块——根据counts里的分布可以决定是否触发采摘指令,或者把turning比例过高作为“再等一天”的决策信号。成熟度识别的价值不止于检测本身,而是为采摘决策提供量化输入,这正是它区别于普通“找到目标”类数据集的落点。
4.3 光照与拍摄角度对成熟度类别的干扰及应对
草莓成熟度数据集的第二只拦路虎是光照。同一颗七成熟的草莓,在强光直射下可能被标成 ripe,在阴影里又会被标成 turning。数据集超过2k张图片时,标注时往往没有对光照做分层处理,容易让模型学到“亮即熟”这种错误的捷径。
推荐引入一个简单的数据增强策略来压制这个问题——在训练配置里增加 HSV 扰动参数:
yolo train \ model=yolov8s.pt \ data=strawberry_dataset/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ hsv_h=0.015 \ hsv_s=0.7 \ hsv_v=0.4 \ mosaic=0.5 \ fliplr=0.5这里的hsv_h=0.015表示色相扰动幅度,对颜色敏感的成熟度识别任务来说这个值要尽可能小(0.01~0.02 为宜),色相是定义草莓红绿边界的最关键维度,扰动过大会直接破坏类别定义,让模型在青果和熟果之间摇摆。hsv_s(饱和度)和hsv_v(明度)可以适当调大,模拟不同光照条件下的果面表现,这能显著提升模型在真实分拣线上的泛化能力。mosaic降到 0.5 而不是默认的 1.0,是为了在 2k 张图的规模下保留更多原图中的真实背景上下文。
5. 评估成熟度检测效果的几个针对性验证技巧
5.1 预留一屏“标签噪声样本”专门观察边界表现
通用目标检测的评估看的是整体 mAP,但成熟度数据集必须单独观察边界样本。从验证集中挑出三类各 30 张典型图——包括明显青绿但带一丝红的、红色占 60% 左右仍在转色的、以及深红发暗的过熟果——把它们单独放到一个文件夹反复做推理测试。
# 单独跑边界样本集 yolo predict \ model=runs/strawberry/maturity_v1/weights/best.pt \ source=boundary_samples/ \ save=True \ conf=0.2 \ project=runs/strawberry_predict \ name=boundary_check这类测试的判定标准只有一个:是否稳定落在人工标注的相邻类别上。比如一张人工标为 turning 的草莓,模型输出 ripe 可以接受;但如果输出 unripe,说明模型的类别边界整体偏离,需要回看训练集的标注一致性,而不是盲目调阈值。
提示:YOLOv8 的 predict 模式默认输出的是带标签的可视化图,里面每颗草莓框上都标了类别名和置信度,比直接看数字指标更直观。
5.2 用混淆矩阵和类别 mAP 验证成熟度分类的可靠性
训练结束后,在runs/strawberry/maturity_v1/目录下会生成三份关键文件——confusion_matrix.png、results.png、PR_curve.png。对成熟度数据集来说,比起整体 mAP,更值得打印的是每类详细指标:
# metrics_per_class.py from ultralytics import YOLO model = YOLO("runs/strawberry/maturity_v1/weights/best.pt") metrics = model.val(data="strawberry_dataset/data.yaml", split="val") for i, name in model.names.items(): cls_ap50 = metrics.box.ap50[i] cls_ap = metrics.box.ap[i] print(f"{name:10s} AP50: {cls_ap50:.4f} AP50-95: {cls_ap:.4f}")如果unripe的 AP50 明显低于ripe(差距超过 0.1),大概率是数据集中青果样本量偏少或者青果与叶片背景颜色接近导致的误检,这时需要回到数据集层面补充青果样本,而不是增加训练轮数。
5.3 成熟度数据集常见的过拟合信号与对应调整
2k 张规模的数据在训练 100 epoch 时极易出现过拟合,最直接的信号是训练集 loss 持续下降而验证集 loss 在某个 epoch 后反弹。对应调整手段按优先级排列:
第一,检查patience是否生效,如果训练在 60 epoch 后就停止,说明验证集已经连续 15 轮没有改善,此时不必强迫它跑到 100 轮。第二,给mosaic=0.5的基础上额外加mixup=0.2,混合样本增强在颜色连续变化的任务上有奇效,能模拟出不同成熟度果实相邻出现的真实场景。第三,降低batch到 8 并对所有图片做一次居中裁剪,减少模型对背景纹理的记忆。
最后还要提一个容易被忽视的验证手段:把best.pt在完全没有标签的新拍摄视频上跑一次连续帧推理,观察同一颗草莓在视角变化时成熟度类别是否跳变。如果频繁跳变,说明模型的类别边界对观察角度敏感,此时最有效的方法不是调模型,而是在标注阶段把多角度样本覆盖进去——这正是超过2k张图片的数据集值得投入的延伸价值所在。
本文还有配套的精品资源,点击获取