diff --git a/src/core/include/openvino/op/psroi_pooling.hpp b/src/core/include/openvino/op/psroi_pooling.hpp index 39610f8bd06..9f35139ac03 100644 --- a/src/core/include/openvino/op/psroi_pooling.hpp +++ b/src/core/include/openvino/op/psroi_pooling.hpp @@ -43,21 +43,56 @@ public: std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; + /** + * @brief Set the output channel dimension size. + * @param output_dim Channel dimension size. + */ + void set_output_dim(size_t output_dim); size_t get_output_dim() const { return m_output_dim; } + + /** + * @brief Set the output groups number. + * @param group_size Number of groups. + */ + void set_group_size(size_t group_size); size_t get_group_size() const { return m_group_size; } + + /** + * @brief Set the spatial scale. + * @param scale Spatial scale value. + */ + void set_spatial_scale(float scale); float get_spatial_scale() const { return m_spatial_scale; } + + /** + * @brief Set the number of bins over image width. + * @param x Number of bins over width (x) axis. + */ + void set_spatial_bins_x(int x); int get_spatial_bins_x() const { return m_spatial_bins_x; } + + /** + * @brief Set the number of bins over image height. + * @param y Number of bins over height (y) axis. + */ + void set_spatial_bins_y(int y); int get_spatial_bins_y() const { return m_spatial_bins_y; } + + /** + * @brief Set the pooling mode. + * @param mode Pooling mode name. + */ + void set_mode(std::string mode); const std::string& get_mode() const { return m_mode; } diff --git a/src/core/shape_inference/include/psroi_pooling_shape_inference.hpp b/src/core/shape_inference/include/psroi_pooling_shape_inference.hpp new file mode 100644 index 00000000000..c47b1211e66 --- /dev/null +++ b/src/core/shape_inference/include/psroi_pooling_shape_inference.hpp @@ -0,0 +1,99 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "compare.hpp" +#include "dimension_util.hpp" +#include "openvino/op/roi_pooling.hpp" +#include "roi_pooling_shape_inference.hpp" + +namespace ov { +namespace op { +namespace psroi_pooling { +namespace validate { +template +void feat_input_shape(const TROIPooling* op, const TShape feat_shape) { + using namespace ov::util; + + roi_pooling::validate::feat_intput_shape(op, feat_shape); + + if (feat_shape.rank().is_static()) { + const auto& mode = op->get_mode(); + const auto& num_channels = feat_shape[1]; + if (mode == "average") { + const auto group_area = op->get_group_size() * op->get_group_size(); + NODE_VALIDATION_CHECK( + op, + num_channels.compatible(group_area * op->get_output_dim()), + "Number of input's channels must be a multiply of output_dim * group_size * group_size"); + } else if (mode == "bilinear") { + const auto bins_area = op->get_spatial_bins_x() * op->get_spatial_bins_y(); + NODE_VALIDATION_CHECK( + op, + num_channels.compatible(bins_area * op->get_output_dim()), + "Number of input's channels must be a multiply of output_dim * spatial_bins_x * spatial_bins_y"); + } + } +} + +template +void output_group_attr(const TROIPooling* op) { + NODE_VALIDATION_CHECK(op, op->get_group_size() > 0, "group_size has to be greater than 0"); +} + +template +void bins_attr(const TROIPooling* op) { + if (op->get_mode() == "bilinear") { + NODE_VALIDATION_CHECK(op, op->get_spatial_bins_x() > 0, "spatial_bins_x has to be greater than 0"); + NODE_VALIDATION_CHECK(op, op->get_spatial_bins_y() > 0, "spatial_bins_y has to be greater than 0"); + } +} + +template +void mode_attr(const TROIPooling* op) { + const auto& mode = op->get_mode(); + NODE_VALIDATION_CHECK(op, + mode == "average" || mode == "bilinear", + "Expected 'average' or 'bilinear' mode. Got " + mode); +} +} // namespace validate +} // namespace psroi_pooling + +namespace v0 { +template +std::vector shape_infer(const PSROIPooling* op, const std::vector& input_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 2); + using namespace ov::util; + + const auto& feat_shape = input_shapes[0]; + const auto& rois_shape = input_shapes[1]; + + psroi_pooling::validate::mode_attr(op); + psroi_pooling::validate::output_group_attr(op); + psroi_pooling::validate::bins_attr(op); + roi_pooling::validate::scale_attr(op); + + psroi_pooling::validate::feat_input_shape(op, feat_shape); + roi_pooling::validate::rois_input_shape(op, rois_shape); + + TShape out_shape; + out_shape.reserve(4); + + out_shape.emplace_back(rois_shape.rank().is_static() ? rois_shape[0] : dim::inf_bound); + out_shape.emplace_back(op->get_output_dim()); + out_shape.insert(out_shape.end(), 2, op->get_group_size()); + + return {out_shape}; +} + +template +void shape_infer(const PSROIPooling* op, const std::vector& input_shapes, std::vector& output_shapes) { + output_shapes = shape_infer(op, input_shapes); +} +} // namespace v0 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/roi_pooling_shape_inference.hpp b/src/core/shape_inference/include/roi_pooling_shape_inference.hpp index 1568ce3cbe9..0de62e9c090 100644 --- a/src/core/shape_inference/include/roi_pooling_shape_inference.hpp +++ b/src/core/shape_inference/include/roi_pooling_shape_inference.hpp @@ -12,8 +12,16 @@ namespace ov { namespace op { -namespace pooling { +namespace roi_pooling { namespace validate { +template +void feat_intput_shape(const TROIPooling* op, const TShape& feat_shape) { + NODE_VALIDATION_CHECK(op, + feat_shape.rank().compatible(4), + "Expected a 4D tensor for the feature maps input. Got: ", + feat_shape); +} + template void rois_input_shape(const TROIPooling* op, const TShape rois_shape) { if (rois_shape.rank().is_static()) { @@ -66,7 +74,7 @@ void method_attr(const TROIPooling* op) { method); } } // namespace validate -} // namespace pooling +} // namespace roi_pooling namespace v0 { template @@ -78,15 +86,11 @@ std::vector shape_infer(const ROIPooling* op, const std::vector& const auto& rois_shape = input_shapes[1]; const auto& feat_rank = feat_shape.rank(); - NODE_VALIDATION_CHECK(op, - feat_rank.compatible(4), - "Expected a 4D tensor for the feature maps input. Got: ", - feat_shape); - - pooling::validate::rois_input_shape(op, rois_shape); - pooling::validate::output_roi_attr(op); - pooling::validate::scale_attr(op); - pooling::validate::method_attr(op); + roi_pooling::validate::feat_intput_shape(op, feat_shape); + roi_pooling::validate::rois_input_shape(op, rois_shape); + roi_pooling::validate::output_roi_attr(op); + roi_pooling::validate::scale_attr(op); + roi_pooling::validate::method_attr(op); TShape out_shape; out_shape.reserve(4); diff --git a/src/core/src/op/psroi_pooling.cpp b/src/core/src/op/psroi_pooling.cpp index 1b211567980..44173d09612 100644 --- a/src/core/src/op/psroi_pooling.cpp +++ b/src/core/src/op/psroi_pooling.cpp @@ -2,22 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/psroi_pooling.hpp" +#include "openvino/op/psroi_pooling.hpp" #include "itt.hpp" -#include "ngraph/attribute_visitor.hpp" +#include "openvino/core/attribute_visitor.hpp" +#include "openvino/core/validation_util.hpp" +#include "psroi_pooling_shape_inference.hpp" using namespace std; -using namespace ngraph; -ov::op::v0::PSROIPooling::PSROIPooling(const Output& input, - const Output& coords, - const size_t output_dim, - const size_t group_size, - const float spatial_scale, - int spatial_bins_x, - int spatial_bins_y, - const string& mode) +namespace ov { +namespace op { +namespace v0 { + +PSROIPooling::PSROIPooling(const Output& input, + const Output& coords, + const size_t output_dim, + const size_t group_size, + const float spatial_scale, + int spatial_bins_x, + int spatial_bins_y, + const string& mode) : Op({input, coords}), m_output_dim(output_dim), m_group_size(group_size), @@ -28,7 +33,7 @@ ov::op::v0::PSROIPooling::PSROIPooling(const Output& input, constructor_validate_and_infer_types(); } -bool ngraph::op::v0::PSROIPooling::visit_attributes(AttributeVisitor& visitor) { +bool PSROIPooling::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v0_PSROIPooling_visit_attributes); visitor.on_attribute("output_dim", m_output_dim); visitor.on_attribute("group_size", m_group_size); @@ -39,70 +44,22 @@ bool ngraph::op::v0::PSROIPooling::visit_attributes(AttributeVisitor& visitor) { return true; } -void ov::op::v0::PSROIPooling::validate_and_infer_types() { +void PSROIPooling::validate_and_infer_types() { OV_OP_SCOPE(v0_PSROIPooling_validate_and_infer_types); - auto feat_maps_et = get_input_element_type(0); - auto coords_et = get_input_element_type(1); + const auto& feat_maps_et = get_input_element_type(0); + const auto& coords_et = get_input_element_type(1); NODE_VALIDATION_CHECK(this, feat_maps_et.is_real(), "Feature maps' data type must be floating point. Got " + feat_maps_et.get_type_name()); NODE_VALIDATION_CHECK(this, coords_et.is_real(), "Coords' data type must be floating point. Got " + coords_et.get_type_name()); - NODE_VALIDATION_CHECK(this, - m_mode == "average" || m_mode == "bilinear", - "Expected 'average' or 'bilinear' mode. Got " + m_mode); - NODE_VALIDATION_CHECK(this, m_group_size > 0, "group_size has to be greater than 0"); - if (m_mode == "bilinear") { - NODE_VALIDATION_CHECK(this, m_spatial_bins_x > 0, "spatial_bins_x has to be greater than 0"); - NODE_VALIDATION_CHECK(this, m_spatial_bins_y > 0, "spatial_bins_y has to be greater than 0"); - } - const ov::PartialShape& feat_map_pshape = get_input_partial_shape(0); - const ov::PartialShape& coords_pshape = get_input_partial_shape(1); - if (feat_map_pshape.rank().is_dynamic() || coords_pshape.rank().is_dynamic()) { - set_output_type(0, feat_maps_et, ov::PartialShape::dynamic()); - } else { - NODE_VALIDATION_CHECK(this, - feat_map_pshape.rank().get_length() == 4, - "PSROIPooling expects 4 dimensions for input. Got ", - feat_map_pshape.rank().get_length()); - NODE_VALIDATION_CHECK(this, - coords_pshape.rank().get_length() == 2, - "PSROIPooling expects 2 dimensions for box coordinates. Got ", - coords_pshape.rank().get_length()); - - if (feat_map_pshape[1].is_static()) { - auto num_input_channels = feat_map_pshape[1].get_interval().get_min_val(); - if (m_mode == "average") { - NODE_VALIDATION_CHECK(this, - num_input_channels % (m_group_size * m_group_size) == 0, - "Number of input's channels must be a multiply of group_size * group_size"); - NODE_VALIDATION_CHECK(this, - m_output_dim == num_input_channels / (m_group_size * m_group_size), - "output_dim must be equal to input channels divided by " - "group_size * group_size"); - } else if (m_mode == "bilinear") { - NODE_VALIDATION_CHECK(this, - num_input_channels % (m_spatial_bins_x * m_spatial_bins_y) == 0, - "Number of input's channels must be a multiply of " - "spatial_bins_x * spatial_bins_y"); - NODE_VALIDATION_CHECK( - this, - m_output_dim == static_cast(num_input_channels / (m_spatial_bins_x * m_spatial_bins_y)), - "output_dim must be equal to input channels divided by " - "spatial_bins_x * spatial_bins_y"); - } - } - std::vector output_shape{coords_pshape[0], static_cast(m_output_dim)}; - for (int64_t i = 2; i < feat_map_pshape.rank().get_length(); i++) { - output_shape.emplace_back(m_group_size); - } - set_output_type(0, feat_maps_et, output_shape); - } + const auto output_shapes = shape_infer(this, get_node_input_partial_shapes(*this)); + set_output_type(0, feat_maps_et, output_shapes[0]); } -shared_ptr ov::op::v0::PSROIPooling::clone_with_new_inputs(const OutputVector& new_args) const { +shared_ptr PSROIPooling::clone_with_new_inputs(const OutputVector& new_args) const { OV_OP_SCOPE(v0_PSROIPooling_clone_with_new_inputs); check_new_args_count(this, new_args); return make_shared(new_args.at(0), @@ -114,3 +71,30 @@ shared_ptr ov::op::v0::PSROIPooling::clone_with_new_inputs(const OutputVec m_spatial_bins_y, m_mode); } + +void PSROIPooling::set_output_dim(size_t output_dim) { + m_output_dim = output_dim; +} + +void PSROIPooling::set_group_size(size_t group_size) { + m_group_size = group_size; +} + +void PSROIPooling::set_spatial_scale(float scale) { + m_spatial_scale = scale; +} + +void PSROIPooling::set_spatial_bins_x(int x) { + m_spatial_bins_x = x; +} + +void PSROIPooling::set_spatial_bins_y(int y) { + m_spatial_bins_y = y; +} + +void PSROIPooling::set_mode(std::string mode) { + m_mode = std::move(mode); +} +} // namespace v0 +} // namespace op +} // namespace ov diff --git a/src/core/tests/type_prop/psroi_pooling.cpp b/src/core/tests/type_prop/psroi_pooling.cpp index 77df086f1f0..7d60b9babb4 100644 --- a/src/core/tests/type_prop/psroi_pooling.cpp +++ b/src/core/tests/type_prop/psroi_pooling.cpp @@ -2,224 +2,265 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/psroi_pooling.hpp" - +#include "common_test_utils/test_assertions.hpp" #include "gtest/gtest.h" -#include "ngraph/ngraph.hpp" -#include "util/type_prop.hpp" +#include "openvino/opsets/opset11.hpp" +#include "type_prop.hpp" -using namespace ngraph; +using namespace ov; +using namespace ov::opset11; +using namespace testing; -TEST(type_prop, psroi_pooling_average) { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - ASSERT_EQ(op->get_shape(), (Shape{150, 2, 6, 6})); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); +class TypePropPSROIPoolingV0 : public TypePropOpTest { +protected: + float spatial_scale = 0.625f; + int bin_not_used = 0; + Shape pooling_roi_2x2{2, 2}; +}; + +TEST_F(TypePropPSROIPoolingV0, basic_average) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, bin_not_used, bin_not_used, "average"); + + EXPECT_EQ(op->get_shape(), (Shape{150, 2, 6, 6})); + EXPECT_EQ(op->get_element_type(), element::f32); } -TEST(type_prop, psroi_pooling_bilinear) { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 18, 6, 1.0f, 2, 2, "bilinear"); - ASSERT_EQ(op->get_shape(), (Shape{150, 18, 6, 6})); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); +TEST_F(TypePropPSROIPoolingV0, basic_bilinear) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + auto op = make_op(inputs, coords, 18, 6, 1.0f, 2, 2, "bilinear"); + + EXPECT_EQ(op->get_shape(), (Shape{150, 18, 6, 6})); + EXPECT_EQ(op->get_element_type(), element::f32); } -TEST(type_prop, psroi_pooling_invalid_type) { - try { - auto inputs = std::make_shared(element::Type_t::i32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("Feature maps' data type must be floating point")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } +TEST_F(TypePropPSROIPoolingV0, invalid_features_element_type) { + const auto inputs = std::make_shared(element::i32, Shape{1, 72, 4, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::i32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("Coords' data type must be floating point")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 6, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("Feature maps' data type must be floating point")); } -TEST(type_prop, psroi_pooling_invalid_mode) { - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "invalid_mode"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("Expected 'average' or 'bilinear' mode")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } +TEST_F(TypePropPSROIPoolingV0, invalid_rois_element_type) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 5}); + const auto coords = std::make_shared(element::u16, Shape{150, 5}); + + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 6, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("Coords' data type must be floating point")); } -TEST(type_prop, psroi_pooling_invalid_shapes) { - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("PSROIPooling expects 4 dimensions for input")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } +TEST_F(TypePropPSROIPoolingV0, invalid_pooling_mode) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 1, 72, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("PSROIPooling expects 2 dimensions for box coordinates")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 6, spatial_scale, bin_not_used, bin_not_used, "invalid"), + NodeValidationFailure, + HasSubstr("Expected 'average' or 'bilinear' mode")); } -TEST(type_prop, psroi_pooling_invalid_group_size) { - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 0, 1.0f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("group_size has to be greater than 0")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } +TEST_F(TypePropPSROIPoolingV0, invalid_features_rank) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 5, 1.0f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), - std::string("Number of input's channels must be a multiply of group_size * group_size")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 6, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("Expected a 4D tensor for the feature maps input")); } -TEST(type_prop, psroi_pooling_invalid_output_dim) { - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 17, 2, 1.0f, 0, 0, "average"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING( - error.what(), - std::string("output_dim must be equal to input channels divided by group_size * group_size")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } +TEST_F(TypePropPSROIPoolingV0, invalid_rois_rank) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 2}); + const auto coords = std::make_shared(element::f32, Shape{150}); + + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 6, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("Expected a 2D tensor for the ROIs input with box coordinates")); } -TEST(type_prop, psroi_pooling_invalid_spatial_bins) { - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 17, 2, 1.0f, 0, 0, "bilinear"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("spatial_bins_x has to be greater than 0")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } +TEST_F(TypePropPSROIPoolingV0, invalid_group_size) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 2}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 17, 2, 1.0f, 1, 0, "bilinear"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("spatial_bins_y has to be greater than 0")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } - - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 17, 2, 1.0f, 2, 5, "bilinear"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), - std::string("Number of input's channels must be a multiply of " - "spatial_bins_x * spatial_bins_y")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } - - try { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 5, 5}); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 10, 2, 1.0f, 2, 4, "bilinear"); - FAIL() << "Exception expected"; - } catch (const NodeValidationFailure& error) { - EXPECT_HAS_SUBSTRING(error.what(), - std::string("output_dim must be equal to input channels divided by " - "spatial_bins_x * spatial_bins_y")); - } catch (...) { - FAIL() << "Unknown exception was thrown"; - } + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 0, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("group_size has to be greater than 0")); } -TEST(type_prop, psroi_pooling_dynamic_ranks) { - { - auto inputs = std::make_shared(element::Type_t::f32, PartialShape::dynamic()); - auto coords = std::make_shared(element::Type_t::f32, Shape{150, 5}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - ASSERT_EQ(op->get_output_partial_shape(0), PartialShape::dynamic()); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); - } - { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::f32, PartialShape::dynamic()); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - ASSERT_EQ(op->get_output_partial_shape(0), PartialShape::dynamic()); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); - } +TEST_F(TypePropPSROIPoolingV0, invalid_number_of_channels_and_group_size_in_avg_mode) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 2}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 2, 5, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("Number of input's channels must be a multiply of output_dim * group_size * group_size")); } -TEST(type_prop, psroi_pooling_dynamic_num_boxes) { - auto inputs = std::make_shared(element::Type_t::f32, Shape{1, 72, 4, 5}); - auto coords = std::make_shared(element::Type_t::f32, PartialShape{{Dimension::dynamic(), 5}}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{{Dimension::dynamic(), 2, 6, 6}})); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); +TEST_F(TypePropPSROIPoolingV0, invalid_output_dim_in_avg_mode) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 4, 2}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 17, 2, spatial_scale, bin_not_used, bin_not_used, "average"), + NodeValidationFailure, + HasSubstr("Number of input's channels must be a multiply of output_dim * group_size * group_size")); } -TEST(type_prop, psroi_pooling_static_rank_dynamic_shape) { - { - auto inputs = std::make_shared( - element::Type_t::f32, - PartialShape{{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()}}); - auto coords = std::make_shared(element::Type_t::f32, - PartialShape{{Dimension::dynamic(), Dimension::dynamic()}}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{{Dimension::dynamic(), 2, 6, 6}})); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); - } - { - auto inputs = std::make_shared( - element::Type_t::f32, - PartialShape{{Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic(), Dimension::dynamic()}}); - auto coords = std::make_shared(element::Type_t::f32, PartialShape{{200, Dimension::dynamic()}}); - auto op = std::make_shared(inputs, coords, 2, 6, 0.0625f, 0, 0, "average"); - ASSERT_EQ(op->get_shape(), (Shape{200, 2, 6, 6})); - ASSERT_EQ(op->get_element_type(), element::Type_t::f32); - } +TEST_F(TypePropPSROIPoolingV0, invalid_spatial_bins_x) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 5, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 17, 2, spatial_scale, 0, 1, "bilinear"), + NodeValidationFailure, + HasSubstr("spatial_bins_x has to be greater than 0")); +} + +TEST_F(TypePropPSROIPoolingV0, invalid_spatial_bins_y) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 5, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + OV_EXPECT_THROW(auto op = make_op(inputs, coords, 17, 2, spatial_scale, 1, 0, "bilinear"), + NodeValidationFailure, + HasSubstr("spatial_bins_y has to be greater than 0")); +} + +TEST_F(TypePropPSROIPoolingV0, invalid_number_of_channels_and_spatial_bins_in_bilinear_mode) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 5, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + OV_EXPECT_THROW( + auto op = make_op(inputs, coords, 17, 2, spatial_scale, 2, 5, "bilinear"), + NodeValidationFailure, + HasSubstr("Number of input's channels must be a multiply of output_dim * spatial_bins_x * spatial_bins_y")); +} + +TEST_F(TypePropPSROIPoolingV0, invalid_output_dim_in_bilinear_mode) { + const auto inputs = std::make_shared(element::f32, Shape{1, 72, 5, 5}); + const auto coords = std::make_shared(element::f32, Shape{150, 5}); + + OV_EXPECT_THROW( + auto op = make_op(inputs, coords, 10, 2, spatial_scale, 2, 4, "bilinear"), + NodeValidationFailure, + HasSubstr("Number of input's channels must be a multiply of output_dim * spatial_bins_x * spatial_bins_y")); +} + +TEST_F(TypePropPSROIPoolingV0, features_dynamic_rank) { + auto coords_shape = PartialShape{150, 5}; + set_shape_labels(coords_shape, 20); + + const auto inputs = std::make_shared(element::f16, PartialShape::dynamic()); + const auto coords = std::make_shared(element::f16, coords_shape); + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, 0, 0, "average"); + + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({150, 2, 6, 6})); // 4d + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), + ElementsAre(20, ov::no_label, ov::no_label, ov::no_label)); +} + +TEST_F(TypePropPSROIPoolingV0, rois_dynamic_rank) { + auto feat_shape = PartialShape{1, 72, 4, 5}; + set_shape_labels(feat_shape, 10); + + const auto inputs = std::make_shared(element::f16, feat_shape); + const auto coords = std::make_shared(element::f16, PartialShape::dynamic()); + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, 0, 0, "average"); + + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, 2, 6, 6})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(ov::no_label)); +} + +TEST_F(TypePropPSROIPoolingV0, dynamic_num_boxes) { + auto coords_shape = PartialShape{{Dimension::dynamic(), 5}}; + set_shape_labels(coords_shape, 20); + + const auto inputs = std::make_shared(element::f16, PartialShape::dynamic()); + const auto coords = std::make_shared(element::f16, coords_shape); + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, 0, 0, "average"); + + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, 2, 6, 6})); + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), + ElementsAre(20, ov::no_label, ov::no_label, ov::no_label)); +} + +TEST_F(TypePropPSROIPoolingV0, feat_static_rank_dynamic_shape) { + auto feat_shape = PartialShape::dynamic(4); + auto coords_shape = PartialShape{{Dimension::dynamic(), 5}}; + set_shape_labels(feat_shape, 10); + set_shape_labels(coords_shape, 20); + + const auto inputs = std::make_shared(element::f16, feat_shape); + const auto coords = std::make_shared(element::f16, coords_shape); + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, 0, 0, "average"); + + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, 2, 6, 6})); // 4d + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), + ElementsAre(20, ov::no_label, ov::no_label, ov::no_label)); +} + +TEST_F(TypePropPSROIPoolingV0, feat_and_rois_static_rank_dynamic_shape) { + auto feat_shape = PartialShape::dynamic(4); + auto coords_shape = PartialShape::dynamic(2); + set_shape_labels(feat_shape, 10); + set_shape_labels(coords_shape, 20); + + const auto inputs = std::make_shared(element::f16, feat_shape); + const auto coords = std::make_shared(element::f16, coords_shape); + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, 0, 0, "average"); + + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, 2, 6, 6})); // 4d + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), + ElementsAre(20, ov::no_label, ov::no_label, ov::no_label)); +} + +TEST_F(TypePropPSROIPoolingV0, feat_and_rois_interval_shapes) { + auto feat_shape = PartialShape{{1, 2}, {10, 100}, {10, 20}, {30, 90}}; + auto coords_shape = PartialShape{{3, 10}, {1, 5}}; + set_shape_labels(feat_shape, 10); + set_shape_labels(coords_shape, 20); + + const auto inputs = std::make_shared(element::f16, feat_shape); + const auto coords = std::make_shared(element::f16, coords_shape); + const auto op = make_op(inputs, coords, 2, 6, spatial_scale, 0, 0, "average"); + + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({{3, 10}, 2, 6, 6})); // 4d + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), + ElementsAre(20, ov::no_label, ov::no_label, ov::no_label)); +} + +TEST_F(TypePropPSROIPoolingV0, default_ctor) { + auto feat_shape = PartialShape{2, {10, 100}, 10, 10}; + auto coords_shape = PartialShape{{3, 10}, {1, 5}}; + set_shape_labels(feat_shape, 10); + set_shape_labels(coords_shape, 20); + + const auto inputs = std::make_shared(element::f16, feat_shape); + const auto coords = std::make_shared(element::f16, coords_shape); + + const auto op = make_op(); + op->set_arguments(OutputVector{inputs, coords}); + op->set_output_dim(2); + op->set_group_size(6); + op->set_spatial_scale(spatial_scale); + op->set_mode("average"); + op->validate_and_infer_types(); + + EXPECT_FLOAT_EQ(op->get_spatial_scale(), spatial_scale); + EXPECT_EQ(op->get_mode(), "average"); + EXPECT_EQ(op->get_group_size(), 6); + EXPECT_EQ(op->get_input_size(), 2); + EXPECT_EQ(op->get_output_size(), 1); + EXPECT_EQ(op->get_element_type(), element::f16); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({{3, 10}, 2, 6, 6})); // 4d + EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), + ElementsAre(20, ov::no_label, ov::no_label, ov::no_label)); } diff --git a/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp b/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp index 1961157ae93..5cc51a792b9 100644 --- a/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp +++ b/src/plugins/intel_cpu/src/utils/shape_inference/shape_inference.cpp @@ -53,6 +53,7 @@ #include "one_hot_shape_inference.hpp" #include "pad_shape_inference.hpp" #include "proposal_shape_inference.hpp" +#include "psroi_pooling_shape_inference.hpp" #include "range_shape_inference.hpp" #include "rdft_shape_inference.hpp" #include "read_value_shape_inference.hpp" @@ -589,6 +590,7 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{ _OV_OP_SHAPE_INFER_REG(ov::op::internal::AUGRUSequence, entryIO), _OV_OP_SHAPE_INFER_REG(Pad, entryIOC), _OV_OP_SHAPE_INFER_REG(Proposal, entryIO), + _OV_OP_SHAPE_INFER_REG(PSROIPooling, entryIO), _OV_OP_SHAPE_INFER_REG(Range, entryIOC), _OV_OP_SHAPE_INFER_REG(RDFT, entryIOC), _OV_OP_SHAPE_INFER_REG(ReadValue, entryIO), diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/psroi_pooling_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/psroi_pooling_shape_inference_test.cpp new file mode 100644 index 00000000000..b5b0b6ca294 --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/psroi_pooling_shape_inference_test.cpp @@ -0,0 +1,97 @@ +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "openvino/opsets/opset11.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +class PSROIPoolingV0StaticShapeInferenceTest : public OpStaticShapeInferenceTest { +protected: + void SetUp() override { + output_shapes.resize(1); + } + + float scale = 0.45f; + size_t group = 3; + int bins_x = 4; + int bins_y = 3; +}; + +TEST_F(PSROIPoolingV0StaticShapeInferenceTest, default_ctor_avg_mode) { + op = make_op(); + op->set_output_dim(5); + op->set_group_size(3); + op->set_spatial_scale(scale); + op->set_mode("average"); + + input_shapes = ShapeVector{{1, 45, 10, 10}, {3, 5}}; + auto shape_infer = make_shape_inference(op); + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({3, 5, 3, 3})); +} + +TEST_F(PSROIPoolingV0StaticShapeInferenceTest, default_ctor_bilinear_mode) { + op = make_op(); + op->set_output_dim(5); + op->set_group_size(8); + op->set_spatial_bins_x(5); + op->set_spatial_bins_y(3); + op->set_spatial_scale(scale); + op->set_mode("bilinear"); + + input_shapes = ShapeVector{{1, 75, 10, 10}, {2, 5}}; + auto shape_infer = make_shape_inference(op); + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({2, 5, 8, 8})); +} + +TEST_F(PSROIPoolingV0StaticShapeInferenceTest, inputs_dynamic_rank) { + const auto feat = std::make_shared(element::f64, PartialShape::dynamic()); + const auto rois = std::make_shared(element::f64, PartialShape::dynamic()); + + op = make_op(feat, rois, 4, group, scale, 0, 0, "average"); + + input_shapes = ShapeVector{{2, 36, 100, 100}, {10, 5}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({10, 4, 3, 3})); +} + +TEST_F(PSROIPoolingV0StaticShapeInferenceTest, inputs_static_rank) { + const auto feat = std::make_shared(element::f64, PartialShape::dynamic(4)); + const auto rois = std::make_shared(element::f64, PartialShape::dynamic(2)); + + op = make_op(feat, rois, 2, 1, scale, bins_x, bins_y, "bilinear"); + + input_shapes = ShapeVector{{2, 24, 20, 100}, {1, 5}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({1, 2, 1, 1})); +} + +TEST_F(PSROIPoolingV0StaticShapeInferenceTest, invalid_rois_batch_size) { + const auto feat = std::make_shared(element::f64, PartialShape::dynamic(4)); + const auto rois = std::make_shared(element::f64, PartialShape::dynamic()); + + op = make_op(feat, rois, 2, 1, scale, bins_x, bins_y, "bilinear"); + + input_shapes = ShapeVector{{2, 24, 20, 100}, {1, 6}}; + + OV_EXPECT_THROW(shape_inference(op.get(), input_shapes, output_shapes), + NodeValidationFailure, + HasSubstr("The second dimension of ROIs input should contain batch id and box coordinates. This " + "dimension is expected to be equal to 5")); +}