Skip to content

Commit

Permalink
Interpolate op cleanup (openvinotoolkit#17026)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomasz Dołbniak authored Apr 19, 2023
1 parent 497a19e commit d230ad9
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 6 deletions.
10 changes: 6 additions & 4 deletions docs/ops/image/Interpolate_11.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* **Range of values**: one of `nearest`, `linear`, `linear_onnx`, `cubic`, `bilinear_pillow`, `bicubic_pillow`
* **Type**: string
* **Required**: *yes*
**Note**: Only 2D, 3D, 4D, 5D tensors with `axes = {0, 1}`, `axes = {0, 1, 2}`, `axes = {2, 3}`, `axes = {2, 3, 4}` respectively are supported for `"mode" == "linear_onnx"`.
* **Note**: Only 2D, 3D, 4D, 5D tensors with `axes = {0, 1}`, `axes = {0, 1, 2}`, `axes = {2, 3}`, `axes = {2, 3, 4}` respectively are supported for `"mode" == "linear_onnx"`. In case of `bilinear_pillow` or `bicubic_pillow` only the spatial dimensions (H, W) can be specified in the `axes` tensor, for example in case of NHWC layout the axes should contain `axes = {1, 2}`.

* *shape_calculation_mode*

Expand All @@ -37,6 +37,7 @@
* **Type**: string
* **Default value**: `half_pixel`
* **Required**: *no*
* **Note**: When the selected interpolation mode is `BILINEAR_PILLOW` or `BICUBIC_PILLOW` this attribute is ignored.

* *nearest_mode*

Expand All @@ -60,6 +61,7 @@
* **Type**: boolean
* **Default value**: false
* **Required**: *no*
* **Note**: When the selected interpolation mode is `BILINEAR_PILLOW` or `BICUBIC_PILLOW` this attribute is ignored. Pillow-kind of antialiasing is applied in those modes.

* *pads_begin*

Expand All @@ -79,10 +81,10 @@

* *cube_coeff*

* **Description**: *cube_coeff* specifies the parameter *a* for cubic interpolation (see, e.g. [article](https://ieeexplore.ieee.org/document/1163711/)). *cube_coeff* is used only when `mode == cubic`.
* **Description**: *cube_coeff* specifies the parameter *a* for cubic interpolation (see, e.g. [article](https://ieeexplore.ieee.org/document/1163711/)). *cube_coeff* is used only when `mode == cubic` or `mode == bicubic_pillow`.
* **Range of values**: floating-point number
* **Type**: any of supported floating-point type
* **Default value**: `-0.75`
* **Default value**: `-0.75` (applicable for `mode == cubic`). The value compatible with `BICUBIC_PILLOW` needs to be manually set to `-0.5`
* **Required**: *no*

**Inputs**
Expand All @@ -91,7 +93,7 @@

* **2**: `scales_or_sizes` - 1D tensor containing the data used to calculate the spatial output shape. The number of elements must match the number of values in the `axes` input tensor, the order needs to match as well. The type of this input tensor is either *T_SCALES* or *T_SIZES* depending on the value of the `shape_calculation_mode` attribute. **Required.**

* **3**: `axes` - 1D tensor of type *T_AXES* specifying dimension indices where interpolation is applied, and `axes` is any unordered list of indices of different dimensions of input tensor, e.g. `[0, 4]`, `[4, 0]`, `[4, 2, 1]`, `[1, 2, 3]`. These indices should be non-negative integers from `0` to `rank(image) - 1` inclusively. Other dimensions do not change. The order of elements in `axes` attribute matters and is mapped directly to the elements in the 2nd input `scales_or_sizes`. **Optional** with default value `[X,...,rank(image) - 1]` pointing to the spatial dimensions of the input image. The number of the default axes matches the number of elements in the `scales_or_sizes` input tensor.
* **3**: `axes` - 1D tensor of type *T_AXES* specifying dimension indices where interpolation is applied, and `axes` is any unordered list of indices of different dimensions of input tensor, e.g. `[0, 4]`, `[4, 0]`, `[4, 2, 1]`, `[1, 2, 3]`. These indices should be non-negative integers from `0` to `rank(image) - 1` inclusively. Input tensor's dimensions not specified in the `axes` tensor are not modified by the operator. The order of elements in `axes` attribute matters and is mapped directly to the elements in the 2nd input `scales_or_sizes`. **Optional** with default value `[0,1,...,rank(image) - 1]`. If the `axes` input is not provided the number of elements in the `scales_or_sizes` tensor needs to match the number of automatically generated axes.

**Outputs**

Expand Down
2 changes: 1 addition & 1 deletion src/core/include/openvino/op/util/interpolate_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class OPENVINO_API InterpolateBase : public Op {
bool antialias = false;
// Specifies the parameter *a* for the cubic interpolation .
// (see, e.g. [article](https://ieeexplore.ieee.org/document/1163711/)).
// *cube_coeff* takes effect only when `mode == CUBIC`
// *cube_coeff* takes effect only when `mode == CUBIC` or `mode == BICUBIC_PILLOW`
double cube_coeff = -0.75f;

InterpolateAttrs() = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ void shape_infer(const Interpolate* op,
[input_rank](size_t axis) {
return axis < input_rank;
}),
"Axis value should less than input rank.");
"Axis value should be smaller than the input's rank.");

// Get padded input shape
for (size_t i = 0; i < input_rank; ++i) {
Expand All @@ -218,6 +218,10 @@ void shape_infer(const Interpolate* op,
if (op->get_attrs().shape_calculation_mode == Interpolate::ShapeCalcMode::SCALES) {
std::vector<float> scales;
if (get_data_as_float<T>(1, op, scales, constant_data)) {
NODE_VALIDATION_CHECK(
op,
axes.size() == scales.size(),
"The number of elements in the 'scales_or_sizes' input does not match the number of axes");
util::infer_using_scales(output_shape, axes, scales);
} else {
for (const auto& axis : axes) {
Expand All @@ -227,6 +231,10 @@ void shape_infer(const Interpolate* op,
} else {
T target_spatial_shape;
if (get_data_as_shape<T>(1, op, target_spatial_shape, constant_data)) {
NODE_VALIDATION_CHECK(
op,
axes.size() == target_spatial_shape.size(),
"The number of elements in the 'scales_or_sizes' input does not match the number of axes");
size_t i = 0;
for (const auto& axis : axes) {
output_shape[axis] = target_spatial_shape[i++];
Expand Down
71 changes: 71 additions & 0 deletions src/core/tests/type_prop/interpolate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,74 @@ TEST(type_prop, interpolate_v11_sizes_incorrect_et) {
ov::NodeValidationFailure,
HasSubstr("Sizes element type must be i32, i64, u32 or u64"));
}

TEST(type_prop, interpolate_v11_intervals_with_scales_mode) {
const auto image = std::make_shared<op::Parameter>(element::f32, PartialShape{1, 3, {1, 10}, {1, 10}});
const auto scales = op::Constant::create<float>(element::f32, Shape{2}, {2.0f, 3.0f});
const auto axes = op::Constant::create<int64_t>(element::i64, Shape{2}, {2, 3});

ov::op::util::InterpolateBase::InterpolateAttrs attrs;
attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES;
attrs.pads_begin = {0, 0, 0, 0};
attrs.pads_end = {0, 0, 0, 0};
auto interp = std::make_shared<ov::op::v11::Interpolate>(image, scales, axes, attrs);

EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{1, 3, {2, 20}, {3, 30}}));
}

TEST(type_prop, interpolate_v11_intervals_with_sizes_mode) {
const auto image = std::make_shared<op::Parameter>(element::f32, PartialShape{1, 3, {1, 10}, {1, 10}});
const auto sizes = op::Constant::create<float>(element::i32, Shape{2}, {200, 300});
const auto axes = op::Constant::create<int64_t>(element::i64, Shape{2}, {2, 3});

ov::op::util::InterpolateBase::InterpolateAttrs attrs;
attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES;
attrs.pads_begin = {0, 0, 0, 0};
attrs.pads_end = {0, 0, 0, 0};
auto interp = std::make_shared<ov::op::v11::Interpolate>(image, sizes, axes, attrs);

EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{1, 3, 200, 300}));
}

TEST(type_prop, interpolate_v11_sizes_with_shapeof) {
const auto image = std::make_shared<op::Parameter>(element::f32, PartialShape{1, 200, 100, 3});
const auto param = std::make_shared<op::Parameter>(element::f32, PartialShape{37, 21});
const auto sizes = std::make_shared<op::ShapeOf>(param);
const auto axes = op::Constant::create<int64_t>(element::i64, Shape{2}, {2, 1});

ov::op::util::InterpolateBase::InterpolateAttrs attrs;
attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES;
attrs.pads_begin = {0, 0, 0, 0};
attrs.pads_end = {0, 0, 0, 0};
auto interp = std::make_shared<ov::op::v11::Interpolate>(image, sizes, axes, attrs);

EXPECT_EQ(interp->get_output_partial_shape(0), (PartialShape{1, 21, 37, 3}));
}

TEST(type_prop, interpolate_v11_scales_incorrect_number) {
const auto image = std::make_shared<op::Parameter>(element::f32, Shape{1, 3, 30, 60});
const auto scales = op::Constant::create<float>(element::f32, Shape{2}, {0.2f, 0.2f});

ov::op::util::InterpolateBase::InterpolateAttrs attrs;
attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SCALES;
attrs.pads_begin = {0, 0, 0, 0};
attrs.pads_end = {0, 0, 0, 0};
OV_EXPECT_THROW(
auto interp = std::make_shared<ov::op::v11::Interpolate>(image, scales, attrs),
ov::NodeValidationFailure,
HasSubstr("The number of elements in the 'scales_or_sizes' input does not match the number of axes"));
}

TEST(type_prop, interpolate_v11_sizes_incorrect_number) {
const auto image = std::make_shared<op::Parameter>(element::f32, Shape{1, 3, 30, 60});
const auto sizes = op::Constant::create<int32_t>(element::i32, Shape{2}, {6, 12});

ov::op::util::InterpolateBase::InterpolateAttrs attrs;
attrs.shape_calculation_mode = ov::op::util::InterpolateBase::ShapeCalcMode::SIZES;
attrs.pads_begin = {0, 0, 0, 0};
attrs.pads_end = {0, 0, 0, 0};
OV_EXPECT_THROW(
auto interp = std::make_shared<ov::op::v11::Interpolate>(image, sizes, attrs),
ov::NodeValidationFailure,
HasSubstr("The number of elements in the 'scales_or_sizes' input does not match the number of axes"));
}

0 comments on commit d230ad9

Please sign in to comment.