title: Python/PIL affine transformation
body: <p>This is a basic transform question in PIL. I've tried at least a couple of times
  in the past few years to implement this correctly and it seems there is
  something I don't quite get about Image.transform in PIL. I want to
  implement a similarity transformation (or an affine transformation) where I can
  clearly state the limits of the image. To make sure my approach works I
  implemented it in Matlab.</p>
  
  <p>The Matlab implementation is the following:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>this works as expected. This is the input:</p>
  
  <p><img src="https://i.stack.imgur.com/phudR.jpg" alt="enter image description here"></p>
  
  <p>and this is the output:</p>
  
  <p><img src="https://i.stack.imgur.com/5qEy8.jpg" alt="enter image description here"></p>
  
  <p>This is the Python/PIL code that implements the same
  transformation:</p>
  
  <pre><code>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')
  </code></pre>
  
  <p>and this is the output from Python:</p>
  
  <p><img src="https://i.stack.imgur.com/KWl71.jpg" alt="enter image description here"></p>
  
  <p>I've tried this with several versions of Python and PIL on multiple OSs through the years and the results is always mostly the same.</p>
  
  <p>This is the simplest possible case that illustrates the problem, I understand that if it was a rotation I wanted, I could do the rotation with the im.rotate call but I want to shear and scale too, this is just an example to illustrate a problem. I would like to get the same output for all affine transformations. I would like to be able to get this right.</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>If I change the transform line to this:</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>this is the output I get:</p>
  
  <p><img src="https://i.stack.imgur.com/ucjma.jpg" alt="enter image description here"></p>
  
  <p><strong>EDIT #2</strong></p>
  
  <p>I rotated by -45 degrees and changed the offset to -0.5*mnx and -0.5*mny and obtained this:</p>
  
  <p><img src="https://i.stack.imgur.com/ynXT1.jpg" alt="enter image description here"></p>