简介橙子目标检测数据集专为农业AI应用设计面向计算机视觉开发者、智慧农业研究者及高校教学实践者解决果园场景下果实识别、计数与形态分析等核心问题。资源包共1802个文件含900张JPG农业实景图像、900个对应YOLO格式TXT标注文件边界框坐标精确至小数点后四位、1个类别定义YAML配置文件及1份详细说明DOCX文档整体压缩包仅25.14MB轻量易部署。已有94人学习下载适配YOLOv5/v7/v8等主流框架开箱即用。用户可直接开展训练验证全流程训练集630张、验证集180张、测试集90张的7:2:1科学划分支持快速迭代真实光照、遮挡与多生长阶段样本显著提升模型泛化能力配套文档明确标注规范、适用场景与农业落地路径是开发采摘机器人视觉系统、果园产量预估模型或农业AI教学实验的理想基础数据集。1. 橙子目标检测数据集.zip不是“随便下个水果图”而是农业AI落地的第一块真实砖你搜“橙子目标检测数据集.zip”大概率是被某篇论文、某次课程作业或某条产线需求逼到这儿来的——不是想学水果分类而是要让摄像头在果园里、分拣线上、冷链车厢里真正认出橙子在哪、有几个、大小如何、有没有磕碰。这个.zip文件名看着朴素但它背后不是一堆网图拼凑的玩具数据而是农业视觉落地最关键的“燃料”带精确框Bounding Box、统一标注格式Pascal VOC 或 COCO、覆盖多场景树上挂果/采摘筐/传送带/不同光照/遮挡的真实采集样本。它解决的不是“能不能识别”而是“在强光反光、枝叶遮挡、果实堆叠、雾气弥漫的田间环境下模型能不能稳定输出可工程部署的坐标和置信度”。适合两类人一是刚跑通YOLOv5但一换真实果园图就崩的算法实习生二是被老板催着“下周给分拣机装上识别模块”的产线工程师。别急着解压——先搞清它为什么不能直接喂进训练脚本以及你解压后第一眼该盯住哪三个文件。2. 解压后别急着训练先用这三步验证数据集是否“能用”一个标称“橙子目标检测数据集”的压缩包90% 的翻车发生在训练前——标注错位、类别名不一致、图像路径断裂。我见过太多人unzip orange_dataset.zip python train.py后卡在KeyError: orange或ValueError: image not found上耗掉两天才意识到问题出在数据根目录结构上。下面这三步是我每次拿到新数据集必做的“安检流程”耗时不到5分钟却能省下8小时debug。2.1 第一步确认标注格式与类别定义VOC 还是 COCO打开解压后的根目录先看有没有Annotations/VOC 风格或annotations/COCO 风格文件夹unzip -l orange_dataset.zip | head -20 # 输出示例 # Length Date Time Name # --------- ---------- ----- ---- # 2345 2023-08-12 14:22 Annotations/000001.xml # 12890 2023-08-12 14:22 JPEGImages/000001.jpg # 123 2023-08-12 14:22 ImageSets/Main/train.txt提示如果看到Annotations/JPEGImages/ImageSets/这是标准 Pascal VOC 结构如果看到annotations/instances_train2017.jsontrain2017/则是 COCO 格式。二者加载逻辑完全不同混用必报错。接着检查类别定义。VOC 数据集的类别藏在 XML 文件里用以下命令快速抽样验证head -n 20 Annotations/000001.xml | grep -E (name|object) # 输出应类似 # object # nameorange/name # poseUnspecified/pose # truncated0/truncated # difficult0/difficult # bndbox # xmin123/xmin # ymin89/ymin # xmax345/xmax # ymax267/ymax # /bndbox # /object关键点name标签内容必须是小写orange不是Orange、citrus或fruit且全数据集中所有 XML 文件都必须统一。我曾遇到一个数据集70% 是orange30% 是oranges复数导致训练时类别映射失败模型只学一半。2.2 第二步校验图像-标注对齐缺图错名路径断VOC 要求JPEGImages/下的图片名如000001.jpg与Annotations/下同名 XML000001.xml严格一一对应。用 shell 快速比对# 提取所有图片名去后缀 ls JPEGImages/ | sed s/.jpg$// | sort img_list.txt # 提取所有XML名去后缀 ls Annotations/ | sed s/.xml$// | sort xml_list.txt # 找出只在图片列表中存在、但无对应XML的文件 comm -23 (cat img_list.txt) (cat xml_list.txt) | head -5 # 找出只在XML列表中存在、但无对应图片的文件 comm -13 (cat img_list.txt) (cat xml_list.txt) | head -5逻辑说明comm -23输出仅在第一个文件图片列表中出现的行即“有图无标”comm -13输出仅在第二个文件XML列表中出现的行即“有标无图”。只要输出非空就必须人工补全或删除——否则 DataLoader 会因FileNotFoundError中断。2.3 第三步可视化抽检肉眼确认框准不准、遮挡标没标别信文档描述直接画框看。用 OpenCV 写个最小可视化脚本Python 3.8无需额外安装import cv2 import xml.etree.ElementTree as ET import os def draw_bbox_from_xml(img_path, xml_path, save_pathNone): img cv2.imread(img_path) tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.find(name).text if name ! orange: # 严格过滤避免误标 continue bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) cv2.putText(img, orange, (xmin, ymin-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 1) if save_path: cv2.imwrite(save_path, img) else: cv2.imshow(check, img) cv2.waitKey(0) cv2.destroyAllWindows() # 示例调用替换为你自己的路径 draw_bbox_from_xml( img_pathJPEGImages/000001.jpg, xml_pathAnnotations/000001.xml, save_pathcheck_000001.jpg )参数说明img_path和xml_path必须是绝对路径或相对于当前工作目录的正确路径save_path设为文件路径则保存图片设为None则弹窗显示关键逻辑if name ! orange是防错保险——万一 XML 里混入了apple或leaf标签这里会跳过避免污染可视化结果。运行后重点看三点框是否完全包住橙子尤其注意枝叶遮挡下的果实边缘小果实直径30像素是否被漏标多个橙子紧贴时框是否各自独立而非合并成一个大框。如果发现系统性偏差比如所有框都偏右10像素说明标注工具存在坐标偏移需批量修正 XML。3. 从 VOC 到 YOLO转换脚本与四个边界坑YOLO 系列v5/v8/v10不吃 XML只认labels/*.txt文件每行格式为class_id center_x center_y width height归一化坐标。把橙子数据集转成 YOLO 格式看似简单但四个边界坑会让转换后训练效果断崖下跌——我踩过全部血泪经验如下。3.1 标准转换脚本支持 train/val/test 划分# convert_voc_to_yolo.py import os import xml.etree.ElementTree as ET from pathlib import Path def convert_annotation(xml_path, classes, output_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) # 输出 .txt 路径与图片同名存入 labels/ txt_name Path(xml_path).stem .txt txt_path os.path.join(output_dir, txt_name) with open(txt_path, w) as f: for obj in root.findall(object): cls_name obj.find(name).text.strip().lower() if cls_name not in classes: continue # 跳过非orange标签 cls_id classes.index(cls_name) bbox obj.find(bndbox) xmin max(0, int(bbox.find(xmin).text)) # 边界截断 ymin max(0, int(bbox.find(ymin).text)) xmax min(w, int(bbox.find(xmax).text)) # 防越界 ymax min(h, int(bbox.find(ymax).text)) # 归一化center_x, center_y, width, height x_center ((xmin xmax) / 2) / w y_center ((ymin ymax) / 2) / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}\n) # 主流程 if __name__ __main__: VOC_ROOT orange_dataset # 替换为你的解压路径 CLASSES [orange] # 严格按实际类别写顺序即 class_id # 创建 labels 目录 labels_dir os.path.join(VOC_ROOT, labels) os.makedirs(labels_dir, exist_okTrue) # 遍历所有 XML xml_dir os.path.join(VOC_ROOT, Annotations) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): convert_annotation( xml_pathos.path.join(xml_dir, xml_file), classesCLASSES, output_dirlabels_dir ) print(f✅ 已生成 {len(os.listdir(xml_dir))} 个 .txt 标签文件)逻辑说明max(0, ...)和min(w, ...)是防错核心——原始标注常因手动拖拽超出图像边界导致归一化后出现负值或 1.0 值YOLO 训练时会静默忽略该样本cls_name.strip().lower()统一处理空格和大小写避免Orange和orange被当两个类别输出精度.6f是 YOLO 官方推荐低于.4f可能因浮点舍入导致框微偏。3.2 四个必须填平的边界坑坑1图像尺寸不一致导致归一化失真现象训练 loss 不降mAP 始终 10%可视化发现预测框严重偏移。原因数据集中部分图像被缩放或旋转但 XML 中size标签仍记录原始分辨率如 4000x3000而实际 JPG 已被重采样为 1280x960。归一化时用错w/h坐标全乱。解决用 OpenCV 读取每张图实际尺寸动态覆盖 XML 中的size# 在 convert_annotation 函数开头加入 img cv2.imread(os.path.join(VOC_ROOT, JPEGImages, Path(xml_path).stem .jpg)) h, w img.shape[:2] # 实际宽高 # 后续归一化用此 w/h而非 XML 中的坑2小目标16x16像素被YOLO层忽略现象树梢上密集的小青橙总漏检验证时发现labels/中对应.txt文件为空。原因YOLOv5 默认model.yaml中anchors最小尺度为 32x32小于该尺寸的框在create_dataloader中被过滤。解决修改data/hyp.scratch-low.yaml中box,cls,obj三项 loss 权重并在train.py中显式启用小目标# data/hyp.scratch-low.yaml box: 0.05 # 降低 box loss 权重避免大框主导 cls: 0.5 # 提升 cls loss强化小目标分类 obj: 1.0 # 保持 obj loss 不变并在训练命令中加参数python train.py --data data/orange.yaml --cfg models/yolov5s.yaml --weights --img 1280 --batch-size 16 --hyp data/hyp.scratch-low.yaml坑3ImageSets/Main/划分文件缺失或错误现象train.py报错IndexError: list index out of range或训练集只有10张图。原因ImageSets/Main/train.txt中写的是000001无后缀但JPEGImages/下是000001.jpg或文件中混入了空行、注释行。解决用以下命令清洗并验证# 清洗 train.txt去空行、去注释、加.jpg后缀 sed /^$/d; /^#/d ImageSets/Main/train.txt | sed s/$/.jpg/ train_clean.txt # 检查是否所有文件真实存在 while read f; do [ ! -f JPEGImages/$f ] echo MISSING: $f; done train_clean.txt坑4中文路径或特殊字符导致读取失败现象Windows 下FileNotFoundError: [Errno 2] No such file or directory: JPEGImages/果园_20230812_001.jpg。原因Python 3.8 在 Windows 上默认用mbcs编码读取路径而中文路径需utf-8。解决在脚本开头强制设置编码import sys if sys.platform win32: sys.stdout.reconfigure(encodingutf-8) sys.stderr.reconfigure(encodingutf-8)4. 避坑训练阶段的五个高频翻车点与硬核解法即使数据集完美、转换无误训练过程仍可能因环境、配置、硬件细节集体翻车。以下是我在果园项目中实测的五条“后悔药”每一条都来自凌晨三点的 error log。4.1 CUDA out of memory 却显存显示只用 60%现象RuntimeError: CUDA out of memory.但nvidia-smi显示 GPU memory usage 仅 60%。原因PyTorch 的 CUDA cache 未释放或 batch-size 设置超过显存物理极限非占用率。解决训练前加torch.cuda.empty_cache()用--batch-size 8起步每轮增加2直到 OOM终极方案在train.py中插入显存监控# 在 train() 函数循环内加入 if torch.cuda.is_available(): print(fGPU memory: {torch.cuda.memory_allocated()/1024**3:.2f}GB / {torch.cuda.max_memory_allocated()/1024**3:.2f}GB)4.2 mAP0.5 从 0.85 骤降到 0.12第30 epoch现象前期收敛极好突然大幅掉点loss 曲线剧烈震荡。原因学习率衰减策略如 CosineAnnealingLR在后期过于激进或mosaic数据增强在小数据集上引入过多噪声。解决关闭 mosaic--nosave --noautoanchor --rect改用 step LR在train.py中注释掉lr_scheduler改用torch.optim.lr_scheduler.StepLR(optimizer, step_size20, gamma0.1)加--evolve参数让超参自动搜索比手动调更稳。4.3 验证时Recall接近 1.0 但Precision0.3现象模型疯狂框出大量低置信度区域几乎全是误检。原因conf_thres置信度阈值设得过低默认 0.001或iou_thresNMS 阈值过高默认 0.65导致框合并过度。解决推理时显式指定python detect.py --weights runs/train/exp/weights/best.pt --conf 0.4 --iou 0.45在val.py中加--task test并导出 PR 曲线用utils.metrics.ConfusionMatrix查看各类别 FP 分布。4.4 模型在测试集上 OK但部署到 Jetson NX 时框全偏移现象PC 端预测精准NX 上所有框向右下角偏移 20 像素。原因ONNX 导出时未固定输入尺寸NX runtime 自动 resize 引起坐标失真。解决导出 ONNX 时强制指定--img-size 1280在 NX 端推理代码中确保预处理cv2.resize与训练时--img参数一致关键ONNX 模型输入 tensor 名必须为imagesYOLOv5 默认否则 TensorRT 解析错位。4.5 多卡训练时 loss 为 nan单卡正常现象CUDA error: device-side assert triggered定位到nn.functional.cross_entropy。原因多卡间DataLoader的shuffleTrue导致 batch 内类别分布不均某卡 batch 全为背景无 orangeCE loss 输入 logits 全零。解决在train.py中trainloader初始化时加drop_lastTrue改用DistributedSampler替代RandomSampler# 替换原 sampler train_sampler torch.utils.data.distributed.DistributedSampler(dataset) train_loader torch.utils.data.DataLoader(..., samplertrain_sampler)5. 部署前必做的三件事让橙子检测从“能跑”变成“敢上线”训练完best.pt不代表结束——农业场景的苛刻条件强光、雾气、枝叶抖动、设备发热会让模型在实验室 OK现场直接失效。这三件事不做交付就是埋雷。5.1 用真实场景视频做 stress test不是单图别只用test/images/下的静态图验证。拿一段 5 分钟果园监控视频MP4H.264 编码用以下脚本抽帧批量推理统计帧级稳定性# stress_test.py import cv2 import torch from models.experimental import attempt_load from utils.general import non_max_suppression, scale_coords from utils.plots import plot_one_box model attempt_load(runs/train/exp/weights/best.pt, map_locationcuda:0) model.half() # 半精度加速 cap cv2.VideoCapture(orchard_daylight.mp4) frame_count 0 success_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 # 预处理resize 到模型输入尺寸如1280 img cv2.resize(frame, (1280, 1280)) img torch.from_numpy(img).to(cuda:0).half().permute(2,0,1).unsqueeze(0) img / 255.0 pred model(img)[0] pred non_max_suppression(pred, conf_thres0.3, iou_thres0.45) # 统计本帧是否检出至少1个橙子 if len(pred[0]) 0: success_count 1 # 可选保存带框帧用于回溯 # plot_one_box(...) cap.release() print(fStress Test: {success_count}/{frame_count} frames detected orange) # ✅ 合格线≥95%连续5分钟无漏检关键点必须用cv2.VideoCapture抽帧模拟真实摄像头流conf_thres0.3比训练默认更低容忍弱光下的低置信度统计“检出率”而非 mAP——产线只关心“有没有”不关心框多准。5.2 构建光照鲁棒性验证集三类必测场景在test/目录下新建三个子目录各放 50 张图场景类型特征获取方式检测要求正午强光镜面反光、橙皮高光过曝、阴影锐利10:00–14:00 拍摄白平衡设为“日光”框不能漂移高光区果实不丢失晨雾/薄雾整体对比度低、边缘模糊、色温偏蓝晨间果园实拍或用 OpenCVcv2.GaussianBlurcv2.addWeighted模拟Recall ≥0.85不能因模糊拒检枝叶遮挡30% 面积被绿叶覆盖、果实半隐人工摆拍确保遮挡物为真实叶片非PS框必须包住可见部分不扩大到叶子训练后在这三组上分别跑val.py记录mAP0.5。若任一组 0.7说明模型泛化不足需用albumentations加针对性增强# 在 datasets.py 中 augment_hsv() 后加 import albumentations as A transform A.Compose([ A.RandomSunFlare(src_radius100, p0.3), # 模拟强光 A.RandomFog(p0.2), # 模拟晨雾 A.RandomShadow(p0.25), # 模拟枝叶投影 ])5.3 生成轻量级推理 APIFlask ONNX非 PyTorch产线设备如工控机、Jetson无法装 PyTorch必须提供 ONNX C/Python 推理接口。用以下最小 Flask 服务封装# api_server.py from flask import Flask, request, jsonify import numpy as np import onnxruntime as ort import cv2 app Flask(__name__) session ort.InferenceSession(best.onnx) app.route(/detect, methods[POST]) def detect(): file request.files[image] img cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) img cv2.resize(img, (1280, 1280)) img img.astype(np.float32) / 255.0 img np.transpose(img, (2,0,1))[None] # (1,3,1280,1280) outputs session.run(None, {images: img}) # outputs[0] shape: (1, 25200, 6) - xyxy conf cls boxes outputs[0][0] result [] for box in boxes: if box[4] 0.3: # conf result.append({ bbox: [int(box[0]), int(box[1]), int(box[2]), int(box[3])], confidence: float(box[4]), class: orange }) return jsonify({detections: result}) if __name__ __main__: app.run(host0.0.0.0, port5000)部署要点ONNX 模型导出命令python export.py --weights best.pt --include onnx --img 1280 --device cuda:0requirements.txt只留flask2.3.3 onnxruntime-gpu1.16.0 opencv-python4.8.0用gunicorn启动gunicorn -w 4 -b 0.0.0.0:5000 api_server:app防单点故障。最后说句实在的我第一次用这个橙子数据集时也以为解压就能训。结果在果园现场调试了三天才发现是晨雾场景下模型把反光当果实框出来。后来养成了一个死习惯——每次更新权重必拿手机拍10秒实时视频跑 stress test不通过绝不提交。农业AI没有“差不多”差一帧分拣机就多扔一筐橙子。希望帮到你。本文还有配套的精品资源点击获取