1. 程式人生 > >雙線性插值演算法的詳細總結

雙線性插值演算法的詳細總結

       最近在做視訊拼接的專案,裡面用到了影象的單應性矩陣變換,在最後的影象重對映,由於目標影象的座標是非整數的,所以需要用到插值的方法,用的就是雙線性插值,下面的博文主要是查看了前輩的部落格對雙線性插值演算法原理進行了一個總結,在這裡也感謝一些大牛的博文。

雙線性插值

      假設源影象大小為mxn,目標影象為axb。那麼兩幅影象的邊長比分別為:m/a和n/b。注意,通常這個比例不是整數,程式設計儲存的時候要用浮點型。目標影象的第(i,j)個畫素點(i行j列)可以通過邊長比對應回源影象。其對應座標為(i*m/a,j*n/b)。顯然,這個對應座標一般來說不是整數,而非整數的座標是無法在影象這種離散資料上使用的。雙線性插值通過尋找距離這個對應座標最近的四個畫素點,來計算該點的值(灰度值或者RGB值)。

  若影象為灰度影象,那麼(i,j)點的灰度值的數學計算模型是:

f(x,y)=b1+b2x+b3y+b4xy

其中b1,b2,b3,b4是相關的係數。關於其的計算過程如下如下:

      如圖,已知Q12,Q22,Q11,Q21,但是要插值的點為P點,這就要用雙線性插值了,首先在x軸方向上,對R1和R2兩個點進行插值,這個很簡單,然後根據R1和R2對P點進行插值,這就是所謂的雙線性插值。

clip_image001

附:維基百科--雙線性插值:

      雙線性插值,又稱為雙線性內插。在數學上,雙線性插值是有兩個變數的插值函式的線性插值擴充套件,其核心思想是在兩個方向分別進行一次線性插值。

假如我們想得到未知函式 f

 在點 P=\left( x, y\right) 的值,假設我們已知函式 f 在 Q_{11} = \left( x_1, y_1 \right)Q_{12} = \left( x_1, y_2 \right)Q_{21} = \left( x_2, y_1 \right), 及 Q_{22} = \left( x_2, y_2 \right) 四個點的值。

首先在 x 方向進行線性插值,得到

f(R_1) \approx \frac{x_2-x}{x_2-x_1} f(Q_{11}) + \frac{x-x_1}{x_2-x_1} f(Q_{21}) \quad\mbox{Where}\quad R_1 = (x,y_1),
f(R_2) \approx \frac{x_2-x}{x_2-x_1} f(Q_{12}) + \frac{x-x_1}{x_2-x_1} f(Q_{22}) \quad\mbox{Where}\quad R_2 = (x,y_2).

然後在 y 方向進行線性插值,得到

f(P) \approx \frac{y_2-y}{y_2-y_1} f(R_1) + \frac{y-y_1}{y_2-y_1} f(R_2).

這樣就得到所要的結果 f \left( x, y \right),

f(x,y) \approx \frac{f(Q_{11})}{(x_2-x_1)(y_2-y_1)} (x_2-x)(y_2-y) + \frac{f(Q_{21})}{(x_2-x_1)(y_2-y_1)} (x-x_1)(y_2-y)
+ \frac{f(Q_{12})}{(x_2-x_1)(y_2-y_1)} (x_2-x)(y-y_1) + \frac{f(Q_{22})}{(x_2-x_1)(y_2-y_1)} (x-x_1)(y-y_1).

如果選擇一個座標系統使得 f 的四個已知點座標分別為 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那麼插值公式就可以化簡為

f(x,y) \approx 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) \approx \begin{bmatrix}1-x & x \end{bmatrix} \begin{bmatrix}f(0,0) & f(0,1) \\f(1,0) & f(1,1) \end{bmatrix} \begin{bmatrix}1-y \\y \end{bmatrix}

這種插值方法的結果通常不是線性的,線性插值的結果與插值的順序無關。首先進行 y 方向的插值,然後進行 x 方向的插值,所得到的結果是一樣的。

opencv和Matlab中的雙線性插值

   這部分的前提是,你已經明白什麼是雙線性插值並且在給定源影象和目標影象尺寸的情況下,可以用筆計算出目標影象某個畫素點的值。當然,最好的情況是你已經用某種語言實現了網上一大堆部落格上原創或轉載的雙線性插值演算法,然後發現計算出來的結果和matlab、openCV對應的resize()函式得到的結果完全不一樣。

那這個究竟是怎麼回事呢?

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

按照網上一些部落格上寫的,源影象和目標影象的原點(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

代替

int x=i*m/a

int y=j*n/b

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