GridSample - version 20#

This page documents version 20 of operator GridSample. See GridSample for the latest version (since version 22).

  • Domain: ai.onnx

  • Since version: 20

Given an input X and a flow-field grid, computes the output Y using X values and pixel locations from the grid. For spatial input X with shape (N, C, H, W), the grid will have shape (N, H_out, W_out, 2), the output Y will have shape (N, C, H_out, W_out). For volumetric input X with shape (N, C, D, H, W), the grid will have shape (N, D_out, H_out, W_out, 3), the output Y will have shape (N, C, D_out, H_out, W_out). More generally, for an input X of rank r+2 with shape (N, C, d1, d2, …, dr), the grid will have shape (N, D1_out, D2_out, …, Dr_out, r), the output Y will have shape (N, C, D1_out, D2_out, …, Dr_out).

The tensor X contains values at centers of square pixels (voxels, etc) locations such as (n, c, d1_in, d2_in, …, dr_in). The (n, d1_out, d2_out, …, dr_out, :) values from the tensor grid are the normalized positions for interpolating the values at the (n, c, d1_out, d2_out, …, dr_out) locations from the output tensor Y using a specified interpolation method (the mode) and a padding mode (for grid positions falling outside the 2-dimensional image).

For example, the values in grid[n, h_out, w_out, :] are size-2 vectors specifying normalized positions in the 2-dimensional space of X. They are used to interpolate output values of Y[n, c, h_out, w_out].

The GridSample operator is often used in doing grid generator and sampler in the Spatial Transformer Networks. See also in torch.nn.functional.grid_sample.

Inputs

  • X (T1): Input tensor of rank r+2 that has shape (N, C, D1, D2, …, Dr), where N is the batch size, C is the number of channels, D1, D2, …, Dr are the spatial dimensions.

  • grid (T2): Input offset of shape (N, D1_out, D2_out, …, Dr_out, r), where D1_out, D2_out, …, Dr_out are the spatial dimensions of the grid and output, and r is the number of spatial dimensions. Grid specifies the sampling locations normalized by the input spatial dimensions. Therefore, it should have most values in the range of [-1, 1]. If the grid has values outside the range of [-1, 1], the corresponding outputs will be handled as defined by padding_mode. Following computer vision convention, the coordinates in the length-r location vector are listed from the innermost tensor dimension to the outermost, the opposite of regular tensor indexing.

Outputs

  • Y (T1): Output tensor of rank r+2 that has shape (N, C, D1_out, D2_out, …, Dr_out) of the sampled values. For integer input types, intermediate values are computed as floating point and cast to integer at the end.

Attributes

  • align_corners (int): If align_corners=1, the extrema (-1 and 1) are considered as referring to the center points of the input’s corner pixels (voxels, etc.). If align_corners=0, they are instead considered as referring to the corner points of the input’s corner pixels (voxels, etc.), making the sampling more resolution agnostic.

  • mode (string): Three interpolation modes: linear (default), nearest and cubic. The “linear” mode includes linear and N-linear interpolation modes depending on the number of spatial dimensions of the input tensor (i.e. linear for 1 spatial dimension, bilinear for 2 spatial dimensions, etc.). The “cubic” mode also includes N-cubic interpolation modes following the same rules. The “nearest” mode rounds to the nearest even index when the sampling point falls halfway between two indices.

  • padding_mode (string): Support padding modes for outside grid values: zeros``(default), ``border, reflection. zeros: use 0 for out-of-bound grid locations, border: use border values for out-of-bound grid locations, reflection: use values at locations reflected by the border for out-of-bound grid locations. If index 0 represents the margin pixel, the reflected value at index -1 will be the same as the value at index 1. For location far away from the border, it will keep being reflected until becoming in bound. If pixel location x = -3.5 reflects by border -1 and becomes x’ = 1.5, then reflects by border 1 and becomes x’’ = 0.5.

Type Constraints

  • T1: Constrain input X and output Y types to all tensor types. Allowed types: tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8).

  • T2: Constrain grid types to float tensors. Allowed types: tensor(double), tensor(float), tensor(float16).

Examples#

test_gridsample

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
    padding_mode = "zeros"
Inputs:
  X: shape=(1, 1, 4, 4), dtype=float32
    [[[[ 0.,  1.,  2.,  3.],
       [ 4.,  5.,  6.,  7.],
       [ 8.,  9., 10., 11.],
       [12., 13., 14., 15.]]]]
  Grid: shape=(1, 6, 6, 2), dtype=float32
    [[[[-1. , -1. ],
       [-0.6, -1. ],
       [-0.2, -1. ],
       [ 0.2, -1. ],
       [ 0.6, -1. ],
       [ 1. , -1. ]],

      [[-1. , -0.6],
       [-0.6, -0.6],
       [-0.2, -0.6],
       [ 0.2, -0.6],
       [ 0.6, -0.6],
       [ 1. , -0.6]],

      [[-1. , -0.2],
       [-0.6, -0.2],
       [-0.2, -0.2],
       [ 0.2, -0.2],
       [ 0.6, -0.2],
       [ 1. , -0.2]],

      [[-1. ,  0.2],
       [-0.6,  0.2],
       [-0.2,  0.2],
       [ 0.2,  0.2],
       [ 0.6,  0.2],
       [ 1. ,  0.2]],

      [[-1. ,  0.6],
       [-0.6,  0.6],
       [-0.2,  0.6],
       [ 0.2,  0.6],
       [ 0.6,  0.6],
       [ 1. ,  0.6]],

      [[-1. ,  1. ],
       [-0.6,  1. ],
       [-0.2,  1. ],
       [ 0.2,  1. ],
       [ 0.6,  1. ],
       [ 1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 6, 6), dtype=float32
    [[[[ 0.        ,  0.14999998,  0.55      ,  0.95      ,  1.35      ,
         0.75      ],
       [ 0.5999999 ,  1.4999998 ,  2.2999997 ,  3.1       ,  3.8999999 ,
         2.1       ],
       [ 2.2       ,  4.7       ,  5.5       ,  6.3       ,  7.1       ,
         3.7       ],
       [ 3.8       ,  7.9       ,  8.7       ,  9.5       , 10.3       ,
         5.3       ],
       [ 5.4       , 11.1       , 11.900001  , 12.7       , 13.5       ,
         6.9       ],
       [ 3.        ,  6.15      ,  6.55      ,  6.95      ,  7.35      ,
         3.75      ]]]]

test_gridsample_aligncorners_true

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
    align_corners = 1
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -1. ],
       [-0.5, -0.5],
       [-0.2, -0.2],
       [ 0. ,  0. ]],

      [[ 0. ,  0. ],
       [-0.2, -0.2],
       [ 0.5,  0.5],
       [ 1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0.  , 1.25, 2.  , 2.5 ],
       [2.5 , 2.  , 3.75, 5.  ]]]]

test_gridsample_bicubic

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "cubic"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -1. ],
       [-0.5, -0.5],
       [-0.2, -0.2],
       [ 0. ,  0. ]],

      [[ 0. ,  0. ],
       [-0.2, -0.2],
       [ 0.5,  0.5],
       [ 1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[-0.140625 ,  0.3828125,  1.7555516,  2.96875  ],
       [ 2.96875  ,  1.7555516,  5.1445312,  1.390625 ]]]]

test_gridsample_bicubic_align_corners_0_additional_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "cubic"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -0.8],
       [-0.6, -0.5],
       [-0.1, -0.2],
       [ 0.7,  0. ]],

      [[ 0. ,  0.4],
       [ 0.2, -0.2],
       [-0.3,  0.5],
       [-1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[-0.17325   ,  0.28426462,  1.923105  ,  2.568     ],
       [ 5.170375  ,  2.2844129 ,  4.7448435 ,  1.046875  ]]]]

test_gridsample_bicubic_align_corners_1_additional_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "cubic"
    align_corners = 1
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -0.8],
       [-0.6, -0.5],
       [-0.1, -0.2],
       [ 0.7,  0. ]],

      [[ 0. ,  0.4],
       [ 0.2, -0.2],
       [-0.3,  0.5],
       [-1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0.304    , 1.12875  , 2.26627  , 3.1448438],
       [4.5315   , 2.45536  , 4.599819 , 4.       ]]]]

test_gridsample_bilinear

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -1. ],
       [-0.5, -0.5],
       [-0.2, -0.2],
       [ 0. ,  0. ]],

      [[ 0. ,  0. ],
       [-0.2, -0.2],
       [ 0.5,  0.5],
       [ 1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0.  , 0.5 , 1.7 , 2.5 ],
       [2.5 , 1.7 , 4.5 , 1.25]]]]

test_gridsample_bilinear_align_corners_0_additional_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -0.8],
       [-0.6, -0.5],
       [-0.1, -0.2],
       [ 0.7,  0. ]],

      [[ 0. ,  0.4],
       [ 0.2, -0.2],
       [-0.3,  0.5],
       [-1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0.  , 0.45, 1.8 , 2.4 ],
       [3.7 , 2.1 , 3.7 , 1.  ]]]]

test_gridsample_bilinear_align_corners_1_additional_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
    align_corners = 1
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -0.8],
       [-0.6, -0.5],
       [-0.1, -0.2],
       [ 0.7,  0. ]],

      [[ 0. ,  0.4],
       [ 0.2, -0.2],
       [-0.3,  0.5],
       [-1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0.39999998, 1.2       , 2.05      , 2.85      ],
       [3.3       , 2.2       , 3.35      , 4.        ]]]]

test_gridsample_border_padding

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    padding_mode = "border"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-10. , -10. ],
       [ -5. ,  -5. ],
       [ -0.2,  -0.2],
       [ 10. ,  10. ]],

      [[ 10. ,  10. ],
       [ -0.2,  -0.2],
       [  5. ,   5. ],
       [ 10. ,  10. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0. , 0. , 1.7, 5. ],
       [5. , 1.7, 5. , 5. ]]]]

test_gridsample_nearest

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "nearest"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -1. ],
       [-0.5, -0.5],
       [-0.2, -0.2],
       [ 0. ,  0. ]],

      [[ 0. ,  0. ],
       [-0.2, -0.2],
       [ 0.5,  0.5],
       [ 1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0., 0., 2., 2.],
       [2., 2., 5., 0.]]]]

test_gridsample_nearest_align_corners_0_additional_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "nearest"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -0.8],
       [-0.6, -0.5],
       [-0.1, -0.2],
       [ 0.7,  0. ]],

      [[ 0. ,  0.4],
       [ 0.2, -0.2],
       [-0.3,  0.5],
       [-1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0., 0., 2., 3.],
       [4., 3., 4., 4.]]]]

test_gridsample_nearest_align_corners_1_additional_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "nearest"
    align_corners = 1
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-1. , -0.8],
       [-0.6, -0.5],
       [-0.1, -0.2],
       [ 0.7,  0. ]],

      [[ 0. ,  0.4],
       [ 0.2, -0.2],
       [-0.3,  0.5],
       [-1. ,  1. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0., 0., 2., 3.],
       [2., 3., 4., 4.]]]]

test_gridsample_reflection_padding

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    padding_mode = "reflection"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-10. , -10. ],
       [ -5. ,  -5. ],
       [ -0.2,  -0.2],
       [ 10. ,  10. ]],

      [[ 10. ,  10. ],
       [ -0.2,  -0.2],
       [  5. ,   5. ],
       [ 10. ,  10. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[2.5, 0. , 1.7, 2.5],
       [2.5, 1.7, 5. , 2.5]]]]

test_gridsample_volumetric_bilinear_align_corners_0

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
Inputs:
  X: shape=(1, 1, 3, 2, 2), dtype=float32
    [[[[[ 1.,  2.],
        [ 3.,  4.]],

       [[ 5.,  6.],
        [ 7.,  8.]],

       [[ 9., 10.],
        [11., 12.]]]]]
  Grid: shape=(1, 2, 4, 2, 3), dtype=float32
    [[[[[-1. , -1. , -1. ],
        [-1. , -0.5,  0.3]],

       [[-0.5, -0.5, -0.5],
        [ 1. , -0.6, -1. ]],

       [[-0.2, -0.2, -0.2],
        [ 0.4,  0.2,  0.6]],

       [[ 0. ,  0. ,  0. ],
        [-1. ,  0. ,  0. ]]],


      [[[ 0. ,  0. ,  0. ],
        [-1. ,  1. ,  0. ]],

       [[-0.2, -0.2, -0.2],
        [ 1. ,  0.4, -0.2]],

       [[ 0.5,  0.5,  0.5],
        [-1. , -0.8,  0.8]],

       [[ 1. ,  1. ,  1. ],
        [ 0.4,  0.6, -0.3]]]]]

Outputs:
  Y: shape=(1, 1, 2, 4, 2), dtype=float32
    [[[[[ 0.125   ,  3.4     ],
        [ 2.      ,  0.45    ],
        [ 4.7     , 10.900001],
        [ 6.5     ,  3.      ]],

       [[ 6.5     ,  1.75    ],
        [ 4.7     ,  3.3     ],
        [11.      ,  2.52    ],
        [ 1.5     ,  5.49    ]]]]]

test_gridsample_volumetric_bilinear_align_corners_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "linear"
    align_corners = 1
Inputs:
  X: shape=(1, 1, 3, 2, 2), dtype=float32
    [[[[[ 1.,  2.],
        [ 3.,  4.]],

       [[ 5.,  6.],
        [ 7.,  8.]],

       [[ 9., 10.],
        [11., 12.]]]]]
  Grid: shape=(1, 2, 4, 2, 3), dtype=float32
    [[[[[-1. , -1. , -1. ],
        [-1. , -0.5,  0.3]],

       [[-0.5, -0.5, -0.5],
        [ 1. , -0.6, -1. ]],

       [[-0.2, -0.2, -0.2],
        [ 0.4,  0.2,  0.6]],

       [[ 0. ,  0. ,  0. ],
        [-1. ,  0. ,  0. ]]],


      [[[ 0. ,  0. ,  0. ],
        [-1. ,  1. ,  0. ]],

       [[-0.2, -0.2, -0.2],
        [ 1. ,  0.4, -0.2]],

       [[ 0.5,  0.5,  0.5],
        [-1. , -0.8,  0.8]],

       [[ 1. ,  1. ,  1. ],
        [ 0.4,  0.6, -0.3]]]]]

Outputs:
  Y: shape=(1, 1, 2, 4, 2), dtype=float32
    [[[[[ 1.  ,  6.7 ],
        [ 3.75,  2.4 ],
        [ 5.4 ,  9.3 ],
        [ 6.5 ,  6.  ]],

       [[ 6.5 ,  7.  ],
        [ 5.4 ,  6.6 ],
        [ 9.25,  8.4 ],
        [12.  ,  6.1 ]]]]]

test_gridsample_volumetric_nearest_align_corners_0

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "nearest"
Inputs:
  X: shape=(1, 1, 3, 2, 2), dtype=float32
    [[[[[ 1.,  2.],
        [ 3.,  4.]],

       [[ 5.,  6.],
        [ 7.,  8.]],

       [[ 9., 10.],
        [11., 12.]]]]]
  Grid: shape=(1, 2, 4, 2, 3), dtype=float32
    [[[[[-1. , -1. , -1. ],
        [-1. , -0.5,  0.3]],

       [[-0.5, -0.5, -0.5],
        [ 1. , -0.6, -1. ]],

       [[-0.2, -0.2, -0.2],
        [ 0.4,  0.2,  0.6]],

       [[ 0. ,  0. ,  0. ],
        [-1. ,  0. ,  0. ]]],


      [[[ 0. ,  0. ,  0. ],
        [-1. ,  1. ,  0. ]],

       [[-0.2, -0.2, -0.2],
        [ 1. ,  0.4, -0.2]],

       [[ 0.5,  0.5,  0.5],
        [-1. , -0.8,  0.8]],

       [[ 1. ,  1. ,  1. ],
        [ 0.4,  0.6, -0.3]]]]]

Outputs:
  Y: shape=(1, 1, 2, 4, 2), dtype=float32
    [[[[[ 1.,  5.],
        [ 1.,  0.],
        [ 5., 12.],
        [ 5.,  5.]],

       [[ 5.,  0.],
        [ 5.,  0.],
        [12.,  9.],
        [ 0.,  8.]]]]]

test_gridsample_volumetric_nearest_align_corners_1

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    mode = "nearest"
    align_corners = 1
Inputs:
  X: shape=(1, 1, 3, 2, 2), dtype=float32
    [[[[[ 1.,  2.],
        [ 3.,  4.]],

       [[ 5.,  6.],
        [ 7.,  8.]],

       [[ 9., 10.],
        [11., 12.]]]]]
  Grid: shape=(1, 2, 4, 2, 3), dtype=float32
    [[[[[-1. , -1. , -1. ],
        [-1. , -0.5,  0.3]],

       [[-0.5, -0.5, -0.5],
        [ 1. , -0.6, -1. ]],

       [[-0.2, -0.2, -0.2],
        [ 0.4,  0.2,  0.6]],

       [[ 0. ,  0. ,  0. ],
        [-1. ,  0. ,  0. ]]],


      [[[ 0. ,  0. ,  0. ],
        [-1. ,  1. ,  0. ]],

       [[-0.2, -0.2, -0.2],
        [ 1. ,  0.4, -0.2]],

       [[ 0.5,  0.5,  0.5],
        [-1. , -0.8,  0.8]],

       [[ 1. ,  1. ,  1. ],
        [ 0.4,  0.6, -0.3]]]]]

Outputs:
  Y: shape=(1, 1, 2, 4, 2), dtype=float32
    [[[[[ 1.,  5.],
        [ 1.,  2.],
        [ 5., 12.],
        [ 5.,  5.]],

       [[ 5.,  7.],
        [ 5.,  8.],
        [12.,  9.],
        [12.,  8.]]]]]

test_gridsample_zeros_padding

Node:
  GridSample(X, Grid) -> (Y)
  Attributes:
    padding_mode = "zeros"
Inputs:
  X: shape=(1, 1, 3, 2), dtype=float32
    [[[[0., 1.],
       [2., 3.],
       [4., 5.]]]]
  Grid: shape=(1, 2, 4, 2), dtype=float32
    [[[[-10. , -10. ],
       [ -5. ,  -5. ],
       [ -0.2,  -0.2],
       [ 10. ,  10. ]],

      [[ 10. ,  10. ],
       [ -0.2,  -0.2],
       [  5. ,   5. ],
       [ 10. ,  10. ]]]]

Outputs:
  Y: shape=(1, 1, 2, 4), dtype=float32
    [[[[0. , 0. , 1.7, 0. ],
       [0. , 1.7, 0. , 0. ]]]]

Differences with previous version (16)#

SchemaDiff: GridSample (domain 'ai.onnx')

  • old version: 16

  • new version: 20

  • breaking: yes

Breaking reasons:

  • attribute ‘mode’ (changed): default value changed bilinear -> linear

Attributes:

  • [BREAKING] changed ‘mode’: default value changed bilinear -> linear

Documentation:

  • line similarity: 0.24 (+15/-10 lines)

--- GridSample v16
+++ GridSample v20
@@ -1,14 +1,19 @@

-Given an input `X` and a flow-field `grid`, computes the output `Y` using `X` values and pixel locations from `grid`.
-Currently, only spatial (4-D) inputs are supported. For input `X` with shape (N, C, H, W) and `grid` with shape (N, H_out, W_out, 2),
-the output `Y` will have shape (N, C, H_out, W_out).
+Given an input `X` and a flow-field `grid`, computes the output `Y` using `X` values and pixel locations from the `grid`.
+For spatial input `X` with shape (N, C, H, W), the `grid` will have shape (N, H_out, W_out, 2),
+the output `Y` will have shape (N, C, H_out, W_out). For volumetric input `X` with shape (N, C, D, H, W),
+the `grid` will have shape (N, D_out, H_out, W_out, 3), the output `Y` will have shape (N, C, D_out, H_out, W_out).
+More generally, for an input `X` of rank r+2 with shape (N, C, d1, d2, ..., dr),
+the `grid` will have shape (N, D1_out, D2_out, ..., Dr_out, r), the output `Y` will have shape (N, C, D1_out, D2_out, ..., Dr_out).

-The tensor `X` contains values at centers of square pixels in a H by W 2-dimensional image.
-The tensor `grid` describes normalized positions where the output `Y` is to be computed
-using a specified interpolation method (the mode) and a padding mode (for grid positions falling outside the 2-dimensional image).
+The tensor `X` contains values at centers of square pixels (voxels, etc) locations such as (n, c, d1_in, d2_in, ..., dr_in).
+The (n, d1_out, d2_out, ..., dr_out, :) values from the tensor `grid` are the normalized positions for interpolating the values
+at the (n, c, d1_out, d2_out, ..., dr_out) locations from the output tensor `Y` using a specified interpolation method (the mode)
+and a padding mode (for `grid` positions falling outside the 2-dimensional image).

-Elements in `grid[N, H_out, W_out]` are size-2 vectors specifying positions in the 2-dimensional space of `X`.
-They are used to interpolate output values of `Y[N, C, H_out, W_out]`.
+For example, the values in `grid[n, h_out, w_out, :]` are size-2 vectors specifying normalized positions in the 2-dimensional space of `X`.
+They are used to interpolate output values of `Y[n, c, h_out, w_out]`.

-The GridSample operator is often used in doing grid generator and sampler in the [Spatial Transformer Networks](https://arxiv.org/abs/1506.02025).
-See also in [torch.nn.functional.grid_sample](https://pytorch.org/docs/master/generated/torch.nn.functional.grid_sample.html#torch-nn-functional-grid-sample).
+The GridSample operator is often used in doing grid generator and sampler in the
+[Spatial Transformer Networks](https://arxiv.org/abs/1506.02025).
+See also in [torch.nn.functional.grid_sample](https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html).