news 2026/9/23 15:44:00

药品小样本目标检测实战:板蓝根颗粒数据集VOC转YOLO与训练全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
药品小样本目标检测实战:板蓝根颗粒数据集VOC转YOLO与训练全解析

简介:针对板蓝根颗粒袋装药品检测任务,这份数据集面向计算机视觉初学者和工业质检应用开发者,提供111张真实拍摄的jpg原图以及完全对应的VOC格式xml与YOLO格式txt标注文件,覆盖“999ganmaoling”和“banlangen”两个类别,可用于训练目标检测模型、验证标签格式转换或搭建药品包装识别小项目。压缩包内共335个文件,主体为jpg图像、xml标注和txt标注三类,整体仅3.71MB,轻量易用;所有标注由labelImg工具完成,共包含134个目标框,其中banlangen类85个、999ganmaoling类49个,类别分布清晰,适合快速跑通从数据加载到模型评估的完整流程。三类文件一一对应,方便对照原图检查标注效果,也可直接作为YOLO、SSD、Faster R-CNN等常见检测器的训练输入。已有121人学习这一资源,对入门级开发者而言是理解VOC与YOLO两种标注规范差异、练习自制数据集制作全流程的实用素材。

1. 目标检测数据集里的“小样本特例”:板蓝根颗粒检测该怎么做

做目标检测的朋友应该都有体会:通用数据集好找,但像药品这种垂直场景,想拿现成的标注数据基本靠缘分。这份“药品板蓝根颗粒检测数据集”一共111张JPEG图片,同时给了Pascal VOC的xml和YOLO的txt两套标注,类别只有两个:999ganmaoling和banlangen,总框数134个。数据量不大,但它是真实药品袋装拍摄样本,对想做药品识别、货架陈列检测、颗粒袋定位这类小目标任务的从业者来说,属于难得的起步数据。本文就把这套数据集的内部结构、格式转换方法、训练配置和踩坑点一次拆完。

2. 拆解数据集文件布局:VOC与YOLO双格式的对应关系

2.1 图片、xml、txt三者如何一一对应

拿到手解压之后,文件结构非常直白:一张jpg对应一个同名xml,再对应一个同名txt。比如banlangen_xyxr_3.jpg,旁边就是banlangen_xyxr_3.xml和banlangen_xyxr_3.txt。这种命名规整的格式是labelImg直接导出的典型产物,做数据集校验和二次开发都很省事。

实际使用时会发现一个关键点:YOLO格式的txt文件里不包含类别名称,只存类别ID和归一化坐标。类别ID的顺序取决于标注时定义的classes.txt,如果这份classes.txt里的顺序是999ganmaoling在前、banlangen在后,那么txt第一列是0就代表999ganmaoling,是1就代表banlangen。很多人会在这里翻车——把txt当成独立文件用,却不核对类别顺序,训练出来的模型两个类别全乱套。

2.2 XML标注内容逐字段读一遍

打开任意一个xml文件,里面的字段是VOC标准结构:filename是图片名,size里记录width、height、depth三个维度,object节点下name是类别名,bndbox给出xmin、ymin、xmax、ymax四个整数坐标。这套结构是CV领域最通用的标注交换格式,几乎所有检测框架都支持直接读。

<annotation> <folder>banlangen</folder> <filename>banlangen_xyxr_3.jpg</filename> <size> <width>960</width> <height>540</height> <depth>3</depth> </size> <object> <name>banlangen</name> <bndbox> <xmin>120</xmin> <ymin>98</ymin> <xmax>362</xmax> <ymax>310</ymax> </bndbox> </object> </annotation>

xml里最关键的是bndbox的四个值,它是像素绝对坐标,左上角为原点,x向右增大,y向下增大。如果之后要自己标数据,labelImg画框存成xml是完全一致的格式,这也是数据集作者选择labelImg的原因——开源免费,导出格式兼容性最好。

2.3 TXT标注的归一化坐标换算逻辑

YOLO格式的txt每一行代表一个目标框,结构是:类别ID、x_center、y_center、width、height。五个值用空格分隔,其中坐标全部做了归一化,也就是用像素坐标除以图片宽高得到的0到1之间的浮点数。

1 0.251042 0.377778 0.252083 0.392593 0 0.718125 0.427778 0.229167 0.437037

第一行开头是1,对应banlangen类别;第二行开头是0,对应999ganmaoling类别。从坐标值能反推出原图尺寸是960x540,比如x_center=0.251042,乘以960约等于241,正好落在xml标注的xmin到xmax区间内。这个换算是理解VOC和YOLO格式差异的钥匙:VOC存绝对坐标方便人读,YOLO存归一化坐标方便模型训练。

3. 用Python脚本统一处理数据集:从校验到划分

3.1 校验框坐标是否越界和格式是否齐全

数据集在训练前最怕的是标注错误。我写了一个快速的校验脚本,逐个xml读bndbox,和图片宽度高度做对比,检测坐标越界、框宽高是否为负、图片是否缺失标注文件这三个最常见问题。

import os import xml.etree.ElementTree as ET from PIL import Image xml_dir = "Annotations" jpg_dir = "JPEGImages" for xml_name in os.listdir(xml_dir): xml_path = os.path.join(xml_dir, xml_name) tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) for obj in root.findall("object"): 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) assert xmin >= 0 and ymin >= 0, f"{xml_name} 坐标出现负值" assert xmax <= img_w and ymax <= img_h, f"{xml_name} 坐标越界" assert xmax > xmin and ymax > ymin, f"{xml_name} 框宽高异常"

这段逻辑并不复杂,但能拦下90%的脏数据。坐标越界是标注时手滑拖出画布导致的,框宽高异常通常是误操作画了个点。如果xml里size的宽高和jpg实际尺寸对不上,说明图片被压缩或旋转过,这个也要提前发现。

3.2 VOC坐标转YOLO坐标的具体实现

虽然这份数据集已经同时给了VOC和YOLO两种格式,但如果你之后自己扩充数据,或者从网上找其他VOC数据集用,转换脚本是躲不开的。转换的原理很简单:用box坐标除以图片宽高得到归一化值。

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_names: continue class_id = class_names.index(name) 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_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines)) class_names = ["999ganmaoling", "banlangen"] voc_to_yolo("banlangen_xyxr_3.xml", "banlangen_xyxr_3.txt", class_names)

class_names的顺序就是YOLO模型训练时的类别ID映射,写死顺序会导致ID错乱。我一般把class_names单独存成classes.txt文件,每次训练前都检查一遍。

3.3 数据集按8:1:1比例切分训练集和验证集

111张图的数据量,全部塞进训练集容易过拟合,划分时我建议训练集占八成左右,验证集留十来张。注意这里有一个容易被忽略的细节:划分后要把图片和标注文件同步移动,或者统一生成路径列表文件,保证训练时正样本对得上。

import os import random from sklearn.model_selection import train_test_split jpg_files = [f for f in os.listdir("JPEGImages") if f.endswith(".jpg")] train_files, val_files = train_test_split(jpg_files, test_size=0.15, random_state=42) with open("train.txt", "w") as f: for name in sorted(train_files): base = os.path.splitext(name)[0] f.write(f"JPEGImages/{name} Annotations/{base}.xml\n") with open("val.txt", "w") as f: for name in sorted(val_files): base = os.path.splitext(name)[0] f.write(f"JPEGImages/{name} Annotations/{base}.xml\n") print(f"训练集 {len(train_files)} 张,验证集 {len(val_files)} 张")

random_state固定为42,保证每次跑出的划分结果一致,复现实验结果时不会因为随机切分产生偏差。如果你用的是新版YOLO,不需要手动划分train.txt和val.txt,按照目录结构放好图片,YOLO会自动扫描。

4. 用YOLOv8训练这套板蓝根数据集:完整配置流程

4.1 目录结构搭建与data.yaml配置

把这份数据集喂给YOLOv8之前,需要先按照YOLO系列框架的约定组织目录。我一般习惯把jpg和txt放在同一个目录下,保持文件名一一对应,然后写一个data.yaml描述数据集信息。

dataset/ ├── images/ │ ├── train/ │ │ └── banlangen_xyxr_3.jpg │ └── val/ │ └── banlangen_xyxr_45.jpg ├── labels/ │ ├── train/ │ │ └── banlangen_xyxr_3.txt │ └── val/ │ └── banlangen_xyxr_45.txt └── data.yaml

data.yaml的内容是训练入口,路径、类别数、类别名都必须写对。这里有一个高频坑点:paths最好写绝对路径,很多新手写相对路径导致训练启动后报错找不到图片。yaml文件不能有tab缩进,只能用空格。

train: /home/user/dataset/images/train val: /home/user/dataset/images/val nc: 2 names: ['999ganmaoling', 'banlangen']

注意names的列表顺序和生成txt时的class_names顺序必须严格一致,0对应999ganmaoling,1对应banlangen,顺序反了模型就是一个废物。验签方法是随机打开几个txt文件,看第一列数字是否和预期类别匹配。

4.2 YOLOv8训练命令与关键参数实测说明

YOLOv8是目前主流框架里对标签格式兼容性最好的一个,直接用yolo命令就能训练。权重建议选yolov8s.pt或者yolov8n.pt,111张图属于微型数据集,模型大了必然过拟合,s模型已经偏大,n模型更稳。

yolo train data=data.yaml model=yolov8n.pt epochs=200 imgsz=640 batch=16 device=0 patience=3

epochs给到200是为了观察loss曲线是否收敛,实际可能在几十轮就早停了。patience=3是早停策略,如果验证集loss连续3轮没有下降,训练自动终止。batch=16要看显卡显存,6GB以下建议8。设备是CPU就改成device=cpu,但训练时间会拉到数小时。

训练结束后看results.csv里的指标,重点看两个数:mAP50和mAP50-95。小数据集上mAP50高只能说明模型对训练集的记忆好,mAP50-95低才是真正的泛化能力差。这个项目类别是药品颗粒袋,形状比较规整,两个指标差异不会特别夸张。

4.3 推理测试与置信度阈值调整

训练完成后用best.pt做推理,把验证集的图片跑一遍看效果:

yolo predict model=runs/detect/train/weights/best.pt source=dataset/images/val conf=0.25 iou=0.45

conf参数是置信度阈值,默认0.25。这个数据集是近距离拍摄的药品袋,目标相对大且清晰,阈值可以往高调到0.4以上,减少误检。iou是NMS的IoU阈值,默认0.45,目标密集重叠时可以调低到0.35,避免两个紧挨着的药袋被合并成一个框。

5. 避坑与常见问题:小样本数据集训练翻车实录

5.1 类别名带数字导致解析错乱

现象:训练时报错Class index out of range,或者推理时999ganmaoling和banlangen的框混乱。原因:999ganmaoling这个类别名以数字开头,某些框架在解析类别字符串时会把它截断或转换成数值,尤其导出ONNX或TFLite时容易出现。解决:在data.yaml里保持names顺序不变,但代码中所有读取类别的地方统一走names数组索引,不要依赖正则提取。我在实际使用中更推荐把类名改为ganmao_999这样的格式,规避数字开头的解析风险。

5.2 用labelImg续标时标签序号没对齐

现象:自己手动补标了几十张图,训练后发现某些框的类别和实际物体不符。原因:labelImg打开的是默认class_list,它把999ganmaoling和banlangen的ID顺序和你之前txt里的顺序弄混了。解决:在labelImg的设置里加载一份写死的classes.txt,每次打开都强制定位到同一份类别表;标注完之后随机抽查20%的txt文件,确认类别ID和框内物体一致。

5.3 数据集过小直接爆内存或显存溢出

现象:batch设大之后CUDA out of memory,或者模型训练一两轮loss就冲到NaN。原因:111张图还带早停,模型很容易把验证集背下来,loss曲线失真;显存溢出多半是图像尺寸和batch配置不合理。解决:imgsz从640降到416,batch降到8,开启mosaic增强。如果做迁移学习,加载预训练权重时记得加freeze=10冻结前10层,保住主干网络的特征提取能力。

5.4 框与图片尺寸不一致导致训练崩溃

现象:ValueError: zero-size image to tensor,或者训练初始化时报标签越界。原因:数据集里jpg文件和xml的尺寸是匹配的,但你用自己的代码转换后可能用了另一批图片。解决:统一用python脚本检查每张jpg的实际尺寸,和xml里size字段做比对,不一致的直接用pillow重写size字段。这个操作不麻烦,但很多做检测的都默认格式完美,结果训练一半才发现标注和图像对不上。

5.5 多标签或同一物体被画了多个框

现象:一张图里同一个药袋被标出两个重叠框,训练时loss震荡。原因:标注者手抖,或者在labelImg里画完框又没删掉旧框直接再画一遍。解决:写一个查重脚本,计算所有框的IoU,大于0.8的只保留一个。这种问题只有检查时才能发现,跑训练之前一定过一遍。用表格整理一下数据集的标注分布:类别999ganmaoling有49个框,banlangen有85个框,总框数134,类别不平衡程度在可接受范围内。

6. 进阶技巧:用数据增强和置信度调优提升小模型实战表现

先把111张原图喂给模型,假设训练结果mAP50在0.85左右,这在小样本场景已经算不错。想继续往上顶,第一个必做的是离线数据增强。不要只依赖YOLO自带的在线增强,药品袋这种目标有明显的色彩和纹理特征,做HSV扰动和随机旋转能成倍扩大有效样本量。

import imgaug.augmenters as iaa import cv2 aug_pipeline = iaa.Sequential([ iaa.Fliplr(0.5), iaa.Affine(rotate=(-15, 15)), iaa.MultiplyHue((0.8, 1.2)), iaa.LinearContrast((0.8, 1.2)) ]) image = cv2.imread("banlangen_xyxr_3.jpg") augmented = aug_pipeline(image=image) cv2.imwrite("banlangen_xyxr_3_aug.jpg", augmented)

增强后的图片必须同步更新标注框,imgaug的Affine和Fliplr会自动做坐标变换,但如果自己手写平移旋转,千万记得把xml里的bndbox一起换算。我见过有人增强后只保存了图片,标注框还停在原坐标上,这种“半套增强”比不增强还坑。

第二个进阶方向是调推理置信度。药品颗粒袋在货架上经常被遮挡,遮挡后模型输出的置信度会掉到0.2左右,但你其他场景又不需要这么低的阈值。做法是跑推理时对每个类别单独设阈值,999ganmaoling的包装颜色偏浅,阈值可以设低到0.15;banlangen的包装颜色深,0.3起步都够。实际调优时在验证集上跑一遍完整推理,把所有的conf值存下来画直方图,找到两个类别置信度分布的分水岭,再分别设阈值。

从那以后我做任何小样本检测任务,都会强制走一遍流程:校验标注、统一类别清单、按比例切分、冻结主干预训练、训练后输出置信度直方图调阈值。这套流程不需要玄学,每一步都有明确产出。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 15:44:00

1)参考移植手册完成,spi_nor U-boot下的移植

1)参考移植手册完成,spi_nor U-boot下的移植 也就是说 fmc_spi_nor_ids.c里面主要添加nor flash的id fmc100_spi_general.c是大部分器件通用的nor flash驱动代码 fmc100_spi_gd25qxxx.c、fmc100_spi_mx25l25635e.c、fmc100_spi_s25fl256s.c 等c文件,是特殊flash,具体的驱…

作者头像 李华
网站建设 2026/9/23 15:43:38

从实验室放电到产线前处理:等离子活化机经历了什么

一切从一团“电离的气体”开始很多难粘的材料&#xff0c;比如手机壳的塑料、汽车密封条的橡胶、光伏硅片的表面&#xff0c;天生就“不亲水、不挂胶”。要让油墨印得上、胶水粘得牢、涂层不脱落&#xff0c;工程师们很早就在研究一件事&#xff1a;怎样在不损伤材料的前提下&a…

作者头像 李华
网站建设 2026/9/23 15:41:34

传送带异物检测数据集与YOLOv11实战:从标注到工业部署

简介&#xff1a;本资源为面向工业质检与安全生产场景的传送带异物检测数据集&#xff0c;可识别铁棍、垃圾等常见异物&#xff0c;适合从事目标检测算法训练、产线智能监控改造的开发者与研究人员使用。数据集采用YOLOv11标注格式&#xff0c;可直接接入主流检测框架进行训练与…

作者头像 李华
网站建设 2026/9/23 15:37:08

从蜘蛛到海星:连锁生意如何摆脱救火式管理,长出自主繁衍能力

之前和一个做连锁小吃的朋友聊天&#xff0c;他四十多家门店&#xff0c;每天凌晨一点还捧着手机盯群&#xff1a;哪家店原料报损超标了、哪个员工又在朋友圈发情绪了、哪家店的卫生检查没过&#xff0c;桩桩件件都要他拍板。他跟我说了一句话&#xff0c;我印象特别深&#xf…

作者头像 李华
网站建设 2026/9/23 15:31:26

昇腾NPU上部署DeepSeek V3/R1:从KV缓存压缩到推理性能调优

简介&#xff1a;面向人工智能工程师、大模型平台架构师及企业技术决策者&#xff0c;这份33页文档系统梳理了华为基于昇腾的深度求索V3与R1方案。内容从深度求索公司背景与系列模型迭代切入&#xff0c;重点拆解V3的混合专家架构、多头潜在注意力、多词元预测与无辅助损失负载…

作者头像 李华