mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-25 19:48:11 +00:00
[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:
parent
1d6f13fb92
commit
eb0ce86db8
7 changed files with 242 additions and 62 deletions
|
|
@ -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>);
|
||||
|
|
|
|||
|
|
@ -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)},
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue