简介这份苹果瑕疵检测数据集面向计算机视觉初学者与农业智能分拣系统开发者提供了393张拍摄于实际生产环境的苹果图像覆盖碰伤、腐烂、裂纹、颜色异常等常见瑕疵类型。全部图像由labelImg完成标注并转换为VOC格式的XML文件边界框准确定位缺陷位置可直接接入TensorFlow、PyTorch等主流目标检测框架进行模型训练。压缩包共786个文件内含393个jpg图片与393个xml标注文件整体大小约30.26MB训练集与验证集可自行切分便于不同实验场景下的使用。已有213人学习下载。借助该数据集不仅可完成苹果好坏自动挑选还可基于大小、甜度、颜色等特征扩展分拣功能并进一步应用于品质评估与成熟度预测为智慧农业提供数据支撑。1. 苹果瑕疵检测数据集拿到 zip 不代表拿到模型苹果瑕疵检测数据集最常见的交付形态就是一个 zip 压缩包里面装着原图、标注文件和说明文档下载解压后你以为万事俱备实际离能训练还有一段路。这类数据集面向的是果品分选线上的具体场景——果锈、磕伤、腐烂、虫眼目标小、类别不平衡、同一颗果实多视角重复出现直接套通用检测流程很容易翻车。这篇笔记写给要用这个数据集跑 YOLOv5/YOLOv8 训练流程的工程师和做毕业设计的学生按“拆包、验包、转标注、划数据、训练、验证”的顺序把每条命令和每个容易踩的坑摊开讲清楚。2. 先弄清 zip 里的家底目录结构、命名规则与多模态文件2.1 数据集 zip 里最常见的目录布局images、annotations、splits 三件套制作方打包时习惯模仿 VOC 或 COCO 的目录布局解压后的骨架一般是 images、annotations、splits 三个目录并列。images 放原图annotations 放每张图对应的标注splits 放训练、验证、测试的划分清单。多数包还会带一个 README里面写类别定义、标注规则和数据集来源。先读 README 再动数据是省时间的第一步。apple_defect/ ├── images/ │ ├── train/ │ │ ├── apple_001.jpg │ │ └── apple_002.jpg │ └── val/ ├── annotations/ │ ├── train/ │ │ ├── apple_001.xml │ │ └── apple_002.xml │ └── val/ ├── splits/ │ ├── train.txt │ ├── val.txt │ └── test.txt └── README.md这套结构里最容易被忽略的是文件命名。同一颗苹果往往会从不同角度拍多张图命名常见为apple_001_01.jpg、apple_001_02.jpg这样前半段是果实 ID后半段是视角编号。后面做数据划分时必须按果实 ID 分组否则同一颗苹果的不同视角会同时出现在训练集和验证集里模型等于提前见过答案。另外还有多模态变体部分采集设备会同时输出深度图或红外图文件名带_depth、_infrared后缀。异常检测类数据集里也有只给灰度图的变体。先确认手里这份 zip 是纯 RGB 还是多模态文件混装再决定训练输入要几张图。2.2 标注文件先认格式VOC xml、COCO json 还是 YOLO txt二标注格式直接决定了你能不能把它喂给现成训练脚本。三种主流的标注格式差别很大拿到后要先识别再转换不能上来就套某个代码。格式字段坐标单位载体VOC xmlxmin ymin xmax ymax像素绝对坐标每张图一个 xmlCOCO jsonbbox: [x, y, w, h]像素绝对坐标整个数据集一个 jsonYOLO txtclass cx cy w h归一化 0~1每张图一个 txt识别方法不用猜打开 annotations 目录看一眼一堆小 xml 就是 VOC一个体积很大的 json 就是 COCO每张图对应一个 txt 就是 YOLO 格式。有些为了兼容多个框架会同时给出 xml 和 json。这里面最关键的是坐标体系。VOC 和 COCO 给的是像素级绝对坐标YOLO 给的是归一化相对坐标。转格式时最容易翻车的动作是把 xml 里的xmin直接除以图片宽度就当作归一化中心点忘了 YOLO 要的是目标框中心点坐标和宽高而不是边界坐标。后面写转换脚本时会把这些公式展开。2.3 解压前先用 unzip -l 看清单别急着双击拿到 zip 的第一件事不是双击解压而是先看包内清单这能帮你避开一半的坑unzip -l apple_defect.zip | head -50-l参数只列出条目不解压。我会重点看三样东西顶层是不是只有一个根目录、有没有 README 或说明文件、标注和图片数量能不能对上。比如 images 下有 2000 张 jpgannotations 里却只有 1500 个 xml说明原图里有负样本没有瑕疵的正常苹果或者标注漏了一段这两种情况对应的后续处理完全不同。需要更多信息时可以用zipinfo apple_defect.zip它会把每个条目的压缩方法、CRC 校验值和权限位都打出来。压缩方法里看到deflater之外的罕见算法解压时可能报错先心里有数。看完清单再解压unzip apple_defect.zip -d apple_defect-d参数指定解压目标目录。我习惯把数据集解压到项目外的独立目录方便多条训练任务共用也避免解压产物污染代码仓库。数据集下载页通常还会给 md5 或 sha256解压前先校验一次文件不完整时后面所有环节都会出奇怪问题。3. 解压 zip 的避坑清单伪加密、损坏包与路径穿越3.1 现象一解压就要密码但数据集明明是公开的这是伪加密在网盘或论坛下载的 zip有时刚执行 unzip 就停在password:提示上作者又没在任何地方给过密码。这不是你运气差而是典型的 zip 伪加密。zip 的通用位标记general purpose bit flag第 0 位被置 1解压工具看到这个位就认为文件加密了但文件数据本身并没有真正加密。用 Python 修掉这个标志位再解压即可python3 -c from zipfile import ZipFile z ZipFile(apple_defect.zip) for i in z.infolist(): i.flag_bits 0xFFF7 # 清除第0位加密标志 z.extractall(apple_defect) flag_bits是 ZipInfo 里的 16 位标志位第 0 位是加密位掩码0xFFF7把它清零。执行前先备份原 zip万一它是真加密这样操作解出来的是乱码文件还能还原再想别的办法。经验是见到公开数据集 zip 要密码优先怀疑伪加密别急着满网找密码。3.2 现象报错 could not find EOCD / missing zip entry包是坏的unzip 时报End-of-central-directory signature not found或者 zipfile 抛could not find EOCD意思是 zip 的中央目录结束标记损坏或缺失。这个标记在包的最末尾常见原因是下载中断、网盘转存被截断、跨平台拷贝时用不兼容的压缩软件重写过。这种包没有后悔药可吃先校验完整性再决定要不要重新下载md5sum apple_defect.zip unzip -t apple_defect.zipunzip -t是 test 模式逐个解压条目并比对 CRC能快速定位哪个文件坏了。如果 md5 对不上直接回下载页重下别用恢复工具硬拼。遇到missing zip entry ...solution这类提示多半也是同一个问题——这个 zip 在传输时丢了一部分条目。3.3 现象zip 密码忘了整个组卡在解压这一步数据集在团队内部流转时常有人套一层密码防盗改结果密码忘了。我的处理顺序是先翻 README、下载页面、邮件里的说明文字很多密码就是发布日期或项目代号。确认找不到才考虑恢复工具。纯数字且位数短hashcat 的 zip 模式能较快跑出来带大小写混排加符号的基本别抱希望直接联系发布方要原始包。值得提醒的是这属于数据集预处理的前置环节跟模型训练无关在排期里要留足冗余别让它卡住后面所有事。3.4 现象解压出一堆../../路径文件跑到上级目录是路径穿越恶意或意外构造的 zip条目名里可能带../或绝对路径。普通解压工具不做过滤时文件会被写到 zip 包外的目录覆盖掉项目里的其他文件这就是 zip slip 攻击。数据集来自陌生分享时必须显式指定输出目录并检查条目名unzip apple_defect.zip -d safe_extract更稳妥的做法是写一段带过滤的解压逻辑from zipfile import ZipFile z ZipFile(apple_defect.zip) for info in z.infolist(): if info.filename.startswith(/) or .. in info.filename: print(fskip unsafe entry: {info.filename}) continue z.extract(info, safe_extract)extract的第二个参数是强制输出目录配合文件名过滤能挡住绝大多数危险条目。安全原则就一条数据集来源不明时永远不要在当前目录直接解压。3.5 现象Windows 解压后文件名乱码训练时图片找不到中文命名的目录或图片在 Windows 自带解压器、Linux unzip、macOS 归档工具之间倒腾很容易变成乱码。原因是制作方的 zip 用了 GBK 编码文件名而 Linux 的 unzip 默认按 UTF-8 猜。乱码之后最麻烦的不是文件名丑而是标注文件里的图片引用和分割清单里的路径全对不上。稳健的解法是在 7-Zip 里把编码切到 GBK 重新压缩一次或在 Linux 下用 convmv 批量纠正。后面做数据集划分和训练时所有路径引用都必须跟实际文件名完全一致这个环节的坑会在训练报image not found时集中爆发。4. 把 VOC/COCO 标注转成 YOLO 训练格式转换逻辑与四个边界坑4.1 转换前先确认原标注格式再决定脚本入口主流的苹果瑕疵标注逃不出三种格式每张图一个 VOC xml、整包一个 COCO json、每张图一个 YOLO txt。如果原包已经是 YOLO txt直接跳到第 5 章做数据划分如果是 VOC 或 COCO就需要把标注统一成 YOLO 的归一化格式。常见的瑕疵类别定义是 rust果锈、bruise磕伤、rot腐烂、wormhole虫眼但也有的数据集把“正常”也作为一类标出来。先打开 README 或任一个标注文件确认类别清单把它做成一个固定顺序的映射表后面所有脚本都用同一份映射避免两次转换产生不一致。4.2 从 VOC xml 到 YOLO txt转换脚本与参数说明import os import xml.etree.ElementTree as ET CLASS_MAP {rust: 0, bruise: 1, rot: 2, wormhole: 3} def voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() img_w float(root.find(size/width).text) img_h float(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: continue 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) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if lines: txt_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, txt_name), w) as f: f.write(\n.join(lines)) # 用法示例遍历 annotations 目录逐个转换 for xml_file in os.listdir(annotations/train): if xml_file.endswith(.xml): voc_to_yolo(os.path.join(annotations/train, xml_file), labels/train)逻辑说明先从 xml 的size节点取图片宽高再把每个object的bndbox四个像素值换算成归一化中心点和归一化宽高。这里用(xmin xmax) / 2计算中心点比xmin width / 2的写法更直观也不容易漏括号。宽高分别用xmax - xmin和ymax - ymin得到像素尺寸后再归一化。参数说明CLASS_MAP的键必须和 xml 里的name完全一致大小写敏感值从 0 开始连续编号中间不能跳号。.6f是保留 6 位小数YOLO 训练脚本读取浮点时足够多写几位不会提升精度。转换完成后务必抽查几个 txt打开一张图用标注可视化脚本画框确认框的位置没有整体偏移。4.3 边界坑一标注框越界归一化后出现负数或大于 1标注工具手滑或数据后处理失误会把框画到图片边界外得到xmin -5或xmax 960图宽只有 800这类值。除以图片宽高后会出现负坐标或大于 1 的坐标YOLO 训练时可能产生 NaN loss。解决转换脚本里对归一化结果做一次 clip强制限制到 0~1 区间x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0)但 clip 只治标。框越界通常意味着原标注质量不可靠我一般会把越界样本单独打印出来人工复查后再决定是丢弃还是修正。别把 clip 当作默认处理它会掩盖标注问题。4.4 边界坑二类别映射表顺序不一致yaml 里类别编号全错位数据集的 README 里列的是“果锈、磕伤、腐烂、虫眼”你写CLASS_MAP时却随手把 bruise 排到了 rust 前面训练时 yaml 的names顺序也跟它对齐了。表面看一切正常实际模型把编号 0 当成磕伤来学编号 1 当成果锈所有指标看起来都对得上但预测出来的class语义全反了。解决转换脚本跑完后统计一遍所有 txt 里每个类别的样本数看看分布是否符合直觉。用一条命令就能完成awk {print $1} labels/train/*.txt | sort | uniq -c如果发现 rust 类预期占比最大数量最少先检查映射表顺序再检查 xml 里的name拼写。很多 xml 里写的是Rust或rust_spot直接导致匹配不到而跳过整个目标这类问题在统计时会露出马脚。4.5 边界坑三图片带 EXIF 旋转信息标注框跟着错位手机或部分工业相机拍的照片会写 EXIF orientation 字段标注工具按旋转前的坐标标框训练时 OpenCV 读图的行为又不一致结果框和实际瑕疵位置差了 90 度模型训练时 loss 不收敛还找不到原因。解决训练前把图片用 PIL 的ImageOps.exif_transpose转正同时重新生成标注。图片转正后标注框的坐标也要按相同的旋转逻辑重算。最省事的做法是直接批处理所有图片把 EXIF 旋转烘焙进像素里让标注工具、读取脚本和训练脚本看到的图完全一致。数据集里带_rot、_raw这类后缀的文件尤其要检查 EXIF。4.6 边界坑四空标注文件和负样本不能一删了之苹果表面没有瑕疵的负样本图片在标注目录里没有对应的 xml。转换脚本用xml_file遍历天然就把这些图漏掉了。YOLO 训练时可以包含负样本图它们会被当成背景参与训练有助于降低误检率但验证集里如果塞了大量负样本mAP 的计算会失真因为 AP 是基于正样本类别算的。解决转换阶段把没有标注的图单独列出来放进一个negative目录或单独写一个neg.txt。训练时普通负样本按一定比例混入主训练集验证集里最多放 5% 的负样本用于评估误检率。这样既利用了背景信息又不会污染评估指标。5. 用 YOLOv8 训练苹果瑕疵检测数据划分与三个必调参数5.1 先按果实分组划分 train/val别按图片随机切苹果瑕疵检测数据集最大的特点是“同果多视角”一颗果实会被拍成多张图片。如果直接对图片做随机划分同一颗果实的不同视角很可能同时出现在训练集和验证集模型相当于开卷考试验证指标虚高上线后换一批新苹果立刻打回原形。正确做法是按文件名前缀提取果实 ID按果实分组后整组划分import os from sklearn.model_selection import train_test_split images [f for f in os.listdir(images) if f.endswith(.jpg)] fruit_ids [f.rsplit(_, 1)[0] for f in images] # apple_001_01.jpg - apple_001 train_idx, val_idx train_test_split( range(len(images)), test_size0.2, random_state42, stratifyNone ) with open(splits/train.txt, w) as f: for i in train_idx: f.write(fimages/{images[i]}\n) with open(splits/val.txt, w) as f: for i in val_idx: f.write(fimages/{images[i]}\n)rsplit(_, 1)从右侧切一次把视角编号去掉剩下的就是果实 ID。stratifyNone是因为单张图可能同时包含多个瑕疵类别做多标签分层划分会复杂得多这步先保证分组不串。切分后可以再统计一下 train 和 val 里的果实 ID 交集如果还有重叠说明命名规则不统一需要手工调整。5.2 写 data.yaml确认路径和类别编号path: /home/user/apple_defect train: splits/train.txt val: splits/val.txt names: 0: rust 1: bruise 2: rot 3: wormholepath是数据集根目录train和val既可以是目录也可以是 txt 列表文件这里用 txt 是因为划分已经完成训练时直接按列表读取。names的顺序必须和第 4 章CLASS_MAP完全一致这是整个流程里最容易沉默出错的地方——训练能跑但类别语义不对。5.3 训练命令与三个必调参数yolo detect train \ dataapple.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience30 \ device0 \ projectapple_defect_runs处理数据集用于 YOLOv8 训练时我一般先调三个参数。imgsz是第一个苹果瑕疵里果锈是细小分散纹理磕伤是边缘不规则的暗斑640 是性价比起点显存和速度允许就换 1280小目标的召回率会有可感知的提升代价是显存占用涨到约 4 倍。batch第二个16 是 24G 显存下yolov8s的安全值OOM 时先减半 batch不要动 imgsz动 imgsz 会把整个任务分辨率改掉。patience第三个30 轮没有更好结果就早停100 的epochs基本会跑不满实际训练在 50 到 70 轮就收敛了这是正常现象。如果是 YOLOv5 系的老项目命令等价于python train.py --img 640 --batch 16 --data apple.yaml --weights yolov5s.pt5.4 训练产物先看这几个文件训练结束后到apple_defect_runs/train/目录下优先看results.png、confusion_matrix.png、PR_curve.png和weights/里的best.pt、last.pt。results.png看 loss 曲线是否平稳下降confusion_matrix.png看哪两类瑕疵互相混淆PR_curve.png看每个类别的 PR 曲线曲线下面积小、断点靠前的类别就是短板类别。默认用best.pt做后续验证不是last.pt早停机制下两者通常差好几个点。6. 验证瑕疵检测效果用混淆矩阵和召回率定位漏检6.1 用 val 集跑预测按瑕疵类别统计召回率训练完先别急着看 mAP对瑕疵检测来说漏检的代价比误检高先按类别统计召回率更实用yolo detect predict \ modelapple_defect_runs/train/weights/best.pt \ sourceimages/val \ save_txtTrue \ save_confTrue \ conf0.25save_txtTrue会把预测结果降到runs/detect/predict/labels/再写个小脚本把预测 txt 和标注 txt 做匹配按类别统计有多少 GT 框被命中。conf参数对结果影响很大我一般会分别按 0.25 和 0.5 跑两遍看漏检率随阈值变化的趋势。如果某个瑕疵类别的召回率明显低于其他类说明它要么样本太少要么特征与其他类别太接近。6.2 把漏检样本捞出来逐张看成因模型是黑匣子但漏检的原因往往在数据里。我会把漏检的 GT 框按置信度排序挑置信度最低的 20 张图出来逐张看果锈漏检多半发生在强光反射处磕伤漏检集中在暗部阴影里腐烂因为颜色接近果皮底色而漏检的也不少。看完这 20 张再决定是补充数据、加光照增强还是单独对某个类别调阈值。这一步比盲目调 backbone 参数有效得多。我自己做果品检测项目的习惯是先跑 30 轮拿到 baseline看混淆矩阵确定主要混淆对再回头改数据而不是一开始就换模型。模型越复杂对数据质量的依赖反而越大数据侧的花费永远比调参侧更值。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
STGCN时空图卷积网络实战:PyTorch实现交通流预测全解析 简介:这是一份基于PyTorch的时空图卷积网络(STGCN)实现代码,源自IJCAI 2018论文官方工程,面向人体行为分析、动作识别等方向的研究者与开发者,解决骨骼序列数据中空间拓扑关系与时间动态规律的联合建模问题… · 2026/9/26 18:45:27
WordPress数据库连接错误排查:从配置到资源耗尽 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 18:45:14
C#调用ONNX Runtime部署SAM2图像分割全链路实践 简介:本资源是面向C#开发者与计算机视觉工程师的ONNX Runtime图像分割实战项目,聚焦SAM(Segment Anything Model)模型在C#环境下的高效部署与应用。项目解决了C#生态中缺乏轻量、可集成的通用图像分割方案的痛点,适用于… · 2026/9/26 18:45:14
Mac自定义快捷键三层体系:系统层、应用层与脚本层实战指南 1. 为什么系统自带的快捷键设置根本不够用Mac 的键盘快捷键体系,表面看是苹果“开箱即用”的优雅代表——Mission Control、Spotlight、截图、音量调节,一按即达。但真正用上三个月后,几乎每个认真工作的人都会发现:系统设置里那几… · 2026/9/26 19:20:38
华为Atlas 300V部署YOLO实战:从ONNX到OM完整指南 1. Atlas到底是什么,它是“运算加速卡”吗先说结论:Atlas 300V 24G就是一块正儿八经的云端AI推理加速卡,网上总有人在问“atlas 300v 24g 是运算加速卡吗”,我估计是名字里带“V”让人犯迷糊,以为它是什么显卡或显示加… · 2026/9/26 19:20:38
telnet端口连通性诊断:TCP层网络排查核心工具 1. 这不是“老古董”,而是你每天都在用却没真正搞懂的端口诊断利器很多人看到telnet这个词,第一反应是“这玩意儿不是90年代就该进博物馆了吗?”——尤其在 Windows 系统里,默认根本不开,点开 CMD 输telnet直接报错“不… · 2026/9/26 19:20:38
DeepSeek电商供应链风险预警:多变量异常检测与模型蒸馏实战 简介:这份PDF文档面向电商供应链从业者、数据分析师与算法工程师,聚焦供应商评估与中断风险早期识别,系统讲解如何借助DeepSeek多变量异常检测技术构建风险预警方案。全文共662页、65个大章节,支持目录跳转与左侧书签大纲快速定位… · 2026/9/26 19:20:32
U盘拒绝访问的根源与权限修复全指南 1. 这个“拒绝访问”不是系统在耍脾气,而是权限链上某处断了你把U盘插进电脑,双击图标——弹窗:“Windows无法访问该文件夹。拒绝访问。”不是蓝屏,不是报错代码,就这六个字,像一堵没门的墙,把你… · 2026/9/26 19:20:32
工业AR智能巡检落地实战:从选型到部署的避坑指南 简介:这份PPT方案面向工业运维工程师、设备管理人员及AR/XR技术方案设计者,聚焦传统巡检中无法实时查看设备状态、误操作漏检、专业水平参差、应急处理能力有限等痛点,给出以XR技术为核心的智能化巡检解决思路。压缩包内仅1个pptx文件&#x… · 2026/9/26 19:20:32
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46