184 lines
9.2 KiB
Markdown
184 lines
9.2 KiB
Markdown
# 🍡 Dorayaki - Smart Supermarket Shelf AI & Inventory Recognition
|
|
|
|
Hệ thống nhận diện sản phẩm trên kệ siêu thị và quản lý tồn kho theo kiến trúc **client - server**: mô hình thị giác máy tính (YOLO + Local Feature Matching: SURF/SIFT, ORB, BRISK, Hybrid theo paper *Electronics 2023, 12, 3640*) chạy trong một **recognition server**, còn các client (web, thiết bị chụp ảnh) chỉ gửi khung hình và nhận kết quả.
|
|
|
|
Server phục vụ **2 chế độ**:
|
|
|
|
1. **Live (nhận diện thời gian thực)** - camera hoặc video gửi liên tục qua WebSocket, sản phẩm được phát hiện trên từng khung hình, theo dõi (tracking) xuyên khung hình, phân loại một lần cho mỗi sản phẩm và gắn nhãn ngay trên màn hình.
|
|
2. **Photo (ảnh tĩnh)** - chụp/upload/chọn ảnh kệ, nhận diện toàn bộ kệ trong một lần, bấm vào từng sản phẩm để xem chi tiết, tồn kho và điểm matching.
|
|
|
|
---
|
|
|
|
## 📋 Yêu cầu hệ thống (System Requirements)
|
|
|
|
- **Hệ điều hành:** Windows 10/11, macOS hoặc Linux.
|
|
- **Python:** 3.10 - 3.12 (khuyên dùng 3.11/3.12).
|
|
- **Phần cứng:** CPU 4 lõi trở lên, RAM 8GB. GPU không bắt buộc: trên máy M5 đo được CPU nhanh hơn MPS (28ms so với 112ms mỗi khung hình).
|
|
|
|
---
|
|
|
|
## 🚀 Cài đặt và chạy
|
|
|
|
### Bước 1: Chuẩn bị thư mục dự án
|
|
|
|
Cần có đầy đủ:
|
|
|
|
- `code/` - mã nguồn server, client web, client mẫu.
|
|
- `runs/` - trọng số YOLO đã train (`runs/detect/train/weights/best.pt`).
|
|
- `weights/dinov2_vits14_lvd142m.safetensors` - DINOv2 backbone for the identity gallery. Not in Git. See "DINOv2 weights" below.
|
|
- `stock/` - catalog `stock.csv` và ảnh mẫu sản phẩm `sku_001` -> `sku_022`.
|
|
- `add/` và `image.jpg` - ảnh kệ mẫu để test/demo.
|
|
- `requirements.txt`, `requirements-dev.txt`.
|
|
|
|
### Bước 2: Tạo môi trường ảo
|
|
|
|
```bash
|
|
conda create -n dorayaki python=3.11 -y
|
|
conda activate dorayaki
|
|
```
|
|
|
|
hoặc venv:
|
|
|
|
```bash
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
```
|
|
|
|
### Bước 3: Cài đặt thư viện
|
|
|
|
```bash
|
|
pip install -r requirements-dev.txt
|
|
```
|
|
|
|
### DINOv2 weights
|
|
|
|
The identity gallery embeds crops with DINOv2 ViT-S/14 (LVD-142M), Apache-2.0. It loads only a local file and does not contact Hugging Face, including when `YOLO_OFFLINE` is unset. Without the file, gallery identity fails and the server log says the weights are missing.
|
|
|
|
The pinned file is `model.safetensors` from `timm/vit_small_patch14_dinov2.lvd142m`, sha256 `04d27f3400d059fc0cfd7d17dd1909a75bf3ea8fb3eeb48b97cb99e57ee20081` (~84 MB). Copy a cache you already have; do not fetch one from an unknown URL:
|
|
|
|
```bash
|
|
mkdir -p weights
|
|
cp "$HOME/.cache/huggingface/hub/models--timm--vit_small_patch14_dinov2.lvd142m/snapshots/"*/model.safetensors \
|
|
weights/dinov2_vits14_lvd142m.safetensors
|
|
```
|
|
|
|
`DORAYAKI_DINOV2_WEIGHTS` overrides that path. A file with any other sha256 is refused.
|
|
|
|
With the gallery identity backend, enrolled views are embedded into an ignored
|
|
`stock-dir/.gallery_cache_<image_size>.npz` feature cache. The cache is reused
|
|
only when its key matches the preprocessing version, image size, pinned DINOv2
|
|
SHA-256, catalog CSV, enrolled SKU/view order and image bytes; its matrix shape,
|
|
numeric finite values and row identities are also checked. A stale or malformed
|
|
cache is rebuilt. A cache hit still checks the local model file's SHA-256, and
|
|
gallery startup refuses readiness if the pinned weights or usable gallery are
|
|
missing. OCR continues on the separate tag worker (`tag_executor`).
|
|
|
|
(`requirements-dev.txt` = `requirements.txt` + `httpx` cho test.)
|
|
|
|
### Bước 4: Khởi chạy recognition server
|
|
|
|
```bash
|
|
python code/app.py --port 8080
|
|
```
|
|
|
|
Script có sẵn: `run_app.bat` (Windows) hoặc `./run_app.sh` (Linux/macOS).
|
|
|
|
Các tham số thường dùng:
|
|
|
|
| Tham số | Mặc định | Ý nghĩa |
|
|
| --- | --- | --- |
|
|
| `--host` | `127.0.0.1` | Đổi thành `0.0.0.0` để mở cho máy khác trong LAN (nên đặt `DORAYAKI_API_TOKEN`). |
|
|
| `--device` | `cpu` | `cpu`, `mps`, `cuda`, `cuda:0`. |
|
|
| `--imgsz` | `640` | Kích thước đầu vào của detector. |
|
|
| `--live-method` | `hybrid` | Thuật toán mặc định cho chế độ live. |
|
|
| `--max-sessions` | `4` | Số client live đồng thời. |
|
|
| `--ssl-certfile` / `--ssl-keyfile` | - | Bật HTTPS (cần cho camera trên điện thoại). |
|
|
|
|
### Bước 5: Mở ứng dụng
|
|
|
|
Mở trình duyệt: **http://127.0.0.1:8080** (hoặc `http://127.0.0.1:8080`)
|
|
|
|
- Tab **Live**: chọn camera (hoặc "Video file…") rồi bấm **Start**. Sản phẩm hiện khung xanh kèm tên và giá khi đã nhận diện chắc chắn; khung vàng "要確認" là trường hợp chưa chắc chắn.
|
|
- Tab **Photo**: chọn ảnh mẫu, chụp ảnh hoặc upload ảnh kệ. Nhấn vào sản phẩm để xem chi tiết và bấm **Show matching** để xem các điểm matching.
|
|
|
|
Camera trên điện thoại chỉ hoạt động khi trang mở bằng **https** hoặc **localhost** (yêu cầu bảo mật của trình duyệt).
|
|
|
|
---
|
|
|
|
## 🔌 API (dùng cho client khác)
|
|
|
|
Server có REST + WebSocket đầy đủ, tài liệu chi tiết trong [`docs/API.md`](docs/API.md). Swagger UI: **http://127.0.0.1:8080/docs**.
|
|
|
|
Live WebSocket protocol `1` also supports an optional full-resolution crop lane:
|
|
clients advertise `capabilities`, then answer per-track `crop_request` messages
|
|
from their buffered capture. Clients without the capability are unchanged; wire
|
|
examples, byte limits, expiry and errors are in [`docs/API.md`](docs/API.md#websocket-v1stream).
|
|
|
|
```bash
|
|
# Nhận diện một ảnh kệ
|
|
curl -X POST --data-binary @image.jpg -H 'Content-Type: image/jpeg' \
|
|
'http://127.0.0.1:8080/v1/recognize?classify=true'
|
|
|
|
# Client live mẫu (camera, video, hoặc pan qua ảnh tĩnh)
|
|
python code/stream_client.py --source image.jpg --pan 12 --seconds 20
|
|
python code/stream_client.py --source 0 # webcam
|
|
```
|
|
|
|
Bất kỳ client nào gửi được ảnh JPEG đều dùng được: điện thoại, iPad, camera gắn xe đẩy, camera đeo cổ, robot. Giao thức không phụ thuộc trình duyệt.
|
|
|
|
For the native app demo, `./run-demo-server.sh` uses `stock-lopia` by default,
|
|
matching its `image6` replay frame. Pass another stock directory explicitly to
|
|
change stores; the script writes ignored local app endpoints and a local token.
|
|
|
|
---
|
|
|
|
|
|
## 🛠️ Kiểm tra hệ thống (Tests)
|
|
|
|
From `recognization/`, after `pip install -r requirements.lock` (or `pip install -r requirements-dev.txt`):
|
|
|
|
```bash
|
|
YOLO_OFFLINE=1 YOLO_AUTOINSTALL=false python -m unittest discover -s code -p 'test_*.py' -v
|
|
```
|
|
|
|
That discovers every `code/test_*.py` module. `test_real_model` is included and skipped unless `DORAYAKI_REAL_MODEL=1` (it loads the YOLO weights). CI runs the same command from `.github/workflows/test.yml`.
|
|
|
|
```bash
|
|
YOLO_OFFLINE=1 python code/bench_engine.py # speed measurement, not part of the suite
|
|
```
|
|
|
|
---
|
|
|
|
## 🌟 Tính năng chính
|
|
|
|
1. **Nhận diện thời gian thực (Live)**: phát hiện sản phẩm trên từng khung hình, theo dõi xuyên khung hình, phân loại một lần cho mỗi sản phẩm, gắn nhãn tên/giá ngay trên khung hình; hiển thị số khung/giây và độ trễ.
|
|
2. **Phát hiện chỗ trống trên kệ (Out-of-Stock)**: khoanh đỏ các vị trí hết hàng kèm ước lượng số lượng thiếu; có bộ lọc ổn định để khung không nhấp nháy.
|
|
3. **Nhận diện ảnh tĩnh (Photo)**: nhận diện toàn bộ kệ trong một lần thay vì bấm từng sản phẩm.
|
|
4. **Phân loại đa thuật toán**: Hybrid (Paper), SURF/SIFT, ORB, BRISK - đổi được ngay trong lúc chạy.
|
|
5. **Trạng thái trung thực**: chỉ hiện tên sản phẩm khi kết quả được chấp nhận; trường hợp mơ hồ hiện "要確認" kèm các ứng viên, không đoán bừa.
|
|
6. **Tương tác chi tiết**: bấm vào sản phẩm để xem tên, giá, ngày nhập, hạn sử dụng, tồn kho và điểm matching.
|
|
7. **Đa client**: nhiều client kết nối đồng thời, mỗi client có session và tracking riêng.
|
|
8. **Đa ngôn ngữ**: Tiếng Anh và Tiếng Nhật.
|
|
9. **Gợi ý chất lượng ảnh**: cảnh báo lóa (glare), rung (blur), chụp quá xa.
|
|
|
|
---
|
|
|
|
## 📁 Cấu trúc mã nguồn
|
|
|
|
```
|
|
code/
|
|
app.py # server: REST /v1 + WebSocket /v1/stream + phục vụ web client
|
|
engine.py # RecognitionEngine: detector + matcher + catalog, thread-safe
|
|
live.py # LiveSession: tracking, phân loại nền, gắn nhãn, chất lượng ảnh
|
|
tracking.py # SessionTracker: ByteTrack với ID riêng cho từng session
|
|
detector.py # YOLO detector
|
|
feature_matcher.py # SURF/SIFT, ORB, BRISK, Hybrid
|
|
identity.py # accepted / ambiguous / unknown / rejected
|
|
empty_shelf_detector.py, inventory.py, pipeline.py, observe.py
|
|
stream_client.py # client mẫu (camera / video / pan ảnh tĩnh)
|
|
bench_engine.py # đo tốc độ
|
|
test_*.py # unittest modules (discover -s code -p 'test_*.py')
|
|
web/ # web client (index.html, styles.css, app.js)
|
|
docs/API.md # tài liệu API đầy đủ
|
|
docs/design-price-tags-and-compliance.md # thiết kế OCR giá, đối chiếu, compliance, mobile
|
|
```
|