1、图像的平移变换
设dx为水平偏移,dy为垂直偏移,则平移变换公式为:
x = x0 + dx
y = y0 + dy
在代码实现的问题上由于图像在内存中倒放,当垂直偏移为10时,应该使图像向下方移动10但是图像倒放所以会向上移动10解决方法就是从下往上取值,先取(height-1-j)详细情况如下:
void jiheBianHuan::PingYi(int m_Xmove,int m_Ymove)
{
LPBYTE lpSrc;
LPBYTE p_data;
LPBYTE lpDst;
LPBYTE temp;
int i, j;
LONG width, height;
LONG perLineBytes;
p_data = getData();
width = getWidth();
height = getHeight();
if (m_pBitmapInfoHeader->biBitCount < 9)//灰度图要保证每行的字节数是4的倍数,除以32是因为4个字节是32位,为了获知这一行总共有几个4字节
{ //width是以像素为单位所以每个像素点的字节数为width*8为了将不满足的部分装满32位所以又加了31
perLineBytes = (((width * 8) + 31) / 32 * 4);
temp = new BYTE[perLineBytes*height];
lpDst = (LPBYTE)temp;
memset(lpDst, (BYTE)255, perLineBytes*height);
for ( i = 0; i < width; i++)
{
for (j = 0; j < height; j++)
{
lpSrc = (LPBYTE)p_data + perLineBytes*(height - 1 - j) + i;//数据在内存中是倒放,先求出来数据所在位置的高度,即所在行的上一行,再加宽度i
int i0, j0; //如果按照width*j+i的算法移动图像那么就会使垂直方向往上方移动
//以下是重要代码
i0 = i + m_Xmove;
j0 = j + m_Ymove;
if (i0 < width&&i0 >= 0 && j0 < height&&j0 >= 0)
{
lpDst = (LPBYTE)temp + perLineBytes*(height - 1 - j0) + i0;
*lpDst = *lpSrc;
}
else
{
*lpDst = 255;
}
}
}
memcpy(p_data, temp, perLineBytes*height);
delete temp;
}
2、图像的镜像变换
设图像的宽度为width,则水平镜像为:{x = width -1 -x0}
垂直镜像为:{y=height-1-y0}
if (m_pBitmapInfoHeader->biBitCount < 9)
{
perLineBytes = (((width * 8) + 31) / 32 * 4);
temp = new BYTE[perLineBytes*height];
lpDst = temp;
for (i = 0; i < width; i++)
{
for (j = 0; j < height; j++)
{
lpSrc = (LPBYTE)p_data + perLineBytes*(height - 1 - j) + i;
int i0, j0;
//以下是重要代码
i0 = perLineBytes - i;
j0 = j;
if (i0 < width&&i0 >= 0 && j0 < height&&j0 >= 0)
{
lpDst = (LPBYTE)temp + perLineBytes*(height - 1 - j0) + i0;
*lpDst = *lpSrc;
}
else
{
*lpDst = 255;
}
}
}
memcpy(p_data, temp, perLineBytes*height);
delete temp;
}
3、图像的缩放处理
图像缩放是用的是向后映射法,先计算出新图像的大小,newWidth和newHeight。水平缩放系数为sx垂直缩放系数为sy。则有以下关系:
newwidth = sx * width
newheight = sy * height
最重要的代码如下:
int x = (int)(1/sx+0.5)
int y = (int)(1/sy+0.5)
这样就能够找到合适的缩放前的点,将这个点的像素值付给缩放后的点,这是一种方法为最近邻插法。还有一种为双线性插值法。
4、图像的旋转变换
图像的旋转变换涉及到的数学问题较多大致分为以下三个部分:
1.将输入图像坐标系转为数学坐标系,由于计算机的坐标系在左上角所以先转为数学坐标系
2.通过数学坐标系计算指定像素旋转后的坐标:
x0=rcosθ y0=rsinθ 转为 x =rcos(θ-α) y=rsin(θ-α)
3.由旋转坐标系再转回输出坐标系
经过一系列的矩阵运算之后可以得到两个常量,和x,y的旋转公式,具体看代码:
angle = (double)RADIAN(angle);
//原图像坐标
int srcX1, srcX2, srcX3, srcX4;
int srcY1, srcY2, srcY3, srcY4;
srcX1 = 0; srcX2 = width-1; srcX3 = 0; srcX4 = width-1;
srcY1 = 0; srcY2 = 0; srcY3 = height-1; srcY4 = height-1;
//新图像坐标
double tranX1, tranX2, tranX3, tranX4;
double tranY1, tranY2, tranY3, tranY4;
//坐标计算
double Fsin = sin(angle);
double Fcos = cos(angle);
//新坐标赋值
tranX1 = srcX1*Fcos + srcY1*Fsin;
tranY1 = -srcX1*Fsin + srcY1*Fcos;
tranX2 = srcX2*Fcos + srcY2*Fsin;
tranY2 = -srcX2*Fsin + srcY2*Fcos;
tranX3 = srcX3*Fcos + srcY3*Fsin;
tranY3 = -srcX3*Fsin + srcY3*Fcos;
tranX4 = srcX4*Fcos + srcY4*Fsin;
tranY4 = -srcX4*Fsin + srcY4*Fcos;
//新图像的长度与宽度
UINT newWidth = (UINT)(max(fabs(tranX1 - tranX4), fabs(tranX2 - tranX3)) + 1.5); //加1.5是为了取整数防止数值丢失
UINT newHeight = (UINT)(max(fabs(tranY1 - tranY4), fabs(tranY2 - tranY3)) + 1.5);
//两个常亮的值
double num1 = -0.5*newWidth*Fcos - 0.5*newHeight*Fsin + 0.5*width;
double num2 = 0.5*newWidth*Fsin - 0.5*newHeight*Fcos + 0.5*height;
temp = new BYTE[newWidth*newHeight];
lpDst = temp;
memset(temp,(BYTE)255, newWidth*newHeight);
if(m_pBitmapInfoHeader->biBitCount<9)
{
for(int j=0;j<newHeight;j++)
for (int i = 0; i < newWidth; i++)
{
lpDst = (LPBYTE)temp + newWidth*j + i;
int x0 = (int)i*Fcos + j*Fsin + num1+0.5;
int y0 = (int)-i*Fsin + j*Fcos + num2+0.5;
if (x0 < width&&x0 >= 0 && y0 < height&&y0 >= 0)
{
lpSrc = (LPBYTE)p_data + width*y0 + x0;
*lpDst = *lpSrc;
}
}
}
this->m_pData = temp;
m_pBitmapInfoHeader->biHeight = newHeight;
m_pBitmapInfoHeader->biWidth = newWidth;
}