MindSpore实现Grid_Sample方法
原理
一. 背景
Grid_Sample方法由两个单词Grid(网格)和Sample(采样),顾名思义,此方法要做的工作就是根据给定的网格对原始数据进行采样。既然是根据网格采样,自然地,就需要给定:
- 原始数据(被采样的数据),称其为input
- 给定的归一化的网格(需要采样的点的坐标),称其为grid,点的取值为[-1, 1]
- 采样所用的方法,称其为mode,包括但不限于:nearest(最近邻)、bilinear(双线性插值)等
除以上三个参数外,也需要给定:
- 原始数据点的指定方式,即原始数据四角上的的点是否位于归一化网格的四个角上(比较绕,下面有图),称该参数为:align_corners
- 外部网格的填充方式,即当采样点位于原始数据外侧时(align_corners=False时可能出现),怎么给定边缘的点的值,该参数为:padding_mode
二. 计算过程
Grid_Sample采样大的分为网格化原始数据和采样两个部分,更详细的采样过程见附录:
- 网格化input,这里分align_corners为真和假两种情况。不失一般性,并为了描述清楚概念,假设这里input的大小为 ,考虑高度为 ,宽度为 点 的坐标,同时为了写代码方便,这里的高度 定义为距离最上方的距离,宽度 定义为距离最左边的距离, , 。
A. *align_corners=True*, 左上角点的坐标为(-1, -1),右下角的点的坐标为(1, 1),点$p_{in}$的坐标为$(\frac{2 \times w_{in}^{'}}{w_{in}-1}-1, \frac{2 \times h_{in}^{'}}{h_{in}-1}-1)$
B. *align_corners=False*,左上角点坐标为($\frac{1}{w_{in}}-1$, $\frac{1}{h_{in}}-1$),右下角的坐标为($1- \frac{1}{w_{in}}$, $1-\frac{1}{h_{in}}$),点$p_{in}$的坐标为$(\frac{2 \times w_{in}^{'}+1}{w_{in}}-1, \frac{2 \times h_{in}^{'}+1}{h_{in}}-1)$
-
寻找input中与grid采样点最近邻点的坐标,这里也要分align_corners为真和假两种情况。不失一般性,并为了描述清楚概念,这里仅考虑一个点且插值方法为双线性插值的情况,假设该采样点 的坐标为 。
A. align_corners=True, 原始图像中,每相邻两个点之间的横坐标间隔 为 ,纵坐标的间隔 为 ,
a. 距离最左边的绝对距离 ,距离最上面的绝对距离
b. 距离最左边的间隔数 ,距离最上面的间隔数
c. 易推得点 最近邻的左上方点 的坐标为 ,最近邻的左下方点 的坐标为 ,最近邻的右上方点 的坐标为 ,最近邻的右下方点 的坐标为 ,在具体实现中求得点的坐标索引值即可,即 和 的向上向下取整即可。
二维情况下的python实现,(注:代码中以h向下为x,w向右为y,与上面描述略有差异)
```python
# 参照pytorch,grid->[w, h]
x_index, y_index = 1, 0
h_in, w_in = input.shape
d_x, d_y = 2 / (h_in - 1), 2 / (w_in -1)
# the given horizontal and vertical coordinates
x_g = grid[..., x_index]
y_g = grid[..., y_index]
# distance from the top/left
d_t, d_l = x_g + 1, y_g + 1
# how many pixels away from the top/left
d_delta_t, d_delta_l = d_t / d_x, d_l / d_y
x_t, y_l = np.floor(d_delta_t).astype(np.int), np.floor(d_delta_l).astype(np.int)
x_b, y_r = x_t + 1, y_l + 1
# obtain the nearest point
p_tl, p_tr, p_bl, p_br = input[x_t, y_l], input[x_t, y_r], input[x_b, y_l], input[x_b, y_r]
# weights are inversely proportional to distance
w_l, w_r, w_t, w_b = y_r - d_delta_l, d_delta_l - y_l, x_b - d_delta_t, d_delta_t - x_t
if mode == 'nearest':
max_lr = np.concatenate((np.expand_dims(w_l, -1), np.expand_dims(w_r, -1)), -1).max(-1)
max_tb = np.concatenate((np.expand_dims(w_t, -1), np.expand_dims(w_b, -1)), -1).max(-1)
w_l, w_r = (max_lr - (w_l, w_r)) < 1e-8
w_t, w_b = (max_tb - (w_t, w_b)) < 1e-8
w_tl, w_tr, w_bl, w_br = w_t * w_l, w_t * w_r, w_b * w_l, w_b * w_r
sample = p_tl * w_tl + p_tr * w_tr + p_bl * w_bl + p_br * w_br
```
考虑到越界(grid中坐标范围超过[-1, 1])的二维情况下的python实现,pad分不同情况:
```python
trans_pix = 0
if padding_mode == 'zeros':
input = np.pad(input, ((1, 1), (1, 1)))
trans_pix = 1
elif padding_mode == 'border':
input = np.pad(input, ((1, 1), (1, 1)), 'edge')
trans_pix = 1
h_in, w_in = input.shape
h_in, w_in = h_in - 2 * trans_pix, w_in - 2 * trans_pix
d_x, d_y = 2 / (h_in - 1), 2 / (w_in - 1)
# the given horizontal and vertical coordinates
x_g = grid[..., x_index]
y_g = grid[..., y_index]
# distance from the top/left
d_t, d_l = x_g + 1, y_g + 1
# how many pixels away from the far top/left
d_delta_t, d_delta_l = d_t / d_x, d_l / d_y
x_t, y_l = np.floor(d_delta_t).astype(np.int) + trans_pix, np.floor(d_delta_l).astype(np.int) + trans_pix
x_b, y_r = np.ceil(d_delta_t).astype(np.int) + trans_pix, np.ceil(d_delta_l).astype(np.int) + trans_pix
if padding_mode == 'zeros' or padding_mode == 'border':
min_coordinate = np.zeros_like(x_t)
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
max_coordinate_x = np.ones_like(x_t) * (h_in + trans_pix)
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(max_coordinate_x, -1)), -1).min(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(max_coordinate_x, -1)), -1).min(-1)
max_coordinate_y = np.ones_like(x_t) * (w_in + trans_pix)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(max_coordinate_y, -1)), -1).min(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(max_coordinate_y, -1)), -1).min(-1)
else:
x_t, x_b, y_l, y_r = np.abs(x_t), np.abs(x_b), np.abs(y_l), np.abs(y_r)
x_t, x_b = x_t % (2 *(h_in - 1)), x_b % (2 *(h_in - 1))
y_l, y_r = y_l % (2 *(w_in - 1)), y_r % (2 * (w_in - 1))
x_t_ref, x_b_ref = 2 * (h_in - 1) - x_t, 2 * (h_in - 1) - x_b
y_l_ref, y_r_ref = 2 * (w_in - 1) - y_l, 2 * (w_in - 1) - y_r
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(x_t_ref, -1)), -1).min(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(x_b_ref, -1)), -1).min(-1)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(y_l_ref, -1)), -1).min(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(y_r_ref, -1)), -1).min(-1)
# obtain the nearest point
p_tl, p_tr, p_bl, p_br = input[x_t, y_l], input[x_t, y_r], input[x_b, y_l], input[x_b, y_r]
# weights are inversely proportional to distance
w_l, w_r, w_t, w_b = np.abs(1.0 - d_delta_l % 1), np.abs(d_delta_l % 1), np.abs(1.0 - d_delta_t % 1), np.abs(d_delta_t % 1)
# w_l, w_r, w_t, w_b = y_r - d_delta_l, d_delta_l - y_l, x_b - d_delta_t, d_delta_t - x_t
if mode == 'nearest':
max_lr = np.concatenate((np.expand_dims(w_l, -1), np.expand_dims(w_r, -1)), -1).max(-1)
max_tb = np.concatenate((np.expand_dims(w_t, -1), np.expand_dims(w_b, -1)), -1).max(-1)
w_l, w_r = (max_lr - (w_l, w_r)) < 1e-8
w_t, w_b = (max_tb - (w_t, w_b)) < 1e-8
w_tl, w_tr, w_bl, w_br = w_t * w_l, w_t * w_r, w_b * w_l, w_b * w_r
sample = p_tl * w_tl + p_tr * w_tr + p_bl * w_bl + p_br * w_br
```
B. *align_corners=False*,原始图像中,每相邻两个点之间的横坐标间隔$d_x$为$\frac{2}{w_{in}}$,纵坐标的间隔$d_y$为$\frac{2}{h_{in}}$,相对于True的情况坐标轴整体向右下平移半个单位距离即可。
```python
def grid_sample_numpy(input, grid, mode=None, padding_mode=None, align_corners=None, grid_format='wh'):
if mode is None:
mode = 'bilinear'
if align_corners is None:
align_corners = True
if padding_mode is None:
padding_mode = 'zeros'
dims_input = len(input.shape)
assert mode in ['nearest', 'bilinear']
assert padding_mode in ['zeros', 'border', 'reflection']
assert dims_input in [2, 3, 4, 5]
sample = None
if grid_format == 'wh':
x_index, y_index = 1, 0
elif grid_format == 'hw':
x_index, y_index = 0, 1
trans_pix = 0
if padding_mode == 'zeros':
input = np.pad(input, ((1, 1), (1, 1)))
trans_pix = 1
elif padding_mode == 'border':
input = np.pad(input, ((1, 1), (1, 1)), 'edge')
trans_pix = 1
# 2-D input
if dims_input == 2:
# origin input: 0->h->x, 1->w->y
h_in, w_in = input.shape
h_in, w_in = h_in - 2 * trans_pix, w_in - 2 * trans_pix
if align_corners is True:
d_x, d_y = 2 / (h_in - 1), 2 / (w_in - 1)
x_0, y_0 = - 1, - 1
else:
d_x, d_y = 2 / h_in, 2 / w_in
x_0, y_0 = d_x / 2 - 1, d_y / 2 - 1
# the given horizontal and vertical coordinates
x_g = grid[..., x_index]
y_g = grid[..., y_index]
# distance from the top/left
d_t, d_l = x_g - x_0, y_g - y_0
# how many pixels away from the far top/left
d_delta_t, d_delta_l = d_t / d_x, d_l / d_y
x_t, y_l = np.floor(d_delta_t).astype(np.int) + trans_pix, np.floor(d_delta_l).astype(np.int) + trans_pix
x_b, y_r = np.ceil(d_delta_t).astype(np.int) + trans_pix, np.ceil(d_delta_l).astype(np.int) + trans_pix
# Handle out-of-bounds grids
if padding_mode == 'zeros' or padding_mode == 'border':
min_coordinate = np.zeros_like(x_t)
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
max_coordinate_x = np.ones_like(x_t) * (h_in + trans_pix)
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(max_coordinate_x, -1)), -1).min(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(max_coordinate_x, -1)), -1).min(-1)
max_coordinate_y = np.ones_like(x_t) * (w_in + trans_pix)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(max_coordinate_y, -1)), -1).min(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(max_coordinate_y, -1)), -1).min(-1)
else:
x_t, x_b, y_l, y_r = np.abs(x_t), np.abs(x_b), np.abs(y_l), np.abs(y_r)
x_t, x_b = x_t % (2 * (h_in - 1)), x_b % (2 * (h_in - 1))
y_l, y_r = y_l % (2 * (w_in - 1)), y_r % (2 * (w_in - 1))
x_t_ref, x_b_ref = 2 * (h_in - 1) - x_t, 2 * (h_in - 1) - x_b
y_l_ref, y_r_ref = 2 * (w_in - 1) - y_l, 2 * (w_in - 1) - y_r
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(x_t_ref, -1)), -1).min(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(x_b_ref, -1)), -1).min(-1)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(y_l_ref, -1)), -1).min(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(y_r_ref, -1)), -1).min(-1)
# obtain the nearest point
p_tl, p_tr, p_bl, p_br = input[x_t, y_l], input[x_t, y_r], input[x_b, y_l], input[x_b, y_r]
# weights are inversely proportional to distance
w_l, w_r, w_t, w_b = 1.0 - np.abs(d_delta_l % 1), np.abs(d_delta_l % 1), 1.0 - np.abs(
d_delta_t % 1), np.abs(d_delta_t % 1)
# interpolation method
if mode == 'nearest':
max_lr = np.concatenate((np.expand_dims(w_l, -1), np.expand_dims(w_r, -1)), -1).max(-1)
max_tb = np.concatenate((np.expand_dims(w_t, -1), np.expand_dims(w_b, -1)), -1).max(-1)
w_l, w_t = (max_lr - w_l) < 1e-8, (max_tb - w_t) < 1e-8
w_r, w_b = 1 - w_l, 1 - w_t
w_tl, w_tr, w_bl, w_br = w_t * w_l, w_t * w_r, w_b * w_l, w_b * w_r
sample = p_tl * w_tl + p_tr * w_tr + p_bl * w_bl + p_br * w_br
return sample
```
- 采样。二维情况下,双线性插值采样,假设距离左右上下整数像素点的距离依次为 , , , ,则左右上下依次在最后采样中所占的权重与他们的距离成反比,依次为 , , , ,四个像素所占的权重依次为 ,采样值为 。
附完整代码:
`import copy
import numpy as np
import mindspore.numpy as mnp
import mindspore
from mindspore import context, ops, nn
from mindspore import Tensor as M_Tensor
import torch
context.set_context(mode=context.PYNATIVE_MODE, device_target=‘GPU’, device_id=0)
class Grid_Sample(nn.Cell):
pass
def grid_sample_numpy(input, grid, mode=None, padding_mode=None, align_corners=None, grid_format=‘wh’):
if mode is None:
mode = ‘bilinear’
if align_corners is None:
align_corners = True
if padding_mode is None:
padding_mode = ‘zeros’
dims_input = len(input.shape)
assert mode in ['nearest', 'bilinear']
assert padding_mode in ['zeros', 'border', 'reflection']
assert dims_input in [2, 3, 4, 5]
sample = None
if grid_format == 'wh':
x_index, y_index = 1, 0
elif grid_format == 'hw':
x_index, y_index = 0, 1
# 2-D input
if dims_input == 2 or dims_input == 3:
trans_pix = 0
if padding_mode == 'zeros':
if dims_input == 2:
input = np.pad(input, ((1, 1), (1, 1)))
elif dims_input == 3:
input = np.pad(input, ((0, 0), (1, 1), (1, 1)))
trans_pix = 1
elif padding_mode == 'border':
if dims_input == 2:
input = np.pad(input, ((1, 1), (1, 1)), 'edge')
elif dims_input == 3:
input = np.pad(input, ((0, 0), (1, 1), (1, 1)), 'edge')
trans_pix = 1
# origin input: 0->h->x, 1->w->y
if dims_input == 2:
h_in, w_in = input.shape
elif dims_input == 3:
c_in, h_in, w_in = input.shape
h_in, w_in = h_in - 2 * trans_pix, w_in - 2 * trans_pix
if align_corners is True:
d_x, d_y = 2 / (h_in - 1), 2 / (w_in - 1)
x_0, y_0 = - 1, - 1
else:
d_x, d_y = 2 / h_in, 2 / w_in
x_0, y_0 = d_x / 2 - 1, d_y / 2 - 1
# the given horizontal and vertical coordinates
x_g = grid[..., x_index]
y_g = grid[..., y_index]
# distance from the top/left
d_t, d_l = x_g - x_0, y_g - y_0
# how many pixels away from the far top/left
d_delta_t, d_delta_l = d_t / d_x, d_l / d_y
x_t, y_l = np.floor(d_delta_t).astype(np.int) + trans_pix, np.floor(d_delta_l).astype(np.int) + trans_pix
x_b, y_r = np.ceil(d_delta_t).astype(np.int) + trans_pix, np.ceil(d_delta_l).astype(np.int) + trans_pix
# Handle out-of-bounds grids
if padding_mode == 'zeros' or padding_mode == 'border':
min_coordinate = np.zeros_like(x_t)
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(min_coordinate, -1)), -1).max(-1)
max_coordinate_x = np.ones_like(x_t) * (h_in + trans_pix)
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(max_coordinate_x, -1)), -1).min(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(max_coordinate_x, -1)), -1).min(-1)
max_coordinate_y = np.ones_like(x_t) * (w_in + trans_pix)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(max_coordinate_y, -1)), -1).min(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(max_coordinate_y, -1)), -1).min(-1)
else:
x_t, x_b, y_l, y_r = np.abs(x_t), np.abs(x_b), np.abs(y_l), np.abs(y_r)
x_t, x_b = x_t % (2 * (h_in - 1)), x_b % (2 * (h_in - 1))
y_l, y_r = y_l % (2 * (w_in - 1)), y_r % (2 * (w_in - 1))
x_t_ref, x_b_ref = 2 * (h_in - 1) - x_t, 2 * (h_in - 1) - x_b
y_l_ref, y_r_ref = 2 * (w_in - 1) - y_l, 2 * (w_in - 1) - y_r
x_t = np.concatenate((np.expand_dims(x_t, -1), np.expand_dims(x_t_ref, -1)), -1).min(-1)
x_b = np.concatenate((np.expand_dims(x_b, -1), np.expand_dims(x_b_ref, -1)), -1).min(-1)
y_l = np.concatenate((np.expand_dims(y_l, -1), np.expand_dims(y_l_ref, -1)), -1).min(-1)
y_r = np.concatenate((np.expand_dims(y_r, -1), np.expand_dims(y_r_ref, -1)), -1).min(-1)
# obtain the nearest point
p_tl, p_tr, p_bl, p_br = input[..., x_t, y_l], input[..., x_t, y_r], input[..., x_b, y_l], input[..., x_b, y_r]
# weights are inversely proportional to distance
w_l, w_r, w_t, w_b = 1.0 - np.abs(d_delta_l % 1), np.abs(d_delta_l % 1), 1.0 - np.abs(
d_delta_t % 1), np.abs(d_delta_t % 1)
# interpolation method
if mode == 'nearest':
max_lr = np.concatenate((np.expand_dims(w_l, -1), np.expand_dims(w_r, -1)), -1).max(-1)
max_tb = np.concatenate((np.expand_dims(w_t, -1), np.expand_dims(w_b, -1)), -1).max(-1)
w_l, w_t = (max_lr - w_l) < 1e-8, (max_tb - w_t) < 1e-8
w_r, w_b = 1 - w_l, 1 - w_t
w_tl, w_tr, w_bl, w_br = w_t * w_l, w_t * w_r, w_b * w_l, w_b * w_r
sample = p_tl * w_tl + p_tr * w_tr + p_bl * w_bl + p_br * w_br
return sample
def cal_np(input, grid, mode, padding_mode, align_corners):
return grid_sample_numpy(input, grid, mode, padding_mode, align_corners)
def cal_torch(input, grid, mode, padding_mode, align_corners):
input = torch.from_numpy(input)
grid = torch.from_numpy(grid)
dims_input = len(input.shape)
if dims_input == 2:
h_in, w_in = input.shape
h_out, w_out, _ = grid.shape
input = input.reshape(1, 1, h_in, w_in)
grid = grid.reshape(1, h_out, w_out, 2)
out = torch.nn.functional.grid_sample(input, grid, mode, padding_mode, align_corners)
out = out.reshape(h_out, w_out)
elif dims_input == 3:
c_in, h_in, w_in = input.shape
h_out, w_out, _ = grid.shape
input = input.reshape(1, c_in, h_in, w_in)
grid = grid.reshape(1, h_out, w_out, 2)
out = torch.nn.functional.grid_sample(input, grid, mode, padding_mode, align_corners)
out = out.reshape(c_in, h_out, w_out)
out = out.numpy()
return out
def normalize_grid(grid, range=(-1, 1)):
max = grid.max()
min = grid.min()
grid = (range[1] - range[0]) * (grid - min) / (max - min) + range[0]
return grid
def generate_data(dims):
if dims == 2:
a, b, c, d, e, f, g, h, i, j = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
k, l, m, n, o, p, q, s, t, u = 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
input = [[a, b, c, d, e], [f, g, h, i, j], [k, l, m, n, o], [p, q, s, t, u]]
input = np.array(input).astype(np.float32)
grid = np.random.random((1, 1, 2)).astype(np.float32)
grid = normalize_grid(grid)
# 0:w, 1:h
index_w, index_h = 0, 1
# d_w, d_h = 2 / 5, 2 / 4
# w_0, h_0 = d_w - 1, d_h - 1
grid[0][0][index_w] = 1
grid[0][0][index_h] = -1
if dims == 3:
c_in, h_in, w_in = 3, 20, 30
input = np.random.random((c_in, h_in, w_in)).astype(np.float32)
grid = np.random.random((2, 1, 2)).astype(np.float32)
grid = normalize_grid(grid)
return input, grid
def tst_grid_sample(input, grid, mode, padding_mode, align_corners):
print(’================================’)
print(mode, align_corners, padding_mode)
torch_grid_sample = cal_torch(input, grid, mode, padding_mode, align_corners)
np_grid_sample = cal_np(input, grid, mode, padding_mode, align_corners)
difference = np.abs(torch_grid_sample - np_grid_sample) > 1e-5
if np.max(difference) == True:
print('failure, for more details as followings:')
print('torch', torch_grid_sample[difference])
print('numpy', np_grid_sample[difference])
# print('grid', grid[difference])
else:
print('success')
if name == ‘main’:
dims_lst = [2, 3]
mode_lst = [‘nearest’, ‘bilinear’]
align_corners_lst = [True, False]
# ‘border’ ‘border’, ‘zeros’
padding_mode_lst = [‘border’, ‘zeros’, ‘reflection’]
for dims in dims_lst:
input, grid = generate_data(dims=dims)
for mode in mode_lst:
for align_corners in align_corners_lst:
for padding_mode in padding_mode_lst:
tst_grid_sample(input, grid, mode, padding_mode, align_corners)
`
- 点赞
- 收藏
- 关注作者
评论(0)