雙線性插值算法進行圖像縮放及性能效果優化

一)轉自http://handspeaker.iteye.com/blog/1545126html

最近在編程時用到了雙線性插值算法,對圖像進行縮放。網上有不少這方面的資料,介紹的也算明白。可是,這些文章只介紹了算法,並無具體說怎麼實現以及怎麼實現最好,舉個例子,你能夠按照網上文章的算法本身寫一個雙線性插值程序,用它對一張圖片進行處理,而後再用matlab或者openCV的resize函數對同一張圖片進行處理,獲得的結果是不同的,若是源圖片較小,效果差距就更大。如下是對於雙線性插值的講解以及上述現象的解釋:算法

 

1.雙線性插值編程

假設源圖像大小爲mxn,目標圖像爲axb。那麼兩幅圖像的邊長比分別爲:m/a和n/b。注意,一般這個比例不是整數,編程存儲的時候要用浮點型。目標圖像的第(i,j)個像素點(i行j列)能夠經過邊長比對應回源圖像。其對應座標爲(i*m/a,j*n/b)。數組

顯然,這個對應座標通常來講不是整數,而非整數的座標是沒法在圖像這種離散數據上使用的。雙線性插值經過尋找距離這個對應座標最近的四個像素點,來計算該點的值(灰度值或者RGB值)。若是你的對應座標是(2.5,4.5),那麼最近的四個像素是(2,4)、(2,5)、(3,4),(3,5)。ide

若圖像爲灰度圖像,那麼(i,j)點的灰度值能夠經過一下公式計算:函數

f(i,j)=w1*p1+w2*p2+w3*p3+w4*p4;優化

其中,pi(i=1,2,3,4)爲最近的四個像素點,wi(i=1,2,3,4)爲各點相應權值。關於權值的計算,在維基百科和百度百科上寫的很明白。spa

 

2.存在的問題code

這部分的前提是,你已經明白什麼是雙線性插值而且在給定源圖像和目標圖像尺寸的狀況下,能夠用筆計算出目標圖像某個像素點的值。固然,最好的狀況是你已經用某種語言實現了網上一大堆博客上原創或轉載的雙線性插值算法,而後發現計算出來的結果和matlab、openCV對應的resize()函數獲得的結果徹底不同。htm

那這個到底是怎麼回事呢?

其實答案很簡單,就是座標系的選擇問題,或者說源圖像和目標圖像之間的對應問題。

按照網上一些博客上寫的,源圖像和目標圖像的原點(0,0)均選擇左上角,而後根據插值公式計算目標圖像每點像素,假設你須要將一幅5x5的圖像縮小成3x3,那麼源圖像和目標圖像各個像素之間的對應關係以下:

只畫了一行,用作示意,從圖中能夠很明顯的看到,若是選擇右上角爲原點(0,0),那麼最右邊和最下邊的像素實際上並無參與計算,並且目標圖像的每一個像素點計算出的灰度值也相對於源圖像偏左偏上。

那麼,讓座標加1或者選擇右下角爲原點怎麼樣呢?很不幸,仍是同樣的效果,不過此次獲得的圖像將偏右偏下。

最好的方法就是,兩個圖像的幾何中心重合,而且目標圖像的每一個像素之間都是等間隔的,而且都和兩邊有必定的邊距,這也是matlab和openCV的作法。以下圖:

若是你不懂我上面說的什麼,不要緊,只要在計算對應座標的時候改成如下公式便可,

 

int x=(i+0.5)*m/a-0.5

int y=(j+0.5)*n/b-0.5

 

instead of 

 

 

int x=i*m/a

int y=j*n/b

 

利用上述公式,將獲得正確的雙線性插值結果


總結:

總結一下,我獲得的教訓有這麼幾條。

1.網上的一些資料有的時候並不靠譜,本身仍是要多作實驗。

2.不要小瞧一些簡單的、基本的算法,讓你寫你未必會寫,並且其中可能還藏着一些玄妙。

3.要多動手編程,多體會算法,多看大牛寫的源碼(雖然有的時候很吃力,可是要堅持看)。

二)轉自http://www.cnblogs.com/Imageshop/archive/2011/11/12/2246808.html

 在圖像處理中,雙線性插值算法的使用頻率至關高,好比在圖像的縮放中,在全部的扭曲算法中,均可以利用該算法改進處理的視覺效果。首先,咱們看看該算法的簡介。

     在數學上,雙線性插值算法能夠當作是兩個變量間的線性插值的延伸。執行該過程的關鍵思路是先在一個方向上執行線性插值,而後再在另一個方向上插值。下圖示意出這個過程的大概意思。

     用一個簡單的數學表達式能夠表示以下:

     f(x,y)=f(0,0)(1-x)(1-y)+f(1,0)x(1-y)+f(0,1)(1-x)y+f(1,1)xy

     合併有關項,可寫爲: f(x,y)=(f(0,0)(1-x)+f(1,0)x) (1-y)+(f(0,1)(1-x)+f(1,1)x)y

     由上式能夠看出,這個過程存在着大量的浮點數運算,對於圖像這樣大的計算用戶來講,是一個較爲耗時的過程。

     考慮到圖像的特殊性,他的像素值的計算結果須要落在0到255之間,最多隻有256種結果,由上式能夠看出,通常狀況下,計算出的f(x,y)是個浮點數,咱們還須要對該浮點數進行取整。所以,咱們能夠考慮將該過程當中的全部相似於1-x、1-y的變量放大合適的倍數,獲得對應的整數,最後再除以一個合適的整數做爲插值的結果。

      如何取這個合適的放大倍數呢,要從三個方面考慮,第一:精度問題,若是這個數取得太小,那麼通過計算後可能會致使結果出現較大的偏差。第二,這個數不能太大,太大會致使計算過程超過長整形所能表達的範圍。第三:速度考慮。假如放大倍數取爲12,那麼算式在最後的結果中應該須要除以12*12=144,可是若是取爲16,則最後的除數爲16*16=256,這個數字好,咱們能夠用右移來實現,而右移要比普通的整除快多了。 

      綜合考慮上述三條,咱們選擇2048這個數比較合適。

      下面咱們假定某個算法獲得了咱們要取樣的座標分別PosX以及PosY,其中PosX=25.489,PosY=58.698。則這個過程的相似代碼片斷以下:

複製代碼
 1 NewX =  Int(PosX)                         ' 向下取整,NewX=25
 2 NewY =  Int(PosY)                         ' 向下取整,NewY=58
 3 PartX = (PosX - NewX) *  2048             ' 對應表達式中的X
 4 PartY = (PosY - NewY) *  2048             ' 對應表達式中的Y
 5 InvX =  2048 - PartX                      ' 對應表達式中的1-X
 6 InvY =  2048 - PartY                      ' 對應表達式中的1-Y
 7 
 8 Index1 = SamStride * NewY + NewX *  3     ' 計算取樣點左上角鄰近的那個像素點的內存地址
 9 Index2 = Index1 + SamStride           ' 左下角像素點地址
10 ImageData(Speed +  2) = ((Sample(Index1 +  2) * InvX + Sample(Index1 +  5) * PartX) * InvY + (Sample(Index2 +  2) * InvX + 
                          Sample(Index2 +  5) * PartX) * PartY) \  4194304       '處理紅色份量
11 ImageData(Speed +  1) = ((Sample(Index1 +  1) * InvX + Sample(Index1 +  4) * PartX) * InvY + (Sample(Index2 +  1) * InvX +
                          Sample(Index2 +  4) * PartX) * PartY) \  4194304       '處理綠色份量
12 ImageData(Speed) = ((Sample(Index1) * InvX + Sample(Index1 +  3) * PartX) * InvY + (Sample(Index2) * InvX +
                      Sample(Index2 +  3) * PartX) * PartY) \  4194304           '處理藍色份量
複製代碼

      以上代碼中涉及到的變量都爲整型(PosX及PosY固然爲浮點型)。

      代碼中Sample數組保存了從中取樣的圖像數據,SamStride爲該圖像的掃描行大小。

      觀察上述代碼,除了有2句涉及到了浮點計算,其餘都是整數之間的運算。

      在Basic語言中,編譯時若是勾選全部的高級優化,則\ 4194304會被編譯爲>>22,即右移22位,若是使用的是C語言,則直接寫爲>>22。

      須要注意的是,在進行這代代碼前,須要保證PosX以及PosY在合理的範圍內,即不能超出取樣圖像的寬度和高度範圍。

      經過這樣的改進,速度較直接用浮點類型快至少100%以上,而處理後的效果基本沒有什麼區別。

相關文章
相關標籤/搜索