圖像旋轉

  參考原文http://vipbase.net/ipbook/chap02.htm   html

   這裏主要討論以圖象的中心爲圓心旋轉。旋轉以後若要保持目標區域大小不變,則整幅圖像變大;若要保持整幅圖像的大小不變,則旋轉出去的部分須要裁剪掉。算法

 

 

         旋轉前的圖函數

    旋轉後的圖spa

 

旋轉後保持原圖大小,轉出的部分被裁掉.net

    以順時針旋轉爲例來堆到旋轉變換公式。以下圖所示。指針

 

旋轉前:code

x0=rcosby0=rsinborm

旋轉a角度後:xml

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

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

矩陣形式爲

逆變換爲

 

    上面的公式是以圖像的左下角爲原點旋轉的。現咱們要以圖像的中心爲原點旋轉。所以須要先將座標平移到圖像中心,以下所示

設圖象的寬爲w,高爲h,容易獲得:

逆變換爲

     如今能夠分三步來完成旋轉變換:

     1. 將座標系x'o'y'平移到xoy ;  2. 在xoy座標系下做旋轉變換;  3.變換後將座標系平移回原來位置。

     用矩陣表示就是        

   其中R表示旋轉變換矩陣。當旋轉不改變圖像大小時,T 與 T' 互爲逆矩陣;當旋轉後圖像變大時,T 與 T'不是逆矩陣關係,由於圖像變大了,第一次平移和第二次平移座標系的距離不同。所以當圖像變大時,公式應該以下:

    因爲算法實現過程當中咱們須要的是逆變換的公式,所以咱們只寫出逆變換的表達式,以下:

       其中wn ,hn 表示新圖像的寬和高,wo, ho 表示原圖像的寬和高。

     這樣,對於新圖中的每一點,咱們就能夠根據上面逆變換公式求出對應原圖中的點,獲得它的灰度。若是超出原圖範圍,則設置爲背景色。要注意的是,因爲有浮點運算,計算出來點的座標可能不是整數,採用取整處理,即找最接近的點,這樣會帶來一些偏差(圖象可能會出現鋸齒)。更精確的方法是採用插值,這裏暫不討論。

    C++代碼以下:

複製代碼
/*
* rotate.cpp
* 圖像旋轉變換(順時針)
* Created on: 2011-10-10
* Author: LiChanghai
*/
// 以圖像中心爲座標原點,旋轉後不改變圖像大小
// 函數返回值爲指針,指向新申請的內存區域
// 由於新圖像大小改變了,須要返回新圖像的尺寸
// 所以形參的高度和寬度都採用指針變量
#include<string.h>
#include<stdlib.h>
#include<cmath>
#define pi 3.1415926535
unsigned char * rotate(unsigned char *pImage, int *width, int *height, int biBitCount, float angle)
{
//定義以圖像中心爲原點的座標系下原圖像和新圖像的四個角點座標
float src_x1, src_y1, src_x2, src_y2, src_x3, src_y3, src_x4, src_y4;
float dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4;

//定義新圖像的高度和寬度
int wnew, hnew;

//定義計算過程當中須要的相關變量
float sina, cosa, temp1, temp2, alpha;

//角度轉化爲弧度
alpha=pi*angle/180;

cosa = float(cos(double(alpha)));
sina = float(sin(double(alpha)));

//原圖像的四個角點的座標
src_x1 = float(-0.5*(*width)); src_y1 = float(0.5*(*height));
src_x2 = float(0.5*(*width)); src_y2 = src_y1;
src_x3 = src_x1; src_y3 = float(-0.5*(*height));
src_x4 = src_x2; src_y4 = src_y3;

//計算新圖像的四個角點座標
dst_x1 = cosa*src_x1+sina*src_y1;
dst_y1 = -sina*src_x1+cosa*src_y1;

dst_x2 = cosa*src_x2+sina*src_y2;
dst_y2 = -sina*src_x2+cosa*src_y2;

dst_x3 = cosa*src_x3+sina*src_y3;
dst_y3 = -sina*src_x3+cosa*src_y3;

dst_x4 = cosa*src_x4+sina*src_y4;
dst_y4 = -sina*src_x4+cosa*src_y4;

//計算新圖像的高度和寬度
float t1 = fabs(dst_x4-dst_x1), t2 = fabs(dst_x3-dst_x2);
wnew = int(t1>t2 ? t1:t2);
t1 = fabs(dst_y4-dst_y1), t2 = fabs(dst_y3-dst_y2);
hnew = int(t1>t2 ? t1:t2);

// 計算旋轉變換中的兩個中間變量,便於之後計算
temp1=float( -0.5*wnew*cosa+0.5*hnew*sina+0.5*(*width));
temp2=float(-0.5*wnew*sina-0.5*hnew*cosa+0.5*(*height));
//計算原圖像和新圖像每行像素所佔的字節數(必須是4的倍數)
int lineByte = ((*width) * biBitCount/8+3)/4*4;
int lineByte2=(wnew * biBitCount/8+3)/4*4;

//申請新的位圖數據存儲空間
unsigned char *pImage2;
pImage2=new unsigned char[lineByte2*hnew];

//將新圖像設置爲背景色
memset(pImage2, 0, lineByte2*hnew);

//遍歷新圖像的每個像素進行判斷
int x, y, x0, y0; // x0, y0爲原圖像中對應座標
for(y=0; y<hnew; y++)
for(x=0; x<wnew; x++)
{
x0= int(x*cosa-y*sina+temp1);
y0= int(x*sina+y*cosa+temp2);
//若是在原圖像範圍內則複製像素值
if( (x0>=0) && (x0<(*width)) && (y0>=0) && (y0<(*height)))
{
*(pImage2+lineByte2*y+x) = *(pImage+lineByte*y0+x0);
}
}

//修改原圖像的高度和寬度
*width = wnew;
*height = hnew;
delete [ ] pImage; //釋放原內存空間
return pImage2;}