在嵌入式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
数组的内容,然后交换width
和height
成员变量。因此,如果我从9x20像素图像开始,然后旋转它,我将最终得到一个20x9像素的图像。这改变了图像的步幅,这使算法复杂化。
答案 0 :(得分:29)
这可能会有所帮助:In-place matrix transposition。
(你可能还需要在换位后做一些镜像,正如rlbond提到的那样)。
答案 1 :(得分:22)
如果您以“错误的顺序”从内存中读取图像,则它与旋转图像基本相同。这可能适用于你正在做的任何事情,也可能不适合,但这里有:
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 */
答案 2 :(得分:6)
不确定旋转后你将做什么处理,但你可以不管它并使用另一个函数从原始内存中读取旋转的像素。
uint16_t getPixel90(Image *img, int x, int y)
{
return img->data[(img->height - x) * img->width + y];
}
输入参数x和y已从原始
交换尺寸答案 3 :(得分:2)
真正的答案:不,你不能不分配一些记忆。
或者你必须使用递归,这会因大图像而失败。
然而,有些方法需要比图像本身更少的内存
例如,你可以取点A(x从0到宽度,y从0到高度),计算它的新位置,B,将B复制到它的新位置(C),然后用A替换它等。
但是,该方法需要跟踪已经移动的字节。 (使用旋转图像中每像素一位的位图)
请参阅维基百科文章,它清楚地表明无法对非方形图片执行此操作:此处再次显示链接:http://en.wikipedia.org/wiki/In-place_matrix_transposition
答案 4 :(得分:1)
这可能太模糊了,不是你想要的,但我还是要发帖。
如果您将图像视为二维像素数组,则只需要颠倒顶级或嵌套数组的顺序,具体取决于您是想要水平还是垂直翻转..
因此,您要遍历每个像素列(0->列/ 2),并交换它们(因此您只需要1个像素的临时存储器,而不是整个图片),或循环遍历行以进行水平翻转。 。 那有意义吗?如果没有详细说明/编写代码..
答案 5 :(得分:1)
这是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--;
}
}
}
答案 6 :(得分:1)
这个问题花了我很长时间,但如果你有正确的方法,这很简单。
请注意,这仅适用于方形矩阵。矩形将要求您使用其他算法(转置和翻转)。如果您想这样做,可能需要暂时调整阵列大小。
考虑以下矩阵:
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;
答案 7 :(得分:-1)
这类似于2D矩阵的旋转。下面是我的算法,它将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;
}
}
答案 8 :(得分:-3)
这是我对矩阵90度旋转的尝试,这是C中的两步解决方案 首先将矩阵转置到位,然后交换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;
}