Shelf Monitoring and Product Recognition System

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
Shelf Monitoring and Product Recognition System
Complex
~1-2 weeks
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    848

System Development распознавания товаров на полках (Shelf Monitoring)

Shelf Monitoring — автоматический контроль выкладки товаров в рознице: наличие продуктов на полках (out-of-stock), соответствие планограмме, правильность ценников. По данным IHL Group, out-of-stock обходится ритейлу в $1 трлн в год глобально. Автоматизация инспекции полок сокращает реакцию на out-of-stock с часов до минут.

Детекция товаров на полке

from ultralytics import YOLO
import numpy as np
import cv2

class ShelfMonitoringSystem:
    def __init__(self, product_model_path: str,
                 planogram_path: str = None):
        self.detector = YOLO(product_model_path)
        self.planogram = self._load_planogram(planogram_path) if planogram_path else None

    def analyze_shelf_image(self, image: np.ndarray) -> dict:
        """Анализ одного фото полки"""
        # Детекция всех SKU
        detections = self.detector(image, conf=0.4, iou=0.5)

        detected_products = []
        for box in detections[0].boxes:
            sku = self.detector.model.names[int(box.cls)]
            x1, y1, x2, y2 = map(int, box.xyxy[0])
            detected_products.append({
                'sku': sku,
                'bbox': [x1, y1, x2, y2],
                'confidence': float(box.conf),
                'facings': 1  # каждый bounding box = 1 facing
            })

        # Агрегация по SKU
        sku_counts = {}
        for product in detected_products:
            sku = product['sku']
            if sku not in sku_counts:
                sku_counts[sku] = {'count': 0, 'positions': []}
            sku_counts[sku]['count'] += 1
            sku_counts[sku]['positions'].append(product['bbox'])

        result = {
            'detected_skus': sku_counts,
            'total_facings': len(detected_products),
        }

        # Сравнение с планограммой
        if self.planogram:
            result['planogram_compliance'] = self._check_planogram(
                sku_counts, self.planogram
            )
            result['out_of_stock'] = self._find_out_of_stock(
                sku_counts, self.planogram
            )

        return result

    def _find_out_of_stock(self, current: dict, planogram: dict) -> list:
        """Нахождение отсутствующих товаров"""
        missing = []
        for sku, expected_facings in planogram.items():
            current_facings = current.get(sku, {}).get('count', 0)
            if current_facings == 0:
                missing.append({'sku': sku, 'status': 'out_of_stock',
                                 'expected_facings': expected_facings})
            elif current_facings < expected_facings * 0.5:
                missing.append({'sku': sku, 'status': 'low_stock',
                                 'current': current_facings,
                                 'expected': expected_facings})
        return missing

Training модели на корпоративном каталоге

Для крупного ритейлера (5000+ SKU) — иерархическая классификация:

# Структура датасета: изображения кропов продуктов по папкам
# dataset/
#   train/
#     category_beverages/
#       sku_cola_1l/
#       sku_juice_orange/
#     category_dairy/
#       ...

from ultralytics import YOLO

model = YOLO('yolov8l.pt')
model.train(
    data='shelf_products.yaml',
    epochs=200,
    imgsz=640,
    batch=32,
    workers=8,
    optimizer='AdamW',
    lr0=1e-3,
    # Аугментации специфичные для полки
    degrees=5.0,        # небольшой поворот
    scale=0.3,          # изменение масштаба (разные расстояния до полки)
    fliplr=0.5,
    hsv_h=0.02,         # небольшое изменение цвета
    mosaic=1.0
)

Мобильное приложение для инспекторов

Сотрудник фотографирует полку через мобильное приложение, система мгновенно показывает:

  • Зелёная рамка: SKU в наличии, соответствует планограмме
  • Жёлтая: мало товара
  • Красная: отсутствует
class ShelfInspectionAPI:
    def __init__(self, monitor: ShelfMonitoringSystem):
        self.monitor = monitor

    def analyze_photo(self, image_bytes: bytes,
                       store_id: str,
                       shelf_id: str) -> dict:
        image = cv2.imdecode(
            np.frombuffer(image_bytes, np.uint8),
            cv2.IMREAD_COLOR
        )
        result = self.monitor.analyze_shelf_image(image)

        # Добавляем визуализацию
        annotated = self._annotate_image(image, result)

        return {
            'store_id': store_id,
            'shelf_id': shelf_id,
            'analysis': result,
            'annotated_image_base64': encode_image_b64(annotated)
        }

Integration с роботами для автоматической инспекции

Автономные роботы (Simbe Tally, Brain Corp) объезжают магазин и фотографируют полки. CV-система обрабатывает фото в реальном времени, передаёт задания на пополнение персоналу.

Метрика Значение
Точность распознавания SKU 88–95% (зависит от схожести продуктов)
Точность детекции out-of-stock 90–97%
Скорость анализа фото < 2 секунды
Точность compliance check 85–92%
Масштаб Срок
100–500 SKU, одна категория 5–7 недель
1000–5000 SKU, полный магазин 10–16 недель
Сеть + роботы + аналитика 18–28 недель