我对scipy.ndimage.interpolation.affine_transform
的API感到困惑。从this issue判断,我不是唯一一个。我实际上想要使用affine_transform
做更多有趣的事情,而不仅仅是旋转图像,但旋转会对初学者有用。 (是的,我很清楚scipy.ndimage.interpolation.rotate
,但是弄清楚如何驾驶affine_transform
是我感兴趣的。)
当我想在像OpenGL这样的系统中做这种事情的时候,我想在计算变换时会对中心R
应用2x2旋转矩阵c
。要转换的p
个点(p-c)R+c
,pR+c-cR
= c-cR
,它会将affine_transform
项用作转换的转换组件。 但是,根据上面的问题,scipy的s
会“首先偏移”所以我们实际上需要计算偏移量(p-c)R+c=(p+s)R
,以便s=(c-cR)R'
1}}通过一些重新排列会给出R'
,其中R
是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()
的倒数。
如果我将其插入ipython笔记本(pylab模式;下面的代码可能需要一些额外的导入):
{{1}}
我得到了
不幸的是,它并没有围绕中心旋转。
那么我在这里缺少什么诀窍呢?
答案 0 :(得分:16)
一旦treddy的答案给我一个工作基线,我设法得到了一个更好的affine_transform
工作模型。它实际上并不像原始问题提示中链接的问题一样奇怪。
基本上,输出图像中的每个点(坐标)p
都会转换为pT+s
,其中T
和s
是传递给函数的矩阵和偏移量。
因此,如果我们希望输出中的点c_out
映射到输入图像并从c_in
采样,则需要旋转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()
答案 1 :(得分:10)
基于@timday在输出坐标系中定义matrix
和offset
的见解,我将提供以下问题的读数,它符合线性代数中的标准符号并允许了解图像的缩放比例。我在这里使用T.inv=T^-1
作为伪python表示法来表示矩阵的逆,而*
表示点积。
对于输出图像中的每个点o
,affine_transform
会在输入图像中找到i
的对应点i=T.inv*o+s
,其中matrix=T.inv
是<用于定义前向仿射变换的2x2变换矩阵的em> inverse ,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)
答案 2 :(得分:6)
快点做一些&amp;肮脏的测试我注意到,偏移的负值似乎围绕中心旋转。