news 2026/9/11 22:45:16

石榴成熟度检测实战:YOLO与VOC数据集训练与评估指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
石榴成熟度检测实战:YOLO与VOC数据集训练与评估指南

简介:目标检测任务中,石榴成熟阶段识别是农业智能化与果园管理的重要环节。这份数据集面向计算机视觉初学者及农业AI项目开发者,提供5855张清晰标注的成熟阶段检测图片,覆盖花蕾、早果、盛花、中果、成熟五个阶段,共11482个矩形框,可直接用于YOLO系列和Faster R-CNN等检测模型的训练与验证。压缩包整体约406.53MB,共2000个文件,以1999个XML标注文件与1个TXT说明文件为主,分别对应VOC格式与YOLO格式的标签文件,图片、XML、TXT按目录分类存放,便于工程化读取与格式转换。数据未做增强处理,标注严谨、质量可靠,可有效减少数据采集与清洗耗时。除检测模型训练外,还可用于农业场景下的果实计数、成熟度分析等任务。目前已有79人学习浏览,适合作为目标检测项目的基础数据资源。

1. 石榴成熟阶段检测:比普通目标检测更考验工程判断的一类任务

把“找到石榴”和“判断石榴到了哪个阶段”放在一起,难度不是叠加,是乘出来的。普通目标检测的类别差异是离散的,而成熟度检测的五个阶段之间没有硬边界,同一个果实换个角度、换个光照,视觉特征就可能滑到相邻类别里去。这个标题给的是 5855 张石榴图像、5 个成熟阶段、YOLO 与 VOC 双格式标注的现成数据集,适合两类人用:一类是做农业自动化采摘或分级的工程师,需要快速验证一个检测方案;另一类是刚入门目标检测,但想避开“只跑通官方示例”这个舒适区的研究者。拿到这种数据,第一件事不是直接训练,而是先把标注格式、阶段分布和类别边界梳理清楚。

2. 5855张石榴图像与5阶段标注:数据集构成与格式拆解

2.1 五个成熟阶段的划分逻辑与边界问题

石榴从坐果到采收的周期里,果皮颜色由绿转黄绿、再转红、最后变暗红或开裂,标注这五个阶段时不同标注员的主观判断差异很大。常见做法是把“全绿且果实体积未定型”标为阶段一,“绿中带红晕”标为阶段二,“红绿相间但红色未连片”标为阶段三,“全红且果皮有光泽”标为阶段四,“果皮颜色发暗、出现裂纹或明显过熟斑”标为阶段五。这套划分在农业上对应“不可采、暂缓采、可试采、适采、过熟”五个商业动作。

阶段三和阶段四是最容易混淆的区域,因为红色占比是一个连续量,不是开关量。有些石榴在遮阴面还是绿色,但向阳面已经全红,模型很难判。另一个常见坑是类别不平衡:果园巡检数据里,适采期的图像往往最多,转色期和过熟期的样本明显偏少。训练前先统计类别分布,如果少数类占比低于 10%,就需要考虑重采样或者在损失函数里加权重,否则最终模型对临界阶段的召回会很难看。

2.2 YOLO格式与VOC格式的字段差异

这个数据集同时给了 YOLO 和 VOC 两种标注,但两者转换时容易丢信息,所以先把字段差异说清楚。VOC 格式是每个图像对应一个 XML 文件,框坐标是像素绝对值,以左上角为原点,记录 xmin、ymin、xmax、ymax 四个整数;YOLO 格式是每个图像对应一个 txt 文件,每行一个检测目标,记录类别索引、中心点 x、中心点 y、宽度 w、高度 h,五个数值全部相对图像宽高做了归一化。表面看只是坐标系不同,实际使用中有三个影响训练结果的点。

维度VOC(.xml)YOLO(.txt)
坐标基准像素绝对坐标,整数相对图像宽高的浮点数,0~1
框表达方式xmin, ymin, xmax, ymaxx_center, y_center, width, height
类别表示<name>节点中的字符串行首整数,对应 data.yaml 类别索引
单图多目标多个<object>节点每行一个目标,行数等于目标数

YOLO 格式不保存图像尺寸信息,单独看 txt 文件无法还原像素坐标,所以转换工具里必须有原图尺寸。反过来,VOC 的 XML 里有<size>节点保存宽高,转 YOLO 时直接取值即可,不需要额外读图。另一个细节是类别索引必须和训练时的 data.yaml 声明保持一致,否则模型会把阶段一当成阶段二来学。

2.3 用脚本给5855张图做一次标注体检

拿到的标注不一定干净,常见问题包括:坐标值越界、负宽高、类别索引超过声明范围、空 txt 文件、同一条记录重复出现。我一般会先跑一个健康检查脚本,把这些问题全量扫一遍,再决定是否直接使用或挑出脏数据重新标注。

# inspect_yolo_labels.py # 用法: python inspect_yolo_labels.py --labels ./labels --num_classes 5 import argparse from pathlib import Path from collections import Counter import numpy as np def main(): parser = argparse.ArgumentParser() parser.add_argument("--labels", type=Path, required=True, help="YOLO标签目录") parser.add_argument("--num_classes", type=int, default=5, help="类别总数") args = parser.parse_args() cls_counter = Counter() box_wh = [] # 收集归一化框宽高,查看尺度分布 malformed = [] # 格式异常文件 empty_files = [] # 空标签文件 for txt_path in sorted(args.labels.glob("*.txt")): lines = [ln.strip() for ln in txt_path.read_text().splitlines() if ln.strip()] if not lines: empty_files.append(txt_path.name) continue for ln in lines: parts = ln.split() if len(parts) != 5: malformed.append((txt_path.name, ln)) continue cls_id, xc, yc, w, h = map(float, parts) if cls_id >= args.num_classes or w <= 0 or h <= 0: malformed.append((txt_path.name, ln)) continue if not (0 <= xc <= 1 and 0 <= yc <= 1): malformed.append((txt_path.name, ln)) continue cls_counter[int(cls_id)] += 1 box_wh.append((w, h)) print("类别分布:", dict(sorted(cls_counter.items()))) if box_wh: wh_arr = np.array(box_wh) print("框宽均值: %.4f, 框高均值: %.4f" % (wh_arr[:, 0].mean(), wh_arr[:, 1].mean())) print("空标签文件数:", len(empty_files), empty_files[:5]) print("异常标注行数:", len(malformed), malformed[:5]) if __name__ == "__main__": main()

脚本里对 cls_id 做了整数比较,避免类别索引写成"2.0"这种浮点字符串导致转换后匹配不上。框宽高均值能帮助判断是整棵树级别的检测还是果实级别的检测,如果框太小,后期做 NMS 或数据增强时要考虑裁剪边界。空标签文件要重点排查,尤其是图像里确实有果实但标注遗漏的情况,这种情况最影响训练——模型会以为“无目标”才是正确输出。

3. VOC与YOLO互转:坐标换算与数据集划分实战

3.1 从四角坐标到中心点坐标的换算逻辑

VOC 转 YOLO 时的核心公式不复杂,但容易写错符号。VOC 记录的是左上角 (xmin, ymin) 和右下角 (xmax, ymax),YOLO 需要的是中心点坐标和宽高,并且全部要用图像宽高做归一化。中心点 x 的计算是(xmin + xmax) / 2 / img_width,框宽是(xmax - xmin) / img_width,y 和 h 同理。这里最容易踩坑的是把除法的分子写成xmax - xmin但忘了除以图像宽度,导致一个像素值混进归一化空间,训练时框位置严重偏移。转换后拿一张图做可视化核对,比看一百条数字管用。

3.2 数据集划分要考虑光照和拍摄批次

划分训练集、验证集、测试集时,不能直接对所有文件做随机 shuffle。果园采集数据通常是一棵树上连续拍几十张,同一棵石榴树下相邻照片的背景、光照、果实位置高度相似,随机划分会把同株果实的照片同时分进训练集和验证集,导致验证指标虚高。更稳妥的做法是把同一个采集批次或同一棵树的图像视为一个组,整组划入训练或验证。如果文件命名里带拍摄时间或树号信息,可以按这个前缀分组;如果没有任何分组信息,那就只能退回到随机划分,但要意识到评估结果会比真实场景乐观。

类别分层也是一个要点。阶段一和阶段五的样本通常远少于阶段四,验证集里少数类可能只有几十条,mAP 波动会非常大。建议按类别比例做分层抽样,确保验证集里每个阶段都有足够样本。常见比例是 7:2:1,数据量大且类别均衡时也可以用 8:1.5:0.5。

3.3 一个可直接改写的VOC转YOLO转换脚本

# voc2yolo.py # 用法: python voc2yolo.py --voc_dir ./annotations --image_dir ./images --out_dir ./labels import xml.etree.ElementTree as ET from pathlib import Path import argparse CLASSES = ["immature", "color_turning", "half_ripe", "ripe", "overripe"] # 顺序与data.yaml一致 def voc_to_yolo(xml_path: Path, img_w: int, img_h: int) -> list[str]: tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall("object"): cls = obj.find("name").text.strip() if cls not in CLASSES: continue 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) xc = (xmin + xmax) / 2.0 / img_w yc = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h cls_id = CLASSES.index(cls) lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") return lines def main(): parser = argparse.ArgumentParser() parser.add_argument("--voc_dir", type=Path, required=True) parser.add_argument("--image_dir", type=Path, required=True) parser.add_argument("--out_dir", type=Path, required=True) args = parser.parse_args() args.out_dir.mkdir(parents=True, exist_ok=True) for xml_path in sorted(args.voc_dir.glob("*.xml")): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = voc_to_yolo(xml_path, img_w, img_h) out_txt = args.out_dir / (xml_path.stem + ".txt") out_txt.write_text("\n".join(lines)) if __name__ == "__main__": main()

这里有一个容易忽视的问题:转换时读的宽高来自 XML 里的<size>节点,不是实际图像文件。如果标注工具写入的 size 和图像真实尺寸不一致,转换出来的 YOLO 坐标全部错位。稳妥做法是在转换前后随机抽样,用 OpenCVimread读图对比实际宽高,确认一致再批量执行。CLASSES 列表顺序改动会导致所有标签重新映射,所以训练前把它和 data.yaml 里的 names 字段对齐是最高优先级。

4. YOLOv8训练配置:成熟度边界的数据集该怎么做超参数调优

4.1 用data.yaml声明五个阶段并启动训练

拿到整理好的标签后,先写训练用的 data.yaml。路径建议用绝对路径或相对 data.yaml 文件的路径,避免不同机器上目录结构不一致导致训练中断。

# data.yaml # 数据集根目录 path: ./datasets/pomegranate train: images/train val: images/val test: images/test nc: 5 names: 0: immature 1: color_turning 2: half_ripe 3: ripe 4: overripe

启动训练的命令如下:

yolo detect train \ model=yolov8s.pt \ data=data.yaml \ imgsz=640 \ batch=16 \ epochs=120 \ optimizer=AdamW \ lr0=0.001 \ device=0

model=yolov8s.pt是在 COCO 预训练权重上继续微调,迁移学习对农业场景通常有帮助。imgsz=640是常规选择,但如果石榴在图像里占比大,可以降到 512 提升推理速度;如果一棵树上果实小且密集,可以升到 768。batch的值以显存不爆为下限,16 到 32 是常选区段。epochs设在 100 到 150 之间,成熟阶段类别边界模糊,收敛比普通检测慢一些,只跑 50 轮往往欠拟合。

4.2 五个关键超参数与不同场景的推荐值

参数推荐区间说明
imgsz512 / 640 / 768果实占比大用小值,小目标多用大值
batch16 / 32受显存限制,小 batch 配小数据增强
epochs100 / 120 / 150看验证损失是否还有下降趋势
optimizerAdamW / SGD数据量小用 AdamW,数据量大用 SGD 更稳
lr00.001(AdamW)/ 0.01(SGD)预训练权重迁移时不宜过大

显存不足时优先降 batch 而不是降 imgsz,因为降 imgsz 会直接损失小目标的框精度。训练过程中要盯着 val 损失,如果训练损失持续下降但验证损失在第 40 轮后不再变化甚至回升,就是过拟合信号,可以提前停止或把数据增强里的翻转概率调高。

4.3 YOLOv8损失函数里和阶段边界相关的两个细节

YOLOv8 的损失由三部分构成:边界框损失box_loss、分类损失cls_loss、分布式焦点损失dfl_loss。默认权重是 box=7.5、cls=0.5、dfl=1.5,但成熟阶段检测里最需要关注的不是默认权重,而是相邻类别的误判模式。阶段二和阶段三、阶段四和阶段五之间的视觉差异小,模型输出概率会在这两对类别上出现明显的高分纠缠,这时如果少数类样本不足,分类损失会主导训练方向,导致模型把边界样本一路推向多数类。

常见做法是在训练时把类别权重传入损失函数,让少数类的分类错误产生更大的梯度。Ultralytics 提供class_weights参数,也可以自己在损失函数外部做一个小技巧:对每个类别的损失乘一个权重系数,权重取总样本数除以各类别样本数的平方根再归一化。类别差别不超过 3 倍时,这个操作带来的收益有限;一旦超过 5 倍,几乎必须做。

另一个细节是dfl_loss,它负责让框的分布更精准。如果标注框本身边界画得松,比如有的标注员把石榴外圈果皮算进去,有的只包住果肉区域,dfl 会把框学得不够收敛。遇到这种情况,先锁模型权重在 0.6 以下,用一个辅助脚本统计同一张图不同标注员的框交并比,框质量太差不做清洗就训练,后期无论怎么调参都要从标注重来。

5. 评估不只看mAP:用混淆矩阵定位成熟阶段误判方向

5.1 从验证命令到mAP指标的读取方式

训练完成后跑验证的命令很简单:

yolo detect val \ model=runs/detect/train/weights/best.pt \ data=data.yaml \ conf=0.001 \ iou=0.7 \ device=0

conf=0.001是把置信度阈值压到最低,保证所有检测框都参与评估,这样算出的 mAP 更客观;iou=0.7是指预测框和真实框的交并比大于 0.7 才视为匹配,这个值在果实密度高的图片里可以调到 0.5,因为果实相互遮挡时标注框本身就很难完全对齐。训练过程会自动生成results.png,里面有 PR 曲线、F1 曲线和混淆矩阵热力图。

5.2 mAP50和mAP50-95怎么穿插使用

mAP50 衡量的是预测框和真实框 IoU 大于 0.5 时的平均精度,对定位误差容忍度较高;mAP50-95 是从 0.5 到 0.95 按 0.05 步长取十个阈值分别计算 mAP 再取平均,对框的精度要求严苛。成熟阶段检测场景里,如果只关心果实能不能被采摘执行器对准,mAP50 够用;如果要做精细分拣,mAP50-95 更能反映框边界的质量。两个指标需要同时看,不能只追某一个。

5.3 混淆矩阵里的阶段误判怎么定位

混淆矩阵的横轴是真实类别,纵轴是预测类别,对角线越亮越好。注意看对角线两侧的邻值方块,阶段二和阶段三之间的误判,以及阶段四和阶段五之间的误判,方向不同处理方式不同。如果阶段四被误判成阶段五,多半是颜色过深的样本过多,模型被带偏;如果阶段五被误判成阶段四,通常是过熟期的特征不够明显,比如裂果样本太少。前者通过加重少数类权重解决,后者要补充或合成过熟样本。

误判模式可能原因建议处理
阶段四 → 阶段五适采期不可采样本偏多降低多数类权重或用难例挖掘
阶段五 → 阶段四过熟样本量不足补充标注或做颜色增强
阶段二 ↔ 阶段三转色期视觉边界本身模糊局部类别重定义,合并或细分

6. 采摘机器人上的成熟度稳定输出:置信度时间滑动平均

训练和评估之后,实际部署时的一个常见痛点是单帧检测结果不稳定。相机是在移动中拍摄石榴的,角度、遮挡、光照变化会让同一个果实连续几帧里阶段判断来回跳,直接影响后端执行器该不该摘。一个有效做法是让模型输出连续帧的置信度向量,做指数滑动平均,再取最终类别。

# confidence_smoother.py # 用法: 初始化后逐帧输入YOLO输出的5维置信度向量 import numpy as np class MaturitySmoother: """对连续帧的成熟度置信度做指数滑动平均""" def __init__(self, alpha: float = 0.4): self.alpha = alpha self.smooth = None def update(self, probs: list[float]) -> int: arr = np.asarray(probs, dtype=float) if self.smooth is None: self.smooth = arr else: self.smooth = (1 - self.alpha) * self.smooth + self.alpha * arr return int(np.argmax(self.smooth))

alpha取值在 0.2 到 0.5 之间比较稳,值越大对当前帧响应越快,但抗抖动能力弱;值越小输出越平滑,但会引入 2 到 3 帧的延迟。在采摘执行器动作速度为每秒 2 次的场景里,0.4 基本能兼顾响应和稳定。除了平滑,还可以加一个最小置信度门限:当平滑后的最高置信度仍低于 0.6 时,输出“待定”而不是硬选一个阶段。这个门限在过熟阶段尤其有用,模型犹豫时的默认动作应该是“不摘”,而不是误判成适采期造成损失。

最后的经验是,平滑器的输入务必用 softmax 或 sigmoid 归一化后的置信度,不要用原始的 logits。YOLO 输出头的数值分布在不同训练轮次差异较大,归一化后滑动平均才能对齐。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 22:45:14

YOLOv10玩手机行为检测实战:万级标注数据集+即用权重

简介&#xff1a;本资源面向计算机视觉方向的算法工程师、高校科研人员及AI竞赛参赛者&#xff0c;聚焦于驾驶场景下危险行为识别这一实际落地需求&#xff0c;提供YOLOv10玩手机/打电话检测的完整训练方案。资源包含已训练好的YOLOv10权重文件、约1万张高质量标注图像构成的数…

作者头像 李华
网站建设 2026/9/11 22:45:02

基于树莓派与Python的寝室监控系统:从运动检测到Flask视频流部署

简介&#xff1a;这是一套基于Python与树莓派打造的寝室小监控系统毕业设计项目&#xff0c;面向软件工程、计算机科学、自动化、电子信息等专业的在校生&#xff0c;适用于毕业设计、课程设计、项目演示或初期立项参考。整套资源聚焦监控场景下的图像采集、状态识别与异常通知…

作者头像 李华
网站建设 2026/9/11 22:44:48

FP8013与FP7153对比:3A大电流手电双电源驱动方案选型实战

去年接了一款户外强光手电的设计需求&#xff0c;客户开口就是三个硬指标&#xff1a;驱动电流做到3A、单节18650要能跑满、还要支持USB-C直接供电。前两个指标在驱动芯片里不算罕见&#xff0c;第三个直接把一批升压方案卡掉了。最后筛选下来&#xff0c;FP8013和FP7153这两颗…

作者头像 李华
网站建设 2026/9/11 22:44:26

猕猴桃检测数据集详解:VOC/YOLO格式转换与YOLOv8训练实践

简介&#xff1a;猕猴桃检测数据集是一份面向目标检测任务的专业标注数据&#xff0c;适合计算机视觉入门者与农业AI开发者用于训练猕猴桃识别模型。数据同时提供Pascal VOC格式的XML标注和YOLO格式的TXT标注&#xff0c;覆盖1838张猕猴桃图像&#xff0c;共包含2000个文件&…

作者头像 李华
网站建设 2026/9/11 22:41:44

单片机毕设选题推荐:基于 STM32 或 51 单片机的多区域无线温度采集报警装置设计 基于 STM32 或 51 单片机的按键可调阈值无线温度监测系统设计(022807)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/9/11 22:37:35

中文手写识别实战:基于PyTorch和CNN的HWDB数据集训练全流程

简介&#xff1a;基于PyTorch的中文手写汉字识别项目&#xff0c;面向具备一定深度学习基础的高校学生或研究人员&#xff0c;可作为高级课程期末项目参考。项目采用HWDB手写汉字数据集&#xff0c;完整覆盖CNN网络设计、图像归一化与灰度预处理、模型训练与准确率评估等流程。…

作者头像 李华