我的图像旋转算法为什么不能正常工作?

5

尝试1和2:

注意: 删除了前几次尝试以缩小问题大小。请参阅社区wiki以查看之前的尝试。

尝试3:

根据fuzzy-waffle的示例,我已经实现了以下内容,但似乎无法正常工作。有什么想法我可能做错了吗?

ImageMatrix ImageMatrix::GetRotatedCopy(VDouble angle)
{
    // Copy the specifications of the original.
    ImageMatrix &source = *this;
    ImageMatrix &target = CreateEmptyCopy();

    double centerX = ((double)(source.GetColumnCount()-1)) / 2;
    double centerY = ((double)(source.GetRowCount()-1)) / 2;

    // Remember: row = y, column = x
    for (VUInt32 y = 0; y < source.GetRowCount(); y++)
    {
        for (VUInt32 x = 0; x < source.GetColumnCount(); x++)
        {
            double dx = ((double)x) - centerX;
            double dy = ((double)y) - centerY;

            double newX = cos(angle) * dx - sin(angle) * dy + centerX;
            double newY = cos(angle) * dy + sin(angle) * dx + centerY;

            int ix = (int)round(newX);
            int iy = (int)round(newY);

            target[x][y][0] = source[ix][iy][0];
        }
    }

    return target;
}

通过这个原型矩阵,我们可以更好地理解IT技术的相关概念。

1 2 1 
0 0 0 
-1 -2 -1 

... prototype.GetRotatedCopy(0)(这是正确的)...


意思是获取原型对象的旋转副本,参数为0。
1 2 1 
0 0 0 
-1 -2 -1 

... prototype.GetRotatedCopy(90) (incorrect) ...

-2 0 0 
-2 0 2 
0 0 2 

... prototype.GetRotatedCopy(180)(不正确,但有一定的逻辑性?)...

原意:获取旋转180度后的原型副本(不正确,但有一定的逻辑性?)
0 -1 -2 
1 0 -1 
2 1 0 

...prototype.GetRotatedCopy(270)(不正确 - 为什么这与0度旋转相同?)...

意思是原型的GetRotatedCopy(270)方法有问题,为什么它和0度旋转一样呢?
1 2 1 
0 0 0 
-1 -2 -1 

解决方案:

正如Mark Ransom指出的那样,我应该使用弧度而不是角度;我已经根据以下方式调整了我的代码:

ImageMatrix ImageMatrix::GetRotatedCopy(VDouble degrees)
{
    // Copy the specifications of the original.
    ImageMatrix &source = *this;
    ImageMatrix &target = CreateEmptyCopy();
    
    // Convert degree measurement to radians.
    double angle = degrees / 57.3;
    
    // ... rest of code as in attempt #3 ...

感谢各位的帮助!

1 2 1 
0 0 0 
-1 -2 -1 

1 2 1 
0 0 0 
-1 -2 -1 

-1 0 1 
-2 0 2 
-1 0 1 

-1 -2 -1 
0 0 0 
1 2 1 

1 0 -1 
2 0 -2 
1 0 -1 

你的意思是输出使用newRow和newColumn(而不是targetRow和targetColumn)吗? - lc.
1
你有没有可能使用角度而不是弧度?请仔细阅读fuzzy-waffle的答案。 - Mark Ransom
啊,是的 - 我刚刚更新了我的问题,并加入了方法调用。我会尝试使用弧度。 - Nick Bolton
1
附注:最好将sin(angle)和cos(angle)移出循环,因为它们不会改变。 - Derek E
5个回答

4

除非我读错了算法,否则它似乎围绕着点0,0旋转,这不是你想要的。也许在插入行和列值之前,你需要将高度/2和宽度/2加入其中。

for (int y = 0; y < 10; y++) { 
   for (int x = 0; x < 10; x++) { 
      VUInt32 newX = (cos(angle) * (x-5)) - (sin(angle) * (y-5)); 
      VUInt32 newY = (sin(angle) * (x-5)) + (cos(angle) * (y-5)); 
      target[newY][newX][0] = source[y][x][0]; 
   } 
} 

这基本上是将旋转中心从左上角调整到图像中心。

for (int y = 0; y < 10; y++) { for (int x = 0; x < 10; x++) { VUInt32 newX = (cos(angle) * (x-5)) - (sin(angle) * (y-5)); VUInt32 newY = (sin(angle) * (x-5)) + (cos(angle) * (y-5)); target[newY][newX][0] = source[y][x][0]; } } - KingErroneous
只需在 newX 和 newY 的计算中用 x-(width/2) 和 y-(height/2) 替换 x 和 y。 - KingErroneous

3
简短回答:你的做法是错误的。
这本质上是一个插值问题,而你的方法引入了不连续性。基本上,这是因为旋转晶格(图像所在的规则网格)并不会导致在同一晶格上进行另一次采样,除非是非常特殊的情况。
顺便说一下,没有单一正确的方法来解决这个问题,但是有各种权衡考虑,包括速度和准确性以及原始信号(图像)可以假设什么。
那么你的设计参数是什么?你需要这个非常快还是非常准确?你想如何处理混叠?

这是一个学术上的初步尝试,所以就设计要求而言,我会说“只要能够工作就行”。 - Nick Bolton
好的r3n,没问题,但你必须意识到旋转图像并不等同于旋转该图像的i、j索引。尝试以下操作以了解:1)反转旋转,遍历目标图像而非源图像。2)使用最接近的源值。这并不是“正确”的方法,但可以尝试一下。 - simon

3

这里是我编写的一个完整示例: 我认为你可能还没有使用弧度(我们都应该使用和喜欢)。我将新坐标保留为双精度,这似乎使它不那么棘手。请注意,我没有进行边界检查,但我很懒。

如果您需要更快的旋转,可以像这样使用剪切 示例

#include <math.h>
#include <stdio.h>

#define SIZEX 3
#define SIZEY 3

int source[SIZEX][SIZEY] = {
  { 1, 0, 0 },
  { 0, 1, 0 },
  { 0, 0, 1 }
};

int target[SIZEX][SIZEY];

int main () {
  double angle = M_PI/2.0;

  memset(target,0,sizeof(int)*SIZEX*SIZEY);

  double centerX = ((double)(SIZEX-1))/2.0;
  double centerY = ((double)(SIZEY-1))/2.0;

  for (int y = 0; y < SIZEY; y++) {
    for (int x = 0; x < SIZEX; x++) {
        double dx = ((double)x)-centerX;
        double dy = ((double)y)-centerY;
        double newX = cos(angle)*dx-sin(angle)*dy+centerX;
        double newY = cos(angle)*dy+sin(angle)*dx+centerY;

        int ix = (int) round(newX);
        int iy = (int) round(newY);
        target[x][y] = source[ix][iy];
    }
  }

  for (int i=0;i<SIZEY;i++) {
    for (int j=0;j<SIZEX;j++) {
      printf("%d ", target[j][i]);
    }
    printf("\n");
  } 
}

非常感谢您的努力,我会尽快尝试这个。 - Nick Bolton
@fuzzy-waffle:我尝试实现你的图像旋转代码,但是没有成功。请问你能给我一些指导吗?谢谢! - Nick Bolton
@fuzzy-waffle:顺便看一下我的答案中的第三个尝试。 - Nick Bolton

2

你的newRow和newColumn公式被颠倒了。请记住,row = y,column = x。

旋转


1

问题在于您的内存访问超出了边界。

旋转后,您的NewRow和NewColumn可能大于图像的原始宽度/高度。它们甚至可能是负数。如果您不注意这一事实,您将得到垃圾数据(最好的情况)或崩溃。

处理此问题的最常见方法是忽略所有这些像素。您还可以夹紧或围绕有效间隔进行包装。这会产生填充或平铺效果。

这里展示了忽略外部像素的情况。

int width = 10;
int height = 10;
for (int row = 0; row < height; row++)
{
        for (int column = 0; column < width; column++)
        {
                int newRow = (cos(angle) * row) - (sin(angle) * column);
                int newColumn = (sin(angle) * row) + (cos(angle) * column);

                if ((newRow >=0) && (newRow < width) && 
                   (newColumn >=0) && (newColumn < height))
                {
                  target[row][column][0] = source[newRow][newColumn][0];
                }
        }
}

或者他可以计算旋转图像的新长度和高度。 - KansaiRobot

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