ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

小番茄目标检测数据集:从VOC XML到YOLO训练全流程解析

小番茄目标检测数据集:从VOC XML到YOLO训练全流程解析 简介这是一份面向计算机视觉与智慧农业领域的小番茄目标检测数据集包含不同光照、拍摄角度下的果实图像及对应XML标注能够支撑YOLO等主流目标检测算法的训练、验证与优化常用于果实成熟度判别、自动化采摘等任务。资源包总计1790个文件内含895张PNG格式实景图片与895个XML标注文件标注记录了边界框坐标与类别标签整体压缩包大小约180.33MB目录结构规整便于直接划分训练集、测试集进行模型评估。目前已有79人浏览学习对于农业检测方向的研究者与学生来说是一份轻量、可直接上手的实战数据。借助该数据集可以系统练习YOLO系列模型的训练和参数调优也可引入迁移学习与数据增强策略提高模型泛化能力为小番茄的精准识别与农业智能化落地提供扎实的数据基础甚至可在此基础上进一步探索改进检测算法的思路。1. 小番茄检测先解决数据问题做农业视觉落地的朋友应该都有同感模型结构不是瓶颈数据才是。一个在COCO上表现很好的YOLO模型换到温室大棚里识别小番茄mAP能掉到没法看——因为果实密集、互相遮挡、光照不均还有成熟度带来的颜色差异。要在这类场景下把检测效果做起来第一步不是调参而是拿到一份标注质量合格、分布合理的数据集。这份小番茄目标检测数据集就是干这个用的图片来自真实拍摄角度标注采用XML格式按PASCAL VOC的标准记录每个果实的边框位置和类别信息可以直接用来训练YOLOv5、YOLOv8或者R-CNN系列模型。适合做农业检测算法验证、迁移学习基线测试也适合当作学习目标检测流程的入门数据。下面从数据格式、转换脚本到训练配置把整个链路拆开讲。2. 理解数据集结构与XML标签格式2.1 图片与标注文件的组织方式拿到压缩包后先看目录结构。常见的组织方式有两种一种是图片和XML文件放在同一个文件夹另一种是分images和annotations两个子目录。这套小番茄数据集里图片文件名为tomato784.png、tomato394.png这种序号格式对应的XML文件名与图片名完全一致后缀不同。同名的XML就是这张图的标注文件训练脚本读取时通过文件名前缀来关联。├── images/ │ ├── tomato784.png │ ├── tomato394.png │ ├── tomato259.png │ └── ... └── annotations/ ├── tomato784.xml ├── tomato394.xml ├── tomato259.xml └── ...如果压缩包内图片和XML混在一起先用下面命令把标注文件筛出来按项目结构重新整理。-name参数指定匹配后缀exec对筛选结果执行移动操作{}是find找到的每个文件的占位符。mkdir -p images annotations for f in *.xml; do mv $f annotations/ done for f in *.png; do mv $f images/ done2.2 XML里到底存了什么信息打开任意一个XML文件核心结构是annotation根节点下的几个子节点。size记录图片宽高和通道数object节点每出现一次就代表图里的一个检测目标。VOC格式里bndbox用xmin、ymin、xmax、ymax四个值表示左上角和右下角坐标单位是像素。一个object节点大概长这样annotation folderimages/folder filenametomato394.png/filename size width640/width height480/height depth3/depth /size object nametomato/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin126/xmin ymin98/ymin xmax210/xmax ymax173/ymax /bndbox /object /annotationname字段是类别标签本数据集里就是tomato。truncated为1表示目标被图片边界截断difficult为1表示目标辨识难度大这两个字段训练时一般直接忽略。补充一点小番茄是圆形果实实际标注中有些框会包住相邻果实的部分区域两个框之间的IoU可能偏高用YOLO训练时nms阈值要留出余量。2.3 批量检查XML标注的合法性标注文件多了之后肉眼检查不现实。用脚本批量读XML重点检查三类问题一是坐标值是否越界比如xmax大于图片宽度二是是否有空标注一个object都没有的图片在训练中会被当成背景图数量过多会影响正负样本平衡三是不同图片间的尺寸是否一致混用分辨率会导致letterbox变形。import os import xml.etree.ElementTree as ET from PIL import Image def check_annotation(img_dir, xml_dir): bad_files [] empty_files [] for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue xml_path os.path.join(xml_dir, xml_name) img_name xml_name.replace(.xml, .png) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): bad_files.append(xml_name : image missing) continue img_w, img_h Image.open(img_path).size tree ET.parse(xml_path) objs tree.findall(object) if len(objs) 0: empty_files.append(xml_name) continue for obj in objs: 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) if xmin 0 or ymin 0 or xmax img_w or ymax img_h: bad_files.append(xml_name : bbox out of range) print(bad files:, bad_files) print(empty annotations:, empty_files) check_annotation(images, annotations)过滤条件里加了图片存在性检查避免XML与图片缺对。发现坐标越界处理方式通常是裁剪或者直接删掉问题标注而不是整个丢弃图片——小番茄果实密集一张图里往往有七八个有效标注为了一个坏框丢掉全部标注不划算。3. 从XML到YOLO训练格式的转换与数据划分3.1 为什么YOLO不直接读XMLYOLOv5和YOLOv8的训练接口不接受PASCAL VOC的XML格式它需要的是TXT标注文件每一行表示一个目标。YOLO的坐标定义和VOC完全不同它存储的是归一化后的中心点坐标和宽高取值范围0到1不依赖图片尺寸。class_id x_center y_center width height举个例子XML里一个框的坐标是(126, 98, 210, 173)图片宽640高480换算规则是x_center (126 210) / 2 / 640 0.2625 y_center (98 173) / 2 / 480 0.2823 width (210 - 126) / 640 0.1313 height (173 - 98) / 480 0.1563这么设计的好处是同一张图无论缩放到什么尺寸标注坐标都无需改动省去了逐尺度调整的麻烦。但代价是坐标信息变得不直观调试时如果发现检测框位置偏了很难直接从txt里看出问题通常要写个可视化脚本来核验转换是否正确。3.2 转换脚本与数据集划分写转换脚本时我一般同时完成两件事XML转TXT以及按比例切分训练集和验证集。下面是一个完整的转换脚本直接放在数据集根目录下运行import os import random import xml.etree.ElementTree as ET from PIL import Image classes [tomato] def convert_annotation(xml_path, img_path, out_txt_path): img_w, img_h Image.open(img_path).size tree ET.parse(xml_path) lines [] for obj in tree.findall(object): name obj.find(name).text if name not in classes: continue class_id classes.index(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{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) img_dir images xml_dir annotations label_dir labels os.makedirs(label_dir, exist_okTrue) items [] for xml_name in os.listdir(xml_dir): if not xml_name.endswith(.xml): continue stem xml_name.replace(.xml, ) xml_path os.path.join(xml_dir, xml_name) img_path os.path.join(img_dir, stem .png) if not os.path.exists(img_path): continue out_txt os.path.join(label_dir, stem .txt) convert_annotation(xml_path, img_path, out_txt) items.append(stem) random.seed(42) random.shuffle(items) val_count max(1, int(len(items) * 0.2)) with open(train.txt, w) as f: for stem in items[val_count:]: f.write(stem \n) with open(val.txt, w) as f: for stem in items[:val_count]: f.write(stem \n) print(ftotal: {len(items)}, train: {len(items) - val_count}, val: {val_count})脚本里random.seed(42)固定了随机种子保证每次运行划分结果一致复现实验结果时这点很关键。classes列表顺序决定了类别IDtomato在列表第一位所以TXT里所有标注的class_id都是0。YOLO训练时的类别顺序必须和这个列表保持一致否则会出现类别标签错位模型能训练但推理结果无法对应到正确的类别名称。划分比例按19:1或8:2都行小番茄这种目标尺寸偏小的数据集验证集不建议低于10%否则mAP评估的置信区间会很大调参时看不出真实效果差异。3.3 转换后做一次可视化验证转换完不要急着训练先抽几张图把TXT标注重新画回去对照原图确认坐标没有偏移。这一步能拦住大多数低级错误比如坐标归一化时除错了边、对象名过滤条件写错导致全图标注丢失。import cv2 def draw_yolo_boxes(img_path, txt_path): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path) as f: for line in f: parts line.strip().split() class_id, x_center, y_center, bw, bh parts x_center float(x_center) * w y_center float(y_center) * h bw float(bw) * w bh float(bh) * h xmin int(x_center - bw / 2) ymin int(y_center - bh / 2) xmax int(x_center bw / 2) ymax int(y_center bh / 2) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) return img img draw_yolo_boxes(images/tomato394.png, labels/tomato394.txt) cv2.imwrite(check_tomato394.png, img)注意YOLO框角点换算回去时要用x_center - bw/2和x_center bw/2不是直接加宽高。另外cv2读图默认是BGR通道直接imwrite输出颜色和原图一致但如果用matplotlib显示需要先转换成RGB再imshow。4. 小番茄检测的YOLO训练配置与参数调整4.1 从零训练还是用预训练权重小番茄数据集规模不大从零训练YOLOv8s效果通常不太理想。常见做法是加载COCO预训练权重把最后一层类别数改成1继续训练。预训练权重已经学会了通用的边缘、纹理、颜色特征小番茄果实和茎秆的视觉特征相对明显迁移学习的起点比随机初始化高很多收敛也快。以YOLOv8为例数据配置文件按下面格式写path: /path/to/tomato_dataset train: train.txt val: val.txt nc: 1 names: 0: tomatopath是数据集绝对路径train和val指向我们之前生成的TXT文件列表。YOLOv8读取这个文件后会自动把列表中的每个stem拼接成图片路径和标签路径。注意train.txt里存的是不带后缀的文件名YOLOv8的假设是图片在images/目录、标签在labels/目录文件名前缀一致。首次创建数据集时在images同级的labels文件夹里新建一个train子目录把转换好的txt标签按划分放进去路径层级不能错。4.2 训练命令与关键超参数训练命令很直接yolo detect train \ datatomato.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20训练超参数对结果影响很大下面逐个解释我在小番茄数据上的设置经验参数设置值说明modelyolov8s.pt用yolov8s的COCO预训练权重作为起点参数700万级显存占用低。精度要求高换yolov8m或yolov8l显存8G以下不建议limgsz640图片输入尺寸。如果你的原图分辨率远大于640可以先缩放。小番茄果实本身很小强制缩得过小会让果实的像素区域只剩几个像素检测头无法提取有效特征batch16受显存限制设置成显存大小 / (模型参数占用量 * imgsz缩放系数)的粗略估算值。批量太小batch norm统计不稳定批量太大训练时间翻倍lr00.01初始学习率。COCO预训练权重已经处于一个较优的局部区域学习率过大会破坏预训练特征过小收敛太慢。从0.01开始配合余弦退火patience20验证集mAP连续20个epoch不提升就提前停止训练。小数据集容易过拟合这个参数能帮你省下大量时间这里额外说明一个容易踩的坑yolov8s.pt会把最后一个检测头去掉换成随机初始化的新检测头nc1。但如果你在同一个数据集上想继续训练比如先训了100轮再训100轮不要再用预训练权重要加载上一次的last.pt否则检测头被重新初始化之前的训练完全被浪费。4.3 小目标改进方向小番茄果实相对整张图而言通常属于中、小目标YOLOv8的检测头里有P3特征层专门处理小目标但训练时是否生效取决于标注框的像素面积分布。先跑一段脚本统计一下import os import numpy as np sizes [] for txt_name in os.listdir(labels): with open(os.path.join(labels, txt_name)) as f: for line in f: parts line.strip().split() w float(parts[3]) * 640 h float(parts[4]) * 640 sizes.append(w * h) arr np.array(sizes) print(box pixel area percentiles:) for p in [10, 25, 50, 75, 90]: print(f {p}%: {np.percentile(arr, p):.1f} px^2)如果大量标注框的面积低于32x32像素也就是1024px^2P3特征层是检测的主力原来的imgsz640可以让P3层对应的特征图分辨率达到80x80基本能满足小番茄的检测需求。但如果你用的是YOLOv5建议把imgsz调大到960同时增大P3层anchor的尺度范围。5. 训练后的效果验证与实用调优技巧5.1 用val指令评估模型并看诊断图训练结束后先跑一次标准的验证流程看mAP50和mAP50-95这两个指标。mAP50是IoU阈值0.5下的平均精度mAP50-95是0.5到0.95每间隔0.05取一个阈值算出的平均值。对密集果实场景mAP50-95比mAP50更能反映定位精度。yolo detect val \ modelruns/detect/train/weights/best.pt \ datatomato.yaml命令输出的results.csv里包含每个类别的Precision、Recall和mAP。对番茄这种成熟度会影响外观的目标Precision和Recall差距过大说明某个成熟阶段的果实没有被正确检测。这时先看confusion_matrix.png如果漏检集中在某个颜色区域建议补充对应光照条件下的图片数据。5.2 推理脚本里的预热与阈值策略嵌入式设备或机器人上做实时检测时推理脚本建议加一次warmup让CUDA kernel先加载完成再开始计时。检测置信度阈值设0.25通常够用但在果实密集场景NMS的IoU阈值建议从默认0.45调低到0.35减少相邻果实的框被合并的概率。from ultralytics import YOLO model YOLO(best.pt) model.predict(sourceimages/tomato394.png, conf0.3, iou0.35, saveTrue)iou参数在NMS后处理阶段生效数值越小两个重叠框被合并的条件越严苛过高会保留大量重复框在密集区域让结果看起来一片红绿。特别提醒不要在预测阶段把conf设太低小番茄的误检框往往出现在叶片和背景纹理上一味降低置信度阈值收获的更多是噪音。5.3 数据增强参数的适度策略YOLOv8默认开启了hsv_h、hsv_s、hsv_v、translate、scale、fliplr等增强项。小番茄检测中不同成熟度的果实颜色差异很大适当增强HSV变换可以让模型忽略颜色、关注形状和纹理特征。但注意不要加强mosaic2.0的强度mosaic会把四张图片拼在一起导致小番茄被切碎反而增加训练难度。建议通过augmenttrue开启在线增强并在验证时用augmentfalse保证推理性能不被打折。真正到现场部署时还会遇到相机安装角度和数据集不完全一样的情况。一个务实的做法是用这个数据集训练出基线模型从验证集里挑出10张最难检测的图片人工分析错误类型——是小番茄被叶子遮挡、果实重叠导致目标合并还是因为背景反光产生了误检。这些错误模式清楚之后再决定是加数据还是调结构。这样一轮轮下来模型在特定农场环境里的表现才会逐步逼近实用水平。本文还有配套的精品资源点击获取
返回列表