Saltar al contenido

Transformación afín Python / PIL

Ya no tienes que buscar más por otras páginas ya que llegaste al sitio necesario, poseemos la respuesta que deseas sin problemas.

Solución:

¡OK! Así que he estado trabajando para entender esto todo el fin de semana y creo que tengo una respuesta que me satisface. ¡Gracias a todos por sus comentarios y sugerencias!

Empiezo mirando esto:

transformación afín en Python PIL?

si bien veo que el autor puede hacer transformaciones de similitud arbitrarias, no explica por qué mi código no funcionaba, ni explica el diseño espacial de la imagen que necesitamos transformar ni proporciona una solución algebraica lineal a mis problemas.

Pero veo en su código que veo que está dividiendo la parte de rotación de la matriz (a, b, dye) en la escala que me pareció extraña. Volví a leer la documentación de PIL que cito:

“im.transform (tamaño, AFFINE, datos, filtro) => imagen

Aplica una transformación afín a la imagen y coloca el resultado en una nueva imagen con el tamaño dado.

Los datos son una tupla de 6 (a, b, c, d, e, f) que contienen las dos primeras filas de una matriz de transformación afín. Para cada píxel (x, y) en la imagen de salida, el nuevo valor se toma de una posición (ax + por + c, dx + ey + f) en la imagen de entrada, redondeado al píxel más cercano.

Esta función se puede utilizar para escalar, trasladar, rotar y distorsionar la imagen original “.

entonces los parámetros (a, b, c, d, e, f) son una matriz de transformación, pero el que asigna (x, y) en la imagen de destino a (ax + by + c, dx + ey + f) en la imagen de origen. Pero no los parámetros de la matriz de transformación desea aplicar, pero es inverso. Es decir:

  • extraño
  • diferente que en Matlab
  • pero ahora, afortunadamente, entendido completamente por mí

Adjunto mi código:

import Image
import math
from numpy import matrix
from numpy import linalg

def rot_x(angle,ptx,pty):
    return math.cos(angle)*ptx + math.sin(angle)*pty

def rot_y(angle,ptx,pty):
    return -math.sin(angle)*ptx + math.cos(angle)*pty

angle = math.radians(45)
im = Image.open('test.jpg')
(x,y) = im.size
xextremes = [rot_x(angle,0,0),rot_x(angle,0,y-1),rot_x(angle,x-1,0),rot_x(angle,x-1,y-1)]
yextremes = [rot_y(angle,0,0),rot_y(angle,0,y-1),rot_y(angle,x-1,0),rot_y(angle,x-1,y-1)]
mnx = min(xextremes)
mxx = max(xextremes)
mny = min(yextremes)
mxy = max(yextremes)
print mnx,mny
T = matrix([[math.cos(angle),math.sin(angle),-mnx],[-math.sin(angle),math.cos(angle),-mny],[0,0,1]])
Tinv = linalg.inv(T);
print Tinv
Tinvtuple = (Tinv[0,0],Tinv[0,1], Tinv[0,2], Tinv[1,0],Tinv[1,1],Tinv[1,2])
print Tinvtuple
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,Tinvtuple,resample=Image.BILINEAR)
im.save('outputpython2.jpg')

y la salida de Python:

ingrese la descripción de la imagen aquí

Permítanme expresar la respuesta a esta pregunta nuevamente en un resumen final:

PIL requiere la inversa de la transformación afín que desea aplicar.

Quería ampliar un poco las respuestas de carlosdc y Ruediger Jungbeck, para presentar una solución de código Python más práctica con un poco de explicación.

Primero, es absolutamente cierto que PIL usa transformaciones afines inversas, como se indica en la respuesta de carlosdc. Sin embargo, no es necesario utilizar álgebra lineal para calcular la transformación inversa a partir de la transformación original; en cambio, se puede expresar fácilmente de forma directa. Usaré escalar y rotar una imagen alrededor de su centro para el ejemplo, como en el código vinculado en la respuesta de Ruediger Jungbeck, pero es bastante sencillo extender esto para hacer, por ejemplo, corte también.

Antes de abordar cómo expresar la transformación afín inversa para escalar y rotar, considere cómo encontraríamos la transformación original. Como se insinúa en la respuesta de Ruediger Jungbeck, la transformación para la operación combinada de escalado y rotación se encuentra como la composición de los operadores fundamentales para escalar una imagen sobre el origen y rotar una imagen sobre el origen.

Sin embargo, dado que queremos escalar y rotar la imagen alrededor de su propio centro, y el origen (0, 0) está definido por PIL como la esquina superior izquierda de la imagen, primero debemos traducir la imagen de modo que su centro coincida con el origen. Después de aplicar la escala y la rotación, también necesitamos traducir la imagen de nuevo de tal manera que el nuevo centro de la imagen (puede que no sea el mismo que el centro anterior después de la escala y la rotación) termine en el centro de la imagen. lienzo.

Entonces, la transformación afín “estándar” original que buscamos será la composición de los siguientes operadores fundamentales:

  1. Encuentra el centro actual (c_x, c_y) de la imagen, y traduzca la imagen por (-c_x, -c_y), por lo que el centro de la imagen está en el origen (0, 0).

  2. Escale la imagen sobre el origen por algún factor de escala (s_x, s_y).

  3. Gire la imagen sobre el origen en algún ángulo  theta.

  4. Encuentra el nuevo centro (t_x, t_y) de la imagen, y traduzca la imagen por (t_x, t_y) por lo que el nuevo centro terminará en el centro del lienzo de la imagen.

Para encontrar la transformación que buscamos, primero necesitamos conocer las matrices de transformación de los operadores fundamentales, que son las siguientes:

  • Traducción por (x, y):
  • Escalando por (s_x, s_y):
  • Rotación por  theta:

Entonces, nuestra transformación compuesta se puede expresar como:

que es igual a

o

dónde

.

Ahora, para encontrar la inversa de esta transformación afín compuesta, solo necesitamos calcular la composición de la inversa de cada operador fundamental en orden inverso. Es decir, queremos

  1. Traducir la imagen por (-t_x, -t_y)

  2. Gire la imagen sobre el origen -  theta.

  3. Escale la imagen sobre el origen por (1 / s_x, 1 / s_y).

  4. Traducir la imagen por (c_x, c_y).

Esto da como resultado una matriz de transformación

dónde

.

Este es exactamente lo mismo como la transformación utilizada en el código vinculado en la respuesta de Ruediger Jungbeck. Se puede hacer más conveniente reutilizando la misma técnica que usó carlosdc en su publicación para calcular (t_x, t_y) de la imagen, y traduzca la imagen por (t_x, t_y): Aplicar la rotación a las cuatro esquinas de la imagen y luego calcular la distancia entre los valores mínimo y máximo de X e Y. Sin embargo, dado que la imagen se gira alrededor de su propio centro, no es necesario girar las cuatro esquinas, ya que cada par de esquinas opuestas se rotan “simétricamente”.

Aquí hay una versión reescrita del código de carlosdc que se ha modificado para usar la transformación afín inversa directamente, y que también agrega escala:

from PIL import Image
import math


def scale_and_rotate_image(im, sx, sy, deg_ccw):
    im_orig = im
    im = Image.new('RGBA', im_orig.size, (255, 255, 255, 255))
    im.paste(im_orig)

    w, h = im.size
    angle = math.radians(-deg_ccw)

    cos_theta = math.cos(angle)
    sin_theta = math.sin(angle)

    scaled_w, scaled_h = w * sx, h * sy

    new_w = int(math.ceil(math.fabs(cos_theta * scaled_w) + math.fabs(sin_theta * scaled_h)))
    new_h = int(math.ceil(math.fabs(sin_theta * scaled_w) + math.fabs(cos_theta * scaled_h)))

    cx = w / 2.
    cy = h / 2.
    tx = new_w / 2.
    ty = new_h / 2.

    a = cos_theta / sx
    b = sin_theta / sx
    c = cx - tx * a - ty * b
    d = -sin_theta / sy
    e = cos_theta / sy
    f = cy - tx * d - ty * e

    return im.transform(
        (new_w, new_h),
        Image.AFFINE,
        (a, b, c, d, e, f),
        resample=Image.BILINEAR
    )


im = Image.open('test.jpg')
im = scale_and_rotate_image(im, 0.8, 1.2, 10)
im.save('outputpython.png')

y así es como se ve el resultado (escalado con (sx, sy) = (0.8, 1.2) y girado 10 grados en sentido antihorario):

Escalado y girado

Puedes reafirmar nuestra publicación fijando un comentario o puntuándolo te damos la bienvenida.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *