获取旋转图像中点的新x,y坐标

18 投票
3 回答
23111 浏览
提问于 2025-04-16 20:00

我有一些谷歌地图的图标,想在绘制到地图上之前,先把它们旋转一定的角度。我是在Python中用PIL库实时进行旋转的,旋转后的图片和原来的大小一样,都是32x32。例如,下面这个默认的谷歌地图标记:

icon before rotation

我用以下的Python代码实现了逆时针旋转30度:

# full_src is a variable holding the full path to image
# rotated is a variable holding the full path to where the rotated image is saved
image = Image.open(full_src)
png_info = image.info
image = image.copy()
image = image.rotate(30, resample=Image.BICUBIC)
image.save(rotated, **png_info)

旋转后的图片是 icon rotated 30 degrees counter-clockwise

这里有个难点,就是在用新的旋转图像创建MarkerImage时,如何设置新的锚点。这个锚点需要是图标的尖端。默认情况下,锚点是在图标的底部中间位置,坐标是(16,32),其中(0,0)是左上角。有人能告诉我在JavaScript中怎么简单实现这一点吗?

谢谢。

更新 2011年6月22日: 之前发错了旋转后的图片(原来的图是逆时针旋转330度的)。我已经修正了。此外,还添加了重采样(Image.BICUBIC),让旋转后的图标更清晰。

3 个回答

2

在一张图片中,向下是正Y方向,向右是正X方向。不过,为了使用旋转公式,我们需要把向上作为正Y方向。因此,第一步是应用这个公式:f(x,y) = f(x,h-y),其中'h'是图片的高度。

假设图片是围绕某个点x0,y0旋转的。接下来,你需要把坐标原点移动到这个点。因此,第二步是f(x,y) = f(x-x0,y-y0)。经过这两步后,你的新坐标会变成x-x0h-y-y0。现在你就可以使用旋转公式了。

x1 = x*cos(theta) - y*sin(theta) 

y1 = xsin(theta) + ycos(theta) 

使用第二步得到的x和y的值。你会得到:

x1 = (x-x0)*cos(theta) - (h-y-y0)*sin(theta) 

y1 = (x-x0)*sin(theta) + (h-y-y0)*cos(theta)

现在,按照顺序撤销第二步和第一步的变换。

撤销第二步后:xNew = x1 + x0yNew = y1 + y0

撤销第一步后:xNew = x1 + x0yNew = h - (y1 + y0)

这样你就得到了:

xNew = (x-x0)*cos(theta) - (h-y-y0)*sin(theta) + x0

yNew = -(x-x0)*sin(theta) - (h-y-y0)*cos(theta) + (h-y0)
9

关于坐标原点(0,0)的旋转公式是:

x1 = cos(theta) x0 - sin(theta) y0
y1 = sin(theta) x0 + cos(theta) y0

不过这个公式是针对普通坐标轴和围绕(0,0)旋转的情况。而PIL(Python Imaging Library)中的旋转是顺时针的,使用的是“图形”坐标轴。而且,旋转是围绕图像的中心进行的。最后一个让人困惑的地方是,图像的大小可能会变化,这一点在最终结果中需要考虑到。

操作步骤是:先取原始点,减去图像的中心点,应用“图形坐标轴”修正后的旋转,找出新图像的大小,然后再把新图像的中心位置加回来。

使用图形坐标轴的旋转公式是:

x1 = cos(theta) x0 + sin(theta) y0
y1 = -sin(theta) x0 + cos(theta) y0

比如,点(16,32)减去中心点(16,16)得到(0,16)。顺时针旋转30度(根据你的图像)会得到一个新点,计算方式是:cos(-30)*0 + sin(-30)*16, -sin(-30)*0 + cos(-30)*16 = -8, 13.86。最后一步是把旋转后的位置的中心点加回来。

25

要计算一个旋转后的点的位置,你可以使用一种叫做旋转矩阵的东西。

下面是用JavaScript写的代码,可以用来计算旋转后的点:

function rotate(x, y, xm, ym, a) {
    var cos = Math.cos,
        sin = Math.sin,

        a = a * Math.PI / 180, // Convert to radians because that is what
                               // JavaScript likes

        // Subtract midpoints, so that midpoint is translated to origin
        // and add it in the end again
        xr = (x - xm) * cos(a) - (y - ym) * sin(a)   + xm,
        yr = (x - xm) * sin(a) + (y - ym) * cos(a)   + ym;

    return [xr, yr];
}

rotate(16, 32, 16, 16, 30); // [8, 29.856...]

撰写回答