前面講解了最近鄰插值法縮放圖像以及不足之處,本篇介紹另外一種插值法,介紹雙線性插值法之前先介紹線性插值。
1. 線性插值
線性插值是指插值函數為一次多項式的插值方式,其在插值節點上的插值誤差為零。線性插值可以用來近似代替原函數,也可以用來計算得到查表過程中表中沒有的數值。如圖所示現在已知y=f(x)的兩個點坐標分別是(x0,y0),(x1,y1),現在在區間(x0,x1)內給定任意x,如何求y,線性插值法采用圖中紅點的y值代替f(x)的y值。假設x處的直線上的紅點坐標為(x,Y),那么Y約等於y。根據圖可以得到公式:
用y0,y1表示得到
公式很好記,將分式看做權重系數。
2. 雙線性插值法
雙線性插值法也叫雙線性內插,其核心思想是在兩個方向分別進行一次線性插值。雙線性插值作為數值分析中的一種插值算法,廣泛應用在信號處理,數字圖像和視頻處理等方面。
如坐標圖所示,用橫縱坐標代表圖像像素的位置,f(x,y)代表該像素點(x,y)的彩色值或灰度值。
將圖像放大或縮小,目的像素dst對應的原像素src中的坐標轉換公式如下,公式很好理解,可參考上一章最近鄰插值法。
srcX=dstX*(srcWidth/dstWidth)
srcY=dstY*(srcHeight/dstHeight)
上式中,dstX與dstY為目標圖像的某個像素的橫縱坐標,dstWidth與dstHeight為目標圖像的長與寬;srcWidth與srcHeight為原圖像的寬度與高度。srcX,srcY為目標圖像在該點(dstX,dstY)對應的原圖像的坐標。
現在假設目標圖像的像素點(x’,y’)映射到原圖像中是(x,y),也就是圖中的P點。設Q11 = (x1, y1)、Q12 = (x1, y2)、Q21 = (x2, y1) 、 Q22 = (x2, y2),圖中Q11,Q12,Q21,Q22分別為距離P點的最近的四個點。分別在X方向進行兩次插值,最后在y方向進行插值即可得到目標圖像的像素值。公式如下:
先計算X方向(也可以先計算Y方向,再計算X方向)的線性插值:
再在y方向進行線性插值得到f(P):
經過公式的進一步化簡可以得到:
由於是最近的點,所以Q的四個點的坐標之間相差1,故進一步化簡為:
現假設x=i+u,y=j+v,i,j為整數,u,v為小數。得到下式:
下面為本人自己設計的例子與具體計算過程:
將圖像放大兩倍(對像素數擴大2倍),以?處的像素為例:
右邊圖中問號的坐標為(5,4),映射到原圖像的像素點的坐標為
srcX=dstX*(srcWidth/dstWidth)=5*(4/8)=2.5
srcY=dstY*(srcHeight/dstHeight)=4*(4/8)=2
(srcX,srcY)=(2+0.5,2+0),u=0.5,v=0,映射的src坐標及對應的最近的四個Q點下如圖所示。若x=i+u,y=j+v。則可以得到紅色框的像素及其周圍四個Q點(i,j)、(i+1,j)、(i,j+1)、(i+1,j+1),即可以得到?處的像素值。
每個像素點的RGB對應如下:
204,255,153 153,255,153 102,255,153 0,255,153
204,255,102 153,255,102 102,255,102 0,255,0
204,255,51 153,255,51 102,255,51 51,204,51
204,204,0 153,204,0 102,153,0 0,153,0
Q11(2,2),Q21(2,3),Q12(3,2),Q22(3,3),P(2.5,2)
采用雙線性插值法得到紅色框處的像素值:
R=0.5*102+0.5*51=51+25.5=76.5
G=0.5*255+0.5*204=127.5+102=229.5
B=0.5*51+0.5*51=25.5+25.5=51
由於顏色表示范圍為0-255整數,R可以用127代替或128代替,B也是。所以問號處的顏色應該為:
然后以同樣的方式計算得到所有的像素值,放大后的圖像對比如下:
當出現i或j為邊界坐標時,會出現(i+1,j)、(i,j+1)、(i+1,j+1)的點在實際的圖中不存在,此時可以用(i,j)、(i-1,j)、(i,j-1)、(i-1,j-1)。如果srcX與srcY是整數,則其中一個點是(i,j),剩余的3個點其實無所謂,因為u,v為0。f(p)=f(i,j)
3. 代碼實現
python實現:
from matplotlib import pyplot as plt import numpy as np def bilinear_interpolation(srcimage_vector,dstwidthtimes ,dstheighttimes ): print("原始圖像",srcimage_vector) srcHeight=srcimage_vector.shape[0] srcWidth=srcimage_vector.shape[1] print("srcHeight",srcHeight) print("srcWidth",srcWidth) dstWidth, dstHeight=int(dstwidthtimes*srcWidth),int(dstheighttimes*srcHeight) # 定義一個三維矩陣存儲目標圖像,每個像素由RGB三種顏色組成,shape接受一個元組,可以創建多維矩陣 dstVector = np.zeros(shape=(dstHeight, dstWidth, 3), dtype=int) # 默認是float64 # 遍歷目標圖像的每一個像素 for dstY in range(0,dstHeight): for dstX in range(0,dstWidth): Q = [(0, 0)] * 4 # Q11=Q12=Q21=Q22=0 Qdict = {} # 坐標換算 dstX_srcX = dstX * (srcWidth / dstWidth) # python中/表示除法,//表示整除 dstY_srcY = dstY * (srcHeight / dstHeight) u = round(dstX_srcX % 1, 2) v = round(dstY_srcY % 1, 2) if int(dstX_srcX)==srcWidth-1: positionX = [int(dstX_srcX)-1, int(dstX_srcX)] # 左右范圍 else: positionX=[int(dstX_srcX),int(dstX_srcX)+1]#左右范圍 if int(dstY_srcY)==srcHeight-1: positionY = [int(dstY_srcY)-1, int(dstY_srcY)] # 上下范圍 else: positionY = [int(dstY_srcY), int(dstY_srcY) + 1] #上下范圍 k=0 for m in range(0, 2): # 得到Q的四個點坐標,分別是Q11,Q12,Q21,Q22 for n in range(0, 2): Q[k] = (positionX[m], positionY[n]) Qdict[Q[k]] = srcimage_vector[positionY[n], positionX[m]] k = k + 1 # 通過四個點計算得到dst的像素值 dstVector[dstY][dstX] = Qdict[Q[0]] * (1 - u) * (1 - v) + Qdict[Q[1]] * (1 - u) * (v) + Qdict[Q[2]] * (u) * ( 1 - v) + Qdict[Q[3]] * (u) * (v) print(dstVector) ax = plt.gca() # 獲取到當前坐標軸信息 ax.xaxis.set_ticks_position('top') # 將X坐標軸移到上面 plt.imshow(dstVector) # 顯示數組 plt.show() plt.imsave('shaungxianxing.jpg', dstVector.astype(np.uint8)) # matplotlib保存圖像的時候,只接受浮點數或者unit8的整數類型 if __name__=="__main__": # imagePath = r'songshu.jpg' imagePath = r'cup.jpg' # imagePath = r'aoyunwuhuan.jpg' image_vector = plt.imread(imagePath) # dstwidth,dstheight用倍數表示,表示是src圖像的多少倍 # dstwidth, dstheight=1.5,1.5 dstwidth, dstheight=0.2,0.2 bilinear_interpolation(image_vector,dstwidth,dstheight)
C++實現:后面補充。
4.效果
自己制作的奧運五環
原圖雙線性插值法放大2倍后的圖像
鄰近法放大2倍后的圖像
網上找的松鼠:
原圖
放大2倍后
縮小0.2倍后
手機拍的圖
原圖
縮小0.2倍
結論:通過圖可以看出,雙線性插值法,鋸齒狀比鄰近法效果好多了,但是也會伴隨着圖片的變模糊。
5.存在問題及改進:
關於中心問題,后面補充