Transformaciones Geométricas

Dedicaremos este tutorial al estudio de las transformaciones geométricas que podemos aplicar para cambiar la posición, rotación, escala, o inclinación de una imagen, este tipo de transformación no cambia el contenido de la imagen, la deformación es produce por un cambio en la posición de los pixeles que la componen.

Escalar o cambio de tamaño

Para realizar esta transformación haremos uso de la función OpenCV llamada cv2.resize() con ella podemos cambiar el tamaño de la imagen de entrada, los parámetros que requiere esta función son: la imagen de entrada, el nuevo tamaño y el método de interpolación utilizado.

cambiar tamaño - escalar imagen

import cv2
import numpy as np

src = cv2.imread('lena.jpg')
dst = cv2.resize(src, (256, 256), interpolation=cv2.INTER_CUBIC)

cv2.imshow('lena.jpg', src)
cv2.imshow('Scale', dst)
cv2.waitKey()

Si lo deseamos no es necesario indicar el tamaño explícitamente, podemos indicar un factor de escala tanto para el ancho como para la altura, por ejemplo, si deseamos que la imagen tenga el 70% de su tamaño original lo hacemos de este modo:

dst = cv2.resize(src, None, fx=0.70, fy=0.70, interpolation=cv2.INTER_CUBIC)

Si no se indica el tamaño se utilizan los parámetros fx y fy, si el tamaño se establece estos parámetros son calculados a partir de lo que hayamos establecido.

Traslación o cambio de posición

Para realizar esta transformación no contamos con una función especifica, para esta tarea usaremos cv2.warpAffine() que nos permite aplicar transformaciones afines a una imagen, la transformación que vallamos a aplicar esta determinada por la correspondiente matriz, una traslación esta definida por la siguiente matriz:

traslación en OpenCV

Los valores de tx y ty definen la nueva posición en x e y respectivamente, de cada pixel.

import cv2
import numpy as np

src = cv2.imread('lena.jpg')

M = np.float32([[1, 0, 100], [0, 1, 50]])
dst = cv2.warpAffine(src, M, (512 + 100,  512 + 50))

cv2.imshow('lena.jpg', src)
cv2.imshow('Traslate', dst)
cv2.waitKey()

El último parámetro que usamos en esta función establece el tamaño de la nueva imagen, debemos tener en cuenta que la imagen será desplazada por lo que aumentamos el tamaño en la misma cantidad del desplazamiento para poder tener la imagen complete, un tamaño menor recortaría la imagen.

Trasladar imagen

Al usar la función cv2.warpAffine(...) también podemos indicar el método de interpolación que deseamos aplicar a través del parámetro flags, por defecto es cv2.INTER_LINEAR.

Rotación con OpenCV

Para rotar una imagen usaremos la misma función que usamos para la traslación, solo debemos cambiar la matriz, anteriormente creamos la matriz manualmente pues era sencilla, la matriz de rotación puede ser un poca mas compleja por lo que nos apoyaremos en la función proporcionada por OpenCV cv2.getRotationMatrix2D() para crearla, ha dicha función debemos indicarle el punto pivote sobre el cual se realiza el giro o rotación, el ángulo y el factor de escala.

import cv2
import numpy as np

src = cv2.imread('lena.jpg')
rows, cols = src.shape[:2]

M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 1)
dst = cv2.warpAffine(src, M, (cols, rows))

cv2.imshow('lena.jpg', src)
cv2.imshow('Rotate', dst)
cv2.waitKey()

En este ejemplo el punto pivote de la rotación se encuentra en el centro de la imagen, el ángulo es de 45 grados y el factor de escala es 1, el resultado:

OpenCV Python Rotar Imagen

Prueba cambiar el punto pivote a (0, 0) por ejemplo y observa los resultados.

Inclinación con OpenCV

La inclinación se puede aplicar sobre el eje X, Y o ambos si lo deseamos así, al igual que las transformaciones previas debemos definir la siguiente matriz:

matriz de inclinación

Nuestro ejemplo agrega una inclinación de 20 grados en el eje X y de 15 grados en el eje Y, si deseamos inclinar solamente en X establecemos Y a cero o viceversa.

import cv2
import numpy as np
import math

src = cv2.imread('lena.jpg')
rows, cols = src.shape[:2]

ix = math.tan(20 * math.pi / 180)
iy = math.tan(15 * math.pi / 180)

M = np.float32([[1, ix, 0], [iy, 1, 0]])

dst = cv2.warpAffine(src, M, (cols + 256, rows + 256))

cv2.imshow('lena.jpg', src)
cv2.imshow('Inclinar', dst)
cv2.waitKey()

El resultado de la operación:

Inclinación con OpenCV Python

Debido la inclinación el tamaño de la imagen de destino debe ser mayor a la original para poder contener la imagen deformada.

Una forma diferente de calcular la matriz de transformación correspondiente es utilizar la función OpenCV llamada cv2.getAffineTransform() para utilizarle debemos indicar dos conjuntos de 3 puntos, el primero define el triangulo que abarca la imagen original, el segundo define la ubicación a donde deben moverse cada uno de estos puntos.  

Warp_Affine_Tutorial_Theory_0

Por ejemplo, para generar una transformación similar a la anterior:

pt1 = np.float32([[0, 0],
                  [cols, 0],
                  [0, rows]])

pt2 = np.float32([[20, 20],
                  [cols - 120, rows / 4],
                  [cols / 4, rows - 120]])

M = cv2.getAffineTransform(pt1, pt2)

Cada pixel ubicado dentro del paralelogramo definido por los puntos de origen serán mapeados al paralelogramo definido por los puntos de destino.

Transformación en perspectiva

Esta transformación se calcula de manera similar a la anterior solo que deberemos indicar 4 puntos y usaremos las funciones cv2.getPerspectiveTransform() para obtener la matriz y cv2.warpPerspective() para aplicar la transformación.

import math
import numpy as np
import cv2

src = cv2.imread('left.jpg')
rows, cols = src.shape[:2]

pts1 = np.float32([[113, 137], [256, 136], [270, 337], [140, 377]])
pts2 = np.float32([[0, 0], [165, 0], [165, 223], [0, 223]])

M = cv2.getPerspectiveTransform(pts1, pts2)

dst = cv2.warpPerspective(src, M, (165, 223))

cv2.imshow('lena.jpg', src)
cv2.imshow('Transform', dst)
cv2.waitKey()

transformación perspectiva

La primera serie de puntos es usada para definir el rectángulo que conforma el libro.

OpenCV imagen en perspectiva

La segunda serie de puntos indica el rectángulo de destino en que deseamos ubicar el primer rectángulo, en este ejemplo logramos obtener una imagen de la portada del libro en perspectiva. 

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

Controles y Contenedores JavaFX 8 - I

Conectar SQL Server con Java

tkinter Canvas