尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

油茶果成熟检测数据集实战:VOC转YOLO训练全流程详解

油茶果成熟检测数据集实战:VOC转YOLO训练全流程详解 简介面向油茶儿果实成熟度自动检测任务这份数据集提供了可直接用于YOLO系列模型训练与验证的完整图像和标签样本。压缩包共220个文件其中110张JPG原图对应110个XML标注文件包体大小约276MB便于一次性下载与部署XML内以边界框形式标定果实位置并记录青涩、半成熟、成熟等不同状态图像采集覆盖不同季节与光照条件能够提升模型在实际果园环境中的泛化能力。所有标注均经专业人员手工校对位置与类别信息具有较高一致性和可靠性用户可据此直接开展目标检测实验省去数据收集、清洗与标注重建等耗时环节。该数据集还可支撑油茶产量评估、品质分级等农业自动化场景适合计算机视觉学习者、农业科研人员以及相关算法工程师用来验证模型性能。当前页面已有85人学习/下载适合用于课堂项目、毕业设计或产业落地前的小规模验证。1. 油茶果成熟检测数据集一份图片xml能帮你省下多少弯腰油茶果采摘前要判断成熟度果园里一棵树几百个果子青果红果混在一起靠人工仰头一个个看效率低还容易漏。这份“油茶儿成熟检测数据集”做的就是把现场拍的果实图片用xml标注框把每一个油茶果框出来再标上成熟状态交给YOLO训练一个自动检测模型。图片xml标签是典型的Pascal VOC组织方式也是YOLO生态里最常用的输入格式转换后能直接喂给YOLOv5、YOLOv8和YOLO11。适合正在做农业视觉落地、手上有自己的果园照片但不知道标注和训练怎么串起来的人。这篇文章按我实际做过的流程从拆包核对、转格式、训练参数到踩坑完整过一遍。2. 先拆开.rar看家底图片、XML标签和类别分布怎么核对拿到数据集合第一步不是急着写训练脚本而是先搞清楚这批数据到底标注了什么、能不能支撑YOLO训练。油茶果成熟检测和通用目标检测有一个本质区别同类目标在不同成熟阶段外观差异明显而不同成熟阶段的果实又可能同时出现在一张画面里所以标注质量直接决定模型上限。2.1 先确认标注粒度框果实还是框成熟区域油茶果成熟检测数据集里常见标注方式是把每一个可见果实单独画一个矩形框类别名区分成熟状态比如immature和mature。少数数据集会把整棵树的成熟区域画成一个大框这种只适合判断“这棵树熟没熟”没法定位单个果实做采摘、估产都不够用。打开一张xml看一眼object的数量和bndbox大小就能判断粒度。单张图十个以上小框基本就是实例级标注一张图就一个大框包住树冠那就是区域级标注。实例级数据才能直接用YOLO做果实定位区域级数据更适合做分类辅助。我一般会先用可视化脚本把标注框画到原图上随机抽30张检查一遍比直接看xml数字直观得多。2.2 解析XML标签VOC格式里有哪些字段VOC格式xml每条object记录一个目标核心字段是name和bndbox。一个标准xml长这样annotation folderimages/folder filenameIMG_20241010_093212.jpg/filename size width1280/width height720/height depth3/depth /size object nameimmature/name bndbox xmin120/xmin ymin80/ymin xmax200/xmax ymax160/ymax /bndbox /object object namemature/name bndbox xmin300/xmin ymin200/ymin xmax390/xmax ymax280/ymax /bndbox /object /annotation这里的size.width和height是整个图像的尺寸bndbox是矩形框左上角和右下角的绝对像素坐标。YOLO训练不直接读xml需要转换但转换前必须确认两个隐患一是size字段和实际图片分辨率是否一致二是name字段是否和后面yaml里class id一一对应。这两处错位转换出来的标签全是废的。如果你之后要补标注建议直接用labelImg或X-AnyLabeling这类工具打开xml它们原生支持VOC格式改完保存还是xml不用自己手写坐标。2.3 类别分布和图像质量核对动手前的三件套数据拿到手先跑一段统计脚本把类别数、框数量、图片尺寸分布、有没有异常框一次查清楚。我一般用这个脚本扫一圈import xml.etree.ElementTree as ET from pathlib import Path from collections import Counter xml_dir Path(Annotations) total_boxes 0 cls_counter Counter() size_set set() for xml_file in xml_dir.glob(*.xml): tree ET.parse(xml_file) root tree.getroot() size root.find(size) size_set.add((size.find(width).text, size.find(height).text)) for obj in root.findall(object): name obj.find(name).text cls_counter[name] 1 total_boxes 1 print(类别分布:, dict(cls_counter)) print(总标注框数:, total_boxes) print(图片尺寸种类:, size_set)类别分布能告诉你成熟和未成熟样本是不是严重失衡总框数能估算单类样本量尺寸种类则暴露图片是统一分辨率还是混着手机拍摄、无人机拍摄的多分辨率。如果尺寸种类超过三种训练时imgsz参数就得格外小心后面第4章会讲。尺寸核对应以实际图片为准不要完全信任xml里的size字段。我遇到过压缩包内图片被批量改小但xml没跟着改的情况训练出来预测框全部偏移。最稳的做法是转换脚本里直接用cv2读取图片的宽高来归一化这个坑第3章会展开。3. 把XML转成YOLO能吃的txt转换脚本和四个边界坑YOLO要求的标签是每张图片一个txt文件每行格式为“class x_center y_center width height”四个坐标值全部归一化到0~1。从xml转txt逻辑不复杂但细节决定能不能顺利train起来这章节给一个可以直接用的转换脚本再把最容易翻车的四个边界坑讲透。3.1 一个干净的VOC转YOLO脚本转换脚本最核心的点是框坐标必须除以图片实际宽高而不是除以xml里的size同时要做边界裁剪和无效框过滤。下面是我常用的版本import xml.etree.ElementTree as ET from pathlib import Path import cv2 class_names [immature, mature] # 顺序就是YOLO类别id别乱换 def convert_xml_to_yolo(xml_path, image_dir, out_dir): tree ET.parse(xml_path) root tree.getroot() filename root.find(filename).text img_path Path(image_dir) / filename img cv2.imread(str(img_path)) if img is None: print(f警告找不到图片 {img_path}) return h, w img.shape[:2] lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: print(f跳过未知类别 {name} 在 {filename}) continue cls_id class_names.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) # 夹紧到图像边界内 xmin max(0, min(xmin, w - 1)) ymin max(0, min(ymin, h - 1)) xmax max(0, min(xmax, w - 1)) ymax max(0, min(ymax, h - 1)) if xmax xmin or ymax ymin: print(f跳过无效框 {filename} {name}) continue x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h width (xmax - xmin) / w height (ymax - ymin) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) out_path Path(out_dir) / (Path(filename).stem .txt) out_path.write_text(\n.join(lines))转换的核心逻辑是先读图拿真实宽高再做边界裁剪最后归一化。为什么必须夹紧坐标因为有些标注工具会把框拉到图片外边界附近xmax可能略大于width不夹紧的话归一化后会出现大于1的坐标YOLO在loss里对越界坐标会非常敏感训练时经常报一堆warning甚至NaN。3.2 归一化坐标的四个边界坑第一个坑是“用xml尺寸还是图片尺寸”。必须用cv2读取的实际尺寸。xml里的size经常被标注工具写错或图片被压缩过以它为准框全部错位。如果图片读不出来宁可跳过也不要硬转。第二个坑是“类别名顺序漂移”。class_names的顺序就是YOLO的类别id任何改名的操作必须在转换前统一。曾经试过把“mature”写成“mature1”训练时类别id少了一位预测和标签对不上排查了半天。第三个坑是“零宽高框”。油茶果在照片上很小标注时手抖容易把xmin和xmax点成同一个像素转换出来的width为0训练时直接崩。解决办法就是上面脚本里的过滤逻辑xmax xmin就放弃这个框。第四个坑是“多行标签拼接”。如果一张xml里没有object代码会写出一个空txtYOLO训练时会认为“negative image”。如果空txt占比较高模型会偏向输出“无目标”导致检测不到任何果实。所以统计阶段就要看empty_xml的数量空图片要么补标要么从数据集里剔除。3.3 数据集划分按场景而不是随机划分转换完毕后就要划分训练集和验证集。这里最常见的选择是random split但对农业数据这是个陷阱同一棵油茶树的照片可能被同时分进训练集和验证集验证结果虚高模型真正遇到没见过的树时效果大跌。最合理的做法是按采集场景划分。油茶果数据集一般来自不同株树或不同日期拍摄文件名里通常带着拍摄时间。我一般会把文件名中的日期或地点编号作为分组key整组切分from pathlib import Path import random, shutil random.seed(42) images sorted(Path(images).glob(*.jpg)) groups {} for img in images: key img.stem.split(_)[1] # 例如 IMG_20241010_093212 - 20241010 groups.setdefault(key, []).append(img) train_imgs, val_imgs [], [] for key, imgs in groups.items(): random.shuffle(imgs) cut max(1, int(len(imgs) * 0.8)) train_imgs.extend(imgs[:cut]) val_imgs.extend(imgs[cut:]) print(ftrain {len(train_imgs)} val {len(val_imgs)})如果文件名没有场景字段可以按图片的拍摄时间元信息或者按目录分组。目标是保证同一株树、同一天拍的照片不跨集合。这样验证的mAP可信度才高后面落地时不容易翻车。4. 训练油茶果成熟检测模型预训练选择、损失权重和必调参数数据准备好了进入训练环节。油茶果成熟检测比通用目标检测更需要精心设参数因为果实小、密集、互相遮挡成熟和未成熟外观又很像。盲用默认参数训练mAP也许能看但去果园实拍就会露馅。4.1 预训练模型怎么选三个档位YOLO训练几乎不会从随机权重开始大家基本都是用COCO预训练权重微调。目前常用的是YOLOv5、YOLOv8和YOLO11训练接口基本一致按硬件条件分三档选表格式文字描述轻量档YOLOv5s或YOLO11n。显存占用小推理快适合边缘设备但小目标漏检明显。均衡档YOLOv8s或YOLO11s。精度和速度兼顾是农业检测的首选起点。精度档YOLOv8m/l或YOLO11m/l。果实密集、遮挡严重时精度上限更高但需要更大显存训练时间翻倍。第一次跑这份油茶果数据我建议直接用YOLOv8s或YOLO11s不要一上来就折腾大模型。数据量不超过几千张时大模型容易过拟合泛化反而不如中等模型。预训练权重会自动下载如果网络不稳定可以单独下载权重文件放在项目目录训练命令里改成本地路径即可。4.2 data.yaml怎么写类别和路径决定成败YOLO训练需要一份数据集描述文件里面指定训练集、验证集路径和类别表。我一般把oiltea.yaml放在datasets目录下path: ./datasets/oiltea train: images/train val: images/val nc: 2 names: 0: immature 1: mature这里的names顺序必须和转换脚本里的class_names顺序完全一致。很多人在这一步踩坑转换脚本里名字是“mature/immature”但yaml里写成了“成熟/未成熟”序号对不上训练不报错但精度完全错乱。path字段建议写成相对路径并且oiltea.yaml放在datasets/oiltea父目录下时train用images/train即可。如果你把yaml放在别的目录train和val就要改成绝对路径或者以项目根目录为基准写。4.3 训练命令与关键参数imgsz、batch、epochs、patience数据量和显存确定后训练命令如下yolo detect train \ modelyolo11s.pt \ dataoiltea.yaml \ epochs200 \ imgsz640 \ batch16 \ patience30 \ projectruns/oiltea \ nameexp1几个必须调的关键参数imgsz控制输入图像尺寸。油茶果在图片里可能只有几十个像素imgsz640是下限。如果显存允许768或1024对密集小目标有明显提升但训练时间也会变长。不要盲目上1280对小数据集容易把模型拖进过拟合。batch受显存制约。batch16是8GB显存的常见选择显存不够时优先减batch不要减imgsz因为小目标对图像分辨率更敏感。如果只有4GB显存batch8搭配imgsz640勉强能跑。epochs和patience配合。我一般设epochs200patience30意思是验证集指标连续30轮不涨就提前停。如果你的数据集只有几百张图epochs可以加到300但一定要开patience不然模型会过拟合到训练集上。4.4 类别不均衡与损失函数成熟样本少怎么办油茶果成熟度数据集常见的问题是不成熟样本远多于成熟样本比如青果占八成、红果占两成。模型会偏向学“多数类”把所有框都判成未成熟mAP里的成熟类召回率低得可怜。解决办法有三个方向按效果排序第一种是数据层面过采样。把成熟样本的图片重复复制到训练集让成熟框的数量和未成熟接近1:1。复制不是简单粘贴最好对成熟样本做水平翻转、亮度变化相当于扩充了一轮增强。第二种是损失层面调权重。Ultralytics训练命令里有cls参数它对应分类损失的系数。把cls从默认的0.5调到0.8或1.0会让模型更重视分类正确性对改善成熟类误判有一定帮助。但注意只调cls不够数据过采样必须做。第三种是调整置信度阈值。训练时不要动conf验证时可以改成conf0.1看模型真实的检出能力。很多时候不是模型没学到成熟特征而是预测分数偏低被默认阈值卡掉了。这个在第6章验收部分会细讲。5. 油茶果数据集训练避坑五个常见问题的现象、原因与解法训练油茶果数据这件事算法本身不难难的是数据里的坑一个个踩完才能跑出像样的结果。这一章把我遇到过的五类问题按“现象、原因、解法”拆开你照着自查能省很多调试时间。5.1 训练时报“No labels found”现象yolo命令启动几秒就报错提示在labels/train目录找不到标签文件。 原因转换脚本输出的txt没有和图片放在匹配的目录结构里。比如图片在images/train标签在根目录labels/train但txt文件名与图片名不一致或者转换后txt后缀写错成txt.txt。 解决先检查单个对应关系是否成立。用下面命令扫一遍缺标签文件for f in datasets/oiltea/images/train/*.jpg; do base${f%.jpg} tagdatasets/oiltea/labels/train/${base##*/}.txt if [ ! -f $tag ]; then echo 缺标签: $f fi done如果少数图片没有标注要么剔除要么给它们单独建一个目录存起来不要让空标签混进训练集。5.2 转出的txt全是0或越界现象训练能跑但日志里频繁出现“box coordinates越界”或loss不下降验证集Precision和Recall都是0。 原因标注框坐标超出图片实际宽高或xml里的width/height与真实图片不同归一化后中心点或宽高大于1。 解决回到第3章的转换脚本确认用了cv2.imread读取真实尺寸并且对坐标做了clip。转换后可以做一个越界检查脚本统计txt里任何一列小于0或大于1的行必须零越界再进入训练。5.3 模型把所有果实都判成未成熟现象验证集mAP整体看着不低但单独看成熟类的混淆矩阵成熟果实大量被预测成未成熟。 原因类别严重不均衡成熟样本太少模型学到的“类先验”就是未成熟输出置信度天然偏向多数类。另一种可能是标注本身漏标了成熟果比如图片里远处模糊的红果没被标出来模型反而学到了“成熟果不重要”。 解决先统计成熟框占比低于20%就做第4章说的过采样。再抽检标注看成熟果漏标率是否超过10%。数据修完再调cls损失系数顺序不能反。5.4 开启增强后模型越训越差现象同一份数据不开增强训练能到80% mAP开了mosaic和翻转后反而掉到70%。 原因油茶果图片里很多果实小到只有几十像素mosaic增强会把果实裁成几块导致小目标标签被切碎。另一个原因是增强尺度变化大模型要同时适应多个分辨率小数据量下学不稳。 解决Ultralytics可以通过augment参数控制强增强策略我一般把mosaic开到0.5而不是默认1.0让一半batch保留完整图片。同时关掉hsv_h和hsv_s的剧烈色彩变化因为油茶果成熟度判断依赖颜色特征颜色增强过头会把青果和红果的特征搞混yolo detect train \ modelyolo11s.pt \ dataoiltea.yaml \ epochs200 \ imgsz640 \ batch16 \ mosaic0.5 \ hsv_h0.01 \ hsv_s0.5增强的目标是让模型对光照变化鲁棒而不是改变果实颜色本身。成熟度本质是颜色特征这个尺度必须控制住。5.5 验证集mAP虚高现场实拍却漏检现象测试集上mAP80%几拿无人机或手机去果园拍一段视频单帧漏检严重成熟果经常没框。 原因验证集和训练集来源高度一致比如都来自同一批照片的随机划分但现场实拍的光照、角度、背景完全不同。这是数据集划分没按场景切分导致的典型问题。 解决严格按第3章的场景分组划分。另外落地时单独留一小组“现场实拍图”做最后验收这一组不参与训练和验证专门用来模拟真实使用条件。如果测试集mAP高但现场组漏检优先扩充现场条件的图片而不是调训练参数。6. 模型到底能不能用用线下测试和现场帧卡验收模型训练完最好的程度不是看train结束时的mAP而是看它在一段现场视频里的表现。我习惯的做法是先把最优权重导出来对一段没参与训练的视频做逐帧预测再看两类漏检的分布yolo detect predict \ modelruns/oiltea/exp1/weights/best.pt \ sourcefield_video.mp4 \ conf0.1 \ save_txtTrue \ saveTrueconf0.1是关键平时默认0.25会漏掉很多低置信度的成熟果实。线上使用可以回到0.25但验收一定要用0.1看模型的真实上限。预测完成后进入保存的labels统计每个类别出现的次数from collections import Counter counter Counter() with open(runs/detect/predict/labels/field_video.txt) as f: for line in f: cls line.split()[0] counter[cls] 1 print(counter)如果成熟类计数明显低于人工估算说明成熟果还是被漏了。我自己的验收标准是青果类漏检率小于5%成熟果漏检率小于10%才算这个数据集和模型组合能去现场用。如果成熟果漏检超过两成不要急着调训练参数先回头抽图看是不是标注漏标、类别不均衡或者数据集场景不够。顺序搞反只会浪费时间。最后留个习惯把训练集、验证集、现场验收组三个目录固定下来每一次重新训练都用同一套分组。这样不同版本之间的mAP才可比模型迭代才有意义。希望帮到你。本文还有配套的精品资源点击获取
返回列表