Transformação afim de Python / PIL
Esta é uma questão básica de transformação no PIL. Eu tentei pelo menos um par de vezes nos últimos anos para implementar isso corretamente e parece que há algo que eu não consigo muito sobre Image.transform no PIL. Eu quero implementar uma transformação de similaridade (ou uma transformação afim) onde eu possa indicar claramente os limites da imagem. Para garantir que minha abordagem funcione, implementei-o no Matlab.
A implementação do Matlab é a seguinte:
im = imread('test.jpg');
y = size(im,1);
x = size(im,2);
angle = 45*3.14/180.0;
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)];
m = [cos(angle) sin(angle) -min(xextremes); -sin(angle) cos(angle) -min(yextremes); 0 0 1];
tform = maketform('affine',m')
round( [max(xextremes)-min(xextremes), max(yextremes)-min(yextremes)])
im = imtransform(im,tform,'bilinear','Size',round([max(xextremes)-min(xextremes), max(yextremes)-min(yextremes)]));
imwrite(im,'output.jpg');
function y = rot_x(angle,ptx,pty),
y = cos(angle)*ptx + sin(angle)*pty
function y = rot_y(angle,ptx,pty),
y = -sin(angle)*ptx + cos(angle)*pty
isso funciona como esperado. Esta é a entrada:
e esta é a saída:
Este é o código Python / PIL que implementa a mesma transformação:
import Image
import math
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)
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,(math.cos(angle),math.sin(angle),-mnx,-math.sin(angle),math.cos(angle),-mny),resample=Image.BILINEAR)
im.save('outputpython.jpg')
e esta é a saída do Python:
Eu tentei isso com várias versões do Python e PIL em vários sistemas operacionais ao longo dos anos e os resultados são sempre os mesmos.
Este é o caso mais simples possível que ilustra o problema, eu entendo que se fosse uma rotação que eu queria, eu poderia fazer a rotação com a chamada im.rotate, mas eu quero cisalhar e dimensionar também, este é apenas um exemplo para ilustrar uma problema. Eu gostaria de obter o mesmo resultado para todas as transformações afins. Eu gostaria de poder fazer isso certo.
EDITAR:
Se eu mudar a linha de transformação para isso:
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,(math.cos(angle),math.sin(angle),0,-math.sin(angle),math.cos(angle),0),resample=Image.BILINEAR)
esta é a saída que recebo:
EDITAR # 2
Eu girei em -45 graus e mudei o offset para -0.5 * mnx e -0.5 * mny e obtive isso: