圖象的幾何變換
4.1平移
平移(translation)變換大概是幾何變換中最簡單的一種了。如下圖,初始座標爲(x0,y0)的點經過平移(tx,ty)(以向右,向下爲正方向)後,座標變爲(x1,y1)。這兩點之間的關係是x1=x0+tx ,y1=y0+ty。
如下圖所示
以矩陣的形式表示爲
我們更關心的是它的逆變換:
這是因爲:我們想知道的是平移後的圖象中每個象素的顏色。例如我們想知道,新圖中左上角點的RGB值是多少?很顯然,該點是原圖的某點經過平移後得到的,這兩點的顏色肯定是一樣的,所以只要知道了原圖那點的RGB值即可。那麼到底新圖中的左上角點對應原圖中的哪一點呢?將左上角點的座標(0,0)入公式(2.2),得到x0=-tx ,y0=-ty;所以新圖中的(0,0)點的顏色和原圖中(-tx , -ty)的一樣。這樣就存在一個問題:如果新圖中有一點(x1,y1),按照公式(2.2)得到的(x0,y0)不在原圖中該怎麼辦?通常的做法是,把該點的RGB值統一設成(0,0,0)或者(255,255,255)。
4.2旋轉
旋轉(rotation)有一個繞着什麼轉的問題,通常的做法是以圖象的中心爲圓心旋轉。在我們熟悉的座標系中,將一個點順時針旋轉a角後的座標變換公式,如下圖所示,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;
以矩陣的形式表示:
上面的公式中,座標系xoy是以圖象的中心爲原點,向右爲x軸正方向,向上爲y軸正方向。它和以圖象左上角點爲原點o’,向右爲x’軸正方向,向下爲y’軸正方向的座標系x’o’y’之間的轉換關係如何呢
設圖象的寬爲w,高爲h,容易得到:
逆變換爲:
理解了上述理論基礎,其實在C#中我們有現成的方法函數進行操作,我們可以利用Graphics對象所生成的g.RotateTransform方法函數來對圖像進行旋轉操作。圖像旋轉後我們還需要將旋轉所得到的圖像填充到指定的矩形區域中,在這裏我們使用了g.FillRectangle方法函數來進行填充。
private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
this.panel2.Refresh();
Graphics g = e.Graphics;
int angel=Convert.ToInt16(this.numericUpDown4.Value);
System.Drawing.Bitmap temp=new Bitmap(filepath);
TextureBrush brush=new TextureBrush(temp);
g.RotateTransform(flaot(angel)); g.FillRectangle(brush,0,0,this.ClientRectangle.Width,this.ClientRectangle.Height);
return;
}
4.3縮放
假設放大因子爲ratio,(爲了避免新圖過大或過小,我們在程序中限制0.25≤ratio≤4),縮放(zoom)的變換矩陣很簡單:
由於放大圖象時產生了新的象素,以及浮點數的操作,得到的座標可能並不是整數,這一點我們在介紹旋轉時就提到了。我們採用的做法是找與之最臨近的點。實際上,更精確的做法是採用插值(interpolation),即利用鄰域的象素來估計新的象素值。其實我們前面的做法也是一種插值,稱爲最鄰近插值(Nearest Neighbour Interpolation)。下面先介紹線形插值(Linear Interpolation)。
線形插值使用原圖中兩個值來構造所求座標處的值。舉一個一維的例子。下圖所示,如果已經知道了兩點x0,x2處的函數值f(x0),f(x2),現在要求x1處的函數值f(x1)。我們假設函數是線形的,利用幾何知識可以知道
f(x1)=(f(x2)-f(x0))(x1-x0)/(x2-x0)+f(x0)
在圖象處理中需要將線形插值擴展到二維的情況,即採用雙線形插值(Bilinear Intrepolation),
線形插值的示意圖 |
雙線形插值的示意圖 |
已知a、b、c、d四點的灰度,要求e點的灰度,可以先在水平方向上由a,b線形插值求出g、c、d線形插值求出f,然後在垂直方向上由g,f線形插值求出e。
線形插值基於這樣的假設:原圖的灰度在兩個象素之間是線形變化的。一般情況下,這種插值的效果還不錯。更精確的方法是採用曲線插值(Curvilinear Interpolation),即認爲象素之間的灰度變化規律符合某種曲線,但這種處理的計算量是很大的。
同樣的,我們可以利用Graphics對象所生成的g.FillRectangle方法函數來對圖像進行縮放操作。圖像縮放後我們還需要將縮放所得到的圖像填充到指定的矩形區域中,同樣在這裏我們使用了g.FillRectangle方法函數來進行填充。
private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
this.panel2.Refresh();
Graphics g = e.Graphics;
float fx=(float)(this.numericUpDown1.Value/10);
float fy=(float)(this.numericUpDown2.Value/10);
System.Drawing.Bitmap temp=new Bitmap(filepath);
TextureBrush brush=new TextureBrush(temp);
g.ScaleTransform(fx,fy);
</