diff --git a/src/core/include/openvino/op/reorg_yolo.hpp b/src/core/include/openvino/op/reorg_yolo.hpp index 56569df4137..83cce7a8ed4 100644 --- a/src/core/include/openvino/op/reorg_yolo.hpp +++ b/src/core/include/openvino/op/reorg_yolo.hpp @@ -35,9 +35,8 @@ public: Strides get_strides() const { return m_strides; } - void set_strides(Strides strides) { - m_strides = std::move(strides); - } + + void set_strides(const size_t stride); private: Strides m_strides; diff --git a/src/core/shape_inference/include/dimension_util.hpp b/src/core/shape_inference/include/dimension_util.hpp index 8416efce6f2..e80ca96afe1 100644 --- a/src/core/shape_inference/include/dimension_util.hpp +++ b/src/core/shape_inference/include/dimension_util.hpp @@ -216,6 +216,36 @@ auto floor_div(const TDim& dim, const typename TDim::value_type divisor) -> TDim } } +/** + * @brief Check if dimension is empty. + * + * For static dimension the empty dimension is equal to zero dimension. + * + * @tparam TDim Dimension type. + * @param d Dimension for check. + * @return true if dimension is empty otherwise false. + */ +template ::type>::value>::type* = nullptr> +bool is_empty(TDim&& d) { + return d == typename std::decay::type{}; +} + +/** + * @brief Check if dimension is empty. + * + * For iv::Dimension the empty means that has no dimension at all. + * + * @tparam TDim Dimension type. + * @param d Dimension for check. + * @return true if dimension is empty otherwise false. + */ +template ::type>::value>::type* = nullptr> +bool is_empty(TDim&& d) { + return d.get_interval().empty(); +} + /** * @brief Check if dimension is evenly divisible. * @@ -226,12 +256,7 @@ auto floor_div(const TDim& dim, const typename TDim::value_type divisor) -> TDim */ template bool is_divisible(const TDim& quotient, const typename TDim::value_type dividend) { - return quotient / dividend != TDim{}; -} - -template <> -inline bool is_divisible(const Dimension& quotient, const typename Dimension::value_type dividend) { - return !(quotient / dividend).get_interval().empty(); + return !is_empty(quotient / dividend); } /** @@ -258,7 +283,6 @@ void scale(TDim& d, float scale) { } } } - } // namespace dim } // namespace util } // namespace ov diff --git a/src/core/shape_inference/include/reorg_yolo_shape_inference.hpp b/src/core/shape_inference/include/reorg_yolo_shape_inference.hpp index 52542d8d53f..3a18a3c0602 100644 --- a/src/core/shape_inference/include/reorg_yolo_shape_inference.hpp +++ b/src/core/shape_inference/include/reorg_yolo_shape_inference.hpp @@ -4,55 +4,50 @@ #pragma once -#include -#include - +#include "compare.hpp" +#include "dimension_util.hpp" +#include "openvino/op/reorg_yolo.hpp" #include "utils.hpp" + namespace ov { namespace op { namespace v0 { template > std::vector shape_infer(const ReorgYolo* op, const std::vector& input_shapes) { - NODE_VALIDATION_CHECK(op, (input_shapes.size() == 1)); + NODE_VALIDATION_CHECK(op, input_shapes.size() == 1); + using namespace ov::util; + const auto& input_shape = input_shapes[0]; + const auto& input_rank = input_shape.rank(); + const auto stride = op->get_strides().front(); + auto output_shapes = std::vector(1); auto& output_shape = output_shapes[0]; - const auto& strides = op->get_strides(); + if (input_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(op, input_shape.size() == 4, "[N, C, H, W] input shape is required."); + NODE_SHAPE_INFER_CHECK(op, input_shapes, input_shape.size() == 4, "[N, C, H, W] input shape is required."); - NODE_VALIDATION_CHECK(op, - input_shape[2].is_dynamic() || (input_shape[2].get_length() % strides[0]) == 0, - "For [N, C, H, W] input shape, H should be divisible by stride."); + const auto stride_sq = typename T::value_type(stride * stride); + NODE_SHAPE_INFER_CHECK( + op, + input_shapes, + input_shape[1].is_dynamic() || cmp::ge(input_shape[1].get_length(), stride_sq.get_length()), + "For [N, C, H, W] input shape, C >= (stride*stride) is required."); - NODE_VALIDATION_CHECK(op, - input_shape[3].is_dynamic() || (input_shape[3].get_length() % strides[0]) == 0, - "For [N, C, H, W] input shape, W should be divisible by stride."); - - NODE_VALIDATION_CHECK(op, - input_shape[1].is_dynamic() || - static_cast(input_shape[1].get_length()) >= (strides[0] * strides[0]), - "For [N, C, H, W] input shape, C >= (stride*stride) is required."); - - output_shape = TRShape({input_shape[0], input_shape[1]}); - - for (size_t i = 2; i < input_shape.size(); i++) { - if (input_shape[i].is_static()) - output_shape.push_back(input_shape[i].get_length() / strides[0]); - else { - const auto& interval = input_shape[i].get_interval(); - if (interval.has_upper_bound()) { - output_shape.push_back( - ov::Dimension(interval.get_min_val() / strides[0], interval.get_max_val() / strides[0])); - } else { - output_shape.push_back(ov::Dimension::dynamic()); - } - } - output_shape[1] *= strides[0]; + output_shape.reserve(input_shape.size()); + auto out_it = std::copy(input_shape.begin(), input_shape.begin() + 2, std::back_inserter(output_shape)); + for (size_t i = 2; i < input_shape.size(); ++i, ++out_it) { + auto d = input_shape[i] / stride; + NODE_SHAPE_INFER_CHECK(op, + input_shapes, + !dim::is_empty(d), + "For [N, C, H, W] input shape, H and W should be divisible by stride."); + out_it = std::move(d); } + output_shape[1] *= stride_sq; } else { - output_shape = ov::PartialShape::dynamic(input_shape.rank()); + output_shape = PartialShape::dynamic(input_rank); } return output_shapes; } diff --git a/src/core/src/op/reorg_yolo.cpp b/src/core/src/op/reorg_yolo.cpp index db68574c2f7..847b6709079 100644 --- a/src/core/src/op/reorg_yolo.cpp +++ b/src/core/src/op/reorg_yolo.cpp @@ -2,45 +2,48 @@ // SPDX-License-Identifier: Apache-2.0 // -#include "ngraph/op/reorg_yolo.hpp" +#include "openvino/op/reorg_yolo.hpp" #include "itt.hpp" -#include "ngraph/runtime/reference/reorg_yolo.hpp" #include "reorg_yolo_shape_inference.hpp" -using namespace std; -using namespace ngraph; - -op::ReorgYolo::ReorgYolo(const Output& input, const Strides& strides) : Op({input}), m_strides(strides) { +namespace ov { +namespace op { +namespace v0 { +ReorgYolo::ReorgYolo(const Output& input, const Strides& strides) : Op({input}), m_strides(strides) { constructor_validate_and_infer_types(); } -op::ReorgYolo::ReorgYolo(const Output& input, const size_t stride) - : Op({input}), - m_strides(std::vector{stride, stride}) { +ReorgYolo::ReorgYolo(const Output& input, const size_t stride) : Op({input}), m_strides(2, stride) { constructor_validate_and_infer_types(); } -void op::ReorgYolo::validate_and_infer_types() { +void ReorgYolo::validate_and_infer_types() { OV_OP_SCOPE(v0_ReorgYolo_validate_and_infer_types); NODE_VALIDATION_CHECK(this, !m_strides.empty(), "Stride attribute is required."); - auto input_et = get_input_element_type(0); - - std::vector input_shapes = {get_input_partial_shape(0)}; + const auto input_shapes = std::vector{get_input_partial_shape(0)}; const auto output_shapes = shape_infer(this, input_shapes); - set_output_type(0, input_et, output_shapes[0]); + set_output_type(0, get_input_element_type(0), output_shapes[0]); } -shared_ptr op::ReorgYolo::clone_with_new_inputs(const OutputVector& new_args) const { +std::shared_ptr ReorgYolo::clone_with_new_inputs(const OutputVector& new_args) const { OV_OP_SCOPE(v0_ReorgYolo_clone_with_new_inputs); check_new_args_count(this, new_args); - return make_shared(new_args.at(0), m_strides); + return std::make_shared(new_args.at(0), m_strides); } -bool op::ReorgYolo::visit_attributes(AttributeVisitor& visitor) { +bool ReorgYolo::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v0_ReorgYolo_visit_attributes); visitor.on_attribute("stride", m_strides); return true; } + +void ReorgYolo::set_strides(const size_t stride) { + m_strides.resize(2); + std::fill_n(m_strides.begin(), 2, stride); +} +} // namespace v0 +} // namespace op +} // namespace ov diff --git a/src/core/tests/type_prop/reorg_yolo.cpp b/src/core/tests/type_prop/reorg_yolo.cpp index 6a1bab2bacb..a2b6f0a1e8e 100644 --- a/src/core/tests/type_prop/reorg_yolo.cpp +++ b/src/core/tests/type_prop/reorg_yolo.cpp @@ -2,18 +2,38 @@ // SPDX-License-Identifier: Apache-2.0 // +#include + +#include "common_test_utils/test_assertions.hpp" #include "common_test_utils/type_prop.hpp" -#include "gtest/gtest.h" -#include "ngraph/ngraph.hpp" +#include "openvino/opsets/opset12.hpp" -using namespace std; -using namespace ngraph; +using namespace ov; +using namespace ov::opset12; +using namespace testing; -TEST(type_prop, reorg_yolo_stride_2) { +class TypePropReorgYoloTest : public TypePropOpTest {}; + +TEST_F(TypePropReorgYoloTest, default_ctor) { + const auto data = std::make_shared(element::f32, PartialShape{1, 4, {13, 16}, 4}); + + auto op = make_op(); + op->set_strides(2); + op->set_arguments(OutputVector{data}); + op->validate_and_infer_types(); + + EXPECT_EQ(op->get_strides(), Strides({2, 2})); + EXPECT_EQ(op->get_input_size(), 1); + EXPECT_EQ(op->get_output_size(), 1); + EXPECT_EQ(op->get_output_element_type(0), element::f32); + EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({1, 16, {7, 8}, 2})); +} + +TEST_F(TypePropReorgYoloTest, stride_2) { const auto in_shape = Shape{1, 64, 26, 26}; size_t stride = 2; - auto data_param = make_shared(element::f32, in_shape); - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); + auto reorg_yolo = make_op(data_param, stride); // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride] Shape expected_shape = Shape{1, 256, 13, 13}; @@ -21,34 +41,38 @@ TEST(type_prop, reorg_yolo_stride_2) { EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape); } -TEST(type_prop, reorg_yolo_stride_2_dynamic_shape) { +TEST_F(TypePropReorgYoloTest, stride_2_dynamic_shape) { const auto in_shape = PartialShape{-1, -1, -1, -1}; size_t stride = 2; - auto data_param = make_shared(element::f32, in_shape); - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); + auto reorg_yolo = make_op(data_param, stride); const auto expected_shape = PartialShape{-1, -1, -1, -1}; EXPECT_EQ(reorg_yolo->get_output_partial_shape(0), expected_shape); } -TEST(type_prop, reorg_yolo_stride_2_dynamic_shape_ranges) { - const auto in_shape = PartialShape{{1, 4}, {3, 9}, {16, 32}, {16, 32}}; +TEST_F(TypePropReorgYoloTest, stride_2_interval_shape_with_labels) { + auto in_shape = PartialShape{{1, 4}, {3, 9}, {16, 32}, {16, 32}}; + set_shape_labels(in_shape, 10); + size_t stride = 2; - auto data_param = make_shared(element::f32, in_shape); - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); + auto reorg_yolo = make_op(data_param, stride); // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride] const auto expected_shape = PartialShape{{1, 4}, {12, 36}, {8, 16}, {8, 16}}; EXPECT_EQ(reorg_yolo->get_output_partial_shape(0), expected_shape); + EXPECT_THAT(get_shape_labels(reorg_yolo->get_output_partial_shape(0)), + ElementsAre(10, no_label, no_label, no_label)); } -TEST(type_prop, reorg_yolo_stride_2_batch_2) { +TEST_F(TypePropReorgYoloTest, stride_2_batch_2) { const auto in_shape = Shape{2, 64, 26, 26}; size_t stride = 2; - auto data_param = make_shared(element::f32, in_shape); - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); + auto reorg_yolo = make_op(data_param, stride); // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride] Shape expected_shape = Shape{2, 256, 13, 13}; @@ -56,22 +80,22 @@ TEST(type_prop, reorg_yolo_stride_2_batch_2) { EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape); } -TEST(type_prop, reorg_yolo_stride_2_smaller_H) { +TEST_F(TypePropReorgYoloTest, stride_2_smaller_H) { const auto in_shape = Shape{1, 24, 34, 62}; size_t stride = 2; - auto data_param = make_shared(element::f32, in_shape); - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); + auto reorg_yolo = make_op(data_param, stride); // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride] Shape expected_shape = Shape{1, 96, 17, 31}; EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape); } -TEST(type_prop, reorg_yolo_stride_3) { +TEST_F(TypePropReorgYoloTest, stride_3) { const auto in_shape = Shape{1, 9, 3, 3}; size_t stride = 3; - auto data_param = make_shared(element::f32, in_shape); - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); + auto reorg_yolo = make_op(data_param, stride); // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride] Shape expected_shape = @@ -80,19 +104,48 @@ TEST(type_prop, reorg_yolo_stride_3) { EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape); } -TEST(type_prop, reorg_yolo_catch_small_shape_stride) { +TEST_F(TypePropReorgYoloTest, catch_small_shape_stride) { const auto in_shape = Shape{1, 1, 4, 4}; size_t stride = 2; - auto data_param = make_shared(element::f32, in_shape); - try { - // Throw error test: For [N, C, H, W] input shape, C >= (stride*stride) is required. - auto reorg_yolo = make_shared(data_param, stride); + auto data_param = std::make_shared(element::f32, in_shape); - // Should have thrown, so fail if it didn't - FAIL() << "Incompatible stride was not detected."; - } catch (const ngraph_error& error) { - EXPECT_HAS_SUBSTRING(error.what(), std::string("stride")); - } catch (...) { - FAIL() << "Stride size check failed for unexpected reason."; - } + OV_EXPECT_THROW(std::ignore = make_op(data_param, stride), NodeValidationFailure, HasSubstr("stride")); +} + +TEST_F(TypePropReorgYoloTest, data_shape_not_compatible_rank_4) { + const auto data_param = std::make_shared(element::f32, PartialShape{1, 1, {13, 14}, 4}); + + OV_EXPECT_THROW(std::ignore = make_op(std::make_shared(element::f32, PartialShape::dynamic(3)), + static_cast(2)), + NodeValidationFailure, + HasSubstr("[N, C, H, W] input shape is required")); + + OV_EXPECT_THROW(std::ignore = make_op(std::make_shared(element::f32, PartialShape::dynamic(5)), + static_cast(2)), + NodeValidationFailure, + HasSubstr("[N, C, H, W] input shape is required")); +} + +TEST_F(TypePropReorgYoloTest, h_dim_not_div_by_stride) { + const auto data_param = std::make_shared(element::f32, PartialShape{1, 9, 13, 3}); + + OV_EXPECT_THROW(std::ignore = make_op(data_param, static_cast(3)), + NodeValidationFailure, + HasSubstr("H and W should be divisible by stride")); +} + +TEST_F(TypePropReorgYoloTest, w_dim_not_div_by_stride) { + const auto data_param = std::make_shared(element::f32, Shape{1, 9, 6, 4}); + + OV_EXPECT_THROW(std::ignore = make_op(data_param, static_cast(3)), + NodeValidationFailure, + HasSubstr("H and W should be divisible by stride")); +} + +TEST_F(TypePropReorgYoloTest, channels_lower_than_sq_stride) { + const auto data_param = std::make_shared(element::f32, PartialShape{1, 8, 6, 6}); + + OV_EXPECT_THROW(std::ignore = make_op(data_param, static_cast(3)), + NodeValidationFailure, + HasSubstr(" C >= (stride*stride) is required.")); } diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/reorg_yolo_shape_inference_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/reorg_yolo_shape_inference_test.cpp new file mode 100644 index 00000000000..8c6947fa54c --- /dev/null +++ b/src/plugins/intel_cpu/tests/unit/shape_inference_test/reorg_yolo_shape_inference_test.cpp @@ -0,0 +1,67 @@ + +// Copyright (C) 2018-2023 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include "common_test_utils/test_assertions.hpp" +#include "reorg_yolo_shape_inference.hpp" +#include "utils.hpp" + +using namespace ov; +using namespace ov::intel_cpu; +using namespace testing; + +class StaticShapeReorgYoloTest : public OpStaticShapeInferenceTest {}; + +TEST_F(StaticShapeReorgYoloTest, default_ctor_no_args) { + op = make_op(); + op->set_strides(3); + + input_shapes = ShapeVector{{2, 9, 12, 6}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({2, 81, 4, 2})); +} + +TEST_F(StaticShapeReorgYoloTest, data_input_is_dynamic_rank) { + const auto data = std::make_shared(element::f32, ov::PartialShape::dynamic()); + op = make_op(data, 2); + + input_shapes = ShapeVector{{2, 12, 12, 24}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({2, 48, 6, 12})); +} + +TEST_F(StaticShapeReorgYoloTest, data_input_is_static_rank) { + const auto data = std::make_shared(element::f32, ov::PartialShape::dynamic(4)); + op = make_op(data, 2); + + input_shapes = ShapeVector{{2, 20, 12, 24}}; + shape_inference(op.get(), input_shapes, output_shapes); + + EXPECT_EQ(output_shapes.size(), 1); + EXPECT_EQ(output_shapes.front(), StaticShape({2, 80, 6, 12})); +} + +TEST_F(StaticShapeReorgYoloTest, data_shape_not_compatible_rank_4) { + const auto data = std::make_shared(element::f32, PartialShape::dynamic()); + op = make_op(data, 2); + + OV_EXPECT_THROW(shape_inference(op.get(), ShapeVector{{2, 20, 12, 24, 1}}, output_shapes), + NodeValidationFailure, + HasSubstr("[N, C, H, W] input shape is required")); +} + +TEST_F(StaticShapeReorgYoloTest, h_dim_not_div_by_stride) { + const auto data = std::make_shared(element::f32, PartialShape::dynamic()); + op = make_op(data, 2); + + OV_EXPECT_THROW(shape_inference(op.get(), ShapeVector{{2, 20, 11, 24}}, output_shapes), + NodeValidationFailure, + HasSubstr("H and W should be divisible by stride")); +} diff --git a/src/plugins/intel_cpu/tests/unit/shape_inference_test/reorg_yolo_test.cpp b/src/plugins/intel_cpu/tests/unit/shape_inference_test/reorg_yolo_test.cpp deleted file mode 100644 index b17bfbf2ab9..00000000000 --- a/src/plugins/intel_cpu/tests/unit/shape_inference_test/reorg_yolo_test.cpp +++ /dev/null @@ -1,20 +0,0 @@ - -// Copyright (C) 2018-2023 Intel Corporation -// SPDX-License-Identifier: Apache-2.0 -// - -#include - -#include "utils.hpp" - -using namespace ov; -using namespace ov::intel_cpu; -using namespace std; - -TEST(StaticShapeInferenceTest, ReorgYoloV0) { - size_t stride = 2; - auto data_param = make_shared(element::f32, ov::PartialShape{-1, -1, -1, -1}); - auto op = make_shared(data_param, stride); - - check_static_shape(op.get(), {StaticShape{1, 64, 26, 26}}, {StaticShape{1, 256, 13, 13}}); -} diff --git a/src/plugins/intel_gpu/src/graph/reorg_yolo.cpp b/src/plugins/intel_gpu/src/graph/reorg_yolo.cpp index 5dd47549f78..d429f8b03c0 100644 --- a/src/plugins/intel_gpu/src/graph/reorg_yolo.cpp +++ b/src/plugins/intel_gpu/src/graph/reorg_yolo.cpp @@ -37,8 +37,7 @@ std::vector reorg_yolo_inst::calc_output_layouts(reorg_yolo_node const& auto output_format = input_layout.format; ov::op::v0::ReorgYolo op; - std::vector strides { static_cast(desc->stride) }; - op.set_strides(strides); + op.set_strides(static_cast(desc->stride)); std::vector input_shapes = { input_layout.get()