Supervision es la librería de visión artificial en Python de Roboflow: toma las detecciones de cualquier modelo (YOLO, RF-DETR, Transformers o Gemini) y te da una sola API para dibujarlas, contarlas por zonas, seguirlas entre fotogramas y convertir datasets entre YOLO, COCO y Pascal VOC. Es gratuita, con licencia MIT, y desde la versión 0.30.0 se instala sin OpenCV. Medimos cuánto cuesta eso y, si trabajas con video, la respuesta es: mucho.
Verificado el 17 de septiembre de 2026 con supervision 0.30.3 (PyPI, 14 de septiembre de 2026) y la rama
develop. Al momento de publicar esta nota, el repositorio tenía 50,8 mil estrellas en GitHub.
¿Qué es Supervision de Roboflow?
Supervision es la capa que va entre tu modelo y tu aplicación de visión artificial (computer vision). Cada framework de detección de objetos (object detection) devuelve su propio tipo de resultado. Supervision los convierte todos en un único objeto, sv.Detections. Así, el código que anota, filtra, cuenta y exporta no cambia cuando cambias de modelo.
Su documentación lista conversores para Ultralytics, Roboflow Inference, Transformers, SAM, Detectron2 y MMDetection, entre otros. También incluye parsers para modelos de visión y lenguaje como Florence-2, PaliGemma, Qwen VL y Gemini. RF-DETR, el detector propio de Roboflow, se salta el paso de conversión: su método predict ya devuelve un sv.Detections.
No es un proyecto nuevo:
- El primer commit es de noviembre de 2022.
- El repositorio suma más de 5.000 commits de más de 200 autores.
- Registró 97 commits en los 30 días previos a esta nota.
La documentación de Roboflow afirma que la librería supera el millón de descargas mensuales en PyPI; esa cifra es del propio proveedor.
¿Cómo se instala Supervision y todavía necesita OpenCV?
pip install supervision
Requiere Python 3.10 o superior; el soporte para 3.9 se eliminó en la 0.30.0.
Esa misma versión agregó un backend alternativo construido sobre NumPy, Pillow y PyAV. La guía de migración del proyecto indica que Supervision ya no instala OpenCV ni ofrece un extra para OpenCV. La guía incluye una línea para comprobar qué backend eligió tu proceso:
python -c "from supervision import _cv2; print(_cv2.BACKEND_NAME)"
En un entorno limpio imprime fallback. Al importar la librería aparece además un UserWarning que recomienda instalar opencv-python para obtener el rendimiento y la compatibilidad completos. Quisimos saber cuánto rendimiento se pierde.
Nuestra medición. Usamos dos entornos virtuales nuevos:
- Máquina: Linux x86_64, una vCPU Xeon de 2,1 GHz, Python 3.12.3 y supervision 0.30.3.
- Entornos: uno con el backend alternativo y otro con
opencv-python-headless5.0.0.93. - Prueba: fotogramas sintéticos de 1920×1080. Solo medimos el dibujo, sin inferencia de ningún modelo. Cada valor es el promedio de 30 ejecuciones.
| Detecciones por fotograma | BoxAnnotator sin OpenCV |
BoxAnnotator con OpenCV |
|---|---|---|
| 1 | 8,1 ms | 0,6 ms |
| 5 | 30,2 ms | 0,6 ms |
| 50 | 268,9 ms | 1,0 ms |
Las otras operaciones mostraron el mismo patrón:
LabelAnnotator: 284,6 ms contra 1,3 ms con 50 detecciones.sv.resize_imagea 640×360: 6,9 ms contra 0,6 ms.
El costo de dibujo sin OpenCV crece unos 5 ms por cada caja. Con 50 objetos, eso deja menos de cuatro fotogramas por segundo solo en dibujar, antes de que corra el modelo.
Lo que sí ahorras es disco: el site-packages del entorno sin OpenCV pesó 434 MB, contra 586 MB con OpenCV, unos 150 MB menos. PyAV, que el backend alternativo necesita para el video, ocupa por sí solo 103 MB en ambos.
La regla práctica:
- Datasets, métricas, conversiones y servidores que no dibujan: el backend sin OpenCV funciona bien y la imagen queda más ligera.
- Cualquier cosa que anote video: instala OpenCV en Python. La guía de migración pide elegir exactamente una familia de paquetes y nunca las dos:
opencv-python-headlesspara servidores y contenedores.opencv-pythonpara aplicaciones de escritorio.
Un detalle: el ejemplo de anotación del propio README empieza con import cv2. Con un pip install supervision sin nada más, esa línea falla con ModuleNotFoundError: No module named 'cv2'. Lo reprodujimos.
¿Cómo detectar objetos con YOLO o RF-DETR en Python?
La guía del proyecto muestra los mismos tres pasos para cualquier framework: ejecutar el modelo, cargar el resultado en sv.Detections y anotar. Esta es la versión con RF-DETR, copiada de la documentación. Usa cv2.imread, así que supone que instalaste OpenCV como se explicó arriba:
import cv2
import supervision as sv
from rfdetr import RFDETRMedium
model = RFDETRMedium()
image = cv2.imread("dog.jpeg")
detections = model.predict(image[:, :, ::-1])
box_annotator = sv.BoxAnnotator()
label_annotator = sv.LabelAnnotator()
annotated_image = box_annotator.annotate(
scene=image, detections=detections)
annotated_image = label_annotator.annotate(
scene=annotated_image, detections=detections)
Con YOLO de Ultralytics cambian solo la llamada al modelo y una línea de conversión: detections = sv.Detections.from_ultralytics(results). Con Transformers, la línea es sv.Detections.from_transformers(...). Todo lo que viene después queda idéntico.
Esa portabilidad tiene una consecuencia de licencia que conviene revisar antes de elegir. Según los metadatos de PyPI al 17 de septiembre de 2026:
rfdetr(1.10.1) usa Apache-2.0.ultralytics(8.4.155) usa AGPL-3.0.
Supervision es MIT, pero el modelo que conectes trae sus propias condiciones.
¿Cómo hacer un contador de personas con Python?
Un contador de personas (people counter) con Supervision tiene tres partes:
- Un polígono que define la zona.
- Un
sv.PolygonZonecreado a partir de ese polígono. - Una llamada a
triggersobre las detecciones de cada fotograma, que devuelve una máscara booleana con las que caen dentro de la zona.
De la guía de conteo del proyecto:
zones = [sv.PolygonZone(polygon=polygon) for polygon in polygons]
mask = zone.trigger(detections=detections)
detections_filtered = detections[mask]
Para obtener las coordenadas del polígono, Roboflow ofrece una herramienta web, PolygonZone: subes un fotograma, marcas las esquinas y te devuelve los arrays de NumPy.
Para contar solo personas, filtra por clase antes de pasar las detecciones a la zona. El ejemplo count_people_in_zone del repositorio lo hace así con RF-DETR:
filter_by_class = detections.class_id == PERSON_CLASS_ID
filter_by_confidence = detections.confidence > confidence_threshold
return detections[filter_by_class & filter_by_confidence]
Aquí está la trampa de un “sin atarte a un modelo” tomado al pie de la letra: los IDs de clase no son portables. Un comentario del mismo ejemplo aclara que en RF-DETR los IDs de clase COCO empiezan en 1, así que person es 1. En el mapeo de Ultralytics e Inference, person es 0, y la versión del ejemplo para Ultralytics filtra con detections.class_id == 0. Si cambias de modelo y no cambias ese número, tu contador de personas pasa a contar otra cosa sin dar ningún error.
Hay otros dos detalles, de la documentación y del changelog:
- Para contar lo que cruza una línea en lugar de lo que está dentro de un área, usa
sv.LineZone. Requieredetections.tracker_id, así que primero necesitas un tracker (siguiente sección). - Polígonos inválidos: desde la 0.30.3, un
PolygonZonecon menos de tres vértices lanza unValueError. Antes creaba una zona que nunca se activaba y no avisaba. Confirmamos el nuevo error en la 0.30.3.
¿Cómo hacer tracking de objetos entre fotogramas?
Aquí la documentación y el paquete publicado no coinciden todavía, así que conviene fijarse en la fecha de todo lo que leas.
El changelog de develop da sv.ByteTrack como eliminado en la próxima 0.31.0. Lo reemplaza ByteTrackTracker, del paquete independiente trackers de Roboflow. Al 17 de septiembre de 2026, la 0.31.0 no se había publicado: la 0.30.3 todavía incluye sv.ByteTrack, y parte de la documentación publicada aún lo describe. Escribe el código nuevo contra trackers para que sobreviva a la próxima versión. De la guía de tracking:
pip install trackers
import numpy as np
import supervision as sv
from rfdetr import RFDETRMedium
from trackers import ByteTrackTracker
model = RFDETRMedium()
tracker = ByteTrackTracker(track_activation_threshold=0.25, minimum_consecutive_frames=1)
box_annotator = sv.BoxAnnotator()
def callback(frame: np.ndarray, _: int) -> np.ndarray:
detections = model.predict(frame[:, :, ::-1])
detections = tracker.update(detections)
return box_annotator.annotate(frame.copy(), detections=detections)
sv.process_video(
source_path="people-walking.mp4",
target_path="result.mp4",
callback=callback
)
Si estás migrando código, ten en cuenta dos cosas:
- El método cambia de nombre: ahora es
update(), noupdate_with_detections(). - Los tracks sin confirmar vuelven con
tracker_idigual a-1. En nuestra prueba, incluso conminimum_consecutive_frames=1, un objeto nuevo recibió-1en su primer fotograma y un ID real desde el segundo. La guía los filtra condetections = detections[detections.tracker_id != -1].
La trampa: trackers 2.6.0 (Apache-2.0) declara opencv-python>=4.8.0 como dependencia. Instalarlo trae OpenCV de vuelta, y en concreto el paquete de escritorio, no el headless. Si seguiste el consejo de usar opencv-python-headless en servidores, agregar tracking deja las dos familias de paquetes en tu entorno. Eso es justamente lo que la guía de migración pide evitar. Lo confirmamos en una instalación limpia.
¿Cómo convertir un dataset de YOLO a COCO?
Cargas el dataset, lo divides si quieres y lo exportas. Lo ejecutamos sobre un dataset sintético de 10 imágenes y volvimos a cargar la salida COCO sin problemas:
dataset = sv.DetectionDataset.from_yolo(
images_directory_path=...,
annotations_directory_path=...,
data_yaml_path=...,
)
train_dataset, test_dataset = dataset.split(split_ratio=0.7)
dataset.as_coco(
images_directory_path=...,
annotations_path=...,
)
from_coco, from_pascal_voc, as_yolo y as_pascal_voc siguen el mismo patrón, así que cualquier conversión entre los tres formatos son dos llamadas.
Un bug que puedes encontrar hoy. Si tus archivos de etiquetas YOLO escriben los IDs de clase con decimales (1.0 en lugar de 1), la 0.30.3 aborta la carga completa con ValueError: invalid literal for int() with base 10: '1.0'. Es lo que produce np.savetxt por defecto, y lo reprodujimos.
La corrección (#2580) ya está fusionada en develop, pero al 17 de septiembre de 2026 no estaba en ninguna versión publicada. Ese mismo lote pendiente corrige los nombres de clase con caracteres fuera de ASCII (como café), que fallan en Windows (#2585). Hasta la próxima versión, escribe IDs de clase enteros.
¿Supervision es gratis y te ata a Roboflow?
Supervision es gratuita, con licencia MIT, y no necesita una cuenta de Roboflow. Solo necesitas una API key de Roboflow si ejecutas modelos con su paquete inference o si descargas datasets desde Roboflow.
Ese paquete tiene hoy su propia fricción. Hicimos una resolución de prueba con pip install --dry-run el 17 de septiembre de 2026 (Linux x86_64, Python 3.12):
pip install inferenceresolvióinference1.6.0 con supervision 0.29.1, no con la 0.30.x.- La misma resolución trajo tres familias de OpenCV:
opencv-python,opencv-contrib-pythonyopencv-python-headless. - Forzando
supervision==0.30.3, pip retrocedió ainference1.3.8.
Si quieres la versión actual de Supervision, los conversores que van directo a RF-DETR, Ultralytics o Transformers evitan ese conflicto.
¿Qué trae la próxima versión de Supervision?
Esto está en develop y, al 17 de septiembre de 2026, no se había publicado:
- Eliminaciones previstas para la 0.31.0, entre otras:
sv.ByteTrack.- El módulo
supervision.keypoint; se usasupervision.key_points. sv.LMMyDetections.from_lmm; se usansv.VLMyDetections.from_vlm.- La ruta de importación antigua de
MeanAveragePrecision.
- Nuevos parsers de modelos de visión y lenguaje:
- La salida estructurada de detección de Gemini 3.6 y 3.7. Gemini 3.5 ya llegó en la 0.30.0.
- Kosmos-2.
- Métricas:
aggregate_metric_results()yplot_aggregate_metric_results(), para comparar varios modelos en una sola tabla o gráfico. - Correcciones de datasets:
- Los IDs de clase con decimales y la codificación en Windows mencionados arriba.
- El manejo de la orientación EXIF en fotos tomadas con el teléfono.
Si fijas supervision==0.30.3 hoy, lee el changelog antes de actualizar.