ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

YOLOv8超市商品识别系统实战:从数据标注到UI部署全流程

YOLOv8超市商品识别系统实战:从数据标注到UI部署全流程 去楼下便利店自助结账第一次用就翻车了三瓶饮料叠在一起系统只识别出两瓶两包外包装几乎一样的薯片直接算成了同款。后来我自己基于YOLOv8搭了一套超市商品识别检测系统把线下采集的超市货架数据、YOLO格式标注、模型训练到带UI界面的Python项目源码全部串起来最后跑出了一个真正能部署到收银场景的Demo。这篇文章就是这次完整落地过程的技术复盘内容包括为什么在众多目标检测方案里选了YOLOv8、超市商品数据集是怎么从零构造并清洗的、训练调参过程中哪些参数真正影响效果以及模型如何导出、封装、接到可视化界面并扛住现场复杂光线和遮挡干扰。不管你是准备做毕业设计还是想给线下门店做一个轻量级的商品识别结算系统这套流程都能直接参考复现。1. 超市商品识别任务里YOLOv8为什么成了首选方案1.1 这个任务比想象中难别拿普通物体检测的思路套很多人一听“识别商品”第一反应是“这不就是目标检测嘛训练一个模型就行”。真正把超市货架上的东西拉到镜头前才发现这个场景比通用检测要刁钻得多。超市商品识别的难点主要集中在几个方面。第一个是类别多且外观极度相似同一类饮料的不同口味、不同容量包装主色调一样只靠局部颜色差异区分第二个是商品密集摆放货架层板上同类商品一个挨一个遮挡严重结算台上商品也会互相叠压第三个是光线条件差超市顶灯往往带强烈暖色光玻璃瓶和铝罐反光严重模型经常把高光区域当成物体边缘第四个是同一SKU的多规格包装同一款零食有大袋、小袋、家庭装宽高比差很多用固定锚框思路的老模型很吃力。传统视觉方案在这个场景里基本不可行。做颜色直方图匹配换一种灯光颜色就失效做形状模板匹配商品只要旋转几度或者被遮住一角匹配率立刻掉下来扫二维码倒是稳定但不是所有商品都有标准码生鲜、散装称重商品、超市自有品牌包装经常没有可扫的码。所以只能上深度学习目标检测。1.2 各代目标检测框架横向对比做商品检测市面上可选的无非就是两阶段检测器、单阶段检测器和最新的端到端方案。我自己把主流的几个都跑过一轮也搜过不少公开评测数据这里直接放一张对比表。方案精度同数据集下mAP参考值推理速度训练复杂度部署生态适合场景Faster R-CNN高但优势随数据量增大而缩小慢帧率偏低需要调RPN、候选框相关参数中TensorRT可用但有兼容坑小目标密集但实时性要求低的场景SSD中低快但易漏检低一般老框架维护少简单场景YOLOv5中高快低非常成熟网上资料多通用落地YOLOv8高快低官方库自带导出ONNX/TensorRT能力商品识别这种类内差异大的场景端到端DETR类高大模型慢显存要求高调参门槛高一般边缘设备跑不动长期研究项目而非快速落地之所以最后选定YOLOv8核心就几条一是它改成了Anchor-Free结构对高宽比差异大的商品包装更友好不再依赖先验锚框的尺寸设置二是C2f结构的特征融合让浅层小目标信息保留得更好货架上那些小瓶饮料、小袋零食也能被召回三是官方库自带训练、验证、导出一条龙脚本省去大量造轮子的时间四是社区活跃踩到坑很快能找到解法。1.3 整套系统包含哪些模块这个项目的最终形态并不是一个孤立的pt权重文件而是一套可运行的完整链路摄像头或图片输入经过YOLOv8模型推理得到每个商品的类别和边界框再走一个后处理模块做置信度过滤、类别合并和价格计算最后把识别结果实时渲染到UI界面上显示商品名、数量、识别置信度和总价。项目文件里包含模型权重、训练脚本、数据集标注文件、UI界面代码以及推理封装模块是一整套能直接打包运行的Python项目。2. 数据的命脉超市商品训练集的构造、标注与质量清洗2.1 数据从哪来光靠公开数据集远远不够咨询了某便利店的运营工程师又结合我自己多次实拍经验可以负责任地说公开的零售商品数据集只能用来验证流程不能直接撑起一个可用系统。公开数据集里的场景大多是国外超市、标准摆拍和国内货架的灯光、价签、陈列密度差异很大模型训练出来一上真实货架掉点非常明显。我的数据来源分了三条线自有实拍数据拿手机和摄像头去线下超市、便利店拍摄货架和收银台场景覆盖平拍、俯拍、斜45度视角并刻意选择不同时段白天、傍晚、夜间弥补光线差异。公开数据集的可用子集只挑其中和自采类别重叠的商品图。网络渠道收集电商平台商品图可以补充大量白底图但白底图与实拍差异大只能做预训练冷启动不能作为主力。这三条线里自拍数据占比至少要拉到60%以上模型才可能在真实场景里看什么像什么。前期建议先选20个高频商品类别做闭环验证每类采集到300~500张有效图片再逐步扩类而不是一上来就标100个类。2.2 标注规范边界框怎么打直接决定模型学成什么样标注工具上我试过LabelImg和labelme还有Roboflow的在线标注。如果只是做矩形检测框LabelImg轻量够用后期如果要转到实例分割再换labelme。所有标注最终都导出为YOLO格式的txt文件每行是“类别序号 中心点x 中心点y 框宽 框高”坐标值是相对于图片宽高的归一化小数。但工具是次要的真正影响模型效果的是标注口径。有几个坑必须提前定好规范。框要紧贴商品可见边缘不要把商品背后的价签、促销牌包进去否则模型会学会“连价签一起框”。多瓶饮料整齐排列时框与框之间允许有轻微重叠但不能把一个商品框进另一个的大半区域。商品被遮挡严重时只标可见部分完全不出来的就不标。遮挡超过一半的样本前期宁可不标后期再专门补充。对透明包装和玻璃瓶标注时以瓶身实际轮廓为准不要把高光反射形成的白色区域当成物体边界剪掉。另外强调一点类别命名不要用中文也不要带空格统一用拼音或英文小写比如“soda”“chips”“milk”写进YAML时省去一堆编码问题。2.3 数据清洗与易混淆类别处理标注完不等于数据能用。我第一版直接拿标注结果训练结果mAP很惨后来检查才发现同一条货架视频里的连续帧很多重复进训练集增强了模型对背景的记忆而不是对商品的识别还有一批网上抓的商品图标签错了让模型对某两个类别彻底混乱。清洗时我做这几步先按图像哈希去重把同一场景的连拍帧用相似度阈值筛掉接着逐类检查标签分布统计每个类别的bbox数量明显偏低的类单独补充拍摄最后人工抽检最难的那批——两种包装差不多的饮料把它们单独挑出来确认是否有标注张冠李戴。超市商品里最典型的就是易混淆对我用表格列一下。易混淆组合人工区分点补充数据策略某品牌苏打水vs某品牌气泡水瓶身标签主色和字体布局不同专门拍两种瓶子混放、叠放的照片原味薯片vs番茄味薯片包装正面大色块区域不同增加多角度倾斜拍摄避免只看正面全脂牛奶vs脱脂牛奶包装顶部色带颜色不同瓶身几乎一样保证框内包含顶部色带区域不裁掉大瓶可乐vs小瓶可乐高宽比不同但平拍时比例感弱同时入镜让模型学到相对尺度2.4 数据集划分防止货架“同源数据”泄漏进验证集划分比例我一般按训练集:验证集:测试集 7:1.5:1.5。但比比例更重要的是按拍摄场景切分。同一时刻、同一机位连续拍摄的帧如果一部分进训练集、一部分进验证集验证集分数会虚高模型实际泛化能力根本没有那么强。我按“拍摄时间地点”作为分组单位一组数据整体划入某一份集合这样验证结果才真实可信。3. 训练实战YOLOv8从默认参数跑到稳定收敛的全过程3.1 环境配置与数据集YAML组织方式训练前先把目录和依赖理顺。我用的Python 3.10PyTorch2.x以及YOLOv8官方训练库。项目的目录结构大致这样project/ ├── datasets/ │ ├── images/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ ├── labels/ │ │ ├── train/ │ │ ├── val/ │ │ └── test/ │ └── data.yaml ├── runs/ ├── models/ ├── train.py ├── infer.py └── ui/标注好的txt文件要和图片文件一一同名放到对应的labels目录下。然后写一个data.yaml内容如下path: ./datasets train: images/train val: images/val test: images/test nc: 20 names: 0: cola 1: soda 2: chips 3: milk # 后面按实际类别继续列训练时我习惯不用命令行的一长串参数而是把关键参数写进一个Python脚本便于版本迭代和记录。核心代码如下from ultralytics import YOLO model YOLO(yolov8n.pt) results model.train( datadatasets/data.yaml, epochs200, imgsz640, batch16, device0, optimizerAdamW, lr00.0005, patience30, mosaic1.0, close_mosaic10, ampTrue, )3.2 默认参数跑第一版结果哪里不对劲第一次训练我几乎全部用默认参数只改了数据路径和epochs。200轮跑完验证集mAP50到了0.72看着还不错。但把模型接到摄像头上跑真实货架问题立刻暴露货架中层的商品能识别最下层的小瓶装漏检严重两个商品叠在一起时置信度双双掉到0.3以下反光的铝罐包装频繁误检成别的类别。后来复盘发现问题不是模型结构而是数据分布和推理设置训练集里货架中层大尺寸样本太多小目标样本不足验证集mAP高是因为验证集里大目标占多数掩盖了小目标短板。最终的解决方向就是两个数据层面补充小目标样本推理层面调整输入分辨率和标定阈值。3.3 调参过程的实测记录下面这组参数是我在多次试验后稳定下来的直接做成表格方便对照。参数第一版值调优过程最终值imgsz640提升到960后小目标mAP涨了约4个点但显存和推理耗时翻倍最后取折中800epochs200用early stopping实际到155轮左右收敛300早停在合理范围batch16显存占用在16GB左右batch调到32会提示OOM16optimizerSGD换成AdamW后收敛更平稳loss曲线不振荡AdamWlr00.01AdamW配0.01偏高前20轮loss发散降到0.0005才稳0.0005mosaic默认1.0全程开mosaic容易在最后阶段震荡采用close_mosaic策略1.0close_mosaic0最后10轮关闭mosaic让模型在真实分布上稳定拟合10有一个很关键的细节别等训完再看曲线训练中要盯着val loss和mAP实时变化。我从第40轮开始每10轮跑一次验证发现mAP50-95在130轮附近开始原地抖动当时就意识到训练增益已经耗尽。用early stopping机制可以自动停但手动盯一下更容易发现数据或参数层面而不是单纯轮数层面的问题。3.4 透过指标看本质用混淆矩阵找到模型“脸盲”的类别只看mAP是远远不够的。超市客户关心的是“结算时不漏、不算错”。mAP50高但可能只是大类识别准真正容易混淆的细分类根本看不出来。我训练完第一版打印混淆矩阵发现某品牌苏打水和某品牌气泡水互相误检的样本数量占该类总样本的11%。这个信息靠mAP看不出来必须看类别层面的混淆矩阵才能发现。同时还要理解precision和recall的权衡。在收银场景漏检一件商品的损失比多框一个空框更严重所以我把推理置信度阈值从默认的0.5下调到0.35宁可多给几个候选框让员工肉眼复核也不要让商品静悄悄跳过结算。分类阈值调整之后实测漏检率下降误检率略有上升但这个方向在业务上是正确的。4. 从权重文件到可用系统导出、封装与UI展示层设计4.1 模型导出为ONNX推理速度提升多少训练好的pt文件不能直接丢给UI项目用一是慢二是依赖重型训练框架。我的做法是先导出为ONNX格式from ultralytics import YOLO model YOLO(datasets/weights/best.pt) model.export(formatonnx, dynamicTrue, imgsz800)导出后可以用ONNX Runtime加载推理CPU环境下帧率比原始PyTorch推理快不少。我之前在测试机上的对比值PyTorch推理一帧约160msONNX Runtime约90ms如果再用TensorRT INT8量化压到边缘盒子上可以跑到30ms以内。精度损失方面常规部署用FP32的ONNX基本看不出掉点INT8会掉2~3个mAP点商品细分场景不太建议激进量化。这里有一个容易踩的坑动态尺寸设置。UI画面输入尺寸和训练尺寸不一致时模型会自动缩放但如果动态轴没开启固定输入尺寸的ONNX遇到非640分辨率的视频流就会报错。我建议导出时把dynamicTrue打开代价是多约几十MB内存但对前端更友好。4.2 Python部署封装图片、单帧视频流统一走一个类推理封装我建议写成一个独立的类这样UI和后端接口都能复用。核心结构就是加载模型、接收图像、返回检测结果列表。class ShelfDetector: def __init__(self, model_path, conf_threshold0.35, iou_threshold0.45): self.model YOLO(model_path) self.conf conf_threshold self.iou iou_threshold def detect(self, frame): results self.model.predict( sourceframe, confself.conf, iouself.iou, verboseFalse ) dets [] for r in results: boxes r.boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() cls_id int(box.cls[0]) score float(box.conf[0]) dets.append({ box: [x1, y1, x2, y2], cls_id: cls_id, label: self.model.names[cls_id], score: score }) return dets实际项目里UI界面并不是直接调用model.predict()裸方法而是通过这个ShelfDetector类传入OpenCV读取的帧。这样换模型、调阈值只改一处UI层保持稳定。4.3 UI界面PyQt5桌面端和Gradio Web端怎么选UI是整个系统最容易做成“看起来像作业、用起来却鸡肋”的部分。我这次对比了三条路线分别试过之后给大家一个明确建议。方案优点缺点适合场景PyQt5/PySide6桌面端界面定制强、可嵌入收银软件、支持摄像头实时显示流畅开发量大、打包体积大正式商用收银系统、实验室演示Gradio Web界面几行代码出网页、有摄像头和图片上传组件、自带可视化定制性弱、不适合集成到现有业务系统快速demo、远程展示、毕设答辩Flask Vue前后端接口清晰、可对接现有收银中间件开发周期最长、需要前后端基础真正要跟门店ERP打通的生产系统如果你的目标是快速看到效果并在答辩或演示中不卡壳首选Gradio。我从封装好推理类到写出一个可交互界面只用了不到20行代码还自带摄像头输入组件。但如果你想做成一个能放在某便利店收银台旁边试用的桌面软件PyQt5是更合适的路线。UI界面重点展示四块内容实时视频画面、识别出的商品列表、每件商品的置信度、以及结算总价。总价的实现并不复杂在代码里维护一个“类别编号到单价”的映射字典按检测结果累加即可。4.4 真实场景的后处理置信度阈值、帧间平滑和提示音模型框出来一堆结果直接显示在界面上会闪烁得让人崩溃。同样一件商品前一帧置信度0.6后一帧变成0.3就导致画面上框忽隐忽现。我加了两层后处理一是帧间平滑连续5帧都识别出同一类别且框位置基本重叠才判定为“确认识别”并在界面上置为稳定状态二是短时窗口去抖某商品在5帧内消失又出现仍当作同一个商品处理不重复计价。接摄像头时还要加一个业务逻辑给每件商品设置“最少间隔时长”防止同框商品被反复触发累计价格。这套逻辑在真实结算场景里非常关键测试时多试几次就会发现没有去重逻辑的结算金额会翻好几倍。5. 部署后才知道的坑光照、遮挡、同款多规格与模型回炉5.1 冷白灯、暖黄灯、反光瓶一个都不能放过第一次把演示系统搬到另一个便利店发现识别率比我们自己测试环境掉了一截。排查之后定位到光照差异训练数据大多在标准白光下拍摄现场用的是暖黄光玻璃瓶反射出来的颜色完全变了。后来在训练阶段加了强力的HSV扰动增强把色调、饱和度、明度的随机变化幅度加大同时采集数据时有意识地覆盖暖光和冷光两种场景。部署时我还加了一个简单预处理把视频帧先做白平衡校正再送入模型这步用OpenCV的灰度世界算法就能实现成本很低收益明显。5.2 密集摆放、互相遮挡是小目标漏检的元凶货架上的商品不可能是整齐单排的。同类商品前后叠放、饮料瓶互相遮挡、收银台上的商品堆在一起这类情况如果训练集里不够多推理时就会漏框。我的建议是在训练集里专门加一组“密集遮挡模拟图”用程序把商品图按随机位置拼贴到大图上再打乱顺序。也可以用简单的图像拼接脚本批量生成带标注的合成样本注意合成图需要确保商品比例和真实场景接近不要为了增加数量而做出扭曲变形。这类合成样本作为补充可以但不要让它超过总数据量的20%否则模型会对合成背景产生依赖。5.3 同一SKU的多规格包装到底算一个类还是多个类做商品结算时最纠结的问题是分类粒度同一款饮料的500ml装、1L装、2L装如果算三个类模型要学三个几乎一样的外观如果算一个类结算时又不知道顾客拿的是大瓶还是小瓶价格没法算。我最后的方案是检测阶段按外观聚成“外观类”每个外观类内部维护一个价格映射表同时用边界框尺寸和宽高比作为辅助判据在识别逻辑层区分规格。比如500ml瓶子的框面积明显小于1L瓶就自动选择对应单价。这个后处理逻辑避开了硬让模型学习细微尺寸差异的难题毕竟训练模型区分“几乎一样的瓶子但大小不同”是非常吃力不讨好的。5.4 新包装上市后模型如何快速回炉超市商品换包装是家常便饭。同一个番茄酱三个月前红色盖子三个月后黄色盖子老模型直接失效。前期设计项目结构时就要预留迭代通道错检图片、新增包装图统一收集到扩展数据集文件夹定期做一轮增量训练。增量训练不是从头训一遍。我的做法是加载现有best.pt继续训练30~50轮学习率设为正常训练的一半并把新数据与旧数据按1:2的比例混合防止灾难性遗忘。这个策略在两次换包装场景实测中召回率都恢复到90%以上。再提一个项目习惯所有训练、推理脚本都要锁定固定随机种子和依赖版本。我第一次没有固定随机种子同一份数据重新训练mAP波动了将近两个点这会给后续排查问题造成很多困扰。把seed、Python版本、torch版本、训练库版本记录在项目的requirements或配置文件中是保证结果可复现的最基本要求。最后分享一点个人经验每次做这类检测系统我都不会一开始就追求“全品类覆盖”。先把20个高频商品类别从数据、训练、UI、结算整条链路跑通让客户或导师看到实物效果再根据反馈扩展。这样标注成本可控迭代方向明确。如果你正准备复现这个项目建议第一次训练时把batch降到8分辨率降到640先让流程分钟级跑通一遍再逐步增加复杂度——别一上来就追求极致精度先把工程链路跑通后面调参才有依据。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表