# 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` ### 安装依赖 ```bash pip install -r requirements.txt ``` > 依赖清单见根目录 `requirements.txt`(识别 / 数据库 / HTTP;海康 MVS 运行库需单独安装)。 ## 快速启动 在项目根目录执行: ```bash # 海康相机 python hik_camera_app.py # USB 相机(无海康时可直接用) python usb_camera_app.py ``` 无相机时也可在界面中「选择识别图片」做离线识别。 ### UI 冒烟测试(改界面后建议先跑) 改动了 `ui/` 下的组件或布局后,可以先跑一遍冒烟测试:它会真实实例化全部自绘组件、 主窗口三栏布局与全部弹窗,捕捉 Tk 运行时的参数错误,并校验旧版纯 ttk 宿主 (`core.common_classifier.BaseClassifierUI`)仍能复用同一套 Mixin。 ```bash 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`)。 ### 常用环境变量(数据库) ```bash # 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`)。启动时会: 1. 用 `X-API-Key` 拉取管理系统基地列表并 upsert 到 MySQL `bases` 2. 按 `DISH_BASE_CODE` 查找本机基地(找不到才本地注册) 3. 学到的模板带上对应 `source_base_id`;模板同步时镜像全部基地并刷新心跳 详见 `docs/第三方接口文档.md`。界面状态栏显示基地编码与总库在线情况。 ### 同步体验(界面) - 顶栏基地徽章旁有 **同步** 入口;左侧有 **立即同步 / 同步详情** - **立即同步**:上传本地队列(离线学菜/删菜)→ 从 MySQL 全量刷新 SQLite - **同步详情**:查看待上传 / 失败队列,可 **重试失败** - 徽章文案示例:`BASE_01 · 在线 16:20:03 · 队2/败1`(有积压时显示) - 后台定时同步也会顺带 flush 上传队列(不再只 pull);默认每 **300 秒**(可用环境变量 `DISH_SYNC_INTERVAL` 调整) 手动迁移旧 JSON(可选): ```bash 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/`。 ```bash # 启动总服务器中心 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 ``` 上报字段示例(仅已识别菜;未知菜只进待学习库): ```json { "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;绝对路径仅适合本机调试预览。