目錄
1.什么是插值
2.常用的插值算法
3.最近鄰法(Nearest Interpolation)
4.單線性插值
5.雙線性插值
6.雙線性插值的優化
1.什么是插值
Interpolation is a method of constructing new data points within the range of a discrete set of known data points. Image interpolation refers to the“guess”of intensity values at missing locations.
圖片放大是圖像處理中的一個特別基礎的操作。在幾乎每一個圖片相關的項目中,從傳統圖像處理到深度學習,都有應用。生活里,和朋友通過微信傳張圖片,從圖片發出,到朋友收到圖片,查看圖片,都會數次的的改變圖像的尺寸,從而用到這個算法。但是大家只是在用這個算法,很少關注這個算法的實現細節:插值算法是如何工作的。
簡單來說,插值指利用已知的點來“猜”未知的點,圖像領域插值常用在修改圖像尺寸的過程,由舊的圖像矩陣中的點計算新圖像矩陣中的點並插入,不同的計算過程就是不同的插值算法。
下圖是自己實現雙線性插值的效果

2.常用的插值算法
插值算法有很多種,這里列出關聯比較密切的三種:
最近鄰法(Nearest Interpolation):計算速度最快,但是效果最差。
雙線性插值(Bilinear Interpolation):雙線性插值是用原圖像中4(2*2)個點計算新圖像中1個點,效果略遜於雙三次插值,速度比雙三次插值快,屬於一種平衡美,在很多框架中屬於默認算法。
雙三次插值(Bicubic interpolation):雙三次插值是用原圖像中16(4*4)個點計算新圖像中1個點,效果比較好,但是計算代價過大。
3.最近鄰法(Nearest Interpolation)
雙線性插值法由原圖中4個點計算新圖中的1個點,在介紹計算過程前,需要先了解如何找到這4個點,雙線性插值法找尋4個點的方式和最近鄰法相似,這里順帶的了解下最近鄰法的計算流程。
最近鄰法實際上是不需要計算新圖像矩陣中點的數值的,直接找到原圖像中對應的點,將數值賦值給新圖像矩陣中的點,根據對應關系找到原圖像中的對應的坐標,這個坐標可能不是整數,這時候找最近的點進行插值。對應關系如下:

變量含義如下:


3.1總結
上圖效果是最近鄰法的計算過程示意圖,由上圖可見,最近鄰法不需要計算只需要尋找原圖中對應的點,所以最近鄰法速度最快,但是會破壞原圖像中像素的漸變關系,原圖像中的像素點的值是漸變的,但是在新圖像中局部破壞了這種漸變關系。
3.2雙線性插值對應關系
雙線性插值的對應公式和前面的最近鄰法一樣,不一樣的是根據對應關系不再是找最近的1個點,而是找最近的4個點,如下圖所示。

這里可能還會有點小疑問,如果根據對應關系找到原圖中的點不是在不同的點之間,而是跟原圖像中的點重合,那該如何找4個點?關於這個問題要看一下雙線性插值的計算公式,雙線性插值實際上是從2個方向一共進行了3次單線性插值,咱們先了解單線性插值的計算方式。
4.單線性插值
已知中P1點和P2點,坐標分別為(x1, y1)、(x2, y2),要計算 [x1, x2] 區間內某一位置 x 在直線上的y值

根據初中的知識,2點求一條直線公式(這是雙線性插值所需要的唯一的基礎公式)

經過簡單整理成下面的格式:

這里沒有寫成經典的AX+B的形式,因為這種形式從權重的角度更好理解。
首先看分子,分子可以看成x與x1和x2的距離作為權重,這也是很好理解的,P點與P1、P2點符合線性變化關系,所以P離P1近就更接近P1,反之則更接近P2。
現在再把公式中的分式看成一個整體,原式可以理解成y1與y2是加權系數,如何理解這個加權,要返回來思考一下,咱們先要明確一下根本的目的:咱們現在不是在求一個公式,而是在圖像中根據2個點的像素值求未知點的像素值。這樣一個公式是不滿足咱們寫代碼的要求的。
現在根據實際的目的理解,就很好理解這個加權了,y1與y2分別代表原圖像中的像素值,上面的公式可以寫成如下形式:

5.雙線性插值
已知Q11(x1,y1)、Q12(x1,y2)、Q21(x2,y1)、Q22(x2,y2),求其中點P(x,y)的值。

前面介紹過雙線性插值是分別在兩個方向計算了共3次單線性插值,如圖所示,先在x方向求2次單線性插值,獲得R1(x, y1)、R2(x, y2)兩個臨時點,再在y方向計算1次單線性插值得出P(x, y)(實際上調換2次軸的方向先y后x也是一樣的結果)。
1.x方向單線性插值 直接帶入前一步單線性插值最后的公式

2.y方向單線性插值

將第一步結果帶入第二步

回顧一下上面雙線性插值對應關系的圖,不難發現,在計算中有這樣的關系:

那么上面的公式中的分母全都為0,如下:

在有些資料中,會寫成權重的形式,上面的展開式是下面的權重表達式的正確求法

這種權重的表達式也不難理解,觀察一下可以發現每個點的權重都和待求點和對角點的距離有關,比如

的坐標有關
5.1遺留問題
上面遺留了個小問題,就是當對應關系公式帶入后跟原圖像中的點發生重合后怎么選取剩下3個點,根據上面的公式可以發現,無論我們怎么選取,其實其余3點的權重都至少1項為0,所以不論我們怎么取剩下的3個點,對最終的結果都不會產生影響。
5.2總結
到此雙線性插值的計算已經完成了,但是我們現在只能說是勉強的完成雙線性插值算法,為什么這么說,現在的計算方式有比較大的問題,看下面的內容。
6.雙線性插值的優化
原始公式:

雙線性插值的對應關系看似比較清晰,但還是有2個問題,我畫圖片進行說明。
根據坐標系的不同,產生的結果不同 這張圖是左上角為坐標系原點的情況,我們可以發現最左邊x=0的點都會有概率直接復制到目標圖像中(至少原點肯定是這樣),而且就算不不和原圖像中的點重合,也相當於進行了1次單線性插值(仔細想想為什么,帶入到權重公式中會發現結果)

現在這張圖是右上角為坐標系原點的情況,我們可以發現最右面的點都會有概率直接復制到目標圖像中(至少原點肯定是這樣),而且就算不不和原圖像中的點重合,也相當於進行了1次單線性插值。這樣如果我們采用不用的坐標系產生的結果是不一樣的,而且無論我們采用什么坐標系,最左側和最右側(最上側和最下側)的點是不“公平的”,這是第一個問題。

整體的圖像相對位置會發生變化看下面這張圖,左側是原圖像(33),右側是目標圖像(55),原圖像的幾何中心點是(1, 1),目標圖像的幾何中心點是(2, 2),根據對應關系,目標圖像的幾何中心點對應的原圖像的位置是(1.2, 1.2),如圖所示,那么問題來了,目標圖像的原點(0, 0)點和原始圖像的原點是重合的,但是目標圖像的幾何中心點相對於原始圖像的幾何中心點偏右下,那么整體圖像的位置會發生偏移,為什么這樣說,其實圖像是由1個個的像素點組成,單純說1個像素點是沒有太大的意義的,1個像素點跟相鄰像素點的值的漸變或者突變形成圖像顏色的漸變或者邊界,所以參與計算的點相對都往右下偏移會產生相對的位置信息損失。這是第二個問題。

6.1計算機視覺中的蝴蝶效應
其實對於咱們人眼,上面的2個問題都不會產生太大的結果,但是對於現在的基於學習的學習類算法,計算機通過卷積神經網絡提取圖像中的深層信息,在這個過程中,我們人眼難以發現的變化也許會發生想象之外的變化,所以不要小看這兩個問題
6.2解決方案
幾何中心點重合對應公式:

再帶入到上面的情況,可以發現問題解決了,到此,才算完成完整的雙線性插值法,當然如果這樣計算發現結果跟OpenCV的結果不一樣,是因為OpenCV還進行了很多速度上的優化,比如用整形計算代替浮點數計算。
對以上解釋的補充
1.雙線性插值
假設源圖像大小為m*n,目標圖像為a*b。那么兩幅圖像的邊長比分別為: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*(m/a)+0.5*(m/a-1)=(i+0.5)*m/a-0.5
int y=j*(n/b)+0.5*(n/b-1)=(j+0.5)*n/b-0.5
instead of
int x=i*m/a
int y=j*n/b
相當於我們在原始的浮點坐標上加上了0.5*(m/a-1)這樣一個控制因子
利用上述公式,將得到正確的雙線性插值結果