product-recognition-demo/recognization
kiyo9w f74366d6a4 test: evaluate on 54 real store photos the pipeline has never seen
Everything else here is measured on two photographs. This collects real store
shelves from Wikimedia Commons (scriptable API, explicit licences: CC0, CC BY,
CC BY-SA, public domain, each recorded in a manifest) and evaluates the pipeline on
them. 46 of 54 images carry a shelf; the rest are book scans and a shop front.

What it found, and it is not what I expected:

- **Detection transfers.** 2,258 detections across dairy cases, soy-sauce shelves,
  spice racks, a confectionery aisle, an Okinawan supermarket and a Hong Kong
  grocery - tight boxes on images the model has never seen, and correctly nothing on
  the book scans.
- **Identity transfers when the product is genuinely there.** On a 2011 milk-case
  photo it labelled six facings of 明治 おいしい牛乳 correctly, and 野菜生活100 and
  R-1 elsewhere. The matcher does reach new photographs of products it has templates
  for.
- **Precision is the failure, and rejection is missing.** In that same photograph it
  labelled 低脂肪殺菌牛乳, 有機豆乳, 特濃4.5, 無調整豆乳 and 北海道牛乳 as おいしい牛乳 -
  they are all white milk cartons. Across 24 shelves whose products are not in the
  catalog it accepted 451 identities, and the score cannot be thresholded to fix it:
  the real shelf's median is 18.2 with a p10 of 5.2, while the foreign shelves' median
  is 6.0 and the worst offenders score 50.5 (a bento), 80.6 (a shop interior) and
  189.9 (deer cookies). A threshold that catches the foreigners also throws away the
  real matches.

That confirms on real data what the literature and the review predicted: the
matcher's raw inlier score is not a confidence, and fine-grained look-alikes are the
failure mode. Step 4 of the plan - shortlist, local re-rank, calibrated rejection -
is the fix, not a refinement.

docs/EXTERNAL-EVAL.md records the numbers, the method and the attribution; the
images themselves are not redistributed. 175 tests pass.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-25 02:21:00 +09:00
..
add Initial commit for push folder 2026-09-18 09:13:53 +09:00
code test: evaluate on 54 real store photos the pipeline has never seen 2026-09-25 02:21:00 +09:00
docs test: evaluate on 54 real store photos the pipeline has never seen 2026-09-25 02:21:00 +09:00
runs/detect/train Initial commit for push folder 2026-09-18 09:13:53 +09:00
stock update stock file 2026-09-18 09:59:06 +09:00
stock-lopia feat: a multi-view embedding gallery, because one template per SKU cannot work 2026-09-24 23:43:27 +09:00
.gitignore Initial commit for push folder 2026-09-18 09:13:53 +09:00
DEMO.md chore: the film-making shortcut leaves the server, and failures stop hiding 2026-09-25 01:30:16 +09:00
detected_empty_slots.jpg Initial commit for push folder 2026-09-18 09:13:53 +09:00
detected_image.jpg Initial commit for push folder 2026-09-18 09:13:53 +09:00
image.jpg Initial commit for push folder 2026-09-18 09:13:53 +09:00
README.md docs: point API and README at the price-tag design 2026-09-24 13:46:02 +09:00
requirements-dev.txt refactor: make the matcher and detector safe for concurrent clients 2026-09-24 11:43:24 +09:00
requirements.txt feat: reconciliation and the price-tag compliance rule family 2026-09-24 13:54:00 +09:00
run_app.bat docs: API contract, README for the server and live mode, real-model test 2026-09-24 12:46:54 +09:00
run_app.sh docs: API contract, README for the server and live mode, real-model test 2026-09-24 12:46:54 +09:00
supermarket_diagram.png Initial commit for push folder 2026-09-18 09:13:53 +09:00
test_empty_slots.jpg Initial commit for push folder 2026-09-18 09:13:53 +09:00

🍡 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).
  • 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

conda create -n dorayaki python=3.11 -y
conda activate dorayaki

hoặc venv:

python3 -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

Bước 3: Cài đặt thư viện

pip install -r requirements-dev.txt

(requirements-dev.txt = requirements.txt + httpx cho test.)

Bước 4: Khởi chạy recognition server

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. Swagger UI: http://127.0.0.1:8080/docs.

# 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.


🛠️ Kiểm tra hệ thống (Tests)

cd code
YOLO_OFFLINE=1 ../.venv/bin/python -m unittest test_observe test_engine test_tracking test_live test_api -v

# Test với model thật (tải trọng số, chạy ~15s)
DORAYAKI_REAL_MODEL=1 YOLO_OFFLINE=1 ../.venv/bin/python -m unittest test_real_model -v

cd ..
YOLO_OFFLINE=1 .venv/bin/python code/test_suite.py        # bộ test gốc
.venv/bin/python code/bench_engine.py                     # đo tốc độ detect/classify

Lưu ý: chạy test bằng danh sách module cụ thể, không dùng unittest discover vì sẽ import test.py/fast_test.py (script scratch với đường dẫn máy khác và cửa sổ OpenCV chặn).


🌟 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 độ
  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