Аффинное преобразование Python/PIL

Это основной вопрос преобразования в PIL. Я пробовал хотя бы пару раз в последние несколько лет, чтобы реализовать это правильно, и кажется, что есть то я не совсем понимаю Image.transform в PIL. я хочу реализовать трансформацию подобия (или аффинное преобразование), где я могу четко указать границы изображения. Чтобы убедиться, что мой подход работает, я реализовал его в Matlab.

Реализация Matlab следующая:

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

это работает так, как ожидалось. Это вход:

enter image description here

и это результат:

enter image description here

Это код Python/PIL, который реализует тот же Преобразование:

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

и это результат работы Python:

enter image description here

Я пробовал это с несколькими версиями Python и PIL на нескольких ОС за эти годы, и результаты всегда в основном одинаковы.

Это самый простой возможный случай, который иллюстрирует проблему. Я понимаю, что если бы это был поворот, который я хотел, я мог бы сделать поворот с вызовом im.rotate, но я тоже хочу сдвинуть и масштабировать, это просто пример чтобы проиллюстрировать проблему. Я хотел бы получить тот же результат для всех аффинных преобразований. Я хотел бы получить это право.

EDIT:

Если я изменил строку преобразования на это:

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)

это результат, который я получаю:

enter image description here

РЕДАКТИРОВАТЬ № 2

Я повернул на -45 градусов и изменил смещение на -0.5 * mnx и -0.5 * mny и получил это:

enter image description here

Ответ 1

OK! Поэтому я работаю над пониманием этого на всех выходных, и я думаю, что у меня есть ответ, который меня удовлетворяет. Спасибо всем за ваши комментарии и предложения!

Начну с этого:

аффинное преобразование в Pyth python?

пока я вижу, что автор может делать произвольные преобразования подобия не объясняет, почему мой код не работает, и он не объясняет пространственную макет изображения, который нам нужно преобразовать, и он не обеспечивает линейную алгебраическое решение моих проблем.

Но я вижу из своего кода, что вижу, что он делит вращательную часть матрицы (a, b, d и e) в шкалу, которая поразила меня как нечетную. Я вернулся, чтобы прочитать документацию PIL, которую я цитирую:

"im.transform(размер, AFFINE, данные, фильтр) = > изображение

Применяет аффинное преобразование к изображению и помещает результат в новое изображение с заданным размером.

Данные представляют собой 6-кортежи (a, b, c, d, e, f), которые содержат первые две строки из матрицы аффинного преобразования. Для каждого пикселя (x, y) на выходном изображении новый пиксель значение берется из позиции (a x + b y + c, d x + e y + f) на входе изображение, округленное до ближайшего пикселя.

Эта функция может использоваться для масштабирования, перевода, поворота и сдвига оригинала образ ".

поэтому параметры (a, b, c, d, e, f) являются матрицей преобразования, но та, которая отображает (x, y) в целевом изображении (a x + b y + c, d x + e y + f) в источнике образ. Но не параметры матрицы преобразования, которые вы хотите применить, но его обратный. То есть:

  • странно
  • отличается от Matlab
  • но теперь, к счастью, полностью понятен мной

Я прикрепляю свой код:

import Image
import math
from numpy import matrix
from numpy import linalg

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)
print mnx,mny
T = matrix([[math.cos(angle),math.sin(angle),-mnx],[-math.sin(angle),math.cos(angle),-mny],[0,0,1]])
Tinv = linalg.inv(T);
print Tinv
Tinvtuple = (Tinv[0,0],Tinv[0,1], Tinv[0,2], Tinv[1,0],Tinv[1,1],Tinv[1,2])
print Tinvtuple
im = im.transform((int(round(mxx-mnx)),int(round((mxy-mny)))),Image.AFFINE,Tinvtuple,resample=Image.BILINEAR)
im.save('outputpython2.jpg')

и вывод из python:

enter image description here

Позвольте мне снова ответить на этот вопрос в заключительном резюме:

PIL требует обратного аффинного преобразования, которое вы хотите применить.

Ответ 2

Я думаю, этот должен ответить на ваш вопрос.

Если нет, вы должны были бы считать, что аффинные преобразования могут быть объединены в другое преобразование.

Итак, вы можете разделить свою желаемую операцию на:

  • Перемещение объекта orgin в центр изображения

  • Вращающийся

  • Перемещение назад назад

  • Resizing

Вы могли бы вычислить одно преобразование из этого.