SoFunction
Updated on 2024-12-20

Based on OpenCV and C++ to realize the picture rotation

Image rotation is, essentially, the calculation of the position of each pixel point in the rotated image in relation to the original image. Then just copy it over.

(As an aside, if the calculated position in the original image is not an integer but a decimal, because the number of pixels is an integer, a decimal-to-integer conversion is needed. This conversion process is delicate, need to use interpolation: nearest neighbor interpolation, bilinear interpolation and so on. Here I use the simplest nearest-neighbor interpolation, i.e. rounding decimals to integers, C/C++ implementation of rounding see here).

Graphic imaging classes typically introduce the rotation transformation matrix, where t is the angle to be rotated and [x'; y'] are the transformed coordinates (where the semicolon indicates an up-down relationship):

which is expressed as [x'; y'] = [cos(t) sin(t); -sin(t) cos(t)][x; y]

Because of my personal interests (Putting a P is what the teacher forced me to do...) Because of my personal interest (putting P is forced by my teacher...), I am not allowed to use the OpenCV wrapped rotation function. I can only realize it by myself. My idea is to find the inverse matrix of the transformation matrix first, and then interpolate each point of an all-black map into the original map one by one.

Turns out the image is all black after conversion ......

Then I realized that the origin was not set correctly. The origin is at the top left corner of a picture stored in OpenCV's Mat format (or a 2D array). But the desired rotation origin is at the center of the image.

At the same time, the y-axis of a Mat-format (or two-dimensional array) is oriented downward in the coordinate system. Thus what is a clockwise rotation in human vision is a counterclockwise rotation in the coordinate system of a 2D array.

The most important point, and one that is easy to overlook in two-dimensional array manipulation, is that the array operates on the array subscripts, not on the coordinate system (the rows of the array are the width of the rectangle, and the columns of the array are the length of the rectangle). For example, in the coordinate system (at this point, we're assuming a downward y-axis coordinate system to more closely match the layout of the array), the rectangle's vertices are:

But in the array, since it is row-first, the subscripts of the four points take the value:

Ever notice that the two coordinates are opposite!

To summarize, we need to pay attention to the following points for our image rotation:

  1. Each pixel (i; j) in the transformed image needs to be translated to the new coordinates relative to the center of rotation, i.e. (i - /2; j - /2). After the computation is done, it needs to be restored again to the old coordinates relative to the origin of the upper-left corner;
  2. Originally, we need to multiply the transformed image by the inverse of the transform matrix of the original image to get the coordinates of the original image. But since the y-axis is downward, multiplying the transformed image by the original transform matrix (no inverse matrix needed) corresponds to the coordinates of the original image (rotated by 50 degrees clockwise, the restore operation is rotated by 50 degrees counterclockwise);
  3. Before multiplying the matrix subscripts with the original transformation matrix, you need to swap the values of the matrix subscripts. After multiplication, the subscripts are interchanged again and the matrix subscripts are restored.

Therefore, for a pixel value with array subscripts [m', n'] after a rotation of t degrees, the reduction to the array subscripts [m; n] in the original image is computed as:

[cos(t) -sin(t); sin(t) cos(t)] ([m'; n'] - [/2; /2]) = [m; n] -  [/2; /2]

The source code is attached:

Mat nearestNeighRotate(cv::Mat img, float angle)
{
	int len = (int)(sqrtf(pow(, 2) + pow(, 2)) + 0.5);

	Mat retMat = Mat::zeros(len, len, CV_8UC3);
	float anglePI = angle * CV_PI / 180;
	int xSm, ySm;

	for(int i = 0; i < ; i++)
		for(int j = 0; j < ; j++)
		{
			xSm = (int)((/2)*cos(anglePI) - (/2)*sin(anglePI) + 0.5);
			ySm = (int)((/2)*sin(anglePI) + (/2)*cos(anglePI) + 0.5);
			xSm +=  / 2;
			ySm +=  / 2;

			if(xSm >=  || ySm >=  || xSm <= 0 || ySm <= 0){
				<Vec3b>(i, j) = Vec3b(0, 0);
			}
			else{
				<Vec3b>(i, j) = <Vec3b>(xSm, ySm);
			}
		}

	return retMat;
}

Okay, let's test it out:

int main() 
{ 
	Mat img = imread("../");
	retImg = nearestNeighRotate(img, -);
	namedWindow("nearNeigh", CV_WINDOW_AUTOSIZE);
	imshow("nearNeigh", retImg);

	waitKey(); 
	cvDestroyAllWindows();
	return 0; 
}

The result (rotated by 20 degrees) is

The above is based on OpenCV and C++ to realize the details of the picture rotation, more information about c++ picture rotation please pay attention to my other related articles!