Eduard Kharaev
Home · Projects

Retail Shelf Recognition

Production multimodal recognition of products on store shelves: GroundingDINO, Qwen-VL OCR, Qdrant retrieval, DINOv2 and ArcFace, deterministic fusion, guardrails and explicit unknowns.

Case study by Eduard Kharaev · Source on GitHub

Live pipeline output: detected packs, brand/SKU labels, price tags, and explicit unknown abstentions

Real shelf output: localized packs, brand/SKU labels, price-tag reads and explicit unknown when the evidence is not strong enough.


Status

This is production engineering with a pilot business rollout.

The engineering pipeline is live; the organizational rollout is still intentionally limited.


Problem

A shelf photo has to become trustworthy commercial data:

The difficult cases are not obvious detections. They are visually similar sibling products that differ only by weight, fat percentage, flavour, label detail or package format.

For this problem, a confident wrong own-vs-competitor decision is worse than returning unknown. The system is therefore designed around evidence, abstention and reproducible post-mortems, not maximum forced coverage.

There is also a non-model problem: retail capture happens under weak connectivity, app interruption and device restarts. A model pipeline is not production-ready if the field user cannot reliably create and synchronize the input data.


End-to-end production architecture

Native Android field terminal
  → Room-backed durable photo queue
  → network-aware WorkManager synchronization
  → ingest API
  → object storage + durable analysis queue
  → async analysis worker
  → GroundingDINO detection
  → Qwen2.5-VL OCR / package reading
  → Qwen3-Embedding-8B
  → Qdrant dense text retrieval
  → attribute-aware reranking
  → DINOv2 visual k-NN
  → fine-tuned ArcFace metric retrieval
  → deterministic signal fusion
  → evidence guardrails
  → SKU / brand / unknown
  → share-of-shelf / assortment / competitor analytics

The production AI stack is self-hosted on owned NVIDIA H200 infrastructure.

The LLM does not choose the final SKU. It extracts textual and package evidence; the final identity is selected by a deterministic decision layer that combines independent retrieval and visual signals.


Field product — offline-first Android terminal

The merchandising terminal is a native Kotlin / Jetpack Compose application built for actual store conditions rather than a thin camera screen.

Durable work before upload

Captured photos enter a Room-backed local queue with the shelf/display and capture context needed for delayed delivery. The field user can keep working before the network request or GPU analysis finishes.

The queue supports:

The recovery path is important: a mobile process can die mid-upload, so UPLOADING cannot be treated as an eternal terminal state.

Offline shelf registry

Later iterations moved more than the media queue offline. The application caches the shelf/display registry in Room and can fall back to the local copy when the API is unavailable. Cover images use disk caching and can be pre-warmed after connectivity returns.

Actions that require authoritative server state are explicitly guarded while offline rather than pretending every mutation succeeded locally.

Analysis vs planogram mode

The same field application distinguishes normal analysis capture from planogram work.

A merchandiser can move through:

store → shelf/display → capture → local queue → analysis

or:

store → shelf/display → planogram image → shelf-zone annotation → sync

The on-device shelf-zone editor supports drawing regions over an image, dragging existing regions and resizing them with edge/corner handles. This keeps structured correction close to the physical shelf, where the scene is easiest to understand.

Why this matters to the AI system

Capture, upload and inference are independent stages. Temporary store connectivity or a busy model server does not block the merchandiser from taking the next shelf photo.

→ Detailed offline field-terminal case study


Ingest & provenance

The ingest layer provides:

This separates field capture from GPU inference and preserves enough context for delayed processing and later post-mortems.

Analysis jobs run through a PostgreSQL queue using FOR UPDATE SKIP LOCKED, with up to 3 attempts and automatic recovery of jobs stuck for more than 30 minutes.


Detection

GroundingDINO localizes product regions before recognition. Earlier detector work also included closed-set / YOLO experiments and deduplication logic for contained, overlapping and shelf-band false-positive regions.

A separate detector track improved measured F1 from 0.68 → 0.91 on unseen shelf photos.

Detection is treated as one stage of the recognition system rather than the final business metric: a good box can still become an incorrect SKU, so downstream retrieval/evidence gates remain mandatory.


OCR / VLM evidence

Each product crop is read by self-hosted Qwen2.5-VL-72B-AWQ served through vLLM.

The VLM extracts evidence such as:

OCR/VLM output is evidence for retrieval and guardrails. It is not trusted as a final classifier.


Dense retrieval

Catalog representation

Each retrieval entry is represented as structured product text combining attributes such as:

brand + name + category + subcategory + flavour + fat% + weight + volume + package_type + visual_markers

Embeddings are produced by a self-hosted Qwen3-Embedding-8B service.

For each detected crop:

  1. OCR/VLM produces the available text evidence.
  2. The query is embedded with Qwen3-Embedding-8B.
  3. Qdrant performs cosine dense retrieval.
  4. Production retrieval depth is top-20.
  5. Reliable brand evidence can activate brand-filtered candidate narrowing.
  6. Candidates are reranked with structured attributes such as brand, weight, fat percentage, category and package evidence.

The embedding vector is 4096-dimensional and normalized.

The production vector collection contains 1,345 retrieval entries. The broader merchandising catalog contains approximately 1.5k own + competitor SKUs, so not every merchandising catalog record is necessarily represented identically in the vector collection.


Multimodal retrieval

Dense text retrieval is only one path.

In parallel, the production system uses:

The working galleries are on the order of tens of thousands of confirmed/reference crops (approximately 18.9k DINOv2 confirmed crops and 9.1k ArcFace references in the audited retrieval configuration).

A typical decision ladder can look like:

OCR brand + visual agreement
→ OCR brand + dense retrieval
→ strong dense retrieval
→ dense retrieval + independent visual support
→ visual retrieval
→ weak evidence
→ unknown

Every final prediction stores enough provenance — decision path, scores, thresholds and supporting signals — to reconstruct why the system made that decision.


Deterministic fusion and guardrails

The acceptance layer is deliberately conservative.

Examples of production guardrails include:

Low-evidence cases become unknown instead of being forced into a SKU.

That abstention is part of the product design: share-of-shelf and assortment numbers are only useful if the system is allowed to say that it does not know.


Evaluation as production architecture

Evaluation is not a notebook step performed after model training. It is part of the release path.

The system uses:

Candidate changes are promoted through:

off → shadow → active

This process has rejected rerankers and encoder replacements that looked promising locally but weakened controlled production evaluation.


Measured results

End-to-end

Retrieval / metric learning

Production telemetry


Business outputs

Recognition results are not the end product. They feed a merchandising dashboard in the Chaban2 platform:

Dashboard data is scoped by team: a manager sees only the stores of their team.

Human-in-the-loop improvement

Low-evidence crops go to an unknown inbox (new → reviewed → promoted / excluded) together with the crop, OCR text, visual candidates and a VLM suggestion. A reviewer assigns the product, and promoted crops are added to the confirmed visual gallery used for DINOv2 visual re-ranking (enabled by a feature flag). The system improves on its own failure cases without retraining a model.

Store compliance and monitoring

Not implemented: comparison against a reference planogram image, and business alerts such as "own share dropped in store N". Report export is also not in production.


What this is — and is not

This is a production retrieval-augmented recognition system embedded in a field workflow.

It is not a classic document-question-answering RAG application:

For this business problem, deterministic fusion and calibrated abstention provide stronger control than asking an LLM to make the final identity decision.


My role

For the broader AI Chaban2 platform I was Head of AI and Technical Owner / platform architect: I built the first production versions hands-on, then hired and led a team of 7 engineers who extended them.

For this merchandising subsystem I owned the technical architecture and production rollout and was hands-on in:

The broader commercial platform and mobile application were extended by the team I led; this repository focuses on this subsystem and the technical work I did on it personally.


Stack

Python · FastAPI · PyTorch · GroundingDINO · Qwen2.5-VL-72B-AWQ · Qwen3-Embedding-8B · Qdrant · DINOv2 ViT-L/14 · ArcFace · vLLM · PostgreSQL · MinIO / S3-compatible storage · Kotlin · Jetpack Compose · Room · WorkManager · Coil · Docker · systemd / cron · NVIDIA H200


Runnable examples

Production code and commercial data are private, but this repository includes small runnable examples of the decision-logic shape and evaluation discipline:

python3 examples/fusion_demo.py
python3 examples/evaluate.py

fusion_demo.py walks synthetic crops through priority-ordered fusion, including abstention paths. evaluate.py demonstrates precision/recall/F1, abstention rate and why naive random splits can inflate metrics on correlated shelf crops.


Deep dives


Author: Eduard Kharaev — GitHub profile · haraev87@gmail.com · Telegram @Edharaev

Hiring for applied AI, computer vision, RAG or LLM agents?
I'm based in Tbilisi and open to new roles. Email haraev87@gmail.com or message me on Telegram.