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
倍后的图像