mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-07 17:15:29 +00:00
[NNAPI EP] Add Resize and Clip support (#5427)
* Add resize and clip support in NNAPI EP * Try to get around tensor rt test failure * Addressed PR comments
This commit is contained in:
parent
c444b9d76a
commit
ce5465d5f3
7 changed files with 631 additions and 114 deletions
|
|
@ -235,7 +235,7 @@ Status ModelBuilder::RegisterInitializers() {
|
|||
shape.push_back(SafeInt<uint32_t>(dim));
|
||||
}
|
||||
|
||||
ORT_RETURN_IF_NOT(!shape.empty(), "NNAPI does not support scalar initializer");
|
||||
ORT_RETURN_IF_NOT(!shape.empty(), "NNAPI does not support scalar initializer, tensor name, ", name);
|
||||
|
||||
Type type = Type::TENSOR_FLOAT32;
|
||||
switch (tensor.data_type()) {
|
||||
|
|
|
|||
|
|
@ -1852,13 +1852,13 @@ bool SoftMaxOpBuilder::IsOpSupportedImpl(ModelBuilder& model_builder, const Node
|
|||
return false;
|
||||
}
|
||||
|
||||
const auto android_skd_ver = model_builder.GetAndroidSdkVer();
|
||||
if (android_skd_ver < 29) {
|
||||
const auto android_sdk_ver = model_builder.GetAndroidSdkVer();
|
||||
if (android_sdk_ver < 29) {
|
||||
NodeAttrHelper helper(node);
|
||||
int32_t axis = helper.Get("axis", 1);
|
||||
if (axis != 1) {
|
||||
LOGS_DEFAULT(VERBOSE)
|
||||
<< "SoftMax only support axis 1 on Android API level: " << android_skd_ver
|
||||
<< "SoftMax only support axis 1 on Android API level: " << android_sdk_ver
|
||||
<< " input axis: " << axis;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1871,13 +1871,13 @@ Status SoftMaxOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, cons
|
|||
auto& shaper(model_builder.GetShaper());
|
||||
const auto& operand_indices(model_builder.GetOperandIndices());
|
||||
const auto& operand_types(model_builder.GetOperandTypes());
|
||||
const auto android_skd_ver = model_builder.GetAndroidSdkVer();
|
||||
const auto android_sdk_ver = model_builder.GetAndroidSdkVer();
|
||||
NodeAttrHelper helper(node);
|
||||
|
||||
auto input = node.InputDefs()[0]->Name();
|
||||
bool input_is_nhwc = model_builder.IsOperandNHWC(input);
|
||||
bool output_is_nhwc = input_is_nhwc;
|
||||
if (android_skd_ver < 29) {
|
||||
if (android_sdk_ver < 29) {
|
||||
if (model_builder.IsOperandNHWC(input)) {
|
||||
output_is_nhwc = false;
|
||||
// We want to transpose nhwc operand back to nchw before softmax
|
||||
|
|
@ -1901,7 +1901,7 @@ Status SoftMaxOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, cons
|
|||
input_indices.push_back(operand_indices.at(input));
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, beta);
|
||||
|
||||
if (android_skd_ver > 28) {
|
||||
if (android_sdk_ver > 28) {
|
||||
// you can only specify axis for android api level 29+
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, axis);
|
||||
}
|
||||
|
|
@ -2494,8 +2494,6 @@ Status QuantizeLinearOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builde
|
|||
ORT_RETURN_IF_ERROR(GetQuantizationZeroPoint(model_builder, node, 2, zero_point));
|
||||
}
|
||||
|
||||
LOGS_DEFAULT(VERBOSE) << "scale: " << scale << " zp: " << zero_point;
|
||||
|
||||
ORT_RETURN_IF_ERROR(shaper.Identity(input, output));
|
||||
const OperandType output_operand_type(output_type, shaper[output], scale, zero_point);
|
||||
std::vector<uint32_t> input_indices;
|
||||
|
|
@ -2624,12 +2622,12 @@ Status LRNOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const No
|
|||
const auto& operand_indices(model_builder.GetOperandIndices());
|
||||
const auto& operand_types(model_builder.GetOperandTypes());
|
||||
NodeAttrHelper helper(node);
|
||||
const auto android_skd_ver = model_builder.GetAndroidSdkVer();
|
||||
const auto android_sdk_ver = model_builder.GetAndroidSdkVer();
|
||||
|
||||
auto input = node.InputDefs()[0]->Name();
|
||||
const auto& output = node.OutputDefs()[0]->Name();
|
||||
bool output_is_nhwc = model_builder.IsOperandNHWC(input);
|
||||
if (android_skd_ver < 29) {
|
||||
if (android_sdk_ver < 29) {
|
||||
// on android api level 28, we need to transpose the nchw input to nhwc
|
||||
output_is_nhwc = true;
|
||||
if (!model_builder.IsOperandNHWC(input)) {
|
||||
|
|
@ -2657,7 +2655,7 @@ Status LRNOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const No
|
|||
ADD_SCALAR_OPERAND(model_builder, input_indices, beta);
|
||||
|
||||
// specify axis is only available on api level >= 29
|
||||
if (android_skd_ver > 28) {
|
||||
if (android_sdk_ver > 28) {
|
||||
// ONNX LRN is always performed on C dimension
|
||||
int32_t axis = output_is_nhwc
|
||||
? 3 // nhwc
|
||||
|
|
@ -2674,6 +2672,311 @@ Status LRNOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const No
|
|||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region op_clip
|
||||
|
||||
class ClipOpBuilder : public BaseOpBuilder {
|
||||
public:
|
||||
void AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) override;
|
||||
|
||||
private:
|
||||
bool IsOpSupportedImpl(ModelBuilder& model_builder, const Node& node) override;
|
||||
Status AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node) override ORT_MUST_USE_RESULT;
|
||||
static bool GetMinMax(ModelBuilder& model_builder, const Node& node, float& min, float& max);
|
||||
};
|
||||
|
||||
void ClipOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) {
|
||||
if (node.InputDefs().size() > 1)
|
||||
model_builder.AddInitializerToSkip(node.InputDefs()[1]->Name()); // min
|
||||
|
||||
if (node.InputDefs().size() > 2)
|
||||
model_builder.AddInitializerToSkip(node.InputDefs()[2]->Name()); // max
|
||||
}
|
||||
|
||||
/* static */ bool ClipOpBuilder::GetMinMax(ModelBuilder& model_builder, const Node& node, float& min, float& max) {
|
||||
if (node.SinceVersion() < 11) { // Clip opset 1, 6 is using attributes for min/max
|
||||
NodeAttrHelper helper(node);
|
||||
min = helper.Get("min", std::numeric_limits<float>::lowest());
|
||||
max = helper.Get("max", std::numeric_limits<float>::max());
|
||||
} else {
|
||||
const auto& initializers(model_builder.GetInitializerTensors());
|
||||
|
||||
if (node.InputDefs().size() > 1) { // we have input min
|
||||
const auto& min_name = node.InputDefs()[1]->Name();
|
||||
if (!Contains(initializers, min_name)) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Input min of Clip must be known";
|
||||
return false;
|
||||
}
|
||||
min = GetTensorFloatData(initializers.at(min_name))[0];
|
||||
}
|
||||
|
||||
if (node.InputDefs().size() > 2) { // we have input max
|
||||
const auto& max_name = node.InputDefs()[2]->Name();
|
||||
if (!Contains(initializers, max_name)) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Input max of Clip must be known";
|
||||
return false;
|
||||
}
|
||||
max = GetTensorFloatData(initializers.at(max_name))[0];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClipOpBuilder::IsOpSupportedImpl(ModelBuilder& model_builder, const Node& node) {
|
||||
float min = std::numeric_limits<float>::lowest();
|
||||
float max = std::numeric_limits<float>::max();
|
||||
if (!GetMinMax(model_builder, node, min, max))
|
||||
return false;
|
||||
|
||||
// We only supoort relu6 or relu1
|
||||
// TODO, support clip between 2 arbitrary numbers
|
||||
if ((min == 0.0f && max == 6.0f) || (min == -1.0f && max == 1.0f)) {
|
||||
return true;
|
||||
} else {
|
||||
LOGS_DEFAULT(VERBOSE) << "Clip only supports [min, max] = [0, 6] or [-1, 1], the input is ["
|
||||
<< min << ", " << max << "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ClipOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node) {
|
||||
auto& shaper(model_builder.GetShaper());
|
||||
const auto& operand_indices(model_builder.GetOperandIndices());
|
||||
const auto& operand_types(model_builder.GetOperandTypes());
|
||||
|
||||
const auto& input = node.InputDefs()[0]->Name();
|
||||
const auto& output = node.OutputDefs()[0]->Name();
|
||||
bool output_is_nhwc = model_builder.IsOperandNHWC(input);
|
||||
|
||||
ORT_RETURN_IF_ERROR(shaper.Identity(input, output));
|
||||
const OperandType output_operand_type(operand_types.at(input).type, shaper[output]);
|
||||
|
||||
float min = std::numeric_limits<float>::lowest();
|
||||
float max = std::numeric_limits<float>::max();
|
||||
GetMinMax(model_builder, node, min, max);
|
||||
|
||||
int32_t op_code;
|
||||
if (min == 0.0f && max == 6.0f)
|
||||
op_code = ANEURALNETWORKS_RELU6;
|
||||
else if (min == -1.0f && max == 1.0f)
|
||||
op_code = ANEURALNETWORKS_RELU1;
|
||||
else
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "ClipOpBuilder, unsupported input [", min, ", ", max, "].",
|
||||
"We should not reach here, ClipOpBuilder::IsOpSupportedImpl should have caught this.");
|
||||
|
||||
std::vector<uint32_t> input_indices;
|
||||
input_indices.push_back(operand_indices.at(input));
|
||||
ORT_RETURN_IF_ERROR(model_builder.AddOperation(op_code, input_indices,
|
||||
{output}, {output_operand_type}, {output_is_nhwc}));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region op_Resize
|
||||
|
||||
class ResizeOpBuilder : public BaseOpBuilder {
|
||||
public:
|
||||
void AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) override;
|
||||
|
||||
private:
|
||||
bool IsOpSupportedImpl(ModelBuilder& model_builder, const Node& node) override;
|
||||
|
||||
int32_t GetMinSupportedSdkVer(ModelBuilder& model_builder, const Node& node) const override;
|
||||
|
||||
Status AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node) override ORT_MUST_USE_RESULT;
|
||||
};
|
||||
|
||||
int32_t ResizeOpBuilder::GetMinSupportedSdkVer(ModelBuilder& /* model_builder */, const Node& /* node */) const {
|
||||
return 28;
|
||||
}
|
||||
|
||||
void ResizeOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const Node& node) {
|
||||
// We will still add scales to the skipped list even sizes are present
|
||||
// since there is no use of it, we will not process it later
|
||||
model_builder.AddInitializerToSkip(node.InputDefs()[2]->Name()); // scales
|
||||
|
||||
if (node.InputDefs().size() > 3)
|
||||
model_builder.AddInitializerToSkip(node.InputDefs()[3]->Name()); // sizes
|
||||
}
|
||||
|
||||
bool ResizeOpBuilder::IsOpSupportedImpl(ModelBuilder& model_builder, const Node& node) {
|
||||
// Resize opset 10- is very different than Resize opset 11+, with many key attributes missing
|
||||
// We only support Resize opset 11+ here
|
||||
if (node.SinceVersion() < 11) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Resize only supports opset 11+";
|
||||
return false;
|
||||
}
|
||||
|
||||
Shape input_shape;
|
||||
if (!GetShape(*node.InputDefs()[0], input_shape))
|
||||
return false;
|
||||
|
||||
const auto input_size = input_shape.size();
|
||||
if (input_size != 4) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Resize only support 4d shape, input is "
|
||||
<< input_size << "d shape";
|
||||
return false;
|
||||
}
|
||||
|
||||
{ // check attributes
|
||||
const auto android_sdk_ver = model_builder.GetAndroidSdkVer();
|
||||
|
||||
NodeAttrHelper helper(node);
|
||||
const auto mode = helper.Get("mode", "nearest");
|
||||
if (mode != "linear") {
|
||||
LOGS_DEFAULT(VERBOSE) << "Resize unsupported input mode, " << mode;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto coord_trans_mode = helper.Get("coordinate_transformation_mode", "half_pixel");
|
||||
bool using_half_pixel = coord_trans_mode == "half_pixel";
|
||||
bool using_align_corners = coord_trans_mode == "align_corners";
|
||||
if (!using_half_pixel && !using_align_corners && coord_trans_mode != "asymmetric") {
|
||||
LOGS_DEFAULT(VERBOSE) << "Resize, unsupported coord_trans_mode, " << coord_trans_mode;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (android_sdk_ver < 30 && (using_half_pixel || using_align_corners)) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Resize only support half_pixel/align_corners on API level 30+, current API level is "
|
||||
<< android_sdk_ver;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto exclude_outside = helper.Get("exclude_outside", 0);
|
||||
if (exclude_outside != 0) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Resize does not support exclude_outside for now";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
{ // scales and sizes (if present) must be initializers
|
||||
const auto& initializers(model_builder.GetInitializerTensors());
|
||||
const auto input_defs = node.InputDefs();
|
||||
// scales
|
||||
if (input_defs.size() < 3 || !Contains(initializers, input_defs[2]->Name())) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Input scales of Resize must be known";
|
||||
return false;
|
||||
}
|
||||
|
||||
// sizes
|
||||
if (input_defs.size() > 3 && !Contains(initializers, input_defs[3]->Name())) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Input sizes of Resize must be known";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to check if the scales or sizes are not trying to resize on N/C channels here
|
||||
if (input_defs.size() == 3) { // we are using scales
|
||||
const auto& scales_tensor = initializers.at(input_defs[2]->Name());
|
||||
const float* scales_data = GetTensorFloatData(scales_tensor);
|
||||
float scale_n = scales_data[0];
|
||||
float scale_c = scales_data[1];
|
||||
if (scale_n != 1.0f || scale_c != 1.0f) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Scales of N/C channel should be 1"
|
||||
<< "Resize of N/C channels are not supported"
|
||||
<< ", scale_n, " << scale_n << ", scale_c, " << scale_c;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// we are using sizes
|
||||
const auto& sizes_name = input_defs[3]->Name();
|
||||
const auto& sizes_tensor = initializers.at(sizes_name);
|
||||
const int64_t* sizes_data = GetTensorInt64Data(sizes_tensor);
|
||||
uint32_t size_n = SafeInt<uint32_t>(sizes_data[0]);
|
||||
uint32_t size_c = SafeInt<uint32_t>(sizes_data[1]);
|
||||
if (size_n != input_shape[0] || size_c != input_shape[1]) {
|
||||
LOGS_DEFAULT(VERBOSE) << "Output sizes of N/C chanel should match the input sizes, "
|
||||
<< "Resize of N/C channels are not supported"
|
||||
<< ", input_size_n, " << input_shape[0] << ", output_size_n, " << size_n
|
||||
<< ". input_size_c, " << input_shape[1] << ", output_size_c, " << size_c;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ResizeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const Node& node) {
|
||||
auto& shaper(model_builder.GetShaper());
|
||||
const auto& operand_indices(model_builder.GetOperandIndices());
|
||||
const auto& operand_types(model_builder.GetOperandTypes());
|
||||
const auto& initializers(model_builder.GetInitializerTensors());
|
||||
NodeAttrHelper helper(node);
|
||||
const auto input_defs = node.InputDefs();
|
||||
const auto android_sdk_ver = model_builder.GetAndroidSdkVer();
|
||||
const auto& output = node.OutputDefs()[0]->Name();
|
||||
|
||||
auto input = input_defs[0]->Name();
|
||||
bool use_nchw = model_builder.UseNCHW();
|
||||
bool input_is_nhwc = model_builder.IsOperandNHWC(input);
|
||||
bool output_is_nhwc = false;
|
||||
if (use_nchw) {
|
||||
ORT_RETURN_IF_NOT(!input_is_nhwc, "model_builder.UseNCHW() but input is NHWC");
|
||||
} else {
|
||||
output_is_nhwc = true;
|
||||
if (!input_is_nhwc) {
|
||||
const auto& nchw_input = input_defs[0]->Name();
|
||||
if (!model_builder.GetNHWCOperand(nchw_input, input)) {
|
||||
input = model_builder.GetUniqueName(nchw_input + "_nchw_to_nhwc");
|
||||
ORT_RETURN_IF_ERROR(TransposeNCHWToNHWC(model_builder, nchw_input, input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO, add support for nearest neighbor
|
||||
int32_t operationCode = ANEURALNETWORKS_RESIZE_BILINEAR;
|
||||
|
||||
const auto coord_trans_mode = helper.Get("coordinate_transformation_mode", "half_pixel");
|
||||
bool using_half_pixel = coord_trans_mode == "half_pixel";
|
||||
bool using_align_corners = coord_trans_mode == "align_corners";
|
||||
|
||||
if (input_defs.size() == 3) { // we are using scales
|
||||
const auto& scales_name = input_defs[2]->Name();
|
||||
const auto& scales_tensor = initializers.at(scales_name);
|
||||
const float* scales_data = GetTensorFloatData(scales_tensor);
|
||||
float scale_h = scales_data[2];
|
||||
float scale_w = scales_data[3];
|
||||
ORT_RETURN_IF_ERROR(
|
||||
shaper.ResizeUsingScales(input, scale_h, scale_w, use_nchw, output));
|
||||
} else { // we are using sizes
|
||||
const auto& sizes_name = input_defs[3]->Name();
|
||||
const auto& sizes_tensor = initializers.at(sizes_name);
|
||||
const int64_t* sizes_data = GetTensorInt64Data(sizes_tensor);
|
||||
ORT_RETURN_IF_ERROR(
|
||||
shaper.ResizeUsingOutputSizes(input, SafeInt<uint32_t>(sizes_data[2]), SafeInt<uint32_t>(sizes_data[3]), use_nchw, output));
|
||||
}
|
||||
|
||||
const auto& output_shape = shaper[output];
|
||||
int32_t output_h = use_nchw ? output_shape[2] : output_shape[1];
|
||||
int32_t output_w = use_nchw ? output_shape[3] : output_shape[2];
|
||||
|
||||
std::vector<uint32_t> input_indices;
|
||||
input_indices.push_back(operand_indices.at(input));
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, output_w);
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, output_h);
|
||||
|
||||
if (android_sdk_ver > 28) {
|
||||
// using nchw is only available on API level 29
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, use_nchw);
|
||||
}
|
||||
|
||||
if (android_sdk_ver > 29 && (using_align_corners || using_half_pixel)) {
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, using_align_corners);
|
||||
if (using_half_pixel)
|
||||
ADD_SCALAR_OPERAND(model_builder, input_indices, using_half_pixel);
|
||||
}
|
||||
|
||||
const OperandType output_operand_type(operand_types.at(input).type, output_shape);
|
||||
ORT_RETURN_IF_ERROR(model_builder.AddOperation(operationCode, input_indices,
|
||||
{output}, {output_operand_type}, {output_is_nhwc}));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CreateOpBuilders
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<IOpBuilder>>
|
||||
|
|
@ -2736,6 +3039,8 @@ CreateOpBuilders() {
|
|||
op_map.emplace("QuantizeLinear", std::make_shared<QuantizeLinearOpBuilder>());
|
||||
op_map.emplace("DequantizeLinear", std::make_shared<DequantizeLinearOpBuilder>());
|
||||
op_map.emplace("LRN", std::make_shared<LRNOpBuilder>());
|
||||
op_map.emplace("Clip", std::make_shared<ClipOpBuilder>());
|
||||
op_map.emplace("Resize", std::make_shared<ResizeOpBuilder>());
|
||||
|
||||
return op_map;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,20 @@ Status Shaper::Squeeze(const std::string& input_name,
|
|||
SHAPER_FUNC(Squeeze, input_name, axes, output_name);
|
||||
}
|
||||
|
||||
Status Shaper::ResizeUsingScales(const std::string& input_name,
|
||||
const float scale_h, const float scale_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) {
|
||||
SHAPER_FUNC(ResizeUsingScales, input_name, scale_h, scale_w, nchw, output_name);
|
||||
}
|
||||
|
||||
Status Shaper::ResizeUsingOutputSizes(const std::string& input_name,
|
||||
const uint32_t output_h, const uint32_t output_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) {
|
||||
SHAPER_FUNC(ResizeUsingOutputSizes, input_name, output_h, output_w, nchw, output_name);
|
||||
}
|
||||
|
||||
#undef SHAPER_FUNC
|
||||
|
||||
Status Shaper::ConvImpl(const std::string& input_name,
|
||||
|
|
@ -384,6 +398,38 @@ Status Shaper::SqueezeImpl(const std::string& input_name,
|
|||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Shaper::ResizeUsingScalesImpl(const std::string& input_name,
|
||||
const float scale_h, const float scale_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) {
|
||||
Shape output_dimen = shape_map_.at(input_name);
|
||||
if (nchw) {
|
||||
output_dimen[2] *= scale_h;
|
||||
output_dimen[3] *= scale_w;
|
||||
} else { // nhwc
|
||||
output_dimen[1] *= scale_h;
|
||||
output_dimen[2] *= scale_w;
|
||||
}
|
||||
shape_map_[output_name] = output_dimen;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Shaper::ResizeUsingOutputSizesImpl(const std::string& input_name,
|
||||
const uint32_t output_h, const uint32_t output_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) {
|
||||
Shape output_dimen = shape_map_.at(input_name);
|
||||
if (nchw) {
|
||||
output_dimen[2] = output_h;
|
||||
output_dimen[3] = output_w;
|
||||
} else { // nhwc
|
||||
output_dimen[1] = output_h;
|
||||
output_dimen[2] = output_w;
|
||||
}
|
||||
shape_map_[output_name] = output_dimen;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void Shaper::AddShape(const std::string& name, const Shape& shape) {
|
||||
shape_map_[name] = shape;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,9 +57,18 @@ class Shaper {
|
|||
Status Concat(const std::vector<std::string>& input_names, const int32_t axis, const std::string& output_name)
|
||||
ORT_MUST_USE_RESULT;
|
||||
|
||||
Status Squeeze(const std::string& input, const std::vector<int32_t>& axes, const std::string& output)
|
||||
Status Squeeze(const std::string& input_name, const std::vector<int32_t>& axes, const std::string& output_name)
|
||||
ORT_MUST_USE_RESULT;
|
||||
|
||||
Status ResizeUsingScales(const std::string& input_name,
|
||||
const float scale_h, const float scale_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) ORT_MUST_USE_RESULT;
|
||||
Status ResizeUsingOutputSizes(const std::string& input_name,
|
||||
const uint32_t output_h, const uint32_t output_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) ORT_MUST_USE_RESULT;
|
||||
|
||||
// If the shape of certain input is dynamic
|
||||
// Use the following 2 functions to update the particular shape
|
||||
// and calculate the new output shape
|
||||
|
|
@ -104,8 +113,16 @@ class Shaper {
|
|||
ORT_MUST_USE_RESULT;
|
||||
Status ConcatImpl(const std::vector<std::string>& input_names, const int32_t axis, const std::string& output_name)
|
||||
ORT_MUST_USE_RESULT;
|
||||
Status SqueezeImpl(const std::string& input, const std::vector<int32_t>& axes, const std::string& output)
|
||||
Status SqueezeImpl(const std::string& input_names, const std::vector<int32_t>& axes, const std::string& output_name)
|
||||
ORT_MUST_USE_RESULT;
|
||||
Status ResizeUsingScalesImpl(const std::string& input_name,
|
||||
const float scale_h, const float scale_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) ORT_MUST_USE_RESULT;
|
||||
Status ResizeUsingOutputSizesImpl(const std::string& input_name,
|
||||
const uint32_t output_h, const uint32_t output_w,
|
||||
bool nchw,
|
||||
const std::string& output_name) ORT_MUST_USE_RESULT;
|
||||
|
||||
std::unordered_map<std::string, Shape> shape_map_;
|
||||
std::vector<std::function<Status(Shaper&)>> shape_ops_;
|
||||
|
|
|
|||
|
|
@ -303,8 +303,12 @@ common::Status NnapiExecutionProvider::Compile(const std::vector<onnxruntime::No
|
|||
OperandType input_type = model_input_type;
|
||||
input_type.SetDimensions(dimensions);
|
||||
|
||||
if (input_type.GetOperandBlobByteSize() == 0)
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "The actual input cannot have 0 dim (dynamic)");
|
||||
// We have some op has input can have {0} shapes, such as Resize.scales/roi, these are valid input
|
||||
// We still want to log the shape info, in case we get an input shape with some zero dim and some non-zero dim
|
||||
if (input_type.GetOperandBlobByteSize() == 0) {
|
||||
LOGS_DEFAULT(INFO) << "The actual input [" << input_name << "] has "
|
||||
<< nnapi::Shape2String(dimensions) << " shape";
|
||||
}
|
||||
|
||||
if (input_type.dimensions != model_input_type.dimensions && model_input_type.GetOperandBlobByteSize() != 0) {
|
||||
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL,
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ TEST(MathOpTest, Clip_6) {
|
|||
{10.0f, 4.4f, 10.0f,
|
||||
-1.3f, 3.5f, 10.0f,
|
||||
-5.4f, 9.3f, 10.0f});
|
||||
#if defined(OPENVINO_CONFIG_MYRIAD) || defined(OPENVINO_CONFIG_VAD_M) || defined(OPENVINO_CONFIG_CPU_FP32)
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kOpenVINOExecutionProvider});
|
||||
#else
|
||||
test.Run();
|
||||
#endif
|
||||
#if defined(OPENVINO_CONFIG_MYRIAD) || defined(OPENVINO_CONFIG_VAD_M) || defined(OPENVINO_CONFIG_CPU_FP32)
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kOpenVINOExecutionProvider});
|
||||
#else
|
||||
test.Run();
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(MathOpTest, Clip_Default) {
|
||||
|
|
@ -42,12 +42,12 @@ TEST(MathOpTest, Clip_Default) {
|
|||
-1.3f, 3.5f, 64.0f,
|
||||
-5.4f, 9.3f, 82.4f});
|
||||
|
||||
#if defined(OPENVINO_CONFIG_MYRIAD) || defined(OPENVINO_CONFIG_VAD_M)
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kOpenVINOExecutionProvider, kNGraphExecutionProvider});
|
||||
#else
|
||||
#if defined(OPENVINO_CONFIG_MYRIAD) || defined(OPENVINO_CONFIG_VAD_M)
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kOpenVINOExecutionProvider, kNGraphExecutionProvider});
|
||||
#else
|
||||
// nGraph does not support Clip opset 12 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider});
|
||||
#endif
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider});
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(MathOpTest, Clip_Default_int8) {
|
||||
|
|
@ -55,13 +55,13 @@ TEST(MathOpTest, Clip_Default_int8) {
|
|||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<int8_t>("X", dims,
|
||||
{11, 4, 127,
|
||||
-1, 3, 64,
|
||||
-5, 9, 82});
|
||||
test.AddOutput<int8_t>("Y", dims,
|
||||
{11, 4, 127,
|
||||
-1, 3, 64,
|
||||
-5, 9, 82});
|
||||
test.AddOutput<int8_t>("Y", dims,
|
||||
{11, 4, 127,
|
||||
-1, 3, 64,
|
||||
-5, 9, 82});
|
||||
|
||||
// TensorRT, nGraph does not support Clip opset 12 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kNGraphExecutionProvider});
|
||||
|
|
@ -72,13 +72,13 @@ TEST(MathOpTest, Clip_Default_uint8) {
|
|||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<uint8_t>("X", dims,
|
||||
{11, 4, 255,
|
||||
1, 3, 64,
|
||||
5, 9, 82});
|
||||
test.AddOutput<uint8_t>("Y", dims,
|
||||
{11, 4, 255,
|
||||
1, 3, 64,
|
||||
5, 9, 82});
|
||||
test.AddOutput<uint8_t>("Y", dims,
|
||||
{11, 4, 255,
|
||||
1, 3, 64,
|
||||
5, 9, 82});
|
||||
|
||||
// TensorRT, nGraph does not support Clip opset 12 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kNGraphExecutionProvider});
|
||||
|
|
@ -89,53 +89,110 @@ TEST(MathOpTest, Clip_Default_int64) {
|
|||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<int64_t>("X", dims,
|
||||
{11, 4, 432,
|
||||
-1, 3, 64,
|
||||
-5, 9, 82});
|
||||
test.AddOutput<int64_t>("Y", dims,
|
||||
{11, 4, 432,
|
||||
-1, 3, 64,
|
||||
-5, 9, 82});
|
||||
test.AddOutput<int64_t>("Y", dims,
|
||||
{11, 4, 432,
|
||||
-1, 3, 64,
|
||||
-5, 9, 82});
|
||||
|
||||
// TensorRT, nGraph does not support Clip opset 12 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kNGraphExecutionProvider});
|
||||
}
|
||||
|
||||
|
||||
TEST(MathOpTest, Clip_Default_uint64) {
|
||||
OpTester test("Clip", 12);
|
||||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<uint64_t>("X", dims,
|
||||
{11, 4, 432,
|
||||
1, 3, 64,
|
||||
5, 9, 82});
|
||||
test.AddOutput<uint64_t>("Y", dims,
|
||||
{11, 4, 432,
|
||||
1, 3, 64,
|
||||
5, 9, 82});
|
||||
test.AddOutput<uint64_t>("Y", dims,
|
||||
{11, 4, 432,
|
||||
1, 3, 64,
|
||||
5, 9, 82});
|
||||
|
||||
// TensorRT, nGraph does not support Clip opset 12 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider, kNGraphExecutionProvider});
|
||||
}
|
||||
|
||||
TEST(MathOpTest, Clip) {
|
||||
OpTester test("Clip", 11);
|
||||
// To test NNAPI EP, we need the min/max to be in initializers
|
||||
auto run_test = [](bool min_max_are_initializer) {
|
||||
OpTester test("Clip", 11);
|
||||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<float>("X", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-6.0f, 0.0f, 6.0f,
|
||||
-5.4f, 2.0f, 6.0f});
|
||||
test.AddInput<float>("min", {}, {-5});
|
||||
test.AddInput<float>("max", {}, {5});
|
||||
test.AddOutput<float>("Y", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-5.0f, 0.0f, 5.0f,
|
||||
-5.0f, 2.0f, 5.0f});
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<float>("X", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-6.0f, 0.0f, 6.0f,
|
||||
-5.4f, 2.0f, 6.0f});
|
||||
test.AddInput<float>("min", {}, {-5}, min_max_are_initializer);
|
||||
test.AddInput<float>("max", {}, {5}, min_max_are_initializer);
|
||||
test.AddOutput<float>("Y", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-5.0f, 0.0f, 5.0f,
|
||||
-5.0f, 2.0f, 5.0f});
|
||||
|
||||
// TensorRT, nGraph and Tensorrt does not support Clip opset 11 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider, kTensorrtExecutionProvider});
|
||||
// TensorRT, nGraph and Tensorrt does not support Clip opset 11 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider, kTensorrtExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
// Use clip between [0, 6] as Relu6 (for some EPs, such as NNAPI)
|
||||
TEST(MathOpTest, Clip_Relu6) {
|
||||
// To test NNAPI EP, we need the min/max to be in initializers
|
||||
auto run_test = [](bool min_max_are_initializer) {
|
||||
OpTester test("Clip", 11);
|
||||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<float>("X", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-6.0f, 3.5f, 6.0f,
|
||||
-5.4f, 2.0f, 8.0f});
|
||||
test.AddInput<float>("min", {}, {0.0f}, min_max_are_initializer);
|
||||
test.AddInput<float>("max", {}, {6.0f}, min_max_are_initializer);
|
||||
test.AddOutput<float>("Y", dims,
|
||||
{0.0f, 0.0f, 1.0f,
|
||||
0.0f, 3.5f, 6.0f,
|
||||
0.0f, 2.0f, 6.0f});
|
||||
|
||||
// TensorRT, nGraph and Tensorrt does not support Clip opset 11 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider, kTensorrtExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
// Use clip between [-1, 1] as Relu1 (for some EPs, such as NNAPI)
|
||||
TEST(MathOpTest, Clip_Relu1) {
|
||||
// To test NNAPI EP, we need the min/max to be in initializers
|
||||
auto run_test = [](bool min_max_are_initializer) {
|
||||
OpTester test("Clip", 11);
|
||||
|
||||
std::vector<int64_t> dims{3, 3};
|
||||
test.AddInput<float>("X", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-6.0f, 3.5f, 6.0f,
|
||||
-5.4f, 2.0f, 8.0f});
|
||||
test.AddInput<float>("min", {}, {-1.0f}, min_max_are_initializer);
|
||||
test.AddInput<float>("max", {}, {1.0f}, min_max_are_initializer);
|
||||
test.AddOutput<float>("Y", dims,
|
||||
{-1.0f, 0.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f});
|
||||
|
||||
// TensorRT, nGraph and Tensorrt does not support Clip opset 11 yet.
|
||||
test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kNGraphExecutionProvider, kTensorrtExecutionProvider});
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
TEST(MathOpTest, ClipDimWithZero) {
|
||||
|
|
|
|||
|
|
@ -87,27 +87,103 @@ TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear) {
|
|||
test.Run();
|
||||
}
|
||||
|
||||
// Since NNAPI(TFLite) only using the scale calulate using the input/output size
|
||||
// For the above test (ResizeOpLinearDownSampleTest_4DBilinear)
|
||||
// The output size is [1,1,2,4].*[1,1,0.6,0.6]=[1,1,1,2]
|
||||
// NNAPI will recaluclate the scales as the output size divided by input size
|
||||
// scales = [1,1,1,2]./[1,1,2,4] = [1,1,0.5,0.5]
|
||||
// See, https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/kernels/internal/reference/reference_ops.h
|
||||
// So the result of the above example will be different than CPU EP
|
||||
// Add the following 2 tests to test with scales valid to NNAPI
|
||||
TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear1) {
|
||||
// To test NNAPI EP, we need the sclaes/sizes to be in initializers
|
||||
auto run_test = [](bool scales_in_initializer) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 0.5f, 0.5f};
|
||||
|
||||
test.AddAttribute("mode", "linear");
|
||||
|
||||
const int64_t N = 1, C = 1, H = 2, W = 4;
|
||||
std::vector<float> X = {
|
||||
1.0f, 2.0f, 3.0f, 4.0f,
|
||||
5.0f, 6.0f, 7.0f, 8.0f};
|
||||
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales, scales_in_initializer);
|
||||
|
||||
std::vector<float> Y = {3.5f, 5.5f};
|
||||
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run();
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear1_WithSizes) {
|
||||
// To test NNAPI EP, we need the sclaes/sizes to be in initializers
|
||||
auto run_test = [](bool scales_and_sizes_in_initializer) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{};
|
||||
const int64_t N = 1, C = 1, H = 2, W = 4;
|
||||
std::vector<int64_t> sizes{N, C, 1, 2};
|
||||
test.AddAttribute("mode", "linear");
|
||||
|
||||
std::vector<float> X = {
|
||||
1.0f, 2.0f, 3.0f, 4.0f,
|
||||
5.0f, 6.0f, 7.0f, 8.0f};
|
||||
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {0}, scales, scales_and_sizes_in_initializer);
|
||||
test.AddInput<int64_t>("sizes", {4}, sizes, scales_and_sizes_in_initializer);
|
||||
|
||||
std::vector<float> Y = {3.5f, 5.5f};
|
||||
|
||||
test.AddOutput<float>("Y", sizes, Y);
|
||||
test.Run();
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_4DBilinear_align_corners) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 0.6f, 0.6f};
|
||||
// To test NNAPI EP, we need the sclaes/sizes to be in initializers
|
||||
auto run_test = [](bool scales_in_initializer) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 0.6f, 0.6f};
|
||||
|
||||
test.AddAttribute("mode", "linear");
|
||||
test.AddAttribute("coordinate_transformation_mode", "align_corners");
|
||||
test.AddAttribute("mode", "linear");
|
||||
test.AddAttribute("coordinate_transformation_mode", "align_corners");
|
||||
|
||||
const int64_t N = 1, C = 1, H = 2, W = 4;
|
||||
std::vector<float> X = {
|
||||
1.0f, 2.0f, 3.0f, 4.0f,
|
||||
5.0f, 6.0f, 7.0f, 8.0f};
|
||||
const int64_t N = 1, C = 1, H = 2, W = 4;
|
||||
std::vector<float> X = {
|
||||
1.0f, 2.0f, 3.0f, 4.0f,
|
||||
5.0f, 6.0f, 7.0f, 8.0f};
|
||||
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales);
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales, scales_in_initializer);
|
||||
|
||||
std::vector<float> Y = {1.0f, 4.0f};
|
||||
std::vector<float> Y = {1.0f, 4.0f};
|
||||
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run();
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run();
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
|
||||
#ifdef USE_NNAPI
|
||||
// NNAPI will need the scales as an initializer
|
||||
// Also tensor RT EP will fail if scales is an initializer but will pass if it is not
|
||||
run_test(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_2DBilinear_pytorch_half_pixel) {
|
||||
|
|
@ -139,37 +215,43 @@ TEST(ResizeOpTest, ResizeOpLinearDownSampleTest_2DBilinear_pytorch_half_pixel) {
|
|||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearUpSampleTest_4DBilinear_asymmetric) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 2.0f, 4.0f};
|
||||
// To test NNAPI EP, we need the sclaes/sizes to be in initializers
|
||||
auto run_test = [](bool scales_in_initializer) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 2.0f, 4.0f};
|
||||
|
||||
test.AddAttribute("mode", "linear");
|
||||
test.AddAttribute("coordinate_transformation_mode", "asymmetric");
|
||||
test.AddAttribute("mode", "linear");
|
||||
test.AddAttribute("coordinate_transformation_mode", "asymmetric");
|
||||
|
||||
const int64_t N = 2, C = 1, H = 2, W = 2;
|
||||
std::vector<float> X = {1.0f, 3.0f,
|
||||
4.0f, 8.0f,
|
||||
const int64_t N = 2, C = 1, H = 2, W = 2;
|
||||
std::vector<float> X = {1.0f, 3.0f,
|
||||
4.0f, 8.0f,
|
||||
|
||||
6.0f, 2.0f,
|
||||
7.0f, 11.0f};
|
||||
6.0f, 2.0f,
|
||||
7.0f, 11.0f};
|
||||
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales);
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales, scales_in_initializer);
|
||||
|
||||
std::vector<float> Y = {
|
||||
1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.0f, 3.0f, 3.0f,
|
||||
2.5f, 3.25f, 4.0f, 4.75f, 5.5f, 5.5f, 5.5f, 5.5f,
|
||||
4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 8.0f, 8.0f, 8.0f,
|
||||
4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 8.0f, 8.0f, 8.0f,
|
||||
std::vector<float> Y = {
|
||||
1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.0f, 3.0f, 3.0f,
|
||||
2.5f, 3.25f, 4.0f, 4.75f, 5.5f, 5.5f, 5.5f, 5.5f,
|
||||
4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 8.0f, 8.0f, 8.0f,
|
||||
4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 8.0f, 8.0f, 8.0f,
|
||||
|
||||
6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 2.0f, 2.0f, 2.0f,
|
||||
6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
|
||||
7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 11.0f, 11.0f, 11.0f,
|
||||
7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 11.0f, 11.0f, 11.0f};
|
||||
6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 2.0f, 2.0f, 2.0f,
|
||||
6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
|
||||
7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 11.0f, 11.0f, 11.0f,
|
||||
7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 11.0f, 11.0f, 11.0f};
|
||||
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run();
|
||||
test.AddOutput<float>("Y", {N, C, static_cast<int64_t>(H * scales[2]), static_cast<int64_t>(W * scales[3])}, Y);
|
||||
test.Run();
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearUpSampleTest_2DBilinear_align_corners) {
|
||||
|
|
@ -257,30 +339,36 @@ TEST(ResizeOpTest, ResizeOpLinearUpSampleTest_5DTrilinear_pytorch_half_pixel) {
|
|||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpLinearScalesNoOpTest) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
test.AddAttribute("mode", "linear");
|
||||
// To test NNAPI EP, we need the sclaes/sizes to be in initializers
|
||||
auto run_test = [](bool scales_in_initializer) {
|
||||
OpTester test("Resize", 11);
|
||||
std::vector<float> roi{};
|
||||
std::vector<float> scales{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
test.AddAttribute("mode", "linear");
|
||||
|
||||
const int64_t N = 2, C = 1, H = 2, W = 2;
|
||||
std::vector<float> X = {1.0f, 3.0f,
|
||||
4.0f, 8.0f,
|
||||
const int64_t N = 2, C = 1, H = 2, W = 2;
|
||||
std::vector<float> X = {1.0f, 3.0f,
|
||||
4.0f, 8.0f,
|
||||
|
||||
6.0f, 2.0f,
|
||||
7.0f, 11.0f};
|
||||
6.0f, 2.0f,
|
||||
7.0f, 11.0f};
|
||||
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales);
|
||||
test.AddInput<float>("X", {N, C, H, W}, X);
|
||||
test.AddInput<float>("roi", {0}, roi);
|
||||
test.AddInput<float>("scales", {4}, scales, scales_in_initializer);
|
||||
|
||||
std::vector<float> Y = {1.0f, 3.0f,
|
||||
4.0f, 8.0f,
|
||||
std::vector<float> Y = {1.0f, 3.0f,
|
||||
4.0f, 8.0f,
|
||||
|
||||
6.0f, 2.0f,
|
||||
7.0f, 11.0f};
|
||||
6.0f, 2.0f,
|
||||
7.0f, 11.0f};
|
||||
|
||||
test.AddOutput<float>("Y", {N, C, H, W}, Y);
|
||||
test.Run();
|
||||
test.AddOutput<float>("Y", {N, C, H, W}, Y);
|
||||
test.Run();
|
||||
};
|
||||
|
||||
run_test(false);
|
||||
run_test(true);
|
||||
}
|
||||
|
||||
TEST(ResizeOpTest, ResizeOpNearestDownSampleTest) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue