图像编程学习笔记3——图像旋转

以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序。

 

旋转(rotation)有一个绕着什么转的问题,通常的做法是以图象的中心为圆心旋转,举个例子,图2.7旋转30度(顺时针方向)后如图2.8所示:

可以看出,旋转后图象变大了。另一种做法是不让图象变大,转出的部分被裁剪掉。如图2.9所示。

我们采用第一种做法,首先给出变换矩阵。在我们熟悉的坐标系中,将一个点顺时针旋转a角后的坐标变换公式,如图2.10所示,r为该点到原点的距离,在旋转过程中,r保持不变;b为r与x轴之间的夹角。

                                                             

旋转前:x0=rcosb;y0=rsinb

旋转a角度后:

x1=rcos(b-a)=rcosbcosa+rsinbsina= x0cosa+y0sina;

y1=rsin(b-a)=rsinbcosa-rcosbsina= -x0sina+y0cosa;

以矩阵的形式表示:

                                                                                           (2.5)

上面的公式中,坐标系xoy是以图象的中心为原点,向右为x轴正方向,向上为y轴正方向。它和以图象左上角点为原点o’,向右为x’轴正方向,向下为y’轴正方向的坐标系x’o’y’之间的转换关系如何呢?如图2.11所示。

 

图2.11    两种坐标系间的转换关系

设图象的宽为w,高为h,容易得到:

                                                                                       (2.6)

逆变换为:

                                                                                        (2.7)

有了上面的公式,我们可以把变换分成三步:

1.将坐标系o’变成o;

2.将该点顺时针旋转a角;

3.将坐标系o变回o’,这样,我们就得到了变换矩阵,是上面三个矩阵的级联。

                                                   (2.8)

要注意的是,因为新图变大,所以上面公式中出现了wold,hold,wnew,hnew,它们分别表示原图(old)和新图(new)的宽、高。我们从图2.8中容易看出:wnew=max(|x4-x1|,|x3-x2|);hnew=max(|y4-y1|,|y3-y2|)。

(2.8)的逆变换为

                                                    (2.9)

这样,对于新图中的每一点,我们就可以根据公式(2.9)求出对应原图中的点,得到它的灰度。如果超出原图范围,则填成白色。要注意的是,由于有浮点运算,计算出来点的坐标可能不是整数,采用取整处理,即找最接近的点,这样会带来一些误差(图象可能会出现锯齿)。更精确的方法是采用插值,将在图象缩放时介绍。

源程序如下:

[cpp] view plaincopy

 

    1. /** 
    2. * 程序名: Rotation.cpp 
    3. * 功  能: 实现灰度图像的旋转,如果超出原图范围,则用白色填充 
    4. *         测试位图为test.bmp放到工程目录下 
    5. */  
    6. #include <iostream>  
    7. #include <fstream>  
    8. #include <cstring>  
    9. #include <cmath>  
    10. #include <windows.h>  
    11. using namespace std;  
    12. #define PI 3.1415926535  
    13. #define RADIAN(angle) (((angle)*PI)/180.0)  
    14. BITMAPFILEHEADER bmpFileHeader;   //bmp文件头  
    15. BITMAPINFOHEADER bmpInfoHeader;   //bmp信息头  
    16. RGBQUAD *pColorTable;            //bmp颜色表     
    17. unsigned char *pBmpData;        //bmp位图数据  
    18. unsigned char *pNewBmpData;   //旋转后bmp位图数据  
    19. int newImgSize;         //旋转后图像大小  
    20.   
    21. /** 
    22. * 函数名: readBmp 
    23. * 参  数: fileName--要读取文件的文件名 
    24. * 功  能: 读取bmp位图数据,成功返回TRUE,否则返回FALSE 
    25. */  
    26. BOOL readBmp(char *fileName)  
    27. {  
    28.     FILE *fp = fopen(fileName,"rb");   //以二进制读方式打开  
    29.     if (NULL == fp)  
    30.     {  
    31.         cout<<"The file is opened failure!"<<endl;  
    32.         return FALSE;  
    33.     }  
    34.     //读取信息到相应的变量中  
    35.     fread(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);  
    36.     fread(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);  
    37.     pColorTable = new RGBQUAD[256];  
    38.     fread(pColorTable,sizeof(RGBQUAD),256,fp);  
    39.     int imgSize = bmpInfoHeader.biSizeImage;     //文图数据的大小,4的倍数  
    40.     pBmpData = new  unsigned char[imgSize];  
    41.     fread(pBmpData,sizeof(unsigned char),imgSize,fp);  
    42.     fclose(fp);  
    43.     return TRUE;  
    44. }  
    45. /** 
    46. * 函数名: rotation 
    47. * 参  数: rotAngle--要转换的角度 
    48. * 功  能: 实现bmp图像的旋转 
    49. */  
    50. void rotation(int rotAngle)  
    51. {  
    52.     double cosa,sina,srcX[4],srcY[4],dstX[4],dstY[4],rad;  
    53.     int oldWidth,oldHeight,newWidth,newHeight;  
    54.     rad = (double)RADIAN(rotAngle);  
    55.     cosa = cos(rad);  
    56.     sina = sin(rad);  
    57.     //原图宽与高  
    58.     oldWidth = bmpInfoHeader.biWidth;  
    59.     oldHeight = bmpInfoHeader.biHeight;  
    60.     //原图四个角的坐标  
    61.     srcX[0] = -0.5 * oldWidth;  
    62.     srcY[0] = 0.5 * oldHeight;  
    63.     srcX[1] = 0.5 * oldWidth;  
    64.     srcY[1] = 0.5 * oldHeight;  
    65.     srcX[2] = 0.5 * oldWidth;  
    66.     srcY[2] = -0.5 * oldHeight;  
    67.     srcX[3] = -0.5 * oldWidth;  
    68.     srcY[3] = -0.5 * oldHeight;  
    69.     //新图四个角坐标  
    70.     for(int i = 0; i < 4; i++ )  
    71.     {  
    72.         dstX[i] = cosa * srcX[i] + sina * srcY[i];  
    73.         dstY[i] = -sina * srcX[i] + cosa * srcY[i];  
    74. //      cout<<dstY[i]<<endl;  
    75.     }  
    76.     //新图的宽与高,向上取整  
    77.     bmpInfoHeader.biWidth = newWidth = (int)(max(fabs(dstX[0] - dstX[2]),fabs(dstX[1] - dstX[3])) + 0.5);  
    78.     bmpInfoHeader.biHeight = newHeight = (int)(max(fabs(dstY[0] - dstY[2]),fabs(dstY[1] - dstY[3])) + 0.5);  
    79. //  cout<<newWidth<<newHeight<<endl;  
    80.     //新图位图数据大小  
    81.     bmpInfoHeader.biSizeImage = newImgSize = newHeight * ((newWidth * bmpInfoHeader.biBitCount + 31) / 32 * 4);  
    82.     pNewBmpData = new unsigned char[newImgSize];  
    83.     double temp1,temp2;   //计算矩阵(2.9)中的两个常数,这样不用以后每次都计算了  
    84.     temp1 = -0.5 * newWidth * cosa - 0.5 * newHeight * sina + 0.5 * oldWidth;  
    85.     temp2 = 0.5 * newWidth * sina - 0.5 * newHeight * cosa + 0.5 * oldHeight;  
    86.     memset(pNewBmpData,(BYTE)255,newImgSize);   //先全部填充成白色  
    87.     int x0,y0,x1,y1;  
    88.     unsigned char *pOldTemp,*pNewTemp;  
    89.     int oldLineByte,newLineByte;  
    90.     oldLineByte = (oldWidth * bmpInfoHeader.biBitCount + 31) / 32 * 4;  
    91.     newLineByte = (newWidth * bmpInfoHeader.biBitCount + 31) / 32 * 4;  
    92.     //把旋转后的图像数据对应存储到pNewBmpData相应位置  
    93.     for(y1 = 0; y1 < newHeight; y1++)  
    94.     {  
    95.         for(x1 = 0; x1 < newWidth; x1++ )  
    96.         {  
    97.             x0 = (int)(x1 * cosa + y1 * sina + temp1);  
    98.             y0 = (int)(-x1 * sina + y1 * cosa + temp2);  
    99.             if((x0 >= 0 && x0 < oldWidth) && (y0 >= 0 && y0 < oldHeight))    //这里不能为<=oldWidth或oldHeight  
    100.             {  
    101.                 pOldTemp = pBmpData + (oldHeight - 1 - y0) * oldLineByte + x0;  
    102.                 pNewTemp = pNewBmpData + (newHeight - 1 - y1) * newLineByte + x1;  
    103.                 *pNewTemp = *pOldTemp;  
    104.             }  
    105.         }  
    106.     }  
    107. }  
    108. /** 
    109. * 函数名: writeBmp 
    110. * 参  数: bmpName -- 旋转后的bmp文件名 
    111. * 功  能: 新建一个bmp文件,把旋转后的图像信息存入其中 
    112. */  
    113. void writeBmp(char *bmpName)  
    114. {  
    115.     FILE *fp = fopen(bmpName,"wb");  //以二进制写方式打开  
    116.     if(NULL == fp)  
    117.         cout<<"The file is opened failure"<<endl;  
    118.     //写入选装后图像信息  
    119.     fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);  
    120.     fwrite(&bmpInfoHeader,sizeof(BITMAPINFOHEADER),1,fp);  
    121.     fwrite(pColorTable,sizeof(RGBQUAD),256,fp);  
    122.     fwrite(pNewBmpData,sizeof(unsigned char),newImgSize,fp);  
    123.     fclose(fp);  
    124.         delete []pColorTable;  
    125.         delete []pNewBmpData;  
    126.         delete []pBmpData;  
    127.  }  
    128. /** 
    129. * 函数名: work 
    130. * 参  数: 无 
    131. * 功  能: 实现处理工作 
    132. */  
    133. void work()  
    134. {  
    135.     char readBmpName[] = "test.bmp";  
    136.     if ( !readBmp(readBmpName))  
    137.         cout<<"The file "<<readBmpName<<"is read failure"<<endl;  
    138.     cout<<"please input the angle to rotate(Clockwise):";  
    139.     int rotAngle;  
    140.     cin>>rotAngle;  
    141.     rotation(rotAngle);  
    142.     char writeBmpName[] = "test_new.bmp";  
    143.     writeBmp(writeBmpName);  
    144. }  
    145. int main()  
    146. {  
    147.     work();  
    148.     return 0;  
    149. }  
时间: 2024-10-29 10:14:11

图像编程学习笔记3——图像旋转的相关文章

图像编程学习笔记8——图像的平滑(去噪)

第一种方法:高斯模版 以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序. 先举个例子说明一下什么是平滑(smoothing),如下面两幅图所示:可以看到,图3.2比图3.1柔和一些(也模糊一些).是不是觉得很神奇?其实实现起来很简单.我们将原图中的每一点的灰度和它周围八个点的灰度相加,然后除以9,作为新图中对应点的灰度,就能实现上面的效果. 这么做并非瞎蒙,而是有其道理的.大概想一想,也很容易明白.举个例子,就象和面一样,先在中间加点水

图像编程学习笔记5——图像镜像

以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序.   镜象(mirror)分水平镜象和垂直镜象两种.图2.2的水平镜象和垂直镜象分别如图2.13和图2.14所示 图2.13   图2.2的水平镜象                                                                                     图2.14   图2.2的垂直镜象 镜象的变换矩阵很简单.设原图宽为w,

图像编程学习笔记9——图像的锐化

锐化(sharpening)和平滑恰恰相反,它是通过增强高频分量来减少图象中的模糊,因此又称为高通滤波(high passfilter).锐化处理在增强图象边缘的同时增加了图象的噪声. 常用的锐化模板是拉普拉斯(Laplacian)模板(见(3.4)式),又是个数学家的名字,可见学好数学,走遍天下都不怕.   (3.4) 容易看出拉普拉斯模板的作法:先将自身与周围的8个象素相减,表示自身与周围象素的差别:再将这个差别加上自身作为新象素的灰度.可见,如果一片暗区出现了一个亮点,那么锐化处理的结果是

图像编程学习笔记7——图像缩放

假设放大因子为ratio,(为了避免新图过大或过小,我们在程序中限制0.25≤ratio≤4),缩放(zoom)的变换矩阵很简单: (2.13) 缩放变换的源代码如下,因为和转置的那段程序很类似,程序中的注释就简单一些.   [cpp] view plaincopy   /**  * 函数名: zoom  * 参  数: ratio -- 缩放率  * 功  能: 对图片进行水平和垂直镜像操作  *         只保存原图大小的图像数据,如果没有就用白色填充  */   void zoom(

图像编程学习笔记6——图像转置

转置(transpose)是指将x,y坐标对换,图2.2的转置如图2.15所示. 图2.2 图2.15   图2.2的转置 要注意的是,转置和旋转900是有区别的,不信你可以试试:怎么旋转,图2.2也转不出图2.15来.另外,转置后图的宽高对换了.转置的变换矩阵很简单:                                                                                              (2.12) 镜象变换的源代码如下,因为和

图像编程学习笔记2——bmp位图平移

以下文字内容copy于<<数字图像处理编程入门>>,code为自己实现,是win32控制台程序. 2.1 平移 平移(translation)变换大概是几何变换中最简单的一种了. 如图2.1所示,初始坐标为(x0,y0)的点经过平移(tx,ty)(以向右,向下为正方向)后,坐标变为(x1,y1).这两点之间的关系是x1=x0+tx,y1=y0+ty.   图2.1    平移的示意图 以矩阵的形式表示为 (2.1)           我们更关心的是它的逆变换: (2.2) 这是因

图像编程学习笔记4——24位真彩色转换为灰度图像

以下文本内容来自http://zhidao.baidu.com/question/152910968.html中的部分内容 把RGB值转换为灰度值的公式:Gray   :=   Trunc(0.3   *   Red   +   0.59   *   Green   +   0.11   *   Blue);//这句用的是浮点运算在图像处理中,速度就是生命,能不用浮点运算,就最好不要用!Gray   :=   (30   *   Red   +   59   *   Green   +   11

信息-图像编程学习,位图存取

问题描述 图像编程学习,位图存取 请问 //图像数据信息是从左下角按行开始存储的 for(int i = 0; i < biHeight; i++ ) { for(int j = 0; j < biWidth; j++ ) { for(int k = 0; k < 3; k++ ) { int temp = *(pBmpBuf + i * lineByte + j + k); count++; outfile<<temp<<" "; if(co

Socket网络编程学习笔记(3):利用套接字助手类

在上一篇中已经介绍了利用Socket建立服务端和客户端进行通信,如果需要 的朋友可访问<Socket网络编程学习笔记(2):面向连接的Socket>.在本篇 中,将利用C#套接字的助手类来简化Socket编程,使得刚刚接触到网络编程的 朋友们更容易上手. 跟上篇一样,通过C#套接字的助手类来编程同样分 服务端和客户端. 一.服务端侦听模式 1.创建套接字与 IPEndPoint绑定,并设置为侦听模式. 1//创建IPEndPoint实例 2 IPEndPoint ipep = new IPEn