Ответ 1
Просто выполняя некоторые быстрые и грязные тесты, я заметил, что отрицательное значение вашего смещения, похоже, вращается вокруг центра.
Я недоумеваю от API до scipy.ndimage.interpolation.affine_transform
. И, судя по этот вопрос, я не единственный. На самом деле я хочу делать более интересные вещи с помощью affine_transform
, чем просто поворачивать изображение, но поворот для стартеров. (И да, я хорошо знаю scipy.ndimage.interpolation.rotate
, но выяснение того, как управлять affine_transform
, меня интересует здесь).
Когда я хочу делать такие вещи в таких системах, как OpenGL, я думаю с точки зрения вычисления преобразования, которое применяет матрицу вращения 2x2 R
о центре c
, и поэтому мышление о точках p
преобразуется (p-c)R+c
= pR+c-cR
, что дает член c-cR
, который будет использоваться в качестве трансляционной составляющей преобразования. Однако, в соответствии с вышеизложенным, scipy affine_transform
делает "смещение первым", поэтому нам действительно нужно вычислить смещение s
, чтобы (p-c)R+c=(p+s)R
, который с бит перестановки дал s=(c-cR)R'
где R'
является обратным к R
.
Если я подключу его к ноутбуку ipython (режим pylab, код ниже, возможно, потребует дополнительного импорта):
img=scipy.misc.lena()
#imshow(img,cmap=cm.gray);show()
centre=0.5*array(img.shape)
a=15.0*pi/180.0
rot=array([[cos(a),sin(a)],[-sin(a),cos(a)]])
offset=(centre-centre.dot(rot)).dot(linalg.inv(rot))
rotimg=scipy.ndimage.interpolation.affine_transform(
img,rot,order=2,offset=offset,cval=0.0,output=float32
)
imshow(rotimg,cmap=cm.gray);show()
Я получаю
который, к сожалению, не вращается вокруг центра.
Итак, какой трюк мне здесь не хватает?
Просто выполняя некоторые быстрые и грязные тесты, я заметил, что отрицательное значение вашего смещения, похоже, вращается вокруг центра.
Как только ответ treddy дал мне исходный результат, мне удалось получить лучшую рабочую модель affine_transform
. Это не так странно, как проблема, связанная с исходными подсказками вопроса.
В принципе, каждая точка (координата) p
в выходном изображении преобразуется в pT+s
, где T
и s
- это матрица и смещение, переданные функции.
Поэтому, если мы хотим, чтобы в выходном файле, который должен отображаться и отбираться из c_in
из входного изображения, точка c_out
, с поворотом R
и (возможно анизотропным) масштабированием s
нам нужно pT+s = (p-c_out)RS+c_in
, которое можно переставить в выход s = (c_int-c_out)T
(с T=RS
).
По какой-то причине мне нужно передать transform.T
в affine_transform
, но я не буду беспокоиться об этом слишком много; вероятно, что-то делать с координатами строк с преобразованиями справа (предположительно выше) и координатами столбца с преобразованиями слева.
Итак, вот простой тест, вращающий центрированное изображение:
src=scipy.misc.lena()
c_in=0.5*array(src.shape)
c_out=array((256.0,256.0))
for i in xrange(0,7):
a=i*15.0*pi/180.0
transform=array([[cos(a),-sin(a)],[sin(a),cos(a)]])
offset=c_in-c_out.dot(transform)
dst=scipy.ndimage.interpolation.affine_transform(
src,transform.T,order=2,offset=offset,output_shape=(512,512),cval=0.0,output=float32
)
subplot(1,7,i+1);axis('off');imshow(dst,cmap=cm.gray)
show()
Здесь он изменен для разных размеров изображений
src=scipy.misc.lena()[::2,::2]
c_in=0.5*array(src.shape)
c_out=array((256.0,256.0))
for i in xrange(0,7):
a=i*15.0*pi/180.0
transform=array([[cos(a),-sin(a)],[sin(a),cos(a)]])
offset=c_in-c_out.dot(transform)
dst=scipy.ndimage.interpolation.affine_transform(
src,transform.T,order=2,offset=offset,output_shape=(512,512),cval=0.0,output=float32
)
subplot(1,7,i+1);axis('off');imshow(dst,cmap=cm.gray)
show()
И здесь версия с анизотропным масштабированием для компенсации анизотропного разрешения исходного изображения.
src=scipy.misc.lena()[::2,::4]
c_in=0.5*array(src.shape)
c_out=array((256.0,256.0))
for i in xrange(0,7):
a=i*15.0*pi/180.0
transform=array([[cos(a),-sin(a)],[sin(a),cos(a)]]).dot(diag(([0.5,0.25])))
offset=c_in-c_out.dot(transform)
dst=scipy.ndimage.interpolation.affine_transform(
src,transform.T,order=2,offset=offset,output_shape=(512,512),cval=0.0,output=float32
)
subplot(1,7,i+1);axis('off');imshow(dst,cmap=cm.gray)
show()
На основе понимания @timday, что matrix
и offset
определены в выходной системе координат, я бы предложил следующее чтение проблемы, которая соответствует стандартным обозначениям в линейной алгебре и позволяет понять масштабирование изображений. Я использую здесь T.inv=T^-1
как обозначение псевдопитона для обозначения обратной матрицы и *
для обозначения точечного произведения.
Для каждой точки o
на выходном изображении affine_transform
находит соответствующую точку i
во входном изображении как i=T.inv*o+s
, где matrix=T.inv
является инверсией матрицы преобразования 2x2, которую можно использовать для определить прямое аффинное преобразование, а offset=s
- это перевод, определенный в выходных координатах. Для чистого вращения T=R=[[cos,-sin],[sin,cos]]
и в этом специальном случае matrix=T.inv=T.T
, поэтому @timday пришлось применить транспозицию еще (в качестве альтернативы можно было бы использовать отрицательный угол).
Значение для смещения s
найдено точно так же, как описано в @timday: если предполагается, что c_in
позиционируется после аффинного преобразования в c_out
(например, центр ввода должен быть помещен на центр вывода), то c_in=T.inv*c_out+s
или s=c_in-T.inv*c_out
(обратите внимание на обычный математический порядок используемого здесь матричного продукта, вектор матрицы *, поэтому @timday, который использовал обратный порядок, не нуждался в транспозиции в этот момент в его код).
Если сначала требуется масштабирование s
, а затем поворот R
, то это означает, что T=R*S
и, следовательно, T.inv=S.inv*R.inv
(обратите внимание на обратный порядок). Например, если вы хотите сделать изображение двойным по ширине в направлении столбцов ('x'), тогда S=diag((1, 2))
, следовательно S.inv=diag((1, 0.5))
.
src = scipy.misc.lena()
c_in = 0.5 * array(src.shape)
dest_shape = (512, 1028)
c_out = 0.5 * array(dest_shape)
for i in xrange(0, 7):
a = i * 15.0 * pi / 180.0
rot = array([[cos(a), -sin(a)], [sin(a), cos(a)]])
invRot = rot.T
invScale = diag((1.0, 0.5))
invTransform = dot(invScale, invRot)
offset = c_in - dot(invTransform, c_out)
dest = scipy.ndimage.interpolation.affine_transform(
src, invTransform, order=2, offset=offset, output_shape=dest_shape, cval=0.0, output=float32
)
subplot(1, 7, i + 1);axis('off');imshow(dest, cmap=cm.gray)
show()
Если изображение должно быть сначала повернуто, а затем растянуто, то порядок точечного продукта должен быть отменен:
invTransform = dot(invRot, invScale)