Почему мой алгоритм поворота изображения не работает? - PullRequest
4 голосов
/ 30 марта 2009

Попытки 1 и 2:

Примечание: Удалены первые попытки сократить размер вопроса. См. Вики сообщества для предыдущих попыток.

Попытка 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;
}

С этой матрицей-прототипом ...

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

... prototype.GetRotatedCopy (0) (это правильно) ...

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

... prototype.GetRotatedCopy (90) (неверно) ...

-2 0 0 
-2 0 2 
0 0 2 

... prototype.GetRotatedCopy (180) (неверно - но вроде логично?) ...

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

... prototype.GetRotatedCopy (270) (неверно - почему это так же, как вращение 0?) ...

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

Решение:

Как указал Марк Рэнсом, я должен использовать радианы, а не градусы; Я изменил свой код следующим образом:

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 

Ответы [ 5 ]

4 голосов
/ 30 марта 2009

Алгоритм, если я не прочитал его неправильно, кажется, вращается вокруг точки 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]; 
   } 
} 

Это в основном регулирует центр вращения от верхнего левого угла к центру изображения.

3 голосов
/ 31 марта 2009

Вот полный пример, который я взломал: Я думаю, среди прочего вы, возможно, не использовали радианы (которые мы все должны использовать и любить). Я держу новые координаты в двойных числах, что, кажется, делает их менее привередливыми. Обратите внимание, что я не проверяю границы, что мне следует, но мне было лень.

Если вам нужно более быстрое вращение, вы всегда можете использовать сдвиг, как этот пример .

#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");
  } 
}
2 голосов
/ 31 марта 2009

короткий ответ: вы делаете это неправильно.

По сути, это проблема интерполяции, и то, как вы к ней подходите, порождает разрывы. По сути, это связано с тем, что поворот решетки (регулярной сетки, на которую выложено изображение) не приводит к повторной выборке на той же решетке, за исключением очень особых случаев.

Между прочим, не существует единственно правильного способа сделать это, но существуют различные компромиссы в отношении скорости и точности, а также того, что можно предположить относительно исходного сигнала (изображения).

Так каковы ваши параметры дизайна? Вам нужно, чтобы это было очень быстро или очень точно? Как вы хотите обрабатывать псевдонимы?

2 голосов
/ 30 марта 2009

ваши формулы для newRow и newColumn переключаются. Помните, что строка = y и столбец = x.

Вращение

1 голос
/ 30 марта 2009

Проблема в том, что ваши обращения к памяти выходят за пределы.

После поворота ваши 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];
                }
        }
}
...