5.23. Corrección de perspectiva¶
Advertencia
La matriz transform arbitraria de 3 por 3 solo es compatible con la OpenMV Cam N6 – el argumento se ignora silenciosamente en todas las demás placas. Las aplicaciones que necesiten ejecutarse en cualquier otro lugar deben usar el método predefinido rotation_corr() (con su forma corners=) o precalcular la imagen corregida fuera de la placa.
El método predefinido rotation_corr() empaqueta una familia particular de deformaciones de perspectiva tras un pequeño conjunto de parámetros, y se ejecuta en todas las placas compatibles. Algunas aplicaciones necesitan una deformación que no encaja en esa forma: un remapeo proyectivo arbitrario de un cuadrilátero a otro, una corrección calibrada para un montaje conocido que ya se ha resuelto sin conexión, una matriz de deformación entregada ya hecha por algún algoritmo previo. Para esos casos, draw_image() – junto con copy(), crop() y scale() – acepta un argumento transform que toma directamente una matriz de 3 por 3 construida a mano que describe la deformación.
5.23.1. Transformaciones afines y proyectivas¶
Las deformaciones geométricas se expresan en coordenadas homogéneas: la posición del píxel (x, y) con un 1 añadido, multiplicada por una matriz de 3 por 3.
La forma afín es el punto de partida. Su fila inferior es fija en \((0, 0, 1)\):
Desarrollado, cada coordenada de salida es una combinación lineal de las coordenadas de entrada más una constante:
lo cual cubre el escalado, la rotación, el cizallamiento y la traslación en cualquier combinación – y bajo todas ellas, las líneas paralelas permanecen paralelas.
La forma proyectiva (perspectiva) libera la fila inferior:
Desarrollado:
La división por \(w' = g x + h y + 1\) es lo que hace que la transformación sea proyectiva en lugar de meramente afín. Cuando \(g\) y \(h\) son ambos cero, \(w'\) se mantiene en uno y la división no hace nada – la forma afín de nuevo. Cuando alguno es distinto de cero, \(w'\) varía con la posición de entrada y los píxeles en distintas posiciones se acortan en cantidades diferentes, lo que ya no mantiene paralelas las líneas paralelas – es exactamente el efecto trapezoidal de mirar un plano plano desde un ángulo oblicuo. Una transformación proyectiva es la deformación geométrica más general que lleva las líneas rectas a líneas rectas; el escalado, el volteo, la transposición, la rotación y la corrección de rotación de cuatro esquinas son todos casos especiales de una sola.
Las transformaciones con nombre se derivan directamente de la forma afín. La transformación identidad es la matriz identidad, y:
Para la mayoría de las transformaciones construidas a mano, una aplicación parte de una de estas como base y multiplica matrices adicionales por cada operación extra, terminando con una única matriz de 3 por 3 que describe la deformación compuesta. Las matrices se aplican de derecha a izquierda: \(M = T R S\) ejecuta primero el escalado, luego la rotación y después la traslación. La composición que todo el mundo necesita tarde o temprano es la rotación en torno al centro de la imagen – una matriz de rotación a secas gira la imagen alrededor del origen de píxeles en la esquina superior izquierda, así que la versión centrada mueve el centro \((c_x, c_y)\) al origen, rota, y lo devuelve a su lugar:
5.23.2. El argumento transform¶
La matriz se introduce mediante un argumento transform, suministrado como un ulab.numpy.ndarray de 3 por 3. El método al que recurrir es draw_image(), que deforma el origen a través de la matriz mientras lo dibuja sobre un destino – el resultado va a parar a un búfer que controla la aplicación, y la deformación se compone con todo lo demás en la llamada: el escalado, la mezcla alfa, el enmascaramiento.
import ulab.numpy as np
M = np.array([[1.2, 0.0, -20.0],
[0.0, 1.2, -15.0],
[0.0, 0.0, 1.0]])
canvas.draw_image(img, transform=M)
El ejemplo deforma img sobre canvas escalado por 1.2 en cada dirección y desplazado a la izquierda y hacia arriba en 20 y 15 píxeles respectivamente – una deformación afín construida directamente a partir de las entradas de la matriz descritas arriba. El mismo argumento en copy(), crop() y scale() aplica la deformación a la imagen misma.