尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

药品泡罩板检测数据集:VOC+YOLO双格式标注与YOLOv8小样本训练实战

发布时间:2026/9/30 1:06:37

资讯中心
01
ARTICLE

药品泡罩板检测数据集:VOC+YOLO双格式标注与YOLOv8小样本训练实战

药品泡罩板检测数据集:VOC+YOLO双格式标注与YOLOv8小样本训练实战
简介这是一份面向目标检测初学者与药品识别方向开发者的布洛芬检测数据集可用于训练和验证单类别目标检测模型适用于药品分拣、智能零售货架识别等场景。数据集共476张jpg图片每张均配有对应的VOC格式xml标注与YOLO格式txt标注标注类别为buluofen共657个矩形框采用labelImg工具完成画框标注可直接用于YOLO系列或Pascal VOC流程的模型训练。压缩包内文件总数1430个包含478个txt、476个xml与476个jpg整体约19.62MB体积轻量便于快速下载与本地部署。目前已有175人学习下载适合作为小样本单类别检测的练手素材帮助读者省去从零采集与标注的成本快速搭建训练管线并验证模型效果。1. 药品泡罩板检测为什么值得单独做一份数据集药品布洛芬检测数据集476 张 VOC 加 YOLO 双格式标注这个体量放在通用目标检测里连零头都算不上但放在药品视觉质检这个细分场景里它恰好卡在一个很实用的位置上。布洛芬这类片剂药品在产线上最常见的包装形态是铝塑泡罩板泡罩板上的药片缺失、破损、异物混入、铝箔封口不严都是药厂灯检工位每天要面对的问题。传统做法靠人工盯着传送带看一条线两班倒至少四个人漏检率还压不下去。用目标检测做自动灯检核心瓶颈从来不是模型结构而是有没有一批标注干净、格式对得上、能直接喂给 YOLO 训练的小样本数据。这份数据集解决的就是这个冷启动问题。476 张图覆盖了泡罩板在不同光照、不同角度、不同批次下的成像VOC 格式给的是 XML 标注YOLO 格式给的是归一化后的 txt 标注两种格式并存意味着你既可以用现成的 YOLOv8 训练脚本直接跑也可以拿 VOC 那套去接 MMDetection 或者做数据增强后再转换。适合谁用做药品视觉质检的算法工程师、想验证小样本目标检测方案的学生、以及需要快速搭一个药片检测 demo 的产品团队。不适合谁指望拿它直接上产线的人——476 张的规模只够做原型验证和迁移学习起点真要落地还得按我后面讲的采集策略补数据。2. 拆开这份 VOCYOLO 数据集目录结构、标注格式与类别定义2.1 拿到压缩包先看什么目录树与文件命名解压之后不要急着写训练脚本先把目录结构摸清楚。这类双格式数据集常见的组织方式有两种一种是 VOC 和 YOLO 各占一个顶层目录各自带 images 和 labels另一种是共享一份 imagesVOC 的 XML 和 YOLO 的 txt 分别放在 Annotations 和 labels 下。两种都能用但转换脚本的路径写法完全不同先确认再动手能省掉半小时的报错排查。# 先看顶层结构确认是分离式还是共享式 find ./ibuprofen_dataset -maxdepth 2 -type d | sort # 统计图片数量和标注数量是否对得上 find ./ibuprofen_dataset -name *.jpg -o -name *.png | wc -l find ./ibuprofen_dataset -name *.xml | wc -l find ./ibuprofen_dataset -name *.txt | wc -l上面三条命令分别做三件事第一条列出两层目录判断组织方式第二条统计图片总数正常应该是 476第三条分别统计 XML 和 txt 数量。如果 XML 数量和图片数量不一致说明有图没标或者标注文件命名对不上这种情况在二手数据集里很常见必须先修掉再训练否则 YOLO 训练时会静默跳过没有标注的图你看到的 loss 曲线是正常的但模型实际少学了那部分样本。提示文件名里的空格和中文是 YOLO 数据加载的隐形杀手。如果发现图片名带空格先用rename s/ /_/g *.jpg批量替换再往下走。2.2 VOC XML 与 YOLO txt 的字段对照VOC 的 XML 标注里一张图对应一个文件核心字段是filename、size里的宽高、以及每个object下的name和bndbox的 xmin/ymin/xmax/ymax。YOLO 的 txt 每行一个目标格式是class_id cx cy w h全部归一化到 0 到 1 之间。这两套格式的转换关系不复杂但有两个地方容易翻车一是 VOC 的坐标是左上角加右下角的绝对像素值YOLO 要的是中心点加宽高再除以图像尺寸二是类别名到 class_id 的映射必须全局一致不能这张图里布洛芬是 0那张图里变成 1。字段VOC XMLYOLO txt转换注意坐标原点左上角图像中心先算中心点再归一化坐标单位绝对像素0-1 归一化除以 width 和 height宽高表示xmax-xminw 直接给别把 xmax 当宽用类别表示name 字符串class_id 整数建全局映射表一图多目标多个 object 节点多行行序无所谓这张表建议打印出来贴在显示器边上写转换脚本的时候对着看。我见过太多人把xmax直接当成w塞进 YOLO 格式训练出来的框全部偏到左上角还以为是模型结构有问题。2.3 类别定义与标注质量自查药品泡罩板检测的类别定义通常不会太复杂常见的是intact完好药片、missing缺片、broken破损、foreign异物这几类。但这份数据集具体定义了哪些类必须自己打开 XML 看不能猜。用下面这段脚本快速统计类别分布顺便检查有没有拼写不一致的类别名。import os import xml.etree.ElementTree as ET from collections import Counter xml_dir ./ibuprofen_dataset/Annotations counter Counter() for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, fname)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text.strip() counter[name] 1 for cls, cnt in counter.most_common(): print(f{cls}: {cnt})这段脚本遍历所有 XML把每个 object 的 name 抽出来计数。跑完之后重点看两件事一是类别总数是不是和你预期的一致二是每个类别的样本量差距有多大。如果missing类只有几十个框而intact有上千个直接训练会导致模型严重偏向多数类后面第 4 章会讲怎么处理这种不均衡。3. 从 VOC 到 YOLO转换脚本、数据划分与训练配置3.1 写一个能复用的 VOC 转 YOLO 脚本网上能找到的转换脚本一大把但大部分只处理单目录、不检查边界、不生成类别映射文件跑完还得手动补。下面这个版本把这三个坑都填了直接改路径就能用。import os import xml.etree.ElementTree as ET import shutil VOC_IMG_DIR ./ibuprofen_dataset/JPEGImages VOC_XML_DIR ./ibuprofen_dataset/Annotations OUT_IMG_DIR ./yolo_dataset/images OUT_LBL_DIR ./yolo_dataset/labels os.makedirs(OUT_IMG_DIR, exist_okTrue) os.makedirs(OUT_LBL_DIR, exist_okTrue) # 先扫一遍所有类别建立稳定的映射 classes set() for f in os.listdir(VOC_XML_DIR): if f.endswith(.xml): root ET.parse(os.path.join(VOC_XML_DIR, f)).getroot() for obj in root.findall(object): classes.add(obj.find(name).text.strip()) classes sorted(classes) cls_map {c: i for i, c in enumerate(classes)} print(类别映射:, cls_map) for f in os.listdir(VOC_XML_DIR): if not f.endswith(.xml): continue root ET.parse(os.path.join(VOC_XML_DIR, f)).getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 边界裁剪防止标注越界导致归一化后超出 0-1 xmin max(0, min(xmin, w)) xmax max(0, min(xmax, w)) ymin max(0, min(ymin, h)) ymax max(0, min(ymax, h)) cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{cls_map[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) stem os.path.splitext(f)[0] with open(os.path.join(OUT_LBL_DIR, stem .txt), w) as fp: fp.write(\n.join(lines)) # 拷贝对应图片 for ext in (.jpg, .png, .jpeg): src os.path.join(VOC_IMG_DIR, stem ext) if os.path.exists(src): shutil.copy(src, os.path.join(OUT_IMG_DIR, stem ext)) break脚本的逻辑分四步先扫全部 XML 建立类别到 id 的稳定映射保证所有图用同一套编号再逐图解析尺寸和每个目标框然后做边界裁剪把越界的坐标拉回图像范围内最后归一化写入 txt 并拷贝图片。参数上唯一需要改的是顶部四个路径classes排序是为了让映射可复现如果你有指定的类别顺序把sorted(classes)换成固定列表即可。3.2 训练集验证集划分476 张怎么分才不浪费476 张图按 8:2 分是 380 训练加 96 验证。这个量级下我一般不会单独切测试集因为切完之后测试集只有几十张指标波动大到没有参考意义。更稳的做法是训练集和验证集按 8:2 分验证集同时承担调参和最终评估的角色等模型定型后再用产线新采的数据做一次独立验证。import os import random import shutil random.seed(42) img_dir ./yolo_dataset/images lbl_dir ./yolo_dataset/labels train_img ./yolo_dataset/images/train val_img ./yolo_dataset/images/val train_lbl ./yolo_dataset/labels/train val_lbl ./yolo_dataset/labels/val for d in (train_img, val_img, train_lbl, val_lbl): os.makedirs(d, exist_okTrue) files [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(files) split int(len(files) * 0.8) for i, f in enumerate(files): stem os.path.splitext(f)[0] if i split: shutil.move(os.path.join(img_dir, f), os.path.join(train_img, f)) shutil.move(os.path.join(lbl_dir, stem .txt), os.path.join(train_lbl, stem .txt)) else: shutil.move(os.path.join(img_dir, f), os.path.join(val_img, f)) shutil.move(os.path.join(lbl_dir, stem .txt), os.path.join(val_lbl, stem .txt))random.seed(42)是为了让划分可复现团队协作时每个人跑出来的划分一致对比实验才有意义。划分比例 0.8 可以调但 476 张的规模下不建议低于 0.75否则训练样本太少模型连基本特征都学不稳。3.3 YOLOv8 训练配置小样本下的参数取舍数据准备好之后写 data.yaml 指向训练和验证目录然后启动训练。小样本场景下有几个参数和默认值不一样需要手动调。# data.yaml path: ./yolo_dataset train: images/train val: images/val nc: 4 names: [intact, missing, broken, foreign]yolo detect train \ data./data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.001 \ patience30 \ augmentTrue \ mosaic0.5 \ degrees10.0 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4参数逐个说modelyolov8n.pt选 nano 版本是因为 476 张图撑不起大模型n 版在 CPU 上也能跑推理适合先验证流程epochs150比默认的 100 多小样本需要更多轮次收敛patience30是早停耐心值验证指标 30 轮不涨就停防止过拟合mosaic0.5把默认的 1.0 降下来因为药品泡罩板图像里目标排列有规律过度拼接会破坏空间关系degrees10.0限制旋转角度泡罩板在产线上不会大角度翻转增强过头反而引入噪声。hsv_h/s/v三组是色调、饱和度、明度扰动药品铝箔反光强适度扰动能提升光照鲁棒性。注意如果训练时看到mosaic增强后的图里药片被裁掉一半把mosaic降到 0.3 或者关掉小目标在拼接边缘容易被切碎。4. 小样本药品检测的避坑与排查清单4.1 类别不均衡导致模型只认完好药片现象训练完看混淆矩阵missing和broken的召回率不到 0.3模型几乎把所有框都预测成intact。原因完好药片在每张图里数量最多缺片和破损是少数样本损失函数被多数类主导。解决在 data.yaml 同级加一个cls_weights配置或者用 YOLOv8 的fraction参数配合过采样把少数类图片复制多份参与训练。更直接的办法是在 loss 里给少数类加权YOLOv8 支持通过class_weights传入。4.2 标注框越界导致训练 loss 突然变 NaN现象训练到第 20 轮左右 loss 突然变成 NaN重启后从断点继续还是 NaN。原因部分 XML 里的 bndbox 坐标超出了图像宽高转换时没裁剪归一化后出现大于 1 或小于 0 的值YOLO 在计算 CIoU 时对非法框没有保护。解决回到 3.1 的转换脚本确认边界裁剪那段逻辑生效转换完用下面这行快速检查。awk {if ($20 || $21 || $30 || $31 || $40 || $41 || $50 || $51) print FILENAME: $0} ./yolo_dataset/labels/train/*.txt4.3 图片和标注文件名对不上导致静默丢样本现象训练日志里train: Scanning...显示的图片数比实际少比如 476 张只扫到 430 张。原因YOLO 按图片名去找同名 txt找不到就跳过不报错。常见于图片是.JPG大写扩展名而标注是.txt小写或者文件名里有不可见字符。解决用diff (ls images/train | sed s/\.[^.]*$// | sort) (ls labels/train | sed s/\.txt$// | sort)对比两边文件名差异项就是问题所在。4.4 验证集指标虚高但实际推理漏检现象验证集 mAP 跑到 0.85拿产线新拍的图一测漏检一半。原因476 张图如果来自同一批次、同一光照条件训练集和验证集分布几乎一样模型学到的是这批数据的特定特征不是药品缺陷的通用特征。解决划分验证集时按批次或按光照条件分层抽样确保验证集里有训练集没见过的成像条件。如果数据本身批次单一那验证集指标只能当参考不能当落地依据。4.5 推理时置信度阈值设太高把缺片漏掉现象模型训练指标正常部署时conf0.5过滤后缺片全没了。原因缺片这类缺陷的预测置信度天然比完好药片低因为缺陷区域的特征不如完好药片规整。解决分类别设阈值完好药片可以用 0.5缺片和破损降到 0.25 到 0.3用 YOLO 推理时的classes参数配合后处理逻辑分开处理。5. 把 476 张用出 4760 张的效果增强策略与迁移验证476 张的规模单靠原始数据训练出来的模型泛化能力有限但通过合理的增强和迁移策略可以把这个小数据集的效用放大。我一般会做三件事。第一件是离线增强扩样本。YOLO 自带的在线增强每次 epoch 随机变换但小样本下模型可能在同一张图上过拟合。用 Albumentations 做一轮离线增强把训练集扩到 3 到 5 倍重点加高斯噪声、运动模糊和局部遮挡模拟产线相机抖动和药片反光。代码不复杂核心是A.Compose里把GaussNoise、MotionBlur、CoarseDropout串起来对图片和 bbox 同步变换。第二件是用预训练权重做迁移。yolov8n.pt本身是在 COCO 上训过的已经学到了边缘、纹理这些底层特征药品检测需要的是在这些特征上微调出泡罩板和药片的语义。冻结 backbone 前 10 层先训 20 轮再解冻全量训 100 轮这个两阶段策略在小样本上比直接全量训练稳定得多。冻结的层数可以通过model.model[:10]查看不同版本索引不一样以实际打印为准。第三件是交叉验证代替单次划分。476 张做 5 折交叉验证每折用 380 张训练、96 张验证轮换五次最终指标取五折平均。这样比单次 8:2 划分的评估结果可靠得多也能看出模型在不同数据子集上的稳定性。如果五折的 mAP 方差超过 0.1说明数据分布不均匀需要回头检查采集环节。策略数据量效果训练时间适用阶段原始 476 张基线最短流程验证离线增强 3 倍约 1400 张中等正式训练5 折交叉验证等效 5 次训练最长指标评估两阶段迁移不变中等小样本首选最后说一个我踩过的坑有次拿这份数据训完模型验证集 mAP 0.88兴冲冲接到产线相机上结果传送带速度一快运动模糊直接把缺片检测干到 0.4 以下。后来在增强里加了MotionBlur(blur_limit7)重新训了一版才稳住。小样本数据集的价值不在于它本身有多大而在于它能让你用最低成本把整条训练和部署链路跑通跑通之后补数据的方向也就清楚了。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。