diff --git a/src/core/include/openvino/op/ops.hpp b/src/core/include/openvino/op/ops.hpp index 7a17f120f73..755bbc9470f 100644 --- a/src/core/include/openvino/op/ops.hpp +++ b/src/core/include/openvino/op/ops.hpp @@ -157,6 +157,7 @@ #include "openvino/op/rnn_cell.hpp" #include "openvino/op/rnn_sequence.hpp" #include "openvino/op/roi_align.hpp" +#include "openvino/op/roi_align_rotated.hpp" #include "openvino/op/roi_pooling.hpp" #include "openvino/op/roll.hpp" #include "openvino/op/round.hpp" diff --git a/src/core/include/openvino/op/roi_align.hpp b/src/core/include/openvino/op/roi_align.hpp index b30257a0ff6..fcd7da20794 100644 --- a/src/core/include/openvino/op/roi_align.hpp +++ b/src/core/include/openvino/op/roi_align.hpp @@ -4,7 +4,7 @@ #pragma once -#include "openvino/op/op.hpp" +#include "openvino/op/util/roi_align_base.hpp" namespace ov { namespace op { @@ -12,23 +12,15 @@ namespace v3 { /// \brief ROIAlign operation. /// /// \ingroup ov_ops_cpp_api -class OPENVINO_API ROIAlign : public Op { +class OPENVINO_API ROIAlign : public util::ROIAlignBase { public: - OPENVINO_OP("ROIAlign", "opset3", op::Op); + OPENVINO_OP("ROIAlign", "opset3", util::ROIAlignBase); enum class PoolingMode { AVG, MAX }; ROIAlign() = default; /// \brief Constructs a ROIAlign node matching the ONNX ROIAlign specification + /// Check util::ROIAlignBase for description of common params. /// - /// \param input Input feature map {N, C, H, W} - /// \param rois Regions of interest to pool over - /// \param batch_indices Indices of images in the batch matching - /// the number or ROIs - /// \param pooled_h Height of the ROI output features - /// \param pooled_w Width of the ROI output features - /// \param sampling_ratio Number of sampling points used to compute - /// an output element - /// \param spatial_scale Spatial scale factor used to translate ROI coordinates /// \param mode Method of pooling - 'avg' or 'max' ROIAlign(const Output& input, const Output& rois, @@ -52,35 +44,8 @@ public: bool visit_attributes(AttributeVisitor& visitor) override; std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; - int get_pooled_h() const { - return m_pooled_h; - } - - void set_pooled_h(const int h) { - m_pooled_h = h; - } - - int get_pooled_w() const { - return m_pooled_w; - } - void set_pooled_w(const int w) { - m_pooled_w = w; - } - - int get_sampling_ratio() const { - return m_sampling_ratio; - } - - void set_sampling_ratio(const int ratio) { - m_sampling_ratio = ratio; - } - - float get_spatial_scale() const { - return m_spatial_scale; - } - - void set_spatial_scale(const float scale) { - m_spatial_scale = scale; + int get_rois_input_second_dim_size() const override { + return 4; } PoolingMode get_mode() const { @@ -98,24 +63,19 @@ private: PoolingMode mode_from_string(const std::string& mode) const; private: - int m_pooled_h; - int m_pooled_w; - int m_sampling_ratio; - float m_spatial_scale; PoolingMode m_mode; }; } // namespace v3 namespace v9 { -class OPENVINO_API ROIAlign : public Op { +class OPENVINO_API ROIAlign : public util::ROIAlignBase { public: - OPENVINO_OP("ROIAlign", "opset9"); + OPENVINO_OP("ROIAlign", "opset9", util::ROIAlignBase); enum class PoolingMode { AVG, MAX }; enum class AlignedMode { ASYMMETRIC, HALF_PIXEL_FOR_NN, HALF_PIXEL }; ROIAlign() = default; /// \brief Constructs a ROIAlign operation. - /// /// \param input Input feature map {N, C, H, W} /// \param rois Regions of interest to pool over /// \param batch_indices Indices of images in the batch matching @@ -125,6 +85,7 @@ public: /// \param sampling_ratio Number of sampling points used to compute /// an output element /// \param spatial_scale Spatial scale factor used to translate ROI coordinates + /// /// \param mode Method of pooling - 'avg' or 'max' /// \param aligned_mode Method of coordinates alignment - 'asymmetric', 'half_pixel_for_nn' or 'half_pixel' ROIAlign(const Output& input, @@ -141,35 +102,8 @@ public: bool visit_attributes(AttributeVisitor& visitor) override; std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; - int get_pooled_h() const { - return m_pooled_h; - } - - void set_pooled_h(const int h) { - m_pooled_h = h; - } - - int get_pooled_w() const { - return m_pooled_w; - } - void set_pooled_w(const int w) { - m_pooled_w = w; - } - - int get_sampling_ratio() const { - return m_sampling_ratio; - } - - void set_sampling_ratio(const int ratio) { - m_sampling_ratio = ratio; - } - - float get_spatial_scale() const { - return m_spatial_scale; - } - - void set_spatial_scale(const float scale) { - m_spatial_scale = scale; + int get_rois_input_second_dim_size() const override { + return 4; } PoolingMode get_mode() const { @@ -190,10 +124,6 @@ public: private: PoolingMode mode_from_string(const std::string& mode) const; - int m_pooled_h; - int m_pooled_w; - int m_sampling_ratio; - float m_spatial_scale; PoolingMode m_mode; AlignedMode m_aligned_mode; }; diff --git a/src/core/include/openvino/op/roi_align_rotated.hpp b/src/core/include/openvino/op/roi_align_rotated.hpp new file mode 100644 index 00000000000..500988e8745 --- /dev/null +++ b/src/core/include/openvino/op/roi_align_rotated.hpp @@ -0,0 +1,62 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/op/util/roi_align_base.hpp" + +namespace ov { +namespace op { +namespace v14 { +/// \brief ROIAlignRotated operation. +/// +/// \ingroup ov_ops_cpp_api +class OPENVINO_API ROIAlignRotated : public util::ROIAlignBase { +public: + OPENVINO_OP("ROIAlignRotated", "opset14", util::ROIAlignBase); + + ROIAlignRotated() = default; + /// \brief Constructs a ROIAlignRotated operation. + /// \param input Input feature map {N, C, H, W} + /// \param rois Regions of interest to pool over + /// \param batch_indices Indices of images in the batch matching + /// the number or ROIs + /// \param pooled_h Height of the ROI output features + /// \param pooled_w Width of the ROI output features + /// \param sampling_ratio Number of sampling points used to compute + /// an output element + /// \param spatial_scale Spatial scale factor used to translate ROI coordinates + /// + /// \param clockwise_mode If true, rotation angle is interpreted as clockwise, otherwise as counterclockwise + ROIAlignRotated(const Output& input, + const Output& rois, + const Output& batch_indices, + const int pooled_h, + const int pooled_w, + const int sampling_ratio, + const float spatial_scale, + const bool clockwise_mode); + + void validate_and_infer_types() override; + bool visit_attributes(AttributeVisitor& visitor) override; + std::shared_ptr clone_with_new_inputs(const OutputVector& new_args) const override; + + int get_rois_input_second_dim_size() const override { + return 5; + } + + bool get_clockwise_mode() const { + return m_clockwise_mode; + } + + void set_clockwise_mode(const bool clockwise_mode) { + m_clockwise_mode = clockwise_mode; + } + +private: + bool m_clockwise_mode; +}; +} // namespace v14 +} // namespace op +} // namespace ov diff --git a/src/core/include/openvino/op/util/roi_align_base.hpp b/src/core/include/openvino/op/util/roi_align_base.hpp new file mode 100644 index 00000000000..b35d241bec7 --- /dev/null +++ b/src/core/include/openvino/op/util/roi_align_base.hpp @@ -0,0 +1,90 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "openvino/op/op.hpp" + +namespace ov { +namespace op { +namespace util { + +/// +/// \brief Base class for ROIAlignXXX operators. +/// +class OPENVINO_API ROIAlignBase : public Op { +public: + OPENVINO_OP("ROIAlignBase", "util"); + +protected: + ROIAlignBase() = default; + /// \brief Constructs a ROIAlignBase operation. + /// + /// \param input Input feature map {N, C, H, W} + /// \param rois Regions of interest to pool over + /// \param batch_indices Indices of images in the batch matching + /// the number or ROIs + /// \param pooled_h Height of the ROI output features + /// \param pooled_w Width of the ROI output features + /// \param sampling_ratio Number of sampling points used to compute + /// an output element + /// \param spatial_scale Spatial scale factor used to translate ROI coordinates + ROIAlignBase(const Output& input, + const Output& rois, + const Output& batch_indices, + const int pooled_h, + const int pooled_w, + const int sampling_ratio, + const float spatial_scale); + +public: + // Return size of second dimension of the ROI input. + // Needed for validation check. + virtual int get_rois_input_second_dim_size() const = 0; + + void validate_and_infer_types() override; + bool visit_attributes(AttributeVisitor& visitor) override; + + int get_pooled_h() const { + return m_pooled_h; + } + + void set_pooled_h(const int h) { + m_pooled_h = h; + } + + int get_pooled_w() const { + return m_pooled_w; + } + + void set_pooled_w(const int w) { + m_pooled_w = w; + } + + int get_sampling_ratio() const { + return m_sampling_ratio; + } + + void set_sampling_ratio(const int ratio) { + m_sampling_ratio = ratio; + } + + float get_spatial_scale() const { + return m_spatial_scale; + } + + void set_spatial_scale(const float scale) { + m_spatial_scale = scale; + } + +private: + int m_pooled_h; + int m_pooled_w; + int m_sampling_ratio; + float m_spatial_scale; +}; + +} // namespace util +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/roi_align_rotated_shape_inference.hpp b/src/core/shape_inference/include/roi_align_rotated_shape_inference.hpp new file mode 100644 index 00000000000..5faeaefa19a --- /dev/null +++ b/src/core/shape_inference/include/roi_align_rotated_shape_inference.hpp @@ -0,0 +1,19 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include "roi_align_shape_utils.hpp" + +namespace ov { +namespace op { +namespace v14 { +class ROIAlignRotated; +template > +std::vector shape_infer(const ROIAlignRotated* op, const std::vector& input_shapes) { + return roi_align::shape_infer(op, input_shapes); +} +} // namespace v14 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/roi_align_shape_inference.hpp b/src/core/shape_inference/include/roi_align_shape_inference.hpp index 9b60f030b3c..a261f11f76f 100644 --- a/src/core/shape_inference/include/roi_align_shape_inference.hpp +++ b/src/core/shape_inference/include/roi_align_shape_inference.hpp @@ -4,115 +4,23 @@ #pragma once -#include - -#include "utils.hpp" +#include "openvino/op/roi_align.hpp" +#include "roi_align_shape_utils.hpp" namespace ov { namespace op { -namespace roi_align { -namespace validate { -/** - * @brief Validates ROIs align input data and ROIs element type. - * - * @param op Pointer to ROIs align node. - * @return Valid ROIs align output element type. - */ -inline element::Type data_and_roi_et(const Node* const op) { - auto out_et = element::dynamic; - - const auto& input_et = op->get_input_element_type(0); - const auto& rois_et = op->get_input_element_type(1); - - NODE_VALIDATION_CHECK(op, - element::Type::merge(out_et, input_et, rois_et) && out_et.is_real(), - "The data type for input and ROIs is expected to be a same floating point type. Got: ", - input_et, - " and: ", - rois_et); - return out_et; -} - -/** - * @brief Check ROIs align batch indicies input element type. - * - * @param op Pointer to ROIs align node. - */ -inline void batch_indicies_et(const Node* const op) { - const auto& indicies_et = op->get_input_element_type(2); - - NODE_VALIDATION_CHECK(op, - indicies_et.is_integral_number(), - "The data type for batch indices is expected to be an integer. Got: ", - indicies_et); -} -} // namespace validate - -template > -std::vector shape_infer(const OpType* op, const std::vector& input_shapes) { - NODE_VALIDATION_CHECK(op, input_shapes.size() == 3); - - using TDim = typename TShape::value_type; - - const auto& input_ps = input_shapes[0]; - const auto& rois_ps = input_shapes[1]; - const auto& batch_indices_ps = input_shapes[2]; - - const auto rois_ps_rank = rois_ps.rank(); - const auto input_ps_rank = input_ps.rank(); - const auto batch_indices_ps_rank = batch_indices_ps.rank(); - - auto output_shapes = std::vector(1); - auto& out_shape = output_shapes.front(); - out_shape.reserve(4); - - NODE_VALIDATION_CHECK(op, input_ps_rank.compatible(4), "Expected a 4D tensor for the input data. Got: ", input_ps); - NODE_VALIDATION_CHECK(op, rois_ps_rank.compatible(2), "Expected a 2D tensor for the ROIs input. Got: ", rois_ps); - NODE_VALIDATION_CHECK(op, - batch_indices_ps_rank.compatible(1), - "Expected a 1D tensor for the batch indices input. Got: ", - batch_indices_ps); - - if (rois_ps_rank.is_static()) { - const auto& rois_second_dim = rois_ps[1]; - NODE_VALIDATION_CHECK(op, - rois_second_dim.compatible(4), - "The second dimension of ROIs input should contain box coordinates. ", - "op dimension is expected to be equal to 4. Got: ", - rois_second_dim); - - out_shape.push_back(rois_ps[0]); - } else { - out_shape.push_back(Dimension::dynamic()); - } - - NODE_VALIDATION_CHECK( - op, - batch_indices_ps_rank.is_dynamic() || TDim::merge(out_shape[0], batch_indices_ps[0], out_shape[0]), - "The first dimension of ROIs input must be equal to the first dimension of the batch indices input. Got: ", - out_shape[0], - " and: ", - batch_indices_ps[0]); - - out_shape.push_back(input_ps_rank.is_static() ? input_ps[1] : Dimension::dynamic()); - out_shape.emplace_back(static_cast(op->get_pooled_h())); - out_shape.emplace_back(static_cast(op->get_pooled_w())); - - return output_shapes; -} -} // namespace roi_align namespace v3 { template > std::vector shape_infer(const ROIAlign* op, const std::vector& input_shapes) { - return roi_align::shape_infer(op, input_shapes); + return roi_align::shape_infer(op, input_shapes); } } // namespace v3 namespace v9 { template > std::vector shape_infer(const ROIAlign* op, const std::vector& input_shapes) { - return roi_align::shape_infer(op, input_shapes); + return roi_align::shape_infer(op, input_shapes); } } // namespace v9 } // namespace op diff --git a/src/core/shape_inference/include/roi_align_shape_utils.hpp b/src/core/shape_inference/include/roi_align_shape_utils.hpp new file mode 100644 index 00000000000..b84d8712be8 --- /dev/null +++ b/src/core/shape_inference/include/roi_align_shape_utils.hpp @@ -0,0 +1,106 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once +#include + +#include "utils.hpp" + +namespace ov { +namespace op { +namespace roi_align { +namespace validate { +/** + * @brief Validates ROIs align input data and ROIs element type. + * + * @param op Pointer to ROIs align node. + * @return Valid ROIs align output element type. + */ +inline element::Type data_and_roi_et(const Node* const op) { + auto out_et = element::dynamic; + + const auto& input_et = op->get_input_element_type(0); + const auto& rois_et = op->get_input_element_type(1); + + NODE_VALIDATION_CHECK(op, + element::Type::merge(out_et, input_et, rois_et) && out_et.is_real(), + "The data type for input and ROIs is expected to be a same floating point type. Got: ", + input_et, + " and: ", + rois_et); + return out_et; +} + +/** + * @brief Check ROIs align batch indicies input element type. + * + * @param op Pointer to ROIs align node. + */ +inline void batch_indicies_et(const Node* const op) { + const auto& indicies_et = op->get_input_element_type(2); + + NODE_VALIDATION_CHECK(op, + indicies_et.is_integral_number(), + "The data type for batch indices is expected to be an integer. Got: ", + indicies_et); +} +} // namespace validate + +template > +std::vector shape_infer(const util::ROIAlignBase* op, const std::vector& input_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 3); + + using TDim = typename TShape::value_type; + + const auto& input_ps = input_shapes[0]; + const auto& rois_ps = input_shapes[1]; + const auto& batch_indices_ps = input_shapes[2]; + + const auto rois_ps_rank = rois_ps.rank(); + const auto input_ps_rank = input_ps.rank(); + const auto batch_indices_ps_rank = batch_indices_ps.rank(); + + auto output_shapes = std::vector(1); + auto& out_shape = output_shapes.front(); + out_shape.reserve(4); + + NODE_VALIDATION_CHECK(op, input_ps_rank.compatible(4), "Expected a 4D tensor for the input data. Got: ", input_ps); + NODE_VALIDATION_CHECK(op, rois_ps_rank.compatible(2), "Expected a 2D tensor for the ROIs input. Got: ", rois_ps); + NODE_VALIDATION_CHECK(op, + batch_indices_ps_rank.compatible(1), + "Expected a 1D tensor for the batch indices input. Got: ", + batch_indices_ps); + + if (rois_ps_rank.is_static()) { + const auto& rois_second_dim = rois_ps[1]; + NODE_VALIDATION_CHECK(op, + rois_second_dim.compatible(op->get_rois_input_second_dim_size()), + "The second dimension of ROIs input should contain box coordinates. " + "op dimension is expected to be equal to ", + op->get_rois_input_second_dim_size(), + ". Got: ", + rois_second_dim); + + out_shape.push_back(rois_ps[0]); + } else { + out_shape.push_back(Dimension::dynamic()); + } + + NODE_VALIDATION_CHECK( + op, + batch_indices_ps_rank.is_dynamic() || TDim::merge(out_shape[0], batch_indices_ps[0], out_shape[0]), + "The first dimension of ROIs input must be equal to the first dimension of the batch indices input. Got: ", + out_shape[0], + " and: ", + batch_indices_ps[0]); + + out_shape.push_back(input_ps_rank.is_static() ? input_ps[1] : Dimension::dynamic()); + out_shape.emplace_back(static_cast(op->get_pooled_h())); + out_shape.emplace_back(static_cast(op->get_pooled_w())); + + return output_shapes; +} +} // namespace roi_align +} // namespace op +} // namespace ov diff --git a/src/core/src/op/roi_align.cpp b/src/core/src/op/roi_align.cpp index fbaccbbe483..4e28c972253 100644 --- a/src/core/src/op/roi_align.cpp +++ b/src/core/src/op/roi_align.cpp @@ -19,12 +19,10 @@ op::v3::ROIAlign::ROIAlign(const Output& input, const int sampling_ratio, const float spatial_scale, const string& mode) - : Op{{input, rois, batch_indices}}, - m_pooled_h{pooled_h}, - m_pooled_w{pooled_w}, - m_sampling_ratio{sampling_ratio}, - m_spatial_scale{spatial_scale}, + : ROIAlignBase{input, rois, batch_indices, pooled_h, pooled_w, sampling_ratio, spatial_scale}, m_mode{EnumNames::as_enum(mode)} { + // NOTE: Cannot be called in base class, since then ROIAlignRotated + // is not fully constructed. constructor_validate_and_infer_types(); } @@ -36,47 +34,21 @@ op::v3::ROIAlign::ROIAlign(const Output& input, const int sampling_ratio, const float spatial_scale, const PoolingMode mode) - : Op{{input, rois, batch_indices}}, - m_pooled_h{pooled_h}, - m_pooled_w{pooled_w}, - m_sampling_ratio{sampling_ratio}, - m_spatial_scale{spatial_scale}, + : ROIAlignBase{input, rois, batch_indices, pooled_h, pooled_w, sampling_ratio, spatial_scale}, m_mode{mode} { + // NOTE: Cannot be called in base class, since then ROIAlignRotated + // is not fully constructed. constructor_validate_and_infer_types(); } void op::v3::ROIAlign::validate_and_infer_types() { OV_OP_SCOPE(v3_ROIAlign_validate_and_infer_types); - - const auto out_et = roi_align::validate::data_and_roi_et(this); - roi_align::validate::batch_indicies_et(this); - - const auto input_shapes = ov::util::get_node_input_partial_shapes(*this); - - auto output_shape = shape_infer(this, input_shapes).front(); - set_output_type(0, out_et, output_shape); - - const auto& input_ps = input_shapes.front(); - - // if the channels dimension is not known - // the first input should be used during the function specialization - if (input_ps.rank().is_static() && input_ps[1].is_dynamic()) { - set_input_is_relevant_to_shape(0); - } - // if the 'NUM_ROIS' value is not known - // the last 2 inputs should be used during the function specialization - if (output_shape[0].is_dynamic()) { - set_input_is_relevant_to_shape(1); - set_input_is_relevant_to_shape(2); - } + ROIAlignBase::validate_and_infer_types(); } bool op::v3::ROIAlign::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v3_ROIAlign_visit_attributes); - visitor.on_attribute("pooled_h", m_pooled_h); - visitor.on_attribute("pooled_w", m_pooled_w); - visitor.on_attribute("sampling_ratio", m_sampling_ratio); - visitor.on_attribute("spatial_scale", m_spatial_scale); + ROIAlignBase::visit_attributes(visitor); visitor.on_attribute("mode", m_mode); return true; @@ -88,11 +60,11 @@ shared_ptr op::v3::ROIAlign::clone_with_new_inputs(const OutputVector& new return make_shared(new_args.at(0), new_args.at(1), new_args.at(2), - m_pooled_h, - m_pooled_w, - m_sampling_ratio, - m_spatial_scale, - m_mode); + get_pooled_h(), + get_pooled_w(), + get_sampling_ratio(), + get_spatial_scale(), + get_mode()); } // ------------------------------ V9 ------------------------------ @@ -106,11 +78,7 @@ op::v9::ROIAlign::ROIAlign(const Output& input, const float spatial_scale, const PoolingMode mode, const AlignedMode aligned_mode) - : Op{{input, rois, batch_indices}}, - m_pooled_h{pooled_h}, - m_pooled_w{pooled_w}, - m_sampling_ratio{sampling_ratio}, - m_spatial_scale{spatial_scale}, + : ROIAlignBase{input, rois, batch_indices, pooled_h, pooled_w, sampling_ratio, spatial_scale}, m_mode{mode}, m_aligned_mode{aligned_mode} { constructor_validate_and_infer_types(); @@ -118,36 +86,12 @@ op::v9::ROIAlign::ROIAlign(const Output& input, void op::v9::ROIAlign::validate_and_infer_types() { OV_OP_SCOPE(v9_ROIAlign_validate_and_infer_types); - - const auto out_et = roi_align::validate::data_and_roi_et(this); - roi_align::validate::batch_indicies_et(this); - - const auto input_shapes = ov::util::get_node_input_partial_shapes(*this); - - auto output_shape = shape_infer(this, input_shapes).front(); - set_output_type(0, out_et, output_shape); - - const auto& input_ps = input_shapes.front(); - - // if the channels dimension is not known - // the first input should be used during the function specialization - if (input_ps.rank().is_static() && input_ps[1].is_dynamic()) { - set_input_is_relevant_to_shape(0); - } - // if the 'NUM_ROIS' value is not known - // the last 2 inputs should be used during the function specialization - if (output_shape[0].is_dynamic()) { - set_input_is_relevant_to_shape(1); - set_input_is_relevant_to_shape(2); - } + ROIAlignBase::validate_and_infer_types(); } bool op::v9::ROIAlign::visit_attributes(AttributeVisitor& visitor) { OV_OP_SCOPE(v9_ROIAlign_visit_attributes); - visitor.on_attribute("pooled_h", m_pooled_h); - visitor.on_attribute("pooled_w", m_pooled_w); - visitor.on_attribute("sampling_ratio", m_sampling_ratio); - visitor.on_attribute("spatial_scale", m_spatial_scale); + ROIAlignBase::visit_attributes(visitor); visitor.on_attribute("mode", m_mode); visitor.on_attribute("aligned_mode", m_aligned_mode); @@ -160,12 +104,12 @@ shared_ptr op::v9::ROIAlign::clone_with_new_inputs(const OutputVector& new return make_shared(new_args.at(0), new_args.at(1), new_args.at(2), - m_pooled_h, - m_pooled_w, - m_sampling_ratio, - m_spatial_scale, - m_mode, - m_aligned_mode); + get_pooled_h(), + get_pooled_w(), + get_sampling_ratio(), + get_spatial_scale(), + get_mode(), + get_aligned_mode()); } template <> @@ -306,7 +250,13 @@ bool evaluate(const TensorVector& args, bool v3::ROIAlign::evaluate(TensorVector& outputs, const TensorVector& inputs) const { OV_OP_SCOPE(v3_ROIAlign_evaluate); - return roi_align::evaluate(inputs, outputs[0], m_pooled_h, m_pooled_w, m_sampling_ratio, m_spatial_scale, m_mode); + return roi_align::evaluate(inputs, + outputs[0], + get_pooled_h(), + get_pooled_w(), + get_sampling_ratio(), + get_spatial_scale(), + get_mode()); } bool v3::ROIAlign::has_evaluate() const { diff --git a/src/core/src/op/roi_align_rotated.cpp b/src/core/src/op/roi_align_rotated.cpp new file mode 100644 index 00000000000..4eec291bc46 --- /dev/null +++ b/src/core/src/op/roi_align_rotated.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/roi_align_rotated.hpp" + +#include "itt.hpp" + +namespace ov { +namespace op { +namespace v14 { +ROIAlignRotated::ROIAlignRotated(const Output& input, + const Output& rois, + const Output& batch_indices, + const int pooled_h, + const int pooled_w, + const int sampling_ratio, + const float spatial_scale, + const bool clockwise_mode) + : ROIAlignBase{input, rois, batch_indices, pooled_h, pooled_w, sampling_ratio, spatial_scale}, + m_clockwise_mode{clockwise_mode} { + // NOTE: Cannot be called in base class, since then ROIAlignRotated + // is not fully constructed. + constructor_validate_and_infer_types(); +} + +void ROIAlignRotated::validate_and_infer_types() { + OV_OP_SCOPE(v14_ROIAlignRotated_validate_and_infer_types); + ROIAlignBase::validate_and_infer_types(); +} + +bool ROIAlignRotated::visit_attributes(AttributeVisitor& visitor) { + OV_OP_SCOPE(v14_ROIAlignRotated_visit_attributes); + ROIAlignBase::visit_attributes(visitor); + visitor.on_attribute("clockwise_mode", m_clockwise_mode); + + return true; +} + +std::shared_ptr ROIAlignRotated::clone_with_new_inputs(const OutputVector& new_args) const { + OV_OP_SCOPE(v14_ROIAlignRotated_clone_with_new_inputs); + check_new_args_count(this, new_args); + return std::make_shared(new_args.at(0), + new_args.at(1), + new_args.at(2), + get_pooled_h(), + get_pooled_w(), + get_sampling_ratio(), + get_spatial_scale(), + get_clockwise_mode()); +} +} // namespace v14 +} // namespace op +} // namespace ov diff --git a/src/core/src/op/util/roi_align_base.cpp b/src/core/src/op/util/roi_align_base.cpp new file mode 100644 index 00000000000..d176f9179f5 --- /dev/null +++ b/src/core/src/op/util/roi_align_base.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2018-2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/op/util/roi_align_base.hpp" + +#include "itt.hpp" +#include "roi_align_shape_utils.hpp" + +namespace ov { +namespace op { +namespace util { + +ROIAlignBase::ROIAlignBase(const Output& input, + const Output& rois, + const Output& batch_indices, + const int pooled_h, + const int pooled_w, + const int sampling_ratio, + const float spatial_scale) + : Op{{input, rois, batch_indices}}, + m_pooled_h{pooled_h}, + m_pooled_w{pooled_w}, + m_sampling_ratio{sampling_ratio}, + m_spatial_scale{spatial_scale} {} + +void ROIAlignBase::validate_and_infer_types() { + OV_OP_SCOPE(util_RoiAlignBase_validate_and_infer_types); + + const auto out_et = roi_align::validate::data_and_roi_et(this); + roi_align::validate::batch_indicies_et(this); + + const auto input_shapes = ov::util::get_node_input_partial_shapes(*this); + + auto output_shape = roi_align::shape_infer(this, input_shapes).front(); + set_output_type(0, out_et, output_shape); + + const auto& input_ps = input_shapes.front(); + + // if the channels dimension is not known + // the first input should be used during the function specialization + if (input_ps.rank().is_static() && input_ps[1].is_dynamic()) { + set_input_is_relevant_to_shape(0); + } + // if the 'NUM_ROIS' value is not known + // the last 2 inputs should be used during the function specialization + if (output_shape[0].is_dynamic()) { + set_input_is_relevant_to_shape(1); + set_input_is_relevant_to_shape(2); + } +} + +bool ROIAlignBase::visit_attributes(AttributeVisitor& visitor) { + OV_OP_SCOPE(util_RoiAlignBase_visit_attributes); + visitor.on_attribute("pooled_h", m_pooled_h); + visitor.on_attribute("pooled_w", m_pooled_w); + visitor.on_attribute("sampling_ratio", m_sampling_ratio); + visitor.on_attribute("spatial_scale", m_spatial_scale); + + return true; +} + +} // namespace util +} // namespace op +} // namespace ov \ No newline at end of file diff --git a/src/core/tests/type_prop/roi_align.cpp b/src/core/tests/type_prop/roi_align.cpp index 21f99890eef..964de695315 100644 --- a/src/core/tests/type_prop/roi_align.cpp +++ b/src/core/tests/type_prop/roi_align.cpp @@ -5,30 +5,48 @@ #include "common_test_utils/test_assertions.hpp" #include "common_test_utils/type_prop.hpp" #include "openvino/opsets/opset11.hpp" +#include "openvino/opsets/opset14.hpp" using namespace std; using namespace ov; using namespace ov::opset11; using namespace testing; -template -class ROIAlignTest : public TypePropOpTest {}; +template +class ROIAlignTest : public testing::Test { +protected: + std::shared_ptr make_roi_op(const Output& input, + const Output& rois, + const Output& batch_indices, + const int pooled_h, + const int pooled_w) { + auto ret = std::make_shared(); + + ret->set_arguments(OutputVector{input, rois, batch_indices}); + ret->set_pooled_h(pooled_h); + ret->set_pooled_w(pooled_w); + ret->validate_and_infer_types(); + + return ret; + } + + ov::Dimension::value_type GetROISecondDimSizeForOp() const { + // Those magic numbers comes from definition of ROIAlign ops. + if (std::is_same::value) + return 5; + return 4; + } +}; TYPED_TEST_SUITE_P(ROIAlignTest); TYPED_TEST_P(ROIAlignTest, default_ctor) { + const size_t second_roi_dim = static_cast(this->GetROISecondDimSizeForOp()); const auto data = make_shared(element::f32, PartialShape{2, 3, 5, 5}); - const auto rois = make_shared(element::f32, Shape{7, 4}); + const auto rois = make_shared(element::f32, Shape{7, second_roi_dim}); const auto batch_indices = make_shared(element::i32, Shape{7}); - const auto op = this->make_op(); - op->set_arguments(OutputVector{data, rois, batch_indices}); - op->set_pooled_h(2); - op->set_pooled_w(2); - op->set_sampling_ratio(1); - op->set_spatial_scale(1.0f); - op->set_mode(TypeParam::PoolingMode::AVG); - op->validate_and_infer_types(); + const auto op = this->make_roi_op(data, rois, batch_indices, 2, 2); EXPECT_EQ(op->get_input_size(), 3); EXPECT_EQ(op->get_output_size(), 1); @@ -37,7 +55,7 @@ TYPED_TEST_P(ROIAlignTest, default_ctor) { TYPED_TEST_P(ROIAlignTest, simple_shape_inference) { auto data_shape = PartialShape{2, 3, 5, 5}; - auto rois_shape = PartialShape{7, 4}; + auto rois_shape = PartialShape{7, this->GetROISecondDimSizeForOp()}; auto batch_shape = PartialShape{7}; auto data_symbols = set_shape_symbols(data_shape); @@ -48,7 +66,7 @@ TYPED_TEST_P(ROIAlignTest, simple_shape_inference) { const auto rois = make_shared(element::f16, rois_shape); const auto batch_indices = make_shared(element::i16, batch_shape); - const auto op = this->make_op(data, rois, batch_indices, 2, 2, 1, 1.0f, TypeParam::PoolingMode::AVG); + const auto op = this->make_roi_op(data, rois, batch_indices, 2, 2); EXPECT_EQ(op->get_element_type(), element::f16); EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({7, 3, 2, 2})); @@ -58,7 +76,7 @@ TYPED_TEST_P(ROIAlignTest, simple_shape_inference) { TYPED_TEST_P(ROIAlignTest, dynamic_channels_dim) { auto data_shape = PartialShape{10, -1, 5, 5}; - auto rois_shape = PartialShape{7, 4}; + auto rois_shape = PartialShape{7, this->GetROISecondDimSizeForOp()}; auto batch_shape = PartialShape{7}; auto data_symbols = set_shape_symbols(data_shape); @@ -68,7 +86,7 @@ TYPED_TEST_P(ROIAlignTest, dynamic_channels_dim) { const auto rois = make_shared(element::f64, rois_shape); const auto batch_indices = make_shared(element::i64, batch_shape); - const auto op = this->make_op(data, rois, batch_indices, 3, 4, 1, 1.0f, TypeParam::PoolingMode::AVG); + const auto op = this->make_roi_op(data, rois, batch_indices, 3, 4); EXPECT_EQ(op->get_element_type(), element::f64); EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({7, -1, 3, 4})); @@ -81,7 +99,7 @@ TYPED_TEST_P(ROIAlignTest, num_rois_from_batch_indices) { const auto rois = make_shared(element::f32, PartialShape::dynamic(2)); const auto batch_indices = make_shared(element::i32, PartialShape{9}); - const auto op = this->make_op(data, rois, batch_indices, 4, 2, 1, 1.0f, TypeParam::PoolingMode::MAX); + const auto op = this->make_roi_op(data, rois, batch_indices, 4, 2); EXPECT_EQ(op->get_element_type(), element::f32); EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({9, 3, 4, 2})); @@ -93,7 +111,7 @@ TYPED_TEST_P(ROIAlignTest, all_inputs_dynamic_rank) { const auto rois = make_shared(element::bf16, PartialShape::dynamic()); const auto batch_indices = make_shared(element::i8, PartialShape::dynamic()); - const auto op = this->make_op(data, rois, batch_indices, 40, 12, 3, 2.2f, TypeParam::PoolingMode::AVG); + const auto op = this->make_roi_op(data, rois, batch_indices, 40, 12); EXPECT_EQ(op->get_element_type(), element::bf16); EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, -1, 40, 12})); @@ -113,7 +131,7 @@ TYPED_TEST_P(ROIAlignTest, all_inputs_static_rank_dynamic_dims) { const auto rois = make_shared(element::f16, rois_shape); const auto batch_indices = make_shared(element::u16, batch_shape); - const auto op = this->make_op(data, rois, batch_indices, 8, 8, 3, 2.2f, TypeParam::PoolingMode::AVG); + const auto op = this->make_roi_op(data, rois, batch_indices, 8, 8); EXPECT_EQ(op->get_element_type(), element::f16); EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({-1, -1, 8, 8})); @@ -134,7 +152,7 @@ TYPED_TEST_P(ROIAlignTest, interval_shapes) { const auto rois = make_shared(element::f32, rois_shape); const auto batch_indices = make_shared(element::u64, batch_shape); - const auto op = this->make_op(data, rois, batch_indices, 8, 18, -3, -42.2f, TypeParam::PoolingMode::AVG); + const auto op = this->make_roi_op(data, rois, batch_indices, 8, 18); EXPECT_EQ(op->get_element_type(), element::f32); EXPECT_EQ(op->get_output_partial_shape(0), PartialShape({{3, 6}, {2, 4}, 8, 18})); @@ -148,7 +166,7 @@ TYPED_TEST_P(ROIAlignTest, incompatible_num_rois) { const auto batch_indices = make_shared(element::i32, PartialShape{9}); OV_EXPECT_THROW( - std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("The first dimension of ROIs input must be equal to the first dimension of the batch indices input")); } @@ -158,7 +176,7 @@ TYPED_TEST_P(ROIAlignTest, incompatible_input_rank) { const auto rois = make_shared(element::f32, PartialShape{1, -1}); const auto batch_indices = make_shared(element::i32, PartialShape{9}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("Expected a 4D tensor for the input data")); } @@ -168,38 +186,38 @@ TYPED_TEST_P(ROIAlignTest, incompatible_rois_rank) { const auto rois = make_shared(element::f32, PartialShape{2}); const auto batch_indices = make_shared(element::i32, PartialShape{9}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("Expected a 2D tensor for the ROIs input")); } TYPED_TEST_P(ROIAlignTest, incompatible_batch_indicies_rank) { const auto data = make_shared(element::f32, PartialShape{10, 3, 5, 5}); - const auto rois = make_shared(element::f32, PartialShape{2, 4}); + const auto rois = make_shared(element::f32, PartialShape{2, this->GetROISecondDimSizeForOp()}); const auto batch_indices = make_shared(element::i32, PartialShape{2, 1}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("Expected a 1D tensor for the batch indices input.")); } TYPED_TEST_P(ROIAlignTest, incompatible_rois_2nd_dim) { const auto data = make_shared(element::f32, PartialShape{10, 3, 5, 5}); - const auto rois = make_shared(element::f32, PartialShape{2, {5, -1}}); + const auto rois = make_shared(element::f32, PartialShape{2, {7, -1}}); const auto batch_indices = make_shared(element::i32, PartialShape{2}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, - HasSubstr("op dimension is expected to be equal to 4")); + HasSubstr("op dimension is expected to be equal to")); } TYPED_TEST_P(ROIAlignTest, incompatible_1st_dim_of_rois_and_batch) { const auto data = make_shared(element::f32, PartialShape{10, 3, 5, 5}); - const auto rois = make_shared(element::f32, PartialShape{{11, -1}, 4}); + const auto rois = make_shared(element::f32, PartialShape{{11, -1}, this->GetROISecondDimSizeForOp()}); const auto batch_indices = make_shared(element::i32, PartialShape{{0, 10}}); OV_EXPECT_THROW( - std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("The first dimension of ROIs input must be equal to the first dimension of the batch indices input")); } @@ -209,37 +227,37 @@ TYPED_TEST_P(ROIAlignTest, data_not_floating_point) { const auto rois = make_shared(element::f32, PartialShape{8, 4}); const auto batch_indices = make_shared(element::i32, PartialShape{8}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("The data type for input and ROIs is expected to be a same floating point type")); } TYPED_TEST_P(ROIAlignTest, rois_not_floating_point) { const auto data = make_shared(element::f32, PartialShape{10, 3, 5, 5}); - const auto rois = make_shared(element::i32, PartialShape{8, 4}); + const auto rois = make_shared(element::i32, PartialShape{8, this->GetROISecondDimSizeForOp()}); const auto batch_indices = make_shared(element::i32, PartialShape{8}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("The data type for input and ROIs is expected to be a same floating point type")); } TYPED_TEST_P(ROIAlignTest, data_and_rois_not_same_type) { const auto data = make_shared(element::f32, PartialShape{10, 3, 5, 5}); - const auto rois = make_shared(element::f16, PartialShape{8, 4}); + const auto rois = make_shared(element::f16, PartialShape{8, this->GetROISecondDimSizeForOp()}); const auto batch_indices = make_shared(element::i32, PartialShape{8}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("The data type for input and ROIs is expected to be a same floating point type")); } TYPED_TEST_P(ROIAlignTest, batch_indicies_not_integer) { const auto data = make_shared(element::f32, PartialShape{10, 3, 5, 5}); - const auto rois = make_shared(element::f32, PartialShape{8, 4}); + const auto rois = make_shared(element::f32, PartialShape{8, this->GetROISecondDimSizeForOp()}); const auto batch_indices = make_shared(element::f32, PartialShape{8}); - OV_EXPECT_THROW(std::ignore = this->make_op(data, rois, batch_indices, 8, 8, 1, 1.0f, TypeParam::PoolingMode::AVG), + OV_EXPECT_THROW(std::ignore = this->make_roi_op(data, rois, batch_indices, 8, 8), NodeValidationFailure, HasSubstr("The data type for batch indices is expected to be an integer")); } @@ -263,5 +281,5 @@ REGISTER_TYPED_TEST_SUITE_P(ROIAlignTest, data_and_rois_not_same_type, batch_indicies_not_integer); -typedef Types ROIAlignTypes; +typedef Types ROIAlignTypes; INSTANTIATE_TYPED_TEST_SUITE_P(type_prop, ROIAlignTest, ROIAlignTypes);