Phase 04 - Lesson 26

Estimación de Profundidad y Geometría Monocular

Un mapa de profundidad es una imagen de un solo canal donde cada píxel es una distancia respecto a la cámara. Predecirlo a partir de un único fotograma RGB solía ser imposible sin estéreo o LiDAR. En 2026, un encoder ViT congelado más una cabeza ligera se acerca a pocos puntos porcentuales de la verdad de referencia.

Tipo: Construir + Usar Lenguajes: Python Prerrequisitos: Fase 4 Lección 14 (ViT), Fase 4 Lección 17 (Visión Autosupervisada), Fase 4 Lección 07 (U-Net) Tiempo: ~60 minutos

Objetivos de Aprendizaje

  • Distinguir profundidad relativa y métrica e indicar cuál de ellas resuelve cada modelo de producción (MiDaS, Marigold, Depth Anything V3, ZoeDepth)
  • Usar Depth Anything V3 (backbone DINOv2) para predecir profundidad de imágenes individuales arbitrarias sin calibración
  • Explicar por qué la profundidad monocular funciona a partir de una sola imagen (claves de perspectiva, gradientes de textura, prioris aprendidos) y qué no puede recuperar (escala absoluta, geometría ocluida)
  • Elevar detecciones 2D a puntos 3D usando un mapa de profundidad y los parámetros intrínsecos de la cámara pinhole

El Problema

La profundidad es el eje ausente en la visión computacional 2D. Dado el RGB, sabes dónde aparecen las cosas en el plano de la imagen; no sabes a qué distancia están. Los sensores de profundidad (montajes estéreo, LiDAR, time-of-flight) resuelven esto directamente, pero son caros, frágiles y limitados en alcance.

La estimación de profundidad monocular — predecir profundidad a partir de un único fotograma RGB — solía producir una salida borrosa y poco fiable. Para 2026, los grandes encoders preentrenados cambiaron eso: Depth Anything V3 usa un backbone DINOv2 congelado y produce mapas de profundidad que generalizan en dominios interiores, exteriores, médicos y satelitales. Marigold replantea la profundidad como un problema de difusión condicional. ZoeDepth regresa distancias métricas reales.

La profundidad también es el puente entre la detección 2D y la comprensión 3D: multiplica los píxeles de una caja detectada por la profundidad y elevas el objeto 2D a una nube de puntos 3D. Ese es el núcleo de todo sistema de oclusión de RA, todo pipeline de evasión de obstáculos y todo robot que "toma la taza".

El Concepto

Profundidad relativa vs métrica

  • Profundidad relativa — valores z ordenados sin una unidad del mundo real. "El píxel A está más cerca que el píxel B, pero la razón de las distancias no está anclada a metros."
  • Profundidad métrica — distancia absoluta en metros respecto a la cámara. Requiere que el modelo haya aprendido la relación estadística entre las claves de la imagen y la distancia real.

MiDaS y Depth Anything V3 producen profundidad relativa. Marigold produce profundidad relativa. ZoeDepth, UniDepth y Metric3D producen profundidad métrica. Los modelos métricos son sensibles a los parámetros intrínsecos de la cámara; los modelos relativos no.

El patrón encoder-decoder

flowchart LR
    IMG["Imagen (H x W x 3)"] --> ENC["Encoder ViT congelado<br/>(DINOv2 / DINOv3)"]
    ENC --> FEATS["Features densas<br/>(H/14, W/14, d)"]
    FEATS --> DEC["Decoder de profundidad<br/>(upsampler conv,<br/>estilo DPT)"]
    DEC --> DEPTH["Mapa de profundidad<br/>(H, W, 1)"]

    style ENC fill:#dbeafe,stroke:#2563eb
    style DEC fill:#fef3c7,stroke:#d97706
    style DEPTH fill:#dcfce7,stroke:#16a34a

Depth Anything V3 congela el encoder y entrena solo el decoder de estilo DPT. El encoder proporciona features ricas; el decoder las interpola de vuelta a la resolución de la imagen y regresa la profundidad.

Por qué una sola imagen produce profundidad en absoluto

Una imagen 2D contiene muchas claves monoculares que se correlacionan con la profundidad:

  • Perspectiva — las líneas paralelas en 3D convergen en 2D.
  • Gradiente de textura — las superficies lejanas tienen textura más pequeña y densa.
  • Orden de oclusión — los objetos más cercanos ocluyen a los más lejanos.
  • Constancia de tamaño — los objetos conocidos (autos, humanos) dan una escala aproximada.
  • Perspectiva atmosférica — los objetos distantes se ven más brumosos y azulados en escenas exteriores.

Un ViT entrenado con miles de millones de imágenes internaliza estas claves. Con suficientes datos y un backbone fuerte, la profundidad monocular alcanza una precisión razonable sin ninguna supervisión 3D explícita.

Lo que la profundidad monocular no puede hacer

  • Escala métrica absoluta sin parámetros intrínsecos o un objeto conocido en la escena. La red puede predecir "la taza está dos veces más lejos que la cuchara" sin saber si la taza está a 1 m o 10 m de distancia.
  • Geometría ocluida — la parte trasera de una silla no se ve y no puede inferirse de manera fiable.
  • Superficies verdaderamente sin textura / reflectantes — espejos, vidrio, paredes uniformes. La red reporta una profundidad plausible pero incorrecta.

Depth Anything V3 en 2026

  • DINOv2 ViT-L/14 puro como encoder (congelado).
  • Decoder DPT.
  • Entrenado con pares de imágenes con pose de fuentes diversas (no se necesita supervisión explícita de profundidad más allá de la consistencia fotométrica).
  • Predice geometría espacialmente consistente a partir de un número arbitrario de entradas visuales, con o sin poses de cámara conocidas.
  • SOTA en profundidad monocular, geometría de cualquier vista, renderizado visual y estimación de pose de cámara.

Este es el modelo plug-and-play al que llamar cuando necesitas profundidad en 2026.

Marigold — difusión para profundidad

Marigold (Ke et al., CVPR 2024) replantea la estimación de profundidad como difusión condicional imagen-a-imagen. Condicionamiento: RGB. Objetivo: mapa de profundidad. Usa una U-Net preentrenada de Stable Diffusion 2 como backbone. Los mapas de profundidad de salida son excepcionalmente nítidos en los bordes de los objetos. Compromiso: inferencia más lenta que los modelos feed-forward (10-50 pasos de denoising).

Parámetros intrínsecos y la cámara pinhole

Para elevar un píxel (u, v) con profundidad d a un punto 3D (X, Y, Z) en coordenadas de cámara:

fx, fy, cx, cy = camera intrinsics
X = (u - cx) * d / fx
Y = (v - cy) * d / fy
Z = d

Los parámetros intrínsecos provienen de metadatos EXIF, un patrón de calibración o un estimador de intrínsecos monocular (Perspective Fields, UniDepth). Sin parámetros intrínsecos, todavía puedes renderizar una nube de puntos asumiendo un FOV de 60-70° y principales de resolución moderada — útil para visualización, no para medición.

Evaluación

Dos métricas estándar:

  • AbsRel (error relativo absoluto): mean(|d_pred - d_gt| / d_gt). Menor es mejor. 0.05-0.1 para modelos de producción.
  • delta < 1.25 (precisión de umbral): fracción de píxeles donde max(d_pred/d_gt, d_gt/d_pred) < 1.25. Mayor es mejor. 0.9+ para SOTA.

Para profundidad relativa (Depth Anything V3, MiDaS), la evaluación usa versiones invariantes a escala y desplazamiento de ambas métricas.

Constrúyelo

Paso 1: Métricas de profundidad

import torch

def abs_rel_error(pred, target, mask=None):
    if mask is not None:
        pred = pred[mask]
        target = target[mask]
    return (torch.abs(pred - target) / target.clamp(min=1e-6)).mean().item()


def delta_accuracy(pred, target, threshold=1.25, mask=None):
    if mask is not None:
        pred = pred[mask]
        target = target[mask]
    ratio = torch.maximum(pred / target.clamp(min=1e-6), target / pred.clamp(min=1e-6))
    return (ratio < threshold).float().mean().item()

Siempre enmascara los píxeles de profundidad inválidos (cero, NaN, saturados) antes de la evaluación.

Paso 2: Alineación de escala y desplazamiento

Para los modelos de profundidad relativa, alinea la predicción a la verdad de referencia antes de computar las métricas. Ajuste por mínimos cuadrados de a * pred + b = target:

def align_scale_shift(pred, target, mask=None):
    if mask is not None:
        p = pred[mask]
        t = target[mask]
    else:
        p = pred.flatten()
        t = target.flatten()
    A = torch.stack([p, torch.ones_like(p)], dim=1)
    coeffs, *_ = torch.linalg.lstsq(A, t.unsqueeze(-1))
    a, b = coeffs[:2, 0]
    return a * pred + b

Ejecuta align_scale_shift antes de abs_rel_error al evaluar MiDaS / Depth Anything.

Paso 3: Elevar la profundidad a una nube de puntos

import numpy as np

def depth_to_point_cloud(depth, intrinsics):
    H, W = depth.shape
    fx, fy, cx, cy = intrinsics
    v, u = np.meshgrid(np.arange(H), np.arange(W), indexing="ij")
    z = depth
    x = (u - cx) * z / fx
    y = (v - cy) * z / fy
    return np.stack([x, y, z], axis=-1)


depth = np.random.uniform(0.5, 4.0, (240, 320))
intr = (320.0, 320.0, 160.0, 120.0)
pc = depth_to_point_cloud(depth, intr)
print(f"point cloud shape: {pc.shape}  (H, W, 3)")

Una función, toda aplicación elevada a 3D. Exporta la nube de puntos a .ply y ábrela en MeshLab o CloudCompare.

Paso 4: Prueba de humo con una escena de profundidad sintética

def synthetic_depth(size=96):
    yy, xx = np.meshgrid(np.arange(size), np.arange(size), indexing="ij")
    # Floor: linear gradient from near (top) to far (bottom)
    depth = 1.0 + (yy / size) * 4.0
    # Box in the middle: closer
    mask = (np.abs(xx - size / 2) < size / 6) & (np.abs(yy - size * 0.6) < size / 6)
    depth[mask] = 2.0
    return depth.astype(np.float32)


gt = torch.from_numpy(synthetic_depth(96))
pred = gt + 0.3 * torch.randn_like(gt)  # simulated prediction
aligned = align_scale_shift(pred, gt)
print(f"before align  absRel = {abs_rel_error(pred, gt):.3f}")
print(f"after align   absRel = {abs_rel_error(aligned, gt):.3f}")

Paso 5: Uso de Depth Anything V3 (referencia)

import torch
from transformers import pipeline
from PIL import Image

pipe = pipeline(task="depth-estimation", model="LiheYoung/depth-anything-v2-large")

image = Image.open("street.jpg").convert("RGB")
out = pipe(image)
depth_np = np.array(out["depth"])

Tres líneas. out["depth"] es un PIL en escala de grises; conviértelo a numpy para los cálculos. Para Depth Anything V3 específicamente, cambia el id del modelo una vez que se lance; la API no cambia.

Úsalo

  • Depth Anything V3 (Meta AI / ByteDance, 2024-2026) — el predeterminado para profundidad relativa. El modelo de backbone ViT-large más rápido en producción.
  • Marigold (ETH, 2024) — mayor calidad visual, inferencia lenta.
  • UniDepth (ETH, 2024) — profundidad métrica con estimación de parámetros intrínsecos de la cámara.
  • ZoeDepth (Intel, 2023) — profundidad métrica; más antiguo, todavía fiable.
  • MiDaS v3.1 — heredado pero estable; buena baseline para comparación.

Patrón típico de integración:

  1. Llega un fotograma RGB.
  2. El modelo de profundidad produce el mapa de profundidad.
  3. El detector produce cajas.
  4. Eleva los centroides de las cajas a través de la profundidad a 3D; fusiona con la nube de puntos si está disponible.
  5. Aguas abajo: oclusión de RA, planificación de trayectoria, estimación de tamaño de objeto, reemplazo de estéreo.

Para uso en tiempo real, Depth Anything V2 Small (cuantizado INT8) alcanza ~30 fps en una GPU de consumo a 518x518.

Entrégalo

Esta lección produce:

  • outputs/prompt-depth-model-picker.md — elige entre Depth Anything V3, Marigold, UniDepth, MiDaS dada la latencia, la necesidad de métrica-vs-relativa y el tipo de escena.
  • outputs/skill-depth-to-pointcloud.md — una skill que construye nubes de puntos a partir de mapas de profundidad con manejo correcto de parámetros intrínsecos y exportación a .ply.

Ejercicios

  1. (Fácil) Ejecuta Depth Anything V2 en cualquier conjunto de 10 imágenes de tu escritorio. Guarda la profundidad como PNGs en escala de grises e inspecciónalos. Identifica un objeto cuya profundidad predicha se vea incorrecta y explica por qué fallaron las claves monoculares.
  2. (Medio) Dado RGB + profundidad de Depth Anything V2, eleva a una nube de puntos y renderiza con open3d. Compara dos escenas (interior / exterior) y nota cuál se ve más creíble.
  3. (Difícil) Toma cinco pares de imágenes que difieren solo por la posición de un objeto conocido (por ejemplo, una botella movida 30 cm más cerca). Usa UniDepth para predecir la profundidad métrica en ambas. Reporta el delta de distancia predicho vs los 30 cm reales.

Términos Clave

Término Lo que la gente dice Lo que realmente significa
Profundidad monocular "Profundidad de imagen única" Estimación de profundidad a partir de un fotograma RGB, sin estéreo ni LiDAR
Profundidad relativa "Profundidad ordenada" Valores z ordenados sin unidades del mundo real
Profundidad métrica "Distancia absoluta" Profundidad en metros; requiere calibración o un modelo entrenado con supervisión métrica
AbsRel "Error relativo absoluto" Media de
Precisión delta "delta < 1.25" Fracción de píxeles con predicción dentro del 25% de la verdad de referencia
Cámara pinhole "fx, fy, cx, cy" El modelo de cámara usado para elevar (u, v, d) a (X, Y, Z)
DPT "Dense Prediction Transformer" El decoder basado en conv usado sobre encoders ViT congelados para profundidad
Backbone DINOv2 "La razón por la que funciona" Features autosupervisadas que generalizan en dominios sin etiquetas de profundidad

Lectura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).