A while ago a friend encountered such a problem: the image in situ clockwise rotation 90 degrees, do not open up new space. This is a seemingly simple question, and it is not easy to study and find out. After some exploration, finally found the correct algorithm, but when the use of OPENCV implementation, have encountered difficulties and have a hard time to find the problem.
First of all, to solve this problem, first simplified to 90 degrees in situ rotation of a MXN matrix A (note is not nxn phalanx). For 2x3 matrix A = {1,2,3;4,5,6}, its target is matrix B = {4,1;5,2;6,3}. Because it is in situ rotation, here A and B should point to the same size of 6 memory space.
Here is an important derivation of the formula, that is b[i][j] = A[m-1-j][i], the reader can make their own scrutiny, is the cornerstone of the later algorithm.
Okay, so if B is pointing to a new open memory, the problem is simple, as long as the space to traverse B is assigned a element by the formula above. However, for in-situ rotation is faced with a problem, give B[i][j] when assigned, the position of the original value is overwritten, how to deal with. The intuitive solution is to exchange elements, which will be exchanged between the b[i][j] and A[m-1-j][i] elements. But the new problem comes out, if you simply adopt this Exchange policy issue as follows:
Note: A and B point to the same size 6 memory space, with the first address being the same name S, then b[i][j] = s[i*m+j],a[i][j] = s[i*n+j].
S s+5
1 2 3 4 5 6
B[0][0] <-> a[1][0] (swap locations for elements 1 and 4 in memory space)
S s+5
4 2 31 5 6
B[0][1] <-> a[0][0] (element 2 in the memory space should be exchanged with the original element 1, but the position is now not 1)
So, a new strategy is needed, and if you find that the element that you are exchanging is already replaced, you need to continue to find where the element is swapped. The new algorithm core pseudo code is as follows: (reference)
For i = 0 to N-1:
For j = 0 to M-1:
///B[I][J] The elements before are correct
if (I*M+J) < ((m-1-j) *n+i)://Replace element behind B[i][j] position
swap (B[i][j], a[m-1-j][i]);
Else://To replace the element before B[i][j], has been replaced, needs to iterate to find where the element is replaced, definitely behind B[I][J]
Tar_i = m-1-j;
Tar_j = i;
While (i*m+j) > (tar_i*n+tar_j):
pos = Tar_i*n+tar_j;
tmp_i = pos/m;
Tmp_j = pos% M;
Tar_i = M-1-tmp_j;
tar_j = tmp_i;
swap (B[i][j], A[tar_i][tar_j])
The above code can achieve arbitrary matrix in situ 90 degrees clockwise rotation, the color image is only 3 channels each position by 3 elements, with the above algorithm should also be able to handle, using OpenCV wrote the following procedures:
#include <cv.h>
#include
Test a few pictures, get a satisfactory result, one example is as follows
However, at the time of contentment, a sudden accident occurred, the individual diagram has the following effect:
This is very frustrating, the algorithm is wrong. After careful thinking, we find that there is no loophole in the algorithm. After some thought, we finally found the potential problem.
Please take a closer look at the C code above, you will find that the author assumed one thing, is that img->widthstep = Img->width * Img->nchannels, this is actually a potential hidden trouble. We usually intuitively think that image imagedata this one-dimensional array of storage is the wx3 byte of each row to store the H segment, but in fact, every row widthstep byte continuously storage h paragraph, but widthstep is not always equal to wx3, often a few more bytes. Because on a 32-bit machine, OpenCV allocates bytes in 4-byte alignment. So if the problem figure W = 706, it should have been allocated 2,118 bytes, but actually allocate 2,120 bytes (multiples of 4), 2 redundant bytes per line.
This is difficult to detect at ordinary times, but in situ rotation of the picture is highlighted in the problem, because the existence of redundant bytes, rotation before and after the OPENCV image need space may not be the same. For example, the original 700x401, image byte size is 700x401x3, and after 90 degrees 401x700, the size of the image byte to 404x700x3, indicating the origin of the space size is not enough to represent the new map.
In summary, if you really encounter the problem of 90 degrees in situ rotation of the image, two solutions:
1. Still use the opencv1.x data structure iplimge, that first resize the picture nearest to the width and height is a multiple of the value of 4.
2. Using opencv2.x data structure Cv::mat, this problem will not occur, the following is a sample program using OPENCV2.
#include <opencv2/opencv.hpp>
void swap (unsigned char& A, unsigned char& b) {
char c = A;
A = b;
b = C;
}
int main () {
cv::mat img = cv::imread ("test.jpg");
int w = img.cols;
int h = img.rows;
for (int i = 0; i < w i++)
{for
(int j = 0; J < H; j +)
{
int i = h-1-J;
int J = i;
while ((I*h + j) > (I*w + j))
{
int p = i*w + j;
int tmp_i = p/h;
int tmp_j = p% h;
I = h-1-Tmp_j;
J = tmp_i;
}
Swap (* (img.data + i*h*3 + j*3 + 0), * (Img.data + i*w*3 + j*3 + 0));
Swap (* (img.data + i*h*3 + j*3 + 1), * (Img.data + i*w*3 + j*3 + 1));
Swap (* (img.data + i*h*3 + j*3 + 2), * (Img.data + i*w*3 + j*3 + 2));
}
Img.cols = h;
Img.rows = W;
Img.step = h*3;
Cv::imshow ("IMG", IMG);
Cv::waitkey ();
return 0;
}