双线性图像插值/缩放 - 计算示例

Bilinear image interpolation / scaling - A calculation example

我想问你一些双线性插值/缩放的细节。假设我们有这个矩阵:

|100 | 50 |
|70  | 20 |

这是一张 2 x 2 灰度图像。现在,我想将它缩放为两倍,我的矩阵如下所示:

| 100   | f1 | 50 | f2 |
| f3    | f4 | f5 | f6 |
| 70    | f7 | 20 | f8 |

所以如果我们想计算f4,计算定义为

f1 = 100 + 0.5(50 - 100) = 75
f7 = 70 +  0.5(20 - 70) = 45

现在终于:

f4 = 75 + 0.5(45 - 75) = 60

但是,我真的无法理解 f3f1

的正确计算方法

我们是否分别在每个方向上进行双线性缩放?因此,这意味着:

f3 = 100 + 0.5(70 - 100) = 85
f1 = 100 + 0.5(50 - 100) = 75

还有,我应该怎么对待f2,f6,f8。这些点是否像最近邻算法中那样简单地被复制?

我想向您指出来自维基百科的这张非常有见地的图表,它说明了如何对一个点进行双线性插值:

来源:Wikipedia

如您所见,四个红点是已知的。这些点您事先知道,P 是我们希望插入的点。因此,我们必须执行两个步骤(正如您在 post 中指出的那样)。要处理 x 坐标(水平),我们必须计算顶行红点和底行红点的插值是多少行。这导致两个蓝点 R1R2。为了处理 y 坐标(垂直),我们使用两个蓝色点并垂直插值以获得最终的 P 点。

当你调整图像大小时,即使我们看不到我要说的内容,但可以想象这个图像是一个 3D 信号 f.矩阵中的每个点实际上是一个 3D 坐标,其中列位置是 x 值,行位置是 y 值,z 值是数量/灰度值矩阵本身。因此,做z = f(x,y)就是矩阵中(x,y)位置的矩阵的值。在我们的例子中,因为您处理的是图像,所以 (x,y) 的每个值都是从 1 到 rows/columns 的整数,具体取决于您正在查看的维度。

因此,给定要在 (x,y) 处插值的坐标,并给定上图中的红色坐标,我们根据图表将其称为 x1,y1,x2,y2 - 具体遵循约定图表和引用图像的访问方式:x1 = 1, x2 = 2, y1 = 2, y2 = 1,蓝色坐标 R1R2 是通过一维插值列计算的,使用同一行,两个点重合:

R1 = f(x1,y1) + (x - x1)/(x2 - x1)*(f(x2,y1) - f(x1,y1))
R2 = f(x1,y2) + (x - x1)/(x2 - x1)*(f(x2,y2) - f(x1,y2))

重要的是要注意 (x - x1) / (x2 - x1) 权重/比例 表示输出包含在 [=37= 处看到的两个值之间的多少混合] 和 f(x2,y1) 用于 R1f(x1,y2)f(x2,y2) 用于 R2。具体来说,x1 是起点,(x2 - x1)x 值的差异。您可以验证将 x1 替换为 x 得到 0,而将 x2 替换为 x 得到 1。此权重在计算所需的 [0,1] 之间波动上班。

需要注意的是,图片原点在左上角,所以(1,1)在左上角。一旦找到 R1R2,我们可以通过按行插值找到 P

P = R2 + (y - y2)/(y2 - y1)*(R1 - R2)

同样,(y - y2) / (y2 - y1) 表示 R1R2 对最终输出 P 的贡献比例/混合。因此,您正确计算了 f5,因为您使用了四个已知点:左上角为 100,右上角为 50,左下角为 70,右下角为 20。具体来说,如果您想计算 f5,这意味着 (x,y) = (1.5,1.5) 因为我们在 100 和 50 之间,因为您将图像缩放了两倍。如果将这些值代入上述计算,您将得到预期的值 60。两次计算的权重也将导致 0.5,这是您在计算中得到的结果,也是我们所期望的。

如果你计算f1,这对应于(x,y) = (1.5,1),如果你将其代入上面的等式,你会看到(y - y2)/(y2 - y1)给你0或者权重为0,所以计算的只是 R2,仅对应于沿顶行的线性插值。同样,如果我们计算 f7,这意味着我们想要在 (x,y) = (1.5,2) 处进行插值。在这种情况下,您会看到 (y - y2) / (y2 - y1) 为 1 或权重为 1,因此 P = R2 + (R1 - R2),这简化为 R1 并且仅是沿底行的线性插值。

现在有 f3f5 的情况。它们分别对应于 (x,y) = (1,1.5)(x,y) = (2,1.5)。将这些值替换为 R1R2 以及 P 两种情况给出:

f3

R1 = f(1,2) + (1 - 1)/(2 - 1)*(f(2,2) - f(1,2)) = f(1,2)
R2 = f(1,1) + (1 - 1)/(2 - 1)*(f(1,2) - f(1,1)) = f(1,1)
P = R1 + (1.5 - 1)*(R1 - R2) = f(1,2) + 0.5*(f(1,2) - f(1,1))

P = 70 + 0.5*(100 - 70) = 85

f5

R1 = f(1,2) + (2 - 1)/(2 - 1)*(f(2,2) - f(1,2)) = f(2,2)
R2 = f(1,1) + (2 - 1)/(2 - 1)*(f(1,2) - f(1,1)) = f(1,2)
P = R1 + (1.5 - 1)*(R1 - R2) = f(2,2) + 0.5*(f(2,2) - f(1,2))

P = 20 + 0.5*(50 - 20) = 35

那么这告诉我们什么?这意味着您仅在 沿 y 方向 进行插值。当我们查看 P 时,这一点很明显。更彻底地检查 Pf3f5 的计算,您会发现我们只考虑垂直方向的值。

因此,如果您想要一个明确的答案,f1f7 是通过仅沿同一行沿 x / 列方向插值找到的. f3f5 是通过沿同一列插值 y / 行方向找到的。如您所见,f4 使用 f1f7 的混合来计算最终值。


为了回答你最后一个问题,f2f6f8根据个人喜好填写。这些值被认为是越界的,xy 值都是 2.5,并且在 (x,y)[1,2] 网格之外。在 MATLAB 中,默认实现是将定义边界之外的任何值填充为非数字 (NaN),但有时,人们会使用线性插值进行外推、复制边界值或执行一些操作精致的填充,如对称或圆形填充。这取决于你所处的情况,但是关于如何填写 f2f6f8 没有正确和明确的答案——这完全取决于你的应用程序和什么使对你来说最有意义。


作为奖励,我们可以在 MATLAB 中验证我的计算是否正确。我们首先在 [1,2] 范围内定义一个 (x,y) 点的网格,然后调整图像大小,使其在我们指定每点 0.5 分辨率而不是 1 分辨率的情况下放大两倍。我要调用您定义的矩阵 A:

A = [100 50; 70 20]; %// Define original matrix
[X,Y] = meshgrid(1:2,1:2); %// Define original grid of points
[X2,Y2] = meshgrid(1:0.5:2.5,1:0.5:2.5) %// Define expanded grid of points
B = interp2(X,Y,A,X2,Y2,'linear'); %// Perform bilinear interpolation

原始 (x,y) 点网格如下所示:

>> X

X =

     1     2
     1     2

>> Y

Y =

     1     1
     2     2

将矩阵的大小扩大两倍的扩展网格如下所示:

>> X2

X2 =

    1.0000    1.5000    2.0000    2.5000
    1.0000    1.5000    2.0000    2.5000
    1.0000    1.5000    2.0000    2.5000
    1.0000    1.5000    2.0000    2.5000

>> Y2

Y2 =

    1.0000    1.0000    1.0000    1.0000
    1.5000    1.5000    1.5000    1.5000
    2.0000    2.0000    2.0000    2.0000
    2.5000    2.5000    2.5000    2.5000

B 是使用 XY 作为点的原始网格的输出, X2Y2 是我们要插值的点在.

我们得到:

>> B

B =

   100    75    50   NaN
    85    60    35   NaN
    70    45    20   NaN
   NaN   NaN   NaN   NaN