赞
踩
双线性插值与双线性采样是在图像插值和采样过程中常用的操作,在pytorch中对应的函数是torch.nn.functional.grid_sample,本文对该操作的原理和代码例程进行笔记。如有谬误,请联系指正,转载请联系作者并注明出处,谢谢。
∇ \nabla ∇ 联系方式:
e-mail: FesianXu@gmail.com
QQ: 973926198
github: https://github.com/FesianXu
知乎专栏: 计算机视觉/计算机图形理论与应用
微信公众号:

插值(interpolation)在数学上指的是 一种估计方法,其根据已知的离散数据点去构造新的数据点。以曲线插值为例子,如Fig 1.1所示的曲线线性插值为例,其中红色数据点是已知的数据点,而蓝色线是根据相邻的两个红色数据点进行线性插值估计出来的。

一维的曲线插值的原理可以推广到任意维度的数据形式上,比如我们常见的图像是一种二维数据,就可以进行二维插值,常见的插值方法如Fig 1.2所示。

在本文中,我们主要讨论的是双线性采样,而双线性采样和双线性插值紧密相关,因此本章节主要介绍双线性插值。还是以2D图像插值为例子,如Fig 1.3所示,假设图片上给定了红色数据点的像素值,假设待求的绿色点
P
=
(
x
,
y
)
P=(x,y)
P=(x,y),其中已知每个顶点像素坐标为:
Q
12
=
(
x
1
,
y
2
)
T
Q
22
=
(
x
2
,
y
2
)
T
Q
11
=
(
x
1
,
y
1
)
T
Q
21
=
(
x
2
,
y
1
)
T
(1.1)
而每个顶点的像素值表示为
f
(
Q
i
j
)
,
i
=
1
,
2
,
j
=
1
,
2
f(Q_{ij}), i =1,2, j=1,2
f(Qij),i=1,2,j=1,2。通过简单的线性插值(按比例划分),我们可以求出蓝色数据点的估计值:
R
2
=
f
(
x
,
y
2
)
=
x
2
−
x
x
2
−
x
1
f
(
Q
12
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
22
)
R
1
=
f
(
x
,
y
1
)
=
x
2
−
x
x
2
−
x
1
f
(
Q
11
)
+
x
−
x
1
x
2
−
x
1
f
(
Q
21
)
(1.2)
然后通过蓝色点,再一次进行线性插值,可以估计出绿色点的值:
f
(
x
,
y
)
=
y
2
−
y
y
2
−
y
1
f
(
x
,
y
1
)
+
y
−
y
1
y
2
−
y
1
f
(
x
,
y
2
)
=
1
(
x
2
−
x
1
)
(
y
2
−
y
1
)
[
x
2
−
x
,
x
−
x
1
]
[
f
(
Q
11
)
f
(
Q
12
)
f
(
Q
21
)
f
(
Q
22
)
]
[
y
2
−
y
y
−
y
1
]
(1.3)
因为该方法涉及到了两轮(注意不是两次,而是三次)的线性插值,因此称之为双线性插值(Bilinear Interpolation)。

在深度学习框架pytorch中提供了一种称之为双线性采样(Bilinear Sample)的函数torch.nn.functional.grid_sample [1],该函数主要输入一个形状为
(
N
,
C
,
H
i
n
,
W
i
n
)
(N,C,H_{in},W_{in})
(N,C,Hin,Win)的input张量,输入一个形状为
(
N
,
H
o
u
t
,
W
o
u
t
,
2
)
(N,H_{out},W_{out},2)
(N,Hout,Wout,2)的grid张量,输出一个形状为
(
N
,
C
,
H
o
u
t
,
W
o
u
t
)
(N,C,H_{out},W_{out})
(N,C,Hout,Wout)的output张量。
其中
N
N
N为batch批次,我们主要关注后面的维度的代表意义。输入的grid是一个
H
o
u
t
×
W
o
u
t
H_{out} \times W_{out}
Hout×Wout大小的空间位置矩阵,其中每个元素都代表着一个二维空间坐标
(
x
,
y
)
(x,y)
(x,y),该坐标指明了在input上采样的坐标,而输出张量的每个位置output[n,:,h,w]的值,取决于这个输入input和采样坐标的值(通过双线性插值形成)。通过这个函数,可以通过指定原图的不同坐标位置,实现图片的变形(deformation)等,在很多研究中有着广泛地应用[2]。
注意到这里的输出张量尺寸和输入张量尺寸是不一定一致的,因此涉及到了插值过程,而且输入的grid的每一个坐标都是归一化到了
[
−
1
,
1
]
[-1,1]
[−1,1]之间的,我们举一个简单的代码例子,明晰下细节。
import torch.nn.functional as F import torch inputv = torch.arange(4*4).view(1, 1, 4, 4).float() print(inputv) ''' 输出尺寸为(1,1,4,4) 输出为:tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]]]]) ''' # 生成grid,这个grid大小为(1,8,8,2),空间尺寸而言是原输入图片的两倍。 d = torch.linspace(-1,1, 8) meshx, meshy = torch.meshgrid((d, d)) grid = torch.stack((meshy, meshx), 2) grid = grid.unsqueeze(0) # add batch dim # 进行双线性采样,其中指定align_corners=True保证了输出的整个图片的角边像素与原输入的一致性。 output = F.grid_sample(inputv, grid,align_corners=True) print(output) ''' tensor([[[[ 0.0000, 0.4286, 0.8571, 1.2857, 1.7143, 2.1429, 2.5714, 3.0000], [ 1.7143, 2.1429, 2.5714, 3.0000, 3.4286, 3.8571, 4.2857, 4.7143], [ 3.4286, 3.8571, 4.2857, 4.7143, 5.1429, 5.5714, 6.0000, 6.4286], [ 5.1429, 5.5714, 6.0000, 6.4286, 6.8571, 7.2857, 7.7143, 8.1429], [ 6.8571, 7.2857, 7.7143, 8.1429, 8.5714, 9.0000, 9.4286, 9.8571], [ 8.5714, 9.0000, 9.4286, 9.8571, 10.2857, 10.7143, 11.1429, 11.5714], [10.2857, 10.7143, 11.1429, 11.5714, 12.0000, 12.4286, 12.8571, 13.2857], [12.0000, 12.4286, 12.8571, 13.2857, 13.7143, 14.1429, 14.5714, 15.0000]]]]) '''
在这个过程中,我们生成的采样坐标网格grid很简单,单纯只是在x,y两个维度,都把
[
−
1
,
1
]
[-1,1]
[−1,1]均分为了8份。
我们分析下双线性采样后的每个像素的大小计算过程。因为每个输入坐标都是
[
−
1
,
1
]
[-1,1]
[−1,1],而实际原输入的矩阵大小为
[
0
,
3
]
[0,3]
[0,3],而且刚好是一个方阵,因此可以计算出从grid到实际坐标的映射为:
f
x
=
f
y
=
3
2
x
n
o
r
m
+
3
2
(1)
f_{x} = f_{y} = \dfrac{3}{2}x_{norm}+\dfrac{3}{2} \tag{1}
fx=fy=23xnorm+23(1)
这个映射将归一化坐标映射到了实际的原图坐标,如果不是方阵,那么就必须对
x
,
y
x,y
x,y每个维度都计算一个映射方程。
我们暂时只考虑怎么计算其中某一个像素的值,暂时我们考虑grid坐标为
[
1
,
1
]
[1,1]
[1,1]的值。我们打印出grid[0,1,1,:],发现这个归一化坐标值为tensor([[-0.7143, -0.7143]]),那么通过反归一化映射,也就是式子(1)后,有实际图片坐标为
(
0.4285
,
0.4285
)
(0.4285, 0.4285)
(0.4285,0.4285),这个时候我们发现这个坐标不是整数,因此为了求出这个坐标的像素值,我们要通过之前谈到的双线性插值去估计。
首先求出每一行的插值结果,有 f ( x , y 1 ) = 0.4285 f(x,y_1) = 0.4285 f(x,y1)=0.4285,这个是在 [ 0 , 1 ] [0,1] [0,1]中插值的结果;有 f ( x , y 2 ) = 4.4285 f(x,y_2) = 4.4285 f(x,y2)=4.4285这个是在 [ 4 , 5 ] [4,5] [4,5]范围内插值的结果,然后再在 [ 0.4285 , 4.4285 ] [0.4285,4.4285] [0.4285,4.4285]中进行插值,有 f ( x , y ) = ( 4.4285 − 0.4285 ) × 0.4285 + 0.4285 = 2.1428 f(x,y) = (4.4285-0.4285) \times 0.4285+0.4285=2.1428 f(x,y)=(4.4285−0.4285)×0.4285+0.4285=2.1428。这就是整个双线性采样的计算过程。
注意:这个输入input也可以是
(
N
,
C
,
D
,
H
i
n
,
W
i
n
)
(N,C,D,H_{in},W_{in})
(N,C,D,Hin,Win)的5D输入,该输入考虑的是对视频进行处理。本文中只考虑了图片数据,不过原理是类似的,不再赘述。
[1]. https://pytorch.org/docs/stable/nn.functional.html#torch.nn.functional.grid_sample
[2]. https://blog.csdn.net/LoseInVain/article/details/108710063
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。