简介这是一套面向农业自动化与智能农业应用的番茄目标检测数据集覆盖果实成熟度、不同生长阶段及多种光照条件专为采摘机器人视觉模块、温室生长监测与产量预估而设计可直接适配YOLOv3/v5/v8/v12等主流检测框架。包内共1792个文件含895张真实农业场景jpg图像、895个一一对应的YOLO格式txt边界框标注文件以及1个yaml配置文件和1份docx说明文档压缩包约15.18MB图像与标注严格对应并按训练集626张、验证集179张、测试集90张划分好数据子集。数据包含果实密集分布、枝干遮挡等实际挑战样本标注经农业专家复核可直接用于迁移学习、轻量化部署或农学表型分析研究。目前已有324人学习适合农业工程、计算机视觉领域的学生与研究者快速上手模型训练和算法验证。1. 番茄目标检测数据集.zip能直接训练还是先要把坑趟平农业数据集在下载列表里沉睡三周以上比模型跑通这件事的概率高得多。很多人解压了名为“番茄目标检测数据集.zip”的压缩包第一眼会觉得它平平无奇——几张温室照片、几份标注文件甚至可能连README都缺。但这正是它和通用目标检测数据集最大的不同番茄目标检测面对的是果实重叠、成熟度分级、叶片遮挡和温室光照波动背后直接连着采摘机器人或产量估测的落地需求。数据能不能训明白十有八九在解压那一刻就已经决定了。下面按一线干活的角度把这份数据集从解压、格式转换到YOLOv8训练、翻车排查和计数落地完整走一遍让新手有步骤可抄让老手能绕开几个真正的暗坑。2. 番茄目标检测数据集为什么和COCO/VOC完全是两回事在动手训练之前先花15分钟把数据集的结构和语义搞清楚。这一步省下来的时间远比在训练阶段来回调参省下的时间多。通用数据集的目标是“把这个东西认出来”番茄目标检测数据集的本质需求却是“把这颗果实的成熟度判断出来、把数量估算出来”。目标不同标注口径和数据分布就完全不同。2.1 番茄检测和通用目标检测的差别成熟度、重叠与光照通用目标检测里类别之间的外观差异一般很大比如人、车、猫、狗边界清晰背景相对可控。番茄则相反同一个类别里果实的大小、颜色、遮挡程度差异极大不同类别之间青番茄和叶片的颜色通道高度重叠成熟番茄的红色又可能和土壤、枯枝在特定光线下接近。所以类别定义通常不只有“番茄”一个词常见的是成熟番茄、未成熟番茄和花三类其中“花”看起来不重要但对产量预测很关键——花多才可能果多。果实重叠是第二个要命的地方。番茄是成串生长的一颗果实经常遮住另一颗的一半。标注框拉出来是个矩形但果实本身是圆形矩形框里会混入大量背景像素。如果一个框里同时存在半颗红果和半颗青果标注者怎么切分直接影响模型学到的特征。常见做法是“框住可见部分不做推断”也就是只标看得见的那块区域。但不同标注者不一定遵守解压后不妨自己抽查几十张看看框的松紧程度是否一致。尺度差异也不能忽略。同一份数据集里近景照片中一颗番茄可能占据300×300像素远景行株距下同样的果实可能只有15×15像素。单尺度训练时imgsz被拉高以后小果实信息能保住但训练速度和显存都会上浮。光照就更难办晴天温室里滴灌带反光、叶片投影、遮阳网下的色温偏移都会让同一个品种的果实呈现出完全不同的RGB分布。这些特性决定了后面所有参数调整的方向这一步没想清楚后面就只能靠调参玄学硬碰。2.2 解压后先摸清目录结构三种常见标注格式一次分清拿到zip包解压后不要急着打开标注文件逐张看。把目录层级打出来确认几个关键路径是否存在别把数据集当成一个黑匣子直接丢给训练脚本。一份典型的番茄目标检测数据集大概长这样tomato_dataset/ ├── images/ # 有的叫 JPEGImages │ ├── 000001.jpg │ └── 000002.jpg ├── labels/ # 有的叫 Annotations │ ├── 000001.xml │ └── 000002.xml ├── classes.txt └── README.md # 很多压缩包里根本没有三类标注格式在农业数据集里最常见。第一类是VOC XML每个文件对应一张图片里面通过object节点记录类别名和bndbox的xmin、ymin、xmax、ymax。第二类是COCO JSON整个数据集只有一个JSON文件图片、标注、类别分三个数组存。第三类是YOLO txt每个文件对应一张图片每行是“类别ID 归一化中心x 归一化中心y 归一化宽 归一化高”。三者对比格式典型扩展名标注内容和YOLOv8的兼容性VOC XML.xml类别名 绝对像素框需要转换COCO JSON.json类别ID 绝对像素框需要转换YOLO txt.txt类别ID 归一化中心宽高可直接训练打开classes.txt先看类别顺序这一步很关键。YOLO格式里的类别ID是按classes.txt的先后顺序定的转换脚本里map写错一个位置后面训练出来的模型就会把青番茄当成熟番茄。我曾见过一份数据集的class顺序是“flower, unripe, ripe”而多数人不看直接按“ripe, unripe, flower”转模型训了两天全部错位。2.3 用一条统计脚本拆开黑匣子类别分布与图片尺寸检查在转换之前先写个两三行的脚本统计类别分布能直接判断这份数据集值不值得投入时间。统计每个类别的目标数量、每张图片的平均目标数以及图片尺寸范围import os import xml.etree.ElementTree as ET from collections import Counter counts Counter() per_img [] img_sizes set() for xml_name in os.listdir(labels): tree ET.parse(os.path.join(labels, xml_name)) root tree.getroot() size root.find(size) img_sizes.add((size.find(width).text, size.find(height).text)) objects root.findall(object) per_img.append(len(objects)) for obj in objects: counts[obj.find(name).text] 1 print(counts) print(每图目标数区间:, min(per_img), -, max(per_img)) print(图片尺寸集合:, img_sizes)这段代码的逻辑很简单遍历labels目录下的每个XML读size节点记录图片尺寸遍历object节点累加每个类别的目标数同时记录每张图的目标数量。参数上不需要复杂的配置文件唯一要注意的是路径要和实际目录对应如果标注是JSON或txt把解析逻辑换成对应格式即可。看到输出后三个判断标准。类别数是否和任务一致成熟番茄和未成熟番茄的数量差有没有超过5倍——超过的话后面就要重点处理类不平衡图片尺寸是否统一——如果不统一训练时imgsz的设定要按最小尺寸和最大尺寸的中间值来或者干脆统一resize再训。3. 把番茄目标检测数据集转成YOLO格式目录规划与转换脚本大多数公开的番茄数据集采用VOC XML或COCO JSONYOLOv8原生吃YOLO txt。转换这一步按顺序处理先统一目录再转换坐标然后划分训练验证。跳过任何一步后面都会付出几倍的debug时间。3.1 从VOC XML到YOLO txt的Python转换脚本目录规划建议是images/里面放图片labels/里面放转换后的txt两个目录下各自建train和val子目录YOLO训练时按data.yaml指定路径。先把原始XML转成txt脚本如下import xml.etree.ElementTree as ET import os, sys # 类别ID映射顺序必须和之后data.yaml里的names完全一致 class_map {flower: 0, unripe: 1, ripe: 2} def voc_to_yolo(xml_path, out_dir): 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 [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] # 读取绝对像素坐标 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) # 转成中心点加宽高的归一化表示 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if not lines: return txt_out os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(txt_out, w) as f: f.write(\n.join(lines)) if __name__ __main__: os.makedirs(labels, exist_okTrue) for xml_name in os.listdir(labels_xml): voc_to_yolo(os.path.join(labels_xml, xml_name), labels)几个关键点说明。第一class_map的顺序就是最终模型的类别ID写错一个训练出来的模型在推理时就会张冠李戴这种错位在训练阶段很难发现因为loss照样收敛。第二归一化坐标必须用原图的真实宽高有的数据集在标注后做过resize图片文件尺寸和XML里的size节点不一致以XML里size为准往往安全但不放心的话顺手用PIL读一张原图核对。第三六位小数的精度对于归一化坐标足够了四舍五入误差换算回像素也不会超过1个像素。如果手里是COCO JSON思路完全相同只是解析库换成json从annotations数组里按image_id关联到图片宽高再套同样的归一化公式。3.2 划分训练集和验证集比例、随机种子与类别平衡转换完成后把图片和对应的txt按8:2分成训练集和验证集。比例上不建议用更少的验证集番茄数据集的目标类别少、目标数量大20%的验证集足够统计出可靠的mAP。划分脚本import os, random, shutil random.seed(42) imgs sorted(os.listdir(images)) random.shuffle(imgs) val_count int(len(imgs) * 0.2) train_files, val_files imgs[val_count:], imgs[:val_count] for split, files in [(train, train_files), (val, val_files)]: os.makedirs(fimages/{split}, exist_okTrue) os.makedirs(flabels/{split}, exist_okTrue) for f in files: stem os.path.splitext(f)[0] shutil.move(os.path.join(images, f), os.path.join(images, split, f)) # 标签文件名与图片同名扩展名换成txt if os.path.exists(os.path.join(labels, stem .txt)): shutil.move(os.path.join(labels, stem .txt), os.path.join(labels, split, stem .txt))seed42的意义不只是复现它决定验证集和训练集的内容构成。如果原始图片文件名连续且相邻文件来自同一段视频的不同帧不shuffle直接切验证集里就有一堆训练集的前后帧测出来的mAP虚高拿到现场就掉点。shuffle之后最好手动检查验证集里有没有同一株番茄连续拍摄的相似帧如果有说明这组数据的采集间隔太短验证集意义有限宁可把这组连续图片整段归入训练集。3.3 转换后必做的数据集校验无效框、越界框和空标签转换和划分之后不要直接进训练。写一个校验脚本把三类问题一次查完标签文件为空、归一化坐标越界、图片和标签对不上。代码很短import os ok 0 empty 0 bad 0 for split in [train, val]: label_dir flabels/{split} for txt_name in os.listdir(label_dir): with open(os.path.join(label_dir, txt_name)) as f: lines [line.strip() for line in f if line.strip()] if not lines: empty 1 print(空标签:, split, txt_name) continue for line in lines: parts line.split() if len(parts) ! 5: bad 1 print(格式错误:, split, txt_name, line) continue cls, x, y, w, h parts x, y, w, h float(x), float(y), float(w), float(h) # 归一化坐标必须在0~1之间宽高必须大于0 if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): bad 1 print(越界框:, split, txt_name, line) continue ok 1 print(f检查完成: 有效 {ok}, 空标签 {empty}, 异常 {bad})这个脚本逻辑上不复杂但它能挡住两种最常见的翻车一是转换脚本里误把xmin、ymin当成中心坐标导致框飘出图片二是空标签文件没有删除YOLOv8会直接警告甚至中断训练。校验通过后把images和labels下的文件一一对应检查一遍确保没有孤立的图片或标签。数据处理阶段多花10分钟训练阶段少花10小时这笔账怎么算都值。提示转换后使用OpenCV或PIL抽查三张图把yolo txt里的坐标画回原图比任何统计脚本都直观。画框结果里如果发现框普遍向右下偏移多半是坐标没有归一化到原图尺寸。4. 用YOLOv8在番茄目标检测数据集上训练最小命令与必调参数YOLOv8是目前把这份数据集跑起来最省事的框架安装一条命令数据集按上一章的目录组织好剩下就是配置data.yaml和训练参数。这里不搞花活直接给最小可复现路径。4.1 data.yaml的正确写法路径、类别、名称一个都不能错data.yaml是训练入口路径错了训练直接报错类别顺序错了训练能跑但语义错位。写一个典型配置# 数据集的根目录建议用绝对路径 path: /data/tomato_dataset train: images/train val: images/val # 类别ID从0开始必须和labels里的txt数字一一对应 names: 0: flower 1: unripe 2: ripepath指向数据集根目录train和val是相对path的目录。注意names的ID顺序必须和转换脚本里的class_map完全一致不能只对名字不对ID。如果数据集里只有一类“tomato”names里就只写一个0: tomato同时labels里的txt第一列只能是0。混用多类标注的时候这个位置最容易埋雷。CLI参数里如果也传了classes相关的选项以data.yaml为准。4.2 最小训练命令从解压到出模型的一行命令安装框架并启动训练pip install ultralytics yolo detect train \ data/data/tomato_dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0参数说明data指向yaml文件model用yolov8s.pt预训练权重s模型在农业小数据集上是性价比之选标注量低于2000张不推荐直接上yolov8x很容易过拟合epochs先给100配合早停机制跑着看imgsz给640是兼顾速度但如果番茄果实普遍偏小后面要往960或1280拉batch按显存来16G显存跑640分辨率、16批量一般没问题不够就降到8device0是单卡。验证训练是否正常看两个地方训练日志里每个epoch的loss是否在前20个epoch内明显下降验证集的mAP50在训练结束后有没有超过0.5。如果数据集只有成熟番茄一类就算是简单任务mAP50到0.6以上不稀奇类别包含三种再加深遮挡0.45以上就是可用水平。4.3 影响番茄检测效果的三个参数imgsz、Conf与NMS IoU训练阶段和推理阶段各有一个关键参数很多人混在一起调。先说训练阶段的imgsz默认640在番茄这种尺度和密度分布不均的数据集上建议先用640跑通基线然后提到960对比一次mAP不涨再试1280同时留意显存翻倍和训练时间翻倍的代价。推理阶段的两个参数一个是置信度阈值conf默认0.25另一个是NMS用的IoU阈值iou默认0.7。它们对重叠果实场景的影响正好相反参数默认值番茄密集场景建议原因conf0.250.15~0.2重叠和遮挡会压低预测分数阈值太高漏检率上升iou0.70.7~0.8重叠果实框之间IoU很高阈值太低会把相邻果实误杀这个组合值得解释一下。番茄果实密集时两个相邻果实的检测框IoU可能超过0.6如果NMS的iou设成0.5后一个果实会被当成前一个的重复框直接抑制掉表现为漏检。把iou往上抬到0.75NMS只抑制真正重合的框。同时遮挡导致每个果实的置信度可能只有0.2左右conf太低会带出大量背景误检太高则把好框全滤掉。落地时正确的做法是先不管conf把预测结果全部流出来画到图上按置信度排序观察再确定阈值。类别不平衡的处理YOLOv8没有直接的class_weight参数常见做法是在数据侧解决比如对样本数偏少的类别做过采样或者对这类样本所在的图像做复制并配合随机增强。标注量少的时候这种数据侧的干预比调任何训练参数都有效。5. 番茄目标检测数据集训练避坑四个最常翻车的现场下面4个坑是很多人在番茄数据集上反反复复踩的每条按现象、原因、解决的口径记录。5.1 现象训练loss不降验证集mAP挂在0.3附近不动训练跑了50个epochloss曲线下降一段之后开始横向波动验证集mAP50长期压在0.3往上怎么调参都没用。新手第一反应是换更大的模型、加训练轮数但这个问题多数发生在数据侧。最常见的元凶是类别ID错位classes.txt里写的是flower、unripe、ripe转换脚本里class_map却按ripe、unripe、flower编号模型在把“花”当“成熟果实”学方向性错误用训练轮数根本救不回来。第二种常见元凶是标签行格式错误比如某几行把xmin、ymin当成中心坐标填了进去导致框整体偏移模型学到的是错位的几何特征。排查要按顺序来。先取样打开三个txt文件把每行五个数字和原图画一次框肉眼确认框是否贴身。没问题再跑3.3节的校验脚本确认没有越界框和空标签。最后写一个统计脚本对比标注类别数和转换后的类别数数量对不上就是class_map的问题。这类错误基本能在十几分钟内定位返工代价远低于继续盲目训练。5.2 现象成熟番茄能检出青番茄几乎全丢训练后单独按类别看指标成熟番茄的mAP50可能已经到0.7青番茄的recall却低得没法看串里只要混了青果模型基本不输出。两类原因叠加第一是类不平衡青番茄和花的标注量可能只有成熟番茄的五分之一到十分之一正样本太少模型学到的特征边界太窄第二是青番茄和叶片在颜色通道上高度重合可分特征本来就弱需要更多样本才能学出来。处理上优先做数据侧重采样——把含青番茄的图片复制一份配随机的HSV扰动、翻转和轻微旋转后混回训练集让每个epoch见到的青番茄样本数翻倍或三倍。重采样之后看两个指标青番茄的per-class AP有没有明显上升整体mAP有没有因为重复样本导致过拟合。另一个常被忽略的问题是标注口径不一半红半绿的果实被标注者有些归成熟、有些归青果模型会在两类边界上无所适从。人工抽查100张青番茄标注图如果“青里带红”的框超过10%建议统一口径把转色期果实归为成熟果或者单独增加一个“转色”类。5.3 现象模型把叶子和阴影当成果实推理时误检很典型框把叶片、滴灌带阴影、甚至地面反光圈出来置信度还不低。这类问题的根源是训练集中缺乏负样本。如果整个数据集只有带果实的图片模型从没见过“图片里什么目标都没有”的情况它会从背景纹理里学出错误的特征。另一个容易被忽略的原因是标注框太松矩形框包住圆形果实本来就会带进背景标注者如果习惯性地多留10%边距框内的叶片像素积累多了模型就把叶片纹理当成目标的一部分。解决第一步是从原图里裁一批干净的背景块大小和果实目标相近每块不写标签丢进数据集当负样本。YOLOv8能正常吃空标签图片这部分样本量不需要太大占训练集10%~20%就有作用。解决第二步是收紧标注框把框贴着果实可见边缘切减少背景污染。如果数据集已经训完了才发现问题可以重新标注一部分松框样本用伪标签辅助迭代修正比例很小的话更快的办法是接受现状在推理阶段把conf阈值适当调高一点把低置信度误检尽量滤掉。5.4 现象验证集表现不错一到温室现场就掉点验证集mAP50有0.6拿到别的温室一测肉眼可见漏检、误检增多。原因通常有三类品种差异训练图片是大番茄现场的千禧小番茄串型和颜色分布完全不同视角差异数据集是采集车俯拍现场是手机平拍甚至仰拍光照差异训练集在晴天强光下采集现场恰好是阴天或早晚侧光果实和背景的对比关系都变了。数据增强能缓解一部分随机翻转、旋转、HSV扰动、Mosaic都值得开但对品种差异基本无效。成熟的做法是现场小样本微调在目标温室里拍一批图片按类别各标200~300个框用已有模型加载权重以0.0005~0.001的学习率跑20~30轮一个下午就能把现场掉点拉回大半。如果现场完全没标注可以让现有模型先出伪标注人工只修明显错误的框再用这批数据微调。微调后还要修一个关键场景正对强光方向时果实容易被阴影吞掉这类情况要么专门补样本要么在推理管线里做一次曝光增强再跑。6. 把番茄目标检测模型接到产量计数置信度阈值与小目标切片推理训练完成之后最常见的落地需求不是画框而是数数——这一串番茄有多少个成熟果实株产量大概多少。把检测结果按类别汇总成计数代码很直接from ultralytics import YOLO model YOLO(best.pt) results model.predict(test.jpg, conf0.2, iou0.75)[0] # 类ID要和data.yaml里的names对应0花1未成熟2成熟 ripe int((results.boxes.cls 2).sum()) unripe int((results.boxes.cls 1).sum()) print(f成熟 {ripe}, 未成熟 {unripe})要注意两个细节。一是conf和iou按上一章表格的思路设现场测试时建议把conf从0.5一路降到0.1画图观察误检和漏检的拐点取拐点处的阈值。二是产量估测场景里熟果和青果的比例往往比总数更有用别只看汇总数量。小目标问题是番茄计数的另一个拦路虎。如果现场图里一串番茄在1080p画面中只占十几个像素直接把整张图送进模型漏检率高得吓人。常规解法是切片推理把原图切成四等份或九等份每份单独推理最后按坐标偏移合并结果切分时重叠20%避免果实恰好被切在边上。这一招对密集小果的recall提升非常明显代价只是推理时间翻了几倍离线统计产量完全可以接受。更根本的做法是回到训练侧如果数据集里果实的平均框宽只有20像素左右把imgsz提升到960甚至1280同时给此类样本增加放大裁剪的增强。我现在的习惯是任何番茄检测模型落地前先把训练集里最小的框找出来用它的尺寸除以整体图像尺寸再乘上推理分辨率就能判断当前分辨率下能不能保住最小目标。按这个顺序处理我被不止一份数据集坑过现在每次拿到新的zip包第一件事永远是跑类别分布统计和画框抽查不再急着训练。希望这些步骤能帮你把番茄目标检测数据集这份压缩包真正用起来少走几轮弯路。本文还有配套的精品资源点击获取