diff --git a/docs/ops/image/Interpolate_11.md b/docs/ops/image/Interpolate_11.md index 05014c8a153bf5..7f91fbd85973cc 100644 --- a/docs/ops/image/Interpolate_11.md +++ b/docs/ops/image/Interpolate_11.md @@ -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* @@ -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* @@ -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* @@ -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** @@ -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** diff --git a/src/core/include/openvino/op/util/interpolate_base.hpp b/src/core/include/openvino/op/util/interpolate_base.hpp index 93a9efe9d4b88d..ac4a897a1a7c50 100644 --- a/src/core/include/openvino/op/util/interpolate_base.hpp +++ b/src/core/include/openvino/op/util/interpolate_base.hpp @@ -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; diff --git a/src/core/shape_inference/include/interpolate_shape_inference.hpp b/src/core/shape_inference/include/interpolate_shape_inference.hpp index d18e49a0c1b382..52743ceb3142fb 100644 --- a/src/core/shape_inference/include/interpolate_shape_inference.hpp +++ b/src/core/shape_inference/include/interpolate_shape_inference.hpp @@ -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) { @@ -218,6 +218,10 @@ void shape_infer(const Interpolate* op, if (op->get_attrs().shape_calculation_mode == Interpolate::ShapeCalcMode::SCALES) { std::vector scales; if (get_data_as_float(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) { @@ -227,6 +231,10 @@ void shape_infer(const Interpolate* op, } else { T target_spatial_shape; if (get_data_as_shape(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++]; diff --git a/src/core/tests/type_prop/interpolate.cpp b/src/core/tests/type_prop/interpolate.cpp index 7f0f5ff3a5bb68..d4c876c1a91512 100644 --- a/src/core/tests/type_prop/interpolate.cpp +++ b/src/core/tests/type_prop/interpolate.cpp @@ -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(element::f32, PartialShape{1, 3, {1, 10}, {1, 10}}); + const auto scales = op::Constant::create(element::f32, Shape{2}, {2.0f, 3.0f}); + const auto axes = op::Constant::create(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(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(element::f32, PartialShape{1, 3, {1, 10}, {1, 10}}); + const auto sizes = op::Constant::create(element::i32, Shape{2}, {200, 300}); + const auto axes = op::Constant::create(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(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(element::f32, PartialShape{1, 200, 100, 3}); + const auto param = std::make_shared(element::f32, PartialShape{37, 21}); + const auto sizes = std::make_shared(param); + const auto axes = op::Constant::create(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(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(element::f32, Shape{1, 3, 30, 60}); + const auto scales = op::Constant::create(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(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(element::f32, Shape{1, 3, 30, 60}); + const auto sizes = op::Constant::create(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(image, sizes, attrs), + ov::NodeValidationFailure, + HasSubstr("The number of elements in the 'scales_or_sizes' input does not match the number of axes")); +}