diff --git a/cmake/onnxruntime_unittests.cmake b/cmake/onnxruntime_unittests.cmake index 4fdb54c32d..2b7b814d7b 100644 --- a/cmake/onnxruntime_unittests.cmake +++ b/cmake/onnxruntime_unittests.cmake @@ -873,6 +873,7 @@ if (NOT onnxruntime_ENABLE_TRAINING_TORCH_INTEROP) ${BENCHMARK_DIR}/main.cc ${BENCHMARK_DIR}/modeltest.cc ${BENCHMARK_DIR}/pooling.cc + ${BENCHMARK_DIR}/resize.cc ${BENCHMARK_DIR}/batchnorm.cc ${BENCHMARK_DIR}/batchnorm2.cc ${BENCHMARK_DIR}/tptest.cc diff --git a/onnxruntime/core/optimizer/transpose_optimizer/transpose_optimizer.cc b/onnxruntime/core/optimizer/transpose_optimizer/transpose_optimizer.cc index bd7ad7efcc..47d5f9ee4a 100644 --- a/onnxruntime/core/optimizer/transpose_optimizer/transpose_optimizer.cc +++ b/onnxruntime/core/optimizer/transpose_optimizer/transpose_optimizer.cc @@ -967,41 +967,35 @@ static void PermuteInput(api::GraphRef& graph, api::NodeRef& node, size_t i, con node.SetInput(i, gather_output); } -// static bool HandleResize(HandlerArgs& args) { -// auto inputs = args.node.Inputs(); -// int64_t rank_int = gsl::narrow_cast(args.perm.size()); -// -// auto p = ChannelFirstToLastPerm(rank_int); -// auto& perm = p == args.perm ? args.perm : args.perm_inv; -// auto& perm_inv = p == args.perm ? args.perm_inv : args.perm; -// -// if (args.ctx.opset < 11) { -// PermuteInput(args.ctx.graph, args.node, 1, perm); -// } else { -// if (inputs[1] != "") { -// std::vector double_perm_inv = perm; -// double_perm_inv.reserve(2 * args.perm.size()); -// for (int64_t p1 : perm) { -// double_perm_inv.push_back(p1 + rank_int); -// } -// PermuteInput(args.ctx.graph, args.node, 1, double_perm_inv); -// } -// for (size_t i = 2; i < inputs.size(); ++i) { -// if (inputs[i] != "") { -// PermuteInput(args.ctx.graph, args.node, i, perm); -// } -// } -// } -// -// TransposeFirstInput(args.ctx, args.node, perm); -// TransposeOutputs(args.ctx, args.node, perm_inv); -// -// SwapNodeOpTypeAndDomain(args.ctx.graph, args.node, args.node.OpType(), "com.microsoft.nhwc"); -// -// return true; -// } +static bool HandleResize(HandlerArgs& args) { + auto inputs = args.node.Inputs(); + int64_t rank_int = gsl::narrow_cast(args.perm.size()); -// constexpr HandlerInfo resize_handler = {&FirstInput, &HandleResize}; + if (args.ctx.opset < 11) { + PermuteInput(args.ctx.graph, args.node, 1, args.perm_inv); + } else { + if (inputs[1] != "") { + std::vector double_perm_inv = args.perm_inv; + double_perm_inv.reserve(2 * args.perm_inv.size()); + for (int64_t p : args.perm_inv) { + double_perm_inv.push_back(p + rank_int); + } + PermuteInput(args.ctx.graph, args.node, 1, double_perm_inv); + } + for (size_t i = 2; i < inputs.size(); ++i) { + if (inputs[i] != "") { + PermuteInput(args.ctx.graph, args.node, i, args.perm_inv); + } + } + } + + TransposeFirstInput(args.ctx, args.node, args.perm_inv); + TransposeOutputs(args.ctx, args.node, args.perm); + + return true; +} + +constexpr HandlerInfo resize_handler = {&FirstInput, &HandleResize}; static bool HandlePad(HandlerArgs& args) { size_t rank = args.perm.size(); @@ -1697,9 +1691,7 @@ static const std::unordered_map handler_ma {"Split", split_handler}, {"Shape", shape_handler}, {"Pad", pad_handler}, - // Todo: renable resize handler after adding NHWC support in upsample op on cpu - // https://github.com/microsoft/onnxruntime/issues/9857 - // {"Resize", resize_handler}, + {"Resize", resize_handler}, {"ReduceSum", reduce_sum_handler}, {"ReduceLogSum", reduce_op_handler}, diff --git a/onnxruntime/core/providers/cpu/tensor/upsample.cc b/onnxruntime/core/providers/cpu/tensor/upsample.cc index 61e4d28cf0..100bcc4f97 100644 --- a/onnxruntime/core/providers/cpu/tensor/upsample.cc +++ b/onnxruntime/core/providers/cpu/tensor/upsample.cc @@ -397,39 +397,24 @@ static Status UpsampleLinear(const T* input, } */ -struct BilinearParams { - std::vector x_original; - std::vector y_original; - - BufferUniquePtr idx_scale_data_buffer_holder; - - int64_t* input_width_mul_y1; - int64_t* input_width_mul_y2; - - int64_t* in_x1; - int64_t* in_x2; - - float* dx1; - float* dx2; - - float* dy1; - float* dy2; -}; - // The following method supports a 4-D input in 'Linear mode' // that amounts to 'Bilinear' Upsampling/Resizing in the sense that it assumes -// the scale values for the outermost 2 dimensions are 1. +// 1. the scale values for the outermost 2 dimensions are 1 or +// 2. the scale values for the outermost and innermost dimensions are 1 // This is the common use-case where the 4-D input (batched multi-channel images) -// is usually of shape [N, C, H, W] and the scales are [1.0, 1.0, height_scale, width_scale] -static BilinearParams SetupUpsampleBilinear(int64_t input_height, - int64_t input_width, - int64_t output_height, - int64_t output_width, - float height_scale, - float width_scale, - const std::vector& roi, - AllocatorPtr& alloc, - const GetOriginalCoordinateFunc& get_original_coordinate) { +// is usually of shapes: +// - [N, C, H, W] and the scales are [1.0, 1.0, height_scale, width_scale] +// - [N, H, W, C] and the scales are [1.0, height_scale, width_scale, 1.0] +BilinearParams SetupUpsampleBilinear(const int64_t input_height, + const int64_t input_width, + const int64_t output_height, + const int64_t output_width, + const float height_scale, + const float width_scale, + const std::vector& roi, + AllocatorPtr& alloc, + const GetOriginalCoordinateFunc& get_original_coordinate, + bool is_nchw) { BilinearParams p; p.x_original.reserve(output_width); @@ -471,8 +456,9 @@ static BilinearParams SetupUpsampleBilinear(int64_t input_height, p.dx2 = p.dx1 + output_width; // Start processing - auto roi_y_start = roi.size() / 2 - 2; - auto roi_y_end = roi.size() - 2; + const size_t height_rindex = is_nchw ? 1 : 2; + auto roi_y_start = roi.size() / 2 - (height_rindex + 1); + auto roi_y_end = roi.size() - (height_rindex + 1); for (int64_t y = 0; y < output_height; ++y) { float in_y = height_scale == 1 ? static_cast(y) : get_original_coordinate(static_cast(y), height_scale, @@ -496,8 +482,9 @@ static BilinearParams SetupUpsampleBilinear(int64_t input_height, p.input_width_mul_y2[y] = input_width * in_y2; } - auto roi_x_start = roi.size() / 2 - 1; - auto roi_x_end = roi.size() - 1; + const size_t width_rindex = is_nchw ? 0 : 1; + auto roi_x_start = roi.size() / 2 - (width_rindex + 1); + auto roi_x_end = roi.size() - (width_rindex + 1); for (int64_t x = 0; x < output_width; ++x) { float in_x = width_scale == 1 ? static_cast(x) : get_original_coordinate(static_cast(x), @@ -522,59 +509,6 @@ static BilinearParams SetupUpsampleBilinear(int64_t input_height, return p; } -template -void UpsampleBilinear(int64_t batch_size, - int64_t num_channels, - int64_t input_height, - int64_t input_width, - int64_t output_height, - int64_t output_width, - float height_scale, - float width_scale, - const std::vector& roi, - bool use_extrapolation, - float extrapolation_value, - const T* XdataBase, - T* YdataBase, - AllocatorPtr& alloc, - const GetOriginalCoordinateFunc& get_original_coordinate, - concurrency::ThreadPool* tp) { - BilinearParams p = SetupUpsampleBilinear(input_height, input_width, output_height, output_width, - height_scale, width_scale, roi, - alloc, get_original_coordinate); - - for (int64_t n = 0; n < batch_size; ++n) { - concurrency::ThreadPool::TrySimpleParallelFor( - tp, num_channels, - [&](std::ptrdiff_t c) { - const T* Xdata = XdataBase + (n * num_channels + c) * (input_height * input_width); - T* Ydata = YdataBase + (n * num_channels + c) * (output_height * output_width); - for (int64_t y = 0; y < output_height; ++y) { - for (int64_t x = 0; x < output_width; ++x) { - // when use_extrapolation is set and original index of x or y is out of the dim range - // then use extrapolation_value as the output value. - if (use_extrapolation && - ((p.y_original[y] < 0 || p.y_original[y] > static_cast(input_height - 1)) || - (p.x_original[x] < 0 || p.x_original[x] > static_cast(input_width - 1)))) { - Ydata[output_width * y + x] = static_cast(extrapolation_value); - continue; - } - - T X11 = Xdata[p.input_width_mul_y1[y] + p.in_x1[x]]; - T X21 = Xdata[p.input_width_mul_y1[y] + p.in_x2[x]]; - T X12 = Xdata[p.input_width_mul_y2[y] + p.in_x1[x]]; - T X22 = Xdata[p.input_width_mul_y2[y] + p.in_x2[x]]; - - Ydata[output_width * y + x] = static_cast(p.dx2[x] * p.dy2[y] * X11 + - p.dx1[x] * p.dy2[y] * X21 + - p.dx2[x] * p.dy1[y] * X12 + - p.dx1[x] * p.dy1[y] * X22); - } - } - }); - } -} - struct TrilinearParams { std::vector x_original; std::vector y_original; @@ -1065,25 +999,78 @@ Status Upsample::BaseCompute(OpKernelContext* context, case UpsampleMode::LINEAR: { // Supports 'bilinear' and 'trilinear' sampling only - //'bilinear' == 2-D input or 4-D input with outermost 2 scales as 1 + //'bilinear' == 2-D input or 4-D input with outermost 2 scales as 1 or + // 4-D input with outermost and innermost scales as 1 if (dims.size() == 2 || dims.size() == 4) { bool is_2D = dims.size() == 2; + bool is_nchw = true; - const int64_t batch_size = is_2D ? 1 : dims[0]; - const int64_t num_channels = is_2D ? 1 : dims[1]; - const int64_t input_height = is_2D ? dims[0] : dims[2]; - const int64_t input_width = is_2D ? dims[1] : dims[3]; + int64_t batch_size; + int64_t num_channels; + int64_t input_height; + int64_t input_width; - const int64_t output_height = is_2D ? output_dims[0] : output_dims[2]; - const int64_t output_width = is_2D ? output_dims[1] : output_dims[3]; + int64_t output_height; + int64_t output_width; + + float height_scale; + float width_scale; + + if (is_2D) { + batch_size = 1; + num_channels = 1; + input_height = dims[0]; + input_width = dims[1]; + + output_height = output_dims[0]; + output_width = output_dims[1]; + + height_scale = scales[0]; + width_scale = scales[1]; + } else { + if (scales[1] == 1.0f) { + batch_size = dims[0]; + num_channels = dims[1]; + input_height = dims[2]; + input_width = dims[3]; + + output_height = output_dims[2]; + output_width = output_dims[3]; + + height_scale = scales[2]; + width_scale = scales[3]; + } else { + ORT_ENFORCE(scales[3] == 1.0f, "4-D input with innermost scale (usually channel of NHWC) as 1."); + is_nchw = false; + + batch_size = dims[0]; + num_channels = dims[3]; + input_height = dims[1]; + input_width = dims[2]; + + output_height = output_dims[1]; + output_width = output_dims[2]; + + height_scale = scales[1]; + width_scale = scales[2]; + } + } AllocatorPtr alloc; ORT_RETURN_IF_ERROR(context->GetTempSpaceAllocator(&alloc)); - UpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width, - is_2D ? scales[0] : scales[2], is_2D ? scales[1] : scales[3], roi, - use_extrapolation_, extrapolation_value_, X->Data(), - Y->MutableData(), alloc, get_original_coordinate_, - output_height * output_width > 64 ? context->GetOperatorThreadPool() : nullptr); + if (is_nchw) { + UpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width, + height_scale, width_scale, roi, + use_extrapolation_, extrapolation_value_, X->Data(), + Y->MutableData(), alloc, get_original_coordinate_, + output_height * output_width > 64 ? context->GetOperatorThreadPool() : nullptr); + } else { + NhwcUpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width, + height_scale, width_scale, roi, + use_extrapolation_, extrapolation_value_, X->Data(), + Y->MutableData(), alloc, get_original_coordinate_, + output_height * output_width * num_channels > 64 ? context->GetOperatorThreadPool() : nullptr); + } return Status::OK(); } else if (dims.size() == 3 || dims.size() == 5) { //'trilinear' == 3-D input or 5-D input with outermost 2 scales as 1 diff --git a/onnxruntime/core/providers/cpu/tensor/upsample.h b/onnxruntime/core/providers/cpu/tensor/upsample.h index 7ce78a7f3e..3d93219c81 100644 --- a/onnxruntime/core/providers/cpu/tensor/upsample.h +++ b/onnxruntime/core/providers/cpu/tensor/upsample.h @@ -50,6 +50,25 @@ enum ResizeNearestMode { NearestModeCount = 5, }; +struct BilinearParams { + std::vector x_original; + std::vector y_original; + + BufferUniquePtr idx_scale_data_buffer_holder; + + int64_t* input_width_mul_y1; + int64_t* input_width_mul_y2; + + int64_t* in_x1; + int64_t* in_x2; + + float* dx1; + float* dx2; + + float* dy1; + float* dy2; +}; + class UpsampleBase { protected: UpsampleBase(const OpKernelInfo& info) : scales_cached_(false), roi_cached_(false), use_extrapolation_(false) { @@ -378,7 +397,125 @@ class Upsample : public UpsampleBase, public OpKernel { const gsl::span& output_dims) const; }; +BilinearParams SetupUpsampleBilinear(const int64_t input_height, + const int64_t input_width, + const int64_t output_height, + const int64_t output_width, + const float height_scale, + const float width_scale, + const std::vector& roi, + AllocatorPtr& alloc, + const GetOriginalCoordinateFunc& get_original_coordinate, + bool is_nchw); + +template +void UpsampleBilinear(const int64_t batch_size, + const int64_t num_channels, + const int64_t input_height, + const int64_t input_width, + const int64_t output_height, + const int64_t output_width, + const float height_scale, + const float width_scale, + const std::vector& roi, + const bool use_extrapolation, + const float extrapolation_value, + const T* const XdataBase, + T* const YdataBase, + AllocatorPtr& alloc, + const GetOriginalCoordinateFunc& get_original_coordinate, + concurrency::ThreadPool* tp) { + BilinearParams p = SetupUpsampleBilinear(input_height, input_width, output_height, output_width, + height_scale, width_scale, roi, + alloc, get_original_coordinate, true); + for (int64_t n = 0; n < batch_size; ++n) { + concurrency::ThreadPool::TrySimpleParallelFor( + tp, num_channels, + [&](std::ptrdiff_t c) { + const T* Xdata = XdataBase + (n * num_channels + c) * (input_height * input_width); + T* Ydata = YdataBase + (n * num_channels + c) * (output_height * output_width); + for (int64_t y = 0; y < output_height; ++y) { + for (int64_t x = 0; x < output_width; ++x) { + // when use_extrapolation is set and original index of x or y is out of the dim range + // then use extrapolation_value as the output value. + if (use_extrapolation && + ((p.y_original[y] < 0 || p.y_original[y] > static_cast(input_height - 1)) || + (p.x_original[x] < 0 || p.x_original[x] > static_cast(input_width - 1)))) { + Ydata[output_width * y + x] = static_cast(extrapolation_value); + continue; + } + + T X11 = Xdata[p.input_width_mul_y1[y] + p.in_x1[x]]; + T X21 = Xdata[p.input_width_mul_y1[y] + p.in_x2[x]]; + T X12 = Xdata[p.input_width_mul_y2[y] + p.in_x1[x]]; + T X22 = Xdata[p.input_width_mul_y2[y] + p.in_x2[x]]; + + Ydata[output_width * y + x] = static_cast(p.dx2[x] * p.dy2[y] * X11 + + p.dx1[x] * p.dy2[y] * X21 + + p.dx2[x] * p.dy1[y] * X12 + + p.dx1[x] * p.dy1[y] * X22); + } + } + }); + } +} + +template +void NhwcUpsampleBilinear(const int64_t batch_size, + const int64_t num_channels, + const int64_t input_height, + const int64_t input_width, + const int64_t output_height, + const int64_t output_width, + const float height_scale, + const float width_scale, + const std::vector& roi, + const bool use_extrapolation, + const float extrapolation_value, + const T* const XdataBase, + T* const YdataBase, + AllocatorPtr& alloc, + const GetOriginalCoordinateFunc& get_original_coordinate, + concurrency::ThreadPool* tp) { + BilinearParams p = SetupUpsampleBilinear(input_height, input_width, output_height, output_width, + height_scale, width_scale, roi, + alloc, get_original_coordinate, false); + for (int64_t n = 0; n < batch_size; ++n) { + const T* Xdata = XdataBase + n * (input_height * input_width) * num_channels; + T* Ydata = YdataBase + n * (output_height * output_width) * num_channels; + concurrency::ThreadPool::TryParallelFor( + tp, output_height * output_width, + static_cast(num_channels * 2), + [&](std::ptrdiff_t first, std::ptrdiff_t last) { + for (std::ptrdiff_t i = first; i < last; ++i) { + const int64_t x = i % output_width; + const int64_t y = i / output_width; + for (int64_t c = 0; c < num_channels; ++c) { + // when use_extrapolation is set and original index of x or y is out of the dim range + // then use extrapolation_value as the output value. + if (use_extrapolation && + ((p.y_original[y] < 0 || p.y_original[y] > static_cast(input_height - 1)) || + (p.x_original[x] < 0 || p.x_original[x] > static_cast(input_width - 1)))) { + Ydata[(output_width * y + x) * num_channels + c] = static_cast(extrapolation_value); + continue; + } + + T X11 = Xdata[(p.input_width_mul_y1[y] + p.in_x1[x]) * num_channels + c]; + T X21 = Xdata[(p.input_width_mul_y1[y] + p.in_x2[x]) * num_channels + c]; + T X12 = Xdata[(p.input_width_mul_y2[y] + p.in_x1[x]) * num_channels + c]; + T X22 = Xdata[(p.input_width_mul_y2[y] + p.in_x2[x]) * num_channels + c]; + + Ydata[(output_width * y + x) * num_channels + c] = static_cast(p.dx2[x] * p.dy2[y] * X11 + + p.dx1[x] * p.dy2[y] * X21 + + p.dx2[x] * p.dy1[y] * X12 + + p.dx1[x] * p.dy1[y] * X22); + } + } + }); + } +} + } // namespace onnxruntime #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) -#endif \ No newline at end of file +#endif diff --git a/onnxruntime/test/onnx/microbenchmark/resize.cc b/onnxruntime/test/onnx/microbenchmark/resize.cc new file mode 100644 index 0000000000..bee8a9b4a9 --- /dev/null +++ b/onnxruntime/test/onnx/microbenchmark/resize.cc @@ -0,0 +1,83 @@ +#include + +#include "common.h" + +#include +#include "core/common/safeint.h" +#include "core/framework/allocator.h" +#include "core/mlas/lib/mlasi.h" +#include "core/providers/cpu/tensor/upsample.h" +#include "core/util/math_cpuonly.h" +#include "core/util/qmath.h" +#include "core/util/thread_utils.h" + +using namespace onnxruntime; + +template +static void BM_NhwcUpsampleBilinear(benchmark::State& state) { + const int64_t output_height = static_cast(state.range(0)); + const int64_t output_width = static_cast(state.range(1)); + constexpr int64_t batch_size = 1; + constexpr int64_t num_channels = 256; + constexpr int64_t input_height = 32; + constexpr int64_t input_width = 32; + const int64_t height_scale = output_height / input_height; + const int64_t width_scale = output_width / input_width; + const std::vector roi{0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + constexpr bool use_extrapolation = false; + constexpr float extrapolation_value = 0; + constexpr size_t XdataBaseSize = batch_size * num_channels * input_height * input_width; + const T* const XdataBase = GenerateArrayWithRandomValue(XdataBaseSize, std::numeric_limits::min(), std::numeric_limits::max()); + const size_t YdataBaseSize = batch_size * num_channels * output_height * output_width; + T* const YdataBase = (T*)aligned_alloc(sizeof(T) * YdataBaseSize, 64); + AllocatorPtr alloc = std::make_shared(); + const GetOriginalCoordinateFunc& get_original_coordinate = + [](float x_resized, float x_scale, float, float, float, float) { + return x_resized / x_scale; + }; + OrtThreadPoolParams tpo; + tpo.auto_set_affinity = true; + std::unique_ptr tp( + concurrency::CreateThreadPool(&onnxruntime::Env::Default(), tpo, concurrency::ThreadPoolType::INTRA_OP)); + + for (auto _ : state) { + NhwcUpsampleBilinear(batch_size, num_channels, input_height, input_width, output_height, output_width, + static_cast(height_scale), static_cast(width_scale), roi, + use_extrapolation, extrapolation_value, XdataBase, + YdataBase, alloc, get_original_coordinate, + output_height * output_width * num_channels > 64 ? tp.get() : nullptr); + } +} + +BENCHMARK_TEMPLATE(BM_NhwcUpsampleBilinear, uint8_t) + ->MeasureProcessCPUTime() + ->UseRealTime() + ->Unit(benchmark::TimeUnit::kNanosecond) + ->Args({32, 32}) + ->Args({64, 64}) + ->Args({96, 96}) + ->Args({128, 128}) + ->Args({160, 160}) + ->Args({1, 1000000}); + +BENCHMARK_TEMPLATE(BM_NhwcUpsampleBilinear, int8_t) + ->MeasureProcessCPUTime() + ->UseRealTime() + ->Unit(benchmark::TimeUnit::kNanosecond) + ->Args({32, 32}) + ->Args({64, 64}) + ->Args({96, 96}) + ->Args({128, 128}) + ->Args({160, 160}) + ->Args({1, 1000000}); + +BENCHMARK_TEMPLATE(BM_NhwcUpsampleBilinear, float) + ->MeasureProcessCPUTime() + ->UseRealTime() + ->Unit(benchmark::TimeUnit::kNanosecond) + ->Args({32, 32}) + ->Args({64, 64}) + ->Args({96, 96}) + ->Args({128, 128}) + ->Args({160, 160}) + ->Args({1, 1000000}); diff --git a/onnxruntime/test/optimizer/transpose_optimizer_test.cc b/onnxruntime/test/optimizer/transpose_optimizer_test.cc index 198bee9f75..176d154158 100644 --- a/onnxruntime/test/optimizer/transpose_optimizer_test.cc +++ b/onnxruntime/test/optimizer/transpose_optimizer_test.cc @@ -291,212 +291,209 @@ TEST(TransposeOptimizerTests, TestPadNonconst) { /*opset_version*/ 11); } -// Todo: renable tests on resize transformer after adding NHWC support in upsample op on cpu -// https://github.com/microsoft/onnxruntime/issues/9857 +TEST(TransposeOptimizerTests, TestResize) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); + auto* const_1 = builder.MakeInitializer({4}, {0.3f, 2.5f, 1.0f, 0.7f}); + auto* transpose_1_out_0 = builder.MakeIntermediate(); + auto* resize_1_out_0 = builder.MakeIntermediate(); + auto* transpose_2_out_0 = builder.MakeOutput(); -// TEST(TransposeOptimizerTests, TestResize) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); -// auto* const_1 = builder.MakeInitializer({4}, {0.3f, 2.5f, 1.0f, 0.7f}); -// auto* transpose_1_out_0 = builder.MakeIntermediate(); -// auto* resize_1_out_0 = builder.MakeIntermediate(); -// auto* transpose_2_out_0 = builder.MakeOutput(); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// builder.AddNode("Resize", {transpose_1_out_0, const_1}, {resize_1_out_0}); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1, -// /*opset_version*/ 10); -// } -// -// TEST(TransposeOptimizerTests, TestResizeOpset11) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); -// auto* const_1 = builder.MakeInitializer({8}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}); -// auto* const_2 = builder.MakeInitializer({4}, {0.3f, 2.5f, 1.0f, 0.7f}); -// auto* transpose_1_out_0 = builder.MakeIntermediate(); -// auto* resize_1_out_0 = builder.MakeIntermediate(); -// auto* transpose_2_out_0 = builder.MakeOutput(); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// builder.AddNode("Resize", {transpose_1_out_0, const_1, const_2}, {resize_1_out_0}); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1, -// /*opset_version*/ 11); -// } -// -// TEST(TransposeOptimizerTests, TestResizeOpset15) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); -// auto* const_1 = builder.MakeInitializer({4}, {0.3f, 2.5f, 1.0f, 0.7f}); -// auto* transpose_1_out_0 = builder.MakeIntermediate(); -// auto* resize_1_out_0 = builder.MakeIntermediate(); -// auto* transpose_2_out_0 = builder.MakeOutput(); -// auto empty_arg = NodeArg("", nullptr); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// builder.AddNode("Resize", {transpose_1_out_0, &empty_arg, const_1}, {resize_1_out_0}); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1, -// /*opset_version*/ 15); -// } -// -// TEST(TransposeOptimizerTests, TestResizeSizeRoi) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); -// auto* const_1 = builder.MakeInitializer({8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f}); -// auto* const_2 = builder.MakeInitializer({4}, {10, 9, 8, 7}); -// auto* transpose_1_out_0 = builder.MakeIntermediate(); -// auto* resize_1_out_0 = builder.MakeIntermediate(); -// auto* transpose_2_out_0 = builder.MakeOutput(); -// auto empty_arg = NodeArg("", nullptr); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, const_1, &empty_arg, const_2}, {resize_1_out_0}); -// resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1, -// /*opset_version*/ 15); -// } -// -// TEST(TransposeOptimizerTests, TestResizeRoiScalesZeroRank0) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input = builder.MakeInput({1, 512, 512, 3}, -// std::numeric_limits::min(), -// std::numeric_limits::max()); -// auto* resize_in_roi = builder.MakeInitializer({0}, {}); -// auto* resize_in_scales = builder.MakeInitializer({0}, {}); -// auto* resize_in_sizes = builder.MakeInitializer({4}, {1, 256, 32, 32}); -// -// auto* transpose1_out_transposed = builder.MakeIntermediate(); -// auto* resize_out_Y = builder.MakeIntermediate(); -// auto* output = builder.MakeOutput(); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input}, {transpose1_out_transposed}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// builder.AddNode("Resize", -// {transpose1_out_transposed, resize_in_roi, resize_in_scales, resize_in_sizes}, -// {resize_out_Y}); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_out_Y}, {output}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1); -// } -// -// TEST(TransposeOptimizerTests, TestResizeNonconst) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); -// auto* input1_arg = MakeInput(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f}); -// auto* input2_arg = MakeInput(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f}); -// auto* transpose_1_out_0 = builder.MakeIntermediate(); -// auto* resize_1_out_0 = builder.MakeIntermediate(); -// auto* transpose_2_out_0 = builder.MakeOutput(); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0}); -// resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1, -// /*opset_version*/ 11); -// } -// -// TEST(TransposeOptimizerTests, TestResizeNonconstOpset13) { -// auto build_test_case_1 = [&](ModelTestBuilder& builder) { -// auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); -// auto* input1_arg = MakeInput(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f}); -// auto* input2_arg = MakeInput(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f}); -// auto* transpose_1_out_0 = builder.MakeIntermediate(); -// auto* resize_1_out_0 = builder.MakeIntermediate(); -// auto* transpose_2_out_0 = builder.MakeOutput(); -// -// auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); -// transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); -// auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0}); -// resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); -// auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); -// transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); -// }; -// -// auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { -// int transpose_cost = EstimateTransposeCost(session.GetGraph()); -// EXPECT_EQ(transpose_cost, 0); -// }; -// -// TransformerTester(build_test_case_1, -// check_optimized_graph_1, -// TransformerLevel::Default, -// TransformerLevel::Level1, -// /*opset_version*/ 13); -// } + auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + builder.AddNode("Resize", {transpose_1_out_0, const_1}, {resize_1_out_0}); + auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1, + /*opset_version*/ 10); +} + +TEST(TransposeOptimizerTests, TestResizeOpset11) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); + auto* const_1 = builder.MakeInitializer({8}, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}); + auto* const_2 = builder.MakeInitializer({4}, {0.3f, 2.5f, 1.0f, 0.7f}); + auto* transpose_1_out_0 = builder.MakeIntermediate(); + auto* resize_1_out_0 = builder.MakeIntermediate(); + auto* transpose_2_out_0 = builder.MakeOutput(); + + auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + builder.AddNode("Resize", {transpose_1_out_0, const_1, const_2}, {resize_1_out_0}); + auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1, + /*opset_version*/ 11); +} + +TEST(TransposeOptimizerTests, TestResizeOpset15) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); + auto* const_1 = builder.MakeInitializer({4}, {0.3f, 2.5f, 1.0f, 0.7f}); + auto* transpose_1_out_0 = builder.MakeIntermediate(); + auto* resize_1_out_0 = builder.MakeIntermediate(); + auto* transpose_2_out_0 = builder.MakeOutput(); + auto empty_arg = NodeArg("", nullptr); + + auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + builder.AddNode("Resize", {transpose_1_out_0, &empty_arg, const_1}, {resize_1_out_0}); + auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1, + /*opset_version*/ 15); +} + +TEST(TransposeOptimizerTests, TestResizeSizeRoi) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); + auto* const_1 = builder.MakeInitializer({8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f}); + auto* const_2 = builder.MakeInitializer({4}, {10, 9, 8, 7}); + auto* transpose_1_out_0 = builder.MakeIntermediate(); + auto* resize_1_out_0 = builder.MakeIntermediate(); + auto* transpose_2_out_0 = builder.MakeOutput(); + auto empty_arg = NodeArg("", nullptr); + + auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, const_1, &empty_arg, const_2}, {resize_1_out_0}); + resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); + auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1, + /*opset_version*/ 15); +} + +TEST(TransposeOptimizerTests, TestResizeRoiScalesZeroRank0) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input = builder.MakeInput({1, 512, 512, 3}, + std::numeric_limits::min(), + std::numeric_limits::max()); + auto* resize_in_roi = builder.MakeInitializer({0}, {}); + auto* resize_in_scales = builder.MakeInitializer({0}, {}); + auto* resize_in_sizes = builder.MakeInitializer({4}, {1, 256, 32, 32}); + + auto* transpose1_out_transposed = builder.MakeIntermediate(); + auto* resize_out_Y = builder.MakeIntermediate(); + auto* output = builder.MakeOutput(); + + auto& transpose_1 = builder.AddNode("Transpose", {input}, {transpose1_out_transposed}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + builder.AddNode("Resize", + {transpose1_out_transposed, resize_in_roi, resize_in_scales, resize_in_sizes}, + {resize_out_Y}); + auto& transpose_2 = builder.AddNode("Transpose", {resize_out_Y}, {output}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1); +} + +TEST(TransposeOptimizerTests, TestResizeNonconst) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); + auto* input1_arg = MakeInput(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f}); + auto* input2_arg = MakeInput(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f}); + auto* transpose_1_out_0 = builder.MakeIntermediate(); + auto* resize_1_out_0 = builder.MakeIntermediate(); + auto* transpose_2_out_0 = builder.MakeOutput(); + + auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0}); + resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); + auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1, + /*opset_version*/ 11); +} + +TEST(TransposeOptimizerTests, TestResizeNonconstOpset13) { + auto build_test_case_1 = [&](ModelTestBuilder& builder) { + auto* input0_arg = MakeInput(builder, {{4, -1, 2, -1}}, {4, 6, 2, 10}, 0.0, 1.0); + auto* input1_arg = MakeInput(builder, {{8}}, {8}, {0.1f, 0.2f, 0.3f, 0.4f, 0.9f, 0.8f, 0.7f, 0.6f}); + auto* input2_arg = MakeInput(builder, {{4}}, {4}, {0.3f, 2.5f, 1.0f, 0.7f}); + auto* transpose_1_out_0 = builder.MakeIntermediate(); + auto* resize_1_out_0 = builder.MakeIntermediate(); + auto* transpose_2_out_0 = builder.MakeOutput(); + + auto& transpose_1 = builder.AddNode("Transpose", {input0_arg}, {transpose_1_out_0}); + transpose_1.AddAttribute("perm", std::vector{0, 3, 1, 2}); + auto& resize_1 = builder.AddNode("Resize", {transpose_1_out_0, input1_arg, input2_arg}, {resize_1_out_0}); + resize_1.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); + auto& transpose_2 = builder.AddNode("Transpose", {resize_1_out_0}, {transpose_2_out_0}); + transpose_2.AddAttribute("perm", std::vector{0, 2, 3, 1}); + }; + + auto check_optimized_graph_1 = [&](InferenceSessionWrapper& session) { + int transpose_cost = EstimateTransposeCost(session.GetGraph()); + EXPECT_EQ(transpose_cost, 0); + }; + + TransformerTester(build_test_case_1, + check_optimized_graph_1, + TransformerLevel::Default, + TransformerLevel::Level1, + /*opset_version*/ 13); +} TEST(TransposeOptimizerTests, TestAdd) { auto build_test_case_1 = [&](ModelTestBuilder& builder) { @@ -4013,6 +4010,5 @@ TEST(TransposeOptimizerTests, RegressionTest_GitHubIssue10305) { ASSERT_STATUS_OK(session_object.Load(model_uri)); ASSERT_STATUS_OK(session_object.Initialize()); // optimizers run during initialization } - } // namespace test } // namespace onnxruntime diff --git a/onnxruntime/test/providers/cpu/tensor/resize_op_test.cc b/onnxruntime/test/providers/cpu/tensor/resize_op_test.cc index 9bb4e5b4bc..b90d4902fd 100644 --- a/onnxruntime/test/providers/cpu/tensor/resize_op_test.cc +++ b/onnxruntime/test/providers/cpu/tensor/resize_op_test.cc @@ -65,6 +65,38 @@ TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extrapol test.Run(); } +TEST(ResizeOpTest, NhwcResizeOpLinearDownSampleTest_tf_crop_and_resize_with_extrapolation) { + OpTester test("Resize", 13); + std::vector scales{1.0f, 0.8f, 0.8f, 1.0f}; + std::vector roi{0.0f, 0.4f, 0.6f, 0.0f, 1.0f, 1.2f, 1.7f, 1.0f}; + + test.AddAttribute("mode", "linear"); + test.AddAttribute("coordinate_transformation_mode", "tf_crop_and_resize"); + test.AddAttribute("extrapolation_value", 10.0f); + + constexpr int64_t N = 1, H = 4, W = 4, C = 1; + std::vector X = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f}; + + test.AddInput("X", {N, H, W, C}, X); + test.AddInput("roi", {8}, roi); + test.AddInput("scales", {4}, scales); + + std::vector Y = {7.6000004f, 10.0f, 10.0f, + 12.400001f, 10.f, 10.0f, + 10.0f, 10.0f, 10.0f}; + + test.AddOutput("Y", {N, static_cast(H * scales[1]), static_cast(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear) { OpTester test("Resize", 13); std::vector roi{}; @@ -689,10 +721,10 @@ class ResizeOpTester : public OpTester { OpTester::AddNodes(graph, graph_input_defs, graph_output_defs, add_attribute_funcs); // set the Graph inputs to just X and roi (exclude 'scales') so the 'scales' are a constant initializer - if(scales_in_initializer_) { + if (scales_in_initializer_) { graph.SetInputs({graph.GetNodeArg(graph_input_defs[0]->Name()), graph.GetNodeArg(graph_input_defs[1]->Name())}); - if(sizes_in_initializer_) { + if (sizes_in_initializer_) { ASSERT_TRUE(graph_input_defs.size() == 4); } else { ASSERT_TRUE(graph_input_defs.size() == 3); diff --git a/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc b/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc index 89dd4c0de8..1a936bb05d 100644 --- a/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc +++ b/onnxruntime/test/providers/cpu/tensor/upsample_op_test.cc @@ -42,6 +42,59 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest) { test.Run(); } +TEST(UpsampleOpTest, NhwcUpsampleOpNearestTest) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 3.0f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1.0f, 3.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 7.0f, 9.0f}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1.0f, 3.0f, + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 1.0f, 3.0f, + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + 7.0f, 9.0f}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearestTest_int32) { OpTester test("Upsample"); @@ -73,6 +126,59 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest_int32) { test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: nvinfer1::query::Ports&): Assertion `!formats.empty()' failed } +TEST(UpsampleOpTest, NhwcUpsampleOpNearestTest_int32) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 3.0f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1, 3, + 3, 5, + + 3, 5, + 7, 9}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1, 3, + 1, 3, + 1, 3, + 3, 5, + 3, 5, + 3, 5, + + 1, 3, + 1, 3, + 1, 3, + 3, 5, + 3, 5, + 3, 5, + + 3, 5, + 3, 5, + 3, 5, + 7, 9, + 7, 9, + 7, 9, + + 3, 5, + 3, 5, + 3, 5, + 7, 9, + 7, 9, + 7, 9}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearestTest_uint8) { OpTester test("Upsample"); @@ -104,6 +210,59 @@ TEST(UpsampleOpTest, UpsampleOpNearestTest_uint8) { test.Run(); } +TEST(UpsampleOpTest, NhwcUpsampleOpNearestTest_uint8) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 3.0f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1, 3, + 3, 5, + + 3, 5, + 7, 9}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1, 3, + 1, 3, + 1, 3, + 3, 5, + 3, 5, + 3, 5, + + 1, 3, + 1, 3, + 1, 3, + 3, 5, + 3, 5, + 3, 5, + + 3, 5, + 3, 5, + 3, 5, + 7, 9, + 7, 9, + 7, 9, + + 3, 5, + 3, 5, + 3, 5, + 7, 9, + 7, 9, + 7, 9}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearest2XTest) { OpTester test("Upsample"); @@ -135,6 +294,51 @@ TEST(UpsampleOpTest, UpsampleOpNearest2XTest) { test.Run(); } +TEST(UpsampleOpTest, NhwcUpsampleOpNearest2XTest) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 2.0f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1.0f, 3.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 7.0f, 9.0f}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearest222XTest) { OpTester test("Upsample"); @@ -176,6 +380,71 @@ TEST(UpsampleOpTest, UpsampleOpNearest222XTest) { test.Run(); } +TEST(UpsampleOpTest, NhwcUpsampleOpNearest222XTest) { + OpTester test("Upsample"); + + std::vector scales{2.0f, 2.0f, 2.0f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1.0f, 3.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 7.0f, 9.0f}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + 7.0f, 9.0f}; + + test.AddOutput("Y", {(int64_t)(N * scales[0]), (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearest15XTest) { OpTester test("Upsample"); @@ -207,6 +476,47 @@ TEST(UpsampleOpTest, UpsampleOpNearest15XTest) { test.Run(); } +TEST(UpsampleOpTest, NhwcUpsampleOpNearest15XTest) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 1.5f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1.0f, 3.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 7.0f, 9.0f}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + + 1.0f, 3.0f, + 1.0f, 3.0f, + 3.0f, 5.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f, + + 3.0f, 5.0f, + 3.0f, 5.0f, + 7.0f, 9.0f}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearestTest_NoScale) { OpTester test("Upsample"); @@ -264,6 +574,51 @@ TEST(UpsampleOpTest, UpsampleOpNearest2XTest_int32) { test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: nvinfer1::query::Ports&): Assertion `!formats.empty()' failed } +TEST(UpsampleOpTest, NhwcUpsampleOpNearest2XTest_int32) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 2.0f, 1.0f}; + test.AddAttribute("mode", "nearest"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1, 3, + 3, 5, + + 3, 5, + 7, 9}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1, 3, + 1, 3, + 3, 5, + 3, 5, + + 1, 3, + 1, 3, + 3, 5, + 3, 5, + + 3, 5, + 3, 5, + 7, 9, + 7, 9, + + 3, 5, + 3, 5, + 7, 9, + 7, 9}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOp4DBilinearTest) { OpTester test("Upsample"); @@ -292,7 +647,111 @@ TEST(UpsampleOpTest, UpsampleOp4DBilinearTest) { 7.0f, 7.5f, 8.0f, 8.5f, 9.0f, 9.0f, 9.0f, 9.0f}; test.AddOutput("Y", {N, C, (int64_t)(H * scales[2]), (int64_t)(W * scales[3])}, Y); - test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); //TensorRT: results mismatch +} + +TEST(UpsampleOpTest, NhwcUpsampleOp4D1CBilinearTest) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 4.0f, 1.0f}; + test.AddAttribute("mode", "linear"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 2, H = 2, W = 3, C = 1; + std::vector X = {1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, + + 7.0f, 8.0f, 9.0f, + 10.0f, 11.0f, 12.0f}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.25f, 2.5f, 2.75f, 3.0f, 3.0f, 3.0f, 3.0f, + 2.5f, 2.75f, 3.0f, 3.25f, 3.5f, 3.75f, 4.0f, 4.25f, 4.5f, 4.5f, 4.5f, 4.5f, + 4.0f, 4.25f, 4.5f, 4.75f, 5.0f, 5.25f, 5.5f, 5.75f, 6.0f, 6.0f, 6.0f, 6.0f, + 4.0f, 4.25f, 4.5f, 4.75f, 5.0f, 5.25f, 5.5f, 5.75f, 6.0f, 6.0f, 6.0f, 6.0f, + + 7.0f, 7.25f, 7.5f, 7.75f, 8.0f, 8.25f, 8.5f, 8.75f, 9.0f, 9.0f, 9.0f, 9.0f, + 8.5f, 8.75f, 9.0f, 9.25f, 9.5f, 9.75f, 10.0f, 10.25f, 10.5f, 10.5f, 10.5f, 10.5f, + 10.0f, 10.25f, 10.5f, 10.75f, 11.0f, 11.25f, 11.5f, 11.75f, 12.0f, 12.0f, 12.0f, 12.0f, + 10.0f, 10.25f, 10.5f, 10.75f, 11.0f, 11.25f, 11.5f, 11.75f, 12.0f, 12.0f, 12.0f, 12.0f}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + +TEST(UpsampleOpTest, NhwcUpsampleOp4DBilinearTest) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 2.0f, 1.0f}; + test.AddAttribute("mode", "linear"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 2, H = 2, W = 2, C = 3; + std::vector X = {1.0f, 2.0f, 3.0f, + 4.0f, 5.0f, 6.0f, + 7.0f, 8.0f, 9.0f, + 10.0f, 11.0f, 12.0f, + + 13.0f, 14.0f, 15.0f, + 16.0f, 17.0f, 18.0f, + 19.0f, 20.0f, 21.0f, + 22.0f, 23.0f, 24.0f}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1.0f, 2.0f, 3.0f, + 2.5f, 3.5f, 4.5f, + 4.0f, 5.0f, 6.0f, + 4.0f, 5.0f, 6.0f, + + 4.0f, 5.0f, 6.0f, + 5.5f, 6.5f, 7.5f, + 7.0f, 8.0f, 9.0f, + 7.0f, 8.0f, 9.0f, + + 7.0f, 8.0f, 9.0f, + 8.5f, 9.5f, 10.5f, + 10.0f, 11.0f, 12.0f, + 10.0f, 11.0f, 12.0f, + + 7.0f, 8.0f, 9.0f, + 8.5f, 9.5f, 10.5f, + 10.0f, 11.0f, 12.0f, + 10.0f, 11.0f, 12.0f, + + 13.0f, 14.0f, 15.0f, + 14.5f, 15.5f, 16.5f, + 16.0f, 17.0f, 18.0f, + 16.0f, 17.0f, 18.0f, + + 16.0f, 17.0f, 18.0f, + 17.5f, 18.5f, 19.5f, + 19.0f, 20.0f, 21.0f, + 19.0f, 20.0f, 21.0f, + + 19.0f, 20.0f, 21.0f, + 20.5f, 21.5f, 22.5f, + 22.0f, 23.0f, 24.0f, + 22.0f, 23.0f, 24.0f, + + 19.0f, 20.0f, 21.0f, + 20.5f, 21.5f, 22.5f, + 22.0f, 23.0f, 24.0f, + 22.0f, 23.0f, 24.0f}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); } TEST(UpsampleOpTest, UpsampleOp2DBilinearTest) { @@ -375,6 +834,41 @@ TEST(UpsampleOpTest, UpsampleOp4DBilinearTest_int32) { test.Run(); } +TEST(UpsampleOpTest, NhwcUpsampleOp4DBilinearTest_int32) { + OpTester test("Upsample"); + + std::vector scales{1.0f, 2.0f, 4.0f, 1.0f}; + test.AddAttribute("mode", "linear"); + test.AddAttribute("scales", scales); + + constexpr int64_t N = 2, H = 2, W = 2, C = 1; + std::vector X = {1, 3, + 3, 5, + + 3, 5, + 7, 9}; + + test.AddInput("X", {N, H, W, C}, X); + + std::vector Y = { + 1, 1, 2, 2, 3, 3, 3, 3, + 2, 2, 3, 3, 4, 4, 4, 4, + 3, 3, 4, 4, 5, 5, 5, 5, + 3, 3, 4, 4, 5, 5, 5, 5, + + 3, 3, 4, 4, 5, 5, 5, 5, + 5, 5, 6, 6, 7, 7, 7, 7, + 7, 7, 8, 8, 9, 9, 9, 9, + 7, 7, 8, 8, 9, 9, 9, 9}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} + TEST(UpsampleOpTest, UpsampleOpNearestTest_1D) { OpTester test("Upsample"); @@ -427,5 +921,50 @@ TEST(UpsampleOpTest, UpsampleOpNearest2XTest_opset9) { test.AddOutput("Y", {N, C, (int64_t)(H * scales[2]), (int64_t)(W * scales[3])}, Y); test.Run(); } + +TEST(UpsampleOpTest, NhwcUpsampleOpNearest2XTest_opset9) { + OpTester test("Upsample", 9); + + std::vector scales{1.0f, 2.0f, 2.0f, 1.0}; + test.AddAttribute("mode", "nearest"); + + constexpr int64_t N = 1, H = 2, W = 2, C = 2; + std::vector X = {1, 3, + 3, 5, + + 3, 5, + 7, 9}; + + test.AddInput("X", {N, H, W, C}, X); + test.AddInput("scales", {4}, scales); + + std::vector Y = { + 1, 3, + 1, 3, + 3, 5, + 3, 5, + + 1, 3, + 1, 3, + 3, 5, + 3, 5, + + 3, 5, + 3, 5, + 7, 9, + 7, 9, + + 3, 5, + 3, 5, + 7, 9, + 7, 9}; + + test.AddOutput("Y", {N, (int64_t)(H * scales[1]), (int64_t)(W * scales[2]), C}, Y); + //CUDA: result mismatch due to not implementing NHWC support + //TensorRT: results mismatch + //ROCm: results mismatch + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kTensorrtExecutionProvider, kRocmExecutionProvider}); +} } // namespace test } // namespace onnxruntime