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)

经过公式的进一步化简可以得到:

将图像放大两倍(对像素数扩大 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 也是。所以问号处的颜色应该为:

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] #上下范围 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++实现:后面补充。

自己制作的奥运五环

原图 双线性插值法放大 2 倍后的图像

邻近法放大 2 倍后的图像