双线性插值法(bilinear interpolation)

前面讲解了最近邻插值法缩放图像以及不足之处,本篇介绍另外一种插值法,介绍双线性插值法之前先介绍线性插值。

1.   线性插值

  线性插值是指插值函数为一次多项式的插值方式,其在插值节点上的插值误差为零。线性插值可以用来近似代替原函数,也可以用来计算得到查表过程中表中没有的数值。如图所示现在已知y=f(x)的两个点坐标分别是(x0,y0),(x1,y1),现在在区间(x0,x1)内给定任意x,如何求y,线性插值法采用图中红点的y值代替f(x)的y值。假设x处的直线上的红点坐标为(x,Y),那么Y约等于y。根据图可以得到公式:

 

 

 

 y0y1表示得到

公式很好记,将分式看做权重系数。

 

2.   双线性插值法

 

  双线性插值法也叫双线性内插,其核心思想是在两个方向分别进行一次线性插值。双线性插值作为数值分析中的一种插值算法,广泛应用在信号处理,数字图像和视频处理等方面。

 

  如坐标图所示,用横纵坐标代表图像像素的位置,f(x,y)代表该像素点(x,y)的彩色值或灰度值。

 

 

  将图像放大或缩小,目的像素dst对应的原像素src中的坐标转换公式如下,公式很好理解,可参考上一章最近邻插值法。

  srcX=dstX*(srcWidth/dstWidth)

 

   srcY=dstY*(srcHeight/dstHeight)

 

  上式中,dstXdstY为目标图像的某个像素的横纵坐标,dstWidthdstHeight为目标图像的长与宽;srcWidthsrcHeight为原图像的宽度与高度。srcXsrcY为目标图像在该点(dstXdstY)对应的原图像的坐标。

  现在假设目标图像的像素点(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+uy=j+vij为整数,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.5v=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也是。所以问号处的颜色应该为:

 

 

 

  然后以同样的方式计算得到所有的像素值,放大后的图像对比如下:

 

 

 

 

 

  当出现ij为边界坐标时,会出现(i+1,j)(i,j+1)(i+1,j+1)的点在实际的图中不存在,此时可以用(i,j)(i-1,j)(i,j-1)(i-1,j-1)。如果srcXsrcY是整数,则其中一个点是(i,j),剩余的3个点其实无所谓,因为u,v0f(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.存在问题及改进:

 

  关于中心问题,后面补充

 

小结:由于采用线性权重计算新的像素值,所以双线性插值法与最近邻不同,会产生中间值,也就是会产生新的像素值。

 

 

posted @ 2021-08-31 22:32  wancy  阅读(18161)  评论(0编辑  收藏  举报