如何旋转一张图片?

15

也请参见:为什么我的图像旋转算法不起作用?

这个问题不是特定于某种编程语言的,而是一个数学问题。然而,我将使用一些C++代码来解释我需要什么,因为我没有经验来表达这个问题所需的数学方程(但如果您知道这个问题,我会有兴趣学习)。

以下是图像的组成方式:

ImageMatrix image;
image[0][0][0] = 1;
image[0][1][0] = 2;
image[0][2][0] = 1;
image[1][0][0] = 0;
image[1][1][0] = 0;
image[1][2][0] = 0;
image[2][0][0] = -1;
image[2][1][0] = -2;
image[2][2][0] = -1;

这是我正在尝试创建的函数的原型:

ImageMatrix rotateImage(ImageMatrix image, double angle);

我想要旋转仅限于前两个索引(行和列),而不是通道。


旋转多少?任意角度还是90度的倍数? - shoosh
@shoosh:正如原型所示(双角度符号),这必须使用任何值的双精度类型。 - Nick Bolton
4个回答

37

通常解决这个问题的方法是反向计算。不是计算输入图像中每个像素在输出图像中的位置,而是计算输出图像中每个像素在输入图像中的位置(通过以相同的角度相反的方向旋转)。这样,您可以确保输出图像中的所有像素都具有值。

output = new Image(input.size())

for each pixel in input:
{
  p2 = rotate(pixel, -angle);
  value = interpolate(input, p2)
  output(pixel) = value
}

有不同的插值方法。对于旋转公式,我认为你应该查看https://en.wikipedia.org/wiki/Rotation_matrix#In_two_dimensions

但是出于友好,这里是它(点(x,y)绕角度/弧度旋转):

 newX = cos(angle)*x - sin(angle)*y
 newY = sin(angle)*x + cos(angle)*y

谢谢kigurai。我尝试实现了这个,但运气不太好,或许你可以看一下?https://dev59.com/6EfRa4cB1Zd3GeqP5wyA - Nick Bolton

3
为了旋转一张图片,您需要创建3个点:
A----B 
|
|
C

将图像围绕点A旋转。要获取新的旋转图像,请执行以下操作:

  • 在2D中将ABC围绕A旋转,这是一个单一的欧拉旋转;
  • 在旋转状态下从A到B遍历。对于您遍历的每个像素,还要在原始图像的水平线上从左到右遍历。因此,如果图像具有100像素宽度和50像素高度,则您将在100个步骤中从A到B遍历,在50个步骤中从A到C遍历,并在其旋转状态下形成的ABC区域内绘制100像素的50条线。

这听起来可能很复杂,但实际上并不是。请参见我之前编写的这段C#代码:rotoZoomer by me

在绘制时,我有点改变源指针以获得橡皮样的效果,但如果您禁用它,就会看到该代码毫无问题地旋转图像。当然,在某些角度上,您会得到看起来略微扭曲的图像。源代码包含了正在进行的注释,因此您应该能够轻松掌握其背后的数学/逻辑。

如果您更喜欢Java,我曾经也制作过一个Java版本,大约14年前;请参见:http://www.xs4all.nl/~perseus/zoom/zoom.java


2

有趣,但我不理解大多数数学术语,如“奈奎斯特频率”或“图像的频谱”。你能指导我阅读背景资料吗?这是我自己可以探索的东西还是需要真正的数学教育?(我是一名高中数学最后一年的学生。) - Iraimbilanja
@Iraimbilanja:信号处理理论是第二或第三年的电子工程课程。首先要知道如何做积分微积分。 - Mr Fooz
总有维基百科:http://en.wikipedia.org/wiki/Nyquist_frequency 将频率视为图像的频谱图:它相当于音频播放器的图形分析器的输出,只是在2D中。它显示了图像中最可见的频率以及方向。 - heeen
这篇论文可能有些复杂,但实际上该方法是基于将行向左或向右移动或将列向上或向下移动(剪切),我会编辑一个更简单的例子。 - heeen
多斜率方法是唯一一种高质量索引图像旋转方法,并且它可以避免RGBA图像的软化。这个答案需要得到顶部的赞成票。 - Malcolm McLean
“别名引起的信息丢失”?该算法的输出质量并不比直接算法更好。因为它使用了3个剪切,每次沿1D进行3倍插值,而另一个算法只需要2倍(每个维度一个)。所以,除非您使用良好的插值方法,否则质量可能会更差。然而,这个算法是快速的。通常比其他算法快得多。 - Cris Luengo

0

看起来你提供的例子是一些边缘检测核。所以,如果你想检测不同角度的边缘,最好选择一些连续函数(在你的情况下可能是x1乘以x2的参数化高斯函数),然后根据kigurai提供的公式旋转它。结果,你将能够更有效地生成一个离散核,而且不会出现混叠。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接