[DML] Resize 18 & 19 (#19071)

### Description
<!-- Describe your changes. -->
Register resize-18 and -19, which will be lit up automatically when dml
feature level bumps up to 6300.

It's worth noting that DML has a different implementation for antialias
than does ORT CPU. DML does iterative downsampling whenever the scale
factor is less than 0.5. This is equivalent to performing resize with a
variable-sized input window (also equivalent to mip mapping). ORT takes
a different approach, using the same convolution approach as PIL. The
two implementations approach each other in certain cases (with
iota-generated data) but they usually aren't perfectly equivalent.

### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->

---------

Co-authored-by: Linnea May <linneamay@microsoft.com>
This commit is contained in:
Linnea May 2024-02-01 10:26:37 -08:00 committed by GitHub
parent 1d6f13fb92
commit eb0ce86db8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 242 additions and 62 deletions

View file

@ -9,11 +9,12 @@ namespace Dml
constexpr NameAndIndex coordinateTransformationModes[] =
{
{"half_pixel", 0},
{"pytorch_half_pixel", 1},
{"align_corners", 2},
{"asymmetric", 3},
{"tf_half_pixel_for_nn", 4},
{"tf_crop_and_resize", 5},
{"half_pixel_symmetric", 1},
{"pytorch_half_pixel", 2},
{"align_corners", 3},
{"asymmetric", 4},
{"tf_half_pixel_for_nn", 5},
{"tf_crop_and_resize", 6},
};
constexpr NameAndIndex nearestNeighborRoundingModes[] =
@ -50,7 +51,7 @@ void ComputePixelOffsetsAndScales(
uint32_t coordinateTransformationModeValue = *optionalCoordinateTransformationModeValue;
ML_CHECK_VALID_ARGUMENT(
!regionOfInterest.empty() || coordinateTransformationModeValue != 5 /*tf_crop_and_resize*/,
!regionOfInterest.empty() || coordinateTransformationModeValue != 6 /*tf_crop_and_resize*/,
"Resize expects 'roi' tensor for 'tf_crop_and_resize' mode."
);
@ -88,6 +89,18 @@ void ComputePixelOffsetsAndScales(
break;
case 1:
// coordinate_transformation_mode is "half_pixel_symmetric",
// adjustment = output_width_int / output_width
// center = input_width / 2
// offset = center * (1 - adjustment)
// x_original = (x + 0.5) / scale - (0.5 - offset)
// x_original = (x + 0.5) / scale - (0.5 - [(input_width / 2) * (1 - (output_width_int / output_width))])
// output_width can be fractional when calculated with scale factor
inputPixelOffset = 0.5f - float((inputDimensions[i] / 2.0f) * (1.0f - outputDimensions[i] / (scales[i] * inputDimensions[i])));
outputPixelOffset = -0.5;
break;
case 2:
// if coordinate_transformation_mode is "pytorch_half_pixel",
// x_original = length_resized > 1 ? (x_resized + 0.5) / scale - 0.5 : 0
if (inputDimensions[i] <= 1)
@ -104,7 +117,7 @@ void ComputePixelOffsetsAndScales(
}
break;
case 2:
case 3:
// if coordinate_transformation_mode is "align_corners",
// x_original = x_resized * (length_original - 1) / (length_resized - 1)
inputPixelOffset = 0.0;
@ -121,7 +134,7 @@ void ComputePixelOffsetsAndScales(
}
break;
case 3:
case 4:
// if coordinate_transformation_mode is "asymmetric",
// x_original = x_resized / scale
inputPixelOffset = 0.0;
@ -129,7 +142,7 @@ void ComputePixelOffsetsAndScales(
// Keep existing scales.
break;
case 4:
case 5:
// if coordinate_transformation_mode is "tf_half_pixel_for_nn",
// x_original = (x_resized + 0.5) / scale
inputPixelOffset = 0.0;
@ -137,7 +150,7 @@ void ComputePixelOffsetsAndScales(
// Keep existing scales.
break;
case 5:
case 6:
// if coordinate_transformation_mode is "tf_crop_and_resize",
// x_original = length_resized > 1 ? start_x * (length_original - 1) + x_resized * (end_x - start_x) * (length_original - 1) / (length_resized - 1)
// : 0.5 * (start_x + end_x) * (length_original - 1)
@ -177,7 +190,7 @@ class DmlOperatorResize : public DmlOperator, public ResizeHelper
public:
// Resample a multidimensional image to a new size.
DmlOperatorResize(const MLOperatorKernelCreationContext& kernelCreationContext, uint32_t opsetVersion)
: DmlOperator(kernelCreationContext),
: DmlOperator(kernelCreationContext),
ResizeHelper(kernelCreationContext, kernelCreationContext.GetTensorShapeDescription(), opsetVersion)
{
ML_CHECK_VALID_ARGUMENT(!m_scales.empty(), "Resize/Upsample expect scales, either a 2nd input tensors or 'scales' attribute.");
@ -250,6 +263,11 @@ public:
std::string mode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::Mode, "NEAREST");
DML_INTERPOLATION_MODE interpolationMode = Dml::MapStringToInteropolationMode(mode);
#if DML_TARGET_VERSION >= 0x6300
const int antialiased = kernelCreationContext.GetOptionalAttribute<int>(AttrName::Antialiased, 0);
#endif
// Map ONNX to DML's mode using offsets and rounding direction.
// These offsets are in addition to the coordinate transform offsets.
DML_AXIS_DIRECTION roundingDirection = DML_AXIS_DIRECTION_DECREASING;
@ -289,7 +307,12 @@ public:
std::vector<DML_TENSOR_DESC> inputDescs = GetDmlInputDescs();
std::vector<DML_TENSOR_DESC> outputDescs = GetDmlOutputDescs();
#if DML_TARGET_VERSION >= 0x6300
DML_RESAMPLE3_OPERATOR_DESC operatorDesc = {};
operatorDesc.Antialiased = static_cast<BOOL>(antialiased);
#else
DML_RESAMPLE2_OPERATOR_DESC operatorDesc = {};
#endif
operatorDesc.InputTensor = inputDescs.data();
operatorDesc.OutputTensor = outputDescs.data();
operatorDesc.InterpolationMode = interpolationMode;
@ -298,8 +321,11 @@ public:
operatorDesc.DimensionCount = gsl::narrow_cast<uint32_t>(paddedScales.size());
operatorDesc.InputPixelOffsets = inputPixelOffsets.data();
operatorDesc.OutputPixelOffsets = outputPixelOffsets.data();
#if DML_TARGET_VERSION >= 0x6300
DML_OPERATOR_DESC opDesc = { DML_OPERATOR_RESAMPLE3, &operatorDesc };
#else
DML_OPERATOR_DESC opDesc = { DML_OPERATOR_RESAMPLE2, &operatorDesc };
#endif
SetDmlOperatorDesc(opDesc, kernelCreationContext);
}
};
@ -342,6 +368,10 @@ void CALLBACK QueryResize(IMLOperatorSupportQueryContextPrivate* context, bool*
DML_OP_DEFINE_CREATION_FUNCTION(Resize10, VersionedKernel<DmlOperatorResize, 10>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize11, VersionedKernel<DmlOperatorResize, 11>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize13, VersionedKernel<DmlOperatorResize, 13>);
#if DML_TARGET_VERSION >= 0x6300
DML_OP_DEFINE_CREATION_FUNCTION(Resize18, VersionedKernel<DmlOperatorResize, 18>);
DML_OP_DEFINE_CREATION_FUNCTION(Resize19, VersionedKernel<DmlOperatorResize, 19>);
#endif
DML_OP_DEFINE_CREATION_FUNCTION(Upsample7, VersionedKernel<DmlOperatorResize, 7>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample9, VersionedKernel<DmlOperatorResize, 9>);
DML_OP_DEFINE_CREATION_FUNCTION(Upsample10, VersionedKernel<DmlOperatorResize, 10>);

View file

@ -508,6 +508,8 @@ DML_OP_EXTERN_CREATION_FUNCTION(Trilu);
#if DML_TARGET_VERSION >= 0x6300
DML_OP_EXTERN_CREATION_FUNCTION(Col2Im);
DML_OP_EXTERN_CREATION_FUNCTION(Resize18);
DML_OP_EXTERN_CREATION_FUNCTION(Resize19);
#endif
DML_OP_EXTERN_CREATION_FUNCTION(Shape);
@ -600,6 +602,7 @@ constexpr static std::array<SupportedTensorDataTypes, 1> supportedTypeListSigned
constexpr static std::array<SupportedTensorDataTypes, 1> supportedTypeListRange = {SupportedTensorDataTypes::Int16|SupportedTensorDataTypes::Int32|SupportedTensorDataTypes::Int64|SupportedTensorDataTypes::Float32};
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListResize11 = {SupportedTensorDataTypes::Float16to32 | SupportedTensorDataTypes::Int8 | SupportedTensorDataTypes::UInt8, SupportedTensorDataTypes::Float16to32 /* ROI read by CPU */};
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListResize13 = supportedTypeListResize11;
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListResize18 = supportedTypeListResize11;
constexpr static std::array<SupportedTensorDataTypes, 3> supportedTypeListInteger = {SupportedTensorDataTypes::Int8|SupportedTensorDataTypes::UInt8, SupportedTensorDataTypes::Int8|SupportedTensorDataTypes::UInt8, SupportedTensorDataTypes::Int32 };
constexpr static std::array<SupportedTensorDataTypes, 1> supportedTypeListInteger8 = {SupportedTensorDataTypes::Int8|SupportedTensorDataTypes::UInt8 };
constexpr static std::array<SupportedTensorDataTypes, 2> supportedTypeListRoiAlign = {SupportedTensorDataTypes::Float16to32, SupportedTensorDataTypes::Int32|SupportedTensorDataTypes::Int64 };
@ -973,7 +976,10 @@ constexpr static OperatorRegistrationInformation operatorRegistrationInformation
{REG_INFO_VER( 10, Resize, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported, requiredConstantCpuInputs(1) /*scales*/)},
{REG_INFO_VER( 11, Resize, typeNameListTwo, supportedTypeListResize11, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*roi, scales, sizes*/, std::nullopt, QueryResize)},
{REG_INFO_VER( 13, Resize, typeNameListTwo, supportedTypeListResize13, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*roi, scales, sizes*/, std::nullopt, QueryResize)},
#if DML_TARGET_VERSION >= 0x6300
{REG_INFO_VER( 18, Resize, typeNameListTwo, supportedTypeListResize18, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*roi, scales, sizes*/, std::nullopt, QueryResize)},
{REG_INFO_VER( 19, Resize, typeNameListTwo, supportedTypeListResize18, DmlGraphSupport::Supported, requiredConstantCpuInputs(1, 2, 3) /*roi, scales, sizes*/, std::nullopt, QueryResize)},
#endif
// Activation Functions
{REG_INFO( 7, Sigmoid, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 13, Sigmoid, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},

View file

@ -12,6 +12,7 @@ namespace AttrName
static constexpr const char* AllowZero = "allowzero";
static constexpr const char* Alpha = "alpha";
static constexpr const char* AlignCorners = "align_corners";
static constexpr const char* Antialiased = "antialias";
static constexpr const char* AutoPad = "auto_pad";
static constexpr const char* Axes = "axes";
static constexpr const char* Axis = "axis";

View file

@ -56,6 +56,18 @@ namespace OperatorHelper
}
}
template <typename T>
void ExpandToAxes(/*inout*/ std::vector<T>& originalValues, gsl::span<const int32_t> axes, std::vector<T> expanded)
{
assert(originalValues.size() == axes.size());
// Fill in roi and scales/sizes
for (size_t i = 0; i < axes.size(); i++)
{
expanded[axes[i]] = originalValues[i];
}
originalValues = std::move(expanded);
}
float CastFloat16ToFloat32(uint16_t input)
{
// Promote float16m10e5s1 to float32m23e8s1.
@ -144,50 +156,6 @@ namespace OperatorHelper
}
#pragma warning(pop)
void ReadCpuLocalTensorIntoInt32(
const MLOperatorTensor& tensor,
std::vector<int32_t>& result
)
{
result.clear();
ML_CHECK_VALID_ARGUMENT(tensor.IsCpuData(), "Tensor must be CPU Tensor.");
const std::vector<uint32_t>& tensorDimensions = tensor.GetShape();
const uint32_t elementCount = ComputeElementCountFromDimensions(tensorDimensions);
switch (tensor.GetTensorDataType())
{
case MLOperatorTensorDataType::Int32:
{
const int32_t* data = tensor.GetData<int32_t>();
result.assign(data, data + elementCount);
}
break;
case MLOperatorTensorDataType::Int64:
{
const int64_t* data = tensor.GetData<int64_t>();
result.reserve(elementCount);
// Use clamped cast rather than static_cast/narrow_cast,
// because it's not uncommon for a model to specify a
// 64-bit INTMAX constant as a sentinel value to mean
// the largest possible value (even though the actual
// dimension values come nowhere close to that, far
// less than 32-bit INTMAX).
for (auto d : gsl::make_span(data, data + elementCount))
{
result.push_back(clamp_cast<int32_t>(d));
}
}
break;
default:
ML_INVALID_ARGUMENT("Expecting CPU local tensor of type int32 or int64.");
break;
}
}
void ReadCpuLocalTensorIntoFloat32(
const MLOperatorTensor& tensor,
std::vector<float>& result
@ -2461,7 +2429,8 @@ namespace OperatorHelper
{
auto& attributes = kernelInformation.GetAttributes();
m_inputDimensions = shapeInformation.GetInputTensorShape(0);
std::vector<int32_t> outputSizes;
std::vector<uint32_t> outputSizes;
std::vector<int32_t> axes;
if (opsetVersion >= 11)
{
@ -2478,7 +2447,38 @@ namespace OperatorHelper
if (kernelInformation.IsInputValid(3))
{
MLOperatorTensor outputSizesTensor = kernelInformation.GetConstantInputTensor(3);
ReadCpuLocalTensorIntoInt32(outputSizesTensor, /*out*/ outputSizes);
ReadCpuLocalTensorIntoInt32<uint32_t>(outputSizesTensor, /*out*/ outputSizes);
}
axes = kernelInformation.GetAttributes().GetOptionalAttributeVectorInt32(AttrName::Axes);
// Handle possible axes input
if (opsetVersion >= 18 && !axes.empty())
{
uint32_t dimCount = gsl::narrow_cast<uint32_t>(m_inputDimensions.size());
HandleEmptyAxes(/*inout*/ axes, m_inputDimensions, false);
HandleNegativeAxes(/*inout*/ axes, dimCount);
// Taken from https://github.com/onnx/onnx/blob/3d69db8fd16873d68e7033479467f9478562a12d/onnx/reference/ops/op_resize.py#L303
if (!m_scales.empty())
{
std::vector<float> defaultScales(dimCount, 1.0f);
ExpandToAxes(/*inout*/ m_scales, axes, defaultScales);
}
if (!outputSizes.empty())
{
ExpandToAxes(/*inout*/ outputSizes, axes, m_inputDimensions);
}
if (!m_regionOfInterest.empty())
{
std::vector<float> defaultRois(dimCount, 0.0f);
defaultRois.resize(dimCount * 2, 1.0f);
size_t numAxes = axes.size();
for (size_t i = 0; i < axes.size(); i++)
{
defaultRois[axes[i]] = m_regionOfInterest[i];
defaultRois[axes[i + dimCount]] = m_regionOfInterest[i + numAxes];
}
}
}
}
else if (opsetVersion >= 9)

View file

@ -120,10 +120,54 @@ double CastToFloat64(MLOperatorTensorDataType tensorDataType, const void* p);
void ReadScalarTensorData(const MLOperatorTensor& tensor, /*out*/ void* data, size_t dataByteSize);
int64_t ReadScalarTensorCastToInt64(const MLOperatorTensor& tensor);
double ReadScalarTensorCastToFloat64(const MLOperatorTensor& tensor);
void ReadCpuLocalTensorIntoInt32(const MLOperatorTensor& tensor, std::vector<int32_t>& result);
void ReadCpuLocalTensorIntoFloat32(const MLOperatorTensor& tensor, std::vector<float>& result);
template<typename T = int32_t>
void ReadCpuLocalTensorIntoInt32(
const MLOperatorTensor& tensor,
std::vector<T>& result
)
{
result.clear();
ML_CHECK_VALID_ARGUMENT(tensor.IsCpuData(), "Tensor must be CPU Tensor.");
const std::vector<uint32_t>& tensorDimensions = tensor.GetShape();
const uint32_t elementCount = ComputeElementCountFromDimensions(tensorDimensions);
switch (tensor.GetTensorDataType())
{
case MLOperatorTensorDataType::Int32:
{
result.resize(elementCount);
const int32_t* data = tensor.GetData<int32_t>();
std::transform(data, data + elementCount, result.begin(), [](auto v) {return static_cast<T>(v); });
}
break;
case MLOperatorTensorDataType::Int64:
{
const int64_t* data = tensor.GetData<int64_t>();
result.reserve(elementCount);
// Use clamped cast rather than static_cast/narrow_cast,
// because it's not uncommon for a model to specify a
// 64-bit INTMAX constant as a sentinel value to mean
// the largest possible value (even though the actual
// dimension values come nowhere close to that, far
// less than 32-bit INTMAX).
for (auto d : gsl::make_span(data, data + elementCount))
{
result.push_back(clamp_cast<T>(d));
}
}
break;
default:
ML_INVALID_ARGUMENT("Expecting CPU local tensor of type int32 or int64.");
break;
}
}
class EdgeShapes
{
public:
@ -1613,6 +1657,8 @@ using ShapeInferenceHelper_Tile = TileHelper;
using ShapeInferenceHelper_Resize10 = VersionedOpsetHelper<ResizeHelper, 10>;
using ShapeInferenceHelper_Resize11 = VersionedOpsetHelper<ResizeHelper, 11>;
using ShapeInferenceHelper_Resize13 = VersionedOpsetHelper<ResizeHelper, 13>;
using ShapeInferenceHelper_Resize18 = VersionedOpsetHelper<ResizeHelper, 18>;
using ShapeInferenceHelper_Resize19 = VersionedOpsetHelper<ResizeHelper, 19>;
using ShapeInferenceHelper_OneHot = OneHotHelper;
using ShapeInferenceHelper_Sqrt = GetOutputShapeAsInputShapeHelper;

View file

@ -408,11 +408,13 @@ namespace OperatorHelper
static const int sc_sinceVer_Split = 18;
static const int sc_sinceVer_LpPool = 18;
static const int sc_sinceVer_Col2Im = 18;
static const int sc_sinceVer_Resize = 18;
}
namespace OnnxOperatorSet19
{
static const int sc_sinceVer_AveragePool = 19;
static const int sc_sinceVer_Resize = 19;
static const int sc_sinceVer_Pad = 19;
static const int sc_sinceVer_Cast = 19;
static const int sc_sinceVer_CastLike = 19;

View file

@ -1870,6 +1870,8 @@ void TestAntialiasing(std::map<std::string, std::string> attributes,
test.AddAttribute<float>("extrapolation_value", std::stof(v));
} else if (k == "roi") {
roi = parse_attr(v, 0.0f);
} else if (k == "antialias") {
test.AddAttribute<int64_t>("antialias", std::stoll(v));
} else {
throw std::invalid_argument("Unknown attribute");
}
@ -1894,6 +1896,9 @@ void TestAntialiasing(std::map<std::string, std::string> attributes,
}
TEST(ResizeOpTest, Antialias_Bilinear_No_ExcludeOutside) {
if (DefaultDmlExecutionProvider().get() != nullptr) {
GTEST_SKIP() << "Skipping because dml implementation of antialias is slightly different and doesn't match in all cases.";
}
std::vector<float> X(16);
std::iota(X.begin(), X.end(), 1.f);
@ -1912,7 +1917,6 @@ TEST(ResizeOpTest, Antialias_Bilinear_ExcludeOutside) {
12.1f, 13.3f, 14.5f};
TestAntialiasing({{"mode", "linear"}, {"exclude_outside", "1"}}, {1, 1, 4, 4}, X, {1, 1, 3, 3}, Y);
}
TEST(ResizeOpTest, Antialias_Bilinear_Scale_Is_All_1) {
std::vector<float> X(3 * 4 * 5 * 6);
std::iota(X.begin(), X.end(), 1.f);
@ -2009,6 +2013,9 @@ TEST(ResizeOpTest, Antialias_NhwcBilinear_dtype) {
}
TEST(ResizeOpTest, Antialias_Trilinear_No_ExcludeOutside) {
if (DefaultDmlExecutionProvider().get() != nullptr) {
GTEST_SKIP() << "Skipping because dml implementation of antialias is slightly different and doesn't match in all cases.";
}
std::vector<float> X(16 * 4);
std::iota(X.begin(), X.end(), 0.f);
std::vector<float> Y = {5.7272725f, 6.9545455f, 8.181818f, 10.636364f, 11.863636f,
@ -2030,6 +2037,9 @@ TEST(ResizeOpTest, Antialias_Trilinear_ExcludeOutside) {
}
TEST(ResizeOpTest, Antialias_Trilinear_Scale_Is_11s_and_1s1) {
if (DefaultDmlExecutionProvider().get() != nullptr) {
GTEST_SKIP() << "Skipping because dml implementation of antialias is slightly different and doesn't match in all cases.";
}
std::vector<float> X(16 * 4 * 4);
std::iota(X.begin(), X.end(), 0.f);
{
@ -2118,6 +2128,9 @@ TEST(ResizeOpTest, Antialias_NHWCBicubic_ExcludeOutside) {
}
TEST(ResizeOpTest, Antialias_Linear_AlignCorners) {
if (DefaultDmlExecutionProvider().get() != nullptr) {
GTEST_SKIP() << "Skipping because dml implementation of antialias is slightly different and doesn't match in all cases.";
}
std::vector<float> X(256);
std::iota(X.begin(), X.end(), 0.0f);
@ -2231,5 +2244,87 @@ TEST(ResizeOpTest, Antialias_Use_Extrapolation) {
},
{4, 4, 4}, X, {3, 3, 3}, Y);
}
TEST(ResizeOpTest, Antialias_Large_half_pixel) {
std::vector<float> X{0.f, 1.f, 2.f, 3.f, 4.f, 5.f};
std::vector<float> Y = {1.f, 4.f};
std::vector<float> roi{};
std::vector<float> scales{};
std::vector<int64_t> output_shape{1, 1, 2, 1};
OpTester test("Resize", 18);
test.AddAttribute<int64_t>("exclude_outside", 0LL);
test.AddAttribute<int64_t>("antialias", 1LL);
test.AddAttribute("mode", "linear");
test.AddInput<float>("X", {1, 1, 6, 1}, X);
test.AddInput<float>("roi", {int64_t(roi.size())}, roi);
test.AddInput<float>("", {0}, scales);
test.AddInput<int64_t>("sizes", {4}, output_shape);
// Have absolute tolerance because ort is slightly different results.
// DML implementation is equivalent to resize with variable input window size while ORT using a convolution approach.
// Absolute error is for ORT CPU.
test.AddOutput<float>("Y", output_shape, Y, false, /*rel_error*/ 0.0f, /*abs_error*/ 0.12f);
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider});
}
// Test without anti-aliasing for better comparison with DirectML
TEST(ResizeOpTest, Axes_and_Scale_18) {
std::vector<float> X(16 * 4);
std::iota(X.begin(), X.end(), 0.f);
std::vector<float> Y = {3.5f, 4.8333335f, 6.1666665f, 8.833333f, 10.166667f, 11.5f, 14.166667f,
15.5f, 16.833334f, 24.833334f, 26.166666f, 27.5f, 30.166666f, 31.5f,
32.833332f, 35.5f, 36.833332f, 38.166668f, 46.166668f, 47.5f, 48.833332f,
51.5f, 52.833332f, 54.166668f, 56.833332f, 58.166668f, 59.5};
std::vector<float> roi{};
std::vector<float> scales{3 / 4.0f, 3 / 4.0f, 3 / 4.0f};
std::vector<int64_t> output_shape{1, 1, 3, 3, 3};
std::vector<int64_t> axes{2, 3, 4};
OpTester test("Resize", 18);
test.AddAttribute<int64_t>("exclude_outside", 0LL);
test.AddAttribute<std::vector<int64_t>>("axes", axes);
test.AddAttribute<int64_t>("antialias", 0LL);
test.AddAttribute("mode", "linear");
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
test.AddInput<float>("roi", {int64_t(roi.size())}, roi);
test.AddInput<float>("scales", {int64_t(scales.size())}, scales, true);
test.AddOutput<float>("Y", output_shape, Y);
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider});
}
TEST(ResizeOpTest, Axes_and_Size_18) {
std::vector<float> X(16 * 4);
std::iota(X.begin(), X.end(), 0.f);
std::vector<float> Y = {3.5f, 4.8333335f, 6.1666665f, 8.833333f, 10.166667f, 11.5f, 14.166667f,
15.5f, 16.833334f, 24.833334f, 26.166666f, 27.5f, 30.166666f, 31.5f,
32.833332f, 35.5f, 36.833332f, 38.166668f, 46.166668f, 47.5f, 48.833332f,
51.5f, 52.833332f, 54.166668f, 56.833332f, 58.166668f, 59.5};
std::vector<float> roi{};
std::vector<float> scales{};
std::vector<int64_t> output_shape{1, 1, 3, 3, 3};
std::vector<int64_t> axes{2, 3, 4};
OpTester test("Resize", 18);
test.AddAttribute<int64_t>("exclude_outside", 0LL);
test.AddAttribute<std::vector<int64_t>>("axes", axes);
test.AddAttribute<int64_t>("antialias", 0LL);
test.AddAttribute("mode", "linear");
test.AddInput<float>("X", {1, 1, 4, 4, 4}, X);
test.AddInput<float>("roi", {int64_t(roi.size())}, roi);
test.AddInput<float>("", {0}, scales);
test.AddInput<int64_t>("sizes", {3}, {3, 3, 3});
test.AddOutput<float>("Y", output_shape, Y);
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kQnnExecutionProvider});
}
} // namespace test
} // namespace onnxruntime