如何使用scipy.ndimage.interpolation.affine_transform旋转图像的中心?

时间:2013-11-23 10:26:16

标签: python numpy matplotlib rotation scipy

我对scipy.ndimage.interpolation.affine_transform的API感到困惑。从this issue判断,我不是唯一一个。我实际上想要使用affine_transform做更多有趣的事情,而不仅仅是旋转图像,但旋转会对初学者有用。 (是的,我很清楚scipy.ndimage.interpolation.rotate,但是弄清楚如何驾驶affine_transform是我感兴趣的。)

当我想在像OpenGL这样的系统中做这种事情的时候,我想在计算变换时会对中心R应用2x2旋转矩阵c。要转换的p个点(p-c)R+cpR+c-cR = c-cR,它会将affine_transform项用作转换的转换组件。 但是,根据上面的问题,scipy的s会“首先偏移”所以我们实际上需要计算偏移量(p-c)R+c=(p+s)R,以便s=(c-cR)R' 1}}通过一些重新排列会给出R',其中Rimg=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}}

我得到了

rotated but not about centre lena image

不幸的是,它并没有围绕中心旋转。

那么我在这里缺少什么诀窍呢?

3 个答案:

答案 0 :(得分:16)

一旦treddy的答案给我一个工作基线,我设法得到了一个更好的affine_transform工作模型。它实际上并不像原始问题提示中链接的问题一样奇怪。

基本上,输出图像中的每个点(坐标)p都会转换为pT+s,其中Ts是传递给函数的矩阵和偏移量。 因此,如果我们希望输出中的点c_out映射到输入图像并从c_in采样,则需要旋转R和(可能是各向异性的)缩放S我们需要pT+s = (p-c_out)RS+c_in可以重新排列以产生s = (c_int-c_out)TT=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()

Spinning Lena

这是针对不同图像尺寸进行修改的

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

Spinning small Lena

这是一个带有各向异性缩放的版本,用于补偿源图像的各向异性分辨率。

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

Spinning anisotropic Lena

答案 1 :(得分:10)

基于@timday在输出坐标系中定义matrixoffset的见解,我将提供以下问题的读数,它符合线性代数中的标准符号并允许了解图像的缩放比例。我在这里使用T.inv=T^-1作为伪python表示法来表示矩阵的逆,而*表示点积。

对于输出图像中的每个点oaffine_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+ss=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()

Lena: first stretched, then rotated

如果要首先旋转图像,然后拉伸图像,则需要反转点积的顺序:

invTransform = dot(invRot, invScale)

Lena: first rotated, then stretched

答案 2 :(得分:6)

快点做一些&amp;肮脏的测试我注意到,偏移的负值似乎围绕中心旋转。