在前一篇文章中,实现了最近邻方法的图像旋转,但是旋转后,由于图像的大小没有改变,所以图像的部分内容有丢失。
在本文章中,我们同样采用最近邻的方法进行图像旋转,保全图像内容,使图像比旋转前的要大,默认旋转中心为图像中心。
图像旋转后,新的图像的 width 以及 height计算方法如下:(其中,width.height 分别为原图像的宽与高)
newWidth = (abs(width/2*CosTheta) + abs(height/2*SinTheta))*2; //计算新图像的长宽
newHeight = (abs(height/2*CosTheta) + abs(width/2*SinTheta))*2;
并且,坐标归一化后,我们得到下面的旋转公式:(其中,xr yr 为旋转前的旋转中心坐标 , xr2 yr2 为旋转后新图像的旋转中心坐标)
(x0 - xr) = (x1 - xr2)*cos(a) - (y1-yr2)*sin(a) ;
(y0 - yr) = (x1 - xr2)*sin(a) - (y1-yr2)*cos(a) ;
根据该公式,我们便可以得到旋转的代码如下:
/*
函数功能:采用最近邻的方式,实现灰度图像的旋转,旋转后,图像的大小改变,保全图像内容。
参数介绍:
src: 原图像 width:原图像的宽度 height:原图像的高度
dst:旋转后的图像
theta:旋转角度(弧度制)
newWidth,newHeight : 旋转后的图像的高度和宽度
backgroundGray:背景灰度值
注意事项:该函数对于传入函数内部的dst指针开辟了空间,所以对于空间的开辟释放要注意
*/
int nearestRotate3(BYTE *src ,int width , int height , BYTE * dst , double theta, int &newWidth,int &newHeight,BYTE backgroundGray)
{
double SinTheta = sin(theta);
double CosTheta = cos(theta);
newWidth = (abs(width/2*CosTheta) + abs(height/2*SinTheta))*2; //计算新图像的长宽
newHeight = (abs(height/2*CosTheta) + abs(width/2*SinTheta))*2;
int xr = width/2;
int yr = height/2; //旋转前的图像中心
int xr2 = newWidth/2; //旋转后的图像中心
int yr2 = newHeight/2;
if(dst)
delete []dst;
dst = new BYTE[newWidth*newHeight];
BYTE * tempImg = new BYTE[newWidth*newHeight];
if(!tempImg)
return -1;
memset(tempImg,backgroundGray,newWidth*newHeight*sizeof(BYTE));
double ConstX = -xr2*CosTheta + yr2*SinTheta + xr + 0.5;
double ConstY = -yr2*CosTheta - xr2*SinTheta + yr + 0.5;
BYTE *p = tempImg;
double x1;
double y1;
int x0;
int y0;
for(int y=0;y<newHeight;y++)
{
x1 = - y*SinTheta - CosTheta + ConstX ;
y1 = y*CosTheta - SinTheta + ConstY ;
for(int x=0;x<newWidth;x++)
{
x1 += CosTheta;
y1 += SinTheta;
x0 = int(x1);
y0 = int(y1);
if(x0<0||x0>width-1 || y0<0||y0>height-1)
{
p++;
continue;
}
*p = src[y0 * width + x0];
p++;
}
}
memcpy(dst,tempImg,newWidth*newHeight*sizeof(BYTE));
return 1;
}
原图以及旋转后的图像效果如下:(逆时针旋转45度)