图像旋转算法与实现

好吧,先下个定义,图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程。当然这个点通常就是图像的中心。既然是按照中心旋转,自然会有这样一个属性:旋转前和旋转后的点离中心的位置不变.

根据这个属性,我们可以得到旋转后的点的坐标与原坐标的对应关系。由于原图像的坐标是以左上角为原点的,所以我们先把坐标转换为以图像中心为原点。假设原图像的宽为w,高为h,(x0,y0)为原坐标内的一点,转换坐标后的点为(x1,y1)。那么不难得到:

x1 = x0 - w/2; y1 = -y0 + h/2;

在新的坐标系下,假设点(x0,y0)距离原点的距离为r,点与原点之间的连线与x轴的夹角为b,旋转的角度为a,旋转后的点为(x1,y1), 如下图所示。

那么有以下结论:

x0=rcosb;y0=rsinb

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

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

得到了转换后的坐标,我们只需要把这些坐标再转换为原坐标系即可。这里还有一点要注意,旋转后的图像的长和宽会发生变化,因此要计算新图像的长和宽。

以下为源程序:

  1 #include "stdafx.h"
  2 #include <stdio.h>
  3 #include <string>
  4 #include <math.h>
  5 #include <windows.h>
  6  using namespace std;
  7 
  8  #define PI 3.1415926535
  9 //角度到弧度转化
 10 #define RADIAN(angle) ((angle)*PI/180.0)
 11 
 12 void Rotation(const string& srcFile,const string& desFile,int angle)
 13 {
 14     BITMAPFILEHEADER bmfHeader;
 15     BITMAPINFOHEADER bmiHeader;
 16     
 17     FILE *pFile;
 18     if ((pFile = fopen(srcFile.c_str(),"rb")) == NULL)
 19     {
 20         printf("open bmp file error.");
 21         exit(-1);
 22     }
 23     //读取文件和Bitmap头信息
 24     fseek(pFile,0,SEEK_SET);
 25     fread(&bmfHeader,sizeof(BITMAPFILEHEADER),1,pFile);
 26     fread(&bmiHeader,sizeof(BITMAPINFOHEADER),1,pFile);
 27     //先不支持小于16位的位图
 28     int bitCount = bmiHeader.biBitCount;
 29     if (bitCount < 16)
 30     {        
 31         exit(-1);
 32     }
 33     int srcW = bmiHeader.biWidth;
 34     int srcH = bmiHeader.biHeight;
 35     //原图像每一行去除偏移量的字节数
 36     int lineSize = bitCount * srcW / 8;
 37     //偏移量,windows系统要求每个扫描行按四字节对齐
 38     int alignBytes = ((bmiHeader.biWidth * bitCount + 31) & ~31) / 8L
 39         - bmiHeader.biWidth * bitCount / 8L;
 40     //原图像缓存
 41     int srcBufSize = lineSize * srcH;
 42     BYTE* srcBuf = new BYTE[srcBufSize];
 43     int i,j;
 44     //读取文件中数据
 45     for (i = 0; i < srcH; i++)
 46     {        
 47         fread(&srcBuf[lineSize * i],lineSize,1,pFile);
 48         fseek(pFile,alignBytes,SEEK_CUR);
 49     }
 50     //以图像中心为原点左上角,右上角,左下角和右下角的坐标,用于计算旋转后的图像的宽和高
 51     POINT pLT,pRT,pLB,pRB;
 52     pLT.x = -srcW/2;pLT.y = srcH/2;
 53     pRT.x = srcW/2;pRT.y = srcH/2;
 54     pLB.x = -srcW/2;pLB.y = -srcH/2;
 55     pRB.x = srcW/2; pRB.y = -srcH/2;
 56     //旋转之后的坐标
 57     POINT pLTN,pRTN,pLBN,pRBN;
 58     double sina = sin(RADIAN(angle));
 59     double cosa = cos(RADIAN(angle));
 60     pLTN.x = pLT.x*cosa + pLT.y*sina;    
 61     pLTN.y = -pLT.x*sina + pLT.y*cosa;
 62     pRTN.x = pRT.x*cosa + pRT.y*sina;
 63     pRTN.y = -pRT.x*sina + pRT.y*cosa;
 64     pLBN.x = pLB.x*cosa + pLB.y*sina;
 65     pLBN.y = -pLB.x*sina + pLB.y*cosa;
 66     pRBN.x = pRB.x*cosa + pRB.y*sina;
 67     pRBN.y = -pRB.x*sina + pRB.y*cosa;
 68     //旋转后图像宽和高
 69     int desWidth = max(abs(pRBN.x - pLTN.x),abs(pRTN.x - pLBN.x));
 70     int desHeight = max(abs(pRBN.y - pLTN.y),abs(pRTN.y - pLBN.y));
 71     //分配旋转后图像的缓存
 72     int desBufSize = ((desWidth * bitCount + 31) / 32) * 4 * desHeight;
 73     BYTE *desBuf = new BYTE[desBufSize];
 74     //将所有像素都预置为白色
 75     memset(desBuf,255,desBufSize);
 76     //新图像每一行字节数,带有偏移量
 77     int desLineSize = ((desWidth * bitCount + 31) / 32) * 4;        
 78     //通过新图像的坐标,计算对应的原图像的坐标
 79     for (i = 0; i < desHeight; i++)
 80     {        
 81         for (j = 0; j < desWidth; j++)
 82         {
 83             //转换到以图像为中心的坐标系,并进行逆旋转
 84             int tX = (j - desWidth / 2)*cos(RADIAN(360 - angle)) + (-i + desHeight / 2)*sin(RADIAN(360 - angle));
 85             int tY = -(j - desWidth / 2)*sin(RADIAN(360 - angle)) + (-i + desHeight / 2)*cos(RADIAN(360 - angle));
 86             //如果这个坐标不在原图像内,则不赋值
 87             if (tX > srcW / 2 || tX < -srcW / 2 || tY > srcH / 2 || tY < -srcH / 2)
 88             {
 89                 continue;
 90             }
 91             //再转换到原坐标系下
 92             int tXN = tX + srcW / 2; int tYN = abs(tY - srcH / 2);
 93             //值拷贝
 94             memcpy(&desBuf[i * desLineSize + j * bitCount / 8],&srcBuf[tYN * lineSize + tXN * bitCount / 8],3);            
 95         }
 96     }
 97 
 98     //创建目标文件
 99     HFILE hfile = _lcreat(desFile.c_str(),0);    
100     //文件头信息
101     BITMAPFILEHEADER nbmfHeader;    
102     nbmfHeader.bfType = 0x4D42;
103     nbmfHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
104         + desWidth * desHeight * bitCount / 8;
105     nbmfHeader.bfReserved1 = 0;
106     nbmfHeader.bfReserved2 = 0;
107     nbmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
108     //Bitmap头信息
109     BITMAPINFOHEADER   bmi; 
110     bmi.biSize=sizeof(BITMAPINFOHEADER); 
111     bmi.biWidth=desWidth; 
112     bmi.biHeight=desHeight; 
113     bmi.biPlanes=1; 
114     bmi.biBitCount=bitCount; 
115     bmi.biCompression=BI_RGB; 
116     bmi.biSizeImage=0; 
117     bmi.biXPelsPerMeter=0; 
118     bmi.biYPelsPerMeter=0; 
119     bmi.biClrUsed=0; 
120     bmi.biClrImportant=0; 
121     
122     //写入文件头信息
123     _lwrite(hfile,(LPCSTR)&nbmfHeader,sizeof(BITMAPFILEHEADER));
124     //写入Bitmap头信息
125     _lwrite(hfile,(LPCSTR)&bmi,sizeof(BITMAPINFOHEADER));
126     //写入图像数据
127     _lwrite(hfile,(LPCSTR)desBuf,desBufSize);
128     _lclose(hfile);
129 }
130 
131 int main(int argc, char* argv[])
132 {
133     FILE *pFile;
134     if ((pFile = fopen("e://t.bmp","rb")) == NULL)
135     {
136         printf("open bmp file error.");
137         return -1;
138     }
139     string srcFile("e://t.bmp");
140     string desFile("e://Rotation.bmp");
141     Rotation(srcFile,desFile,150);
142     system("pause");
143     return 0;
144 }

测试效果如下,旋转前:


旋转后:
 

 

转自:http://www.cnblogs.com/tingshuo/archive/2011/05/15/2047016.html

posted @ 2017-02-23 15:54  鸭子船长  阅读(1241)  评论(0编辑  收藏  举报