简介:面向目标检测入门与课程设计场景,这份YOLO番茄高清数据集由作者自行标注,包含150张高清番茄图像及对应150个YOLO格式txt标注文件,并附train/val缓存文件与XML配置文件,RAR压缩包共303个文件、约387MB。图像分辨率高、标记边界准确,可与站内已有番茄数据集区分使用,适合计算机、电子信息、数学等专业学生在课程设计、期末大作业或毕业设计中直接训练YOLO系列模型。包内标注与图片一一对应,解压后按YOLO目录结构放入即可开始训练,减少人工标注成本;作者在说明中提到参数可方便更改、注释明细,表明这组数据在使用上注重易用性和可扩展性。目前已有325人学习下载,作为自标注高清数据集,在同类资源中具备稀缺性和实用价值,适合需要高质量小目标检测样本的读者使用。
1. 一份“已标注可直接使用”的番茄数据集,为什么值得从零跑一遍YOLO
如果你最近在搜“YOLO目标检测+番茄高清数据集已标注可以直接使用”,多半是已经受够了手工标注番茄的折磨。番茄这个目标看似好标,真标起来全是麻烦:成熟果和背景的红土、绿叶子调容易混,枝叶遮挡导致框要反复微调,一串圣女果按“单个果”标还是按“整串”标,不同标注习惯直接决定训练出来的模型能不能用。这份rar包把“高清图片+同名txt标注”打包在一起,价值在于省掉了从标注到YOLO格式转换的那两三天时间,适合做番茄分拣、成熟度识别、大棚巡检这类场景的人,也适合只想把YOLO训练流程跑通、不想先学一星期标注工具的0基础纯小白。下面按我处理别人交付的数据集时的习惯走一遍:拆包体检、配置训练、导出部署,中间的坑都是我实际翻过的车。
2. 解压之后先别急着训练:YOLO标注目录结构与格式校验
拿到rar先做两件事:解压后看一眼目录结构,然后核对label是否和图片一一对应。大多数打包者会按YOLO约定目录组织文件,但总有人把图片和标注嵌套进多层文件夹,或者把标注目录命名为“Annotations”。Ultralytics YOLO训练时是根据data.yaml里的train路径去推导标签路径的,命名不规范,轻则报“All labels empty”,重则训练几小时才发现模型根本没学到东西。
2.1 解压后的目录结构:先确认images与labels同级
常见做法是下面这样一套结构:
tomato_dataset/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── 002.jpg │ └── val/ │ ├── 101.jpg │ └── 102.jpg └── labels/ ├── train/ │ ├── 001.txt │ └── 002.txt └── val/ ├── 101.txt └── 102.txt解压后先执行find . -maxdepth 3 -type d | sort看实际目录,如果看到的是“数据/图片/训练”这类中文名或非约定名,不要急着在data.yaml里写train路径,因为Ultralytics找标注逻辑是“把images替换成labels”。最稳妥的做法是把目录重命名成标准结构:图片统一叫images,标注统一叫labels,训练集和验证集各自再分train、val子目录。重命名用mv即可:
# 解压后如果目录不标准,按下面的方式规整 mv "标注" labels mv "图片" images mv "训练集" train mv "验证集" val注意:Linux环境大小写敏感,Labels和labels是两个完全不同的目录。Windows上解压再传到Linux服务器训练时,这类问题最容易出现。
如果rar里已经带了一份data.yaml,先打开看train和val两行写的是相对路径还是绝对路径。拿到别的机器上训练时,绝对路径经常失效,我的习惯是统一放到项目根目录下,用相对路径或干脆重新写一份。
2.2 labels里的txt到底存了什么:类别ID与归一化坐标
YOLO标注文件和COCO、VOC都不一样,不存像素坐标,每行存的是“类别ID + 框中心点x + 框中心点y + 框宽w + 框高h”,且都除以图片宽高做了归一化,数值在0到1之间。一份番茄标注txt长这样:
0 0.4531 0.5123 0.2105 0.3012 1 0.7122 0.6247 0.1803 0.2266第一行的0、第二行的1分别是类别ID,这个数字要和data.yaml里names列表的下标对应。假如data.yaml里names写的是0: mature_tomato, 1: green_tomato,那txt里不能出现2或更大的数字,否则训练时会报“class number out of range”。这个对应关系是打包者最容易出错的地方,训练前最好先统计全部txt里最大的类别ID,免得训练到一半才崩。
另外,标注工具如果导出时忘记除以图片宽高,txt会出现大于1的坐标。判断标准很简单:任何一列的值在0到1之外,这份标注就是坏数据。第4章会给排查脚本,这里先记住结论。
2.3 用脚本检查图片与标注文件是否一一对应
“有图没标注”和“有标注没图”都会让结果失控。前者会让这张图被当成无目标样本,后者是纯垃圾数据。写个小脚本扫一遍:
import os image_dir = "tomato_dataset/images/train" label_dir = "tomato_dataset/labels/train" image_exts = {".jpg", ".jpeg", ".png", ".bmp"} img_names = set() for f in os.listdir(image_dir): name, ext = os.path.splitext(f) if ext.lower() in image_exts: img_names.add(name) label_names = set() for f in os.listdir(label_dir): name, ext = os.path.splitext(f) if ext == ".txt": label_names.add(name) missing = sorted(img_names - label_names) # 有图没标注 orphan = sorted(label_names - img_names) # 有标注没图 print(f"图片总数: {len(img_names)}") print(f"标注总数: {len(label_names)}") print(f"缺少标注的图片数量: {len(missing)}") print(f"没有对应图片的标注数量: {len(orphan)}")逻辑说明:YOLO训练时按图片名寻址同名txt,所以比对的是去掉扩展名的文件名。大小写和命名风格必须完全一致,IMG_001.jpg和img_001.txt在Linux上不算同名。missing数量超过个位数,就要考虑是打包漏文件还是文件名映射规则不同,对这种情况直接删图比手工补标更划算。
空标注文件也是常见问题。txt里如果没有任何有效行,等价于这张图里没有目标。少量空标注正常,但如果占比超过5%,说明打包者可能漏标了,不是“图上真的没有番茄”。
3. 用这份数据集跑通第一个YOLO训练:data.yaml配置、命令与参数选择
目录体检通过之后进入训练环节。这一章以Ultralytics YOLO为基准,它把数据加载、训练、验证、导出串成一条命令,是目前对新手最友好的入门方案。你要做的事只有两件:写一份data.yaml,然后敲一条训练命令。
3.1 data.yaml的必备三要素:train、val、names
在项目目录下新建tomato.yaml:
# 数据集配置文件 train: tomato_dataset/images/train val: tomato_dataset/images/val nc: 2 names: 0: mature_tomato 1: green_tomato参数说明:train和val指向图片目录,Ultralytics按文件名推导标注文件,机制是把路径里的images替换成labels。所以tomato_dataset/images/train/001.jpg对应标注是tomato_dataset/labels/train/001.txt,目录名必须严格是images和labels。nc是类别总数,names是类别名列表,顺序就是类别ID顺序,txt第一列写0代表mature_tomato,写1代表green_tomato。names写反不会报错,但推理时类别名全乱,属于“能跑但全错”的坑,防坑手段是拿一张已知结果的图片去验证。
3.2 训练命令与关键参数:imgsz、batch、epochs、lr0
写好后执行:
yolo detect train \ model=yolov8n.pt \ data=tomato.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=tomato_run \ name=exp1 \ device=0参数说明:model用预训练权重起步,比从零训练收敛快得多,番茄检测属于简单目标,不要一上来就用yolov8x,网络容量过大在几百张的小数据上容易过拟合。imgsz是训练时resize到的分辨率,这份数据集标注是“高清”,原图可能是1080P甚至更高,若直接把大图缩到640,画面里的小番茄只占十几个像素,模型会漏检。显存允许时先试960,效果不行再试1280;只有8G显存就用640,后面单独讲高清图增强。batch设为16起步,OOM时降到8,不要为了凑batch把imgsz砍到416,对番茄这种小目标得不偿失。lr0保持0.01,数据集只有几百张时调到0.005更稳。patience=20表示验证指标连续20个epoch不提升就早停,既防过拟合又省时间。
训练结束后在tomato_run/exp1/weights/下会生成best.pt和last.pt,best.pt是验证集上表现最好的权重,后续验证和导出都用它。终端最后几行会给出mAP50、mAP50-95、precision、recall,这是判断“这份数据集能不能直接出活”的第一手证据。
3.3 训练后看什么:results.csv、混淆矩阵与标签分布图
训练中Ultralytics会在tomato_run/exp1/生成results.png、confusion_matrix.png、labels.jpg。results.png里最值得看两条曲线:val/box_loss持续下降说明框回归在收敛;metrics/mAP50稳步爬升说明番茄都被框中了。confusion_matrix.png告诉你两类番茄是否互相认错,成熟果那一行有大量样本被分到青果,说明两类在颜色上过于接近或某类样本太少。labels.jpg把标注框画在缩略图上,一眼就能看出打包者有没有把叶子也框进去,这类“框是框了内容不对”只有可视化才看得见。如果某类mAP明显偏低,还可以做特征热力图,看模型关注的到底是番茄本身还是背景叶片。
4. 标注质量决定模型上限:越界框、空标注与类别失衡的排查脚本
数据集标注质量直接决定模型上限。YOLO训练对脏标注容忍度有限,下面几个检查项目在我处理别人打包的数据集时必跑。
4.1 用一个脚本筛查三种坏标注:越界、异常行、空文件
import os def inspect_labels(label_dir, tol=0.01): bad_lines = [] empty_files = [] max_cls = -1 for root, _, files in os.walk(label_dir): for f in sorted(files): if not f.endswith(".txt"): continue path = os.path.join(root, f) with open(path, encoding="utf-8", errors="ignore") as fp: lines = [ln.strip() for ln in fp if ln.strip()] if not lines: empty_files.append(f) continue for ln in lines: parts = ln.split() if len(parts) != 5: bad_lines.append(f"{f}: 格式错误 -> {ln}") continue try: cls, x, y, w, h = map(float, parts) except ValueError: bad_lines.append(f"{f}: 无法解析 -> {ln}") continue max_cls = max(max_cls, int(cls)) if x - w / 2 < -tol or x + w / 2 > 1 + tol: bad_lines.append(f"{f}: x方向越界 -> {ln}") if y - h / 2 < -tol or y + h / 2 > 1 + tol: bad_lines.append(f"{f}: y方向越界 -> {ln}") if w <= 0 or h <= 0: bad_lines.append(f"{f}: 宽高异常 -> {ln}") print(f"空标注文件: {len(empty_files)} 个") print(f"异常标注行: {len(bad_lines)} 条") print(f"出现过的最大类别ID: {max_cls}") inspect_labels("tomato_dataset/labels/train")逻辑说明:空标注文件本身不算错误,但要统计占比;格式错误和值域异常必须处理,这两类会直接污染训练。越界容差tol给0.01,是因为鼠标拖出图片边界在标注时很常见,YOLO能把微越界框拉回来,但超过1%的越界就该修。最大类别ID若和data.yaml里的nc-1不相等,训练前就要处理。
4.2 把标注画回原图:用OpenCV做抽样可视化验证
数值检查只能证明“格式对”,证明“标注对”还得靠人眼。写个脚本把标注画回原图,抽样50到100张翻一遍:
import cv2, os, random label_dir = "tomato_dataset/labels/train" image_dir = "tomato_dataset/images/train" class_names = {0: "mature_tomato", 1: "green_tomato"} colors = {0: (0, 0, 255), 1: (0, 255, 0)} samples = random.sample(os.listdir(label_dir), min(50, len(os.listdir(label_dir)))) os.makedirs("visual_check", exist_ok=True) for label_file in samples: name, _ = os.path.splitext(label_file) image_path = os.path.join(image_dir, name + ".jpg") img = cv2.imread(image_path) if img is None: image_path = image_path.replace(".jpg", ".png") img = cv2.imread(image_path) if img is None: print("图片不存在:", name) continue h, w = img.shape[:2] with open(os.path.join(label_dir, label_file)) as fp: for ln in fp: parts = ln.strip().split() if len(parts) != 5: continue cls = int(parts[0]) cx, cy, bw, bh = map(float, parts[1:]) x0 = int((cx - bw / 2) * w) y0 = int((cy - bh / 2) * h) x1 = int((cx + bw / 2) * w) y1 = int((cy + bh / 2) * h) cv2.rectangle(img, (x0, y0), (x1, y1), colors.get(cls, (255, 0, 0)), 2) cv2.putText(img, class_names.get(cls, str(cls)), (x0, y0 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors.get(cls, (255, 0, 0)), 2) cv2.imwrite(f"visual_check/{name}.jpg", img) print("可视化结果输出到 visual_check/ 目录")逻辑说明:坐标从归一化还原成像素时,中心点减半宽得到左上角,加半宽得到右下角,这四行换算就是YOLO标注可视化的全部核心。用random.sample抽样,避免永远只看前几张。高清图全尺寸画框没问题,数据集有几千张时全量可视化不现实,50张足够定位“漏标集中在背光区域”这样的规律。
4.3 统计类别实例数量:数据分布决定增强策略
番茄检测最常见的数据分布问题,是成熟果和青果数量差太多。统计每类有多少框:
import os from collections import Counter def count_instances(label_dir): counter = Counter() per_image = Counter() for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f)) as fp: lines = [ln.strip() for ln in fp if ln.strip()] if lines: per_image[f] = len(lines) for ln in lines: counter[int(ln.split()[0])] += 1 print("各类别实例总数:", dict(counter)) print("单图框数分布: 最小%d 最大%d 平均%.1f" % (min(per_image.values()), max(per_image.values()), sum(per_image.values()) / len(per_image))) count_instances("tomato_dataset/labels/train")逻辑说明:两类框数比超过10比1,模型会对多数类过拟合,推理时把青果漏掉。此时按倍数复制少数类图片做简单过采样,或用mosaic、copy-paste增强,都比调损失函数直接。单图平均框数也很关键:平均框数只有1到2,说明每张图只有一个番茄,模型学不到密集遮挡场景,实际棚拍一串多果时会翻车,需要补充密集样本。
5. 番茄数据集训练翻车记录:五个最容易踩的坑
“已标注可直接使用”这句话最容易麻痹人。下面五条是我处理别人交付的数据集时踩过的坑,全部按“现象→原因→解决”写,你踩到哪条直接对号入座。
5.1 现象:训练报错“class number out of range”直接中断
原因:txt里的类别ID大于data.yaml里的nc减1。最常见是打包者用的标注工具从1开始编号(1、2),而YOLO从0开始(0、1);也可能是txt里混入了其他项目的类别标签。
解决:先跑4.1脚本看最大ID。如果所有ID整体偏大1,用下面脚本全局减1:
import os def fix_class_ids(label_dir, offset=-1): for f in os.listdir(label_dir): if not f.endswith(".txt"): continue path = os.path.join(label_dir, f) with open(path) as fp: lines = fp.readlines() fixed = [] for ln in lines: parts = ln.split() if not parts: continue cls = int(parts[0]) + offset if cls < 0: print(f"{f}: 类别ID减成了负数,请人工看这一行") continue fixed.append(f"{cls} " + " ".join(parts[1:]) + "\n") with open(path, "w") as fp: fp.writelines(fixed) fix_class_ids("tomato_dataset/labels/train", offset=-1)这段代码把所有ID减1后原样写回,执行前先用4.1脚本确认没有负数和越界行。如果只是个别行混入错误ID,不要全局改,把那几行删掉更安全。
5.2 现象:日志刷“All labels empty”,训练全程学不到东西
原因:标注目录命名或路径推导失败。Ultralytics处理data.yaml时,是把train路径里的images替换成labels来定位标注目录,如果你的目录叫Annotations、Labels(大写L),或labels不在与images同级的目录里,训练时找不到txt,全部按空样本处理。模型等于在给无目标的空图训练,几个小时后mAP还是0。
解决:重命名目录统一为labels,并保证images与labels同级。改名后用一行命令抽查:
ls tomato_dataset/labels/train | head -5再打开一张图和它的txt对比文件名是否完全一致。这类路径大小写问题在Windows上解压、再复制到Linux服务器训练时最常出现,属于典型的“本地能跑,一上服务器就翻车”。
5.3 现象:loss曲线下降但mAP50一直是0
原因:txt里的坐标是用像素值直接存的,没有除以图片宽高;或某个框的w/h为0。YOLO对非法标注有一套容错机制,训练不崩,损失看似在走,但预测框全部偏离画面。
解决:用4.1脚本扫一遍值域,发现x或y的数值大于10时,说明是像素坐标不是归一化坐标,需要按图片实际宽高重新处理。不要用“几千张里只有几十张有问题”的心态放过,模型每次迭代都在重复读这批脏数据,会被反复带偏。修完之后再跑一遍校验脚本,确认值域全部落在0到1之间再开训。
5.4 现象:显存OOM,训练在第一个epoch就崩溃
原因:高清图加上batch过大,显存瞬间打满。8G显存用yolov8n、imgsz=640、batch=16大概率OOM。
解决:先降batch到8或4,再考虑降imgsz。如果原图是4000×3000级别的高清图,更推荐切patch再训练:把大图切成若干960×960的小图,同时把标注框坐标减掉偏移量后重新归一化。切完重新过一遍4.1校验脚本。这样既保住小番茄的像素细节,又让模型学到局部纹理,比单纯缩图效果好得多。坐标换算很简单:
# patch左上角为(patch_x0, patch_y0),patch尺寸为patch_w, patch_h new_cx = (old_cx - patch_x0) / patch_w new_cy = (old_cy - patch_y0) / patch_h new_w = old_w / patch_w new_h = old_h / patch_h换算时注意旧坐标要先用原图宽高还原成像素值,再代入上面的公式。
5.5 现象:训练完precision很高,recall很低
原因:阈值或类别不平衡。训练时用默认conf=0.25验证,模型对青果的置信度普遍低于阈值,recall就会非常难看。
解决:在验证集上画PR曲线(results.png自带),找precision和recall的交点,推理时把conf改成那个值。如果交点处的recall还是低,回到4.3的类别统计,对少数类做加权或过采样。这类问题最容易误判成“数据集标注坏了”,实际是数据分布问题,不要急着重新标数据。
6. 把训练好的模型用在番茄检测现场:阈值、导出与多路部署
训练收敛后,现场推理不能直接无脑照搬默认参数。用best.pt对一张实际场景图预测:
from ultralytics import YOLO model = YOLO("tomato_run/exp1/weights/best.pt") results = model.predict( source="greenhouse_photo.jpg", conf=0.30, iou=0.5, imgsz=960, save=True, save_txt=True, )conf影响最大:设太高会漏掉被叶子挡住一半的番茄,设太低会把背景红果误检成番茄。我的做法是先用0.1跑一遍看有多少低置信度框,再从0.2到0.5各跑一遍比对漏检和误检,选漏检可接受的那档。正式封装时把imgsz固定成训练时的分辨率,推理时临时换分辨率会引入精度波动。
部署上有一个改一行就有明显收益的小技巧:导出为ONNX后推理速度比直接跑ultralytics预测快不少:
yolo export model=tomato_run/exp1/weights/best.pt format=onnx imgsz=960 opset=12导出后用onnxruntime加载,能把GPU推理耗时压到能接多路视频流的水平;还嫌慢再研究TensorRT导出engine,相同精度下吞吐还能再提一档。视频流场景不要用Python死循环处理每一帧,用队列做生产者消费者模型,否则首帧延迟会吃掉吞吐优势。
我的习惯是拿到别人打包的标注数据集之后,先花半小时把第4章三个脚本跑完,再花十分钟画几十张图肉眼看一遍,确认数据干净才启动训练。跳过这个流程的,后面全在别的地方把时间还回去了。希望这些经验能帮到你,特别是第一次摸到“已标注可直接使用”的rar包时,别被“可直接”三个字麻痹。
本文还有配套的精品资源,点击获取