Receta: analítica de retail con ocupación y conteo de personas
Contá personas cruzando una puerta, medí el tiempo de permanencia en zonas, y empujalo todo a un nodo edge con GPU opcional que marca a casa por una sola conexión saliente. Las cuatro apps de visión, dimensionadas con honestidad.
Por qué analítica de retail
El dueño de un local quiere tres números: cuántas personas entraron, cuánto se quedaron donde están las promociones, y cuándo se llenó demasiado. StreamHub responde los tres con apps de visión que corren sobre tus propias cámaras — sin un proveedor de conteo de gente, sin suscripción por local. Y cuando un servidor no alcanza para tantas cámaras, empujás la inferencia a un nodo edge con GPU que se conecta hacia tu master sin puertos de entrada.
La arquitectura en palabras
Hay cuatro Studio Apps de visión, cada una un worker Python sobre el HLS de la app: occupancy (permanencia y sesiones por zona), counting (conteo de personas por cruce de línea), crowd (umbral de densidad) y demography (edad/género/emoción). Su geometría por cámara — las zonas y líneas — no está en la config del plugin; vive por sala en una tabla stream_settings que editás con un editor de polígonos/líneas dibujado sobre el reproductor en vivo. Las detecciones emiten eventos (count.in, occupancy.session_started, crowd.threshold, …) y caen en la base propia de cada app.
1. Instalar las apps que necesitás
export BASE="https://your-server.example.com/api/v1"
export TOKEN="sk_...."
curl -X POST $BASE/apps/store/plugins/counting/install -H "Authorization: Bearer $TOKEN"
curl -X POST $BASE/apps/store/plugins/occupancy/install -H "Authorization: Bearer $TOKEN"
curl -X PATCH $BASE/apps/store/plugins/counting -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"enabled": true, "config": {"device": "cpu", "fps": 3.5, "confidence": 0.35}}'
La config del manifiesto para occupancy, counting y crowd son solo tres campos: device (cpu por defecto, o cuda), fps (3.5), confidence (0.35). Demography agrega engine (local InsightFace por defecto, o openai/deepseek) y un ai_api_key.
2. Dibujar la línea y las zonas
La geometría es por sala. Definí una línea de conteo y una zona de ocupación por el endpoint de settings (o el editor visual en la pestaña de la app):
# una línea de conteo en la puerta
curl -X PUT $BASE/apps/store/plugins/counting/streams/entrance/settings \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"lines":[{"id":"door","x":0.5,"y":0.5,"length":0.6,"angle":0,"inside":"bottom"}],"capacity":50}'
# un polígono de permanencia en la zona de promo
curl -X PUT $BASE/apps/store/plugins/occupancy/streams/promo/settings \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"zones":[{"id":"z1","tag":"promo","detect":"personas","points":[[0.2,0.2],[0.8,0.2],[0.8,0.8],[0.2,0.8]],"alertLimit":15,"alertAfterSec":300}]}'
Los puntos están normalizados 0–1; un polígono necesita al menos tres; detect es personas o vehiculos.
3. Leer los números
Cada app expone un resumen, streams en vivo y un historial de eventos:
curl -s $BASE/apps/store/plugins/counting/overview -H "Authorization: Bearer $TOKEN"
curl -s "$BASE/apps/store/plugins/counting/events?limit=100" -H "Authorization: Bearer $TOKEN"
Counting emite count.in / count.out / count.occupancy; occupancy emite occupancy.session_started / _ended / alert; crowd emite crowd.count / crowd.threshold.
4. Opcional — empujar la inferencia a un nodo edge con GPU
Instalá una segunda máquina como nodo edge (--node-type edge). Mantiene una WebSocket saliente hacia el master en wss://your-server.example.com/api/v1/edge/ws, no abre ningún puerto de entrada, tira el HLS que necesita, y hace POST de los resultados de vuelta. Luego asignale un stream:
curl -s -X POST $BASE/apps/store/edge/assignments -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"pluginId":"occupancy","streamId":"promo","policy":"round_robin"}'
Una GPU es opcional pero transformadora: cualquier placa CUDA baja la inferencia a menos de ~10 ms/cuadro.
Dimensionamiento, con honestidad
La cadencia domina. En un Tesla T4 medido, occupancy (yolo11m + ByteTrack) corre ~77 FPS por proceso y la máquina se satura cerca de ~145 FPS agregados. Eso es aproximadamente 36 cámaras por T4 a 4 fps de muestreo (la cadencia de Vision One), bajando a ~9–10 a 15 fps. La VRAM rara vez es el límite — el planificador presupuesta (vram_free_mb − 500) / 200 streams, unos 68 en un T4 de 16 GB. Un host solo-CPU maneja un puñado de cámaras a bajo fps; una sola GPU maneja uno o dos locales.
Qué deberías ver
Las personas que cruzan la puerta incrementan count.in; el resumen de occupancy muestra el conteo en vivo y las sesiones de permanencia por zona; superar tu alertLimit durante alertAfterSec levanta una alerta de ocupación. En un nodo edge, los mismos eventos llegan al master por la conexión saliente.
Resolución de problemas
- Los conteos se ven duplicados o saltones — counting corre a 3.5 fps fijos a propósito (los trackers intercambian IDs cuando el intervalo de cuadros varía); no subas
fpsesperando más precisión. - Sin detecciones — confirmá que la sala tenga un stream en vivo y que hayas guardado la geometría para ese nombre de sala exacto.
- Nodo edge inactivo — verificá que se registró y hace heartbeat, y que el plugin esté instalado en el master con capacidad
worker.edge; las asignaciones solo apuntan a apps edge-capaces.
Cierre
El runtime de visión es open source (AGPL-3.0), así que podés correr cada modelo en hardware tuyo. Si necesitás un despliegue multi-local, un plan de dimensionamiento de GPU, o dashboards que atan el flujo de gente a las ventas, nuestro equipo hace la planificación de capacidad y el armado de la flota edge como servicio de consultoría.