双线性插值这个算法和公式,着实有点晦涩难懂,花了我很长时间。现在用图文并茂的方法,把我的理解记录一下。

一、先看一下线性插值法

学双线性插值法之前,先把线性插值法学懂,这个很重要,因为后面需要直接套公式。
上个图(网上盗的……):

请添加图片描述
假设我们已知坐标(x0,y0)与(x1,y1),要得到[x0,x1]区间内某一位置x在直线上的值。根据上图中所示,我们从两点式直线方程:
请添加图片描述

将这方程化解一下可得:
请添加图片描述

这个是有公式的,直接套就行:‘两点式方程公式’:已知直线1上的两点P1(x1, y1)、P2(x2, y2), (x1≠x2),那么可得:(y-y1)/(y2-y1)=(x-x1)/(x2-x1)。

二、再来看双线性插值法

知道了线性插值法学了之后,再来看双线性插值法。
要是不乐意看公式的,可以直接跳到图片演示,示例能看懂也行。
下面是关于双线性插值的经典说明图例:

请添加图片描述
首先,图中有5个像素点:Q00, Q01, Q10, Q11, P。
其中四个红色点Q是原图的点,绿色点P则是目标图片上像素点在原图上的投影。
四个红色点Q就是投影点P的四周最近的点,通过这四个红色点Q,可以计算出投影点P的像素值,这样目标图片上的像素点点值就求出来了。

三、目标图的点如何投影到原图上?

已知:
1. 原图的高、宽、通道数为:height_src, width_src, channel_src。
2. 目标图的高、宽、通道数为:height_dst, width_dst, channel_src。因为通道要相同嘛

可以计算出投影公式为:h = h_dst * height_src / height_dst, w = w_dst * width_src / width_dst。

举个例子:原图大小是3 * 3, 目标图大小是9 * 9, 目标图的中心点坐标是(4,4)。按照上面的投影公式,计算目标图的中心点投影到原图中心点的坐标为:h = 4 * 3 / 9 = 1.3333, w = 4 * 3 / 9 = 1.3333,中心点坐标为(1.3333, 1.3333)。
这里就发现一个问题了,原图的大小为3*3,中心点坐标应该是(1,1)啊,公式计算出来的明显不一样。

之所以会出现这个情况,原因是每个像素点实际上是一个边长为1的正方形,所以对于坐标为(h,w)的像素点,它的中心其实是(h+0.5,w+0.5)。

所以精确的算法应该是:请添加图片描述请添加图片描述,转换一下得到正确的投影公式:
请添加图片描述
请添加图片描述

四、如何插值计算得到投影点的像素值?

已知:

1. 四个红色点Q的坐标值为:h0, h1, w0, w1,它们的像素值为f(Q00), f(Q01), f(Q10), f(Q11)。
2. 投影点P的坐标值h, w。

思路是:每个Q点的像素值乘以各自的权重,然后想加得到投影点P像素值。
Q点跟P点的距离越近,它的权重就越大。
双线性插值给出的算法很简单粗暴:先在横轴方向上进行两次线性插值计算,然后在纵轴方向上进行一次插值计算

求横轴上的两次线性插值计算,就是求R0和R1这两个蓝色点的像素值。
请添加图片描述
再通过R0和R1两点的像素值,求得P点的像素值。
因其上已经求过线性插值的公式为:请添加图片描述,现在直接代入就可以了。

具体计算如下:
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

前面有说过,四个红色点Q是投影点P四周最近的点,点P又是目标图片上的一个坐标,其实相当于是一个宽度为1的像素方块。
所以w1-w0=1, h1-h0=1
即公式又可以写成:

请添加图片描述

再设置u和v,令:u=h-h0, v=w-w0,公式又可以写成:
请添加图片描述
到这一步,双线性插值的原理和公式就全部讲完了,再用一个图片来演示一下,融会贯通。

五、图片演示

请添加图片描述
现在有如上这样一张图片,大小为20 * 20,通道3。现在想通过双线性插值的算法,将它变成3 * 3大小的图片。
先做一个坐标轴,记得图片的原坐标(0,0)是在左上角的,将整个图片分为3 * 3等份。
请添加图片描述
一份一份来计算:
请添加图片描述
请添加图片描述
那么来计算一下第一份的P点坐标。因为P点其实是目标图的像素点的投影。那么也就是(0,0)的投影,代入中心对齐后的算法公式,应该是:
请添加图片描述
那么P点的坐标值应该是(2.833, 2.833),Q作为离P点最近的坐标点,坐标应为:h0=2, h1=3, w0=2, w1=3。
到这里,就可以求出u和v的值来,u=h-h0=0.833, v=w-w0=0.833。

又因为我们这个图片是RGB的彩色图,通道为3,所以对应到3*3目标图上时,每一个通道的值都要计算。
现在我们可以来代入公式了。

已知:
1. Q的颜色值为:Q00(0, 0, 255), Q01(0, 0, 255), Q10(0, 0, 255), Q11(0, 0, 255)。
2. u=0.833, v=0.833。

代入公式可得:
请添加图片描述
可求得P点对应的目标图的坐标值为(0, 0, 255)。
以此类推,可讲3*3的目标图求出为:

请添加图片描述

代码

from PIL import Image
import numpy as np
import math


def bilinear_interpolate(src, dst_size):
    height_src, width_src, channel_src = src.shape  # (h, w, ch)
    height_dst, width_dst = dst_size  # (h, w)

    """
    中心对齐,投影目标图的横轴和纵轴到原图上
    """
    ws_p = np.array([(i + 0.5) / width_dst * width_src - 0.5 for i in range(width_dst)], dtype=np.float32)
    hs_p = np.array([(i + 0.5) / height_dst * height_src - 0.5 for i in range(height_dst)], dtype=np.float32)

    """找出每个投影点在原图横轴方向的近邻点坐标对"""
    # w_0的取值范围是 0 ~ (width_src-2),因为w_1 = w_0 + 1
    ws_0 = np.clip(np.floor(ws_p), 0, width_src - 2).astype(int)
    print(ws_p, ws_0)

    """找出每个投影点在原图纵轴方向的近邻点坐标对"""
    # h_0的取值范围是 0 ~ (height_src-2),因为h_1 = h_0 + 1
    hs_0 = np.clip(np.floor(hs_p), 0, height_src - 2).astype(int)

    dst = np.zeros(shape=(height_dst, width_dst, channel_src), dtype=np.float32)
    us = hs_p - hs_0
    vs = ws_p - ws_0
    _1_us = 1 - us
    _1_vs = 1 - vs
    for h in range(height_dst):
        h_0, h_1 = hs_0[h], hs_0[h] + 1  # 原图的坐标
        for w in range(width_dst):
            w_0, w_1 = ws_0[w], ws_0[w] + 1  # 原图的坐标
            for c in range(channel_src):
                dst[h][w][c] = src[h_0][w_0][c] * _1_us[h] * _1_vs[w]\
                               + src[h_0][w_1][c] * _1_us[h] * vs[w]\
                               + src[h_1][w_0][c] * us[h] * _1_vs[w]\
                               + src[h_1][w_1][c] * us[h] * vs[w]

    return dst

im_path = 'test.bmp'
image = np.array(Image.open(im_path))
image2 = bilinear_interpolate(image, (3, 3))
image2 = Image.fromarray(image2.astype('uint8')).convert('RGB')
image2.save('out.png')

参考:
https://www.jianshu.com/p/29e5c84ea539

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐