Ejecutar inferencia en vídeo
Ejecuta YOLO26 con archivos de vídeo y cámaras en directo con memoria acotada, sigue objetos para contar cada uno una sola vez y comprueba que el modelo mantiene la frecuencia de fotogramas.
La mayoría de los sistemas de visión desplegados analizan vídeo: cámaras sobre cintas transportadoras, puertas y carreteras. El vídeo plantea dos problemas que no existen con las imágenes individuales. Memoria: no se puede mantener una grabación larga en la RAM. Identidad: un detector ve a la misma persona en 60 fotogramas y la cuenta como 60 personas.
En esta lección transmitirás vídeo fotograma a fotograma con el modo Predict, añadirás el seguimiento de objetos para contar a cada persona una sola vez, escribirás tu propio bucle de cámara y medirás si el modelo mantiene el ritmo.
Práctico
Ejemplo práctico: hacer predicciones en un archivo de vídeo#
Los vídeos usan el mismo modo Predict que las imágenes. Este clip de 62 fotogramas muestra a personas cruzando un atrio:
yolo predict model=yolo26n.pt source=https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4video 1/1 (frame 1/62) solutions_ci_demo.mp4: 384x640 22 persons, 27.3ms
...
video 1/1 (frame 62/62) solutions_ci_demo.mp4: 384x640 22 persons, 54.8ms
Results saved to runs/detect/predictframe 1/62: Ultralytics decodifica el vídeo y procesa cada fotograma como si fuera una imagen.22 persons: detecciones en ese fotograma. Los mismos peatones se vuelven a contar en cada fotograma.27.3ms...54.8ms: el tiempo por fotograma varía según lo que esté haciendo el equipo.
El vídeo anotado es runs/detect/predict/solutions_ci_demo.mp4. Tu propio modelo funciona igual: sustituye yolo26n.pt por runs/detect/pills_v1/weights/best.pt.
Transmitir fuentes largas y en directo#
Para ejecutar código con las detecciones, recorre el flujo en un bucle. Cada iteración devuelve el Results de un fotograma y, después, lo descarta:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
frames, people = 0, 0
for result in model(source, stream=True, classes=[0], verbose=False): # one Results object per frame
frames += 1
people += len(result.boxes)
print(f"{frames} frames, {people / frames:.1f} people per frame")62 frames, 22.2 people per frameEl mismo bucle acepta el índice de una cámara web (source=0) o una URL de cámara como rtsp://camera.local/stream, y se ejecuta hasta que termina el flujo o lo detienes. Añade vid_stride=2 para procesar un fotograma de cada dos. Para crear una interfaz web en torno a este bucle, la guía de inferencia en directo con Streamlit lo integra en una aplicación.
Ejemplo práctico: contar a cada persona una sola vez#
«22 personas por fotograma» no es la cifra que interesa; lo que interesa es «cuántas personas han pasado por aquí». El seguimiento de múltiples objetos enlaza las cajas entre fotogramas, y el modo Track asigna a cada objeto un ID en boxes.id:
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
source = "https://github.com/ultralytics/assets/releases/download/v0.0.0/solutions_ci_demo.mp4"
seen = set()
for result in model.track(source, stream=True, persist=True, classes=[0], tracker="bytetrack.yaml", verbose=False):
if result.boxes.is_track:
seen.update(result.boxes.id.int().tolist())
print(f"{len(seen)} unique people tracked")32 unique people trackedis_track es False en los fotogramas anteriores a la confirmación de cualquier seguimiento, cuando boxes.id es None. Usa los ID para contar, calcular trayectorias o aplicar una lógica del tipo «este objeto lleva aquí 3 segundos».

| Rastreador | Velocidad | Robustez |
|---|---|---|
bytetrack.yaml | Más rápido y sencillo | Pierde los ID con más frecuencia cuando los objetos desaparecen brevemente |
botsort.yaml | Más lento | Mantiene mejor los ID cuando se mueve la cámara (compensación del movimiento; ReID por apariencia es opcional mediante with_reid) |
Si omites tracker, Ultralytics usa tracktrack.yaml, el valor predeterminado actual; el modo Track también incluye ocsort.yaml, deepocsort.yaml y fasttrack.yaml. La elección y el ajuste de los rastreadores, así como la creación de contadores de objetos, mapas de calor y estimaciones de velocidad con ellos, se explican en Seguimiento de objetos con ByteTrack, BoT-SORT y TrackTrack.
Ejemplo práctico: tu propio bucle de cámara#
Las aplicaciones reales suelen leer los fotogramas directamente —para cambiar su tamaño, recortarlos, añadirles marcas de tiempo o compartir una cámara con otro código— y pasar cada uno al modelo. En ese caso, persist=True es lo que mantiene activo el rastreador entre llamadas:
import cv2
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
cap = cv2.VideoCapture("solutions_ci_demo.mp4") # saved locally by the commands above; or 0, or an rtsp:// URL
seen = set()
while cap.isOpened():
ok, frame = cap.read()
if not ok:
break
result = model.track(frame, persist=True, classes=[0], tracker="bytetrack.yaml", verbose=False)[0]
if result.boxes.is_track:
seen.update(result.boxes.id.int().tolist())
cap.release()
print(f"{len(seen)} unique people, highest ID {max(seen)}")32 unique people, highest ID 67Los mismos 32 personas que con track.py. Fíjate en que el ID más alto es 67, no 32: el rastreador también asigna ID a seguimientos provisionales que nunca se confirman, por lo que hay saltos en la numeración.
Mantener el ritmo de la fuente#
Una cámara de 30 FPS entrega un fotograma cada 33 ms, y la frecuencia efectiva es la más lenta de entre la decodificación, la inferencia y la escritura. En la CPU de nuestro portátil, yolo26n tardó entre 26 y 46 ms por fotograma de 384×640 en distintas ejecuciones, según lo que estuviera haciendo el equipo: se alcanza o se supera el presupuesto, sin margen. Si tu equipo no puede mantener el ritmo, prueba estas medidas por orden:
| Medida | Efecto |
|---|---|
Modelo más pequeño (n en lugar de s) | A menudo, entre 2 y 3 veces más rápido |
Reduce imgsz (480 en lugar de 640) | Menos píxeles y menos cálculo; los objetos pequeños son los primeros en verse afectados |
vid_stride=2 | Reduce el trabajo a la mitad; adecuado para escenas con poco movimiento |
| Una GPU o una exportación optimizada | Las mayores mejoras: un motor TensorRT en NVIDIA o INT8; consulta la siguiente lección |
El uso simultáneo de varias cámaras y los hilos necesarios se explican en Inferencia con varios flujos.
Evita estos errores#
Síntoma: el uso de memoria aumenta hasta que el proceso se cierra durante una grabación larga, o una llamada a una cámara en directo no devuelve nunca el control. Causa: sin ese parámetro, la llamada de Python recopila un objeto Results por cada fotograma antes de devolver los resultados. Solución: pasa stream=True y recorre el generador; no hace nada hasta que lo iteras.
Síntoma: en cv2_loop.py sin persist=True, el recuento bajó a 27 y el ID más alto también era 27: los ID se reiniciaban en cada fotograma, así que el recuento «único» era solo el del fotograma con más objetos. Solución: pasa persist=True cada vez que llames a track una vez por fotograma.
Síntoma: un recuento de 67 para 32 personas. Causa: hay saltos en los ID porque algunas trayectorias no se confirmaron. Solución: cuenta el tamaño de un conjunto de ID, como en track.py.
El rastreador predeterminado puede cambiar entre versiones. Pasa tracker="bytetrack.yaml" (o el que prefieras) para que los recuentos sigan siendo comparables entre versiones.
Mostrar solución
import sys
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
seen, times = set(), []
for result in model.track(sys.argv[1], stream=True, persist=True, tracker="bytetrack.yaml", verbose=False):
times.append(result.speed["inference"])
if result.boxes.is_track:
seen.update(result.boxes.id.int().tolist())
print(f"{len(seen)} unique objects, {sum(times) / len(times):.1f} ms inference per frame")Ejecútalo con python track_any.py your_video.mp4.
32 unique objects, 30.5 ms inference per frame (26-46 ms across runs on a laptop CPU)
Source is 30 FPS -> 33 ms budget: fits only on a quiet machine, with no headroom
First lever: export to OpenVINO or ONNX for this CPU, then vid_stride=2 if still short