📖 Teoría

Detección

Detección de objetos con modelos YOLO (Ultralytics), detección de landmarks con MediaPipe, y otros paradigmas de detección: R-CNN, CenterNet, FCOS, DETR. Incluye ejemplos prácticos de código en PyTorch y TensorFlow.

🎯 ¿Qué es la detección de objetos?

La detección de objetos va un paso más allá de la clasificación. Mientras que un clasificador responde «¿qué hay en la imagen?», un detector responde «¿qué hay y dónde está?». Para cada objeto relevante en la imagen, el modelo produce:

  • Una clase (persona, coche, perro…).
  • Un bounding box — un rectángulo que enmarca al objeto.
  • Una confianza (confidence score) de la predicción.
Clasificación Imagen de entrada 🐕 → "perro" (94%) Detección 🐕 perro 94% 🐈 gato 87%
💡

Detección ≠ Clasificación + localización: un clasificador con localización predice un único objeto por imagen. Un detector maneja un número variable de objetos — pueden ser 0, 1, 5 o 50 en la misma imagen. Este es el reto fundamental.

📦 Bounding boxes y representaciones

Un bounding box (bbox) se puede representar de varias formas. Las dos más comunes son:

Formato Valores Uso típico
(x₁, y₁, x₂, y₂) Esquina superior-izquierda + esquina inferior-derecha Pascal VOC, Faster R-CNN
(x_c, y_c, w, h) Centro + ancho + alto YOLO, COCO

Todos los valores suelen estar normalizados entre 0 y 1 respecto al tamaño de la imagen, facilitando el aprendizaje y la generalización a distintas resoluciones.

Anchor boxes

Muchos detectores (YOLO, SSD, Faster R-CNN) no predicen bounding boxes «desde cero». En su lugar, parten de anchor boxes: un conjunto de recuadros predefinidos con distintas proporciones (aspect ratios) distribuidos en una grid sobre la imagen. El modelo aprende a ajustar estos anchors — predice offsets (Δx, Δy, Δw, Δh) respecto al anchor más cercano.

Ajuste del anchor box \hat{b}_x = \sigma(t_x) + c_x, \quad \hat{b}_y = \sigma(t_y) + c_y, \quad \hat{b}_w = p_w \cdot e^{t_w}, \quad \hat{b}_h = p_h \cdot e^{t_h}

Donde (c_x, c_y) es la posición de la celda de la grid, (p_w, p_h) son las dimensiones del anchor, y (t_x, t_y, t_w, t_h) son las predicciones del modelo.

📐 IoU y métricas de detección

Intersection over Union (IoU)

La métrica fundamental para evaluar la calidad de un bounding box es el IoU (también llamado Jaccard index): la razón entre el área de intersección y el área de unión de la predicción y el ground truth.

Intersection over Union \text{IoU} = \frac{|B_{\text{pred}} \cap B_{\text{gt}}|}{|B_{\text{pred}} \cup B_{\text{gt}}|} = \frac{\text{Área de intersección}}{\text{Área de unión}}
IoU = 0 pred gt IoU ≈ 0.5 IoU ≈ 1.0 pred ≈ gt IoU < 0.5 → Falso Positivo 0.5 ≤ IoU → OK (COCO) IoU ≥ 0.75 → Excelente

Non-Maximum Suppression (NMS)

Un detector suele generar múltiples bounding boxes solapados para el mismo objeto. NMS filtra los duplicados manteniendo solo la predicción con mayor confianza:

Ordenar todos los bboxes por su confidence score de mayor a menor.

Seleccionar el bbox con mayor confianza y añadirlo al resultado final.

Eliminar todos los bboxes restantes cuyo IoU con el seleccionado supere un umbral (típicamente 0.5).

Repetir los pasos 2-3 hasta procesar todos los bboxes.

Métricas: Precision, Recall y mAP

Para evaluar un detector se usa la curva Precision-Recall y su área bajo la curva:

Precision y Recall \text{Precision} = \frac{TP}{TP + FP}, \qquad \text{Recall} = \frac{TP}{TP + FN}
Métrica Definición Uso
AP Área bajo la curva Precision-Recall para una clase Rendimiento por clase
mAP@0.5 Media del AP sobre todas las clases con IoU ≥ 0.5 Métrica principal Pascal VOC
mAP@[.5:.95] Media del AP promediada en IoU de 0.5 a 0.95 (paso 0.05) Métrica principal COCO (más estricta)
📝

mAP@[.5:.95] es más exigente porque penaliza los bboxes imprecisos. Un modelo puede tener mAP@0.5 del 70% pero mAP@[.5:.95] del 45% — los bboxes aciertan en ubicación general pero no son lo suficientemente ajustados.

⚙️ Pipeline general de detección

Independientemente del modelo concreto, todo detector sigue un pipeline con tres etapas principales:

Imagen
Backbone (features)
Neck (FPN / PAN)
Head (predicciones)
NMS → Bboxes

Componentes del pipeline

Componente Función Ejemplos
Backbone Extrae features jerárquicas de la imagen ResNet, CSPDarknet, EfficientNet
Neck Fusiona features de distintas escalas (multi-scale) FPN, PANet, BiFPN
Head Predice clases + bounding boxes Anchor-based, anchor-free, transformer

Two-stage vs. One-stage

Históricamente, los detectores se dividen en dos grandes familias según cómo generan las predicciones:

🐢 Two-stage (R-CNN family)
  • Paso 1: proponer regiones candidatas (RPN)
  • Paso 2: clasificar y refinar cada propuesta
  • Mayor precisión (especialmente para objetos pequeños)
  • Más lento (10-20 FPS típico)
  • Ejemplos: Faster R-CNN, Cascade R-CNN
🚀 One-stage (YOLO, SSD)
  • Un solo paso: predicción directa sobre la grid
  • Mucho más rápido (30-300+ FPS)
  • Ideal para tiempo real
  • Históricamente menos preciso, pero YOLO moderno cierra la brecha
  • Ejemplos: YOLO, SSD, RetinaNet

Estado actual (2024-2025): los detectores one-stage modernos (especialmente YOLO) han alcanzado y en muchos casos superado a los two-stage en precisión, manteniendo velocidades de inferencia muy superiores. Por eso YOLO domina las aplicaciones de producción.

YOLO: You Only Look Once

YOLO (Redmon et al., 2016) revolucionó la detección de objetos con una idea radical: en lugar de recorrer la imagen múltiples veces buscando objetos, mirar la imagen completa una sola vez y predecir simultáneamente todas las bounding boxes y clases. Esto convirtió la detección en un problema de regresión directa, logrando velocidades de tiempo real por primera vez.

La idea fundamental

YOLO divide la imagen en una grid de S×S celdas. Cada celda es responsable de detectar los objetos cuyo centro cae dentro de ella. Para cada celda, el modelo predice:

  • B bounding boxes, cada una con 5 valores: (x, y, w, h, \text{confidence})
  • C probabilidades de clase condicionales
Salida de YOLO \text{Output tensor: } S \times S \times (B \times 5 + C)
Imagen + Grid S×S Predicción por celda bbox₁: x,y,w,h,conf bbox₂: x,y,w,h,conf P(clase₁ | obj) P(clase₂ | obj) ... Una sola pasada • Clasificación • Localización • Confianza → Tiempo real

📈 Evolución de YOLO

Desde el YOLO original (v1, 2016) hasta las versiones actuales de Ultralytics, la familia YOLO ha atravesado una evolución extraordinaria, incorporando innovaciones de toda la comunidad:

2016 — YOLOv1
Joseph Redmon. Primera formulación de detección como regresión. 45 FPS, pero baja precisión en objetos pequeños y dificultad con objetos agrupados.
2017 — YOLOv2 (YOLO9000)
Introduce anchor boxes, batch normalization, entrenamiento multi-escala. Puede detectar 9000+ categorías.
2018 — YOLOv3
Detección multi-escala con FPN (Feature Pyramid Network). Backbone Darknet-53. Gran mejora en objetos pequeños. Último YOLO de Redmon.
2020 — YOLOv4 & YOLOv5
YOLOv4 (Bochkovskiy): CSPDarknet, Mish activation, Mosaic augmentation. YOLOv5 (Ultralytics): reimplementación en PyTorch, auto-anchor, export a ONNX/TensorRT. Marca el inicio del ecosistema Ultralytics.
2023 — YOLOv8
Ultralytics. Arquitectura anchor-free, head desacoplado (clasificación y regresión separadas), C2f modules. Soporta detección, segmentación, clasificación, pose y OBB en una sola API.
2024 — YOLOv11 (YOLO11)
Última versión de Ultralytics. C3k2 blocks, SPPF mejorado, mayor eficiencia en todas las tareas. Modelos: n, s, m, l, x (de 2.6M a 56.9M params).

🏗️ Arquitectura de YOLO moderno

Las versiones modernas de YOLO (v8, v11) comparten una arquitectura de tres componentes que ha convergido como el estándar de los detectores one-stage:

BACKBONE Conv + BN C3k2 block SPPF P3 (80×80) → P4 (40×40) → P5 (20×20) → NECK (PAN) Upsample Concat + C2f Downsample Concat + C2f Multi-scale feature fusion Top-down ↕ Bottom-up HEAD (Decoupled) Cls branch Reg branch Anchor-free distribución de offsets → (cls, x, y, w, h) por cada celda × escala

Innovaciones clave de YOLO moderno

📦 Anchor-free detection (YOLOv8+)

Las versiones clásicas de YOLO usaban anchor boxes predefinidas. YOLOv8+ elimina los anchors y predice directamente las distancias desde el centro del objeto a los cuatro bordes del bounding box:

(l, t, r, b) = distancias left, top, right, bottom desde el centro del objeto. Esto simplifica el diseño, elimina hiperparámetros de anchors, y mejora la generalización.

📦 Head desacoplado (Decoupled Head)

En lugar de usar un único head para clasificación y regresión, YOLOv8+ usa ramas separadas. La clasificación y la localización son tareas con objetivos diferentes — desacoplar los heads permite que cada rama se especialice, mejorando la precisión.

📦 Loss functions: CIoU + DFL

YOLO moderno usa CIoU loss (Complete IoU) para regresión de bboxes, que considera overlap, distancia de centros y proporción de aspecto. Para la distribución de offsets usa DFL (Distribution Focal Loss), que modela la localización como una distribución de probabilidad discreta en lugar de un valor puntual.

Familia de modelos Ultralytics

Modelo Params mAP@[.5:.95] Velocidad (ms) Caso de uso
YOLO11n 2.6M 39.5 1.5 Edge devices, IoT, móviles
YOLO11s 9.4M 47.0 2.5 Balance velocidad/precisión
YOLO11m 20.1M 51.5 4.7 Uso general de producción
YOLO11l 25.3M 53.4 6.2 Alta precisión
YOLO11x 56.9M 54.7 11.3 Máxima precisión (servidor)
💡

Ecosistema Ultralytics: más allá de detección, los modelos YOLO de Ultralytics soportan 5 tareas con la misma API: detección, segmentación de instancias, clasificación, estimación de pose (keypoints) y detección orientada (OBB). Un solo framework para todo.

🔄 Otras arquitecturas one-stage

Aunque YOLO domina el ecosistema, existen otros detectores one-stage relevantes con ideas complementarias:

SSD (Single Shot MultiBox Detector)

SSD (Liu et al., 2016) fue contemporáneo de YOLO y pionero en la detección multi-escala: aplica heads de detección en múltiples capas de features del backbone, cada una con resolución diferente. Los objetos grandes se detectan en capas profundas (baja resolución) y los pequeños en capas tempranas (alta resolución).

RetinaNet y Focal Loss

RetinaNet (Lin et al., 2017) identificó que el principal problema de los detectores one-stage no era la arquitectura sino el desbalance de clases: en una grid de detección, la inmensa mayoría de celdas son background (negativos fáciles) que dominan la loss. Su solución fue la Focal Loss:

Focal Loss \text{FL}(p_t) = -\alpha_t (1 - p_t)^\gamma \log(p_t)

El factor (1 - p_t)^\gamma reduce el peso de los ejemplos fáciles (alta p_t), concentrando el aprendizaje en los casos difíciles. Con \gamma = 2, RetinaNet superó a Faster R-CNN por primera vez desde un detector one-stage.

📝

Focal Loss en YOLO: las versiones modernas de YOLO incorporaron variantes de focal loss. YOLOv8 usa BCE con logits para clasificación y DFL + CIoU para regresión — ideas que evolucionaron a partir del principio de la focal loss.

📍 Detección de landmarks y keypoints

Mientras que la detección de objetos localiza qué hay y dónde (bounding box), la detección de landmarks (o keypoints) va un paso más allá: identifica puntos anatómicos o estructurales precisos dentro del objeto — articulaciones del cuerpo, rasgos faciales, puntos de la mano, etc.

Detección de objetos bbox + clase Salida: (x, y, w, h) + clase Detección de landmarks N puntos (xₖ, yₖ, conf) Salida: (x₁,y₁,c₁) (x₂,y₂,c₂) ... (xₙ,yₙ,cₙ)

Dos enfoques principales

🔥 Heatmap-based

Se genera un mapa de calor para cada keypoint. El modelo predice una imagen del mismo tamaño (o downscaled) donde la intensidad indica la probabilidad de cada punto:

Heatmap gaussiano H_k(x,y) = \exp\left(-\frac{(x-x_k)^2 + (y-y_k)^2}{2\sigma^2}\right)
  • ✅ Alta precisión espacial (resolución pixel)
  • ✅ Robusto ante oclusiones parciales
  • ❌ Costoso en memoria (un mapa por keypoint)
  • ❌ Resolución limitada por el stride de la red

📐 Regression-based

El modelo predice directamente las coordenadas (x, y) de cada keypoint como salida de regresión. Es más eficiente pero históricamente menos preciso.

Regresión directa \hat{K} = f_\theta(\mathbf{x}) \in \mathbb{R}^{N \times 3}
  • ✅ Ligero, rápido, bajo consumo de memoria
  • ✅ End-to-end sin post-procesado
  • ❌ Menor precisión sub-pixel
  • ❌ Más difícil de entrenar (señal supervisora débil)
💡

Tendencia actual: los modelos modernos combinan ambos enfoques. Por ejemplo, YOLO-Pose usa regresión directa de coordenadas, mientras que HRNet usa heatmaps de alta resolución. MediaPipe usa un enfoque híbrido con regresión para velocidad en dispositivos móviles.

🤖 MediaPipe: detección en tiempo real

MediaPipe (Google) es un framework de ML para procesamiento de medios en tiempo real, diseñado para ejecutarse de forma eficiente en dispositivos móviles, navegadores y edge devices. Su suite de solutions incluye modelos de landmarks preentrenados de alto rendimiento.

Soluciones de landmarks de MediaPipe

Solución Landmarks Descripción Uso típico
Face Mesh 478 puntos 3D Malla facial completa con iris tracking Filtros AR, análisis de expresiones
Hand Landmarks 21 puntos 3D Articulaciones de cada mano Gestos, lengua de señas, XR
Pose Landmarks 33 puntos 3D Esqueleto corporal completo Fitness, deporte, motion capture
Holistic 543 puntos Face + hands + pose combinados Interacción multimodal, XR

Arquitectura de MediaPipe Pose

MediaPipe sigue un pipeline de dos etapas para lograr velocidad en tiempo real:

1. Detector
Localiza la persona en la imagen (solo en el primer frame o cuando se pierde el tracking)
2. Tracker/Landmark model
Recibe el ROI (region of interest) y predice las coordenadas de los N landmarks
3. ROI update
Usa los landmarks predichos para actualizar el ROI del siguiente frame
📦 ¿Por qué dos etapas?

El detector (ligero, ~4M params) es más costoso que el tracker, pero solo se ejecuta esporádicamente (primer frame o cuando se pierde el tracking). El tracker recibe un crop estrecho alrededor de la persona, lo que reduce drásticamente el área de entrada y permite correr a 30+ FPS en móviles.

Esta estrategia de detect-then-track es clave para la eficiencia de MediaPipe en edge devices.

📦 Coordenadas 3D en MediaPipe

MediaPipe predice landmarks en 3D: (x, y, z, \text{visibility}). Las coordenadas (x, y) están normalizadas al tamaño de imagen [0, 1]. La coordenada z representa la profundidad relativa al centro de la cadera (en Pose) o la muñeca (en Hand), con la misma escala que x. Visibility indica la confianza de que el punto sea visible (no ocluido).

Otros frameworks de landmarks

📦 YOLO-Pose (Ultralytics)

Ultralytics integra la estimación de pose directamente en el framework YOLO. El modelo predice simultáneamente bounding boxes + keypoints en una sola inferencia. Ventajas:

  • Multi-persona nativo: detecta y estima pose de varias personas a la vez
  • Misma API de Ultralytics: model.predict(source)
  • Alto rendimiento: hereda la velocidad de YOLO
📦 HRNet (High-Resolution Network)

HRNet mantiene representaciones de alta resolución a lo largo de toda la red, en lugar de reducir la resolución y luego recuperarla (como en los encoders-decoders tradicionales). Esto lo hace especialmente preciso para tareas que requieren localización fina como keypoints.

Es el backbone de referencia para benchmarks de pose estimation como COCO Keypoints y MPII.

¿Cuándo usar cada framework?

  • MediaPipe: cuando necesitas velocidad en dispositivos con recursos limitados (móviles, navegadores).
  • YOLO-Pose: cuando quieres detección + pose multi-persona en un pipeline unificado.
  • HRNet: cuando la máxima precisión de landmarks es más importante que la velocidad.

🔬 Detectores two-stage (R-CNN family)

Antes de YOLO, la detección de objetos estaba dominada por los detectores two-stage: primero generan propuestas de regiones candidatas, y después clasifican y refinan cada propuesta individualmente. Aunque más lentos, durante años lograron la mayor precisión.

Evolución de la familia R-CNN

2014 — R-CNN
Girshick et al. Selective Search genera ~2000 propuestas de región. Cada propuesta se procesa por una CNN separadamente. Muy lento (~47s/imagen) pero demostró el potencial de las CNNs para detección.
2015 — Fast R-CNN
Comparte el cómputo de features: la imagen pasa una sola vez por la CNN, y se extraen features de cada propuesta usando RoI Pooling. Velocidad: ~0.3s/imagen (150× más rápido).
2015 — Faster R-CNN
Introduce el Region Proposal Network (RPN): una red pequeña que genera propuestas directamente de las features del backbone, eliminando Selective Search. End-to-end entrenable. ~5 FPS.
2017 — Mask R-CNN
Extiende Faster R-CNN añadiendo una rama de segmentación paralela que predice máscaras de instancia. Usa RoI Align en lugar de RoI Pooling para mejor precisión espacial. Base de la segmentación de instancias (que veremos en el siguiente submódulo).
BACKBONE ResNet / VGG Feature map C4 / FPN STAGE 1: RPN Sliding window 3×3 Anchors (k ratios) objectness score bbox regression → ~300 proposals STAGE 2: HEAD RoI Align FC layers clasificación bbox refinement → detecciones finales Predicciones clase + bbox + score
📝

Two-stage vs one-stage: los detectores two-stage examinan cada propuesta individualmente con más detalle, lo que les da ventaja en objetos pequeños y escenas complejas. Sin embargo, los detectores one-stage modernos (YOLO, FCOS) han cerrado la brecha de precisión manteniendo su ventaja de velocidad.

🎯 Detectores anchor-free

Los anchor boxes (cajas predefinidas de referencia) fueron centrales en los primeros detectores modernos, pero introducen hiperparámetros difíciles de ajustar. Los detectores anchor-free eliminan esta dependencia, prediciendo las bounding boxes de formas más directas.

CenterNet: detección como detección de puntos

CenterNet (Zhou et al., 2019) reformula la detección como un problema de detección del punto central del objeto. El modelo predice un heatmap de centros (como en landmark detection) y luego regresa el tamaño del bbox desde cada centro detectado:

CenterNet output \hat{Y} \in [0,1]^{\frac{W}{R} \times \frac{H}{R} \times C} \quad \text{(heatmap)} \quad + \quad (\hat{w}, \hat{h}) \quad \text{(size)}
  • Sin anchors, sin NMS (el heatmap produce centros directamente)
  • Simple y elegante — un solo backbone + 3 heads (heatmap, size, offset)
  • Extensible a 3D detection, pose, tracking

FCOS: Fully Convolutional One-Stage detector

FCOS (Tian et al., 2019) predice, para cada posición del feature map, las 4 distancias al borde del bbox (l, t, r, b) más un score de centerness que indica qué tan centrado está el punto respecto al objeto:

Centerness de FCOS \text{centerness} = \sqrt{\frac{\min(l,r)}{\max(l,r)} \times \frac{\min(t,b)}{\max(t,b)}}

El centerness penaliza predicciones lejos del centro del objeto, reduciendo detecciones de baja calidad sin necesidad de heurísticas complejas.

💡

YOLO heredó ideas anchor-free: la transición de YOLOv5 (anchor-based) a YOLOv8 (anchor-free) incorporó directamente la idea de FCOS de predecir distancias (l, t, r, b) desde cada punto, combinándola con Distribution Focal Loss para mayor precisión.

🔮 Detección con Transformers

Los transformers, originados en NLP, han revolucionado también la detección de objetos. El concepto clave es usar atención para modelar relaciones globales entre las features de la imagen y un conjunto de queries que aprenden a detectar objetos.

DETR: DEtection TRansformer

DETR (Carion et al., 2020, Meta AI) fue el primer detector basado en transformers. Su innovación radical: eliminar completamente anchors, NMS y todo el post-procesado heurístico, reemplazándolo con un enfoque puramente basado en conjuntos.

CNN backbone
ResNet-50 → features
Transformer encoder
Self-attention sobre features + pos. encoding
Transformer decoder
N object queries atenúan a features
Predicción
FFN: clase + bbox por query
📦 Object queries y matching bipartito

DETR usa N queries aprendibles (típicamente N=100) que funcionan como slots de detección. Cada query se especializa en detectar objetos en ciertas posiciones y escalas. El entrenamiento usa Hungarian matching (asignación bipartita) para emparejar predicciones con ground truth:

Hungarian matching \hat{\sigma} = \arg\min_{\sigma \in \mathfrak{S}_N} \sum_{i=1}^{N} \mathcal{L}_{\text{match}}(y_i, \hat{y}_{\sigma(i)})

Esto elimina la necesidad de NMS: el propio mecanismo de atención aprende a no predecir el mismo objeto dos veces.

Evolución de DETR

Modelo Mejora principal mAP (COCO) Característica
DETR (2020) Primer detector con transformer 42.0 Sin NMS, sin anchors
Deformable DETR Atención deformable multi-escala 46.2 10× más rápido en entrenamiento
DINO (2022) Denoising anchor boxes + contrastive 49.4 SOTA en COCO
RT-DETR (2023) Hybrid CNN-transformer, tiempo real 53.1 Integrado en Ultralytics

RT-DETR en Ultralytics: desde YOLOv8, Ultralytics integra RT-DETR como alternativa a YOLO. Usa un encoder híbrido CNN-transformer y un decoder IoU-aware. Compite en velocidad con YOLO manteniendo las ventajas de la arquitectura transformer (sin NMS, mejor en objetos parcialmente ocluidos). from ultralytics import RTDETR

Comparativa de paradigmas

Aspecto Two-stage One-stage (YOLO) Transformer (DETR)
Velocidad 🔴 Lenta (~5 FPS) 🟢 Tiempo real (30+ FPS) 🟡 Variable (5-30 FPS)
Precisión objetos grandes 🟢 Alta 🟢 Alta 🟢 Alta
Precisión objetos pequeños 🟡 Media-alta 🟡 Media (mejorando) 🟡 Media (mejorando)
Post-procesado NMS necesario NMS necesario Sin NMS ✨
Anchors Sí (RPN) No (v8+) No (queries)
Deploy edge 🔴 Difícil 🟢 Fácil (ONNX, TRT) 🟡 Posible (RT-DETR)
📝

Recuerda: en el submódulo anterior vimos clasificación de imágenes y transfer learning. En el siguiente submódulo exploraremos la segmentación (semántica e instancias), que extiende la detección prediciendo una máscara pixel a pixel en lugar de un rectángulo.

💻 Código: Detección con Ultralytics YOLO

El ecosistema Ultralytics ofrece la API más sencilla para detección de objetos con modelos YOLO. A continuación, los casos de uso más comunes.

Instalación

# Instalación
pip install ultralytics

# Verificar instalación
yolo checks

Inferencia básica (detección de objetos)

from ultralytics import YOLO

# Cargar modelo preentrenado (descarga automática)
model = YOLO("yolo11n.pt")   # nano: rápido, ligero
# model = YOLO("yolo11s.pt") # small: balance
# model = YOLO("yolo11m.pt") # medium: producción

# Inferencia en una imagen
results = model("imagen.jpg")

# Procesar resultados
for result in results:
    boxes = result.boxes          # Bounding boxes
    for box in boxes:
        xyxy = box.xyxy[0]        # Coordenadas [x1, y1, x2, y2]
        conf = box.conf[0]        # Confianza
        cls  = box.cls[0]         # Índice de clase
        name = model.names[int(cls)]  # Nombre de clase

        print(f"{name}: {conf:.2f} en ({xyxy[0]:.0f}, {xyxy[1]:.0f}) → "
              f"({xyxy[2]:.0f}, {xyxy[3]:.0f})")

# Guardar imagen anotada
result.save("resultado.jpg")

Detección en vídeo y webcam

from ultralytics import YOLO

model = YOLO("yolo11n.pt")

# Detección en vídeo
results = model("video.mp4", stream=True)  # stream=True para uso eficiente de memoria
for result in results:
    annotated = result.plot()  # Frame con anotaciones dibujadas
    # cv2.imshow("YOLO", annotated)

# Detección en webcam (fuente 0)
results = model(source=0, show=True)

# Detección en stream RTSP
results = model("rtsp://192.168.1.100:554/stream")

Entrenamiento con dataset personalizado

from ultralytics import YOLO

# Cargar modelo preentrenado como base
model = YOLO("yolo11n.pt")

# Entrenar con dataset personalizado
# El archivo data.yaml define clases, paths de train/val
results = model.train(
    data="mi_dataset/data.yaml",
    epochs=100,
    imgsz=640,
    batch=16,
    patience=20,          # Early stopping
    lr0=0.01,             # Learning rate inicial
    augment=True,         # Augmentación automática
    device="cuda",        # GPU (o "cpu", "mps")
    project="runs/detect",
    name="mi_experimento"
)

# Evaluar en validation set
metrics = model.val()
print(f"mAP@50: {metrics.box.map50:.3f}")
print(f"mAP@50:95: {metrics.box.map:.3f}")
# mi_dataset/data.yaml
path: /ruta/a/mi_dataset
train: images/train
val: images/val
test: images/test

names:
  0: gato
  1: perro
  2: pajaro

Exportación del modelo

from ultralytics import YOLO

model = YOLO("runs/detect/mi_experimento/weights/best.pt")

# Exportar a diferentes formatos
model.export(format="onnx")       # ONNX (multiplataforma)
model.export(format="torchscript")# TorchScript (C++ deployment)
model.export(format="engine")     # TensorRT (NVIDIA, máxima velocidad)
model.export(format="coreml")     # CoreML (Apple)
model.export(format="tflite")     # TFLite (Android, Edge TPU)

# Inferencia con modelo exportado
onnx_model = YOLO("best.onnx")
results = onnx_model("imagen.jpg")
💡

Tareas adicionales con la misma API: Ultralytics soporta model = YOLO("yolo11n-seg.pt") para segmentación, "yolo11n-pose.pt" para estimación de pose, y "yolo11n-obb.pt" para bounding boxes orientados.

🤖 Código: Landmarks con MediaPipe

MediaPipe ofrece detección de landmarks ligera y eficiente. A continuación se muestran ejemplos para las tres tareas principales.

Instalación

pip install mediapipe opencv-python

Detección de pose (33 landmarks)

import cv2
import mediapipe as mp

mp_pose = mp.solutions.pose
mp_draw = mp.solutions.drawing_utils

# Inicializar detector de pose
with mp_pose.Pose(
    static_image_mode=False,        # False para vídeo (tracking)
    model_complexity=1,             # 0=lite, 1=full, 2=heavy
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
) as pose:

    cap = cv2.VideoCapture(0)  # Webcam

    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        # Convertir BGR → RGB (MediaPipe usa RGB)
        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = pose.process(rgb)

        if results.pose_landmarks:
            # Dibujar landmarks y conexiones
            mp_draw.draw_landmarks(
                frame,
                results.pose_landmarks,
                mp_pose.POSE_CONNECTIONS
            )

            # Acceder a landmarks individuales
            landmarks = results.pose_landmarks.landmark
            nariz = landmarks[mp_pose.PoseLandmark.NOSE]
            print(f"Nariz: x={nariz.x:.3f}, y={nariz.y:.3f}, "
                  f"z={nariz.z:.3f}, vis={nariz.visibility:.2f}")

        cv2.imshow("Pose", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()

Detección de manos (21 landmarks)

import cv2
import mediapipe as mp

mp_hands = mp.solutions.hands
mp_draw  = mp.solutions.drawing_utils

with mp_hands.Hands(
    static_image_mode=False,
    max_num_hands=2,
    min_detection_confidence=0.7,
    min_tracking_confidence=0.5
) as hands:

    cap = cv2.VideoCapture(0)
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = hands.process(rgb)

        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                # Dibujar landmarks
                mp_draw.draw_landmarks(
                    frame, hand_landmarks, mp_hands.HAND_CONNECTIONS
                )

                # Ejemplo: posición de la punta del índice
                index_tip = hand_landmarks.landmark[
                    mp_hands.HandLandmark.INDEX_FINGER_TIP
                ]
                h, w, _ = frame.shape
                px = int(index_tip.x * w)
                py = int(index_tip.y * h)
                cv2.circle(frame, (px, py), 8, (0, 255, 0), -1)

        cv2.imshow("Hands", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cap.release()

Face Mesh (478 landmarks)

import cv2
import mediapipe as mp

mp_face = mp.solutions.face_mesh
mp_draw = mp.solutions.drawing_utils
mp_styles = mp.solutions.drawing_styles

with mp_face.FaceMesh(
    static_image_mode=False,
    max_num_faces=1,
    refine_landmarks=True,    # Incluye iris (478 en total)
    min_detection_confidence=0.5,
    min_tracking_confidence=0.5
) as face_mesh:

    cap = cv2.VideoCapture(0)
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break

        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = face_mesh.process(rgb)

        if results.multi_face_landmarks:
            for face_landmarks in results.multi_face_landmarks:
                # Dibujar malla facial
                mp_draw.draw_landmarks(
                    image=frame,
                    landmark_list=face_landmarks,
                    connections=mp_face.FACEMESH_TESSELATION,
                    landmark_drawing_spec=None,
                    connection_drawing_spec=mp_styles
                        .get_default_face_mesh_tesselation_style()
                )

        cv2.imshow("Face Mesh", frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    cap.release()

🔧 Detección con PyTorch y TensorFlow

Aunque Ultralytics y MediaPipe son las opciones más prácticas, entender cómo usar los frameworks base es valioso para proyectos personalizados.

PyTorch: detección con torchvision

import torch
import torchvision
from torchvision.models.detection import fasterrcnn_resnet50_fpn_v2
from torchvision.transforms import functional as F
from PIL import Image

# Cargar modelo preentrenado
model = fasterrcnn_resnet50_fpn_v2(
    weights=torchvision.models.detection
        .FasterRCNN_ResNet50_FPN_V2_Weights.DEFAULT
)
model.eval()

# Preprocesar imagen
image = Image.open("imagen.jpg")
tensor = F.to_tensor(image).unsqueeze(0)

# Inferencia
with torch.no_grad():
    predictions = model(tensor)[0]

# Filtrar por confianza
threshold = 0.5
for i, score in enumerate(predictions["scores"]):
    if score >= threshold:
        box   = predictions["boxes"][i].tolist()
        label = predictions["labels"][i].item()
        print(f"Clase {label}: {score:.2f} → {box}")

TensorFlow: TF Hub Object Detection

import tensorflow as tf
import tensorflow_hub as hub
import numpy as np
from PIL import Image

# Cargar modelo desde TF Hub
model = hub.load(
    "https://tfhub.dev/tensorflow/efficientdet/d0/1"
)

# Cargar y preprocesar imagen
image = Image.open("imagen.jpg")
image_np = np.array(image)
input_tensor = tf.convert_to_tensor(image_np)[tf.newaxis, ...]

# Inferencia
results = model(input_tensor)

# Extraer resultados
boxes  = results["detection_boxes"][0].numpy()    # [y1,x1,y2,x2] normalizado
scores = results["detection_scores"][0].numpy()
classes = results["detection_classes"][0].numpy().astype(int)

# Filtrar por confianza
threshold = 0.5
for i, score in enumerate(scores):
    if score >= threshold:
        y1, x1, y2, x2 = boxes[i]
        h, w = image_np.shape[:2]
        print(f"Clase {classes[i]}: {score:.2f} → "
              f"({int(x1*w)}, {int(y1*h)}) → ({int(x2*w)}, {int(y2*h)})")
⚠️

Nota práctica: para la mayoría de proyectos de detección, Ultralytics YOLO es la opción recomendada por su simplicidad, rendimiento y comunidad. Usa los frameworks base (PyTorch/TF) cuando necesites control total sobre la arquitectura o estés investigando nuevos modelos.