最臨近 雙線性 三次卷積插值(影象放縮)
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
對影象進行放縮,實際上根據原影象的畫素資訊推匯出放縮後圖像的畫素資訊,實際上是通過插值實現了這一問題。常用的插值演算法由以下四種:
1. 最近畫素插值演算法(Nearest Neighbour interpolation)最近畫素插值演算法是最簡單的一種插值演算法,當圖片放大時,缺少的畫素通過直接使用與之最接近的原有的畫素的顏色生成,也就是說照搬旁邊的畫素,這樣做的結果是產生了明顯可見的鋸齒。
2. 雙線性插值(Bilinear interpolation)
這種演算法輸出的影象的每個畫素都是原圖中四個畫素(2×2)運算的結果,這種演算法極大地消除了鋸齒現象
3. 雙三次插值演算法(Bicubic interpolation)
這種演算法是上一種演算法的改進演算法,它輸出影象的每個畫素都是原圖16個畫素(16×16)運算的結果。這種演算法是一種很常見的演算法,普遍用在影象編輯軟體、印表機驅動和數碼相機上。
4. 分形演算法(Fractal interpolation)
這是Altamira Group提出的一種演算法,這種演算法得到的影象跟其它演算法相比更清晰、銳利。
原理:
1. 線性插值
已知座標 (x0, y0) 與 (x1, y1),要得到 [x0, x1] 區間內某一位置 x 在直線上的值。
由於 x 值已知,所以可以從公式得到 y 的值
已知 y 求 x 的過程與以上過程相同,只是 x 與 y 要進行交換。
2. 雙線性插值(Bilinear Interpolation)
在數學上,雙線性插值是有兩個變數的插值函式的線性插值擴充套件,其核心思想是在兩個方向分別進行一次線性插值。
圖中:紅色的資料點與待插值得到的綠色點
假如我們想得到未知函式 f 在點 P = (x, y) 的值,假設我們已知函式 f 在 Q11 = (x1, y1)、Q12 = (x1, y2), Q21 = (x2, y1) 以及 Q22 = (x2, y2) 四個點的值。
首先在 x 方向進行線性插值,得到
然後在 y 方向進行線性插值,得到
這樣就得到所要的結果 f(x, y),
雙線性插值在三維空間的延伸是三線性插值。
實際問題:
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)。
若影象為灰度影象,那麼(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)為各點相應權值。關於權值的計算,在維基百科和百度百科上寫的很明白。
2.存在的問題
這部分的前提是,你已經明白什麼是雙線性插值並且在給定源影象和目標影象尺寸的情況下,可以用筆計算出目標影象某個畫素點的值。當然,最好的情況是你已經用某種語言實現了網上一大堆部落格上原創或轉載的雙線性插值演算法,然後發現計算出來的結果和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
instead of
int x=i*m/a
int y=j*n/b
利用上述公式,將得到正確的雙線性插值結果
總結:
總結一下,我得到的教訓有這麼幾條。
1.網上的一些資料有的時候並不靠譜,自己還是要多做實驗。
2.不要小瞧一些簡單的、基本的演算法,讓你寫你未必會寫,而且其中可能還藏著一些玄妙。
3.要多動手程式設計,多體會演算法,多看大牛寫的原始碼(雖然有的時候很吃力,但是要堅持看)。