基于 YOLO11 的超市店铺偷窃行为智能识别系统
一套面向便利店 / 商超 / 连锁零售场景的实时偷窃行为视觉识别平台,使用 YOLO11n + Streamlit 构建,支持单图、批量、视频与摄像头多源接入,自动标记可疑行为并输出风险分析。
一、背景
1.1 行业痛点
零售门店因品类多、客流大、监控盲区分散,人工盯屏识别"商品藏匿、未结账离场"等行为效率低、漏检率高。传统视频监控只能事后回看取证,无法做到实时告警。本项目用计算机视觉目标检测的方式,对收银区、货架区、通道等关键区域的正常购物与可疑盗窃两类行为进行实时识别与风险分级,给店员和店长一个"看得见"的智能复核工具。
数据集采集自真实超市场景,规模 4000 张图像,覆盖货架区、收银区、通道等核心区域,包含光线变化、人员密度差异等真实情况,二分类标注(正常/盗窃),可有效缓解模型在真实零售环境中的过拟合问题。
视频演示与源码下载
https://www.bilibili.com/video/BV1HTph6EEtR/
包含:
📦完整项目源码
📦预训练模型权重
🗂️数据集
1.2 项目运行效果
Web 界面总览(深海蓝 + 警戒红的监控风视觉,侧栏聚合关键指标):
批量图片识别 + 行为分析图表:
1.3 项目介绍
创新点 / 亮点
| 维度 | 说明 |
|---|---|
| 告警类别可配 | configs/project.yaml中alert_classes决定哪些类别算"风险",无需改代码 |
| 风险分级 UI | 命中可疑行为时顶部 banner 自动变红,正常时变绿,便于一线门店人员快速识别 |
| 多源接入 | 同一界面切换"单图 / 批量 / 视频 / 摄像头",覆盖事后复核 + 实时巡店 |
| 中文模型类别 | 训练阶段直接使用['正常','盗窃']中文标签,前端零翻译展示 |
系统功能概览
- 单张图片识别:上传现场画面 → 输出标注图 + 目标明细 + 风险等级
- 批量图片识别:多张图片或 ZIP 压缩包批量推理,输出风险分布图表
- 视频识别:逐帧推理 + 时间线分布,导出标注视频与行为明细 CSV
- 摄像头实时检测:浏览器调用本机摄像头,单帧推理 + 风险标注
技术栈
| 层级 | 技术选型 |
|---|---|
| 检测模型 | YOLO11n(Ultralytics) |
| Web 框架 | Streamlit |
| 图像处理 | OpenCV、Pillow |
| 数据 / 图表 | Pandas、Plotly、Streamlit 原生图表 |
| 视频编解码 | imageio-ffmpeg |
| 部署 | 本地 Python 3.9 + macOS / Linux,支持容器化部署 |
类别定义:nc=2,names=['正常', '盗窃']。
二、设计框架
系统架构:浏览器 ↔ Streamlit 前端 ↔ 推理服务(YOLO11) ↔ 模型权重model/best.pt,配置由configs/project.yaml驱动业务话术、告警类别和 UI 主题色。
┌────────────────────────────────────────────────────────────┐ │ 用户浏览器 │ │ 单张图片 │ 批量图片 │ 视频 │ 摄像头 │ └──────────────────────┬─────────────────────────────────────┘ │ HTTP / WebSocket ┌──────────────────────▼─────────────────────────────────────┐ │ Streamlit App (app/streamlit_app.py) │ │ · 模式路由(st.radio) │ │ · 上传 / 预览 / 分析图表 │ │ · 风险等级 banner + 类别分布 │ └──────────────────────┬─────────────────────────────────────┘ │ model.predict() ┌──────────────────────▼─────────────────────────────────────┐ │ YOLO11n 推理引擎 │ │ Backbone (C2f + SPPF) → Neck (FPN + PAN) → Decoupled Head │ │ + DFL(分布式回归) → NMS │ └──────────────────────┬─────────────────────────────────────┘ │ result.boxes (cls, conf, xyxy) ┌──────────────────────▼─────────────────────────────────────┐ │ 后处理:apply_class_display + is_alert_class │ │ → 渲染标注图 + DataFrame 明细 + 风险分级 │ └────────────────────────────────────────────────────────────┘YOLO11 网络结构(与 v8 同源,更深更轻):
- Backbone:连续
Conv + C2f块下采样 5 次,末端SPPF聚合多尺度特征 - Neck:
FPN(自顶向下)+PAN(自底向上)双向融合 - Head:解耦头(分类 / 回归分离),回归侧使用DFL输出分布而非单值,小目标定位更稳
- 损失:BCE(分类)+ CIoU + DFL(回归)
检测流程:图像/帧 → 缩放至 imgsz=640 → 推理 → 置信度过滤(conf=0.35)→ NMS(iou=0.7)→ 类别映射 → 风险打标 → 前端渲染。
三、代码结构(核心 50 行 + 解析)
项目结构(精简版):
超市偷窃行为检测/ ├── app/streamlit_app.py # Streamlit Web 入口(含 4 种接入模式) ├── configs/project.yaml # 业务配置(标题、告警类别、UI 主题) ├── model/best.pt # 训练好的 YOLO11n 权重 ├── scripts/ # 训练 / 评测 / 导出脚本 ├── data/dataset/ # 数据集(train / valid / test) └── 超市店铺偷窃行为检测train_CH_yolo11n/train/ # 训练产物(指标、曲线)核心 50 行(app/streamlit_app.py关键片段,已删减无关 UI):
frompathlibimportPathimportpandasaspdimportstreamlitasstfromultralyticsimportYOLO PROJECT_ROOT=Path(__file__).resolve().parent.parent MODEL_PATH=PROJECT_ROOT/"model"/"best.pt"ALERT_CLASSES={"盗窃"}# 来源:project.yaml -> app.alert_classesDEFAULT_CONF=0.35# 来源:project.yaml -> inference.conf@st.cache_resource(show_spinner=False)defload_model(model_path:str)->YOLO:returnYOLO(model_path)# 一次加载、全局复用,避免每次推理重载权重definfer_image_path(model:YOLO,image_path:Path,conf:float):result=model.predict(str(image_path),conf=conf,verbose=False)[0]annotated=result.plot()# 自带画框 + 类别 + 置信度rows=[]ifresult.boxesisnotNone:forboxinresult.boxes:cls_id=int(box.cls.item())rows.append({"class_id":cls_id,"class_name":result.names[cls_id],"confidence":round(float(box.conf.item()),4),"x1":float(box.xyxy[0][0]),"y1":float(box.xyxy[0][1]),"x2":float(box.xyxy[0][2]),"y2":float(box.xyxy[0][3]),})returnannotated[:,:,::-1],pd.DataFrame(rows)# BGR -> RGBdefis_alert_class(name:str)->bool:returnnameinALERT_CLASSES# ---------------- Streamlit 主流程 ----------------st.set_page_config(page_title="超市店铺偷窃行为智能识别系统",layout="wide")model=load_model(str(MODEL_PATH))conf=st.sidebar.slider("置信度阈值",0.05,0.95,DEFAULT_CONF,0.05)mode=st.radio("接入源",["单张图片","批量图片","视频","摄像头"],horizontal=True)ifmode=="单张图片":uploaded=st.file_uploader("上传现场图片",type=["jpg","png","jpeg"])ifuploadedisnotNone:img_bytes=uploaded.getvalue()annotated,df=infer_image_path(model,Path(uploaded.name),conf)col1,col2=st.columns(2)col1.image(img_bytes,caption="现场画面",width="stretch")col2.image(annotated,caption="识别结果",width="stretch")alert_n=int(df["class_name"].map(is_alert_class).sum())ifnotdf.emptyelse0st.metric("可疑行为数量",alert_n,delta="需复核"ifalert_nelseNone)st.dataframe(df,width="stretch")逐段解析
@st.cache_resource装饰load_model:Streamlit 每次交互都会重跑脚本,缓存可避免反复YOLO(...)加载,单图响应从秒级降到毫秒级。model.predict(conf=conf, verbose=False)一行完成"预处理 + 推理 + NMS",返回Results对象,.plot()直接得带框图,省去手画 OpenCV 矩形的工作量。result.boxes遍历每个目标,把cls / conf / xyxy抽成 DataFrame,前端用st.dataframe展示明细,也便于后续导出 CSV / 入库。is_alert_class把"业务告警"与"模型类别"解耦:告警清单在configs/project.yaml里维护,新增风险类型时不用改代码。st.radio+if/elif路由四种接入模式,复用同一个model实例,单图 / 批量 / 视频 / 摄像头共享同一份推理逻辑。
四、训练效果
训练采用 YOLO11n 预训练权重微调,epochs=200, imgsz=640, batch=16, patience=50。最终关键指标:
| 指标 | 数值 |
|---|---|
| Precision (B) | 0.731 |
| Recall (B) | 0.852 |
| mAP@0.5 | 0.824 |
| mAP@0.5:0.95 | 0.630 |
说明:当前 mAP@0.5 ≈ 0.82,整体可用但仍在迭代中。下一阶段会通过以下方式继续提升:
- 扩充"盗窃"难样本:藏入袖口、推车遮挡、低光环境等长尾场景
- 引入跟踪(ByteTrack)抑制单帧抖动,把"可疑帧"聚合成"可疑事件"
- 升级到 YOLO11s/m 提升小目标识别精度,再用 INT8 量化平衡推理速度
由于本阶段 mAP 尚未达到放出 PR / 混淆矩阵的内部阈值(≥ 0.85),暂仅展示训练 loss / 指标随 epoch 变化的总览图。从曲线看,box / cls / dfl loss 平稳下降,P/R/mAP 在中后段趋于平台期,未出现过拟合震荡。
五、项目总结
做对了什么
- 业务配置全部抽到
project.yaml,告警类别、置信度、UI 主题、品牌话术可一键替换,一套代码切换不同零售场景。 - 类别名直接用中文训练,前端零翻译、风险 banner 与告警颜色(深海蓝 + 警戒红)符合监控审美。
- 推理函数返回
(annotated_image, DataFrame),单图 / 批量 / 视频 / 摄像头四个入口复用同一份核心,新增模式只改 UI。
还能怎么做
- 加入轻量目标跟踪(ByteTrack / BoT-SORT),把帧级检测升级为事件级告警,减少单帧误报对店员的打扰。
- 接入门店摄像头 RTSP 流,结合时间窗口聚合,对"长时间停留 + 商品消失"做组合规则。
- 模型蒸馏 / 量化:在边缘盒子(如 Jetson Orin Nano)上跑 INT8 YOLO11n,把响应时延压到 50ms 以内。
一句话总结
YOLO11n + Streamlit 给了零售业一套"开箱即用、配置驱动"的偷窃行为复核工具:训练数据替换 +project.yaml改两行,就能复用到便利店、商超、连锁店等多形态门店场景。