MODELOS
Suscripción Unlimited Basic: Qwen 3.8 27B ilimitado por 14,95 €/mes. GLM + DeepSeek con 5.000 AI Credits compartidos. Sujeto a fair use.

Guía técnica · Visión artificial y previsión de demanda

Contar personasDe la cámara a la previsión con YOLO26, tracking y TimesFM 3.0 en QDivZero

Usa YOLO26 y el tracking de QDivZero para contar entradas y prever afluencia con TimesFM 3.0. Guía técnica con WebSocket, Python, cURL y capturas reales.

QDivZero Team20 min de lectura
Configura los modelosCómo funcionan los desplieguesLeer la guía ↓
Corzos recorriendo un sendero al amanecer con los títulos YOLO26 y TimesFM 3.0
Contenido de la guía13 secciones

Con YOLO26 puedes detectar personas en el vídeo de una cámara y seguir sus movimientos con ByteTrack en QDivZero. Un contador en Python registra los cruces del acceso y agrupa las entradas por hora. TimesFM 3.0 utiliza ese histórico para prever la afluencia de las próximas horas.

Usaremos un restaurante como caso práctico. Puedes adaptar el recorrido a una tienda, un gimnasio o cualquier local cuyas cámaras permitan observar los accesos. Cambiarán el encuadre, los horarios y las franjas de actividad que te interesa medir para planificar turnos o atender las horas punta.

El ejemplo incluye una imagen generada para probar la detección y ocho semanas de datos sintéticos para practicar la previsión. El histórico se genera por separado y no procede de entradas medidas en esa imagen. Puedes descargar ambos materiales y empezar sin disponer todavía de una cámara.

Qué aprenderás y qué necesitas

Seguirás los pasos desde la interfaz de QDivZero y desde Python hasta obtener una previsión de 24 horas que podrás exportar a CSV. Necesitarás lo siguiente según la parte que quieras probar:

  • Para explorar el ejemplo: Python 3.11 o posterior y los archivos descargables. Puedes leer los resultados guardados sin cuenta, GPU ni llamadas a la API.
  • Para ejecutar tus propias inferencias: una cuenta de QDivZero con capacidad de despliegue y una clave API si utilizas el código. Los modelos se ejecutan en la GPU del despliegue y tu ordenador envía las peticiones.
  • Para medir un negocio real: una cámara fija que observe el acceso, un proceso que envíe fotogramas y un histórico con intervalos regulares. El WebSocket de QDivZero mantiene el seguimiento con ByteTrack entre los fotogramas de cada conexión.

Si es tu primer despliegue, puedes consultar el inicio rápido de QDivZero. Sigue primero el ejemplo y sustituye después la imagen, el horario y los datos por los de tu negocio.

Qué vas a medir y cómo se conectan los modelos

El recorrido es vídeo → YOLO26 y ByteTrack en QDivZero → cruces del acceso → entradas por hora → TimesFM 3.0 en QDivZero. La plataforma ejecuta la detección, mantiene los trackers por sesión y sirve la previsión. El cliente del ejemplo captura fotogramas, interpreta los cruces y prepara los números que consume TimesFM.

PasoDónde se ejecutaResultado
Decodificar el vídeoNavegador o cliente PythonFotogramas JPEG/PNG
Detectar y seguir personasDespliegue YOLO de QDivZeroCajas, confianza y track_id opcional
Contar cruces y agrupar horasCódigo cliente descargableEventos y serie de entradas
Prever afluenciaDespliegue TimesFM de QDivZeroPrevisión y cuantiles
MétricaCómo se obtienePara qué sirve
Personas visiblesDetecciones de la clase person en un fotogramaRevisar si la cámara y el modelo funcionan
Entradas por horaCruces hacia el interior de una línea virtualMedir afluencia y preparar el histórico
Salidas por horaCruces de la misma línea hacia el exteriorComprobar el flujo y estimar ocupación
Ocupación estimadaOcupación inicial + entradas − salidasEntender cuánto se llena el local
Previsión de entradasHistórico agregado enviado a TimesFMAnticipar llegadas por franja
Cobertura de cámaraTiempo observado / duración de la franjaDetectar datos incompletos

Cinco personas visibles no significan cinco nuevas entradas. Una persona sentada puede aparecer en cientos de fotogramas. Tampoco puedes deducir las entradas de todo un día a partir de una fotografía.

El recuento incluye a quienes cruzan el acceso observado, también personal, repartidores y personas que vuelven a entrar. Por eso no equivale a clientes únicos, reservas o ventas. Define qué quieres medir y utiliza esa definición durante toda la evaluación.

Prepara la escena y los archivos de ejemplo

Descarga la imagen y el histórico para seguir el ejemplo. Cuando recojas datos propios, coloca la cámara de forma que puedas distinguir el sentido de los cruces del acceso.

Imagen sintética de cinco personas distribuidas junto a la entrada de un restaurante
Imagen generada para el tutorial con cinco personas visibles junto a la entrada de un restaurante. Ver galería

Descargar la imagen de prueba · Descargar el histórico sintético · Descargar la petición para TimesFM · Descargar el generador de datos · Descargar el ejemplo Python para exportar a CSV

El histórico contiene 1.344 observaciones horarias de 56 días completos, con picos de comida y cena, diferencias entre días y cierre los lunes. Cada observación incluye la hora UTC, la hora local, entradas, apertura y cobertura. Puedes reproducir estos datos ficticios con el generador descargable. Guarda los archivos en una misma carpeta y abre ahí la terminal para ejecutar los ejemplos.

Con una cámara real, limita el encuadre al acceso que necesitas medir y conserva únicamente los datos necesarios para tu análisis. El contador puede funcionar con identificadores temporales de seguimiento y métricas agregadas, sin identificar a las personas.

Despliega los modelos en QDivZero

En Despliegues → Catálogo de modelos, busca Ultralytics/YOLO26. Elige el checkpoint yolo26n.pt para una primera prueba de detección de objetos. Comprueba que sea un detector con la clase person, ya que la familia YOLO26 incluye modelos para distintas tareas y tamaños. La documentación de Ultralytics explica las variantes.

Revisión real de YOLO26 en QDivZero con el checkpoint yolo26n.pt seleccionado.
Comprueba el checkpoint de detección yolo26n.pt en la revisión del despliegue. Las variantes con el sufijo pose resuelven una tarea diferente. Ver galería

Continúa con la infraestructura compatible. Puedes utilizar la selección inteligente y fijar un precio máximo por hora, o seleccionar el pod manualmente. La capacidad y el precio dependen de la disponibilidad, así que revisa las opciones que muestra la plataforma al desplegar.

Asigna un nombre reconocible al despliegue. En este tutorial utilizamos guia-restaurante-yolo26n como nombre de servicio para la API. Para pruebas es útil activar Serverless con un tiempo de inactividad de 300 segundos. El límite de precio por hora controla qué ofertas puede elegir el planificador. El gasto total dependerá también del tiempo de uso.

Repite el proceso con google/timesfm-3.0-pytorch y el runtime timesfm3. Usaremos el nombre de servicio guia-restaurante-timesfm3. La pantalla de lanzamiento indica los requisitos estimados y el endpoint de cada tarea.

Revisión real del despliegue de TimesFM 3.0 en QDivZero.
Revisa modelo, runtime e infraestructura antes de desplegar. Los precios de la captura corresponden a esta prueba y no son una tarifa garantizada. Ver galería

Después de crear el despliegue, abre su fila y utiliza Estado del modelo → Iniciar modelo. Espera a que esté listo antes de interpretar una primera petición como una prueba de rendimiento. Un endpoint serverless también puede arrancar al recibir tráfico y tardar más en responder en esa primera llamada.

Revisa las condiciones de cada modelo antes de utilizarlo. YOLO26 se distribuye con opciones AGPL-3.0 y Enterprise. Los pesos descargables de TimesFM 3.0 tienen una licencia no comercial. Desplegarlo en QDivZero no concede permisos adicionales para un uso comercial.

Sube una imagen y comprueba las detecciones con YOLO26

Abre Desarrolladores, selecciona la tarea Detección de objetos y poses y elige tu endpoint YOLO26. En la pestaña Imagen, sube restaurant-example.png.

Empieza con estos parámetros y ajústalos después con escenas de tu cámara:

Campo de QDivZeroValor inicialQué comprobar
Umbral de confianza0.25Que detecte personas sin llenar la imagen de falsos positivos
Umbral de IoU0.7Su efecto depende del modo de inferencia del runtime
Número máximo de detecciones300Que no recorte una escena concurrida
Tamaño de imagen640Si las personas pequeñas necesitan mayor resolución
IDs de clase0En el detector COCO general corresponde a person

Pulsa Detectar objetos y compara cada caja con la imagen original. Anota las personas omitidas, las cajas duplicadas y los objetos detectados por error. Cambia una opción cada vez y repite con entradas vacías, grupos, contraluz y oclusiones.

La ejecución de este ejemplo devolvió cinco detecciones de la clase person, con puntuaciones de confianza entre el 80,4 % y el 90,5 %.

Resultado real de YOLO26n en QDivZero con cinco personas detectadas y sus cajas delimitadoras
Resultado de YOLO26n sobre la imagen de prueba, con cinco personas detectadas y sus cajas delimitadoras. Ver galería

Descargar el JSON original de las detecciones

Repite la petición desde tu aplicación

El botón Ver código muestra el contrato del endpoint seleccionado. La detección recibe una imagen mediante multipart/form-data en POST /v1/images/detections. Guarda la clave en tu servidor y sustituye el identificador de cuenta y el nombre de servicio por los tuyos.

1export QDIV0_API_KEY='TU_CLAVE_API'
2export QDIV0_ACCOUNT_ID='TU_ID_DE_CUENTA'
3
4curl --fail-with-body --silent --show-error \
5  'https://api.qdiv0.com/v1/images/detections' \
6  -H "Authorization: Bearer ${QDIV0_API_KEY}" \
7  -H "X-Account-Id: ${QDIV0_ACCOUNT_ID}" \
8  --form-string 'model=guia-restaurante-yolo26n' \
9  --form-string 'confidence=0.25' \
10  --form-string 'iou=0.7' \
11  --form-string 'max_detections=300' \
12  --form-string 'image_size=640' \
13  --form-string 'classes=0' \
14  -F 'image=@restaurant-example.png' \
15  --output detections.json
16

Esta llamada detecta objetos en una imagen y no conserva estado. Para vídeo, utiliza el WebSocket de detección con tracking: true: QDivZero mantiene el seguimiento entre los fotogramas de esa conexión y devuelve track_id en las detecciones confirmadas.

Activa el tracking de vídeo en QDivZero

Abre Desarrolladores → Detección de objetos y poses → Vídeo en directo y selecciona el mismo endpoint YOLO26. En Fuente, elige Archivo de vídeo para repetir una grabación, o Cámara para observar el acceso. Activa Seguir objetos antes de pulsar Iniciar. Mantén el filtro de clase 0 para el detector COCO del ejemplo.

La vista anotada muestra etiquetas como person #17. El número es un identificador temporal dentro de esa conexión. Algunas detecciones pueden no tener ID hasta que el tracker las confirme. Compara el recorrido de una persona entre varios fotogramas.

El navegador captura y envía fotogramas. En QDivZero, el runtime ejecuta YOLO26 y actualiza un tracker ByteTrack propio de esa conexión. Si hay varias réplicas, el gateway envía todos los fotogramas de la sesión a la primera réplica asignada para conservar la continuidad del seguimiento.

Cómo funciona el protocolo WebSocket

El botón Ver código genera ejemplos con el modelo, la cuenta y las opciones seleccionadas. Para integrarlo en un cliente Python de servidor:

  1. Abre wss://api.qdiv0.com/v1/images/detections/stream?model=guia-restaurante-yolo26n con la cabecera Authorization: Bearer TU_CLAVE_API y espera session.ready.
  2. Envía esta configuración y espera session.configured:
1{
2  "type": "session.configure",
3  "options": {
4    "tracking": true,
5    "classes": [0],
6    "confidence": 0.25,
7    "iou": 0.7,
8    "image_size": 640,
9    "max_detections": 300
10  }
11}
12
  1. Envía cada fotograma PNG/JPEG como un mensaje binario y espera su frame.result antes de enviar el siguiente. Conserva el mismo tamaño de imagen y el orden temporal.
  2. Usa result.detections[].track_id para asociar las posiciones de una persona. Si una detección aún no tiene ID, espera a que lo reciba antes de crear su trayectoria.

El cliente decodifica el MP4 y envía una imagen en cada mensaje binario. El navegador se autentica con su sesión de QDivZero, mientras que el cliente Python usa la clave API. QDivZero resuelve la cuenta y comprueba el acceso al modelo en cada fotograma.

Este es un ejemplo del formato de respuesta, no una ejecución adicional de esta guía:

1{
2  "type": "frame.result",
3  "frame_id": 1,
4  "processing_ms": 12,
5  "result": {
6    "model": "guia-restaurante-yolo26n",
7    "width": 1280,
8    "height": 720,
9    "detections": [{
10      "class_id": 0, "class_name": "person", "confidence": 0.91,
11      "track_id": 17,
12      "box": {"x1": 120, "y1": 40, "x2": 310, "y2": 680}
13    }]
14  }
15}
16

Las cajas usan píxeles del fotograma enviado. frame_id indica el orden de los mensajes. Para fechar los cruces, conserva el momento de captura o el tiempo del vídeo. La duración en processing_ms incluye la llamada de inferencia desde el gateway, el enrutamiento y la red hasta la instancia, además de la ejecución en GPU.

Cuándo se conserva un ID

SituaciónQué ocurre y cómo actuar
Misma conexión y configuraciónContinúan el tracker y la réplica
Cambio de opciones de inferenciaSe reinician los IDs y debes vaciar las trayectorias del contador. Un parche sin cambios los conserva
Desconexión y nueva conexiónCambia el espacio de identidades y debes iniciar una nueva sesión de conteo
Más de 90 s sin un fotograma completadoCaduca el estado. Reconecta y empieza trayectorias nuevas
Cambio de dimensiones o fallo de inferenciaSe produce un error sin recuperar automáticamente la continuidad

Utiliza una conexión por cámara y mantén constantes las dimensiones. Un track_id no identifica a una persona entre cámaras o días. La sesión WebSocket tiene un máximo de 30 minutos. Para una integración continua tendrás que renovarla y registrar las interrupciones del seguimiento.

Esta función requiere soporte de tracking en gateway, inferencia y runtime. Reinicia los despliegues YOLO existentes después de actualizar el launcher. Si un runtime antiguo no confirma la sesión, la petición falla y no puedes continuar el seguimiento.

Convierte las trayectorias en entradas por hora

La API devuelve posiciones e IDs. Para convertirlos en entradas y salidas, el cliente del ejemplo configura una línea virtual en el acceso, registra los cruces y los agrupa por hora.

En nuestro contador, el centro inferior de cada caja cruza una línea vertical. Izquierda → derecha es una entrada y el sentido inverso una salida. Una banda del 2 % del ancho reduce cambios por ruido junto a la línea. Esperamos observar el ID a ambos lados, ignoramos detecciones sin ID y permitimos salidas y reentradas. Tras más de un segundo del vídeo sin observar una trayectoria, dejamos de unir sus posiciones.

Ejecuta el cliente Python con una grabación

Descarga track_footfall.py y footfall_counter.py en la misma carpeta. Utiliza una grabación local con FPS constantes, cámara fija y cruces visibles.

Conserva QDIV0_API_KEY y QDIV0_ACCOUNT_ID de la primera petición y ejecuta:

1python3 -m venv .venv
2source .venv/bin/activate
3python -m pip install websocket-client opencv-python requests
4export QDIV0_DETECTION_MODEL='guia-restaurante-yolo26n'
5
6python track_footfall.py acceso.mp4 \
7  --recorded-at '2026-10-16T10:00:00+02:00' \
8  --line-x 0.5 --y-min 0.1 --y-max 0.9 \
9  --output camera-output
10

Sustituye acceso.mp4 por tu archivo e indica en --recorded-at la fecha real de su primer fotograma. Las coordenadas son proporciones entre 0 y 1. El ejemplo cuenta sobre una línea en la mitad del ancho, del 10 % al 90 % de la altura. El código procesa todos los fotogramas en orden y espera cada respuesta, conservando las horas de la grabación aunque la inferencia tarde más. Usa una carpeta de salida nueva en cada ejecución.

Este fragmento muestra cómo enviar los fotogramas y recoger las detecciones. Los archivos descargables incluyen también la autenticación, el tratamiento de errores, las marcas horarias, el cierre de recursos y la exportación:

1from footfall_counter import LineCounter
2
3counter = LineCounter(line_x=0.5, y_min=0.1, y_max=0.9)
4# ws ya ha confirmado session.configure con tracking=True.
5# encoded contiene un JPEG; source_seconds es el tiempo del vídeo.
6ws.send_binary(encoded.tobytes())
7event = receive(ws, "frame.result")
8for crossing in counter.observe(event["result"], source_seconds):
9    print(crossing["track_id"], crossing["direction"])
10

OpenCV se utiliza para decodificar el vídeo y codificar las imágenes que envías. No necesitas instalar Ultralytics ni descargar checkpoints en el cliente.

Archivos de salida y conexión con TimesFM

Archivo en camera-output/Contenido
tracking-results.jsonlRespuestas del stream, sesión local y tiempos del vídeo
crossings.jsonlEventos con ID, dirección, track_id y fecha UTC
camera-history.jsonEntradas, salidas y cobertura de las horas observadas
camera-forecast-draft.jsonPetición para TimesFM con horas completas y consecutivas, si existen

El ID de evento permite deduplicar un reintento del mismo cruce. Conserva el espacio de identidades por sesión. Una persona puede salir y volver a entrar, así que debes contar los nuevos cruces aunque tengan el mismo ID de seguimiento.

El cliente exporta el histórico al terminar la grabación completa. Si ocurre un error, conserva registros parciales para inspeccionarlos. La cobertura indica cuánto tiempo se ha procesado dentro de cada hora. Una hora observada solo durante treinta segundos está incompleta, aunque no se haya contado ninguna entrada. Esas horas conservan su cobertura y quedan fuera de la petición a TimesFM. Si ninguna está completa, se guardan las métricas y se explica por qué no se crea el borrador.

Cuando hayas recogido suficientes ciclos de actividad, pega camera-forecast-draft.json en Predicción temporal → JSON avanzado y selecciona TimesFM. Este archivo utiliza entradas horarias sin covariables. Puedes añadir la apertura futura si conoces el calendario. El código rechaza huecos internos en vez de comprimir el tiempo.

Para practicar ahora la previsión, utiliza las ocho semanas sintéticas del siguiente paso, independientes del vídeo. El cliente se detiene si falla la conexión y debe completar la grabación dentro del límite de 30 minutos de la sesión. Contrasta el conteo con una revisión manual del vídeo antes de utilizar tus datos.

Trabaja con intervalos regulares en UTC y convierte a Europe/Madrid para mostrar los horarios. Registra cero solo en los intervalos observados sin entradas. Si la cámara está desconectada, marca el intervalo como dato ausente. Para calcular la ocupación necesitas además una referencia inicial y cobertura de todos los accesos.

Prevé las próximas 24 horas con TimesFM 3.0

TimesFM recibe una serie de números ordenados en el tiempo. Tu aplicación mantiene la correspondencia entre cada posición y su hora, porque el modelo no interpreta las marcas horarias del archivo. En este ejemplo cada paso es una hora, por lo que horizon: 24 representa el día siguiente. Si trabajas a intervalos de 15 minutos, un día requiere 96 pasos.

Abre Desarrolladores → Predicción temporal, selecciona el endpoint TimesFM y cambia a JSON avanzado. Copia el contenido de forecast-draft.json y pulsa Ejecutar predicción. El selector de modelo aporta el nombre de servicio, por lo que el borrador descargable no incluye model.

La petición completa contiene:

CampoForma del ejemploSignificado
horizon24Pasos futuros
inputs[0].targetUna matriz de 1 × 1344Un canal de entradas con 56 días de historial
inputs[0].past_future_covariatesUna matriz de 1 × 1368Apertura conocida durante historial y horizonte
return_quantilestrueSolicitar cuantiles además de la previsión puntual
use_symmetric_averagingfalseMantener desactivada esta opción para la prueba

Una covariable es información adicional que puede ayudar a la previsión. Aquí usamos la apertura, con valor 1 cuando el negocio está abierto y 0 cuando está cerrado. Conocemos el calendario futuro antes de predecir. Si añades meteorología, utiliza la previsión que estaba disponible en ese momento, no el tiempo real observado después.

El formulario permite varios registros independientes. Dentro de cada registro, las variables objetivo son canales conjuntos con la misma longitud. Puedes experimentar con entradas de varios accesos, pero no mezcles sus unidades ni sumes canales que contabilicen a la misma persona dos veces.

Envía el mismo histórico mediante la API

TimesFM utiliza POST /v1/timeseries/forecasts. Este ejemplo añade el nombre de servicio al borrador y guarda la respuesta sin modificarla. Conserva en esta terminal las variables QDIV0_API_KEY y QDIV0_ACCOUNT_ID del ejemplo de detección y comprueba que tienes forecast-draft.json en la carpeta actual.

En macOS o Linux, prepara un entorno para instalar requests:

1python3 -m venv .venv
2source .venv/bin/activate
3python -m pip install requests
4

Guarda el siguiente código como forecast.py. El nombre de servicio debe coincidir con el de tu despliegue TimesFM.

1import json
2import os
3from pathlib import Path
4
5import requests
6
7input_path = Path(os.environ.get("QDIV0_FORECAST_INPUT", "forecast-draft.json"))
8payload = json.loads(input_path.read_text())
9payload["model"] = os.environ.get("QDIV0_FORECAST_MODEL", "guia-restaurante-timesfm3")
10
11response = requests.post(
12    "https://api.qdiv0.com/v1/timeseries/forecasts",
13    headers={
14        "Authorization": f"Bearer {os.environ['QDIV0_API_KEY']}",
15        "X-Account-Id": os.environ["QDIV0_ACCOUNT_ID"],
16    },
17    json=payload,
18    timeout=(10, 600),
19)
20response.raise_for_status()
21result = response.json()
22Path("forecast-response.json").write_text(json.dumps(result, indent=2))
23print("Respuesta guardada en forecast-response.json")
24

Para utilizar el histórico de tu cámara, define export QDIV0_FORECAST_INPUT="camera-output/camera-forecast-draft.json" cuando exista ese archivo. Para el material sintético, deja la variable sin definir.

Ejecútalo con python forecast.py. El resultado esperado es un archivo forecast-response.json con 24 previsiones en predictions[0].forecast[0]. El número 24 describe el horizonte, no el total de personas previstas.

Si el endpoint aún está arrancando, espera a que esté listo. Antes de reintentar, revisa el estado HTTP y el mensaje de error para evitar acumular solicitudes idénticas mientras se prepara la GPU.

Resultado real sobre el histórico de ejemplo

Ejecutamos esta petición en QDivZero y conservamos la respuesta original. En la captura puedes ver el histórico seguido de los 24 pasos de la previsión.

Previsión completada en QDivZero con el histórico sintético y los cuantiles de TimesFM 3.0.
Previsión obtenida con TimesFM 3.0 a partir del histórico sintético. El gráfico incluye la previsión puntual y los cuantiles. Ver galería

Estas son algunas horas del 16 de octubre de 2026, en Europe/Madrid, redondeadas a dos decimales únicamente para la tabla:

Franja de inicioPrevisión puntualP10P90
12:009,057,4211,09
14:0039,9336,0544,05
21:0047,8243,4252,61
23:0011,459,5613,94

Descargar la respuesta original de esta ejecución

En el JSON, predictions[0].forecast[0] contiene los 24 valores del primer canal. quantile_levels identifica los niveles y predictions[0].quantiles[0] contiene los cuantiles de cada paso. Conservamos también los pequeños valores negativos devueltos durante algunas horas de cierre para poder distinguir la salida del modelo de cualquier ajuste del panel.

Exporta la previsión a CSV con Python

Para practicar sin desplegar nada, descarga el histórico, el JSON de detecciones y la respuesta de previsión enlazados en esta guía. Guarda también inspect_results.py en la misma carpeta y ejecuta:

1python3 inspect_results.py
2

La salida esperada del ejemplo es:

15 person detections (not arrival events)
224 forecasts -> footfall-forecast.csv
3

Este es el código del archivo descargable. Cuenta las detecciones de person, busca P10 y P90 por su nivel y calcula las fechas desde el último intervalo observado. Solo lee archivos locales: no envía peticiones ni arranca una GPU.

1import csv
2import json
3from datetime import datetime, timedelta
4from pathlib import Path
5from zoneinfo import ZoneInfo
6
7
8def main():
9    history = json.loads(Path("history.json").read_text(encoding="utf-8"))
10    detection = json.loads(Path("detection-response.json").read_text(encoding="utf-8"))
11    response = json.loads(Path("forecast-response.json").read_text(encoding="utf-8"))
12
13    people = sum(item["class_name"] == "person" for item in detection["detections"])
14    print(f"{people} person detections (not arrival events)")
15
16    prediction = response["predictions"][0]
17    p10 = response["quantile_levels"].index(0.1)
18    p90 = response["quantile_levels"].index(0.9)
19    interval = timedelta(seconds=history["interval_seconds"])
20    complete = [row for row in history["observations"] if row["coverage"] == 1]
21    if not complete:
22        raise ValueError("No complete observed hour for the forecast cutoff")
23    last = datetime.fromisoformat(complete[-1]["timestamp_utc"])
24    local_zone = ZoneInfo(history["timezone"])
25
26    with Path("footfall-forecast.csv").open("w", newline="", encoding="utf-8") as output:
27        writer = csv.writer(output)
28        writer.writerow(["timestamp_utc", "timestamp_local", "forecast", "p10", "p90"])
29        for step, value in enumerate(prediction["forecast"][0]):
30            timestamp = last + (step + 1) * interval
31            quantiles = prediction["quantiles"][0][step]
32            writer.writerow([
33                timestamp.isoformat(), timestamp.astimezone(local_zone).isoformat(),
34                value, quantiles[p10], quantiles[p90],
35            ])
36
37    print(f"{len(prediction['forecast'][0])} forecasts -> footfall-forecast.csv")
38
39
40if __name__ == "__main__":
41    main()
42

Abre footfall-forecast.csv en tu hoja de cálculo o panel. Incluye 24 filas con fecha UTC, fecha local, previsión puntual, P10 y P90, sin redondear ni corregir valores negativos. La primera corresponde al 16 de octubre de 2026 a las 00:00 en Europe/Madrid. En tus propias ejecuciones utiliza siempre el histórico que originó esa respuesta, con el mismo intervalo y orden de canales.

Interpreta la previsión antes de organizar el servicio

Compara las últimas horas observadas con las horas pronosticadas. Revisa las horas de apertura, los picos de comida y cena y las diferencias respecto al mismo día de la semana anterior. Los valores decimales expresan una estimación de afluencia.

Mantén la salida original para evaluar el modelo. Si tu panel muestra enteros o aplica max(0, previsión), registra esa transformación. Puedes imponer cero durante un cierre conocido como regla de negocio, pero comprueba por separado el comportamiento del modelo y el de la regla.

Los cuantiles ayudan a explorar escenarios. La banda entre P10 y P90 no garantiza por sí sola una cobertura del 80 % en tu negocio. Mide cuántos valores reales quedan dentro de ella con datos posteriores. Tampoco sumes los P90 horarios para presentarlos como el P90 del total diario: los cuantiles no se suman de esa manera.

Para dimensionar el equipo y las instalaciones, combina la afluencia prevista con la duración de las visitas, la capacidad del local y la experiencia de operación. En el restaurante del ejemplo también puedes considerar las mesas disponibles. Entradas por hora y ocupación simultánea son magnitudes diferentes. Una prueba con datos sintéticos enseña el flujo, pero no demuestra que el modelo acierte en tu local.

Comprueba si mejora una previsión sencilla

Reserva los últimos siete días como prueba. En cada corte, utiliza únicamente las observaciones anteriores y pide las siguientes 24 horas. Compara TimesFM con una referencia fácil de explicar: la misma hora del mismo día de la semana anterior.

ComprobaciónCómo evaluarla
Error medio absoluto, MAEMedia de la diferencia absoluta entre entradas reales y previstas
WAPESuma de errores absolutos / suma de entradas reales. Omítelo si el denominador es cero
Error en horas abiertasRepite las métricas excluyendo cierres, para que los ceros no oculten los errores de servicio
Error en picosRevisa las franjas de mayor actividad, como comida y cena en este ejemplo
Cobertura de cuantilesFracción de observaciones que quedan dentro de la banda elegida
Calidad del contadorCompara cruces con un recuento manual sobre vídeo representativo

Evalúa varios días y conserva el histórico, el modelo, sus parámetros y el momento de cada predicción. Si la referencia semanal gana, sigue usándola mientras mejoras los datos o la configuración. La complejidad del modelo no asegura una mejor previsión.

Lleva el flujo a tu negocio y controla los recursos

En una instalación continua, cada cámara utiliza su WebSocket con seguimiento de QDivZero. El cliente registra cruces y cierra intervalos. Después, una tarea de tu aplicación envía el histórico a TimesFM. Guarda fechas, configuración y cobertura junto a las métricas. Para pasar del ejemplo con grabaciones a una cámara permanente tendrás que gestionar las reconexiones y las ventanas de observación.

Puedes mantener YOLO disponible durante el horario de captura y ejecutar TimesFM en lotes, por ejemplo antes de abrir o al planificar el siguiente turno. Mide frecuencia de fotogramas, latencia y coste con tu cámara antes de elegir un pod mayor. Si envías muy pocos fotogramas, el contador puede perder cruces al no disponer de suficientes posiciones para seguir el movimiento.

Al terminar una prueba, abre Despliegues → Estado del modelo → Apagar modelo para los recursos que has usado. Aunque tengas Serverless activado, comprueba que se han detenido tras el periodo de inactividad. Mantén las claves en el backend y utiliza Ver código cuando cambies de tarea o de endpoint para revisar la petición que admite tu despliegue.

Preguntas frecuentes sobre conteo de personas con cámaras

¿Sirve para una tienda, un gimnasio o cualquier negocio físico?

Sí, si sus cámaras permiten observar los accesos y distinguir los cruces. Ajusta el encuadre, los horarios y la definición de entrada. Para estimar la ocupación necesitas observar todos los accesos y conocer la ocupación inicial.

¿Necesito instalar YOLO26 y TimesFM en mi ordenador?

YOLO26, ByteTrack y TimesFM se ejecutan en los despliegues de QDivZero. Tu ordenador solo necesita las herramientas cliente: OpenCV para leer vídeo, una biblioteca WebSocket para enviar fotogramas y el contador Python para interpretar cruces. No necesitas descargar checkpoints.

¿Puedo contar las entradas del día a partir de una fotografía?

No. Una fotografía muestra las personas presentes en ese instante. Para contar entradas necesitas vídeo y registrar los cruces hacia el interior. TimesFM utiliza después el histórico de esos eventos agrupados por hora.

¿Cuántos datos necesito para prever la afluencia?

Este tutorial usa ocho semanas de datos horarios como punto de partida reproducible. No es un mínimo universal ni una garantía de precisión. Reúne ciclos representativos de tu negocio y evalúa días posteriores, especialmente festivos, cambios de horario y horas punta.

¿El tracking de QDivZero cuenta clientes únicos?

Devuelve IDs temporales por conexión. El contador mide cruces del acceso, que pueden incluir empleados, repartidores y visitas repetidas. Valida esa métrica con vídeo representativo antes de utilizarla para planificar el negocio.

Fuentes y materiales

Las capturas se tomaron en la interfaz real de QDivZero en español. La escena y el histórico son materiales sintéticos de enseñanza. Los contratos de petición proceden del formulario y de los ejemplos generados por la plataforma.