Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DML] Resize 18 & 19 #19071

Merged
merged 20 commits into from
Feb 1, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,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.");
Expand Down Expand Up @@ -250,6 +250,11 @@ class DmlOperatorResize : public DmlOperator, public ResizeHelper
std::string mode = kernelCreationContext.GetOptionalAttribute<std::string>(AttrName::Mode, "NEAREST");
DML_INTERPOLATION_MODE interpolationMode = Dml::MapStringToInteropolationMode(mode);


#if DML_TARGET_VERSION >= 0x6300
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
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;
Expand Down Expand Up @@ -289,7 +294,12 @@ class DmlOperatorResize : public DmlOperator, public ResizeHelper
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);
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
#else
DML_RESAMPLE2_OPERATOR_DESC operatorDesc = {};
#endif
operatorDesc.InputTensor = inputDescs.data();
operatorDesc.OutputTensor = outputDescs.data();
operatorDesc.InterpolationMode = interpolationMode;
Expand All @@ -298,8 +308,11 @@ class DmlOperatorResize : public DmlOperator, public ResizeHelper
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);
}
};
Expand Down Expand Up @@ -342,6 +355,9 @@ 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>);
#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>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ DML_OP_EXTERN_CREATION_FUNCTION(Trilu);

#if DML_TARGET_VERSION >= 0x6300
DML_OP_EXTERN_CREATION_FUNCTION(Col2Im);
DML_OP_EXTERN_CREATION_FUNCTION(Resize18);
#endif

DML_OP_EXTERN_CREATION_FUNCTION(Shape);
Expand Down Expand Up @@ -598,6 +599,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;
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
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 };
Expand Down Expand Up @@ -961,7 +963,9 @@ 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)},
#endif
// Activation Functions
{REG_INFO( 7, Sigmoid, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
{REG_INFO( 13, Sigmoid, typeNameListDefault, supportedTypeListFloat16to32, DmlGraphSupport::Supported)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ namespace OperatorHelper
}
}

template <typename T>
void ExpandToAxes(/*inout*/ std::vector<T>& originalValues, gsl::span<int32_t> axes, std::vector<T> expanded)
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
{
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 = expanded;
}

float CastFloat16ToFloat32(uint16_t input)
{
// Promote float16m10e5s1 to float32m23e8s1.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,53 @@ 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:
{
const int32_t* data = tensor.GetData<int32_t>();
std::transform(data, data + elementCount, result.begin(), [](auto v) {return static_cast<T>(v); });
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
}
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:
Expand Down Expand Up @@ -1611,6 +1654,7 @@ 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_OneHot = OneHotHelper;

using ShapeInferenceHelper_Sqrt = GetOutputShapeAsInputShapeHelper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ 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
Expand Down
59 changes: 59 additions & 0 deletions onnxruntime/test/providers/cpu/tensor/resize_op_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -2231,5 +2233,62 @@ TEST(ResizeOpTest, Antialias_Use_Extrapolation) {
},
{4, 4, 4}, X, {3, 3, 3}, Y);
}

// 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});
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
}

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);
linnealovespie marked this conversation as resolved.
Show resolved Hide resolved
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