Algorithme pour faire tourner une image à 90 degrés en place? (Pas de mémoire supplémentaire)

Dans un C embarqué application, j'ai une grande image que j'aimerais faire pivoter de 90 degrés. Actuellement, j'utilise l'algorithme simple bien connu pour le faire. Cependant, cet algorithme nécessite moi de faire une autre copie de l'image. J'aimerais éviter d'allouer de la mémoire pour une copie, je préférerais la faire tourner en place. Comme l'image n'est pas carrée, c'est délicat. Personne ne sait d'un algorithme convenable?

édité pour ajouter la clarification, parce que les gens demande:

je stocke une image dans le format habituel:

// 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];
}

j'espère déplacer le contenu du tableau data autour, puis échanger sur les variables membres width et height . Donc si je commence avec une image de 9x20 pixels, puis que je la tourne, je vais finir avec une image de 20x9 pixels. Cela modifie la foulée de l'image, ce qui complique l'algorithme beaucoup.

34
demandé sur user9876 2010-06-03 21:38:04

9 réponses

Ce qui pourrait aider: En place de la matrice de transposition .

(vous pourriez aussi avoir à faire un miroir après la transposition, comme le mentionne rlbond).

28
répondu 2010-06-03 17:51:32

si vous lisez l'image de mémoire dans "le mauvais ordre", c'est essentiellement la même chose que de la tourner. Cela peut ou ne peut pas être approprié pour tout ce que vous faites, mais voici:

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 */
21
répondu Matti Virkkunen 2017-06-06 05:01:44

pas sûr de ce que le traitement que vous ferez après la rotation, mais vous pouvez le laisser seul et utiliser une autre fonction pour lire pixel tourné de la mémoire d'origine.

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

où les paramètres d'entrée x et y ont changé la dimension de l'original

6
répondu sjchoi 2010-06-03 18:29:32

la vraie réponse: non, on ne peut pas sans allouer un peu de mémoire.

ou vous devez utiliser la récursion, qui échouera avec les grandes images.

cependant il y a des méthodes qui nécessitent moins de mémoire que l'image elle-même

par exemple, vous pouvez prendre le point A (x de 0 à Largeur, y de 0 à hauteur), calculer son nouvel emplacement, B, copier B à son nouvel emplacement (C) avant de le remplacer par A, etc..

mais, cette méthode nécessiterait de garder une trace des octets qui ont déjà été déplacés. (utilisant une image bitmap d'un bit par pixel dans l'image tournée)

voir l'article de wikipedia, il démontre clairement que cela ne peut pas être fait pour des images non carrées: voici le lien à nouveau: http://en.wikipedia.org/wiki/In-place_matrix_transposition

2
répondu Pizzaiola Gorgonzola 2014-08-09 18:52:42

c'est peut-être trop vague, et ce n'est pas ce que vous cherchez, mais je vais le poster quand même.

si vous considérez une image comme un tableau 2d de pixels, vous n'avez qu'à inverser l'ordre du tableau de haut niveau ou du tableau imbriqué, selon que vous voulez un retournement horizontal ou vertical..

de sorte que vous feriez soit boucle à travers chaque colonne de pixel (0 - > colonnes/2), et les échanger (de sorte que vous avez seulement besoin de mémoire temporaire pour 1 pixel, pas l'image entière), ou boucle à travers les rangées pour le retournement horizontal.. Cela fait-il sens? Développera / écrira le code si ce n'est pas le cas..

1
répondu Jeriko 2010-06-03 17:46:25

Voici une méthode simple en 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
répondu Kay Kay 2014-09-17 04:37:01

Ce problème m'a pris du temps, mais si vous avez la bonne approche, c'est très simple.

Note Ceci ne fonctionne que pour une matrice carrée . Un rectangle vous demandera d'utiliser l'autre algorithme (transposer et flip). Si vous voulez le faire en place, il se peut que vous ayez besoin de redimensionner temporairement le tableau.

simplifier le problème

envisager la matrice suivante:

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

pivote de 90 degrés, et ne regarde que les coins (numéros 1, 4, 16 et 13). Si vous avez des problèmes pour la visualiser, utilisez un post-it.

Maintenant, considérons le suivant:

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

tourner de 90 degrés, et de noter comment les nombres sont tournés d'une manière circulaire: 2 devient 1, 3 devient 2, 4 devient 3, 1 devient 4.

Tourner les coins ronds

In pour tourner des coins, il est nécessaire de définir tous les coins en termes du premier coin:

  • 1er coin serait (i, j)
  • 2ème coin serait (SIZE - j, i)
  • 3e coin serait (SIZE - i, SIZE - j)
  • 4ème coin serait (j, SIZE - i)

notez que les tableaux sont basés sur 0, donc SIZE devra être basé sur 0 aussi. (ce qui signifie que vous devrez soustraire 1).

Maintenant que vous avez compris l'idée de tourner les coins, nous allons développer l'idée de "tourner les coins ronds" à "rotation quadrants". Le principe est le même.

Code

vous aurez besoin de s'assurer aucun nombre si réécrit. Ce qui signifie que vous aurez besoin de tourner 4 numéros à la fois simultanément.

#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);

impression

Pour imprimer la matrice, vous pouvez utiliser:

#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;
0
répondu arboreal84 2017-04-30 04:18:48

c'est similaire à la rotation de la matrice 2D. Voici mon algorithme ci-dessous qui fait tourner la matrice 2D de 90 degrés. Il fonctionne aussi pour M X N. prenez la transposition de la matrice donnée et puis changez la première colonne avec la dernière, la deuxième colonne avec la deuxième dernière colonne et ainsi de suite. Vous pouvez aussi faire avec les lignes au lieu des colonnes.

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;
}
}
-2
répondu Akshay 2014-01-21 06:28:37

voici ma tentative de rotation de matrix 90 deg qui est une solution en 2 étapes en C.

D'abord transposer la matrice en place puis échangez les cols.

#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;
}
-3
répondu user1596193 2013-03-15 13:28:05