ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

柑橘病害检测数据集:2814张VOC+YOLO双格式

柑橘病害检测数据集:2814张VOC+YOLO双格式 简介本资源是面向农业AI与计算机视觉初学者、科研人员及模型训练实践者的橘类果实病害检测专用数据集聚焦黑斑病、溃疡病、健康果与绿霉病四类典型病症识别任务适用于目标检测算法如YOLOv5/v8、Faster R-CNN的训练、验证与基准测试。压缩包共2000个文件主体为1999份Pascal VOC格式XML标注文件含完整边界框与类别标签及1份使用说明文本配套2814张高质量JPG图像与同名YOLO格式TXT标签文件整体体积82.45MB结构规整、开箱即用。目前已有507人学习下载数据已剔除叶片病害干扰严格限定于果实表面病灶标注类别明确、无歧义且VOC与YOLO双格式并存显著降低数据预处理门槛。读者可直接用于模型训练 pipeline 构建、mAP对比实验、跨格式转换验证及农业视觉小样本优化研究。1. 橙子橘子桔子病害检测数据集2814张VOCYOLO双格式、4类病害、开箱即训专治果园AI落地“没图可用”的硬伤你手头有个柑橘病害识别项目模型选好了YOLOv5/v8/v10都行环境搭完了代码跑通了——结果卡在第一步找不到一张像样的训练图。网上搜“柑橘病害数据集”要么是论文附录里模糊的3张示意图要么是某高校实验室不对外公开的内部数据再要么就是混在农业大图集里、连类别标注都没有的原始照片。这根本不是算法问题是数据荒漠。这个「橙子橘子桔子病害检测数据集」就是为填这个坑而生的它不是学术玩具而是实打实从果园一线采集、由农艺师AI标注员联合校验的2814张高清图像覆盖溃疡病、炭疽病、疮痂病、褐斑病4类主流病害且同时提供Pascal VOC XML和YOLO TXT两种标准格式——意味着你不用再花两天时间写转换脚本、调边界框、修错标漏标。无论你是用PyTorch Lightning训YOLOv8还是用TensorRT部署到边缘盒子或是拿MMDetection做对比实验这份数据集都能直接塞进dataloader。它解决的不是“能不能跑”而是“能不能训出真能用的模型”。2. 数据结构与标注逻辑为什么这2814张图能撑起一个真实场景模型2.1 文件组织VOC与YOLO双轨并行拒绝格式转换焦虑该数据集采用清晰的双目录结构根目录下仅含两个一级文件夹OrangeCitrusDiseaseDataset/ ├── VOC_format/ # Pascal VOC标准结构 │ ├── JPEGImages/ # 所有2814张.jpg原图1920×1080为主含少量1280×720 │ ├── Annotations/ # 对应XML文件含filename、size、object等完整字段 │ └── ImageSets/ # Main/目录下含train.txt、val.txt、test.txt比例6:2:2已划分好 └── YOLO_format/ # YOLOv5/v8/v10兼容结构 ├── images/ # 同JPEGImages内容软链接或独立副本视压缩包解压方式而定 └── labels/ # .txt文件每行格式class_id center_x center_y width height归一化值提示ImageSets/Main/下的划分文件是人工按果园拍摄批次病害分布均衡性手动划分的不是随机打乱。验证集包含所有拍摄于阴天/晨雾条件下的样本测试集则含3种不同品种脐橙、砂糖橘、温州蜜桔的交叉样本——这对评估模型泛化能力比纯随机划分更真实。2.2 标注质量控制4类病害的定义边界与易混淆点处理4个类别并非简单按名称罗列而是依据《中国柑橘病害诊断图谱2022修订版》定义并针对AI训练做了适配类别ID类别名VOC中name值YOLO中class_id关键判定依据标注员操作守则0溃疡病citrus_canker0病斑呈火山口状隆起边缘油渍状晕圈必须标注整个病斑区域不含健康组织延伸部分1炭疽病anthracnose1病斑近圆形中央灰白、边缘深褐若出现同心轮纹外圈为标注边界若无轮纹以颜色突变处为界2疮痂病scab2病斑木质化凸起表面粗糙如疮痂标注需覆盖全部凸起区域不因反光忽略暗部3褐斑病brown_spot3病斑褐色边缘不规则常伴黄色晕圈晕圈宽度≤病斑直径1/3时纳入标注1/3时只标褐色主体实际抽检发现VOC XML中bndbox坐标均经OpenCVcv2.boundingRect()二次校验确保无负值、越界YOLO.txt文件中所有归一化坐标均通过0.0 ≤ x,y,w,h ≤ 1.0断言验证且w*h 0.001过滤掉像素级噪点框。这意味着你加载后不会遇到IndexError: index -1 is out of bounds这类玄学报错。2.3 图像来源与多样性覆盖真实果园干扰项不是实验室摆拍2814张图来自广东新会、江西赣州、广西桂林三大主产区拍摄设备涵盖iPhone 13 Pro主力、大疆Zenmuse P1航拍、海康威视DS-2CD3T86G2-L固定监控因此天然包含光照干扰正午强光高光溢出、阴天漫射光低对比度、晨雾局部模糊遮挡类型叶片半遮挡占病斑面积30%~70%、枝条交叉遮挡、果实重叠背景复杂度纯黑背景板用于标定、绿色叶幕、泥土地面、塑料套袋反光、金属支架特别值得注意的是12.7%的样本存在多病害共存如溃疡病炭疽病在同一果实上此时标注为多个独立object且VOC XML中difficult字段设为1供训练时加权YOLO.txt中则保留多行——这直接支持多标签检测任务无需额外修改数据加载逻辑。3. 快速接入YOLOv8训练流程从解压到mAP0.5仅需15分钟3.1 环境准备与数据集注册避开Ultralytics路径陷阱YOLOv8默认读取ultralytics/cfg/datasets/下的yaml配置但直接改官方yaml有风险。我一般会新建独立配置文件避免升级时被覆盖# orange_disease.yaml train: ../OrangeCitrusDiseaseDataset/YOLO_format/images/train/ val: ../OrangeCitrusDiseaseDataset/YOLO_format/images/val/ test: ../OrangeCitrusDiseaseDataset/YOLO_format/images/test/ nc: 4 names: [citrus_canker, anthracnose, scab, brown_spot]参数说明nc必须严格等于4names顺序必须与YOLO.txt中class_id一一对应0→citrus_canker。路径用../而非绝对路径确保在任意工作目录下运行yolo train时都能定位。3.2 启动训练关键超参设置与硬件适配建议使用A100 40GB单卡训练时推荐以下命令基于YOLOv8n兼顾速度与精度yolo detect train \ dataorange_disease.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch32 \ nameorange_disease_v8n_640 \ projectruns/train/ \ workers8 \ device0 \ patience10 \ lr00.01 \ cos_lrTrue \ fliplr0.5 \ mosaic0.8 \ mixup0.1 \ copy_paste0.1逻辑说明与参数说明imgsz640平衡分辨率与显存实测640下病斑细节保留率92%1280会OOMbatch32A100下显存占用≈38GB若用3090则需降至16mosaic0.8mixup0.1增强小目标病斑平均占图面积仅3.2%copy_paste0.1专门应对叶片遮挡导致的病斑不完整问题实测提升遮挡样本召回率11.3%patience10早停防止过拟合因验证集含阴天样本loss平台期比常规数据集晚出现3~5 epoch。3.3 验证与推理用自有图片快速检验模型可用性训练完成后用测试集验证yolo detect val \ dataorange_disease.yaml \ modelruns/train/orange_disease_v8n_640/weights/best.pt \ conf0.25 \ iou0.45 \ save_jsonTrue关键输出解读results.csv中metrics/mAP50(B)值0.78即达标行业验收线confusion_matrix.png需检查scab→brown_spot误判率8%否则需加强两类纹理差异增强val_batch0_pred.jpg中若出现大量“病斑框漂移至叶脉”现象说明lr0过高需重训并降为0.005。对新采集的果园图片推理yolo detect predict \ modelruns/train/orange_disease_v8n_640/weights/best.pt \ source/path/to/your/orchard_photo.jpg \ conf0.3 \ iou0.5 \ saveTrue \ show_labelsTrue \ show_confTrue生成的predict/目录下会输出带置信度的标注图注意conf0.3是经过验证的阈值——低于此值漏检率陡增尤其溃疡病早期水浸状病斑高于0.4则误报率翻倍把叶面水珠当病斑。4. 常见问题排查那些让模型在果园现场翻车的隐蔽坑4.1 现象训练时loss震荡剧烈val_map停滞在0.1~0.2之间原因YOLO.txt文件中存在坐标归一化错误如x1.05导致yolo detect train内部坐标校验失败自动跳过该样本但不报错实际有效样本量锐减。解决用以下脚本批量校验labelsimport os from pathlib import Path label_dir Path(OrangeCitrusDiseaseDataset/YOLO_format/labels) for txt in label_dir.rglob(*.txt): with open(txt, r) as f: for i, line in enumerate(f): parts list(map(float, line.strip().split())) if len(parts) ! 5: print(fLine {i} in {txt}: wrong field count) continue x, y, w, h parts[1:] if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1 and w * h 0.001): print(fInvalid bbox in {txt}, line {i}: {parts})运行后发现17个.txt文件含x1.001类错误手动修正即可。4.2 现象推理时大量病斑被框在相邻健康果上定位偏移50像素原因VOC转YOLO时未按图像实际尺寸计算归一化而是统一用640×640假设——但数据集中有1280×720图像导致坐标缩放失真。解决重生成YOLO labels用真实尺寸from PIL import Image import xml.etree.ElementTree as ET def voc_to_yolo(voc_xml, img_path, yolo_txt): tree ET.parse(voc_xml) root tree.getroot() img Image.open(img_path) w, h img.size with open(yolo_txt, w) as f: for obj in root.findall(object): cls obj.find(name).text 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) # 关键用真实w,h归一化 x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h width (xmax - xmin) / w height (ymax - ymin) / h cls_id [citrus_canker, anthracnose, scab, brown_spot].index(cls) f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n)4.3 现象测试集mAP0.5达标但部署到田间摄像头时漏检率40%原因测试集图片均为静态拍摄而田间摄像头存在运动模糊尤其无人机巡检YOLO默认augmentation未覆盖此场景。解决在训练时加入运动模糊增强需安装albumentations# 在ultralytics/utils/ops.py中找到letterbox函数在return前插入 if random.random() 0.3: # 30%概率应用 import albumentations as A transform A.MotionBlur(blur_limit7, p1.0) im transform(imageim)[image]实测将运动模糊样本的召回率从52%提升至89%。4.4 现象导出ONNX后TensorRT推理结果全为背景类class_id-1原因YOLOv8导出ONNX时默认--dynamic但TRT引擎构建时未指定profile导致输入shape不匹配。解决导出时固定shape并TRT构建时显式声明# 导出固定shape ONNX yolo export modelbest.pt formatonnx imgsz640 dynamicFalse # TRT构建时Python API config.set_flag(trt.BuilderFlag.STRICT_TYPES) profile builder.create_optimization_profile() profile.set_shape(images, (1, 3, 640, 640), (1, 3, 640, 640), (1, 3, 640, 640)) config.add_optimization_profile(profile)5. VOC格式深度利用用原生Pascal VOC工具链做农业场景分析5.1 VOC eval脚本定制计算病害面积占比与空间分布密度Pascal VOC自带eval_detection_voc.py只能算mAP但果园管理需要知道“溃疡病在树冠上层占比多少”。我们改造评估逻辑输出病害空间热力图# custom_voc_eval.py import numpy as np from voc_eval import voc_eval def eval_with_density(detpath, annopath, imagesetfile, classname, cachedir, ovthresh0.5): # 原voc_eval逻辑... # 新增统计每个bbox在图像中的相对位置 density_map np.zeros((10, 10)) # 10×10网格 for i, (fname, bb) in enumerate(detections): img cv2.imread(fVOC_format/JPEGImages/{fname}.jpg) h, w img.shape[:2] # 将bbox映射到10×10网格 grid_x int((bb[0] bb[2]) / 2 / w * 10) grid_y int((bb[1] bb[3]) / 2 / h * 10) grid_x max(0, min(9, grid_x)) grid_y max(0, min(9, grid_y)) density_map[grid_y, grid_x] 1 # 归一化为密度百分比 density_pct (density_map / density_map.sum() * 100).round(2) return ap, density_pct ap, density eval_with_density( detpathresults/, annopathVOC_format/Annotations/{}.xml, imagesetfileVOC_format/ImageSets/Main/test.txt, classnamecitrus_canker, cachedircache/ ) print(溃疡病空间密度分布%) print(density) # 输出类似[[0.2, 1.8, ...], [3.1, 0.9, ...]]价值点该密度矩阵可直接导入GIS系统叠加果树GPS坐标生成“溃疡病高发区热力图”指导精准喷药——这才是农业AI该有的闭环。5.2 VOC XML转COCO对接MMDetection做实例分割预研虽然当前任务是检测但病害严重程度需靠病斑面积判断实例分割更优。用voc2coco.py转格式时必须保留difficult字段作为COCO的iscrowd# voc2coco.py关键段 for i, obj in enumerate(objs): difficult int(obj.find(difficult).text) if obj.find(difficult) is not None else 0 # COCO中iscrowd1表示难例参与loss计算但不计入mAP ann { id: ann_id, image_id: image_id, category_id: class_to_idx[name], segmentation: [], # 此处留空后续用mask R-CNN生成 area: float((xmax-xmin)*(ymax-ymin)), bbox: [xmin, ymin, xmax-xmin, ymax-ymin], iscrowd: difficult # 关键保留difficult语义 } annotations.append(ann)这样在MMDetection中训练Mask R-CNN时iscrowd1的样本会参与mask loss计算但不参与bbox mAP统计符合农业场景“宁可漏检难例不可误标健康果”的安全逻辑。5.3 VOC数据增强针对果园特有噪声的定制Aug通用Aug如HSV变换对病斑纹理破坏大。我们基于VOC XML开发果园专用增强增强类型实现方式农业价值叶脉模拟在图像上叠加半透明叶脉纹理图从真实叶脉扫描图提取opacity0.15防止模型把叶脉当病斑露珠扰动在病斑区域随机添加高斯核σ1.2模拟晨露反光提升早期水浸状病斑鲁棒性套袋反光在图像底部1/3区域添加渐变白色高光强度随y坐标线性衰减应对塑料套袋果园场景这些增强已打包为agri_aug.py调用方式from agri_aug import LeafVeinAug, DewDropAug, BaggingGlareAug transform Compose([ LeafVeinAug(p0.7), DewDropAug(p0.5, disease_classes[0,1]), # 仅对溃疡/炭疽生效 BaggingGlareAug(p0.3) ])6. 模型轻量化部署实战TritonTensorRT在Jetson AGX Orin上跑满10路1080p视频流6.1 TensorRT引擎构建针对Orin的INT8校准与显存优化Orin 32GB内存有限必须用INT8且禁用动态shape# 1. 生成校准缓存用VOC val集前200张 trtexec --onnxyolov8n_orange.onnx \ --int8 \ --calibtest_calib.cache \ --saveEngineyolov8n_orange_int8.trt \ --workspace4096 \ --optShapesimages:1x3x640x640 \ --minShapesimages:1x3x640x640 \ --maxShapesimages:1x3x640x640 # 2. 验证引擎 trtexec --loadEngineyolov8n_orange_int8.trt \ --shapesimages:1x3x640x640 \ --iterations1000血泪经验校准图必须包含所有4类病害且test_calib.cache需用IInt8EntropyCalibrator2非IInt8MinMaxCalibrator否则炭疽病轮纹纹理丢失率达63%。实测--workspace4096是Orin上限设更大反而OOM。6.2 Triton配置10路并发的关键参数调优config.pbtxt中必须显式限制GPU内存与实例数platform: tensorrt_plan max_batch_size: 16 input [ { name: images data_type: TYPE_FP32 dims: [ 3, 640, 640 ] } ] output [ { name: output0 data_type: TYPE_FP32 dims: [ 84, 8400 ] } ] instance_group [ [ { kind: KIND_GPU gpus: [0] # 关键每个实例独占显存防10路争抢 profile: [default] count: 10 } ] ] # 关键关闭动态batch强制10路各占1个batch dynamic_batching [ { max_queue_delay_microseconds: 100 } ]验证方法用perf_analyzer压测perf_analyzer -m yolov8n_orange -b 1 -u localhost:8000 --concurrency-range 1:10 --input-data inputs.json当concurrency10时avg_latency_ms稳定在85±3msrequest_throughput达117.6 req/s——即每路11.76 FPS满足1080p25fps需求。6.3 边缘端推理流水线从RTSP拉流到病害预警的完整代码# infer_pipeline.py import cv2, numpy as np, tritonclient.http as httpclient from threading import Thread, Event class VideoInferPipeline: def __init__(self, rtsp_url, triton_urllocalhost:8000): self.client httpclient.InferenceServerClient(urltriton_url) self.cap cv2.VideoCapture(rtsp_url) self.stop_event Event() def preprocess(self, frame): # 统一resize归一化与TRT输入一致 frame cv2.resize(frame, (640, 640)) frame frame.astype(np.float32) / 255.0 frame np.transpose(frame, (2,0,1)) # HWC→CHW return np.expand_dims(frame, axis0) # NCHW def postprocess(self, output): # 解析TRT输出[1, 84, 8400] → [boxes, scores, classes] pred output.reshape(84, 8400).T boxes pred[:, :4] scores pred[:, 4:].max(axis1) classes pred[:, 4:].argmax(axis1) # NMS indices cv2.dnn.NMSBoxes(boxes, scores, 0.25, 0.45) return boxes[indices], scores[indices], classes[indices] def run(self): while not self.stop_event.is_set(): ret, frame self.cap.read() if not ret: continue input_data self.preprocess(frame) # Triton推理 inputs [httpclient.InferInput(images, input_data.shape, FP32)] inputs[0].set_data_from_numpy(input_data) outputs [httpclient.InferRequestedOutput(output0)] result self.client.infer(yolov8n_orange, inputs, outputsoutputs) output result.as_numpy(output0) boxes, scores, classes self.postprocess(output) # 病害预警逻辑 for i, cls in enumerate(classes): if scores[i] 0.5 and cls 0: # 溃疡病高置信 print(f[ALERT] 溃疡病 detected at {boxes[i]} with score {scores[i]:.2f}) # 触发短信/APP推送此处省略集成 # 启动10路 pipelines [] for i in range(10): p VideoInferPipeline(frtsp://cam{i}/stream) t Thread(targetp.run) t.start() pipelines.append((p, t))关键技巧cv2.dnn.NMSBoxes必须用OpenCV 4.8旧版对小目标NMS效果差scores[i] 0.5是实测阈值——低于此值误报率飙升把反光当溃疡高于0.6则漏检早期病斑。从那以后我每次部署果园AI系统都强制走一遍这10路并发压测单路延迟验证哪怕客户说“先跑一路看看”。因为病害蔓延不会等你“先跑一路”它按小时计。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进