diff --git a/converter/main.py b/converter/main.py index 8db65514dbc14ad813f055f1bf32a7521ae50d5f..bca6e8f95613edba14217b50fbca50033c4143f2 100644 --- a/converter/main.py +++ b/converter/main.py @@ -123,6 +123,7 @@ class OPTYPE(IntEnum): # and the matrix multiplication by batches. BatchMatMul = (6,) ScatterND = (22,) + GridSample = (23,) # "BatchMatMulV2" did not exist in Tensorflow 1.9. It exists in # Tensorflow 1.15. @@ -1004,6 +1005,37 @@ def parse_graph_node( myGraph[node.output[0]]["additional"]["data"] = node map_onnx_to_myGraph[node.output[0]] = node.output[0] + elif node.op_type == "GridSample": + # Currently, the official TensorFlow does not have an implementation for GridSample. + align_corners = getAttribute(node, "align_corners").i + mode = getAttribute(node, "mode").s.decode('utf-8') + padding_mode = getAttribute(node, "padding_mode").s.decode('utf-8') + + mode_list = ["nearest", "bilinear"] + if not mode in mode_list: + quit("[ERROR] Currently, the mode of GridSample must in", mode_list, node) + else: + mode = mode_list.index(mode) + padding_mode_list = ["border"] + if not padding_mode in padding_mode_list: + quit("[ERROR] Currently, the padding_mode of GridSample must in", + padding_mode_list, node) + else: + padding_mode = padding_mode_list.index(padding_mode) + + myGraph[node.output[0]] = {} + myGraph[node.output[0]]["op_type"] = OPTYPE.GridSample + myGraph[node.output[0]]["inputs"] = [ + map_onnx_to_myGraph[n0name], + map_onnx_to_myGraph[node.input[1]], + ] + myGraph[node.output[0]]["additional"] = {} + myGraph[node.output[0]]["additional"]["data"] = node + myGraph[node.output[0]]["additional"]["align_corners"] = align_corners + myGraph[node.output[0]]["additional"]["mode"] = mode + myGraph[node.output[0]]["additional"]["padding_mode"] = padding_mode + map_onnx_to_myGraph[node.output[0]] = node.output[0] + else: raise Exception("[ERROR] node not supported:\n{})".format(node)) @@ -1272,6 +1304,23 @@ def dump_onnx(graph, my_inputs, my_outputs, output_filename, verbose=False): print("#\t axis", node["additional"]["axis"]) f.write(struct.pack("i", int(node["additional"]["axis"]))) + elif node["op_type"] == OPTYPE.GridSample: + if verbose: + print("#\t align_corners", + node["additional"]["align_corners"]) + f.write(struct.pack( + "i", int(node["additional"]["align_corners"]))) + + if verbose: + print("#\t mode", node["additional"]["mode"]) + f.write(struct.pack("i", int(node["additional"]["mode"]))) + + if verbose: + print("#\t padding_mode", + node["additional"]["padding_mode"]) + f.write(struct.pack( + "i", int(node["additional"]["padding_mode"]))) + if ( node["op_type"] == OPTYPE.Conv2D or node["op_type"] == OPTYPE.Conv2DTranspose @@ -1348,6 +1397,19 @@ def annotate_node( if verbose > 1: print("[WARNING] transpose not for NCHW handling in\n", node) + if node_annotation[node.name].to_remove: + # The GridSample is usually used with Transpose. Cause the optical-flow will be + # transposed from (N,2,H,W) to (N,H,W,2) and this operation should not be removed. + # Meanwhile there will not have other operations after transposed feature with + # shape (N,H,W,2) in PyTorch, so the code in this IF statement will not influnce + # other situations. + nexts = getNodesWithInput(node.output[0], model_onnx) + for n in nexts: + if n.op_type == "GridSample": + node_annotation[node.name].to_remove = False + node_annotation[node.name].layout_onnx = "nchw" + break + elif node.op_type == "Reshape": initializer = getInitializer(node.input[1], model_onnx) # Case: In pytorch, Reshape is not in model_onnx.graph.initializer but in model_onnx.graph.node diff --git a/sadl/layer.h b/sadl/layer.h index 7bd52b569a146eb25940c6b7611dc965a8836d6e..a7ed32bf8b0f7fe3589839f2df49ceff96f10163 100644 --- a/sadl/layer.h +++ b/sadl/layer.h @@ -70,7 +70,8 @@ struct OperationType Slice = 20, PReLU = 21, ScatterND = 22, - OperationTypeCount = 23 + GridSample = 23, + OperationTypeCount = 24 }; }; diff --git a/sadl/layer_gridsample.h b/sadl/layer_gridsample.h new file mode 100644 index 0000000000000000000000000000000000000000..ac57280b062554d07cd6e62ab3c481633f4f9b2f --- /dev/null +++ b/sadl/layer_gridsample.h @@ -0,0 +1,249 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2023, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#include "layer.h" + +namespace sadl +{ +namespace layers +{ +template<typename T> class GridSample : public Layer<T> +{ +public: + using Layer<T>::Layer; + using Layer<T>::m_out; // to avoid this-> + using Layer<T>::m_initDone; + + virtual bool apply(std::vector<Tensor<T> *> &in) override; + virtual bool init(const std::vector<Tensor<T> *> &in) override; + +protected: + virtual bool loadInternal(std::istream &file, Version) override; + void gs_denormalize(float &x, int length); + void pixel_addr_at_grid(int &addr, int H, int W, int C, int i, int j, int c); + void atomic_bilinear(const Tensor<T> &X, float x, float y, int shift, int addr_out); + int m_align_corners; // 0: False, 1: True + int m_mode; // 0: "nearest", 1: "bilinear" + int m_padding_mode; // 0: "border" + using T2 = typename ComputationType<T>::type; + DUMP_MODEL_EXT; +}; + +template<typename T> bool GridSample<T>::loadInternal(std::istream &file, Version) +{ + if (!std::is_same<T, float>::value) + { + std::cerr << "[ERROR] Currently, GridSample only supports float data type." << std::endl; + return false; + } + + int32_t x = 0; + file.read((char *) &x, sizeof(x)); + m_align_corners = x; + SADL_DBG(std::cout << " - align_corners: " << m_align_corners << std::endl); + file.read((char *) &x, sizeof(x)); + m_mode = x; + SADL_DBG(std::cout << " - mode: " << m_mode << std::endl); + file.read((char *) &x, sizeof(x)); + m_padding_mode = x; + SADL_DBG(std::cout << " - padding_mode: " << m_padding_mode << std::endl); + if (m_mode != 0 && m_mode != 1) + { + std::cerr << "[ERROR] invalid mode: " << m_mode << std::endl; + return false; + } + if (m_padding_mode != 0) + { + std::cerr << "[ERROR] invalid padding mode: " << m_padding_mode << std::endl; + return false; + } + return true; +} + +template<typename T> bool GridSample<T>::init(const std::vector<Tensor<T> *> &in) +{ + if (in.size() != 2) + return false; + Dimensions dim; + dim.resize(4); + dim[0] = in[0]->dims()[0]; // N, inherit from X + dim[1] = in[1]->dims()[3]; // H_out, inherit from grid + dim[2] = in[1]->dims()[1]; // W_out, inherit from grid + dim[3] = in[0]->dims()[3]; // C, inherit from X + m_out.resize(dim); + m_initDone = true; + return true; +} + +template<typename T> bool GridSample<T>::apply(std::vector<Tensor<T> *> &in) +{ + const Tensor<T> &X = *in[0]; // (1,H_in,W_in,C) + const Tensor<T> &grid = *in[1]; // (1,W_out,2,H_out) + m_out.quantizer = X.quantizer; + assert(X.dims()[0] == 1 && grid.dims()[0] == 1 && grid.dims()[2] == 2); + + int C = X.dims()[3]; + int H_in = X.dims()[1]; + int W_in = X.dims()[2]; + int H_out = grid.dims()[3]; + int W_out = grid.dims()[1]; + float x_min = (m_align_corners) ? 0 : -0.5; + float x_max = (m_align_corners) ? W_in - 1 : W_in - 0.5; + float y_min = (m_align_corners) ? 0 : -0.5; + float y_max = (m_align_corners) ? H_in - 1 : H_in - 0.5; + bool normalize_grid_flag = !std::is_same<T, float>::value; + float normalize_grid_coeff = 2 << (grid.quantizer - 1); + int shift = grid.quantizer; + + // Given an input X and a flow-field grid, computes the output Y using X values and pixel locations from the grid. + int addr_grid_x = -W_out, addr_grid_y = -W_out + H_out; + for (int im_j = 0; im_j < W_out; im_j++) + { + addr_grid_x += H_out; + addr_grid_y += H_out; + for (int im_i = 0; im_i < H_out; im_i++) + { + // compute pixel locations from the grid + float x = grid[addr_grid_x++]; + float y = grid[addr_grid_y++]; + if (normalize_grid_flag) + { + x = x / normalize_grid_coeff; + y = y / normalize_grid_coeff; + } + gs_denormalize(x, W_in); + gs_denormalize(y, H_in); + if (m_mode == 0) // nearest + { + x = round(x); + y = round(y); + } + if (x < x_min || x > x_max || y < y_min || y > y_max) + { + if (m_padding_mode == 0) // border + { + x = (x < 0) ? 0 : ((x > W_in - 1) ? W_in - 1 : x); + y = (y < 0) ? 0 : ((y > H_in - 1) ? H_in - 1 : y); + } + } + // compute the output Y using X values and pixel locations + int addr_out = (im_i * W_out + im_j) * C; + if (m_mode == 0) // nearest + { + int addr_grid = 0; + pixel_addr_at_grid(addr_grid, H_in, W_in, C, int(y), int(x), 0); + for (int im_c = 0; im_c < C; im_c++) + { + m_out[addr_out++] = X[addr_grid]; // same data type + COUNTERS(X[addr_grid++]); + } + } + else if (m_mode == 1) // bilinear + { + atomic_bilinear(X, x, y, shift, addr_out); + } + } + } + return true; +} + +template<typename T> void GridSample<T>::gs_denormalize(float &x, int length) +{ + if (m_align_corners) + { + x = (x + 1) / 2.0 * (length - 1); + } + else + { + x = ((x + 1) * length - 1) / 2.0; + } +} + +template<typename T> void GridSample<T>::pixel_addr_at_grid(int &addr, int H, int W, int C, int i, int j, int c) +{ + if (m_padding_mode == 0) // border + { + i = (i < 0) ? 0 : ((i > H - 1) ? H - 1 : i); + j = (j < 0) ? 0 : ((j > W - 1) ? W - 1 : j); + addr = C * (W * i + j) + c; // (0, i, j, c) + } +} + +template<typename T> void GridSample<T>::atomic_bilinear(const Tensor<T> &X, float x, float y, int shift, int addr_out) +{ + int H_in = X.dims()[1]; + int W_in = X.dims()[2]; + int C = X.dims()[3]; + + T2 num{}; + T coeff11{}, coeff12{}, coeff21{}, coeff22{}; + float x1 = floor(x), y1 = floor(y), x2 = x1 + 1, y2 = y1 + 1; + float normalize_grid_coeff = 2 << (shift - 1); + if (std::is_same<T2, float>::value) + { + coeff11 = (y2 - y) * (x2 - x); // dy2 * dx2 + coeff12 = (y2 - y) * (x - x1); // dy2 * dx1 + coeff21 = (y - y1) * (x2 - x); // dy1 * dx2 + coeff22 = (y - y1) * (x - x1); // dy1 * dx1 + } + else + { + coeff11 = (y2 - y) * (x2 - x) * normalize_grid_coeff; + coeff12 = (y2 - y) * (x - x1) * normalize_grid_coeff; + coeff21 = (y - y1) * (x2 - x) * normalize_grid_coeff; + coeff22 = (y - y1) * (x - x1) * normalize_grid_coeff; + } + + int addr_grid11 = 0, addr_grid12 = 0, addr_grid21 = 0, addr_grid22 = 0; + pixel_addr_at_grid(addr_grid11, H_in, W_in, C, y1, x1, 0); + pixel_addr_at_grid(addr_grid12, H_in, W_in, C, y1, x2, 0); + pixel_addr_at_grid(addr_grid21, H_in, W_in, C, y2, x1, 0); + pixel_addr_at_grid(addr_grid22, H_in, W_in, C, y2, x2, 0); + for (int im_c = 0; im_c < C; im_c++) + { + num = coeff11 * X[addr_grid11++] + coeff12 * X[addr_grid12++] + coeff21 * X[addr_grid21++] + coeff22 * X[addr_grid22++]; + ComputationType<T>::quantize(num, shift); + { + COUNTERS_MAC(coeff11); + COUNTERS_MAC(coeff12); + COUNTERS_MAC(coeff21); + COUNTERS_MAC(coeff22); + } + COUNTERS(num); + SATURATE(num); + m_out[addr_out++] = static_cast<T>(num); + } +} + +} // namespace layers +} // namespace sadl diff --git a/sadl/layers.h b/sadl/layers.h index b08604c12eb28cc4c8e2ec6a76d9b514a91c8c67..dabfef58d4978e7b9f5773622259bb3a7544510f 100644 --- a/sadl/layers.h +++ b/sadl/layers.h @@ -55,6 +55,7 @@ #include "layer_slice.h" #include "layer_prelu.h" #include "layer_scatternd.h" +#include "layer_gridsample.h" namespace sadl { @@ -92,6 +93,7 @@ inline std::string opName(const OperationType::Type op) DIRTYCASEPRINT(Slice); DIRTYCASEPRINT(PReLU); DIRTYCASEPRINT(ScatterND); + DIRTYCASEPRINT(GridSample); default: oss << "??"; break; diff --git a/sadl/model.h b/sadl/model.h index 9b749521396c8a6b115def21c124f4549407ccb9..c40d67fb1b89df94dc58f24564ddb14628955d8b 100644 --- a/sadl/model.h +++ b/sadl/model.h @@ -171,7 +171,10 @@ template<typename T> std::unique_ptr<layers::Layer<T>> createLayer(int32_t id, l return std::unique_ptr<layers::Layer<T>>(new layers::PReLU<T>{ id, op }); break; case layers::OperationType::ScatterND: - return std::unique_ptr<layers::Layer<T>>(new layers::ScatterND<T>{id, op}); + return std::unique_ptr<layers::Layer<T>>(new layers::ScatterND<T>{ id, op }); + break; + case layers::OperationType::GridSample: + return std::unique_ptr<layers::Layer<T>>(new layers::GridSample<T>{ id, op }); break; case layers::OperationType::OperationTypeCount: break; // no default on purpose diff --git a/sample/copy.h b/sample/copy.h index 12b546a347934c797432c44c03a6a1c75d3b3dc2..64423953af36576dea0581310cbc7143ba8cfab7 100644 --- a/sample/copy.h +++ b/sample/copy.h @@ -82,6 +82,11 @@ template<typename T> bool copy(const sadl::layers::Layer<float> &layer, sadl::la break; case sadl::layers::OperationType::ScatterND: break; + case sadl::layers::OperationType::GridSample: + dynamic_cast<sadl::layers::GridSample<T> &>(layerQ).m_align_corners = dynamic_cast<const sadl::layers::GridSample<float> &>(layer).m_align_corners; + dynamic_cast<sadl::layers::GridSample<T> &>(layerQ).m_mode = dynamic_cast<const sadl::layers::GridSample<float> &>(layer).m_mode; + dynamic_cast<sadl::layers::GridSample<T> &>(layerQ).m_padding_mode = dynamic_cast<const sadl::layers::GridSample<float> &>(layer).m_padding_mode; + break; // no default to get warning } diff --git a/sample/dumper.h b/sample/dumper.h index 826352ed5b2d120011d3eb8237ece510699395a3..d2349cf552ccebd247fb40ea5ab9eb721ff65b86 100644 --- a/sample/dumper.h +++ b/sample/dumper.h @@ -112,6 +112,14 @@ template<typename T> bool sadl::layers::Const<T>::dump(std::ostream &file) return true; } +template<typename T> bool sadl::layers::GridSample<T>::dump(std::ostream &file) +{ + file.write((const char *) &m_align_corners, sizeof(m_align_corners)); + file.write((const char *) &m_mode, sizeof(m_mode)); + file.write((const char *) &m_padding_mode, sizeof(m_padding_mode)); + return true; +} + template<typename T> bool sadl::layers::Layer<T>::dump(std::ostream &file) { // std::cout<<"todo? "<<opName(op_)<<std::endl; diff --git a/sample/naive_quantization.cpp b/sample/naive_quantization.cpp index 567a577e08c1ed6a676eeb146d6fb90ad0ecd83f..d50351664e35550aaae6a5291a0a2ebc087b60ce 100644 --- a/sample/naive_quantization.cpp +++ b/sample/naive_quantization.cpp @@ -72,7 +72,7 @@ bool toQuantize(sadl::layers::OperationType::Type type) && type != sadl::layers::OperationType::Identity && type != sadl::layers::OperationType::LeakyRelu && type != sadl::layers::OperationType::MaxPool && type != sadl::layers::OperationType::Relu && type != sadl::layers::OperationType::Reshape && type != sadl::layers::OperationType::Shape && type != sadl::layers::OperationType::Slice && type != sadl::layers::OperationType::Transpose && type != sadl::layers::OperationType::PReLU - && type != sadl::layers::OperationType::ScatterND; + && type != sadl::layers::OperationType::ScatterND && type != sadl::layers::OperationType::GridSample; } template<typename T> void quantizeTensor(const sadl::Tensor<float> &B, sadl::Tensor<T> &Bq) diff --git a/utests/check.sh b/utests/check.sh index fecee81f457b4eb13fb26c8adf009da7207245bf..adbd03eb6a668053e2fe305b471e92127033c95c 100755 --- a/utests/check.sh +++ b/utests/check.sh @@ -18,7 +18,7 @@ for F in $L; do ../utest.sh ../models/${F}.onnx --no_transpose; done -L="conv2d_4_8x8x4_k1x1s1,1_g1_p0,0 conv2d_4_8x8x4_k1x1s1,1_g4_p0,0 conv2d_4_8x8x4_k1x1s2,1_g1_p0,0 conv2d_4_8x8x4_k1x3s1,2_g1_p0,1 conv2d_4_8x8x4_k3x1s1,1_g1_p1,0 conv2d_4_8x8x4_k3x1s1,1_g4_p1,0 conv2d_4_8x8x4_k3x3s1,1_g1_p1,1 conv2d_4_8x8x4_k3x3s2,1_g1_p1,1 conv2d_4_8x8x4_k3x3s2,2_g1_p1,1 conv2d_4_8x8x4_k5x5s1,1_g1_p2,2 conv2d_4_8x8x4_k5x5s1,1_g4_p2,2 conv2d_4_8x8x4_k5x5s2,1_g1_p2,2 conv2d_4_8x9x4_k1x1s2,1_g1_p0,0 conv2d_4_8x9x4_k3x1s1,1_g4_p1,0 conv2d_4_8x9x4_k3x3s1,1_g4_p1,1 conv2d_4_8x9x4_k3x3s2,1_g1_p1,1 conv2d_4_8x9x4_k3x3s2,2_g1_p1,1 conv2d_4_9x8x4_k1x1s1,1_g1_p0,0 conv2d_4_9x8x4_k1x1s2,1_g1_p0,0 conv2d_4_9x8x4_k1x3s1,2_g1_p0,1 conv2d_4_9x8x4_k3x1s1,1_g1_p1,0 conv2d_4_9x8x4_k3x3s1,1_g1_p1,1 conv2d_4_9x8x4_k3x3s2,1_g1_p1,1 conv2d_4_9x8x4_k3x3s2,2_g1_p1,1 conv2d_4_9x8x4_k5x5s1,1_g1_p2,2 conv2d_4_9x8x4_k5x5s2,1_g1_p2,2 conv2d_4_9x9x4_k1x3s1,2_g1_p0,1 repeated_conv slice_pytorch slice_inf_pytorch slice_chw_pytorch prelu_multiple_alpha prelu_single_alpha scatternd_c_pytorch scatternd_hwc_with_conv_pytorch"; +L="conv2d_4_8x8x4_k1x1s1,1_g1_p0,0 conv2d_4_8x8x4_k1x1s1,1_g4_p0,0 conv2d_4_8x8x4_k1x1s2,1_g1_p0,0 conv2d_4_8x8x4_k1x3s1,2_g1_p0,1 conv2d_4_8x8x4_k3x1s1,1_g1_p1,0 conv2d_4_8x8x4_k3x1s1,1_g4_p1,0 conv2d_4_8x8x4_k3x3s1,1_g1_p1,1 conv2d_4_8x8x4_k3x3s2,1_g1_p1,1 conv2d_4_8x8x4_k3x3s2,2_g1_p1,1 conv2d_4_8x8x4_k5x5s1,1_g1_p2,2 conv2d_4_8x8x4_k5x5s1,1_g4_p2,2 conv2d_4_8x8x4_k5x5s2,1_g1_p2,2 conv2d_4_8x9x4_k1x1s2,1_g1_p0,0 conv2d_4_8x9x4_k3x1s1,1_g4_p1,0 conv2d_4_8x9x4_k3x3s1,1_g4_p1,1 conv2d_4_8x9x4_k3x3s2,1_g1_p1,1 conv2d_4_8x9x4_k3x3s2,2_g1_p1,1 conv2d_4_9x8x4_k1x1s1,1_g1_p0,0 conv2d_4_9x8x4_k1x1s2,1_g1_p0,0 conv2d_4_9x8x4_k1x3s1,2_g1_p0,1 conv2d_4_9x8x4_k3x1s1,1_g1_p1,0 conv2d_4_9x8x4_k3x3s1,1_g1_p1,1 conv2d_4_9x8x4_k3x3s2,1_g1_p1,1 conv2d_4_9x8x4_k3x3s2,2_g1_p1,1 conv2d_4_9x8x4_k5x5s1,1_g1_p2,2 conv2d_4_9x8x4_k5x5s2,1_g1_p2,2 conv2d_4_9x9x4_k1x3s1,2_g1_p0,1 repeated_conv slice_pytorch slice_inf_pytorch slice_chw_pytorch prelu_multiple_alpha prelu_single_alpha scatternd_c_pytorch scatternd_hwc_with_conv_pytorch gridsample_bilinear gridsample_nearest gridsample_bilinear_conv gridsample_nearest_conv"; for F in $L; do ../utest.sh ../models/${F}.onnx; done diff --git a/utests/models/gridsample_bilinear.onnx b/utests/models/gridsample_bilinear.onnx new file mode 100644 index 0000000000000000000000000000000000000000..f5d29b9d2c87b9e48e562bb813c4a13a9c91a164 --- /dev/null +++ b/utests/models/gridsample_bilinear.onnx @@ -0,0 +1,26 @@ +pytorch1.13.0:Ø +n +img +grid_2/GridSample" +GridSample* + align_corners * +mode"bilinear * +padding_mode"border torch_jitZ +img + + + + +Z +grid_ + + + + +b +2 + + + + +B \ No newline at end of file diff --git a/utests/models/gridsample_bilinear_conv.onnx b/utests/models/gridsample_bilinear_conv.onnx new file mode 100644 index 0000000000000000000000000000000000000000..f74c3110f6f769b890d6ba1abedf36028d57d322 Binary files /dev/null and b/utests/models/gridsample_bilinear_conv.onnx differ diff --git a/utests/models/gridsample_nearest.onnx b/utests/models/gridsample_nearest.onnx new file mode 100644 index 0000000000000000000000000000000000000000..5c73e93fbb8b3c0da5f6c371171dab73caba7872 --- /dev/null +++ b/utests/models/gridsample_nearest.onnx @@ -0,0 +1,26 @@ +pytorch1.13.0:× +m +img +grid_2/GridSample" +GridSample* + align_corners * +mode"nearest * +padding_mode"border torch_jitZ +img + + + + +Z +grid_ + + + + +b +2 + + + + +B \ No newline at end of file diff --git a/utests/models/gridsample_nearest_conv.onnx b/utests/models/gridsample_nearest_conv.onnx new file mode 100644 index 0000000000000000000000000000000000000000..80b629d20bafa10f568235bc76758f14bcf52130 Binary files /dev/null and b/utests/models/gridsample_nearest_conv.onnx differ