Алгоритм поворота изображения на 90 градусов на месте? (Без дополнительной памяти) - PullRequest
37 голосов
/ 03 июня 2010

Во встроенном приложении C у меня есть большое изображение, которое я хотел бы повернуть на 90 градусов. В настоящее время я использую известный простой алгоритм , чтобы сделать это. Однако этот алгоритм требует, чтобы я сделал еще одну копию изображения. Я хотел бы избежать выделения памяти для копии, я бы предпочел повернуть ее на месте. Поскольку изображение не квадратное, это сложно. Кто-нибудь знает подходящий алгоритм?

Отредактировано, чтобы добавить пояснения, потому что люди спрашивают:

Я храню изображение в обычном формате:

// Images are 16 bpp
struct Image {
    int width;
    int height;
    uint16_t * data;
};

uint16_t getPixel(Image *img, int x, int y)
{
    return img->data[y * img->width + x];
}

Я надеюсь переместить содержимое массива data, а затем поменять местами переменные-члены width и height. Поэтому, если я начну с изображения 9x20 пикселей, а затем поверну его, я получу изображение 20x9 пикселей. Это меняет шаг изображения, что сильно усложняет алгоритм.

Ответы [ 9 ]

29 голосов
/ 03 июня 2010

Это может помочь: Транспонирование матрицы на месте .

(Возможно, вам также придется выполнить зеркалирование после транспонирования, как упоминает rlbond).

22 голосов
/ 03 июня 2010

Если вы читаете изображение из памяти в «неправильном порядке», это по сути то же самое, что и вращение. Это может или не может подходить для всего, что вы делаете, но здесь идет:

image[y][x] /* assuming this is the original orientation */
image[x][original_width - y] /* rotated 90 degrees ccw */
image[original_height - x][y] /* 90 degrees cw */
image[original_height - y][original_width - x] /* 180 degrees */
6 голосов
/ 03 июня 2010

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

uint16_t getPixel90(Image *img, int x, int y) 
{
    return img->data[(img->height - x) * img->width + y];
}

Где входной параметр x и y поменял размер с оригинала

2 голосов
/ 09 августа 2014

реальный ответ: нет, вы не можете без выделения памяти.

или вы должны использовать рекурсию, которая не будет работать с большими изображениями.

однако существуют методы, которые требуют меньше памяти, чем само изображение

например, вы можете взять точку A (x от 0 до ширины, y от 0 до высоты), вычислить ее новое местоположение, B, скопировать B в новое местоположение (C), прежде чем заменить ее на A и т. Д.

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

см. Статью в википедии, она ясно демонстрирует, что этого нельзя сделать для неквадратных изображений: вот ссылка снова: http://en.wikipedia.org/wiki/In-place_matrix_transposition

1 голос
/ 29 апреля 2017

Эта проблема заняла у меня довольно много времени, но при правильном подходе она очень проста.

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

Упрощение задачи

Рассмотрим следующую матрицу:

 1  2  3  4
 5  6  7  8
 9 10 11 12
13 14 15 16

Поверните на 90 градусов и посмотрите только на углы (цифры 1, 4, 16 и 13). Если у вас возникли проблемы с его визуализацией, постарайтесь написать заметку.

Теперь давайте рассмотрим следующее:

1 - - 2
- - - -
- - - -
4 - - 3

Поверните его на 90 градусов и обратите внимание, как числа вращаются по кругу: 2 становится 1, 3 становится 2, 4 становится 3, 1 становится 4.

Вращающиеся углы

Чтобы повернуть углы, необходимо определить все углы в терминах первого угла:

  • 1-й угол будет (i, j)
  • 2-й угол будет (SIZE - j, i)
  • 3-й угол будет (SIZE - i, SIZE - j)
  • 4-й угол будет (j, SIZE - i)

Обратите внимание, что массивы основаны на 0, поэтому SIZE также должен быть основан на 0. Теперь, когда вы поняли идею вращающихся углов, мы расширим идею «вращающихся углов» до «вращающихся квадрантов». Тот же принцип сохраняется.

Код

Вам нужно будет убедиться, что номер не перезаписан. Это означает, что вам нужно будет вращать 4 числа одновременно.

#include <algorithm>
#include <numeric>
#include <vector>

using std::iota;
using std::swap;
using std::vector;

// Rotates 4 numbers.
// e.g: 1, 2, 3, 4 becomes 4, 1, 2, 3
// int& means numbers are passed by reference, not copy.
void rotate4(int &a, int &b, int &c, int &d)
{
   swap(a, b);
   swap(b, c);
   swap(c, d);
}

void rotateMatrix(vector<vector<int>>& m) {
    int n = m.size();

    // NOTE: i and j from 0 to n/2 is a quadrant
    for (int i = 0; i < n/2; i++) {
    // NOTE : here + 1 is added to make it work when n is odd
    for (int j = 0; j < (n + 1)/2; j++) {
        int r_i = (n - 1) - i;
        int r_j = (n - 1) - j;

        rotate4(
             m   [i]   [j],
             m [r_j]   [i],
             m [r_i] [r_j],
             m   [j] [r_i]
        );
    }
    }
}

void fillMatrix(vector<vector<int>>& m) {
    int offset = 0;

    for (auto &i : m) {
        iota(i.begin(), i.end(), offset);
        offset += i.size();
    }
}

// Usage:
const int size = 8;
vector<vector<int>> matrix (size, vector<int>(size));
fillMatrix(matrix);
rotateMatrix(matrix);

печать

Для печати матрицы вы можете использовать:

#include <algorithm>
#include <iostream>
#include <iterator>

using std::copy;
using std::cout;
using std::ostream;
using std::ostream_iterator;
using std::vector;

ostream& operator<<(ostream& os, vector<vector<int>>& m) {
    for (auto const &i : m) {
        copy(i.begin(), i.end(), ostream_iterator<int>(os, " "));
        os << "\n";
    }

    return os;
}

// Usage
cout << matrix;
1 голос
/ 17 сентября 2014

Вот простой метод в Java,

    public static void rotateMatrix(int[][] a) {                                                                            
    int m =0;
    for(int i=0; i<a.length; ++i) {
        for(int j=m; j<a[0].length; ++j) {
            int tmp = a[i][j];
            a[i][j] = a[j][i];
            a[j][i] = tmp;
        }
        m++;
    }

    for(int i=0; i<a.length; ++i) {
        int end = a.length-1;
        for(int j=0; j<a[0].length; j++) {
            if(j>=end)
                break;
            int tmp = a[i][j];
            a[i][j] = a[i][end];
            a[i][end] = tmp;
            end--;
        }
    }
}
1 голос
/ 03 июня 2010

Это может быть слишком расплывчато, и не то, что вы ищете, но я все равно отправлю.

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

Таким образом, вы либо просматриваете каждый столбец пикселей (0-> columns / 2), и меняете их местами (так что вам нужна только временная память на 1 пиксель, а не все изображение), или циклически проходите по строкам для горизонтального пролистывания. Имеет ли это смысл? Будет разрабатывать / писать код, если нет ..

0 голосов
/ 21 января 2014

Это похоже на вращение 2D матрицы. Вот мой алгоритм, ниже которого вращается 2D матрица на 90 градусов. Это также работает для M X N. Возьмите транспонирование данной матрицы и затем поменяйте местами 1-й столбец с последним, 2-й столбец с 2-м последним столбцом и так далее. Вы также можете делать со строками вместо столбцов.

import java.io.*;
import java.util.*;

public class MatrixRotationTest
{
public static void main(String arg[])throws Exception
{
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    System.out.println("Enter the matrix rows:");
    int r = Integer.parseInt(br.readLine());
    System.out.println("Enter the matrix columns:");
    int c = Integer.parseInt(br.readLine());
    int[][] matrix = new int[r*c][r*c];
    for(int i=0;i<r;i++)
    {
        System.out.println("Enter row "+(i+1));
        for(int j=0;j<c;j++)
        {
            matrix[i][j] = Integer.parseInt(br.readLine());
        }
    }
    matrix = reverseMatrixColumns(transformMatrix(matrix),r,c);
    System.out.println("Rotated Matrix");
    for(int i=0;i<c;i++)
    {
        for(int j=0;j<r;j++)
        {
            System.out.print(matrix[i][j]+" ");
        }
        System.out.println();
    }
}

    //Transform the given matrix
public static int[][] transformMatrix(int[][] matrix)throws Exception
{
    for(int i=0;i<matrix.length;i++)
    {
        for(int j=i;j<matrix[0].length;j++)
        {
            int temp = matrix[i][j];
            matrix[i][j] = matrix [j][i];
            matrix[j][i] = temp;
        }
    }
}

    //Swap columns
public static int[][] reverseMatrixColumns(int[][] matrix,int r,int c)
{
    int i=0,j=r-1;
    while(i!=r/2)
    {
        for(int l=0;l<c;l++)
        {
            int temp = matrix[l][i];
            matrix[l][i] = matrix[l][j];
            matrix[l][j] = temp;
        }
        i++;
        j--;
    }
    return matrix;
}
}
0 голосов
/ 15 марта 2013

Вот моя попытка поворота матрицы на 90 градусов, которая является двухшаговым решением в С.
Сначала перенесите матрицу на место, а затем поменяйте местами столбцы.

#define ROWS        5
#define COLS        5

void print_matrix_b(int B[][COLS], int rows, int cols) 
{
    for (int i = 0; i <= rows; i++) {
        for (int j = 0; j <=cols; j++) {
            printf("%d ", B[i][j]);
        }
        printf("\n");
    }
}

void swap_columns(int B[][COLS], int l, int r, int rows)
{
    int tmp;
    for (int i = 0; i <= rows; i++) {
        tmp = B[i][l];
        B[i][l] = B[i][r];
        B[i][r] = tmp;
    }
}


void matrix_2d_rotation(int B[][COLS], int rows, int cols)
{
    int tmp;
    // Transpose the matrix first
    for (int i = 0; i <= rows; i++) {
        for (int j = i; j <=cols; j++) {
            tmp = B[i][j];
            B[i][j] = B[j][i];
            B[j][i] = tmp;
        }
    }
    // Swap the first and last col and continue until
    // the middle.
    for (int i = 0; i < (cols / 2); i++)
        swap_columns(B, i, cols - i, rows);
}



int _tmain(int argc, _TCHAR* argv[])
{
    int B[ROWS][COLS] = { 
                  {1, 2, 3, 4, 5}, 
                      {6, 7, 8, 9, 10},
                          {11, 12, 13, 14, 15},
                          {16, 17, 18, 19, 20},
                          {21, 22, 23, 24, 25}
                        };

    matrix_2d_rotation(B, ROWS - 1, COLS - 1);

    print_matrix_b(B, ROWS - 1, COLS -1);
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...