Arquitectura del Pipeline
1
Captura + Pre-procesamiento
Frame 1080p de cámara IP → resize a 640×640 → normalización [0,1] → batch de 4 imágenes simultáneas.
2
Detección de Objetos — YOLO v11m
Localiza y cuenta botellas, paquetes, frutas y huecos vacíos. Fine-tuning sobre dataset FreshMart con 8.400 imágenes anotadas.
3
Segmentación de Instancias — SAM
Para cada bbox detectado, SAM genera la máscara de píxeles exacta → calcula % de espacio vacío en el lineal.
4
Clasificación de Referencia — EfficientNet V2
Recorta cada producto detectado y clasifica la referencia exacta (manzana roja Royal Gala / Fuji, zumo Granini naranja / manzana…).
5
Alerta + Dashboard en tiempo real
Si stock de un SKU cae bajo umbral → alerta Slack al responsable de tienda. Resultados en dashboard Grafana + InfluxDB.
Tarea A — Detección con YOLO v11
from ultralytics import YOLO import json # Cargar modelo fine-tuned FreshMart (exportado a TensorRT) model = YOLO("freshmart_yolo11m.engine") # TRT FP16 def analizar_lineal(imagen_path: str) -> dict: results = model(imagen_path, conf=0.55, iou=0.45)[0] conteo = {} for box in results.boxes: label = model.names[int(box.cls[0])] conf = float(box.conf[0]) conteo[label] = conteo.get(label, 0) + 1 # Guardar imagen anotada results.save(f"output/{imagen_path.stem}_anotada.jpg") return {"sku_count": conteo, "total": sum(conteo.values())} # Resultado ejemplo: # {"sku_count": {"botella_agua_1L": 12, "zumo_naranja": 8, # "manzana_roja": 24, "hueco_vacio": 3}, "total": 47}
Tarea B — Segmentación con SAM
import numpy as np from segment_anything import sam_model_registry, SamPredictor sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h.pth") sam.to(device="cuda") predictor = SamPredictor(sam) def medir_espacio_lineal(imagen_np, bboxes_yolo) -> float: """Retorna % píxeles vacíos en el lineal.""" predictor.set_image(imagen_np) area_total = imagen_np.shape[0] * imagen_np.shape[1] area_ocupada = 0 for bbox in bboxes_yolo: cx, cy = int((bbox[0]+bbox[2])/2), int((bbox[1]+bbox[3])/2) masks, _, _ = predictor.predict( point_coords=np.array([[cx, cy]]), point_labels=np.array([1]), multimask_output=False ) area_ocupada += masks[0].sum() return round(100 * (1 - area_ocupada / area_total), 1) # Ejemplo: 22.4% del lineal está vacío → alerta reposición
Resultado de Detección (ejemplo)
📸 lineal_camara_03_tienda07.jpg · 1080×1080 → 640×640
Estante 1
97%💧agua 1L
95%💧agua 1L
93%💧agua 1L
89%🍊zumo
91%🍊zumo
87%🥛leche
92%🥛leche
⚠ vacío
⚠ vacío
Estante 2
94%🍎manz. roja
96%🍎manz. roja
88%🍎manz. roja
91%🍏manz. verde
85%🍏manz. verde
90%🍋limón
92%🍋limón
87%🍋limón
■ Detectado OK
■ Hueco vacío
Conf umbral: 0.55 · Total detectado: 47 uds.
⚠ Alerta: Estante 1 · 22.4% vacío detectado → Notificando responsable tienda 07 via Slack
Benchmark — Jetson Orin (TRT FP16)
| Modelo | mAP@0.5 | Latencia | FPS | |
|---|---|---|---|---|
|
YOLOv11n
nano · TRT FP16
|
87.1%
|
48 ms | 20.8 | |
|
YOLOv11m
medium · TRT FP16
|
94.2%
|
127 ms | 7.9 | ★ ELEGIDO |
|
YOLOv11l
large · TRT FP16
|
96.8%
|
342 ms | 2.9 | |
|
YOLOv11m
medium · PyTorch FP32
|
94.2%
|
483 ms | 2.1 | sin TRT |
★ YOLOv11m con TRT FP16 ofrece el mejor balance precisión/latencia. 3.8× más rápido que PyTorch FP32.
Dataset YAML — Fine-tuning FreshMart
freshmart_dataset.yaml
# FreshMart — Dataset de detección de stock en lineales path: ./datasets/freshmart train: images/train # 6.720 imágenes val: images/val # 1.680 imágenes nc: 8 # número de clases (MVP — expandible a 32) names: 0: agua_1L 1: zumo_naranja 2: zumo_manzana 3: leche_entera 4: manzana_roja 5: manzana_verde 6: limon 7: hueco_vacio augmentation: hsv_h: 0.015 # variación de tono (iluminación tienda) hsv_s: 0.7 flipud: 0.0 # sin flip vertical (productos en pie) mosaic: 1.0 # mosaic 4x muy útil para lineales
Impacto esperado en producción
3h
Ahorro diario de trabajo manual por tienda (conteo de stock)
54h
Ahorro semanal en las 18 tiendas FreshMart
8.400
Imágenes en el dataset de fine-tuning (anotadas con Roboflow)
<500ms
Latencia total del pipeline (detectar + segmentar + clasificar)