前面講解了最近鄰插值法縮放圖像以及不足之處,本篇介紹另外一種插值法,介紹雙線性插值法之前先介紹線性插值。
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.存在問題及改進:
關於中心問題,后面補充
