简单

行远必自迩,登高必自卑

图像旋转(图像处理)

图像旋转说明

虽然在 EasyX 中有专门的图像旋转的函数,但是这个函数只是实现了图像旋转的这个功能。其中具体的旋转实现过程就像是个黑匣子一样,而且该函数旋转之后的图像显示不够完整。针对以上问题,我使用双线性内插法,实现了一个比较完整的图像旋转算法。

我对图像旋转的理解

图像旋转是宏观,而旋转的过程是微观的。我们看到的是一个图像整体的旋转,实现整体旋转的过程是每一个像素值位置的移动。

关于移动的方式,分为直接法和间接法。直接法是由原图像的像素位置去计算旋转后图像的位置,间接法是有旋转后图像的位置去反算原图的位置从而获得像素值。这里我使用的是间接法。间接法又存在一个问题,就是由旋转后图像的每一个位置反算到原图时,所在的坐标可能不是一个整数坐标。那么这个坐标我们给它的像素值就需要内插出来。

关于内插,又分为最近邻元法,双线性内插法,三次内插法。这里使用的是双线性内插法,它的算法简单来说,就是根据距离周围四个像素值的远近,离得越近,权重越大,来内插一个像素值。(个人理解,仅供参考)

示例图像

图一 冰冰

图二 双线性内插法旋转后

源码

///////////////////////////////////////////////////
// 程序名称:图像处理——图像旋转
// 编译环境:Mictosoft Visual Studio 2013, EasyX_20200315(beta)
// 作    者:luoyh <2864292458@qq.com>
// 学    校:河南理工大学
// 最后修改:2021-11-3
//

#include<graphics.h>
#include<conio.h>
#include<math.h>
#include<stdio.h>

#define PI acos(-1.0)

//矩阵a乘以矩阵b,结果存储在c中,a为m×n大小,b为n×l大小
void multiply(double *a, double *b, double *c, int m, int n, int l);

struct Point
{
	int x;
	int y;
};

class Algorithm
{
public:
	Algorithm(IMAGE *img, int width, int heigth, double hudu);
	void Rotate();					// 图像旋转
private:
	IMAGE *pimg;
	int WIDTH;
	int HEIGHT;
	double HD;						//	旋转角度
};

Algorithm::Algorithm(IMAGE *img, int width, int height, double hudu)
{
	pimg = img;
	WIDTH = width;
	HEIGHT = height;
	HD = hudu;
};

void Algorithm::Rotate()			// 图像旋转双线性内插法
{
									//	计算旋转后图像的大小
	Point point[4];
	point[0] = { 0, 0 };
	point[1] = { WIDTH, 0 };
	point[2] = { 0, HEIGHT };
	point[3] = { WIDTH, HEIGHT };

	double A[3];
	double B[3][3];

	B[0][0] = cos(HD);
	B[0][1] = -sin(HD);
	B[0][2] = 0;
	B[1][0] = sin(HD);
	B[1][1] = cos(HD);
	B[1][2] = 1;
	B[2][0] = 0;
	B[2][1] = 0;
	B[2][2] = 1;

	double C[3][3];
	C[0][0] = 1;
	C[0][1] = 0;
	C[0][2] = 0;
	C[1][0] = 0;
	C[1][1] = -1;
	C[1][2] = 0;
	C[2][0] = -0.5 * WIDTH;
	C[2][1] = 0.5 * HEIGHT;
	C[2][2] = 1;

	int max_x = 0;
	int max_y = 0;
	for (int i = 0; i < 4; i++)
	{
		double D[3];
		double E[3];
		A[0] = point[i].x;
		A[1] = point[i].y;
		A[2] = 1;

		multiply(A, *C, D, 1, 3, 3);
		multiply(D, *B, E, 1, 3, 3);

		if (fabs(E[0] * 2) > max_x)
		{
			max_x = int(fabs(E[0] * 2) + 0.5);
		}
		if (fabs(E[1] * 2) > max_y)
		{
			max_y = int(fabs(E[1] * 2) + 0.5);
		}
	}

	closegraph();
	initgraph(max_x, max_y);
	saveimage("picture.png");
	IMAGE img;
	loadimage(&img, _T("picture.png"));		// 加载图片

	DWORD* P_data = GetImageBuffer(pimg);
	IMAGE *myimg;
	myimg = new IMAGE();
	*myimg = *pimg;
	DWORD* p_data = GetImageBuffer(myimg);
	DWORD* my_data = GetImageBuffer(&img);

	double F[3][3];
	F[0][0] = 1;
	F[0][1] = 0;
	F[0][2] = 0;
	F[1][0] = 0;
	F[1][1] = -1;
	F[1][2] = 0;
	F[2][0] = -0.5 * max_x;
	F[2][1] = 0.5 * max_y;
	F[2][2] = 1;

	double nB[3][3];
	nB[0][0] = cos(HD);
	nB[0][1] = sin(HD);
	nB[0][2] = 0;
	nB[1][0] = -sin(HD);
	nB[1][1] = cos(HD);
	nB[1][2] = 0;
	nB[2][0] = 0;
	nB[2][1] = 0;
	nB[2][2] = 1;

	double nC[3][3];
	nC[0][0] = 1;
	nC[0][1] = 0;
	nC[0][2] = 0;
	nC[1][0] = 0;
	nC[1][1] = -1;
	nC[1][2] = 0;
	nC[2][0] = 0.5*WIDTH;
	nC[2][1] = 0.5*HEIGHT;
	nC[2][2] = 1;
	for (int j = 0; j < max_y; j++)
	{
		for (int i = 0; i < max_x; i++)
		{
			// 将图像反算
			double XY_3[3];
			XY_3[0] = i;
			XY_3[1] = j;
			XY_3[2] = 1;
			double XY2F[3];
			double XY2G[3];
			double needxy[3];
			multiply(XY_3, *F, XY2F, 1, 3, 3);
			multiply(XY2F, *nB, XY2G, 1, 3, 3);
			multiply(XY2G, *nC, needxy, 1, 3, 3);
			double db_x = needxy[0];
			double db_y = needxy[1];

			// 两种情况
			// 1.反算回去在矩阵中则双线性内插
			// 2.反算回去不在矩阵中则给它白色
			double gr;
			double gg;
			double gb;

			if (0 <= db_x && db_x <= WIDTH && 0 <= db_y && db_y <= HEIGHT)
			{
				// 这里进行双线性内插法
				if (((int(db_y) + 1) * WIDTH + int(db_x)) > (WIDTH - 1)*(HEIGHT - 1) || ((int(db_y) + 1) * WIDTH + int(db_x) + 1) > (WIDTH - 1)*(HEIGHT - 1))
				{
					continue;
				}
				else
				{
					double d_x = db_x - int(db_x);
					double d_y = db_y - int(db_y);
					//上下左右
					int gr11 = GetRValue(p_data[(int(db_y) * WIDTH + int(db_x))]);
					int gr12 = GetRValue(p_data[((int(db_y) + 1) * WIDTH + int(db_x))]);
					int gr21 = GetRValue(p_data[((int(db_y)) * WIDTH + int(db_x)) + 1]);
					int gr22 = GetRValue(p_data[((int(db_y) + 1) * WIDTH + int(db_x) + 1)]);
					gr = (1 - d_x) * (1 - d_y) * gr11 + (1 - d_x) * d_y * gr12 + d_x * (1 - d_y) * gr21 + d_x * d_y * gr22;

					int gg11 = GetGValue(p_data[(int(db_y) * WIDTH + int(db_x))]);
					int gg12 = GetGValue(p_data[((int(db_y) + 1) * WIDTH + int(db_x))]);
					int gg21 = GetGValue(p_data[((int(db_y)) * WIDTH + int(db_x)) + 1]);
					int gg22 = GetGValue(p_data[((int(db_y) + 1) * WIDTH + int(db_x) + 1)]);
					gg = (1 - d_x) * (1 - d_y) * gg11 + (1 - d_x) * d_y * gg12 + d_x * (1 - d_y) * gg21 + d_x * d_y * gg22;

					int gb11 = GetBValue(p_data[(int(db_y) * WIDTH + int(db_x))]);
					int gb12 = GetBValue(p_data[((int(db_y) + 1) * WIDTH + int(db_x))]);
					int gb21 = GetBValue(p_data[((int(db_y)) * WIDTH + int(db_x)) + 1]);
					int gb22 = GetBValue(p_data[((int(db_y) + 1) * WIDTH + int(db_x) + 1)]);
					gb = (1 - d_x) * (1 - d_y) * gb11 + (1 - d_x) * d_y * gb12 + d_x * (1 - d_y) * gb21 + d_x * d_y * gb22;

				}
			}
			else
			{
				gr = 255;
				gg = 255;
				gb = 255;
			}
			my_data[j * max_x + i] = RGB(gr, gg, gb);
		}
	}
	putimage(0, 0, &img);
	saveimage("picture.png");
	_getch();
}

int main()
{
	int	width = 0;
	int	height = 0;
	IMAGE g_img;
	loadimage(&g_img, _T("bingbing.jpeg"));		// 加载图片
	width = g_img.getwidth();
	height = g_img.getheight();
	initgraph(width, height);
	putimage(0, 0, &g_img);
	_getch();
	double degree = 30;							//30度
	double radian = degree / 180.0 * PI;
	Algorithm myA(&g_img, width, height, radian);
	myA.Rotate();
	_getch();
	return 0;
}

//矩阵 a 乘以矩阵 b,结果存储在 c 中,a 为 m × n 大小,b 为 n × l 大小
void multiply(double *a, double *b, double *c, int m, int n, int l)
{
	int i, j, k;
	double t;
	for (i = 0; i < m; i++)
	{
		for (j = 0; j < l; j++)
		{
			t = 0;
			for (k = 0; k < n; k++)
			{
				t += a[i * n + k] * b[k * l + j];
			}
			c[i * l + j] = t;
		}
	}
}

分享到

添加评论