Python / PIL-affine Transformation

Dies ist eine grundlegende Transformationsfrage in PIL. Ich habe in den letzten Jahren mindestens ein paar Mal versucht, dies korrekt umzusetzen, und es scheint, dass es etwas gibt, das ich bei Image.transform in PIL nicht ganz verstehe. Ich möchte eine Ähnlichkeitstransformation (oder eine affine Transformation) implementieren, bei der ich die Grenzen des Bildes klar angeben kann. Um sicherzustellen, dass mein Ansatz funktioniert, habe ich ihn in Matlab implementiert.

Die Matlab-Implementierung sieht folgendermaßen aus:

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

das funktioniert wie erwartet. Dies ist die Eingabe:

und das ist die Ausgabe:

Dies ist der Python / PIL-Code, der dieselbe Transformation implementiert:

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

und dies ist die Ausgabe von Python:

Ich habe dies mit mehreren Versionen von Python und PIL auf mehreren Betriebssystemen im Laufe der Jahre versucht und die Ergebnisse sind meistens immer gleich.

Dies ist der einfachste mögliche Fall, der das Problem veranschaulicht. Ich verstehe, dass ich, wenn es eine Rotation wäre, die Rotation mit dem im.rotate-Aufruf ausführen könnte, aber ich möchte auch scheren und skalieren. Dies ist nur ein Beispiel, um a zu veranschaulichen Problem. Ich möchte für alle affinen Transformationen die gleiche Ausgabe erhalten. Ich würde gerne in der Lage sein, dies richtig zu machen.

BEARBEITEN:

Wenn ich die Transformationslinie in diese ändere:

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)

Das ist die Ausgabe, die ich bekomme:

EDIT # 2

Ich habe mich um -45 Grad gedreht und den Versatz auf -0,5 * mnx und -0,5 * mny geändert und dies erhalten:

Antworten auf die Frage(3)

Ihre Antwort auf die Frage