图像旋转(图像处理)
2021-11-3 ~ 2021-11-16
(0)
图像旋转说明
虽然在 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;
}
}
}
添加评论
取消回复