【问题标题】:How can I use scipy.ndimage.interpolation.affine_transform to rotate an image about its centre?如何使用 scipy.ndimage.interpolation.affine_transform 围绕其中心旋转图像?
【发布时间】:2013-12-08 07:06:40
【问题描述】:

我对@9​​87654321@ 的 API 感到困惑。从this issue来看,我不是唯一一个。我实际上想用affine_transform 做更多有趣的事情,而不仅仅是旋转图像,但对于初学者来说,旋转就可以了。 (是的,我很了解scipy.ndimage.interpolation.rotate,但我对如何驾驶affine_transform 很感兴趣)。

当我想在像 OpenGL 这样的系统中做这种事情时,我会考虑计算应用 2x2 旋转矩阵 R 关于中心 c 的变换,因此考虑点 @987654331 @ 被转换 (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()

我明白了

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

那么我在这里缺少什么技巧?

【问题讨论】:

  • 只是做一些快速而肮脏的测试,我注意到你的偏移量的负值似乎围绕中心旋转。
  • 啊哈!是的,很好,在这里确认。看来我的 scipy 系统模型应该是(p-s)R。把它作为一个答案,我会接受它。

标签: python numpy matplotlib rotation scipy


【解决方案1】:

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

【讨论】:

  • 太棒了,我怎么能让它旋转并填满屏幕? (旋转而不在角落留下任何黑色/透明空间?
  • 您必须在变换中加入足够高的关于图像中心的缩放系数;对于方形窗口中的方形图像,sqrt(2) 的因子就可以了。
  • 任何在 2016+ 年来到这里的人请注意,最近似乎有一个 scipy 错误修复,它可能会或可能不会改变上述的一些细节;见github.com/scipy/scipy/issues/2255github.com/scipy/scipy/pull/5794
  • @FightFireWithFire 你是 Metallica 的粉丝吗? :-)
  • 谢谢谢谢谢谢。非常有用的答案。
【解决方案2】:

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

对于输出图像中的每个点oaffine_transform在输入图像中找到对应的点ii=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+ss=c_in-T.inv*c_out(注意这里使用的矩阵乘积的传统数学顺序,matrix*vector,这就是为什么使用反转顺序的@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)

【讨论】:

    【解决方案3】:

    只是做了一些快速而肮脏的测试,我注意到取负值的偏移量似乎围绕中心旋转。

    【讨论】:

      猜你喜欢
      • 2011-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-31
      • 1970-01-01
      相关资源
      最近更新 更多