From 3d244a41ab4dfc0ae178ea489b62b40217ea3659 Mon Sep 17 00:00:00 2001 From: Luwei Zhou Date: Thu, 23 Dec 2021 11:02:15 +0800 Subject: [PATCH] [shape_infer]Implement shape inference of Roll, ROIAlign,Proposal (#8610) * Implement the proposal and experimental_detecron_generate_proposals * Implement the proposal shape infer * Add ROI_Align OP shape infer implement. * Fix building issue * Fix bug. * Update test cases. * Add test cases for the OPs * Apply the CI coding style check. * Move the shape_infer API to the new folder. * Update some fix. * Applied review comments * Move the shape infer tests into new folder. * Apply review comments. * Fix missing header when mering with master --- .../utils/shape_inference/shape_inference.cpp | 16 ++- src/core/include/openvino/op/proposal.hpp | 1 + ...ron_generate_proposals_shape_inference.hpp | 86 ++++++++++++++ .../include/proposal_shape_inference.hpp | 109 ++++++++++++++++++ .../include/roi_align_shape_inference.hpp | 77 +++++++++++++ .../include/roll_shape_inference.hpp | 73 ++++++++++++ ...erimental_detectron_generate_proposals.cpp | 74 ++---------- src/core/src/op/proposal.cpp | 94 ++++----------- src/core/src/op/roi_align.cpp | 64 ++-------- src/core/src/op/roll.cpp | 54 ++------- ...perimental_detectron_generate_proposal.cpp | 39 +++++++ .../cpu/shape_inference_test/proposal.cpp | 53 +++++++++ .../roi_align_shape_inference.cpp | 26 +++++ .../roll_shape_inference.cpp | 50 ++++++++ 14 files changed, 582 insertions(+), 234 deletions(-) create mode 100644 src/core/shape_inference/include/experimental_detectron_generate_proposals_shape_inference.hpp create mode 100644 src/core/shape_inference/include/proposal_shape_inference.hpp create mode 100644 src/core/shape_inference/include/roi_align_shape_inference.hpp create mode 100644 src/core/shape_inference/include/roll_shape_inference.hpp create mode 100644 src/tests/unit/cpu/shape_inference_test/experimental_detectron_generate_proposal.cpp create mode 100644 src/tests/unit/cpu/shape_inference_test/proposal.cpp create mode 100644 src/tests/unit/cpu/shape_inference_test/roi_align_shape_inference.cpp create mode 100644 src/tests/unit/cpu/shape_inference_test/roll_shape_inference.cpp diff --git a/inference-engine/src/mkldnn_plugin/utils/shape_inference/shape_inference.cpp b/inference-engine/src/mkldnn_plugin/utils/shape_inference/shape_inference.cpp index a597e406977..3e1a3b083d3 100644 --- a/inference-engine/src/mkldnn_plugin/utils/shape_inference/shape_inference.cpp +++ b/inference-engine/src/mkldnn_plugin/utils/shape_inference/shape_inference.cpp @@ -52,6 +52,10 @@ #include "variadic_split_shape_inference.hpp" #include "einsum_shape_inference.hpp" #include "strided_slice_shape_inference.hpp" +#include "experimental_detectron_generate_proposals_shape_inference.hpp" +#include "roi_align_shape_inference.hpp" +#include "roll_shape_inference.hpp" +#include "proposal_shape_inference.hpp" #include "static_shape.hpp" #include "tile_shape_inference.hpp" #include "utils.hpp" @@ -204,6 +208,16 @@ void shape_inference(ov::Node* op, shape_infer(node, input_shapes, output_shapes); } else if (auto node = ov::as_type(op)) { shape_infer(node, input_shapes, output_shapes); + } else if (auto node = ov::as_type(op)) { + shape_infer(node, input_shapes, output_shapes, constant_data); + } else if (auto node = ov::as_type(op)) { + shape_infer(node, input_shapes, output_shapes); + } else if (auto node = ov::as_type(op)) { + shape_infer(node, input_shapes, output_shapes); + } else if (auto node = ov::as_type(op)) { + shape_infer(node, input_shapes, output_shapes); + } else if (auto node = ov::as_type(op)) { + shape_infer(node, input_shapes, output_shapes); } else { ngraph::OutputVector new_inputs; for (size_t i = 0; i < op->get_input_size(); ++i) { @@ -226,4 +240,4 @@ void shape_inference(ov::Node* op, output_shapes[i] = ov::StaticShape(partial_shape.to_shape()); } } -} \ No newline at end of file +} diff --git a/src/core/include/openvino/op/proposal.hpp b/src/core/include/openvino/op/proposal.hpp index add6145e9cb..3d20716456d 100644 --- a/src/core/include/openvino/op/proposal.hpp +++ b/src/core/include/openvino/op/proposal.hpp @@ -66,6 +66,7 @@ public: protected: Attributes m_attrs; + void validate_element_types(); }; } // namespace v0 diff --git a/src/core/shape_inference/include/experimental_detectron_generate_proposals_shape_inference.hpp b/src/core/shape_inference/include/experimental_detectron_generate_proposals_shape_inference.hpp new file mode 100644 index 00000000000..0ea1facdac9 --- /dev/null +++ b/src/core/shape_inference/include/experimental_detectron_generate_proposals_shape_inference.hpp @@ -0,0 +1,86 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +namespace ov { +namespace op { +namespace v6 { + +template +void shape_infer(const ExperimentalDetectronGenerateProposalsSingleImage* op, + const std::vector& input_shapes, + std::vector& output_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 4 && output_shapes.size() == 2); + auto post_nms_count = static_cast(op->get_attrs().post_nms_count); + + const auto& im_info_shape = input_shapes[0]; + const auto& anchors_shape = input_shapes[1]; + const auto& deltas_shape = input_shapes[2]; + const auto& scores_shape = input_shapes[3]; + const auto im_info_shape_rank = im_info_shape.rank(); + NODE_VALIDATION_CHECK(op, + im_info_shape_rank.compatible(1), + "The 'input_im_info' input is expected to be a 1D. Got: ", + im_info_shape); + + if (im_info_shape_rank.is_static()) { + NODE_VALIDATION_CHECK(op, + im_info_shape[0].compatible(3), + "The 'input_im_info' shape is expected to be a compatible with [3]. Got: ", + im_info_shape); + } + + const auto anchors_shape_rank = anchors_shape.rank(); + NODE_VALIDATION_CHECK(op, + anchors_shape_rank.compatible(2), + "The 'input_anchors' input is expected to be a 2D. Got: ", + anchors_shape); + if (anchors_shape_rank.is_static()) { + NODE_VALIDATION_CHECK(op, + anchors_shape[1].compatible(4), + "The second dimension of 'input_anchors' should be compatible with 4. Got: ", + anchors_shape[1]); + } + const auto deltas_shape_rank = deltas_shape.rank(); + const auto scores_shape_rank = scores_shape.rank(); + + NODE_VALIDATION_CHECK(op, + deltas_shape_rank.compatible(3), + "The 'input_deltas' input is expected to be a 3D. Got: ", + deltas_shape); + NODE_VALIDATION_CHECK(op, + scores_shape_rank.compatible(3), + "The 'input_scores' input is expected to be a 3D. Got: ", + scores_shape); + if (deltas_shape_rank.is_static() && scores_shape_rank.is_static()) { + NODE_VALIDATION_CHECK(op, + deltas_shape[1].compatible(scores_shape[1]), + "Heights for inputs 'input_deltas' and 'input_scores' should be " + "equal. Got: ", + deltas_shape[1], + scores_shape[1]); + + NODE_VALIDATION_CHECK(op, + deltas_shape[2].compatible(scores_shape[2]), + "Width for inputs 'input_deltas' and 'input_scores' should be " + "equal. Got: ", + deltas_shape[2], + scores_shape[2]); + } + auto& rois_shape = output_shapes[0]; + auto& rois_scores_shape = output_shapes[1]; + + rois_shape.resize(2); + rois_scores_shape.resize(1); + rois_shape[0] = post_nms_count; + rois_shape[1] = 4; + rois_scores_shape[0] = post_nms_count; +} + +} // namespace v6 +} // namespace op +} // namespace ov diff --git a/src/core/shape_inference/include/proposal_shape_inference.hpp b/src/core/shape_inference/include/proposal_shape_inference.hpp new file mode 100644 index 00000000000..25375e62e8c --- /dev/null +++ b/src/core/shape_inference/include/proposal_shape_inference.hpp @@ -0,0 +1,109 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +namespace ov { +namespace op { +namespace v0 { +template +void infer_prop_shape(const OpType* op, + const std::vector& input_shapes, + std::vector& output_shapes) { + using DimType = typename std::iterator_traits::value_type; + const auto& class_probs_ps = input_shapes[0]; + const auto& bbox_deltas_ps = input_shapes[1]; + const auto& image_shape_ps = input_shapes[2]; + + NODE_VALIDATION_CHECK(op, + class_probs_ps.rank().compatible(4), + "Proposal layer shape class_probs should be rank 4 compatible (", + class_probs_ps, + ")."); + + NODE_VALIDATION_CHECK(op, + bbox_deltas_ps.rank().compatible(4), + "Proposal layer shape bbox_deltas should be rank 4 compatible (", + bbox_deltas_ps, + ")."); + + NODE_VALIDATION_CHECK(op, + image_shape_ps.rank().compatible(1), + "Proposal layer shape image_shape should be rank 1 compatible (", + image_shape_ps, + ")."); + if (bbox_deltas_ps.rank().is_static() && class_probs_ps.rank().is_static()) { + // check anchor count and batch number consistency + NODE_VALIDATION_CHECK(op, + bbox_deltas_ps[1].compatible(class_probs_ps[1] * 2), + "Anchor number inconsistent between class_probs (", + class_probs_ps[1] * 2, + "), and bbox_deltas (", + bbox_deltas_ps[1], + ")."); + + NODE_VALIDATION_CHECK(op, + class_probs_ps[0].compatible(bbox_deltas_ps[0]), + "Batch size inconsistent between class_probs (", + class_probs_ps[0], + ") and bbox deltas (", + bbox_deltas_ps[0], + ")."); + } + + if (image_shape_ps.is_static()) { + const auto image_shape_elem = image_shape_ps[0].get_length(); + NODE_VALIDATION_CHECK(op, + image_shape_elem >= 3 && image_shape_elem <= 4, + "Image_shape 1D tensor must have => 3 and <= 4 elements (image_shape_shape[0]", + image_shape_ps[0], + ")."); + } + + auto out_dim = DimType{}; + + if (class_probs_ps.rank().is_static() && bbox_deltas_ps.rank().is_static()) { + DimType::merge(out_dim, class_probs_ps[0], bbox_deltas_ps[0]); + } else if (class_probs_ps.rank().is_static()) { + out_dim = class_probs_ps[0]; + } else if (bbox_deltas_ps.rank().is_static()) { + out_dim = bbox_deltas_ps[0]; + } else { + out_dim = Dimension::dynamic(); + } + + auto& proposed_boxes_shape = output_shapes[0]; + proposed_boxes_shape.resize(2); + proposed_boxes_shape[0] = out_dim * op->get_attrs().post_nms_topn; + proposed_boxes_shape[1] = 5; +} +template +void shape_infer(const ov::op::v0::Proposal* op, const std::vector& input_shapes, std::vector& output_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 1); + ov::op::v0::infer_prop_shape(op, input_shapes, output_shapes); +} + +} // namespace v0 +} // namespace op +} // namespace ov + +namespace ov { +namespace op { +namespace v4 { + +template +void shape_infer(const ov::op::v4::Proposal* op, const std::vector& input_shapes, std::vector& output_shapes) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 2); + + ov::op::v0::infer_prop_shape(op, input_shapes, output_shapes); + const auto& proposals_ps = output_shapes[0]; + auto& out_ps = output_shapes[1]; + out_ps = T{proposals_ps[0]}; +} + +} // namespace v4 +} // 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 new file mode 100644 index 00000000000..820ece21bd7 --- /dev/null +++ b/src/core/shape_inference/include/roi_align_shape_inference.hpp @@ -0,0 +1,77 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "utils.hpp" + +namespace ov { +namespace op { +namespace v3 { + +template +void shape_infer(const ov::op::v3::ROIAlign* op, const std::vector& input_shapes, std::vector& output_shapes) { + using DimType = typename std::iterator_traits::value_type; + NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 1); + + 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(); + + 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); + + if (batch_indices_ps_rank.is_static()) { + NODE_VALIDATION_CHECK(op, + rois_ps[0].compatible(batch_indices_ps[0]), + "The first dimension of ROIs input must be equal to the first dimension ", + "of the batch indices input. Got: ", + rois_ps[0], + " and: ", + batch_indices_ps[0]); + } + } + + auto& output_shape = output_shapes[0]; + output_shape.resize(4); + output_shape[1] = input_ps_rank.is_static() ? input_ps[1] : -1; + output_shape[2] = op->get_pooled_h(); + output_shape[3] = op->get_pooled_w(); + + // if either of those 2 dimensions is static its value will be used + // for the first dimension of the output shape - 'NUM_ROIS' + if (rois_ps_rank.is_static() && batch_indices_ps_rank.is_static()) { + DimType::merge(output_shape[0], batch_indices_ps[0], rois_ps[0]); + } else if (rois_ps_rank.is_static()) { + output_shape[0] = rois_ps[0]; + } else if (batch_indices_ps_rank.is_static()) { + output_shape[0] = batch_indices_ps[0]; + } else { + output_shape[0] = Dimension::dynamic(); + } +} + +} // namespace v3 +} // namespace op +} // namespace ov \ No newline at end of file diff --git a/src/core/shape_inference/include/roll_shape_inference.hpp b/src/core/shape_inference/include/roll_shape_inference.hpp new file mode 100644 index 00000000000..f421a36ca47 --- /dev/null +++ b/src/core/shape_inference/include/roll_shape_inference.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#pragma once + +#include + +#include "utils.hpp" + +namespace ov { +namespace op { +namespace v7 { + +template +void shape_infer(const ov::op::v7::Roll* op, + const std::vector& input_shapes, + std::vector& output_shapes, + const std::map>& constant_data = {}) { + NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 1); + + const auto& data_pshape = input_shapes[0]; + const auto& shift_pshape = input_shapes[1]; + const auto& axes_pshape = input_shapes[2]; + + if (shift_pshape.rank().is_static()) { + const auto& shift_rank = shift_pshape.size(); + NODE_VALIDATION_CHECK(op, shift_rank <= 1, "Shift must be a scalar or 1D tensor."); + // If shift is a scalar, than axes can be arbitrary 1d tensor and we don't need + // to check shift shape consistency with axes, otherwise the check is needed. + if (shift_rank == 1) { + NODE_VALIDATION_CHECK(op, + shift_pshape.compatible(axes_pshape), + "If shift is a 1D vector, axes must be a 1D tensor of the same size."); + } + } + + if (axes_pshape.rank().is_static()) { + const auto& axes_rank = axes_pshape.size(); + NODE_VALIDATION_CHECK(op, axes_rank <= 1, "Axes must be a scalar or 1D tensor."); + } + + std::vector axes{}; + + if (get_data_as_int64(2, op, axes, constant_data)) { + if (data_pshape.rank().is_static()) { + const auto& data_rank = data_pshape.size(); + for (int64_t& axis : axes) { + NODE_VALIDATION_CHECK(op, + axis < static_cast(data_rank), + "Axes must be less than data tensor rank. Got " + "data tensor rank: ", + data_rank, + ", axis: ", + axis); + if (axis < 0) { + axis += static_cast(data_rank); + } + NODE_VALIDATION_CHECK(op, + axis >= 0, + "Axes must be positive or equal to zero. Got " + "axis: ", + axis); + } + } + } + + output_shapes[0] = input_shapes[0]; +} + +} // namespace v7 +} // namespace op +} // namespace ov \ No newline at end of file diff --git a/src/core/src/op/experimental_detectron_generate_proposals.cpp b/src/core/src/op/experimental_detectron_generate_proposals.cpp index 9efad71f2a8..6b4d35f9c37 100644 --- a/src/core/src/op/experimental_detectron_generate_proposals.cpp +++ b/src/core/src/op/experimental_detectron_generate_proposals.cpp @@ -4,6 +4,8 @@ #include "ngraph/op/experimental_detectron_generate_proposals.hpp" +#include + #include "itt.hpp" #include "ngraph/attribute_visitor.hpp" #include "ngraph/op/util/op_types.hpp" @@ -47,69 +49,15 @@ bool op::v6::ExperimentalDetectronGenerateProposalsSingleImage::visit_attributes void op::v6::ExperimentalDetectronGenerateProposalsSingleImage::validate_and_infer_types() { NGRAPH_OP_SCOPE(v6_ExperimentalDetectronGenerateProposalsSingleImage_validate_and_infer_types); - auto post_nms_count = static_cast(m_attrs.post_nms_count); - auto input_et = get_input_element_type(0); - set_output_size(2); - set_output_type(0, input_et, ov::Shape{post_nms_count, 4}); - set_output_type(1, input_et, ov::Shape{post_nms_count}); + std::vector output_shapes = {ov::PartialShape{}, ov::PartialShape{}}; + std::vector input_shapes = {get_input_partial_shape(0), + get_input_partial_shape(1), + get_input_partial_shape(2), + get_input_partial_shape(3)}; + shape_infer(this, input_shapes, output_shapes); - auto im_info_shape = get_input_partial_shape(0); - auto anchors_shape = get_input_partial_shape(1); - auto deltas_shape = get_input_partial_shape(2); - auto scores_shape = get_input_partial_shape(3); - - if (im_info_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - im_info_shape.rank().get_length() == 1, - "The 'input_im_info' input is expected to be a 1D. Got: ", - im_info_shape); - - NODE_VALIDATION_CHECK(this, - im_info_shape[0].is_dynamic() || im_info_shape[0] == 3, - "The 'input_im_info' shape is expected to be a [3]. Got: ", - im_info_shape); - } - - if (anchors_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - anchors_shape.rank().get_length() == 2, - "The 'input_anchors' input is expected to be a 2D. Got: ", - anchors_shape); - - NODE_VALIDATION_CHECK(this, - anchors_shape[1].is_dynamic() || anchors_shape[1] == 4, - "The second dimension of 'input_anchors' should be 4. Got: ", - anchors_shape[1]); - } - - if (deltas_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - deltas_shape.rank().get_length() == 3, - "The 'input_deltas' input is expected to be a 3D. Got: ", - deltas_shape); - } - if (scores_shape.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - scores_shape.rank().get_length() == 3, - "The 'input_scores' input is expected to be a 3D. Got: ", - scores_shape); - } - if (deltas_shape.rank().is_static() && scores_shape.rank().is_static()) { - NODE_VALIDATION_CHECK( - this, - deltas_shape[1].is_dynamic() || scores_shape[1].is_dynamic() || deltas_shape[1] == scores_shape[1], - "Heights for inputs 'input_deltas' and 'input_scores' should be " - "equal. Got: ", - deltas_shape[1], - scores_shape[1]); - - NODE_VALIDATION_CHECK( - this, - deltas_shape[2].is_dynamic() || scores_shape[2].is_dynamic() || deltas_shape[2] == scores_shape[2], - "Width for inputs 'input_deltas' and 'input_scores' should be " - "equal. Got: ", - deltas_shape[2], - scores_shape[2]); - } + const auto& input_et = get_input_element_type(0); + set_output_type(0, input_et, output_shapes[0]); + set_output_type(1, input_et, output_shapes[1]); } diff --git a/src/core/src/op/proposal.cpp b/src/core/src/op/proposal.cpp index 91db02c34f8..c0eea79ae52 100644 --- a/src/core/src/op/proposal.cpp +++ b/src/core/src/op/proposal.cpp @@ -4,6 +4,8 @@ #include "ngraph/op/proposal.hpp" +#include + #include "itt.hpp" #include "ngraph/op/constant.hpp" @@ -21,12 +23,7 @@ op::v0::Proposal::Proposal(const Output& class_probs, constructor_validate_and_infer_types(); } -void op::v0::Proposal::validate_and_infer_types() { - NGRAPH_OP_SCOPE(v0_Proposal_validate_and_infer_types); - const auto& class_probs_ps = get_input_partial_shape(0); - const auto& bbox_deltas_ps = get_input_partial_shape(1); - const auto& image_shape_ps = get_input_partial_shape(2); - Dimension out_dim = Dimension::dynamic(); +void op::v0::Proposal::validate_element_types() { NODE_VALIDATION_CHECK(this, get_input_element_type(0).is_real(), "Proposal layer input class_probs should have floating point type (", @@ -44,63 +41,17 @@ void op::v0::Proposal::validate_and_infer_types() { "Proposal layer input image_shape should have floating point type (", get_input_element_type(2), ")."); +} - NODE_VALIDATION_CHECK(this, - class_probs_ps.rank().compatible(4), - "Proposal layer shape class_probs should be rank 4 compatible (", - class_probs_ps, - ")."); - - NODE_VALIDATION_CHECK(this, - bbox_deltas_ps.rank().compatible(4), - "Proposal layer shape bbox_deltas should be rank 4 compatible (", - bbox_deltas_ps, - ")."); - - NODE_VALIDATION_CHECK(this, - image_shape_ps.rank().compatible(1), - "Proposal layer shape image_shape should be rank 1 compatible (", - image_shape_ps, - ")."); - - if (bbox_deltas_ps.is_static() && class_probs_ps.is_static()) { - // class probs and bbox deltas shapes are static, check anchor count and batch number - // consistency - NODE_VALIDATION_CHECK(this, - class_probs_ps[1].get_length() * 2 == bbox_deltas_ps[1].get_length(), - "Anchor number inconsistent between class_probs (", - class_probs_ps[1].get_length() / 2, - "), and bbox_deltas (", - bbox_deltas_ps[1].get_length() / 4, - ")."); - - NODE_VALIDATION_CHECK(this, - class_probs_ps[0] == bbox_deltas_ps[0], - "Batch size inconsistent between class_probs (", - class_probs_ps[0], - ") and bbox deltas (", - bbox_deltas_ps[0], - ")."); - } - - if (image_shape_ps.is_static()) { - NODE_VALIDATION_CHECK(this, - image_shape_ps[0].get_length() >= 3 && image_shape_ps[0].get_length() <= 4, - "Image_shape 1D tensor must have => 3 and <= 4 elements (image_shape_shape[0]", - image_shape_ps[0], - ")."); - } - - if (class_probs_ps.rank().is_static() && bbox_deltas_ps.rank().is_static()) { - out_dim = (class_probs_ps[0] & bbox_deltas_ps[0]); - } else if (class_probs_ps.rank().is_static()) { - out_dim = class_probs_ps[0]; - } else if (bbox_deltas_ps.rank().is_static()) { - out_dim = bbox_deltas_ps[0]; - } - - // intersect the batch size - set_output_type(0, get_input_element_type(0), ov::PartialShape{out_dim * m_attrs.post_nms_topn, 5}); +void op::v0::Proposal::validate_and_infer_types() { + NGRAPH_OP_SCOPE(v0_Proposal_validate_and_infer_types); + validate_element_types(); + std::vector output_shapes = {ov::PartialShape{}}; + std::vector input_shapes = {get_input_partial_shape(0), + get_input_partial_shape(1), + get_input_partial_shape(2)}; + shape_infer(this, input_shapes, output_shapes); + set_output_type(0, get_input_element_type(0), output_shapes[0]); } shared_ptr op::v0::Proposal::clone_with_new_inputs(const OutputVector& new_args) const { @@ -140,14 +91,17 @@ op::v4::Proposal::Proposal(const Output& class_probs, void op::v4::Proposal::validate_and_infer_types() { NGRAPH_OP_SCOPE(v4_Proposal_validate_and_infer_types); - v0::Proposal::validate_and_infer_types(); - // Output shape was inferred in v0's validate_and_infer_types - const auto proposals_ps = get_output_partial_shape(0); - auto out_ps = ov::PartialShape{Dimension::dynamic()}; - if (proposals_ps.rank().is_static() && proposals_ps.rank().compatible(2)) { - out_ps = ov::PartialShape{proposals_ps[0]}; - } - set_output_type(1, get_input_element_type(0), out_ps); + v0::Proposal::validate_element_types(); + + std::vector output_shapes = {ov::PartialShape{}, ov::PartialShape{}}; + std::vector input_shapes = {get_input_partial_shape(0), + get_input_partial_shape(1), + get_input_partial_shape(2)}; + shape_infer(this, input_shapes, output_shapes); + + const auto& input0_type = get_input_element_type(0); + set_output_type(0, input0_type, output_shapes[0]); + set_output_type(1, input0_type, output_shapes[1]); } std::shared_ptr op::v4::Proposal::clone_with_new_inputs(const OutputVector& new_args) const { diff --git a/src/core/src/op/roi_align.cpp b/src/core/src/op/roi_align.cpp index 9660e18e4e2..5e655a94ce1 100644 --- a/src/core/src/op/roi_align.cpp +++ b/src/core/src/op/roi_align.cpp @@ -4,6 +4,8 @@ #include "ngraph/op/roi_align.hpp" +#include + #include "itt.hpp" #include "ngraph/runtime/host_tensor.hpp" #include "ngraph/runtime/reference/roi_align.hpp" @@ -69,69 +71,25 @@ void op::v3::ROIAlign::validate_and_infer_types() { "The data type for batch indices is expected to be an integer. Got: ", get_input_element_type(2)); - const auto& input_ps = get_input_partial_shape(0); - NODE_VALIDATION_CHECK(this, - input_ps.rank().compatible(4), - "Expected a 4D tensor for the input data. Got: ", - input_ps); - - const auto& rois_ps = get_input_partial_shape(1); - NODE_VALIDATION_CHECK(this, - rois_ps.rank().compatible(2), - "Expected a 2D tensor for the ROIs input. Got: ", - rois_ps); - - const auto& batch_indices_ps = get_input_partial_shape(2); - NODE_VALIDATION_CHECK(this, - 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(this, - rois_second_dim.compatible(4), - "The second dimension of ROIs input should contain box coordinates. ", - "This dimension is expected to be equal to 4. Got: ", - rois_second_dim); - - if (batch_indices_ps.rank().is_static()) { - NODE_VALIDATION_CHECK(this, - rois_ps[0].compatible(batch_indices_ps[0]), - "The first dimension of ROIs input must be equal to the first dimension ", - "of the batch indices input. Got: ", - rois_ps[0], - " and: ", - batch_indices_ps[0]); - } - } - - // the output shape should have the following format [NUM_ROIS, C, pooled_h, pooled_w] - auto output_shape = ov::PartialShape{{Dimension::dynamic(), - input_ps[1], - Dimension{static_cast(m_pooled_h)}, - Dimension{static_cast(m_pooled_w)}}}; - - // if either of those 2 dimensions is static its value will be used - // for the first dimension of the output shape - 'NUM_ROIS' - if (rois_ps.rank().is_static() && rois_ps[0].is_static()) { - output_shape[0] = rois_ps[0]; - } else if (batch_indices_ps.rank().is_static() && batch_indices_ps[0].is_static()) { - output_shape[0] = batch_indices_ps[0]; - } + std::vector output_shapes = {ov::PartialShape{}}; + const std::vector input_shapes = {get_input_partial_shape(0), + get_input_partial_shape(1), + get_input_partial_shape(2)}; + shape_infer(this, input_shapes, output_shapes); set_output_size(1); - set_output_type(0, get_input_element_type(0), output_shape); + set_output_type(0, get_input_element_type(0), output_shapes[0]); + + const auto& input_ps = get_input_partial_shape(0); // 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()) { + if ((output_shapes[0])[0].is_dynamic()) { set_input_is_relevant_to_shape(1); set_input_is_relevant_to_shape(2); } diff --git a/src/core/src/op/roll.cpp b/src/core/src/op/roll.cpp index 0d5308255c6..43e3edb8a1f 100644 --- a/src/core/src/op/roll.cpp +++ b/src/core/src/op/roll.cpp @@ -5,6 +5,7 @@ #include "ngraph/op/roll.hpp" #include +#include #include "itt.hpp" @@ -31,54 +32,13 @@ void op::v7::Roll::validate_and_infer_types() { axes_et.is_dynamic() || axes_et == element::i32 || axes_et == element::i64, "Axes must have int32 or int64 element type."); - const auto& data_pshape = get_input_partial_shape(0); - const auto& shift_pshape = get_input_partial_shape(1); - const auto& axes_pshape = get_input_partial_shape(2); + std::vector output_shapes = {ov::PartialShape{}}; + const std::vector input_shapes = {get_input_partial_shape(0), + get_input_partial_shape(1), + get_input_partial_shape(2)}; + shape_infer(this, input_shapes, output_shapes); - if (shift_pshape.is_static()) { - const auto& shift_rank = shift_pshape.rank().get_length(); - NODE_VALIDATION_CHECK(this, shift_rank <= 1, "Shift must be a scalar or 1D tensor."); - } - - if (axes_pshape.is_static()) { - const auto& axes_rank = axes_pshape.rank().get_length(); - NODE_VALIDATION_CHECK(this, axes_rank <= 1, "Axes must be a scalar or 1D tensor."); - } - - // If shift is a scalar, than axes can be arbitrary 1d tensor and we don't need - // to check shift shape consistency with axes, otherwise the check is needed. - if (!(shift_pshape.is_static() && ngraph::is_scalar(shift_pshape.to_shape()))) { - NODE_VALIDATION_CHECK(this, - shift_pshape.compatible(axes_pshape), - "If shift is a 1D vector, axes must be a 1D tensor of the same size."); - } - - if (const auto& const_axes = get_constant_from_source(input_value(2))) { - auto axes = const_axes->cast_vector(); - - if (data_pshape.is_static()) { - const auto& data_rank = data_pshape.rank().get_length(); - for (int64_t& axis : axes) { - NODE_VALIDATION_CHECK(this, - axis < data_rank, - "Axes must be less than data tensor rank. Got " - "data tensor rank: ", - data_rank, - ", axis: ", - axis); - if (axis < 0) { - axis += data_rank; - } - NODE_VALIDATION_CHECK(this, - axis >= 0, - "Axes must be positive or equal to zero. Got " - "axis: ", - axis); - } - } - } - - set_output_type(0, get_input_element_type(0), get_input_partial_shape(0)); + set_output_type(0, get_input_element_type(0), output_shapes[0]); } bool op::v7::Roll::visit_attributes(AttributeVisitor& visitor) { diff --git a/src/tests/unit/cpu/shape_inference_test/experimental_detectron_generate_proposal.cpp b/src/tests/unit/cpu/shape_inference_test/experimental_detectron_generate_proposal.cpp new file mode 100644 index 00000000000..ecf912a5a72 --- /dev/null +++ b/src/tests/unit/cpu/shape_inference_test/experimental_detectron_generate_proposal.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include + +using namespace ov; +using ExperimentalProposals = op::v6::ExperimentalDetectronGenerateProposalsSingleImage; + +TEST(StaticShapeInferenceTest, ExperimentalProposalsTest) { + ExperimentalProposals::Attributes attrs; + attrs.min_size = 0.0f; + attrs.nms_threshold = 0.699999988079071f; + attrs.post_nms_count = 1000; + attrs.pre_nms_count = 1000; + size_t post_nms_count = 1000; + + auto im_info = std::make_shared(element::f32, PartialShape{-1}); + auto anchors = std::make_shared(element::f32, PartialShape{-1, -1}); + auto deltas = std::make_shared(element::f32, PartialShape{-1, -1, -1}); + auto scores = std::make_shared(element::f32, PartialShape{-1, -1, -1}); + + auto proposals = std::make_shared(im_info, anchors, deltas, scores, attrs); + + const std::vector input_shapes = {ov::StaticShape{3}, + ov::StaticShape{201600, 4}, + ov::StaticShape{12, 200, 336}, + ov::StaticShape{3, 200, 336}}; + std::vector output_shapes = {ov::StaticShape{}, ov::StaticShape{}}; + shape_inference(proposals.get(), input_shapes, output_shapes); + + ASSERT_EQ(output_shapes[0], (StaticShape{post_nms_count, 4})); + ASSERT_EQ(output_shapes[1], (StaticShape{post_nms_count})); +} \ No newline at end of file diff --git a/src/tests/unit/cpu/shape_inference_test/proposal.cpp b/src/tests/unit/cpu/shape_inference_test/proposal.cpp new file mode 100644 index 00000000000..af5889e117f --- /dev/null +++ b/src/tests/unit/cpu/shape_inference_test/proposal.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include + +using namespace ov; + +TEST(StaticShapeInferenceTest, ProposalV0Test) { + op::v0::Proposal::Attributes attrs; + attrs.base_size = 1; + attrs.pre_nms_topn = 20; + attrs.post_nms_topn = 200; + const size_t batch_size = 7; + + auto class_probs = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); + auto class_bbox_deltas = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); + auto image_shape = std::make_shared(element::f32, PartialShape{-1}); + auto op = std::make_shared(class_probs, class_bbox_deltas, image_shape, attrs); + const std::vector input_shapes = {ov::StaticShape{batch_size, 12, 34, 62}, + ov::StaticShape{batch_size, 24, 34, 62}, + ov::StaticShape{3}}; + std::vector output_shapes = {ov::StaticShape{}}; + shape_inference(op.get(), input_shapes, output_shapes); + + ASSERT_EQ(output_shapes[0], (StaticShape{batch_size * attrs.post_nms_topn, 5})); +} + +TEST(StaticShapeInferenceTest, ProposalV4Test) { + op::v0::Proposal::Attributes attrs; + attrs.base_size = 1; + attrs.pre_nms_topn = 20; + attrs.post_nms_topn = 200; + const size_t batch_size = 7; + + auto class_probs = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); + auto class_bbox_deltas = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); + auto image_shape = std::make_shared(element::f32, PartialShape{-1}); + auto op = std::make_shared(class_probs, class_bbox_deltas, image_shape, attrs); + const std::vector input_shapes = {ov::StaticShape{batch_size, 12, 34, 62}, + ov::StaticShape{batch_size, 24, 34, 62}, + ov::StaticShape{3}}; + std::vector output_shapes = {ov::StaticShape{}, ov::StaticShape{}}; + shape_inference(op.get(), input_shapes, output_shapes); + + ASSERT_EQ(output_shapes[0], (StaticShape{batch_size * attrs.post_nms_topn, 5})); + ASSERT_EQ(output_shapes[1], (StaticShape{batch_size * attrs.post_nms_topn})); +} \ No newline at end of file diff --git a/src/tests/unit/cpu/shape_inference_test/roi_align_shape_inference.cpp b/src/tests/unit/cpu/shape_inference_test/roi_align_shape_inference.cpp new file mode 100644 index 00000000000..d21c099baf9 --- /dev/null +++ b/src/tests/unit/cpu/shape_inference_test/roi_align_shape_inference.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include + +using namespace ov; + +TEST(StaticShapeInferenceTest, ROIAlignTest) { + const auto data = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); + const auto rois = std::make_shared(element::f32, PartialShape{-1, -1}); + const auto batch_indices = std::make_shared(element::i32, PartialShape{-1}); + const auto op = std::make_shared(data, rois, batch_indices, 2, 2, 1, 1.0f, "avg"); + const std::vector input_shapes = {ov::StaticShape{2, 3, 5, 5}, + ov::StaticShape{7, 4}, + ov::StaticShape{7}}; + std::vector output_shapes = {ov::StaticShape{}}; + shape_inference(op.get(), input_shapes, output_shapes); + + ASSERT_EQ(output_shapes[0], (StaticShape{7, 3, 2, 2})); +} \ No newline at end of file diff --git a/src/tests/unit/cpu/shape_inference_test/roll_shape_inference.cpp b/src/tests/unit/cpu/shape_inference_test/roll_shape_inference.cpp new file mode 100644 index 00000000000..89aa0701860 --- /dev/null +++ b/src/tests/unit/cpu/shape_inference_test/roll_shape_inference.cpp @@ -0,0 +1,50 @@ +// Copyright (C) 2018-2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include + +#include +#include +#include +#include +#include + +TEST(StaticShapeInferenceTest, RollTest) { + auto arg = + std::make_shared(ov::element::f32, + ov::PartialShape{ov::Dimension::dynamic(), ov::Dimension::dynamic()}); + auto shift = std::make_shared(ov::element::i64, ov::PartialShape{ov::Dimension::dynamic()}); + auto axes = std::make_shared(ov::element::i32, ov::PartialShape{ov::Dimension::dynamic()}); + + auto roll = std::make_shared(arg, shift, axes); + + int32_t axes_val[] = {0, 1, -1}; + auto axes_tensor = std::make_shared(ov::element::i32, ov::Shape{3}, axes_val); + + std::map> constant_data; + constant_data[2] = axes_tensor; + + const std::vector input_shapes = {ov::StaticShape{3, 3, 3}, + ov::StaticShape{3}, + ov::StaticShape{3}}; + std::vector output_shapes = {ov::StaticShape{}}; + shape_inference(roll.get(), input_shapes, output_shapes, constant_data); + ASSERT_EQ(output_shapes[0], input_shapes[0]); +} + +TEST(StaticShapeInferenceTest, RollTestWithConstAxis) { + auto arg = + std::make_shared(ov::element::f32, + ov::PartialShape{ov::Dimension::dynamic(), ov::Dimension::dynamic()}); + auto shift = std::make_shared(ov::element::i64, ov::PartialShape{ov::Dimension::dynamic()}); + auto axes = std::make_shared(ov::element::i32, ov::Shape{3}, std::vector{0, 1, -1}); + auto roll = std::make_shared(arg, shift, axes); + + const std::vector input_shapes = {ov::StaticShape{3, 3, 3}, + ov::StaticShape{3}, + ov::StaticShape{3}}; + std::vector output_shapes = {ov::StaticShape{}}; + shape_inference(roll.get(), input_shapes, output_shapes); + ASSERT_EQ(output_shapes[0], input_shapes[0]); +}