|
|
||
|---|---|---|
| .idea | ||
| camera | ||
| central_server | ||
| core | ||
| dao | ||
| docs | ||
| models | ||
| service | ||
| tools | ||
| ui | ||
| .gitignore | ||
| README.md | ||
| best.pt | ||
| hik_camera_app.py | ||
| requirements.txt | ||
| start_main.vbs | ||
| start_usb.bat | ||
| usb_camera_app.py | ||
| 菜品识别.bat | ||
README.md
Dish_Recognition 菜品识别
食堂场景下的菜品识别客户端:YOLO 检测餐盘区域 → ConvNeXt 特征匹配认菜 → 结果上报业务 API。
支持海康工业相机 / USB 相机,模板库使用 MySQL 总库 + SQLite 本地缓存。
功能概览
- 拍照或选图识别,多盘自动裁剪
- 菜品模板学习 / 删除 / 阈值调节
- 未知菜即时确认学习,或进入待学习库稍后处理
- 识别结果上报(默认本地 mock,可切真实 API)
- 多基地模板同步:本地读 SQLite,变更写入 MySQL 后再拉取刷新;支持手动「立即同步」、上传队列查看与失败重试
目录结构
Dish_Recognition/
├── hik_camera_app.py # 入口:海康相机
├── usb_camera_app.py # 入口:USB 相机
├── best.pt # YOLO 餐盘检测权重(需放在项目根目录)
│
├── ui/ # 界面
│ ├── classifier_ui.py # 公共主界面(三栏:模板库 / 主区 / 结果栏)
│ ├── pending_learning_ui.py # 待学习库 / 新菜确认弹窗
│ ├── widgets.py # 自绘组件:圆角卡片、胶囊按钮、徽章、Toast 等
│ └── theme.py # 设计令牌:COLORS / FONTS / RADIUS / SPACING
│
├── core/ # 识别核心
│ ├── common_classifier.py # ConvNeXt 特征提取与模板匹配
│ ├── detect_plate_process.py# YOLO 检盘与标注可视化
│ └── pending_learning.py # 待学习持久化
│
├── camera/ # 海康相机封装 + MvImport SDK
├── central_server/ # 总服务器中心 API(单独拷贝部署)
├── service/ # 工位机上报客户端等
├── dao/ # 数据库连接、模板仓储、同步
├── models/ # 表模型
├── tools/ # 杂项脚本 + UI 冒烟测试
│
├── api_mock_results/ # mock 模式下的识别上报 JSON
├── media_store/ # 工位机 mock 时的备用目录(可选)
├── cutting/ # 裁剪的餐盘图
└── detect/ # 带标注的识别预览图
根目录只保留两个启动入口;业务代码按模块分目录。
环境要求
- Windows + Python 3.10+(推荐)
- 海康入口需安装 MVS 运行库,默认 DLL:
C:\Program Files (x86)\Common Files\MVS\Runtime\Win64_x64\MvCameraControl.dll - 项目根目录放置
best.pt
安装依赖
pip install -r requirements.txt
依赖清单见根目录
requirements.txt(识别 / 数据库 / HTTP;海康 MVS 运行库需单独安装)。
快速启动
在项目根目录执行:
# 海康相机
python hik_camera_app.py
# USB 相机(无海康时可直接用)
python usb_camera_app.py
无相机时也可在界面中「选择识别图片」做离线识别。
UI 冒烟测试(改界面后建议先跑)
改动了 ui/ 下的组件或布局后,可以先跑一遍冒烟测试:它会真实实例化全部自绘组件、
主窗口三栏布局与全部弹窗,捕捉 Tk 运行时的参数错误,并校验旧版纯 ttk 宿主
(core.common_classifier.BaseClassifierUI)仍能复用同一套 Mixin。
python tools/ui_smoke_test.py
脚本会自动为缺失的重依赖(torch / torchvision / ultralytics)打桩,
因此不装 YOLO 与深度学习框架也能运行。全部通过时输出 全部通过,
失败时会打印具体组件名与完整堆栈。
主流程
拍照 / 选图
→ YOLO 检测餐盘并裁剪
→ 与本地 SQLite 模板特征比对
→ 达阈值:输出菜名;未达:新菜确认弹窗 / 待学习库
→ 结构化结果上报 API(mock 或真实)
学新菜 / 删模板时:优先写 MySQL → 再拉回 SQLite;离线则写入本地 upload_queue,联网后补传。
数据库说明
| 层级 | 用途 | 主要表 |
|---|---|---|
| MySQL(总服务器) | 权威数据,多基地共享 | dish_categories、global_templates、bases、app_settings 等 |
| SQLite(本地) | 识别缓存,默认 dao/dish_templates.db |
同类镜像 + upload_queue |
- 对比认菜只依赖
feature_blob向量,不依赖本机图片路径。 image_url/thumbnail_url若存绝对路径,本机可预览;跨基地展示需改为可访问 URL。- 旧版
~/ImageClassifierTemplates/templates_data.json会在首次启动时自动迁入数据库(备份为.bak)。
常用环境变量(数据库)
# MySQL
set DISH_DB_NAME=goods
set CARC_CRAWLER_DB_HOST=127.0.0.1
set CARC_CRAWLER_DB_PORT=3306
set CARC_CRAWLER_DB_USER=root
set CARC_CRAWLER_DB_PASSWORD=123456
# SQLite 路径(可选)
set DISH_SQLITE_PATH=D:\path\to\dish_templates.db
# 本机基地(学菜写入 source_base_id;结果 API 默认也用此编码)
# 建议填写管理系统返回的 code,例如:上海路基地-1788059884828-e55vu
set DISH_BASE_CODE=BASE_01
set DISH_BASE_NAME=一号食堂
set DISH_BASE_REGION=上海
# 管理系统基地同步(GET + X-API-Key)
set DISH_MGMT_API_URL=https://cpsb.ruimeida.cc/api/public/bases
set DISH_MGMT_API_KEY=dish_a4f7a1287198179fdc6a6571aa14064c2afc6338c6730e2b668ac5be15bdf769
set DISH_MGMT_SYNC_ON_START=1
# 当天排餐(启动拉取,界面不展示;识别比对不在菜单→待学习)
set DISH_MGMT_MEAL_ON_START=1
set DISH_BASE_NAME=上海路基地
# 设备状态定时上报
set DISH_DEVICE_CODE=SETTLE_01
set DISH_DEVICE_NAME=一号结算机
set DISH_DEVICE_STATUS_INTERVAL=60
set DISH_DEVICE_STATUS_ENABLED=1
# 识别结果上报到管理系统(默认已指向此地址)
set DISH_RESULT_API_MOCK=0
set DISH_RESULT_API_URL=https://cpsb.ruimeida.cc/api/public/recognition-orders
# 图片上传仍可用本机 central_server(管理系统公开接口一般不提供 upload)
set DISH_MEDIA_UPLOAD_URL=http://127.0.0.1:9000/api/dish/media/upload
# 同步
set DISH_SYNC_ON_START=1
set DISH_SYNC_INTERVAL=300
每个客户端设置不同的 DISH_BASE_CODE(对齐管理系统 code)。启动时会:
- 用
X-API-Key拉取管理系统基地列表并 upsert 到 MySQLbases - 按
DISH_BASE_CODE查找本机基地(找不到才本地注册) - 学到的模板带上对应
source_base_id;模板同步时镜像全部基地并刷新心跳
详见 docs/第三方接口文档.md。界面状态栏显示基地编码与总库在线情况。
同步体验(界面)
- 顶栏基地徽章旁有 同步 入口;左侧有 立即同步 / 同步详情
- 立即同步:上传本地队列(离线学菜/删菜)→ 从 MySQL 全量刷新 SQLite
- 同步详情:查看待上传 / 失败队列,可 重试失败
- 徽章文案示例:
BASE_01 · 在线 16:20:03 · 队2/败1(有积压时显示) - 后台定时同步也会顺带 flush 上传队列(不再只 pull);默认每 300 秒(可用环境变量
DISH_SYNC_INTERVAL调整)
手动迁移旧 JSON(可选):
python dao/migrate_json_templates.py
识别记录
每次选图/拍照识别(含上报)会写入表 recognition_records:
- 本地 SQLite 必写(
dao/dish_templates.db) - MySQL 可用时同步写入总库
- 字段含:
request_id、基地编码、原图/标注图、菜数/未知数、摘要、完整 dishes JSON
界面左侧「识别记录」可查看最近记录,双击看详情。
识别结果 API
工位机上报见 service/result_api.py。总服务器单独目录 central_server/(可整夹拷走部署)。
协议:先传图,再上报。 source_image / labeled_image / crop_image 必须是 HTTP URL。
默认发往菜品管理系统:POST https://cpsb.ruimeida.cc/api/public/dish/recognize/result(带 X-API-Key)。
设 DISH_RESULT_API_MOCK=1 时不发 HTTP,只写本地 media_store/ + api_mock_results/。
# 启动总服务器中心 API(推荐:进入独立目录)
cd central_server
python start.py
# 或项目根:python -m central_server
# 或双击 central_server/start.bat
# 模拟:只写本地 media_store + JSON
set DISH_RESULT_API_MOCK=1
# 对接菜品管理系统(默认)
set DISH_RESULT_API_MOCK=0
set DISH_RESULT_API_URL=https://cpsb.ruimeida.cc/api/public/recognition-orders
set DISH_MGMT_API_KEY=你的密钥
# 或对接本地中心服务
set DISH_RESULT_API_URL=http://127.0.0.1:9000/api/dish/recognize/result
# 可选;不设则由上报地址推导;指向管理系统时会回落到本机 upload
set DISH_MEDIA_UPLOAD_URL=http://127.0.0.1:9000/api/dish/media/upload
set DISH_MEDIA_PUBLIC_BASE=http://127.0.0.1:9000/media
set DISH_RESULT_BASE_ID=BASE_01
上报字段示例(仅已识别菜;未知菜只进待学习库):
{
"request_id": "REQ_...",
"base_id": "BASE_LOCAL_01",
"capture_time": "2026-09-08 09:22:41",
"source_image": "http://127.0.0.1:9000/media/BASE_LOCAL_01/REQ_.../source_xxx.jpg",
"labeled_image": "http://127.0.0.1:9000/media/BASE_LOCAL_01/REQ_.../labeled_xxx.jpg",
"dish_count": 1,
"dishes": [
{
"index": 1,
"name": "土豆丝",
"crop_image": "http://127.0.0.1:9000/media/BASE_LOCAL_01/REQ_.../crop_xxx.jpeg"
}
]
}
界面结构
主窗口为三栏布局,顶栏常驻状态与入口,底部为状态条:
顶栏 品牌区 │ 相机 / API / 基地 徽章 │ 同步 · 设置
左侧栏 模板库:搜索框 → 菜品卡片列表 → 待学习库 / 识别记录 / 立即同步
主区 识别控制(选图·拍照 + 阈值) → 模板学习 → 预览(自适应撑满)
结果栏 本次识别结果:逐盘菜名 + 相似度 + 状态标签
状态条 单行结果摘要(识别中显示旋转动效)
- 视觉令牌集中在
ui/theme.py,改配色 / 字号 / 圆角只需改这一处。 - 组件集中在
ui/widgets.py,新增了Tooltip、Toast、SearchBox、Chip、StatTile、EmptyState等;原有类的构造签名保持兼容。 - 鼠标滚轮由主界面统一接管,指针在哪个滚动区就滚哪个。
界面操作摘要
主界面为结算机布局:左已点清单、右预览+总价、底栏绿按钮。
| 操作 | 说明 |
|---|---|
| 拍照识别 / 选图识别 | 检盘 → 认菜 → 写入已点清单并上报 |
| 已点清单 | 缩略图 / 菜名 / 数量± / 单价 / 小计 / 删除 |
| 清空 | 清空本次已点(不影响模板库) |
| 设置 → 模板学习 / 模板库 | 学菜、删模板、待学习库、识别记录、同步 |
| 设置 → 识别与上报 | 阈值、API 模式、本地单价、同步入口 |
| 新菜确认弹窗 | 识别后当场命名学习 |
本地模板图片与待学习图片目录:~/ImageClassifierTemplates/(含 pending_images/、缩略图等)。
当前范围说明
- 业务目标:识别菜品并发送给 API;界面可展示本地单价与数量,不做真实支付结算。
- 跨基地模板图展示:总库建议存 URL;绝对路径仅适合本机调试预览。