
简介数据集面向农产品霉变识别与视觉检测场景涵盖 7654 张真实拍摄的橙子/橘子图像以 COCO JSON 格式标注正常与发霉两类目标适合目标检测、图像分类、模型评估等深度学习任务也适合食品安全分拣与仓储质检场景。资源包共 2000 个文件含 1997 张 JPG 原始图像与 3 个 JSON 标注文件整体大小 361.32MB解压后可直接用于 MMDetection、YOLO 等主流检测框架的数据加载。目前已有 130 人学习浏览。获取该数据集可免去自行采集与标注的繁琐工作直接使用多样化的霉变样本和边界框标注开展训练与验证同时便于构造类别不均衡分析、数据增强、迁移学习等实验也能够为柑橘类品质检测与自动分拣系统的落地提供可靠的数据支撑。1. 发霉橙子数据集农产品外观质检的第一份喂给模型的素材水果分拣线上最怕的不是全烂的果子而是半坏不坏的那一类腐烂面积小、颜色变化不典型老手都要拿起来转一圈才敢判断。这个发霉橙子/橘子数据集解决的就是这个场景——7654张图片每张都带COCO JSON格式标注类别明确分成正常橙子和发霉橙子两张覆盖不同拍摄角度、光线和腐烂程度。对做农产品质检、分拣机器人视觉、货架巡检的人来说它可以直接作为目标检测模型的训练底料省掉从零采集和标注的周期。适合谁两类人一是想快速验证YOLO系列模型在腐烂检测这类细粒度外观任务上效果的同学二是做小规模工业质检demo、需要一份现成标注数据跑通流程的工程师。我拆这份资源时最关心的不是图片好不好看而是COCO JSON能不能干净利落地转成YOLO能吃的格式、标注边界踩不踩坑下面把整个过程和参数细节摊开讲。2. 拆解7654张COCO JSON标注先搞清楚里面装的是什么2.1 COCO JSON的核心结构images、annotations、categories三件套COCO格式的标注文件是一个JSON对象顶层固定有images、annotations、categories三个数组。images记录每张图的文件名、宽高和IDannotations记录每个标注框的坐标和类别归属categories做类别名到数字ID的映射。读这份数据集之前我建议先用脚本把三个数组的规模和各字段类型打印出来避免后面转格式时临时抓瞎。import json import collections with open(annotations/instances_train.json, r, encodingutf-8) as f: data json.load(f) print(images 数量:, len(data[images])) print(annotations 数量:, len(data[annotations])) print(categories:, data[categories]) # 统计每个类别的标注框数量 cat_count collections.Counter() for ann in data[annotations]: cat_count[ann[category_id]] 1 print(各类别标注数量:, dict(cat_count)) # 看一眼单条 annotation 的字段 print(单条 annotation 示例:, data[annotations][0])这段代码解决两个问题一是确认这份COCO JSON是检测格式还是实例分割格式关键看annotations里有没有segmentation字段、area是不是完整多边形面积二是统计类别平衡度发霉样本太少就要考虑数据增强策略。单条annotation示例打印出来后重点看bbox的格式——COCO里是[x, y, width, height]左上角坐标加宽高单位是像素不是归一化值。2.2 文件名里的隐藏信息Roboflow导出痕迹与训练/验证划分这批图片的命名很有规律比如p34_JPG_jpg.rf.112051185ff4b2ae1ea96f20943340dd.jpg和IMG_8791_JPG_jpg.rf.400569fc43b8ad5df32d1f68595d4b37.jpg。中间的.rf.是Roboflow平台导出时留下的标记后面跟一串哈希值。这说明数据集经过自动预处理可能做过自动定向、图像缩放或简单增强实际原图尺寸和你看到的不一定一致。从文件名还能推断原始素材一部分是编号命名的棚拍p34、p43一部分是手机实拍IMG_xxxx混合来源意味着光照条件杂这反而是好事——模型泛化能力通常比单一场景采集的更好。如果你下载后发现整个数据集只给了一份annotations文件没有train/valid/test三个子目录需要自己按比例划分。常见做法是把images数组按8:1:1切分再用对应的image_id过滤annotations。我一般会用固定随机种子切分保证每次复现实验结果一致。import random random.seed(42) image_ids [img[id] for img in data[images]] random.shuffle(image_ids) train_ids set(image_ids[:int(len(image_ids)*0.8)]) val_ids set(image_ids[int(len(image_ids)*0.8):int(len(image_ids)*0.9)]) test_ids set(image_ids[int(len(image_ids)*0.9):]) print(ftrain: {len(train_ids)}, valid: {len(val_ids)}, test: {len(test_ids)})2.3 这份数据能训什么、不能训什么边界要说清楚严格来说COCO JSON有两种常见形态检测标注只含bbox和category_id实例分割标注还包含segmentation多边形坐标。这个数据集标题强调可识别正常橙子和发霉的橙子能力边界是目标检测——定位每个橙子的位置并分类。如果你的目标是做像素级分割、算腐烂面积占比需要确认segmentation字段是否完整不完整就得自己补标注别拿到手默认啥都能干。另外类别只有两类场景局限在橙子上。直接拿去检测橘子问题不大因为橙子和橘子在形态上接近但换成苹果、芒果就需要重新标注或微调。我建议把这份数据当成腐烂水果检测的baseline先跑通流程、验证模型再往自己的品类扩展。3. 把COCO JSON转成YOLO格式转换脚本与四个细节3.1 为什么必须转格式YOLO不吃JSON只吃txtYOLO系列训练时每张图片对应一个同名txt文件每行格式是class_id x_center y_center width height全部归一化到0~1之间。COCO JSON是集中式标注YOLO是文件级标注所以拿到数据集后的第一件事永远是转换。转换本身不难但坐标系、类别映射、文件路径对齐这三个细节错了训练就会静默出错——不是报错是loss不降或mAP为0。3.2 转换脚本从JSON到txt的完整实现import json import os def coco_to_yolo(json_path, output_dir, categories_map, splittrain): with open(json_path, r, encodingutf-8) as f: data json.load(f) # 建立 image_id - 文件名 的映射 img_id_to_name {} for img in data[images]: img_id_to_name[img[id]] img[file_name] # 建立 image_id - [annotations] 的映射 img_id_to_anns {} for ann in data[annotations]: img_id_to_anns.setdefault(ann[image_id], []).append(ann) os.makedirs(output_dir, exist_okTrue) for img_id, file_name in img_id_to_name.items(): # 将 .jpg 替换为 .txt 作为标签文件名 txt_path os.path.join(output_dir, file_name.replace(.jpg, .txt)) anns img_id_to_anns.get(img_id, []) with open(txt_path, w) as f: for ann in anns: category_id ann[category_id] # COCO bbox: [x, y, width, height]左上角坐标 x, y, w, h ann[bbox] # 图片宽高从 images 字段读取 img_w, img_h None, None for img in data[images]: if img[id] img_id: img_w, img_h img[width], img[height] break # 归一化并转成 YOLO 格式中心点坐标 宽高 x_center (x w / 2.0) / img_w y_center (y h / 2.0) / img_h w_norm w / img_w h_norm h / img_h f.write(f{categories_map[category_id]} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}\n) print(f{split} 转换完成共处理 {len(img_id_to_name)} 张图片)几个参数要解释清楚。categories_map是类别映射字典比如{1: 0, 2: 1}含义是把COCO里的category_id 1假设是正常橙子映射成YOLO的class 0category_id 2发霉橙子映射成class 1。bbox的坐标原点是图片左上角转YOLO时先算中心点再除以宽高顺序不能搞反。还有一点image_id在COCO里是整数文件名是字符串拿img[file_name]去找txt路径时确保和图片下载目录里的实际文件名完全一致Roboflow导出的文件经常带.rf.xxx后缀别在中间手动改过名。3.3 转换后必须做的两件校验数量对账和坐标范围转换完别急着训练。第一件校验是数量对账统计输出目录里的txt文件数应该和images数组长度一致同时把所有txt的行数相加应该等于annotations数组长度。第二件校验是坐标范围正常标签每一行五个数都应在0~1之间出现大于1的值说明除错了宽高或者bbox本来就超出图像边界这种脏标注会导致训练早期loss爆炸或者收敛到错误位置。# 数量对账 find labels/train -name *.txt | wc -l # 检查归一化值越界 grep -rE (1\.[0-9]|[2-9]\.) labels/train/ | head -20第二条命令用正则找出第二个到第五个字段中大于等于1的异常行。出现异常行时回到COCO JSON检查对应bbox是否超出图片尺寸常见原因是标注工具允许框出界或者图片被Roboflow自动resize后标注信息没有同步更新。处理方式是裁剪越界部分到图像边界内而不是直接删掉整条标注。4. 训练发霉橙子检测模型四个避坑点与排查顺序4.1 坑一类别不平衡导致发霉橙子几乎不被预测现象训练完验证时正常橙子的recall接近0.9发霉橙子的recall只有0.4混淆矩阵里大量发霉橙子被预测成正常类。原因腐烂样本天然稀少采摘后完好果实的比例远高于腐烂果实标注数量可能只有正常类别的五分之一甚至更低。模型在梯度更新时被多数类主导决策边界整体偏向正常类。解决不要只改cls_loss权重。我一般先给少数类设置更高的loss权重然后对发霉类别做离线增强——复制发霉样本并施加旋转、亮度扰动、随机遮挡。YOLOv8训练参数里cls控制分类损失权重把它从默认的0.5提到0.7~1.0配合mosaic增强让发霉样本穿插进更多上下文中。改完后重新看每类recall而不是只看整体mAP。4.2 坑二发霉区域边缘模糊anchor尺寸收敛异常现象训练时训练集loss正常下降验证集loss却震荡最终在验证集上小目标全部漏检。原因发霉区域和正常果皮之间是渐变色过渡标注员在框选时习惯把整个橘子框进去导致同一类别里框尺寸方差极大从几十像素的小框到占图三分之二的大框都有。默认anchor对极大多样性的框适配差。解决训练前跑一遍yolov8自动anchor检查或者直接关掉anchor限定。YOLOv8默认开启自动anchor优化把anchor相关参数关掉后改用auto模式让模型重新聚类。我实际跑下来把imgsz统一到640后让模型自己学anchor分布比手动调参稳得多关键还是让标注边界更贴合实际腐烂区域。4.3 坑三手机原图比例杂乱直接resize导致标注错位现象训练不报错推理阶段检测框偏移明显尤其是竖构图的长图框往左上角或右下角漂。原因原始图片是手机拍摄比例从4:3到9:16都有训练框架默认直接拉伸到正方形输入标注框跟着图片一起变形。拉伸带来的非仿射畸变让模型学到错误的几何特征。解决强制走letterbox适配。训练和推理都保持rectTrue矩形推理把长边缩放到640、短边padding到32的倍数这样原图比例不丢标注坐标在归一化后不需要额外同步调整。代价是图像边缘多了一圈灰边对目标检测影响很小但对后续分类子任务有干扰——如果你打算把检测框裁出来再送分类模型记得裁切时去掉padding。4.4 坑四只看mAP漏检与误检的真实代价被平均掉现象验证集mAP0.5有0.92看起来很好上线测试时发霉果漏检率依然高被现场质疑模型没用。原因mAP把两类的AP平均了正常橙子AP高把整体拉上来。腐烂检测任务是典型的漏检代价大于误检漏掉一个坏果可能让整箱被退货而误检一个正常果只是多一次人工复核。解决把评估指标拆开看。用yolov8 val输出的results.csv读每一类的recall重点盯发霉类的recall同时做一个成本矩阵设置误检和漏检的惩罚系数按实际业务场景选模型阈值。YOLO的conf参数默认0.25发霉类recall不够就往下调0.1甚至0.05都可以试代价是误检增多。5. 训练前必须做的数据校验可视化标注与参数预设5.1 可视化标注框二十行代码确认标签没飞转换格式和划分数据集都做完后我强烈建议随机抽20~30张图把标注框和类别名画上去看一眼。这一步能发现大量自动化脚本发现不了的问题框是不是框住了腐烂区域而不是整个果实云朵、阴影、包装袋被标成了发霉橙子类别ID有没有互换import cv2 import json import random with open(annotations/instances_train.json, r, encodingutf-8) as f: data json.load(f) # 随机选图片 samples random.sample(data[images], 20) for img_info in samples: img cv2.imread(fimages/{img_info[file_name]}) h, w img.shape[:2] for ann in data[annotations]: if ann[image_id] img_info[id]: x, y, bw, bh ann[bbox] x1, y1 int(x), int(y) x2, y2 int(x bw), int(y bh) cls_id ann[category_id] color (0, 255, 0) if cls_id 1 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, str(cls_id), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(fcheck_{img_info[file_name]}, img)可视化脚本的cls_id 1是占位判断实际要对照categories里定义的ID。25M像素级别的原图直接画框后另存即可注意Roboflow导出的图片可能是分块路径读图前先确认相对路径正确。让我特别提醒如果发现标注框明显包着两个橙子多半是标注规范不一致这类标注对训练有害无益宁可删掉重标也不要留下。5.2 YOLOv8训练参数预设照着这套起步不会翻车数据集校验没问题后训练参数我建议按下面这张表起步跑通后再针对性调优。显存紧张就把batch降到8imgsz降到512但mAP通常会掉2~3个点。参数建议值说明imgsz640平衡精度与显存发霉细节不算极细小目标640够用batch168GB显存可跑更大显存直接上调到32epochs100数据集够大100轮基本收敛看验证loss早停optimizerAdamW小数据集上比SGD收敛更快调参空间大patience20验证集指标连续20轮不涨就停省钱省时间augment默认RGB三通道数据增强已够不需要额外增加裁剪策略训练命令最简形式yolo detect train dataorange.yaml modelyolov8s.pt epochs100 imgsz640 batch16orange.yaml里要写清nc: 2和类别名顺序这个顺序和你转换标签时的class_id必须一致写反了训练不会报错但评估结果会完全反掉。YOLOv8用s尺寸起步是为了快速验证如果发霉类recall上不去换m或l通常能拉回几个点代价是训练时间翻倍。5.3 数据增强的取舍别把发霉这个特征增强没了腐烂检测的本质是识别纹理和颜色的异常这决定了数据增强策略和通用物体检测不一样。旋转、水平翻转对腐烂判定是安全的——发霉区域转动一下依然是发霉。但hsv_h、hsv_s这类颜色扰动参数建议压低默认0.015和0.7对正常橙子偏暖色调影响不大但对发霉区域的青绿色、灰褐色影响显著色调漂移会让模型把暗斑和霉变之间的边界学混乱。我习惯把hsv_h降到0.005hsv_s降到0.3增加translate到0.1模拟不同位置摆放这样模型关注的是局部纹理异常而不是依赖某一固定色调。6. 验证阶段的实用技巧从PR曲线到误报归因的半小时检查法验证阶段指的是训练结束后、正式评估模型性能的阶段。这时候模型文件已经产出runs/detect/train目录下会有PR_curve.png、confusion_matrix.png和results.csv。我每次都会先看PR曲线而不是直接看mAP数字PR曲线下面积就是AP但曲线形状能告诉我更多——如果曲线在recall接近1时precision急剧掉到0.5以下说明模型靠宁多勿漏的策略取高分误报率实际很高得把conf阈值往上调。误报归因我有个固定动作用验证集跑一次推理把所有预测结果和真实标签做对比找出那些预测为发霉但标签是正常的样本单独存到一个文件夹里逐一过目。重复三到五次就会总结出规律日光直射造成的橙皮局部反光常被误判为霉斑阴影区域的暗角也容易触发报警。发现这类系统性误差后回训练集把这类困难负样本复制几份加进去或在预处理里加一个高光抑制步骤都能有效改善。验证命令和结果文件读取yolo detect val modelruns/detect/train/weights/best.pt dataorange.yamlval输出的results.csv是按类别和整体分别统计的precision、recall和mAP我通常只看两类指标发霉类的recall和所有类的混淆情况。发霉类recall如果低于0.7先检查第4章里说的增强权重问题而不是直接换大模型。翻车过几次之后我的习惯就固定下来了下载任何数据集、转换任何格式、训练任何检测模型都强制先跑一遍可视化校验加数量对账再动训练命令。这套流程不保证模型一定刷到高分但保证你看到的分数是真实可信的不会在调试半天后发现是标签错位或坐标系搞反。希望这些拆解能帮你在自己的黄橙橙数据集上少走几步弯路拿着这份标注直接开工。本文还有配套的精品资源点击获取