如何旋转图像?

时间:2021-06-14 04:16:52

See also: Why is my image rotation algorithm not working?

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

This question isn't language specific, and is a math problem. I will however use some C++ code to explain what I need as I'm not too hot on math.

这个问题不是语言特定的,而且是一个数学问题。然而,我将使用一些C ++代码来解释我需要什么,因为我对数学不太热。

Here's how the image is composed:

以下是图像的组成方式:

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;

Here's the prototype for the function I'm trying to create:

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

ImageMatrix rotateImage(ImageMatrix image, double angle);

I'd like to rotate only the first two indices (rows and columns) but not the channel.

我想只旋转前两个索引(行和列)而不是通道。

4 个解决方案

#1


The usual way to solve this is by doing it backwards. Instead of calculating where each pixel in the input image ends up in the output image, you calculate where each pixel in the output image is located in the input image (by rotationg the same amount in the other direction. This way you can be sure that all pixels in the output image will have a value.

解决这个问题的通常方法是向后做。您不必计算输入图像中每个像素在输出图像中的最终位置,而是计算输出图像中每个像素在输入图像中的位置(通过在另一个方向上旋转相同的量。这样您就可以确定输出图像中的所有像素都将具有值。

output = new Image(input.size())

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

There are different ways to do interpolation. For the formula of rotation I think you should check https://en.wikipedia.org/wiki/Rotation_matrix#In_two_dimensions

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

But just to be nice, here it is (rotation of point (x,y) angle degrees/radians):

但只是为了好,这里是(点(x,y)角度/弧度的旋转):

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

#2


Note there's another solution apart from rotation matrices, that doesn't loose image information through aliasing. You can separate 2D image rotation into skews and scalings, which preserve the image quality.

请注意除了旋转矩阵之外还有另一种解决方案,即不会通过混叠来丢失图像信息。您可以将2D图像旋转分离为倾斜和缩放,从而保持图像质量。

Here's a simpler explanation

这是一个更简单的解释

#3


To rotate an image, you create 3 points:

要旋转图像,您需要创建3个点:

A----B 
|
|
C

and rotate that around A. To get the new rotated image you do this:

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

  • rotate ABC around A in 2D, so this is a single euler rotation
  • 在2D中围绕A旋转ABC,因此这是单个euler旋转

  • traverse in the rotated state from A to B. For every pixel you traverse also from left to right over the horizontal line in the original image. So if the image is an image of width 100, height 50, you'll traverse from A to B in 100 steps and from A to C in 50 steps, drawing 50 lines of 100 pixels in the area formed by ABC in their rotated state.
  • 从A到B的旋转状态进行遍历。对于每个像素,您也在原始图像的水平线上从左到右遍历。因此,如果图像是宽度为100,高度为50的图像,则您将以100步从A到B,以50步从A到C,在ABC旋转状态下在ABC形成的区域中绘制50行100像素。

This might sound complicated but it's not. Please see this C# code I wrote some time ago: rotoZoomer by me

这可能听起来很复杂,但事实并非如此。请看我前面写的这个C#代码:rotoZoomer由我

When drawing, I alter the source pointers a bit to get a rubber-like effect, but if you disable that, you'll see the code rotates the image without problems. Of course, on some angles you'll get an image which looks slightly distorted. The sourcecode contains comments what's going on so you should be able to grab the math/logic behind it easily.

绘图时,我稍微改变源指针以获得类似橡胶的效果,但如果禁用它,您将看到代码旋转图像没有问题。当然,在某些角度你会得到一个看起来有点扭曲的图像。源代码包含正在进行的注释,因此您应该能够轻松地获取其背后的数学/逻辑。

If you like Java better, I also have made a java version once, 14 or so years ago ;) -> http://www.xs4all.nl/~perseus/zoom/zoom.java

如果你更喜欢Java,我也已经制作了一个java版本,大约14年前;) - > http://www.xs4all.nl/~perseus/zoom/zoom.java

#4


It seems like the example you've provided is some edge detection kernel. So if what you want to is detect edges of different angles you'd better choose some continuous function (which in your case might be a parametrized gaussian of x1 multiplied by x2) and then rotate it according to formulae provided by kigurai. As a result you would be able to produce a diskrete kernel more efficiently and without aliasing.

您提供的示例似乎是一些边缘检测内核。因此,如果您想要检测不同角度的边缘,您最好选择一些连续函数(在您的情况下可能是x1的参数化高斯乘以x2),然后根据kigurai提供的公式旋转它。因此,您将能够更有效地生成磁盘内核并且没有别名。

#1


The usual way to solve this is by doing it backwards. Instead of calculating where each pixel in the input image ends up in the output image, you calculate where each pixel in the output image is located in the input image (by rotationg the same amount in the other direction. This way you can be sure that all pixels in the output image will have a value.

解决这个问题的通常方法是向后做。您不必计算输入图像中每个像素在输出图像中的最终位置,而是计算输出图像中每个像素在输入图像中的位置(通过在另一个方向上旋转相同的量。这样您就可以确定输出图像中的所有像素都将具有值。

output = new Image(input.size())

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

There are different ways to do interpolation. For the formula of rotation I think you should check https://en.wikipedia.org/wiki/Rotation_matrix#In_two_dimensions

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

But just to be nice, here it is (rotation of point (x,y) angle degrees/radians):

但只是为了好,这里是(点(x,y)角度/弧度的旋转):

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

#2


Note there's another solution apart from rotation matrices, that doesn't loose image information through aliasing. You can separate 2D image rotation into skews and scalings, which preserve the image quality.

请注意除了旋转矩阵之外还有另一种解决方案,即不会通过混叠来丢失图像信息。您可以将2D图像旋转分离为倾斜和缩放,从而保持图像质量。

Here's a simpler explanation

这是一个更简单的解释

#3


To rotate an image, you create 3 points:

要旋转图像,您需要创建3个点:

A----B 
|
|
C

and rotate that around A. To get the new rotated image you do this:

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

  • rotate ABC around A in 2D, so this is a single euler rotation
  • 在2D中围绕A旋转ABC,因此这是单个euler旋转

  • traverse in the rotated state from A to B. For every pixel you traverse also from left to right over the horizontal line in the original image. So if the image is an image of width 100, height 50, you'll traverse from A to B in 100 steps and from A to C in 50 steps, drawing 50 lines of 100 pixels in the area formed by ABC in their rotated state.
  • 从A到B的旋转状态进行遍历。对于每个像素,您也在原始图像的水平线上从左到右遍历。因此,如果图像是宽度为100,高度为50的图像,则您将以100步从A到B,以50步从A到C,在ABC旋转状态下在ABC形成的区域中绘制50行100像素。

This might sound complicated but it's not. Please see this C# code I wrote some time ago: rotoZoomer by me

这可能听起来很复杂,但事实并非如此。请看我前面写的这个C#代码:rotoZoomer由我

When drawing, I alter the source pointers a bit to get a rubber-like effect, but if you disable that, you'll see the code rotates the image without problems. Of course, on some angles you'll get an image which looks slightly distorted. The sourcecode contains comments what's going on so you should be able to grab the math/logic behind it easily.

绘图时,我稍微改变源指针以获得类似橡胶的效果,但如果禁用它,您将看到代码旋转图像没有问题。当然,在某些角度你会得到一个看起来有点扭曲的图像。源代码包含正在进行的注释,因此您应该能够轻松地获取其背后的数学/逻辑。

If you like Java better, I also have made a java version once, 14 or so years ago ;) -> http://www.xs4all.nl/~perseus/zoom/zoom.java

如果你更喜欢Java,我也已经制作了一个java版本,大约14年前;) - > http://www.xs4all.nl/~perseus/zoom/zoom.java

#4


It seems like the example you've provided is some edge detection kernel. So if what you want to is detect edges of different angles you'd better choose some continuous function (which in your case might be a parametrized gaussian of x1 multiplied by x2) and then rotate it according to formulae provided by kigurai. As a result you would be able to produce a diskrete kernel more efficiently and without aliasing.

您提供的示例似乎是一些边缘检测内核。因此,如果您想要检测不同角度的边缘,您最好选择一些连续函数(在您的情况下可能是x1的参数化高斯乘以x2),然后根据kigurai提供的公式旋转它。因此,您将能够更有效地生成磁盘内核并且没有别名。