
简介这是一份面向计算机视觉开发者与智能制造质检场景的药片缺陷检测数据集共包含8625张真实药片图像并提供COCO JSON格式的标注文件可直接用于药片完整性判断、表面缺陷识别等模型的训练与验证适合有监督学习、目标检测及缺陷分类方向。压缩包共2000个文件核心为1996张JPG图片另有2个JSON标注文件和2个TXT说明文件整体大小约42.62MB结构轻量便于下载、解压并接入主流检测框架。目前已有746人学习/下载。读者无需自行采集和手动标注即可获得一套带完整标注的工业质检数据用于快速开展模型实验与效果评估。该数据集覆盖多种药片外观样本与缺陷形态可作为课程设计、毕业设计或企业算法预研的数据基础有效节省数据准备时间聚焦于检测模型调优与业务落地。1. 为什么要单独做一个药片缺陷检测数据集8625张图背后的两类问题药片缺陷检测数据集8625张图片全部使用COCO JSON格式标注它解决的是工业视觉里最尴尬的一个问题缺陷样本根本凑不齐。药品产线上的AOI设备一天能拍几万张图但真正带划痕、缺角、污点的药片可能只有几十片凑成可用训练集往往要攒几个月。所以当看到这份数据集时第一反应是它的价值不在8625这个数字而在标注层面已经把“是否有缺陷、是否完整”两类判断做了实例级标注模型可以直接学。适合三类人做药品或食品外观质检的工程师、想拿真实工业数据练YOLO训练流程的算法岗新手、以及需要把COCO JSON格式迁移到自己业务场景里的开发者。它有坑但坑不在数据质量而在格式转换和训练习惯上下面逐层拆。2. COCO JSON标注拆解五个顶层字段与缺陷标注的真实含义2.1 为什么选COCO JSON格式选型的三个理由目标检测领域主流标注格式无非COCO JSON、VOC XML、YOLO txt三种。这份数据集选COCO JSON不是随手定的三个理由很实际。第一COCO JSON是单文件结构所有标注集中在一个JSON里分发、校验、备份都比VOC那种“每图一个XML”方便8625张图对应8000多个XML文件管起来是灾难。第二COCO JSON自带categories、images、annotations三张逻辑表类别体系、图片元信息、实例标注解耦清晰换任务时只需要改categories不用重写解析器。第三从COCO JSON可以无损推导出YOLO txt格式反向推导则很难因为YOLO的归一化中心点坐标丢失了原图尺寸信息。所以COCO JSON作为原始格式是合理的训练前再转YOLO格式即可。2.2 annotations文件里的信息从bbox、area到category_id一个标准的COCO JSON顶层有五个字段info、licenses、images、annotations、categories。对训练有用的是后三个。images数组里的每条记录包含id、file_name、width、height这是后续归一化坐标的基准annotations数组每条记录包含id、image_id、category_id、bbox、area、segmentation、iscrowd其中bbox是[x, y, w, h]格式x和y是框左上角坐标w和h是宽高单位是像素categories数组定义了类别ID到类别名的映射。这份数据集按“可识别药品是否有缺陷是否完整”来组织实际标注通常拆成两个类别完整药片和缺陷药片每个药片实例都落在其中一个类别里模型输出两个类别的框再根据框的类别做后续判定。以下是一段典型的COCO标注条目示例{ images: [ { id: 1, file_name: pill_00001.jpg, width: 1280, height: 720 } ], annotations: [ { id: 1, image_id: 1, category_id: 2, bbox: [410.5, 300.0, 120.0, 118.0], area: 14160.0, iscrowd: 0, segmentation: [] } ], categories: [ {id: 1, name: intact_pill}, {id: 2, name: defective_pill} ] }逻辑说明注意bbox的坐标是浮点数这是COCO JSON的正常写法卷积网络下采样后坐标精度本来就有限保留浮点比取整更利于训练收敛area字段在训练时基本用不到但做数据过滤时有用比如筛掉面积小于某个像素阈值的疑似错误标注。参数说明segmentation字段在本数据集里通常是空数组因为这份资源只标注了矩形框没有做多边形分割所以转YOLO格式时不需要理会segmentationiscrowd固定为0表示没有群体标注每个框对应一个独立药片如果有多个粘连药片被标成一个框iscrowd应该为1训练时会被当作背景处理这是常见误用之一。2.3 数据集的文件构成与缺失内容自检拿到资源后典型目录结构一般是这样的具体文件名可能略有差异路径内容是否必须images/ 目录8625张JPG或PNG原图是annotations/instances.json 或 train.json val.jsonCOCO格式标注是README.md数据来源、类别定义、拍摄设备说明建议有classes.txt类别列表每行一个类名建议有train.txt / val.txt图片路径列表供YOLO直接读取可选我拿到任何COCO数据集的第一件事不是看标注而是用脚本自检三件事一是images数量是否等于实际图片文件数防止有图无标注或标注无图二是bbox的x、y是否可能为负数或超出图片边界这类脏数据会让loss训练出NaN三是categories中的类别ID是否连续如果不连续转YOLO格式时category_id必须做重映射否则训练会报类别数不匹配。3. 从COCO JSON到YOLO训练划分、转换与可视化验证一把过3.1 划分策略随机划分能跑按实例分组划分才靠谱把8625张图按比例拆成训练集和验证集常见做法是随机抽80%做train、20%做val。但这套数据有个特殊性同一缺陷类型往往在同批次药片上有相似形态产线照片里同一个缺陷药片的多个视角可能出现在多张图中随机划分会让同一枚药片的不同照片同时进train和val导致验证指标虚高部署时一换新药片立刻翻车。我一般会在划分前先看README,如果原资源没提供分组信息就退而求其次按image_id的语义过滤文件名相同的批次号做粗分组尽量让同一批次的药片只落在同一个集合里。代码层面不复杂关键是不要把划分逻辑写死成random.shuffle一把梭。3.2 COCO转YOLO txt的完整划分与转换脚本下面的脚本把train/val划分、格式转换、可视化验证三步合在一起适用于绝大多数COCO JSON数据集import json import os import random from PIL import Image, ImageDraw # 文件路径配置按实际目录修改 coco_json_path annotations/instances.json image_dir images output_dir yolo_dataset random.seed(42) val_ratio 0.2 # 读取COCO标注 with open(coco_json_path, r) as f: coco json.load(f) # 构建类别ID到连续索引的映射防止类别ID不连续 cat_id_to_idx {cat[id]: idx for idx, cat in enumerate(coco[categories])} with open(classes.txt, w, encodingutf-8) as f: for _, name in sorted(cat_id_to_idx.items(), keylambda x: x[1]): f.write(name \n) # 按image_id聚合annotations便于按图写入 anns_by_image {} for ann in coco[annotations]: anns_by_image.setdefault(ann[image_id], []).append(ann) image_ids list(range(len(coco[images]))) random.shuffle(image_ids) val_ids set(image_ids[: int(len(image_ids) * val_ratio)]) train_txt, val_txt [], [] for img_info in coco[images]: img_id img_info[id] file_name img_info[file_name] img_path os.path.join(image_dir, file_name) txt_path os.path.join(output_dir, labels, file_name.rsplit(., 1)[0] .txt) anns anns_by_image.get(img_id, []) lines [] for ann in anns: cat_idx cat_id_to_idx[ann[category_id]] x, y, w, h ann[bbox] img_w img_info[width] img_h img_info[height] # COCO的(x, y)是左上角YOLO需要中心点加归一化 x_center (x w / 2) / img_w y_center (y h / 2) / img_h w_norm w / img_w h_norm h / img_h lines.append(f{cat_idx} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) os.makedirs(os.path.dirname(txt_path), exist_okTrue) with open(txt_path, w) as f: f.write(\n.join(lines)) subset val if img_id in val_ids else train txt_line os.path.join(os.path.abspath(output_dir), images, file_name) (val_txt if subset val else train_txt).append(txt_line) with open(os.path.join(output_dir, train.txt), w) as f: f.write(\n.join(train_txt)) with open(os.path.join(output_dir, val.txt), w) as f: f.write(\n.join(val_txt)) print(ftrain: {len(train_txt)}, val: {len(val_txt)})逻辑说明脚本先读COCO JSON并构建类别映射然后用image_id聚合所有标注框划分时先对整张图做shuffle再按比例切出验证集保证同一张图不会同时出现在两个集合里写YOLO标签时把左上角坐标换算成中心点坐标并除以图片宽高做归一化这一步很多人漏掉除以宽高直接写了像素值训练时loss直接爆炸。参数说明val_ratio取0.2是默认值如果你的缺陷类别样本少可以把验证集降到0.15多留点训练数据给罕见缺陷random.seed(42)保证每次运行划分结果一致否则复现结果时会找不到是数据划分还是模型结构导致的指标差异输出目录里的labels文件夹结构要和YOLO要求的images并列否则训练时找不到对应标签。3.3 用可视化验证转换结果别让坐标错位浪费一次训练转换完不能直接开训必须抽几张图把框画回原图肉眼看一遍。经验是每200张抽1张8625张里抽40张左右覆盖白天和夜间拍摄、不同批次药片。画框脚本很简单import os import random from PIL import Image, ImageDraw image_files os.listdir(images) random.seed(7) samples random.sample(image_files, 20) for fname in samples: txt_path yolo_dataset/labels/ fname.rsplit(., 1)[0] .txt if not os.path.exists(txt_path): continue img Image.open(os.path.join(images, fname)).convert(RGB) draw ImageDraw.Draw(img) with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) 5: continue cat_idx parts[0] x_c, y_c, w_n, h_n map(float, parts[1:]) # 还原像素坐标 w, h img.size x0 (x_c - w_n / 2) * w y0 (y_c - h_n / 2) * h x1 (x_c w_n / 2) * w y1 (y_c h_n / 2) * h color (255, 0, 0) if cat_idx 1 else (0, 255, 0) draw.rectangle([x0, y0, x1, y1], outlinecolor, width3) img.save(visual_check/ fname)逻辑说明脚本把归一化中心点还原成像素坐标按类别决定画红还是绿框红色代表缺陷药片、绿色代表完整药片。这一步重点看两类问题一是框有没有明显偏移比如框住了半个背景说明原标注坐标本身错了这类脏数据要在训练前去重而不是指望模型自己学出来二是两张相邻图片的同类药片框尺寸是否一致如果同类别框大小差异超过三倍说明类别定义有歧义比如部分药片没框全、只框了缺陷区域需要统一标注粒度。4. 训练配置与评估指标用YOLOv8把COCO JSON跑成检测模型4.1 data.yaml与超参数这份数据集的推荐配置格式搞定后训练阶段最关键的是data.yaml。YOLOv8系列的训练入口已经是纯配置文件驱动data.yaml写错比模型结构写错更常见。针对这份药片数据集推荐的配置如下path: /path/to/yolo_dataset train: train.txt val: val.txt nc: 2 names: 0: intact_pill 1: defective_pill注意train和val一项填的是上一步生成的txt文件路径而不是images文件夹路径这是Ultralytics YOLO读取标签的方式。path字段用绝对路径最稳相对路径在不同工作目录下容易翻车。训练推荐参数速查表参数推荐值说明modelyolov8s.pt药片目标中等大小s模型足够m太慢收益有限imgsz640如果原图是1280以上宽幅先试640效果差再上960epochs100工业数据100轮足够收敛再多就是过拟合batch16看显卡显存8G卡降到8lr00.01YOLOv8默认值数据集小不用调patience20验证集20轮不涨就早停省时间seed42和转换脚本保持一致方便排查问题4.2 训练命令从预训练权重起步还是从头训首选从yolov8s.pt预训练权重开始不要从yolov8n.pt随机初始化硬训。药片缺陷类型和COCO的日常物体在边缘、纹理上有相似性预训练特征能大幅缩短收敛时间。启动命令yolo detect train \ datapill_dataset.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ seed42 \ projectruns/pill_defect \ nametrain_v1训练过程中重点看两个指标曲线train/loss和val/loss。train/loss下降而val/loss在40轮左右开始反弹就是过拟合信号优先加数据增强而不是加模型容量train/loss和val/loss一起降但最终mAP还是低问题大概率不在训练而在标注质量回到第三章的可视化检查再筛一遍框。4.3 指标怎么看mAP、PR曲线与混淆矩阵的读法训练结束别急着看总的mAP50先看按类别的Per Class结果。这份数据集只有两个类指标解读逻辑完全不同。完整药片类别的mAP50通常能到0.98以上因为该类样本多、形态规整缺陷药片类别mAP50如果能到0.90以上已经非常可用了。mAP50和mAP50-95的差距值得关注如果mAP50有0.95但mAP50-95只有0.6说明模型给出的框定位不精确或者置信度校准差对产线实际应用来说影响很大因为后续剔除误检要依赖置信度阈值置信度分布差会直接压缩阈值调节空间。混淆矩阵要关注的是“缺陷被误判为完整”的比例这类漏检比误检严重得多。误检顶多多停一次机把正常药片拍了再看一遍漏检会让缺陷药片直接流向下游性质完全不同。如果混淆矩阵显示漏检率高优先检查缺陷类的正样本数量不够就做针对缺陷的过采样或复制粘贴增强不要动整体数据分布。5. 避坑指南药片形态、小目标漏检、评估虚高这六关怎么过5.1 图片尺寸不统一导致坐标错位或训练崩溃现象训练日志里出现RuntimeError: CUDA out of memory或者loss直接从10以上起步且不下降。原因工业相机拍摄的药片图常见1280×720、1920×1080、甚至4000×3000混在一起YOLO按imgsz统一resize大图缩到640时药片会变得很小小图放大时药片会模糊两种情况都让模型学不到稳定特征。解决先统计所有图片的宽高分布分辨率差异超过两倍就把最大边超过1600的图按比例缩小保存再做转换。我一般会把所有图统一处理成最长边不超1280并同步更新COCO JSON里的width和height字段。这一步必须在格式转换前做否则坐标归一化的基准是错乱的。5.2 缺陷区域太小整图检测直接漏掉现象验证集上完整药片全检出缺陷药片大量没框住尤其是划痕、污点这类只占药片面积5%以下的小缺陷。原因药片本体只占画面一部分缺陷又是药片的一部分双层小目标叠加后缺陷实际只占全图几个像素。YOLO默认的640输入下特征图最后一个尺度是20×20每格对应32×32像素小缺陷直接淹没在背景特征里。解决两条路第一是imgsz直接从640升到960或1280显存够就优先用这个改动成本最低第二是切图推理把原图切成四个patch分别检测再合并结果这会增加后处理复杂度。对产线项目我一般先用imgsz960试mAP50能提3到5个点就够用切图留到最后。5.3 类别不均衡把模型带偏现象训练正常但验证集上模型把几乎所有药片都预测成完整类别缺陷类精确率高召回率极低。原因药片产线缺陷率本身就在1%到5%之间8625张图里如果完整药片占90%、缺陷只占10%模型学到的最优策略就是“永远预测完整”。这种情况不是模型坏了是损失函数在类别不均衡下的正常表现。解决按类别统计annotations里的框数量如果缺陷类占比低于20%在划分时对缺陷图片做重采样让训练集里两类图片比例接近50:50。最省事的方式是复制缺陷图片路径重复写进train.txt但要注意同一图片不能同时进验证集。5.4 验证集与训练集重复导致指标虚高现象训练时mAP50一直在0.95以上部署到新批次药片直接掉到0.7落差巨大。原因最常见的坑是划分前没去重。同一批次的药片照片高度相似随机划分会让训练集和验证集里出现几乎一模一样的图验证集退化成了记忆测试而不是泛化测试。解决转换脚本里已经按image_id做了单图去重但这不够。还要在划分前检查是否有同名文件在不同子目录下重复以及同一个药片的连续帧照片。最稳妥的做法是手工抽20个验证集文件名去原图里确认和训练集照片不相似。5.5 反光区域被当成缺陷现象模型把药片表面的高光反射点、圆弧过渡处的光晕全框成缺陷精确率被拉低。原因工业场景为了看清药片表面通常打环形光药片表面的糖衣层对光线敏感反光区域在形态上确实和污点接近模型学到的是亮度异常而不是真正的缺陷纹理。解决数据增强阶段把hsv_h、hsv_s、hsv_v的随机范围加大模拟光照变化让模型学会忽略单纯亮度变化更直接的办法是重新看标注如果标注人员把反光也标成了缺陷需要筛掉这部分框再训练这属于原始标注质量问题模型再训练也不会修正。5.6 训练loss降但验证指标卡住不过拟合现象train/loss稳定下降到0.05以下val/loss却不再下降甚至微升mAP50连续30个epoch没有变化。原因模型已经过拟合训练集中药片的具体纹理验证集上新形态的缺陷没有对应的训练样本。药片缺陷不像通用目标检测那样有清晰的类别内一致性——裂纹可能有几十种方向、长度、宽度组合数据量覆盖不到全部组合。解决先确认是不是缺陷类样本太少如果是就先过采样重训练数据量确实没法再扩的话降学习率到0.001把best.pt换成last.pt做推理后者因为没收敛到极致泛化性反而更好。我遇到过一次用last.pt比best.pt在产线测试上mAP50高了4个点的情况训练日志不能全信。6. 数据量不够时的两个后手伪标签迭代与测试时增强模型第一版训完如果缺陷类mAP50仍然达不到产线要求的0.9以上先别急着换大模型这份数据集还有两个后手能榨出剩余价值。第一个是伪标签迭代。用训练好的模型去跑无标注的产线图片把置信度高于0.9的缺陷预测框抽出来人工抽查100个确认不误标之后作为新标注追加到原始JSON里。这里有个细节伪标签只加入缺陷类完整类不加因为完整药片的漏检框一旦被当成伪标签放进训练集等于教模型把漏检当作正确行为。每轮只追加置信度得分最高的样本控制增量在原始数据量的20%以内收敛太快模型容易在少量伪标签上过拟合。我一般会做三轮每轮结束重新跑一次验证集评估如果新增样本没有让验证集mAP50提升立即停止说明数据质量已经到顶。第二个是测试时增强。推理时分别以原图、水平翻转、垂直翻转、0.8和1.2倍缩放共五路输入跑模型把五个结果的框做加权合并置信度取五路预测的平均置信度坐标用置信度加权平均。这个技巧对药片这种小目标检测提升明显尤其能救回边缘被光晕遮挡的药片框定位。代价是推理耗时变为五倍产线部署如果机台节拍允许只对置信度落在0.6到0.9之间的“模糊对像”做TTA重判就能兼顾速度和准确率。从那以后我每次拿到一份COCO格式的工业数据集都强制走一遍三件套先校验bbox合法性再按实例分组划分最后画框可视化抽查。这三步花不了二十分钟但能拦住大部分训练中期的玄学问题。这份8625张的药片缺陷检测数据集做完整个流程之后我对工业缺陷检测的数据认知改变挺大——真正贵的不是标注数量而是标注里的类内一致性希望帮到你。本文还有配套的精品资源点击获取