45. Álgebra lineal aplicada a visión por computadora

La visión por computadora transforma píxeles en información sobre formas, posiciones y movimiento. Vectores y matrices permiten modelar la cámara y relacionar el mundo tridimensional con la imagen.

45.1 Introducción

Una cámara reduce una escena tridimensional a una imagen bidimensional. El objetivo de la visión por computadora es recuperar información útil a partir de esa proyección: objetos, profundidad, movimiento o posición.

escena 3D ⟶ cámara ⟶ imagen 2D ⟶ mediciones ⟶ interpretación

El álgebra lineal aparece en cada etapa: representa puntos, cambia sistemas de coordenadas, proyecta la escena y permite estimar parámetros a partir de datos.

45.2 Píxeles, puntos y vectores de características

La posición de un píxel se expresa como (u, v). Además de su posición, un punto visual puede describirse mediante un vector de intensidad, color, orientación o textura.

const puntoImagen = { u: 320, v: 240 };
const caracteristica = [0.2, 0.8, 0.1, 0.6];

const norma = Math.hypot(...caracteristica);
const descriptorNormalizado = caracteristica.map(x => x / norma);
console.log(puntoImagen, descriptorNormalizado);

Normalizar descriptores permite comparar su dirección sin que domine su escala.

45.3 Coordenadas homogéneas

Un punto 2D (u, v) se escribe en coordenadas homogéneas como (su, sv, s), con s distinto de cero. Todos los múltiplos no nulos representan el mismo punto.

(u, v) ↔ (u, v, 1) ↔ (su, sv, s)
(x, y, z) ↔ (x/z, y/z), si z ≠ 0
function deshomogeneizar([x, y, w]) {
  if (Math.abs(w) < 1e-12) throw new Error("Punto en el infinito");
  return [x / w, y / w];
}

console.log(deshomogeneizar([960, 720, 3])); // [320, 240]

Esta notación permite expresar proyecciones y transformaciones afines mediante multiplicaciones de matrices.

45.4 Modelo de cámara estenopeica

El modelo pinhole ideal supone que los rayos de luz atraviesan un único centro de proyección. Si un punto de la cámara es (X, Y, Z), su posición en un plano focal de distancia f es:

x = fX/Z     y = fY/Z
function proyectarPinhole([X, Y, Z], f) {
  if (Z <= 0) return null;
  return [f * X / Z, f * Y / Z];
}

console.log(proyectarPinhole([2, 1, 10], 800)); // [160, 80]

Dos puntos sobre el mismo rayo producen el mismo píxel; por eso una sola imagen no determina directamente la profundidad.

45.5 Parámetros intrínsecos

La matriz intrínseca K convierte coordenadas normalizadas de cámara en píxeles. Incluye distancias focales en píxeles, punto principal y, en el modelo general, sesgo entre ejes.

K = [[fx, s, cx], [0, fy, cy], [0, 0, 1]]
function pixelDesdeCamara([X, Y, Z], K) {
  const normalizado = [X / Z, Y / Z, 1];
  const h = K.map(fila =>
    fila.reduce((suma, valor, i) => suma + valor * normalizado[i], 0)
  );
  return [h[0] / h[2], h[1] / h[2]];
}

const K = [[800, 0, 320], [0, 800, 240], [0, 0, 1]];
console.log(pixelDesdeCamara([1, 0.5, 5], K)); // [480, 320]

45.6 Parámetros extrínsecos

Los parámetros extrínsecos sitúan la cámara respecto del mundo. Una rotación R y una traslación t convierten un punto mundial Xw en coordenadas de cámara:

Xc = RXw + t
function transformarPunto(R, t, punto) {
  return R.map((fila, i) =>
    fila.reduce((suma, valor, j) => suma + valor * punto[j], 0) + t[i]
  );
}

const R = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
console.log(transformarPunto(R, [0, 0, 5], [1, 2, 3])); // [1, 2, 8]

Es importante declarar la convención: t no siempre representa la posición de la cámara. En esta ecuación, el centro mundial de la cámara es C = −RTt.

45.7 Matriz de proyección

Los parámetros internos y externos se reúnen en una matriz P de 3 × 4:

p ∼ PX     con     P = K[R | t]

El símbolo ∼ indica igualdad salvo un factor de escala. X es un punto homogéneo 3D y p es el punto homogéneo de la imagen.

function proyectar(P, X) {
  const p = P.map(fila =>
    fila.reduce((suma, valor, i) => suma + valor * X[i], 0)
  );
  return [p[0] / p[2], p[1] / p[2]];
}

const P = [[800, 0, 320, 0], [0, 800, 240, 0], [0, 0, 1, 0]];
console.log(proyectar(P, [1, 0.5, 5, 1])); // [480, 320]

45.8 Distorsión y calibración

Una cámara real presenta distorsión radial y tangencial. La calibración estima K, los coeficientes de distorsión y, para cada imagen de referencia, la pose de la cámara.

datos conocidos + puntos observados ⟶ minimizar error de reproyección

El error de reproyección es la distancia entre el píxel detectado y la proyección predicha por el modelo.

function errorReproyeccion(observados, estimados) {
  const suma = observados.reduce((total, p, i) =>
    total + (p[0] - estimados[i][0]) ** 2 +
            (p[1] - estimados[i][1]) ** 2, 0);
  return Math.sqrt(suma / observados.length);
}

console.log(errorReproyeccion([[10, 10], [20, 20]], [[11, 10], [20, 22]]));
// aproximadamente 1.58 píxeles

45.9 Líneas y productos vectoriales

En el plano proyectivo, una línea se representa mediante l = (a, b, c), y un punto p pertenece a ella cuando lTp = 0.

línea por p y q: l = p × q
intersección de l y m: r = l × m
const cruz = (a, b) => [
  a[1] * b[2] - a[2] * b[1],
  a[2] * b[0] - a[0] * b[2],
  a[0] * b[1] - a[1] * b[0]
];

const linea = cruz([1, 1, 1], [4, 3, 1]);
console.log(linea); // [-2, 3, -1]

45.10 Comparación de características

Para reconocer el mismo punto en dos imágenes se comparan descriptores. Son frecuentes la distancia euclídea y la similitud coseno.

function distancia(a, b) {
  return Math.hypot(...a.map((valor, i) => valor - b[i]));
}

function coseno(a, b) {
  const producto = a.reduce((s, x, i) => s + x * b[i], 0);
  return producto / (Math.hypot(...a) * Math.hypot(...b));
}

console.log(distancia([1, 2, 3], [1, 2, 4])); // 1
console.log(coseno([1, 0], [0, 1]));          // 0

Una correspondencia cercana no siempre es correcta; suelen aplicarse pruebas de ambigüedad y verificación geométrica.

45.11 Gradientes de imagen

El gradiente aproxima cuánto cambia la intensidad en las direcciones x e y. Su magnitud ayuda a detectar bordes y su ángulo indica la dirección del cambio.

∇I = (Ix, Iy)     magnitud = √(Ix2 + Iy2)
function gradienteCentral(imagen, x, y) {
  const Ix = (imagen[y][x + 1] - imagen[y][x - 1]) / 2;
  const Iy = (imagen[y + 1][x] - imagen[y - 1][x]) / 2;
  return { Ix, Iy, magnitud: Math.hypot(Ix, Iy) };
}

const I = [[0, 0, 0], [0, 10, 20], [0, 20, 40]];
console.log(gradienteCentral(I, 1, 1));

45.12 Flujo óptico

El flujo óptico estima el desplazamiento aparente entre fotogramas. Bajo cambios pequeños y brillo aproximadamente constante se obtiene la restricción:

Ixu + Iyv + It = 0

Una ecuación no basta para hallar las dos componentes (u, v). Métodos como Lucas-Kanade reúnen un vecindario y resuelven un sistema sobredeterminado por mínimos cuadrados.

function resolver2x2(a, b, c, d, e, f) {
  const det = a * d - b * c;
  if (Math.abs(det) < 1e-12) return null;
  return [(e * d - b * f) / det, (a * f - e * c) / det];
}

// Ecuaciones normales de un pequeño vecindario.
console.log(resolver2x2(5, 1, 1, 4, 11, 9)); // [1.842..., 1.789...]

45.13 Homografías

Una homografía H es una matriz 3 × 3 que relaciona puntos de dos vistas de un mismo plano, o imágenes tomadas por una cámara que solo rota.

p' ∼ Hp
function aplicarHomografia(H, [x, y]) {
  const p = H.map(fila => fila[0] * x + fila[1] * y + fila[2]);
  if (Math.abs(p[2]) < 1e-12) return null;
  return [p[0] / p[2], p[1] / p[2]];
}

const H = [[1, 0.2, 30], [0.1, 1, 20], [0.001, 0, 1]];
console.log(aplicarHomografia(H, [100, 50]));

Se emplea para rectificar documentos, crear panoramas, insertar gráficos sobre superficies y comparar planos vistos desde diferentes posiciones.

45.14 Estimación robusta con RANSAC

Las correspondencias incluyen valores atípicos. RANSAC selecciona muestras mínimas, estima un modelo y cuenta cuántos datos son compatibles con él.

repetir: muestra ⟶ modelo ⟶ errores ⟶ inliers
conservar el modelo con mayor consenso y reajustarlo con sus inliers
function contarInliers(errores, umbral) {
  return errores.filter(error => error <= umbral).length;
}

const errores = [0.4, 1.1, 0.8, 25, 0.3, 17];
console.log(contarInliers(errores, 2)); // 4

RANSAC no garantiza el mejor resultado, pero es eficaz cuando hay suficientes correspondencias correctas y un umbral adecuado.

45.15 Geometría epipolar

Con dos cámaras, un punto de la primera imagen determina una línea epipolar en la segunda. La correspondencia correcta debe encontrarse sobre esa línea.

p'TFp = 0

La matriz fundamental F, de 3 × 3 y rango 2, resume la geometría proyectiva entre dos cámaras no calibradas. Para cámaras calibradas se utiliza la matriz esencial E.

function multiplicarMatrizVector(M, v) {
  return M.map(fila => fila.reduce((s, x, i) => s + x * v[i], 0));
}

const F = [[0, 0, 0], [0, 0, -1], [0, 1, 0]];
const p = [2, 3, 1];
const lineaEpipolar = multiplicarMatrizVector(F, p);
console.log(lineaEpipolar); // [0, -1, 3]

45.16 Triangulación y profundidad

Si un punto se observa en dos cámaras conocidas, se intersectan aproximadamente sus rayos para recuperar su posición 3D. Debido al ruido, los rayos rara vez se cruzan exactamente.

p1 ∼ P1X     y     p2 ∼ P2X

En un par estéreo rectificado, la profundidad se relaciona con la disparidad d = uizq − uder:

Z = fB/d
function profundidadEstereo(focalPixeles, baseMetros, disparidad) {
  if (disparidad <= 0) return Infinity;
  return focalPixeles * baseMetros / disparidad;
}

console.log(profundidadEstereo(800, 0.12, 24)); // 4 metros

45.17 Mínimos cuadrados y SVD

La calibración, la homografía y la triangulación producen sistemas con más ecuaciones que incógnitas. Se busca una solución que minimice el error total.

minimizar ||Ax − b||2
ecuaciones normales: ATAx = ATb

En implementaciones numéricas se prefieren factorizaciones QR o SVD a formar explícitamente ATA, porque suelen ser más estables. La SVD también permite imponer restricciones de rango.

function errorCuadratico(A, x, b) {
  return A.reduce((total, fila, i) => {
    const estimado = fila.reduce((s, valor, j) => s + valor * x[j], 0);
    return total + (estimado - b[i]) ** 2;
  }, 0);
}

console.log(errorCuadratico([[1, 0], [0, 1], [1, 1]], [2, 3], [2, 3, 5])); // 0

45.18 Aplicaciones, rendimiento y errores frecuentes

Estos modelos se utilizan en realidad aumentada, lectura de documentos, robótica, navegación, inspección industrial, reconstrucción 3D y seguimiento de objetos.

AplicaciónHerramientas algebraicas
PanoramasCaracterísticas, homografías y RANSAC
Realidad aumentadaCalibración, pose y proyección
Visión estéreoGeometría epipolar y triangulación
SeguimientoGradientes, flujo óptico y mínimos cuadrados

Errores frecuentes son mezclar filas con columnas, confundir coordenadas mundiales y de cámara, olvidar dividir por la coordenada homogénea, aceptar puntos detrás de la cámara, ignorar la distorsión, utilizar datos sin normalizar o estimar un modelo sin rechazar correspondencias falsas.

En sistemas reales conviene emplear bibliotecas optimizadas, tipos numéricos consistentes y pruebas con escenas cuya geometría sea conocida.

45.19 Qué debes recordar de este tema

  • La cámara proyecta puntos 3D sobre una imagen 2D y pierde profundidad.
  • Las coordenadas homogéneas convierten muchas transformaciones en productos matriciales.
  • K describe los parámetros internos y [R | t] relaciona el mundo con la cámara.
  • La matriz P = K[R | t] reúne el modelo de proyección.
  • La calibración minimiza errores de reproyección.
  • Una homografía relaciona vistas de un plano.
  • La matriz fundamental restringe correspondencias mediante líneas epipolares.
  • La triangulación recupera puntos 3D a partir de varias vistas.
  • Los mínimos cuadrados, QR y SVD permiten estimar modelos en presencia de ruido.

45.20 Conclusión

La visión por computadora utiliza el álgebra lineal para construir un puente entre píxeles y geometría. Las matrices describen cámaras y transformaciones; los vectores representan observaciones, y los métodos de estimación convierten mediciones imperfectas en modelos útiles.

En el próximo tema aplicaremos estas ideas a la ciencia de datos, donde matrices y vectores permiten organizar, transformar y analizar grandes conjuntos de observaciones.