Transformación afín Python / PIL

Esta es una pregunta básica de transformación en PIL. He intentado al menos un par de veces en los últimos años implementar esto correctamente y parece que hay algo que no entiendo sobre Image.transform en PIL. Quiero implementar una transformación de similitud (o una transformación afín) donde pueda establecer claramente los límites de la imagen. Para asegurarme de que mi enfoque funciona, lo implementé en Matlab.

La implementación de Matlab es la siguiente:

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

esto funciona como se esperaba. Esta es la entrada:

y esta es la salida:

Este es el código Python / PIL que implementa la misma transformación:

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')

y esta es la salida de Python:

He intentado esto con varias versiones de Python y PIL en múltiples sistemas operativos a través de los años y los resultados son casi siempre los mismos.

Este es el caso más simple posible que ilustra el problema, entiendo que si fuera una rotación que yo quisiera, podría hacer la rotación con la llamada de im.rotate pero también quiero cortar y escalar, esto es solo un ejemplo para ilustrar un problema. Me gustaría obtener el mismo resultado para todas las transformaciones afines. Me gustaría poder hacer esto bien.

EDITAR:

Si cambio la línea de transformación a esto:

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 es la salida que recibo:

EDITAR # 2

Giré -45 grados y cambié el offset a -0.5 * mnx y -0.5 * mny y obtuve esto:

Respuestas a la pregunta(3)

Su respuesta a la pregunta