简介面向石榴成熟阶段检测的目标检测数据集覆盖花蕾、早果、花朵、中果、成熟5个生长阶段标注框总数达11482个适合农业视觉识别、智慧果园监测等场景下的模型训练与算法研究。压缩包整体约406.53MB共2000个文件以VOC格式的XML标注文件与YOLO格式的TXT标签说明为主并配套对应JPG图片数据清晰且未做增强矩形框标注规范准确。压缩包内目录划分明确JPEGImages、Annotations、labels分别存放图片、XML和TXT文件可直接用于格式转换与模型输入。目前已有79人学习且声明不保证训练精度但数据集本身提供准确合理的标注可帮助研究者快速完成数据准备、预处理和检测模型调优。1. 石榴成熟阶段检测数据集5855张5个阶段做农业目标检测的第一手标注资源做果树生长监测的人最头疼的不是模型选型而是标注数据。石榴从开花到成熟中间隔着bud、flower、early-fruit、mid-growth、ripe五个阶段形态差异大边界模糊自己拍图加标注没两三周下不来。这份数据集把最耗时的那部分直接打包好了5855张清晰图片VOC和YOLO双格式标注共11482个矩形框。下载解压后JPEGImages、Annotations、labels三个文件夹齐整排列能直接喂给YOLO系列训练。无论是做毕业设计、跑通目标检测流程还是做石榴生长阶段识别的原型验证它都是一份能省下大量重复劳动的起步资源。接下来从数据组织、格式转换到训练避坑按实际使用顺序逐一拆开讲。2. 拆开压缩包看数据结构三个文件夹、5855张图与五类标签的对应关系拿到压缩包后先别急着跑训练把文件夹结构和标签分布摸清楚后面能少踩一半的坑。这套数据集的组织方式是目标检测任务中最常见也最稳妥的三目录结构图片、VOC标注、YOLO标注各占一处彼此通过文件名关联。2.1 文件夹划分与双格式的工作方式压缩包解压后核心是三个平级文件夹。JPEGImages存放全部jpg图片Annotations存放同名xml文件labels存放同名txt文件。三者一一对应比如xyxr_images_hehe4138.jpg对应xyxr_images_hehe4138.xml和xyxr_images_hehe4138.txt。这种布局的直接好处是用YOLO训练时只需要指定images和labels两个路径模型会自动按同名字找标注用VOC格式做迁移学习或跑SSD、Faster R-CNN时直接指向Annotations即可不需要额外写转换脚本。我一般把整个解压后的目录结构保持原样不重命名不挪动避免路径字符串出幺蛾子。三个文件夹统计如下文件夹文件格式文件数量用途JPEGImages.jpg5855原始图像分辨率清晰未增强Annotations.xml5855VOC格式标注含对象类别与坐标labels.txt5855YOLO格式标注含归一化坐标需要留意的是labels文件夹内的txt是直接从VOC转换好的成品但类别编号不一定和人类直觉顺序一致。数据集的说明文档里特别强调了一点YOLO格式类别顺序以labels文件夹下的classes.txt为准这个文件通常只有五行分别对应五个阶段名。训练前务必打开看一眼确认编号0到4分别映射到哪个阶段。2.2 五类标签的框数分布说明什么五个阶段标签分别是bud、early-fruit、flower、mid-growth、ripe各标签的框数有一定差异标签名称阶段含义框数占比bud花蕾期243321.2%early-fruit幼果期130711.4%flower开花期282724.6%mid-growth中果期298626.0%ripe成熟期192916.8%总计11482个目标框每张图平均约1.96个目标。从分布能看出两个实用信息第一early-fruit只有1307框属于样本偏少的类别训练时如果不做针对性处理模型对它最容易漏检第二mid-growth和flower框数最多模型对这两类的拟合会更充分。做类别平衡、数据增强或loss加权时这个分布数据就是首要依据。数据集图片分辨率整体清晰未做增强处理。这意味着图像本身的亮度、翻转、噪声状态都是原始采集样貌。我建议训练时自己叠加mosaic、随机翻转和色彩抖动不必担心数据被二次处理过。3. 把VOC标注转成YOLO训练格式解析XML并归一化坐标的完整脚本虽然压缩包已经附带了YOLO格式的txt文件但实际训练时仍然建议自己从VOC转换一遍。一个很直接的原因是不同版本的YOLO工具对标注文件的解析细节不完全一致与其被txt的格式差异卡住不如把转换逻辑攥在自己手里出了问题能立刻定位。3.1 为什么最好自己转一次而不是依赖现成转换工具网上能找到不少现成的VOC转YOLO脚本但普遍有两个毛病。一是硬编码类别列表把原本的猫狗类顺序写死换到石榴五分类就得手工改数组二是脚本默认输入图片路径和xml路径都在同一级目录而这个数据集的Annotations里只有xml没有图片。更隐蔽的问题是某些转换工具在遇到坐标超出图像边界时会直接报错退出导致转换中断而手工脚本可以按需要做钳制或过滤控制粒度更细。亲手转换一次还有一个附带收获你会天然知道每个txt的第一列编号到底对应哪个阶段。这对后续调试预测结果极其重要。3.2 转换脚本从XML读取边界框并生成labels文本下面这个脚本以VOC xml为输入逐文件读取对象类别和边界框输出归一化坐标的YOLO txt文件。import os import xml.etree.ElementTree as ET voc_dir Annotations label_dir labels class_names [bud, early-fruit, flower, mid-growth, ripe] os.makedirs(label_dir, exist_okTrue) def convert_xml_to_yolo(xml_path, output_path): tree ET.parse(xml_path) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) bndbox obj.find(bndbox) x_min float(bndbox.find(xmin).text) y_min float(bndbox.find(ymin).text) x_max float(bndbox.find(xmax).text) y_max float(bndbox.find(ymax).text) x_center (x_min x_max) / 2.0 / img_width y_center (y_min y_max) / 2.0 / img_height box_width (x_max - x_min) / img_width box_height (y_max - y_min) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) with open(output_path, w) as f: f.write(\n.join(lines)) for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue xml_path os.path.join(voc_dir, xml_file) txt_name xml_file.replace(.xml, .txt) txt_path os.path.join(label_dir, txt_name) convert_xml_to_yolo(xml_path, txt_path) print(fconverted: {xml_file} - {txt_name})转换逻辑的核心是坐标归一化。VOC格式存的是像素绝对坐标YOLO格式需要的是以图像宽高为分母的相对坐标。这里使用x_center、y_center、width、height四个字段表示边界框全部控制在0到1区间内。类别编号直接按class_names的索引顺序生成0对应bud1对应early-fruit依次类推。代码中从xml中取值时通过root.find(size/width)这类路径写法定位节点比层层遍历更简洁。注意float()包裹了所有坐标值避免遇到整数字符串时类型不匹配。生成结果没有包含置信度字段纯检测任务不需要。转换完成后建议抽样打开几个txt文件检查重点看坐标值是否都在0到1之间。如果出现大于1的情况说明xml里的坐标超出了图像尺寸需要追溯图片是否被裁切过。4. 进入YOLO训练管线数据集划分、配置文件与训练参数选择标注格式就绪后下一步是把数据喂给YOLO训练。这里涉及三个关键动作划分训练集与验证集、编写数据集配置文件、启动训练并理解参数。4.1 按“训练/验证”比例切分数据而不是随机抽全部图片训练集和验证集切分不能图省事直接random.shuffle全部文件。因为同一次拍摄中相邻图片往往高度相似随机切分会导致验证集和训练集出现近亲样本测试分数虚高。我一般按文件名排序后先做分组再按比例选取。import os import random random.seed(42) image_dir JPEGImages label_dir labels train_ratio 0.85 images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] images.sort() random.shuffle(images) split_idx int(len(images) * train_ratio) train_images images[:split_idx] val_images images[split_idx:] with open(train.txt, w) as f: for img in train_images: f.write(os.path.abspath(os.path.join(image_dir, img)) \n) with open(val.txt, w) as f: for img in val_images: f.write(os.path.abspath(os.path.join(image_dir, img)) \n) print(ftrain: {len(train_images)}, val: {len(val_images)})这段代码首先固定随机种子为42确保每次运行划分结果一致。排序后shuffle再按比例切分可以避免连续文件名的图片全落在同一个集合里。写的train.txt和val.txt存的是图片的绝对路径YOLO训练时会自动按路径前缀去匹配同名的txt标注文件。seed参数可以按需要修改。有一点要注意如果后续要复现实验seed必须记录下来。如果换一台机器重跑随机种子相同但Python版本不同shuffle结果也可能变这属于正常现象。4.2 编写data.yaml并向模型指定路径以YOLOv8为例数据集配置文件是一个yaml文件包含路径和类别信息。下面这份配置可以直接复制使用path: /absolute/path/to/dataset_parent train: train.txt val: val.txt names: 0: bud 1: early-fruit 2: flower 3: mid-growth 4: ripepath字段填写数据集的上级目录绝对路径train和val填写划分出来的txt文件名。names字段的编号顺序必须和转换脚本里的class_names一致不能凭感觉排序。两种常见错误一是names顺序和labels里的txt第一列编号对不上导致模型标签语义错乱二是path字段写相对路径换目录运行训练时直接报FileNotFoundError。我用这个配置时习惯把path写成绝对路径一劳永逸。4.3 训练启动命令与关键参数选择训练命令本身不长但参数影响显著。以下是一份在单卡GPU上可行的训练命令yolo detect train datadata.yaml modelyolov8n.pt epochs150 imgsz640 batch16 patience20epochs建议在100到200之间选择。这个任务类别多但目标尺寸不小150轮左右通常能收敛。imgsz选择一个适中值。如果显卡显存有限降imgsz比降batch更划算因为大幅降batch会影响BN层统计。patience设为20表示验证集指标连续20轮不提升就早停能节省时间。对于这份数据集还有一个参数值得调整近邻匹配的IoU阈值。石榴果实的边界有时比较模糊默认阈值可能漏匹配低重叠度的真值框。可以尝试在训练命令后追加close_mosaic10让最后10轮关闭mosaic增强帮助模型稳定收敛。5. 避坑阶段记录标签顺序错位、坐标越界与小类样本漏检训练接近落地时问题才真正冒出来。我实际跑这套数据时先后踩过几个坑每一条都值得提前避开。5.1 坑一类别顺序与classes.txt不一致导致模型输出语义错乱现象模型训练完推理结果里早该显示“ripe”的成熟石榴输出标签变成了“bud”但精度数值看着还算正常。原因训练时data.yaml的names顺序和labels文件夹下txt第一列的编号含义不一致。常见做法是直接把数据集作者给的VOC类名顺序抄进yaml但YOLO格式的类别编号是转换脚本定的两套顺序未必相同。解决以labels/classes.txt为准先打开该文件查看每一行对应的类名再据此编写data.yaml的names。我每次都会在训练前写一行校验代码把每个txt的首列值范围打印出来确保没有出现超出0到4的编号。5.2 坑二xml里的坐标超出图片边界导致YOLO报错或loss异常现象训练刚开始时某些batch直接抛出坐标相关的断言错误或者损失值出现NaN。原因xml文件中个别框的x_max大于图片宽度或y_max大于图片高度。这类数据通常在标注时误框了边缘原始VOC训练工具不敏感但YOLO的训练管线对归一化结果有区间要求。解决转换脚本里加入钳制逻辑把x_max钳到img_widthy_max钳到img_height。以下是修改片段x_max min(float(bndbox.find(xmax).text), img_width) y_max min(float(bndbox.find(ymax).text), img_height)钳位不影响正常框只修正边缘异常。执行转换后重新统计一遍坐标最大值确保全部落在合法区间。5.3 坑三图片文件名带前缀导致划分后集合与标注文件不匹配现象训练时提示大量标注文件找不到或标签文件对空。原因数据集的部分文件名以xyxr_images_hehe开头但jpg、xml、txt三者的文件名主体一致。若在划分数据时用glob直接匹配*.jpg再手动拼接txt路径可能因为路径拼接方式不对导致找不到同名txt。解决划分脚本里统一使用os.path.splitext去掉扩展名再分别追加.jpg和.txt后缀保证三套文件始终基于同一stem。生成train.txt后遍历每一行验证对应txt存在缺一个就及时报警。5.4 坑四直接用全部数据做训练验证集指标虚高假象现象训练完成后验证集mAP高达0.95但换一批实拍石榴图预测效果明显变差。原因未做数据划分或划分时未打乱顺序。同类图片被聚在一起验证时模型“记住”了邻近样本的特征泛化能力被高估。解决严格按4.1的脚本先划分训练验证集保证验证集样本时间跨度尽量分散。如果原始采集是连续帧可以按数量间隔抽帧而不是连续切分。5.5 坑五early-fruit样本偏少导致漏检率居高不下现象验证集里bud、flower的recall都不错early-fruit的recall只有0.6左右大量幼果被漏成背景或误判为bud。原因early-fruit框数只有1307个占总量11.4%。模型整体学习到的该类别特征不足且幼果与花蕾形态相近特征混淆度高。解决在训练时对early-fruit类别调高loss权重或在预处理阶段复制该类别样本生成额外的增强图。数据层面可以将该类的mosaic比例调高让每个batch中这类样本出现更多。训练完成后优先检查该类的recall指标而不只看mAP均值。6. 用小样本类别权重校准预测recall分布检查与loss权重设置技巧模型跑通后真正考验效果的是细分类别能不能平衡。这里分享一个我常用的校准流程特别适合处理early-fruit这类小样本标签。第一步训练结束后分别输出每个类别在验证集上的recall。YOLOv8默认训练结束会生成混淆矩阵和各类别指标曲线图优先查看results.png里的recall-per-class柱状图。如果early-fruit明显低于其他类就执行第二步。第二步在data.yaml同级目录下增加一个类别权重配置。YOLOv8的loss权重参数为cls可以配合class_weights使用# 假设原始各框数为 bud:2433, early-fruit:1307, flower:2827, mid-growth:2986, ripe:1929 # 用小类权重 最大框数 / 当前类框数 weights [ 2986 / 2433, # bud 2986 / 1307, # early-fruit 2986 / 2827, # flower 2986 / 2986, # mid-growth 2986 / 1929 # ripe ] print(weights) # 输出约 [1.23, 2.28, 1.06, 1.0, 1.55]权重计算逻辑是让框数最少的类别获得最大惩罚系数。实际训练时把最高权重限制在2.5以内避免过拟合。第三步利用验证集混淆矩阵做阈值修正。YOLO推理时的conf阈值默认0.25若early-fruit的漏检集中在低置信度预测可以单独降低该类别阈值或增加一个后处理规则。以下是一个最小化验证脚本from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.val(splitval, conf0.15, iou0.5) print(results.box.mp, results.box.mr)这里的conf从默认0.25降到0.15观察整体recall变化。如果提升明显且precision没有崩说明早期阈值过高可以在推理阶段按此参数输出。从那以后我每次拿新数据集做检测都会先统计每个类别的框数和计算类别权重再进训练流程。这套顺序执行下来漏检问题基本在训练阶段就能暴露不用等部署上线才追悔。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?