将图像旋转 90 度的算法? (没有额外的内存)

Posted

技术标签:

【中文标题】将图像旋转 90 度的算法? (没有额外的内存)【英文标题】:Algorithm to rotate an image 90 degrees in place? (No extra memory) 【发布时间】:2011-02-27 10:40:33 【问题描述】:

在嵌入式 C 应用程序中,我有一个大图像,我想将其旋转 90 度。目前我使用著名的简单algorithm 来执行此操作。但是,此算法需要我制作图像的另一个副本。我想避免为副本分配内存,我宁愿就地旋转它。由于图像不是方形的,这很棘手。有人知道合适的算法吗?

编辑添加澄清,因为人们在问:

我以通常的格式存储图像:

// 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 数组的内容,然后交换widthheight 成员变量。所以如果我从一个 9x20 像素的图像开始,然后旋转它,我最终会得到一个 20x9 像素的图像。这会改变图像的步幅,从而使算法变得非常复杂。

【问题讨论】:

您打算如何在不分配额外空间的情况下旋转非方形图像?您是否打算在此过程中交换 x/y 索引? 你能告诉我们一些细节是如何存储图像的吗? 哦,一个平面阵列...呃,我应该想到 一个有趣的问题。我想如果图像是单色的,每像素 1 位,这可能会给问题增加另一个级别的复杂性。 我在处理yuv420p图像帧时遇到了这个问题,我需要旋转90度然后将其转换为jpeg格式。我真的需要就地旋转它,因为图像类似于视频流,大约 25 fps,并且需要低延迟。谁能给我一个有效的算法? 【参考方案1】:

如果您以“错误的顺序”从内存中读取图像,则本质上与旋转图像相同。这可能适合也可能不适合您正在做的任何事情,但这里有:

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 */

【讨论】:

这基本上是我想说的,说得更优雅:) +1 因为这让我想到在 blit 到屏幕期间进行旋转。那时有一个屏幕缓冲区要写入,所以我可以使用传统的旋转算法。 我很确定您的 cwccw 已交换。【参考方案2】:

这个问题花了我相当长的时间,但如果你有正确的方法,它非常简单。

请注意,这只适用于方阵。矩形将要求您使用其他算法(转置和翻转)。如果你想就地做,那可能需要你临时调整数组的大小。

简化问题

考虑以下矩阵:

 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。

旋转角

为了旋转拐角,必须根据第一个拐角定义所有拐角:

第一个角是(i, j) 第二个角是(SIZE - j, i) 第三个角是(SIZE - i, SIZE - j) 第四个角是(j, SIZE - i)

请注意,数组是基于 0 的,因此 SIZE 也需要基于 0。(意思是,您需要减去 1)。

既然您了解了旋转角的概念,我们将把“旋转角”的概念扩展为“旋转象限”。同样的道理也成立。

代码

如果被覆盖,您需要确保没有数字。也就是说,您需要同时轮换 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;

【讨论】:

【参考方案3】:

这里有一个简单的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--;
        
    

【讨论】:

【参考方案4】:

真正的答案:不,你不能不分配一些内存。

或者你必须使用递归,这对于大图像会失败。

但是有些方法比图像本身需要更少的内存

例如,您可以取点 A(x 从 0 到宽度,y 从 0 到高度),计算它的新位置 B,在用 A 替换之前将 B 复制到新位置 (C),等等。

但是,该方法需要跟踪已移动的字节。 (使用旋转图像中每像素一位的位图)

请参阅***文章,它清楚地表明对于非方形图像无法做到这一点:这里又是链接:http://en.wikipedia.org/wiki/In-place_matrix_transposition

【讨论】:

【参考方案5】:

这类似于二维矩阵的旋转。这是我的算法,它将 2D 矩阵旋转 90 度。它也适用于 M X N。对给定矩阵进行转置,然后将第一列与最后一列交换,将第二列与最后一列交换,依此类推。您也可以使用行而不是列。

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;


【讨论】:

这仅在您分配的图像大于需要时才有效。例如。如果我有一个 1920x1080 的图像,您基本上是在建议我分配一个 1920x1920 的缓冲区并执行一种著名的“原位旋转方形图像”算法。这可能比拥有两个 1920x1080 缓冲区要好,但这仍然不是我想要的。【参考方案6】:

这是我对矩阵 90 度旋转的尝试,这是 C 中的 2 步解决方案。 首先将矩阵转置到位,然后交换列。

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

【讨论】:

如果矩阵不是方形的,那就不行了。方形的情况很简单,这就是为什么问题会询问非方形图像:-)【参考方案7】:

不确定旋转后你会做什么处理,但你可以不用管它,使用另一个函数从原始内存中读取旋转后的像素。

uint16_t getPixel90(Image *img, int x, int y) 

    return img->data[(img->height - x) * img->width + y];

输入参数 x 和 y 与原来的尺寸交换了

【讨论】:

如果 x 大于图像高度,您会得到负 x 索引【参考方案8】:

这可能会有所帮助:In-place matrix transposition。

(正如 rlbond 所提到的,您可能还需要在转置之后进行一些镜像)。

【讨论】:

请注意,移调并不是他想要的——他也需要水平镜像。 @rlbond:不过,这很容易做到。我将编辑答案以提及这一点。谢谢。 是的,这看起来像我所追求的,谢谢。不幸的是,这些算法似乎需要每个像素进行乘法和除法,这在嵌入式 CPU 上过于昂贵...... 不幸的是,这种方法很慢......我遇到了同样的问题,我选择了辅助内存分配而不是无休止的字节复制。【参考方案9】:

这可能太模糊了,不是你要找的,但我还是要发帖。

如果您将图像视为 2d 像素数组,则只需反转顶层或嵌套数组的顺序,具体取决于您是要水平翻转还是垂直翻转..

因此,您可以循环遍历每个像素列 (0->columns/2),然后交换它们(因此您只需要 1 个像素的临时内存,而不是整个图片),或者循环遍历行以进行水平翻转..那有意义吗?如果没有,将详细说明/编写代码..

【讨论】:

这是有道理的,但不幸的是我需要旋转而不仅仅是翻转。 非常有趣的想法,需要以编程方式检查奇数列。

以上是关于将图像旋转 90 度的算法? (没有额外的内存)的主要内容,如果未能解决你的问题,请参考以下文章

Leecode-48:旋转图像(矩阵顺时针旋转90度)

面试题 01.07. 旋转矩阵

方阵原地顺时针旋转90度

matlab中figure的图像旋转

旋转矩阵

图像旋转