资讯动态

果园柑橘检测数据集实战:从VOC/YOLO标注到模型训练部署

发布时间:2026/10/17 9:35:42 来源:尧图企业网站定制
做目标检测这几年农业场景是我觉得最“折磨人”也最有意思的方向之一。最近拿到一批“果园柑橘树上柑橘检测数据集”VOCYOLO两种标注格式2511张图1个类别专盯果树上还没摘的柑橘。别看只有1个类别、两千多张图真拿它去训练、去部署里面能展开的门道一点都不少。这篇文章就从一个开发者的角度把这个数据集从头到尾拆一遍它包含什么、适合做什么、标注格式怎么转、怎么训出能用的模型、又有哪些坑等着你。不管是刚入门目标检测的学生还是准备做农业视觉落地的工程师都能在这里找到可以直接抄的作业。1. 先把这个数据集看明白它到底是什么、能用来做什么1.1 数据集画像2511张、1类别、树上柑橘从标题就能看出三个关键数字2511张图片、1个类别、树上柑橘检测。图片内容基本都是果园实拍柑橘挂在树上背景有树叶、树枝、天空、土壤有时候还有远处的树冠。跟网上常见的商品图或者实验室白底图不一样这批数据更接近真实作业环境所以训练出来的模型抗干扰能力理论上会更好。这个数据集的标注格式同时给了VOC和YOLO两种这一点对新手特别友好。VOC格式是XML文件坐标是绝对像素值早期很多检测框架都在用YOLO格式是TXT文件坐标是归一化后的中心点加宽高。两种格式同时给意味着你不需要费劲去找转换脚本想用哪个框架都可以直接上手。类别只有1个就叫柑橘或者orange这点也值得琢磨一下。很多公开数据集动辄80类、800类看起来热闹但落到实际场景里我们大部分时候只需要检测一个目标。类别少不代表简单反而会把问题聚焦在“定位”本身——怎么把树上的果子跟树叶、树枝、光影区分开这才是真正的难点。1.2 能落到哪些真实场景这类数据集不是拿来做学术消遣的它背后连着好几个具体的农业需求。第一个场景是果园产量预估。过去果农靠经验估算产量误差很大如果能在花期后、果实膨大期通过拍照检测树上的柑橘数量再结合单果重量模型就能比较准确地预测整个果园的产量对销售定价、物流调度都有帮助。第二个场景是采摘机器人视觉。现在有不少团队在做柑橘采摘机器人机械臂要抓果子首先得知道果实在哪里。检测模型输出每个柑橘的边框机械臂才能规划路径、避开树枝。这个场景对检测的实时性要求很高而且采摘时往往有遮挡模型得扛得住。第三个场景是生长监测与植保作业。比如无人机或者地面巡检车在果园里走一圈自动统计每棵树的挂果数量、果实大小分布帮助农艺师判断哪些区域需要施肥、疏果或者防病。这时候检测模型是整套监测系统里的第一环后面挂上统计模块就能用了。还有一个容易被忽略的场景是数据清洗工具的前置环节。比如果园管理App里用户上传一张照片系统先检测出柑橘再截取每个果实的局部图做成熟度分级、病虫害识别。也就是说这个数据集可以作为后续多级模型的基础先定位再细看。1.3 哪个基础模型适合它哪些不适合对于只有1个类别的任务其实不需要一上来就选特别大的模型。我自己试过几种方案给个参考YOLOv8n/s小模型速度快适合边缘设备和实时场景精度也不算差是首选。YOLOv8m/l精度更高但训练时间和推理时间都上去了除非你的设备性能很强否则没必要。更早的YOLOv5或者SSD也能做但新框架在很多细节上更省心比如数据增强、训练策略都内置好了。Faster R-CNN这类两阶段检测器在遮挡和小目标上往往更稳缺点是慢。如果对实时性没要求追求最高精度可以试试。我的建议是先用YOLOv8s跑通全流程看mAP能不能接受如果精度不够再考虑增大模型或者换两阶段方案。农业场景数据本身就有点脏一开始就用大模型容易过拟合反而不划算。2. 为什么“树上柑橘检测”比想象中更难2.1 果园不是实验室光照和遮挡的干扰很多人以为检测柑橘就是把那张橙色圆球找出来实际上一进果园就傻眼了。强光下果实表面会反光橙色的饱和度变化非常大阴天时光线均匀但果实和树叶的对比度低模型容易把叶子也当成目标。遮挡问题更让人头疼。柑橘是成串长的前面的果子挡住后面的果子这种情况在数据里很常见。标注人员不可能把每一个被完全遮住的果子都标出来——实际上数据集里的标注通常也只标可见的部分。这就给模型传递了一个信号看不到的果子就不用框。这个定义本身合理但模型学完后在遮挡特别严重的时候会漏检这是这类数据的通病。所以训练时我一般会开着Mosaic增强和随机旋转让模型见过更多遮挡和光照变化。如果发现验证集里漏检集中在“被叶子半遮挡”的果子上还可以专门从数据集里挑出这些图做一次微调拿少量样本校正模型的偏见。2.2 小目标和密集目标的双重挑战柑橘挂在树上拍摄距离稍微远一点果实可能只占图片的几十个像素。YOLO系列在训练时会把图片缩放到640×640果实一旦小于32×32检测难度就直线上升。密集目标又是另一个问题一串果子五六颗挤在一起相互之间的IoU重叠很严重NMS后处理一不小心就会把旁边的果子过滤掉。我踩过的坑是拿到数据后只盯着平均mAP看mAP看着挺高但实际用的时候发现成串的果漏掉一大半。后来把验证集按“单果图”和“多果图”分开统计发现mAP差距能差到十几个点。这个现象在农业检测里特别典型——目标往往密集且重叠不能只看总体指标。2.3 正负样本和标注一致性1个类别听起来简单但标注一致性反而是最容易出问题的地方。比如一个小柑橘离镜头特别远、只有几个像素标还是不标一个果实80%被叶子挡住只露出一点橙色边缘标还是不标不同的标注员给出的答案可能完全不一样。我在实际使用数据集前会抽几十张图自己看一眼有时候同一张图、不同人标出来的结果差别不小这在公开数据集中也会遇到。所以拿到任何数据集第一件事不是急着训练而是先做一致性检查。如果发现某些标注框明显不完整或者把远处模糊的果子都标了进去你需要决定是保留还是清洗。这个决定会影响整个模型的输出口径——你到底想让它把“很远的果”也检测出来还是只检测近处的可采摘果。明确这个目标后面所有工作才有的放矢。3. VOC和YOLO两种标注格式到底怎么用3.1 两种格式的原理差异VOC格式本质上是一份XML描述文件图片里每一个物体都对应一个YOLO格式完全不同。每个目标对应TXT文件里的一行内容是“类别ID x_center y_center width height”。这四个坐标值全部是归一化后的相对值范围在0到1之间用图片宽度和高度做分母。也就是说同样的一个框VOC里可能是“xmin500, ymin300, xmax800, ymax700”到了YOLO里就变成了“0.3385 0.4630 0.1563 0.3704”看起来毫无头绪但只要还原回去就知道它们描述的是同一个框。为什么要归一化因为不同图片的尺寸不一样目标检测模型要批量训练图片会被缩放或者填充到固定尺寸。归一化后的坐标不管原图多大都可以直接映射到新的画布上省去了逐张图片重新计算的麻烦。如果不小心把绝对像素值当成了归一化值喂进模型训练出来的模型就会完全无法收敛这是一个特别常见的坑。3.2 目录结构解析拿到数据集之后先看目录结构。标准的VOC格式目录通常长这样dataset/ ├── Annotations/ │ ├── 000001.xml │ ├── 000002.xml │ └── ... ├── JPEGImages/ │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... └── ImageSets/ └── Main/ ├── train.txt └── val.txtYOLO格式目录则一般长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/YOLO要求图片和标注文件同名标签文件后缀是.txt。如果一份数据同时给VOC和YOLO通常意味着VOC版本是原始标注YOLO版本是转换好的。这个数据集的标题既然写明“VOCYOLO格式”大概率两边是齐整的但我每次都建议先核验一遍文件名是否对上避免有的图片缺标注、有的标注没图片。3.3 格式互转时的常见坑如果你拿到的只是其中一种格式需要手动转换那有几个地方要特别留意。第一坐标越界。人工标注时偶尔会把框画出图片边界比如xmax写成了图片宽度加10。VOC格式里还能发现问题转成YOLO格式后坐标就会大于1训练时YOLO会直接报错或者忽略这个目标。稳妥的做法是转换时把坐标裁剪到图片边界内再计算归一化值。第二类别ID顺序。VOC格式里类别是字符串YOLO格式里类别是数字ID这个ID取决于你写classes文件时的顺序。如果数据集只有1个类别那所有目标都写成0不会有歧义但如果后面你加了第二个类就必须确保ID和类别名的映射关系不乱。第三空标注文件。有些图片里确实没有目标那对应的TXT文件应该是什么正确答案是空文件或者干脆不生成文件。但有些转换脚本会生成一个只有一个换行符的文件这在部分框架里会被解析成异常目标导致训练中断。我一般会写一个小脚本扫一遍把所有不含有效内容的TXT文件清空掉。第四XML文件编码问题。有些标注工具生成的XML带BOM头Python的xml解析库偶尔会报错。用的时候最好统一用UTF-8无BOM格式重新写一遍。这些问题单个看都不大但一旦数据集规模化就会变成训练流程里的定时炸弹。4. 从零训练一个柑橘检测模型的完整流程4.1 环境准备训练一个YOLOv8模型环境配置其实很简单。先确认电脑有Python环境然后装一个依赖库就够用了。我通常用虚拟环境隔离避免不同项目之间的依赖冲突。pip install ultralytics如果用的是GPU还需要提前装好对应版本的CUDA和PyTorch。装完以后可以简单验证一下版本yolo predict modelyolov8s.pt sourcehttps://ultralytics.com/images/bus.jpg这条命令能跑通就说明环境基本没问题。CPU也能训练但2511张图跑100个epochCPU可能要熬好几个通宵GPU则快得多。所以有条件还是建议用GPU。4.2 划分数据集并准备YAML2511张图常见做法是按8:1:1划分成训练集、验证集、测试集也有人直接按8:2分训练和验证。需要注意的是如果数据采集时是连续拍摄同一个果园那同一条过道、同一棵树的照片很可能在时间上连续简单随机划分容易导致训练集和验证集里有高度相似的图验证指标会虚高。更严谨的做法是先按拍摄区域分组再在组级别划分避免数据泄漏。划分完成后YOLOv8要求数据目录结构清晰还需要一个YAML文件描述路径和类别。参考内容如下path: ../citrus_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: citrus这里注意path是相对YAML文件所在位置的路径train和val是相对于path的路径。如果写错了路径训练一开始就会报“找不到图片”的错误。跑之前先打印一下数据集配置确认每个路径都能命中再开始训练。4.3 训练与评估YOLOv8的训练命令很简洁核心参数就几个yolo detect train datacitrus.yaml modelyolov8s.pt epochs100 imgsz640 batch16modelyolov8s.pt加载预训练权重在这个基础上微调收敛快、泛化好。epochs100迭代次数。农业小数据集100轮通常够用了再多容易过拟合。imgsz640训练输入尺寸。想要更好的小目标检测能力可以改成960或者1280但显存占用会变大。batch16批大小视显存调整显存不够就降到8。训练结束后模型会在run目录里生成weights/best.pt和weights/last.pt。best.pt是验证集上表现最好的权重后面部署用这个。这时候要看几个关键指标mAP0.5、mAP0.5:0.95、Precision和Recall。柑橘检测任务里我特别关注Recall因为漏检一个果子的代价往往比误检大——漏检意味着产量少算一个或者采摘机器人漏摘一个都是直接损失。如果Recall偏低可以考虑降低置信度阈值或者增加训练样本。4.4 输出与导出训练完的模型如果想在别的设备上跑可以导出成不同格式。yolo export modelbest.pt formatonnx yolo export modelbest.pt formatengine导出的选择取决于部署端。普通服务器用ONNX就够了移动端可以用NCNN或者TFLite嵌入式设备如果用的是配套推理引擎可以导出对应的engine格式。导完以后建议拿一张测试图跑一遍推理确认导出后的模型和PyTorch原模型的预测结果基本一致。5. 我踩过的坑和你要避开的坑5.1 标注错位与漏标拿到数据集后我习惯先随机抽20张图同时把标注框画出来肉眼核对一遍。这个过程很快但作用非常大。有一次我抽到一张图片发现标注框和实际的果实位置有明显偏移推测是标注工具坐标转换出了问题。如果照着这种标注去训练模型学到的定位自然就会歪。漏标是另一个常见问题。一张树冠的照片上可能有十几颗果结果只标了最显眼的几颗。模型训练时会把这些漏标区域当成背景相当于主动告诉模型“这些地方没有果”推理时模型自然就学了不去检测它们。如果漏标比例太高整体Recall就废了。遇到这种情况我建议把漏标严重的图清理掉或者自己补标一下。5.2 类别不平衡与背景误检只有1个类别时没有类别不平衡的问题但“前景-背景”不平衡依然存在。果园图片里大部分区域是叶子、天空和树干柑橘只占很小一部分。YOLO的默认训练策略已经考虑了这个问题但如果发现误检很多——比如把卷曲的枯叶当成果子——可以考虑增加背景负样本图片单独抽一些没有果子的树冠图放进去训练帮助模型学会“这些不是果”。背景误检在测试时最容易暴露。模型在验证集上mAP很高但一跑到真实果园就乱框。解决办法是收集部署环境里的负样本图做一轮针对性微调哪怕只有几十张背景图效果往往都很明显。5.3 评估指标的陷阱mAP是目标检测最常用的指标但它容易掩盖问题。举例来说验证集里如果每张图平均只有2个果子模型漏掉1个mAP可能只掉几个点但真实果园里一张图有20个果子漏掉的可能是10个。所以不要只看mAP要专门看一下Recall和PR曲线在低置信度区域的表现。我自己的做法是训练完以后拿一个小视频或者一组连续照片做压力测试统计模型输出的数量是否和人工点数基本一致。如果在视频里检测数量忽高忽低说明模型对光照和姿态变化敏感需要进一步优化数据增强策略或者重新划分验证集。6. 把模型真正用到果园里还需要做什么6.1 拍摄设备和角度怎么定数据集的图片是果园场景但你的实际部署场景可能和它不完全一致。如果数据集中图片主要用手机横拍而你要部署在无人机向下俯拍的画面上模型效果肯定会打折扣。所以部署前先弄清楚拍摄设备的分辨率、镜头焦距和机位高度。我个人的经验是果园场景中相机和果实的距离决定了目标在图片中的大小。距离越远目标越小原有模型的有效检测距离越短距离太近果实占满画面又会出现目标截断的问题。建议实际部署时固定机位高度和角度让输入分布和训练分布尽量接近。不同果园的树形、品种、疏果习惯都不一样最好在目标果园里重新拍一批图做几十张的快速微调这比直接拿原模型硬上要靠谱得多。6.2 推理部署的轻量化取舍如果模型要跑到采摘机器人的控制端实时性就必须认真对待。YOLOv8n在嵌入式设备上可以达到不错的帧率但前提是输入尺寸别拉太大、后处理别写得太复杂。导出模型之后建议先用测试视频实测一遍重点看处理一帧要多少毫秒、显存或者内存占用多少。如果速度不够优先考虑降低输入尺寸比如从640降到416然后看有没有算子不支持导致的中途转换失败最后才考虑换更小的模型。轻量化不是只看参数数量实际跑起来的行为才是最准确的判断标准。6.3 从检测到计数再到成熟度识别检测模型只是第一步。农业上真正要的往往是“数量”和“品质”。所以做完检测之后可以再接一个计数模块。计数不是简单数框需要处理同一个果在多帧画面里重复出现的问题这时候可以用跟踪算法比如ByteTrack加一个目标ID按ID去重才能得到比较准确的树上挂果总数。再往后走可以把每个检测框裁剪出来输入另一个分类模型做成熟度分级区分青果、转色果、成熟果。这类下游任务最好在独立的裁剪图上做因为它们关注的是细节颜色和纹理目标检测模型的输出分辨率不一定够用。把“检测”和“细分”拆成两级整个系统的可维护性会好很多。最后再分享一条经验做完模型训练只是起点在果园里跑通整个闭环才算数。数据集的2511张图给你打了一个底子但真正到了不同品种、不同地域的果园光照角度、枝叶密度都会变。省钱省时的做法是带上模型到现场拍一段视频用一个快速脚本把检测结果可视化出来让果农看一眼框得准不准。这个反馈比任何指标都直接。农业视觉的难点从来不在算法本身而在于你能不能把算法放进真实世界的复杂环境里让它稳定工作。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑