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:

questionAnswers(3)

yourAnswerToTheQuestion