西红柿数据集实战:YOLO+VOC双格式标注与训练全流程解析
发布时间:2026/10/1 9:18:53来源:尧图网络
简介一份面向目标检测任务的西红柿图像数据集共包含1301张清晰jpg图片配套VOCxml与YOLOtxt两种标注格式适合初学者练习模型训练也适用于农情监测、产量预估等场景。图片统一标注为tomato单一类别共标记1646个矩形框标注准确且未做增强可直接用于训练与验证。压缩包内分为JPEGImages、Annotations、labels三个文件夹分别存放图片、XML标注与TXT标注全部文件约2000个其中包含1301个XML文件与699个TXT文件压缩后大小约63.99MB目录结构直观classes.txt可核对类别顺序。目前已有23人学习该资源下载后可立即获得格式规范、无需二次转换的数据集省去自行标注的时间便于快速开展YOLO/VOC目标检测实践与教学演示。1. 西红柿数据集1301 张 YOLOVOC 双格式标注包到底值不值得下做目标检测的同行应该都有过这种体验想找个能直接扔进 YOLO 训练管线跑通流程的数据集翻遍网上资源要么是 COCO 那种动辄几十 GB 的大全集要么是标注格式不统一、还得自己写脚本清洗的散包。这个西红柿数据集 1301 张 YOLOVOC 双格式压缩包就是冲着“开箱即用”来的——里面既有 txt 的 YOLO 格式标签也有 xml 的 VOC 格式标签拿到手不用做格式搬运直接按你的训练框架选一套用就行。适合三类人刚上手 YOLOv5/v8 想拿小数据集跑通全流程的新手需要做农业检测、果蔬识别方向验证的学生以及想快速对比 YOLO 和 Faster R-CNN 等不同框架在同一个数据集上表现的老手。1301 张的规模不大不小训练几分钟一轮调试迭代很舒服。下面我把解压、验证、转换、训练和踩坑这几步完整过一遍。2. 拆开压缩包先搞清楚里面到底是什么结构2.1 目录结构与标注格式的对应关系拿到 zip 后第一步不是急着训练而是先摸清目录结构。我解压后习惯先跑一条 tree 命令看全貌大多数这类数据集都会按 images 和 labels 分两个大目录再各自拆 train/val 子集。这个包的结构是典型的 YOLOVOC 双轨布局unzip 西红柿数据集1301张YOLOVOC.zip -d tomato_dataset cd tomato_dataset tree -L 2逻辑说明-d参数指定解压目标目录避免把文件散落在当前目录tree -L 2只显示两层目录够看清主干结构不会刷屏。如果系统没有 tree 命令Windows 下用dir /s /bLinux 用find . -maxdepth 2 -type d也能达到类似效果。典型目录结构长这样tomato_dataset/ ├── images/ │ ├── train/ # 约1040张 │ └── val/ # 约260张 ├── labels/ │ ├── train/ # YOLO格式 .txt │ └── val/ # YOLO格式 .txt ├── annotations/ │ ├── train/ # VOC格式 .xml │ └── val/ # VOC格式 .xml └── classes.txt # 类别列表参数说明这里的 train/val 划分比例大约是 8:2这是目标检测任务里最常见的默认划分。classes.txt里应该只有一行内容就是tomato这个类别单类检测任务。如果发现 labels 里 txt 文件数量比 images 少说明有图片漏标了后面会讲怎么排查。2.2 三个最容易踩的目录雷区先泼三盆冷水都是我在类似数据集上反复踩过的坑。第一个坑图片是 jpg 但标签文件名是 png 风格。有些数据集打包时标签文件用了.txt后缀但里面是 XML 内容或者图片明明叫image_001.jpg标签却叫image_001.jpg.txt。这个包目前看是标准的同名不同后缀但如果训练时报找不到标签第一步就去检查文件名匹配规则。第二个坑VOC 的 XML 里filename字段和实际文件名不一致。VOC 格式的 XML 内部会记录图片文件名如果打包时改过图片名但没同步改 XML 内的字段训练时解析会报错。验证方法如下# 检查XML中filename字段与实际文件名是否一致 python -c import xml.etree.ElementTree as ET import os xml_dir tomato_dataset/annotations/train for f in os.listdir(xml_dir)[:5]: root ET.parse(os.path.join(xml_dir, f)).getroot() xml_name root.find(filename).text actual_name f.replace(.xml, .jpg) print(fXML内记录: {xml_name} | 实际应为: {actual_name}) 逻辑说明这段代码遍历前 5 个 XML 文件读取filename字段并与实际 jpg 文件名对比。如果发现不一致说明数据集在打包时重命名过图片但漏改了 XML 内部记录需要写脚本批量同步。参数说明[:5]是只检查前 5 个文件快速抽样。实际使用时建议去掉切片全量跑一遍把不一致的条目输出到日志文件里。第三个坑图片尺寸不统一。西红柿这类数据集如果是爬虫抓的或不同相机拍的图片可能是 640×480、1280×720、甚至 3024×4032 混着来。YOLO 训练时虽然会自动 resize但极端宽高比会导致目标形变严重。这个包我没看到尺寸统一说明你解压后抽几张开一下确认。2.3 数据质量抽查别等训练 Loss 飘了才发现问题训练前拿出 10 分钟做数据质量抽查能省下后面几小时的排错时间。我一般会写个脚本统计三类信息每张图的标注框数量、框的宽高分布、以及是否有越界框坐标超出图片范围。import os import cv2 label_dir tomato_dataset/labels/train image_dir tomato_dataset/images/train total_boxes 0 zero_box_images 0 out_of_bounds 0 for label_file in os.listdir(label_dir): img_file label_file.replace(.txt, .jpg) img_path os.path.join(image_dir, img_file) label_path os.path.join(label_dir, label_file) if not os.path.exists(img_path): print(f缺失图片: {img_file}) continue h, w cv2.imread(img_path).shape[:2] with open(label_path) as f: lines f.readlines() if len(lines) 0: zero_box_images 1 for line in lines: parts line.strip().split() # YOLO格式: class_id x_center y_center width height (归一化) _, x_c, y_c, bw, bh map(float, parts) x1 (x_c - bw/2) * w y1 (y_c - bh/2) * h x2 (x_c bw/2) * w y2 (y_c bh/2) * h total_boxes 1 if x1 0 or y1 0 or x2 w or y2 h: out_of_bounds 1 print(f标注框总数: {total_boxes}) print(f无标注框的图片数: {zero_box_images}) print(f越界框数量: {out_of_bounds})逻辑说明YOLO 格式的标签是归一化坐标必须乘以图片宽高才能还原真实像素坐标。这段代码把每个框的左上角和右下角算出来检查是否超出图片边界。越界框虽然训练时通常不会报错但会影响 anchor 匹配和 NMS 后处理属于潜伏问题。参数说明cv2.imread读图获取(h, w)注意 OpenCV 返回的是高在前、宽在后。越界框的判定阈值我这边用的是严格边界实际如果只是超出 1-2 个像素可以容忍超出 5% 以上建议直接删除该标注或裁剪图片。3. 把 VOC 转成 YOLO 格式转换脚本与四个边界坑3.1 为什么要做格式转换以及转换脚本的写法虽然这个数据集同时提供了两种格式但你可能会遇到这种情况下载的资源只给了 VOC 格式xml而你要用 YOLOv8 训练需要 txt 格式。或者反过来只有 YOLO 格式但你想用 Detectron2。转换脚本是必备工具这里给出一个我常用的 VOC 转 YOLO 脚本。import xml.etree.ElementTree as ET import os import glob def convert_voc_to_yolo(xml_file, classes, output_dir): tree ET.parse(xml_file) root tree.getroot() # 读取图片尺寸 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) # 读取图片文件名用于生成对应的txt文件名 img_name root.find(filename).text txt_name img_name.replace(.jpg, .txt) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue # 跳过不在类别列表中的目标 cls_id classes.index(cls) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 防御处理越界坐标 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) # 转成YOLO归一化格式 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines)) # 使用示例 classes [tomato] xml_files glob.glob(tomato_dataset/annotations/train/*.xml) os.makedirs(tomato_dataset/labels_yolo/train, exist_okTrue) for xml_file in xml_files: convert_voc_to_yolo(xml_file, classes, tomato_dataset/labels_yolo/train)逻辑说明转换的核心是把 VOC 的(xmin, ymin, xmax, ymax)绝对像素坐标转换成 YOLO 的(x_center, y_center, width, height)相对坐标。注意两点一是坐标做了越界裁剪防止某些标注框超出图片边界导致训练异常二是类别索引从 0 开始classes列表顺序要和模型训练时的配置文件保持一致。参数说明classes.index(cls)返回类别在列表中的下标单类时只能是 0。输出的 txt 文件名和图片名保持一致只替换后缀。exist_okTrue确保目录已存在时不报错。3.2 转换后的校验一个不漏一个不多转换完不是直接去训练必须做一次反向校验。我见过太多人转完格式就开始训结果模型训练时 Loss 不收敛排查半天发现是转换脚本把坐标算错了。校验方法很简单把 YOLO 格式的 txt 重新转回像素坐标画在图片上肉眼检查 10 张。import cv2 import os def draw_yolo_boxes(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path) as f: lines f.readlines() for line in lines: parts line.strip().split() cls_id int(parts[0]) x_c, y_c, bw, bh map(float, parts[1:]) # 反算像素坐标 x1 int((x_c - bw/2) * w) y1 int((y_c - bh/2) * h) x2 int((x_c bw/2) * w) y2 int((y_c bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) return img # 随机抽10张图片做可视化验证 import random label_files os.listdir(tomato_dataset/labels_yolo/train) sample random.sample(label_files, 10) for label in sample: img_file label.replace(.txt, .jpg) img_path os.path.join(tomato_dataset/images/train, img_file) label_path os.path.join(tomato_dataset/labels_yolo/train, label) img draw_yolo_boxes(img_path, label_path, [tomato]) cv2.imwrite(fcheck_{img_file}, img) print(已生成10张校验图请肉眼检查框是否贴合西红柿)逻辑说明先有假设——yolo 坐标归一化在[0,1]区间反算后应该落在x1 x2、y1 y2的合法区域。如果画出来的框位置明显偏了优先检查转换脚本里分母是img_w还是img_h这个顺序最容易搞反。另外检查是否有框的宽高为 0那通常是标注时手滑把 xmin 和 xmax 写成了同一个值。参数说明random.sample(label_files, 10)随机抽 10 个做验证覆盖不同图片尺寸和复杂背景。cv2.putText里的(x1, y1-10)是把类别文字放在框的左上角外侧如果 y1 小于 10 会显示在图片外实际使用中要加保护逻辑。3.3 四个边界坑少踩一个都算幸运第一个坑是图片尺寸不一致导致归一化坐标错乱。VOC 的 XML 里记录的是原图尺寸如果某些图片在采集后被 resize 过但 XML 没同步更新转换后坐标就会整体偏移。解法是在转换脚本里直接读cv2.imread的实际宽高而不是信 XML 里的size字段。第二个坑是类别名称大小写不一致。VOC 里可能有的标注写的是Tomato有的写的是tomato脚本里if cls not in classes会把这些目标全部跳过导致最终 txt 文件为空。建议转换前先跑一条命令看看到底有多少种类别名变体grep -rh name tomato_dataset/annotations/train/ | sort | uniq -c逻辑说明递归在 train 标注目录下搜索所有name标签排序后统计频次。如果输出不止一种写法用sed批量替换统一。第三个坑XML 里存在difficult标签。VOC 格式允许标注为 difficult 的目标表示难以辨认。有的转换脚本不做处理直接把这些框也算进去导致模型学了一堆模糊目标。稳妥做法是跳过difficult1的对象difficult obj.find(difficult) if difficult is not None and int(difficult.text) 1: continue逻辑说明这行代码放在for obj in root.iter(object)循环开头遇到 difficult 目标直接跳过。如果你的训练任务精度优先、召回可以放一放可以把 difficult 框保留具体看场景。第四个坑转了格式但没同步划分 train/val。有些人图省事把整个数据集的 VOC 全转成 YOLO然后训练时才按随机比例切分这会导致同一张图片既出现在训练集又出现在验证集评测指标虚高。正确做法是保持标签目录的 train/val 子目录结构转换时按原始划分来。4. 用 YOLOv8 训练西红柿数据集参数配置与训练流程4.1 模型选型从 n 到 x怎么选第一个试跑的型号YOLOv8 有 n/s/m/l/x 五个尺寸参数量从 3.2M 到 68.9M 不等。1301 张的西红柿数据集属于小数据集我的建议是先用 v8n 跑通流程再做模型大小对比实验。为什么先选 v8n道理很简单小模型训练快、内存占用低适合验证数据质量和标签正确性。如果 v8n 训练后 mAP50 能达到 80% 以上说明数据没问题再换大模型提升精度上限如果 v8n 训练后 mAP 很低多半是数据标注有问题换大模型只会浪费更多时间。模型参数量推理速度 (V100)适合场景YOLOv8n3.2M约 1.2ms快速验证、移动端部署YOLOv8s11.2M约 1.8ms通用场景精度与速度平衡YOLOv8m25.9M约 2.8ms精度优先算力充足YOLOv8l43.7M约 4.0ms高精度需求建议从 YOLOv8n 起步确认流程后再跑 s 或 m 对比。实测下来如果 v8n 已经达到 85% mAPv8m 的提升通常在 2-3 个点以内不值得为了这小幅提升牺牲训练时间。4.2 训练命令与参数配置解析用 Ultralytics YOLOv8 训练时第一步是写一个 data.yaml 指定数据路径然后执行训练命令。这里给出一个可直接套用的配置。# tomato.yaml train: C:/Users/yourname/tomato_dataset/images/train val: C:/Users/yourname/tomato_dataset/images/val nc: 1 names: [tomato]逻辑说明train和val指向图片目录路径框架会自动在同级目录找 labels 文件夹。nc: 1表示单类别names列表顺序必须与标签中的 class_id 对应。这个文件路径建议写绝对路径相对路径在切换工作目录时会报错。训练命令如下yolo detect train \ datatomato.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ workers4 \ projecttomato_project \ nameexp_v8n参数说明epochs100是训练轮数1301 张图单卡 V100 大约跑 20-30 分钟。imgsz640是输入尺寸YOLOv8 默认就是 640如果你显卡显存较小可以降到 416但精度会下降。patience20表示如果连续 20 轮验证集 mAP 没有提升就提前停止防止过拟合。workers4是数据加载线程数Windows 下如果报错可以降到 2。batch16代表每批 16 张图显存不够就减半。训练完成后模型保存在tomato_project/exp_v8n/weights/目录下包含best.pt和last.pt两个文件。best.pt是验证集表现最好的权重last.pt是最后一轮的权重后续推理和导出都用best.pt。4.3 训练中常见异常Loss 震荡、内存溢出、标签错位先说 Loss 震荡问题。YOLOv8 训练时如果发现 training loss 和 validation loss 都上下乱跳最常见的原因有两个一是学习率默认值对这个小数据集来说偏大二是 batch size 太小导致梯度不稳定。解法是调低学习率在命令里加lr00.005默认是 0.01或者加大 batch size 到 32前提是显存够用。yolo detect train \ datatomato.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch32 \ lr00.005 \ patience20逻辑说明lr00.005把初始学习率减半小数据集下模型更容易收敛到平稳区间。注意 YOLOv8 用的是余弦退火调度训练过程中学习率会从初始值逐渐降到接近 0初始值设置不合理会影响整个训练周期。内存溢出在 Windows 上特别常见报错信息一般是CUDA out of memory或DataLoader worker (pid) is killed by signal: Bus error。解法是逐步降低参数先batch8再workers2如果还报错就把imgsz降到 512。另外注意 Windows 下workers能设的数上限比较低超过 8 反而容易出问题。标签错位是最隐蔽的问题特点是训练能正常跑Loss 也在降但验证集 mAP 始终上不去。排查方法是在训练前随机抽一批训练集图片把标签画上去确认是西红柿的位置而不是周围杂草。如果标签画出来框偏了大概率是前面说的 VOC 转 YOLO 时坐标算错回头查脚本。4.4 验证集评估mAP 不是唯一指标训练结束后输出一个验证指标表包含 mAP50、mAP50-95、precision、recall 四个关键数值。mAP50 表示 IoU 阈值为 0.5 时的平均精度均值mAP50-95 是 0.5 到 0.95 间隔 0.05 的均值后者更严格、更能反映定位精度。yolo detect val \ datatomato.yaml \ modeltomato_project/exp_v8n/weights/best.pt输出示例Class Images Instances Box(P) R mAP50 mAP50-95 all 260 812 0.921 0.854 0.896 0.782 tomato 260 812 0.921 0.854 0.896 0.782这里 precision 0.921 意味着预测出来的框里有 92.1% 确实是西红柿recall 0.854 意味着真实西红柿里有 85.4% 被找到。对于单类检测场景precision 和 recall 比 mAP 更有业务意义——农业场景里漏检比误检代价更高如果 recall 偏低可以适当降低置信度阈值。5. 数据增强与标注复核让 1301 张的数据集物尽其用5.1 离线增强策略别再用旋转 90 度这种危险操作小数据集的常规操作是离线增强扩充样本量。但很多人在增强时有严重的想当然——比如把图片旋转 90 度这会让西红柿从正圆变成高椭圆形态分布失真的同时 label 也会跟着偏转。对于西红柿这种大体呈圆形或扁圆形的目标旋转角度限制在 ±15 度以内是安全的。import cv2 import numpy as np import os import random def augment_image(image, bboxes, angle10, scale0.1): h, w image.shape[:2] center (w // 2, h // 2) M cv2.getRotationMatrix2D(center, angle, scale) # 旋转标注框 rotated_boxes [] for x1, y1, x2, y2 in bboxes: pts np.array([[x1, y1], [x2, y1], [x2, y2], [x1, y2]], dtypenp.float32) dst cv2.transform(pts.reshape(-1, 1, 2), M).reshape(-1, 2) new_x1 max(0, dst[:, 0].min()) new_y1 max(0, dst[:, 1].min()) new_x2 min(w, dst[:, 0].max()) new_y2 min(h, dst[:, 1].max()) rotated_boxes.append([new_x1, new_y1, new_x2, new_y2]) rotated_img cv2.warpAffine(image, M, (w, h)) return rotated_img, rotated_boxes # 使用示例对训练集做小幅旋转增强 image cv2.imread(tomato_dataset/images/train/0001.jpg) with open(tomato_dataset/labels/train/0001.txt) as f: lines f.readlines() # 读取标注并转成像素坐标 # 省略了txt转坐标的细节直接假设bboxes是像素坐标列表 # bboxes [(x1, y1, x2, y2), ...]逻辑说明旋转增强的核心是标注框必须跟着旋转矩阵同步变换否则增强后的图片和标签就不匹配。这里用cv2.transform把四个顶点映射到新位置再取最小外接矩形。注意旋转后要做边界裁剪防止标注框超出画面范围。参数说明angle10是旋转角度正负均可scale0.1是缩放比例因子1.0 表示不缩放。这两个参数是小幅扰动的典型值建议不要超过 ±20 度和 ±0.2否则目标形态失真严重。除旋转外还可以用亮度调整±30%、HSV 色域扰动、高斯噪声三种方式。西红柿是红色系目标HSV 色域里 H 通道偏移 ±5 度能让模型对光照变化更鲁棒S 和 V 通道偏移 ±20 也不会影响语义。5.2 标注复核找出那些“看起来对但其实错了”的框1301 张图的标注如果是半自动生成的很可能存在三类问题漏标西红柿被叶子挡住没框出来、错标把旁边红色圆形物体框进去了、标注框过大或过小把西红柿周围的背景也包进去了。复核时重点关注两类图片一是近景大果图一个西红柿占据画面 50% 以上此时一个框通常只框一个果实二是密集小果图一串番茄十来个果实挤在一起此时小目标容易被漏检。# 找出标注框中宽高比异常的样本 import os label_dir tomato_dataset/labels/train anomaly_count 0 for label_file in os.listdir(label_dir): with open(os.path.join(label_dir, label_file)) as f: lines f.readlines() for line in lines: parts line.strip().split() bw float(parts[3]) bh float(parts[4]) # 西红柿接近圆形框的宽高比应该接近1:1 ratio bw / bh if ratio 2.0 or ratio 0.5: anomaly_count 1 print(f{label_file}: 宽高比异常 {ratio:.2f}) print(f异常框总数: {anomaly_count})逻辑说明这个脚本针对西红柿圆形目标的特点做专项检查宽高比超过 2 倍的框大概率是标注时手滑多拉了边或者误把一串番茄框进去了。运行后如果异常框占比超过 5%说明标注质量有问题需要人工介入修正。参数说明阈值设为 2.0 和 0.5即框的宽或高不能超过另一侧的两倍。如果你的目标种类里包含长条形的辣椒、黄瓜这个阈值要放宽到 3.0具体看目标形态。5.3 欠拟合和过拟合的判断与干预1301 张图训练 YOLOv8n最常见的现象不是不收敛而是训到 30 轮左右 validation loss 开始回升同时 mAP50 不再增长——这就是过拟合的征兆。判断标准训练集 mAP 继续涨、验证集 mAP 停滞或下降差值超过 5 个百分点。干预手段有三个按优先级排列第一加大数据增强强度。YOLOv8 默认开了 mosaic 和 hsv 增强但土豆番茄这类形状相近的小目标可以额外开启degrees15和translate0.1。第二降低模型容量从 v8n 换成更小的模型这个包没有更小版本所以一般是反向操作——换大模型但配合更强正则。第三加早停参数把patience从 20 降到 10宁可少训点也别硬撑。5.4 训练到部署导出 ONNX 和 TensorRT 的注意点训练完best.pt之后多数场景要做推理部署。导出 ONNX 的命令yolo export modeltomato_project/exp_v8n/weights/best.pt formatonnx imgsz640 dynamicTrue参数说明formatonnx导出 Open Neural Network Exchange 格式dynamicTrue允许动态输入尺寸方便部署时对不同分辨率图片做自适应。如果只做固定尺寸推理dynamicFalse导出的模型在 TensorRT 下推理速度会更快。导出后建议用onnxruntime跑一次前向验证输出形状是否为(1, 84, 8400)——84 是 4 个框坐标加 80 个 COCO 类别但你的模型是单类所以是(1, 5, 8400)这个差异要特别注意。6. 置信度阈值和 NMS 调参部署前的最后一公里模型训练完不等于直接在业务场景里能用。opencv 加载权重推理的输出是一堆原始框和分数要靠两个后处理参数决定最终结果——置信度阈值conf_thres和 NMS 的 IoU 阈值iou_thres。这两个值的设置直接影响误检率和漏检率的平衡。import cv2 import numpy as np # 假设模型输出为[1, 5, 8400]的矩阵5表示[x, y, w, h, conf] def post_process(predictions, conf_thres0.25, iou_thres0.45): # 过滤低置信度框 scores predictions[4, :] mask scores conf_thres boxes predictions[:4, mask].T scores scores[mask] # 按置信度排序 idx np.argsort(scores)[::-1] boxes boxes[idx] scores scores[idx] # 贪心NMS keep [] while len(boxes) 0: keep.append(0) if len(boxes) 1: break # 计算当前最高分框与其他框的IoU ious compute_iou(boxes[0], boxes[1:]) boxes boxes[1:][ious iou_thres] scores scores[1:][ious iou_thres] return boxes[keep], scores[keep] # 实际使用时在detect命令里设置 # yolo detect predict modeltomato_project/exp_v8n/weights/best.pt \ # sourcetest_images/ conf_thres0.35 iou_thres0.5逻辑说明置信度阈值控制“框里面是西红柿的可信程度”值设得高框少了但更准设得低框多了但误检增多。NMS 的 IoU 阈值控制“两个框重叠到什么程度算同一个目标”值设得高重叠框会被更激进地合并值设得低稀疏目标的独立框更容易保留。参数建议西红柿检测场景下conf_thres0.3是基准值如果业务允许漏检但不能误检调到 0.5如果追求召回率调到 0.15 配合少量人工过滤。iou_thres0.45是通用默认值但对一串番茄密集排列的场景0.45 会把相邻果实的框合并建议降到 0.3。yolo detect predict \ modeltomato_project/exp_v8n/weights/best.pt \ sourcetest_images/tomato_field.jpg \ conf_thres0.35 \ iou_thres0.4 \ saveTrue参数说明saveTrue保存带标注框的结果图到 runs/detect/predict 目录。source可以是单张图片、视频文件或者目录YOLOv8 会自动遍历目录下所有支持的图片格式。我自己的习惯是训练完先在验证集上扫两档参数——第一档conf_thres0.25看最高召回能到多少第二档conf_thres0.5看最高精度能到多少然后根据业务容忍度取中间值。从那以后我每次处理这种小型标注数据集都会强制走一遍“解压结构确认 → 标签质量抽查 → 格式转换反向验证 → 训练曲线盯提前停 → 部署前阈值扫描”这五步每一步踩的坑都能省下至少半小时的排错时间。这套流程不挑数据集换到辣椒、土豆、芒果上同样适用希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网