[ShapeInfer] Review Proposal op for shape inference aspects (#17578)

* Review Proposal op for shape infer aspects:
- Check partial shape and label propagation.
- Check template implementation of shape_infer.
- Add/update unit test for static and dynamic shapes.
- Drop using `ngraph` namespace from reviewed operator.

* Add missing includes

* Correct shape infer if batch defined in input only

* Improvement in tensor accessor:
- Make possible to create TA as constexpr
- Make empty TA as cons reference to existing object

* Remove not used tensor accessor
This commit is contained in:
Pawel Raasz 2023-05-26 07:24:41 +02:00 committed by GitHub
parent b1b5d65951
commit 23258c8bcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 573 additions and 421 deletions

View File

@ -64,6 +64,13 @@ public:
const Attributes& get_attrs() const { const Attributes& get_attrs() const {
return m_attrs; return m_attrs;
} }
/**
* @brief Set the Proposal operator attributes.
* @param attrs Attributes to be set.
*/
void set_attrs(Attributes attrs);
bool visit_attributes(AttributeVisitor& visitor) override; bool visit_attributes(AttributeVisitor& visitor) override;
protected: protected:
@ -93,9 +100,6 @@ public:
void validate_and_infer_types() override; void validate_and_infer_types() override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override; std::shared_ptr<Node> clone_with_new_inputs(const OutputVector& new_args) const override;
const Attributes& get_attrs() const {
return m_attrs;
}
}; };
} // namespace v4 } // namespace v4
} // namespace op } // namespace op

View File

@ -18,8 +18,7 @@ namespace v6 {
// 2. out_rois_shape = [number_of_ROIs, 4] // 2. out_rois_shape = [number_of_ROIs, 4]
template <class TShape> template <class TShape>
std::vector<TShape> shape_infer(const ExperimentalDetectronROIFeatureExtractor* op, std::vector<TShape> shape_infer(const ExperimentalDetectronROIFeatureExtractor* op,
const std::vector<TShape>& input_shapes, const std::vector<TShape>& input_shapes) {
const ITensorAccessor& tensor_accessor = make_tensor_accessor()) {
using TDim = typename TShape::value_type; using TDim = typename TShape::value_type;
using namespace ov::util; using namespace ov::util;
NODE_VALIDATION_CHECK(op, input_shapes.size() >= 2); NODE_VALIDATION_CHECK(op, input_shapes.size() >= 2);

View File

@ -4,16 +4,18 @@
#pragma once #pragma once
#include <openvino/op/proposal.hpp> #include "dimension_util.hpp"
#include "openvino/op/proposal.hpp"
#include "utils.hpp"
namespace ov { namespace ov {
namespace op { namespace op {
namespace v0 { namespace proposal {
template <class OpType, class ShapeType> template <class TOp, class TShape>
void infer_prop_shape(const OpType* op, TShape shape_infer_boxes(const TOp* op, const std::vector<TShape>& input_shapes) {
const std::vector<ShapeType>& input_shapes, using TDim = typename TShape::value_type;
std::vector<ShapeType>& output_shapes) { NODE_VALIDATION_CHECK(op, input_shapes.size() == 3);
using DimType = typename std::iterator_traits<typename ShapeType::iterator>::value_type;
const auto& class_probs_ps = input_shapes[0]; const auto& class_probs_ps = input_shapes[0];
const auto& bbox_deltas_ps = input_shapes[1]; const auto& bbox_deltas_ps = input_shapes[1];
const auto& image_shape_ps = input_shapes[2]; const auto& image_shape_ps = input_shapes[2];
@ -30,62 +32,56 @@ void infer_prop_shape(const OpType* op,
bbox_deltas_ps, bbox_deltas_ps,
")."); ").");
NODE_VALIDATION_CHECK(op, if (image_shape_ps.rank().is_static()) {
image_shape_ps.rank().compatible(1), NODE_VALIDATION_CHECK(
"Proposal layer shape image_shape should be rank 1 compatible (", op,
image_shape_ps, image_shape_ps.size() == 1 && (image_shape_ps[0].compatible(3) || image_shape_ps[0].compatible(4)),
")."); "Image_shape must be 1-D tensor and has got 3 or 4 elements (image_shape_shape[0]",
if (bbox_deltas_ps.rank().is_static() && class_probs_ps.rank().is_static()) { image_shape_ps,
").");
}
const auto is_bbox_rank_dynamic = bbox_deltas_ps.rank().is_dynamic();
TShape proposed_boxes_shape;
proposed_boxes_shape.reserve(2);
if (class_probs_ps.rank().is_static()) {
proposed_boxes_shape.push_back(class_probs_ps[0]);
// check anchor count and batch number consistency // check anchor count and batch number consistency
NODE_VALIDATION_CHECK(op, NODE_VALIDATION_CHECK(op,
bbox_deltas_ps[1].compatible(class_probs_ps[1] * 2), is_bbox_rank_dynamic || bbox_deltas_ps[1].compatible(class_probs_ps[1] * 2),
"Anchor number inconsistent between class_probs (", "Anchor number inconsistent between class_probs (",
class_probs_ps[1] * 2, class_probs_ps[1] * 2,
"), and bbox_deltas (", "), and bbox_deltas (",
bbox_deltas_ps[1], 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()) {
OPENVINO_ASSERT(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 { } else {
out_dim = Dimension::dynamic(); proposed_boxes_shape.emplace_back(ov::util::dim::inf_bound);
} }
auto& proposed_boxes_shape = output_shapes[0]; NODE_VALIDATION_CHECK(
proposed_boxes_shape.resize(2); op,
proposed_boxes_shape[0] = out_dim * op->get_attrs().post_nms_topn; is_bbox_rank_dynamic || TDim::merge(proposed_boxes_shape[0], proposed_boxes_shape[0], bbox_deltas_ps[0]),
proposed_boxes_shape[1] = 5; "Batch size inconsistent between class_probs (",
} class_probs_ps[0],
template <class T> ") and bbox deltas (",
void shape_infer(const ov::op::v0::Proposal* op, const std::vector<T>& input_shapes, std::vector<T>& output_shapes) { bbox_deltas_ps[0],
NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 1); ").");
ov::op::v0::infer_prop_shape(op, input_shapes, output_shapes);
}
proposed_boxes_shape[0] *= op->get_attrs().post_nms_topn;
proposed_boxes_shape.emplace_back(5);
return proposed_boxes_shape;
}
} // namespace proposal
namespace v0 {
template <class TShape>
std::vector<TShape> shape_infer(const Proposal* op, const std::vector<TShape>& input_shapes) {
return {ov::op::proposal::shape_infer_boxes(op, input_shapes)};
}
} // namespace v0 } // namespace v0
} // namespace op } // namespace op
} // namespace ov } // namespace ov
@ -93,15 +89,11 @@ void shape_infer(const ov::op::v0::Proposal* op, const std::vector<T>& input_sha
namespace ov { namespace ov {
namespace op { namespace op {
namespace v4 { namespace v4 {
template <class TShape>
template <class T> std::vector<TShape> shape_infer(const Proposal* op, const std::vector<TShape>& input_shapes) {
void shape_infer(const ov::op::v4::Proposal* op, const std::vector<T>& input_shapes, std::vector<T>& output_shapes) { auto output_shapes = std::vector<TShape>(2, ov::op::proposal::shape_infer_boxes(op, input_shapes));
NODE_VALIDATION_CHECK(op, input_shapes.size() == 3 && output_shapes.size() == 2); output_shapes[1].resize(1);
return output_shapes;
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 v4

View File

@ -18,8 +18,6 @@ public:
* @return Tensor to data at port. * @return Tensor to data at port.
*/ */
virtual Tensor operator()(size_t port) const = 0; virtual Tensor operator()(size_t port) const = 0;
virtual ~ITensorAccessor() = default;
}; };
/** /**
@ -35,14 +33,14 @@ public:
* @tparam TContainer Type of tensor container. * @tparam TContainer Type of tensor container.
*/ */
template <class TContainer> template <class TContainer>
class TensorAccessor : public ITensorAccessor { class TensorAccessor final : public ITensorAccessor {
public: public:
/** /**
* @brief Construct a new Tensor Accessor object for tensors container. * @brief Construct a new Tensor Accessor object for tensors container.
* *
* @param tensors Pointer to container with tensors. * @param tensors Pointer to container with tensors.
*/ */
TensorAccessor(const TContainer* tensors) : m_tensors{tensors} {} constexpr TensorAccessor(const TContainer* tensors) : m_tensors{tensors} {}
/** /**
* @brief Get tensor for given port number. * @brief Get tensor for given port number.
@ -79,7 +77,7 @@ Tensor TensorAccessor<void>::operator()(size_t port) const;
* @return TensorContainer for specific type. * @return TensorContainer for specific type.
*/ */
template <class TContainer> template <class TContainer>
auto make_tensor_accessor(const TContainer& c) -> TensorAccessor<TContainer> { constexpr auto make_tensor_accessor(const TContainer& c) -> TensorAccessor<TContainer> {
return TensorAccessor<TContainer>(&c); return TensorAccessor<TContainer>(&c);
} }
@ -88,5 +86,5 @@ auto make_tensor_accessor(const TContainer& c) -> TensorAccessor<TContainer> {
* *
* @return TensorAccessor to return empty tensor. * @return TensorAccessor to return empty tensor.
*/ */
auto make_tensor_accessor() -> TensorAccessor<void>; auto make_tensor_accessor() -> const TensorAccessor<void>&;
} // namespace ov } // namespace ov

View File

@ -41,8 +41,8 @@ Tensor TensorAccessor<void>::operator()(size_t) const {
return empty; return empty;
} }
auto make_tensor_accessor() -> TensorAccessor<void> { auto make_tensor_accessor() -> const TensorAccessor<void>& {
static const auto empty_tensor_accessor = TensorAccessor<void>(nullptr); static constexpr auto empty_tensor_accessor = TensorAccessor<void>(nullptr);
return empty_tensor_accessor; return empty_tensor_accessor;
} }
} // namespace ov } // namespace ov

View File

@ -2,16 +2,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
#include "ngraph/op/proposal.hpp" #include "openvino/op/proposal.hpp"
#include <proposal_shape_inference.hpp>
#include "itt.hpp" #include "itt.hpp"
#include "ngraph/op/constant.hpp" #include "openvino/core/validation_util.hpp"
#include "proposal_shape_inference.hpp"
using namespace std;
using namespace ngraph;
namespace ov {
op::v0::Proposal::Proposal(const Output<Node>& class_probs, op::v0::Proposal::Proposal(const Output<Node>& class_probs,
const Output<Node>& bbox_deltas, const Output<Node>& bbox_deltas,
const Output<Node>& image_shape, const Output<Node>& image_shape,
@ -43,19 +40,20 @@ void op::v0::Proposal::validate_element_types() {
void op::v0::Proposal::validate_and_infer_types() { void op::v0::Proposal::validate_and_infer_types() {
OV_OP_SCOPE(v0_Proposal_validate_and_infer_types); OV_OP_SCOPE(v0_Proposal_validate_and_infer_types);
validate_element_types(); validate_element_types();
std::vector<ov::PartialShape> output_shapes = {ov::PartialShape{}}; OPENVINO_SUPPRESS_DEPRECATED_START
std::vector<ov::PartialShape> input_shapes = {get_input_partial_shape(0), const auto intput_shapes = get_node_input_partial_shapes(*this);
get_input_partial_shape(1), OPENVINO_SUPPRESS_DEPRECATED_END
get_input_partial_shape(2)}; const auto output_shapes = shape_infer(this, intput_shapes);
shape_infer(this, input_shapes, output_shapes);
set_output_type(0, get_input_element_type(0), output_shapes[0]); set_output_type(0, get_input_element_type(0), output_shapes[0]);
} }
shared_ptr<Node> op::v0::Proposal::clone_with_new_inputs(const OutputVector& new_args) const { std::shared_ptr<Node> op::v0::Proposal::clone_with_new_inputs(const OutputVector& new_args) const {
OV_OP_SCOPE(v0_Proposal_clone_with_new_inputs); OV_OP_SCOPE(v0_Proposal_clone_with_new_inputs);
check_new_args_count(this, new_args); check_new_args_count(this, new_args);
return make_shared<op::v0::Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs); return std::make_shared<Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs);
} }
bool op::v0::Proposal::visit_attributes(AttributeVisitor& visitor) { bool op::v0::Proposal::visit_attributes(AttributeVisitor& visitor) {
@ -77,32 +75,40 @@ bool op::v0::Proposal::visit_attributes(AttributeVisitor& visitor) {
return true; return true;
} }
void op::v0::Proposal::set_attrs(Attributes attrs) {
m_attrs = std::move(attrs);
}
// --- v4 ---
op::v4::Proposal::Proposal(const Output<Node>& class_probs, op::v4::Proposal::Proposal(const Output<Node>& class_probs,
const Output<Node>& class_bbox_deltas, const Output<Node>& class_bbox_deltas,
const Output<Node>& image_shape, const Output<Node>& image_shape,
const op::v0::Proposal::Attributes& attrs) const op::v0::Proposal::Attributes& attrs)
: v0::Proposal(class_probs, class_bbox_deltas, image_shape, attrs) { : v0::Proposal() {
set_arguments({class_probs, class_bbox_deltas, image_shape});
set_attrs(attrs);
constructor_validate_and_infer_types(); constructor_validate_and_infer_types();
} }
void op::v4::Proposal::validate_and_infer_types() { void op::v4::Proposal::validate_and_infer_types() {
OV_OP_SCOPE(v4_Proposal_validate_and_infer_types); OV_OP_SCOPE(v4_Proposal_validate_and_infer_types);
v0::Proposal::validate_element_types();
std::vector<ov::PartialShape> output_shapes = {ov::PartialShape{}, ov::PartialShape{}}; validate_element_types();
std::vector<ov::PartialShape> input_shapes = {get_input_partial_shape(0), OPENVINO_SUPPRESS_DEPRECATED_START
get_input_partial_shape(1), const auto intput_shapes = get_node_input_partial_shapes(*this);
get_input_partial_shape(2)}; OPENVINO_SUPPRESS_DEPRECATED_END
shape_infer(this, input_shapes, output_shapes); const auto output_shapes = shape_infer(this, intput_shapes);
const auto& out_et = get_input_element_type(0);
const auto& input0_type = get_input_element_type(0); for (size_t i = 0; i < output_shapes.size(); ++i) {
set_output_type(0, input0_type, output_shapes[0]); set_output_type(i, out_et, output_shapes[i]);
set_output_type(1, input0_type, output_shapes[1]); }
m_attrs.infer_probs = true; // Proposal v4 requires default true of infer_probs so output_1 has valid data. m_attrs.infer_probs = true; // Proposal v4 requires default true of infer_probs so output_1 has valid data.
} }
std::shared_ptr<Node> op::v4::Proposal::clone_with_new_inputs(const OutputVector& new_args) const { std::shared_ptr<Node> op::v4::Proposal::clone_with_new_inputs(const OutputVector& new_args) const {
OV_OP_SCOPE(v4_Proposal_clone_with_new_inputs); OV_OP_SCOPE(v4_Proposal_clone_with_new_inputs);
check_new_args_count(this, new_args); check_new_args_count(this, new_args);
return make_shared<op::v4::Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs); return std::make_shared<Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs);
} }
} // namespace ov

View File

@ -4,12 +4,14 @@
#include "ngraph/op/proposal.hpp" #include "ngraph/op/proposal.hpp"
#include "gtest/gtest.h" #include "common_test_utils/test_assertions.hpp"
#include "gmock/gmock.h"
#include "ngraph/ngraph.hpp" #include "ngraph/ngraph.hpp"
#include "util/type_prop.hpp" #include "util/type_prop.hpp"
using namespace std; using namespace std;
using namespace ngraph; using namespace ngraph;
using namespace testing;
// ------------------------------ V0 ------------------------------ // ------------------------------ V0 ------------------------------
@ -19,15 +21,9 @@ TEST(type_prop, proposal_v0_invalid_class_probs_rank) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape class_probs should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape class_probs should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_anchor_count) { TEST(type_prop, proposal_v0_invalid_anchor_count) {
@ -36,15 +32,9 @@ TEST(type_prop, proposal_v0_invalid_anchor_count) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Anchor number inconsistent between"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Anchor number inconsistent between"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_class_bbox_deltas_rank) { TEST(type_prop, proposal_v0_invalid_class_bbox_deltas_rank) {
@ -53,15 +43,9 @@ TEST(type_prop, proposal_v0_invalid_class_bbox_deltas_rank) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape bbox_deltas should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape bbox_deltas should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_image_shape_rank) { TEST(type_prop, proposal_v0_invalid_image_shape_rank) {
@ -70,15 +54,9 @@ TEST(type_prop, proposal_v0_invalid_image_shape_rank) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{2, 1}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{2, 1});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Image_shape must be 1-D tensor"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape image_shape should be rank 1 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_image_shape_size) { TEST(type_prop, proposal_v0_invalid_image_shape_size) {
@ -87,17 +65,31 @@ TEST(type_prop, proposal_v0_invalid_image_shape_size) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Image_shape must be 1-D tensor and has got 3 or 4 elements (image_shape_shape[0]"));
FAIL() << "Invalid input tensor rank."; }
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING( TEST(type_prop, proposal_v0_default_ctor) {
error.what(), op::ProposalAttrs attrs;
std::string("Image_shape 1D tensor must have => 3 and <= 4 elements (image_shape_shape[0]")); attrs.base_size = 1;
} catch (...) { attrs.pre_nms_topn = 20;
FAIL() << "Deduced type check failed for unexpected reason"; attrs.post_nms_topn = 200;
} const size_t batch_size = 7;
auto class_probs = make_shared<op::Parameter>(element::f16, Shape{batch_size, 12, 34, 62});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f16, Shape{batch_size, 24, 34, 62});
auto image_shape = make_shared<op::Parameter>(element::f16, Shape{3});
auto op = make_shared<op::v0::Proposal>();
op->set_arguments(OutputVector{class_probs, class_bbox_deltas, image_shape});
op->set_attrs(attrs);
op->validate_and_infer_types();
EXPECT_EQ(op->get_input_size(), 3);
EXPECT_EQ(op->get_output_size(), 1);
EXPECT_EQ(op->get_output_element_type(0), element::f16);
EXPECT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
} }
TEST(type_prop, proposal_v0_shape_infer) { TEST(type_prop, proposal_v0_shape_infer) {
@ -107,72 +99,134 @@ TEST(type_prop, proposal_v0_shape_infer) {
attrs.post_nms_topn = 200; attrs.post_nms_topn = 200;
const size_t batch_size = 7; const size_t batch_size = 7;
auto class_probs = make_shared<op::Parameter>(element::f32, Shape{batch_size, 12, 34, 62}); auto class_probs = make_shared<op::Parameter>(element::bf16, Shape{batch_size, 12, 34, 62});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62}); auto class_bbox_deltas = make_shared<op::Parameter>(element::bf16, Shape{batch_size, 24, 34, 62});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::bf16, Shape{3});
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_element_type(0), element::bf16);
EXPECT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
} }
TEST(type_prop, proposal_v0_dynamic_class_probs_dim1_batch_size_infer) { TEST(type_prop, proposal_v0_dynamic_class_probs_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
const size_t batch_size = 2; const auto batch_size = Dimension(2);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 4, 3, 4}); auto class_props_shape = PartialShape{-1, 2, 3, 4};
auto class_bbox_shape = PartialShape{batch_size, 4, {0, 3}, {1, 4}};
set_shape_labels(class_props_shape, 10);
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f32, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_element_type(0), element::f32);
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, ov::no_label));
} }
TEST(type_prop, proposal_v0_dynamic_bbox_deltas_dim1_batch_size_infer) { TEST(type_prop, proposal_v0_dynamic_bbox_deltas_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
const size_t batch_size = 2; const auto batch_size = Dimension(2);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 4, 3, 4}); auto class_props_shape = PartialShape{batch_size, 2, {1, 3}, {1, 4}};
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto class_bbox_shape = PartialShape{-1, 4, 3, 4};
set_shape_labels(class_props_shape, 10);
auto class_probs = make_shared<op::Parameter>(element::f64, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f64, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f64, Shape{3});
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_element_type(0), element::f64);
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, ov::no_label));
} }
TEST(type_prop, proposal_v0_dynamic_class_probs_bbox_deltas_dim1_batch_size_infer) { TEST(type_prop, proposal_v0_dynamic_class_probs_bbox_deltas_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4}); auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{-1, 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{-1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(ov::no_label));
} }
TEST(type_prop, proposal_v0_dynamic_range_class_probs_bbox_deltas_dim1_batch_size_infer) { TEST(type_prop, proposal_v0_dynamic_range_class_probs_bbox_deltas_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 2; attrs.post_nms_topn = 2;
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension(8, 14), 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{Dimension(10, 15), 4, 3, 4}); auto class_props_shape = PartialShape{{8, 14}, 2, 3, 4};
auto class_bbox_shape = PartialShape{{10, 15}, 4, {0, 3}, {1, 4}};
set_shape_labels(class_props_shape, 10);
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f32, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), EXPECT_EQ(op->get_output_partial_shape(0),
(PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn), 5})); (PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn), 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(ov::no_label));
} }
TEST(type_prop, proposal_v0_dynamic_image_shape_shape_infer) { TEST(type_prop, proposal_v0_dynamic_image_shape_shape_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.base_size = 1; attrs.base_size = 2;
attrs.pre_nms_topn = 20; attrs.pre_nms_topn = 20;
attrs.post_nms_topn = 200; attrs.post_nms_topn = 200;
const size_t batch_size = 7; const auto batch_size = Dimension(7);
auto class_probs = make_shared<op::Parameter>(element::f32, Shape{batch_size, 12, 34, 62}); auto class_props_shape = PartialShape{batch_size, 12, 34, 62};
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62}); auto class_bbox_shape = PartialShape{batch_size, 24, 34, 62};
set_shape_labels(class_props_shape, 10);
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f32, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic()); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(ov::no_label));
}
TEST(type_prop, proposal_v0_class_probs_dynamic_rank_but_batch_shape_defined_in_bbox) {
op::ProposalAttrs attrs;
attrs.post_nms_topn = 2;
const auto batch_size = Dimension(7);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{batch_size, 24, 32, 32});
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
}
TEST(type_prop, proposal_v0_bbox_dynamic_rank_but_batch_defined_in_class_probs) {
op::ProposalAttrs attrs;
attrs.post_nms_topn = 2;
const auto batch_size = Dimension(7);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{batch_size, 24, 32, 32});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
} }
TEST(type_prop, proposal_v0_everything_dynamic_shape_infer) { TEST(type_prop, proposal_v0_everything_dynamic_shape_infer) {
@ -183,18 +237,18 @@ TEST(type_prop, proposal_v0_everything_dynamic_shape_infer) {
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
} }
TEST(type_prop, proposal_v0_everything_dynamic_class_probs_dynamic_rank_shape_infer) { TEST(type_prop, proposal_v0_everything_dynamic_class_probs_dynamic_rank_shape_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(Rank::dynamic())); auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(4)); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(4));
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
} }
TEST(type_prop, proposal_v0_everything_dynamic_class_probs_bbox_deltas_dynamic_rank_shape_infer) { TEST(type_prop, proposal_v0_everything_dynamic_class_probs_bbox_deltas_dynamic_rank_shape_infer) {
@ -205,7 +259,7 @@ TEST(type_prop, proposal_v0_everything_dynamic_class_probs_bbox_deltas_dynamic_r
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
} }
TEST(type_prop, proposal_v0_invalid_class_probs_dynamic) { TEST(type_prop, proposal_v0_invalid_class_probs_dynamic) {
@ -214,15 +268,9 @@ TEST(type_prop, proposal_v0_invalid_class_probs_dynamic) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape class_probs should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape class_probs should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_bbox_deltas_dynamic) { TEST(type_prop, proposal_v0_invalid_bbox_deltas_dynamic) {
@ -231,15 +279,9 @@ TEST(type_prop, proposal_v0_invalid_bbox_deltas_dynamic) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(3)); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(3));
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape bbox_deltas should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape bbox_deltas should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_image_shape_dynamic) { TEST(type_prop, proposal_v0_invalid_image_shape_dynamic) {
@ -248,15 +290,9 @@ TEST(type_prop, proposal_v0_invalid_image_shape_dynamic) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(0)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(0));
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Image_shape must be 1-D tensor"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape image_shape should be rank 1 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_class_probs_type) { TEST(type_prop, proposal_v0_invalid_class_probs_type) {
@ -265,16 +301,9 @@ TEST(type_prop, proposal_v0_invalid_class_probs_type) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer input class_probs should have floating point type"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(),
std::string("Proposal layer input class_probs should have floating point type"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_bbox_deltas_type) { TEST(type_prop, proposal_v0_invalid_bbox_deltas_type) {
@ -283,16 +312,9 @@ TEST(type_prop, proposal_v0_invalid_bbox_deltas_type) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::i32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::i32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer input bbox_deltas should have floating point type"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(),
std::string("Proposal layer input bbox_deltas should have floating point type"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v0_invalid_image_shape_type) { TEST(type_prop, proposal_v0_invalid_image_shape_type) {
@ -301,17 +323,11 @@ TEST(type_prop, proposal_v0_invalid_image_shape_type) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::i32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::i32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer input image_shape should have floating point type"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(),
std::string("Proposal layer input image_shape should have floating point type"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
// ------------------------------ V4 ------------------------------ // ------------------------------ V4 ------------------------------
TEST(type_prop, proposal_v4_invalid_class_probs_rank) { TEST(type_prop, proposal_v4_invalid_class_probs_rank) {
@ -320,15 +336,9 @@ TEST(type_prop, proposal_v4_invalid_class_probs_rank) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape class_probs should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape class_probs should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_class_bbox_deltas_rank) { TEST(type_prop, proposal_v4_invalid_class_bbox_deltas_rank) {
@ -337,15 +347,9 @@ TEST(type_prop, proposal_v4_invalid_class_bbox_deltas_rank) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape bbox_deltas should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape bbox_deltas should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_image_shape_rank) { TEST(type_prop, proposal_v4_invalid_image_shape_rank) {
@ -354,15 +358,9 @@ TEST(type_prop, proposal_v4_invalid_image_shape_rank) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{2, 1}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{2, 1});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Image_shape must be 1-D tensor"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape image_shape should be rank 1 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_image_shape_size) { TEST(type_prop, proposal_v4_invalid_image_shape_size) {
@ -371,17 +369,33 @@ TEST(type_prop, proposal_v4_invalid_image_shape_size) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Image_shape must be 1-D tensor and has got 3 or 4 elements (image_shape_shape[0]"));
FAIL() << "Invalid input tensor rank."; }
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING( TEST(type_prop, proposal_v4_default_ctor) {
error.what(), op::ProposalAttrs attrs;
std::string("Image_shape 1D tensor must have => 3 and <= 4 elements (image_shape_shape[0]")); attrs.base_size = 1;
} catch (...) { attrs.pre_nms_topn = 20;
FAIL() << "Deduced type check failed for unexpected reason"; attrs.post_nms_topn = 200;
} const size_t batch_size = 7;
auto class_probs = make_shared<op::Parameter>(element::f16, Shape{batch_size, 12, 34, 62});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f16, Shape{batch_size, 24, 34, 62});
auto image_shape = make_shared<op::Parameter>(element::f16, Shape{3});
auto op = make_shared<op::v4::Proposal>();
op->set_arguments(OutputVector{class_probs, class_bbox_deltas, image_shape});
op->set_attrs(attrs);
op->validate_and_infer_types();
EXPECT_EQ(op->get_input_size(), 3);
EXPECT_EQ(op->get_output_size(), 2);
EXPECT_THAT(op->outputs(), Each(Property("Element type", &Output<Node>::get_element_type, element::f16)));
EXPECT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_shape(1), (Shape{batch_size * attrs.post_nms_topn}));
} }
TEST(type_prop, proposal_v4_shape_infer) { TEST(type_prop, proposal_v4_shape_infer) {
@ -395,44 +409,78 @@ TEST(type_prop, proposal_v4_shape_infer) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
ASSERT_EQ(op->get_output_shape(1), (Shape{batch_size * attrs.post_nms_topn})); EXPECT_THAT(op->outputs(), Each(Property("Element type", &Output<Node>::get_element_type, element::f32)));
EXPECT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_shape(1), (Shape{batch_size * attrs.post_nms_topn}));
} }
TEST(type_prop, proposal_v4_dynamic_class_probs_dim1_batch_size_infer) { TEST(type_prop, proposal_v4_dynamic_class_probs_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
const size_t batch_size = 2; const auto batch_size = Dimension(2);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 4, 3, 4}); auto class_props_shape = PartialShape{-1, 2, 3, 4};
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto class_bbox_shape = PartialShape{batch_size, 4, {0, 3}, {1, 4}};
set_shape_labels(class_props_shape, 10);
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f64, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f64, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f64, Shape{3});
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(op->outputs(), Each(Property("Element type", &Output<Node>::get_element_type, element::f64)));
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, ov::no_label));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{batch_size * attrs.post_nms_topn}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(1)), ElementsAre(20));
} }
TEST(type_prop, proposal_v4_dynamic_bbox_deltas_dim1_batch_size_infer) { TEST(type_prop, proposal_v4_dynamic_bbox_deltas_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
const size_t batch_size = 2;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4}); const auto batch_size = Dimension(2);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto class_props_shape = PartialShape{batch_size, 2, 3, 4};
auto class_bbox_shape = PartialShape{-1, 4, {0, 3}, {1, 4}};
set_shape_labels(class_props_shape, 10);
auto class_probs = make_shared<op::Parameter>(element::bf16, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::bf16, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::bf16, Shape{3});
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(op->outputs(), Each(Property("Element type", &Output<Node>::get_element_type, element::bf16)));
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(10, ov::no_label));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{batch_size * attrs.post_nms_topn}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(1)), ElementsAre(10));
} }
TEST(type_prop, proposal_v4_dynamic_class_probs_bbox_deltas_dim1_batch_size_infer) { TEST(type_prop, proposal_v4_dynamic_class_probs_bbox_deltas_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 1; attrs.post_nms_topn = 1;
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{Dimension::dynamic(), 4, 3, 4}); auto class_props_shape = PartialShape{-1, 2, 3, 4};
auto class_bbox_shape = PartialShape{-1, 4, 3, 4};
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f32, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
ASSERT_EQ(op->get_output_partial_shape(1), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), ElementsAre(20, ov::no_label));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{-1}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(1)), ElementsAre(20));
} }
TEST(type_prop, proposal_v4_dynamic_image_shape_shape_infer) { TEST(type_prop, proposal_v4_dynamic_image_shape_shape_infer) {
@ -440,13 +488,24 @@ TEST(type_prop, proposal_v4_dynamic_image_shape_shape_infer) {
attrs.base_size = 1; attrs.base_size = 1;
attrs.pre_nms_topn = 20; attrs.pre_nms_topn = 20;
attrs.post_nms_topn = 200; attrs.post_nms_topn = 200;
const size_t batch_size = 7; const auto batch_size = Dimension(7);
auto class_probs = make_shared<op::Parameter>(element::f32, Shape{batch_size, 12, 34, 62}); auto class_props_shape = PartialShape{batch_size, 2, 3, 4};
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62}); auto class_bbox_shape = PartialShape{batch_size, 4, {0, 3}, {1, 4}};
set_shape_labels(class_props_shape, 10);
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f32, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic()); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(ov::no_label));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{batch_size * attrs.post_nms_topn}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(1)), ElementsAre(ov::no_label));
} }
TEST(type_prop, proposal_v4_everything_dynamic_shape_infer) { TEST(type_prop, proposal_v4_everything_dynamic_shape_infer) {
@ -457,8 +516,9 @@ TEST(type_prop, proposal_v4_everything_dynamic_shape_infer) {
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
ASSERT_EQ(op->get_output_partial_shape(1), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{-1}));
} }
TEST(type_prop, proposal_v4_everything_dynamic_class_probs_dynamic_rank_shape_infer) { TEST(type_prop, proposal_v4_everything_dynamic_class_probs_dynamic_rank_shape_infer) {
@ -469,8 +529,9 @@ TEST(type_prop, proposal_v4_everything_dynamic_class_probs_dynamic_rank_shape_in
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
ASSERT_EQ(op->get_output_partial_shape(1), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{-1}));
} }
TEST(type_prop, proposal_v4_everything_dynamic_class_probs_bbox_deltas_dynamic_rank_shape_infer) { TEST(type_prop, proposal_v4_everything_dynamic_class_probs_bbox_deltas_dynamic_rank_shape_infer) {
@ -481,22 +542,61 @@ TEST(type_prop, proposal_v4_everything_dynamic_class_probs_bbox_deltas_dynamic_r
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
ASSERT_EQ(op->get_output_partial_shape(1), (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{-1, 5}));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{-1}));
} }
TEST(type_prop, proposal_v4_dynamic_range_class_probs_bbox_deltas_dim1_batch_size_infer) { TEST(type_prop, proposal_v4_dynamic_range_class_probs_bbox_deltas_dim1_batch_size_infer) {
op::ProposalAttrs attrs; op::ProposalAttrs attrs;
attrs.post_nms_topn = 2; attrs.post_nms_topn = 2;
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{Dimension(8, 14), 2, 3, 4});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{Dimension(10, 15), 4, 3, 4}); auto class_props_shape = PartialShape{{8, 14}, 2, 3, 4};
auto class_bbox_shape = PartialShape{{10, 15}, 4, {0, 3}, {1, 4}};
set_shape_labels(class_props_shape, 10);
set_shape_labels(class_bbox_shape, 20);
auto class_probs = make_shared<op::Parameter>(element::f32, class_props_shape);
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, class_bbox_shape);
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
ASSERT_EQ(op->get_output_partial_shape(0),
EXPECT_EQ(op->get_output_partial_shape(0),
(PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn), 5})); (PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn), 5}));
ASSERT_EQ(op->get_output_partial_shape(1), EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(0)), Each(ov::no_label));
EXPECT_EQ(op->get_output_partial_shape(1),
(PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn)})); (PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn)}));
EXPECT_THAT(get_shape_labels(op->get_output_partial_shape(1)), Each(ov::no_label));
}
TEST(type_prop, proposal_v4_class_dynamic_rank_but_batch_shape_defined_in_bbox) {
op::ProposalAttrs attrs;
attrs.post_nms_topn = 1;
const auto batch_size = Dimension(7);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape{batch_size, 24, 32, 32});
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{batch_size * attrs.post_nms_topn}));
}
TEST(type_prop, proposal_v4_bbox_dynamic_rank_but_batch_defined_in_class_probs) {
op::ProposalAttrs attrs;
attrs.post_nms_topn = 1;
const auto batch_size = Dimension(10);
auto class_probs = make_shared<op::Parameter>(element::f32, PartialShape{batch_size, 24, 32, 32});
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(1));
auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
EXPECT_EQ(op->get_output_partial_shape(0), (PartialShape{batch_size * attrs.post_nms_topn, 5}));
EXPECT_EQ(op->get_output_partial_shape(1), (PartialShape{batch_size * attrs.post_nms_topn}));
} }
TEST(type_prop, proposal_v4_invalid_class_probs_dynamic) { TEST(type_prop, proposal_v4_invalid_class_probs_dynamic) {
@ -505,15 +605,9 @@ TEST(type_prop, proposal_v4_invalid_class_probs_dynamic) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape class_probs should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape class_probs should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_bbox_deltas_dynamic) { TEST(type_prop, proposal_v4_invalid_bbox_deltas_dynamic) {
@ -522,15 +616,9 @@ TEST(type_prop, proposal_v4_invalid_bbox_deltas_dynamic) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(3)); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(3));
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer shape bbox_deltas should be rank 4 compatible"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape bbox_deltas should be rank 4 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_image_shape_dynamic) { TEST(type_prop, proposal_v4_invalid_image_shape_dynamic) {
@ -539,15 +627,9 @@ TEST(type_prop, proposal_v4_invalid_image_shape_dynamic) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(0)); auto image_shape = make_shared<op::Parameter>(element::f32, PartialShape::dynamic(0));
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Image_shape must be 1-D tensor"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(), std::string("Proposal layer shape image_shape should be rank 1 compatible"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_class_probs_type) { TEST(type_prop, proposal_v4_invalid_class_probs_type) {
@ -556,16 +638,9 @@ TEST(type_prop, proposal_v4_invalid_class_probs_type) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer input class_probs should have floating point type"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(),
std::string("Proposal layer input class_probs should have floating point type"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_bbox_deltas_type) { TEST(type_prop, proposal_v4_invalid_bbox_deltas_type) {
@ -574,16 +649,9 @@ TEST(type_prop, proposal_v4_invalid_bbox_deltas_type) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::i32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::i32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer input bbox_deltas should have floating point type"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(),
std::string("Proposal layer input bbox_deltas should have floating point type"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }
TEST(type_prop, proposal_v4_invalid_image_shape_type) { TEST(type_prop, proposal_v4_invalid_image_shape_type) {
@ -592,14 +660,7 @@ TEST(type_prop, proposal_v4_invalid_image_shape_type) {
auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4}); auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared<op::Parameter>(element::i32, Shape{3}); auto image_shape = make_shared<op::Parameter>(element::i32, Shape{3});
try { OV_EXPECT_THROW(std::ignore = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs),
auto proposal = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs); NodeValidationFailure,
// Should have thrown, so fail if it didn't HasSubstr("Proposal layer input image_shape should have floating point type"));
FAIL() << "Invalid input tensor rank.";
} catch (const NodeValidationFailure& error) {
EXPECT_HAS_SUBSTRING(error.what(),
std::string("Proposal layer input image_shape should have floating point type"));
} catch (...) {
FAIL() << "Deduced type check failed for unexpected reason";
}
} }

View File

@ -305,11 +305,7 @@ protected:
/** /**
* @brief Base shape inference object implementing the IStaticShapeInfer without padding support * @brief Base shape inference object implementing the IStaticShapeInfer without padding support
*
* @tparam TOp Type of operator.
* @tparam mask Bit Mask of data dependent ports.
*/ */
template <class TOp, uint32_t mask>
class ShapeInferBase : public IStaticShapeInfer { class ShapeInferBase : public IStaticShapeInfer {
public: public:
using iface_type = IStaticShapeInfer; using iface_type = IStaticShapeInfer;
@ -330,10 +326,8 @@ public:
return infer(input_shapes, make_tensor_accessor(constant_data)); return infer(input_shapes, make_tensor_accessor(constant_data));
} }
IShapeInferCommon::Result infer(const std::vector<StaticShape>& input_shapes, IShapeInferCommon::Result infer(const std::vector<StaticShape>& input_shapes, const ov::ITensorAccessor&) override {
const ov::ITensorAccessor& tensor_accessor) override { OPENVINO_THROW("Not implemented by base class");
auto result = shape_infer(static_cast<TOp*>(m_node.get()), input_shapes, tensor_accessor);
return {std::move(result), ShapeInferStatus::success};
} }
const ov::CoordinateDiff& get_pads_begin() override { const ov::CoordinateDiff& get_pads_begin() override {
@ -349,7 +343,7 @@ public:
} }
port_mask_t get_port_mask() const override { port_mask_t get_port_mask() const override {
return mask; return 0;
} }
protected: protected:
@ -357,6 +351,44 @@ protected:
std::shared_ptr<ov::Node> m_node; std::shared_ptr<ov::Node> m_node;
}; };
/**
* @brief Shape inference using tensor accessor to get constant data.
*
* @tparam TOp Type of operator.
* @tparam MASK The bit mask where each bit corresponds to an input port number.
*/
template <class TOp, uint32_t MASK>
class ShapeInferTA : public ShapeInferBase {
public:
using ShapeInferBase::ShapeInferBase;
IShapeInferCommon::Result infer(const std::vector<StaticShape>& input_shapes,
const ov::ITensorAccessor& tensor_accessor) override {
return {shape_infer(static_cast<TOp*>(m_node.get()), input_shapes, tensor_accessor), ShapeInferStatus::success};
}
port_mask_t get_port_mask() const override {
return MASK;
}
};
/**
* @brief Shape inference not using tensor accessor
*
* The MASK is 0 there is no dependant inputs with data for shape inference.
*
* @tparam TOp Type of operator.
*/
template <class TOp>
class ShapeInferTA<TOp, 0> : public ShapeInferBase {
public:
using ShapeInferBase::ShapeInferBase;
IShapeInferCommon::Result infer(const std::vector<StaticShape>& input_shapes, const ov::ITensorAccessor&) override {
return {shape_infer(static_cast<TOp*>(m_node.get()), input_shapes), ShapeInferStatus::success};
}
};
/** /**
* \brief Shape infer factory * \brief Shape infer factory
* *
@ -510,7 +542,6 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{
_OV_OP_SHAPE_INFER_REG(Pad, entryIOC), _OV_OP_SHAPE_INFER_REG(Pad, entryIOC),
_OV_OP_SHAPE_INFER_REG(PriorBox, entryIOC), _OV_OP_SHAPE_INFER_REG(PriorBox, entryIOC),
_OV_OP_SHAPE_INFER_REG(PriorBoxClustered, entryIOC), _OV_OP_SHAPE_INFER_REG(PriorBoxClustered, entryIOC),
_OV_OP_SHAPE_INFER_REG(Proposal, entryIO),
_OV_OP_SHAPE_INFER_REG(PSROIPooling, entryIO), _OV_OP_SHAPE_INFER_REG(PSROIPooling, entryIO),
_OV_OP_SHAPE_INFER_REG(Range, entryIOC), _OV_OP_SHAPE_INFER_REG(Range, entryIOC),
_OV_OP_SHAPE_INFER_REG(RDFT, entryIOC), _OV_OP_SHAPE_INFER_REG(RDFT, entryIOC),
@ -566,7 +597,6 @@ const IShapeInferCommonFactory::TRegistry IShapeInferCommonFactory::registry{
_OV_OP_SHAPE_INFER_REG(opset1::Interpolate, entryIOC), _OV_OP_SHAPE_INFER_REG(opset1::Interpolate, entryIOC),
_OV_OP_SHAPE_INFER_REG(opset1::LSTMCell, entryIO), _OV_OP_SHAPE_INFER_REG(opset1::LSTMCell, entryIO),
_OV_OP_SHAPE_INFER_REG(opset1::MaxPool, ShapeInferWithPadding), _OV_OP_SHAPE_INFER_REG(opset1::MaxPool, ShapeInferWithPadding),
_OV_OP_SHAPE_INFER_REG(opset1::Proposal, entryIO),
_OV_OP_SHAPE_INFER_REG(opset1::Range, entryIOC), _OV_OP_SHAPE_INFER_REG(opset1::Range, entryIOC),
_OV_OP_SHAPE_INFER_REG(opset1::ShapeOf, entryIO), _OV_OP_SHAPE_INFER_REG(opset1::ShapeOf, entryIO),
_OV_OP_SHAPE_INFER_REG(opset1::TopK, entryIOC), _OV_OP_SHAPE_INFER_REG(opset1::TopK, entryIOC),
@ -583,11 +613,13 @@ using IStaticShapeInferFactory =
template <> template <>
const IStaticShapeInferFactory::TRegistry IStaticShapeInferFactory::registry{ const IStaticShapeInferFactory::TRegistry IStaticShapeInferFactory::registry{
// Default opset // Default opset
_OV_OP_SHAPE_INFER_MASK_REG(Tile, ShapeInferBase, util::bit::mask(1)), _OV_OP_SHAPE_INFER_MASK_REG(ExperimentalDetectronROIFeatureExtractor, ShapeInferTA, util::bit::mask()),
_OV_OP_SHAPE_INFER_MASK_REG(ExperimentalDetectronROIFeatureExtractor, ShapeInferBase, util::bit::mask()), _OV_OP_SHAPE_INFER_MASK_REG(Proposal, ShapeInferTA, util::bit::mask()),
_OV_OP_SHAPE_INFER_MASK_REG(Tile, ShapeInferTA, util::bit::mask(1)),
// Operators shape inferences for specific opset version should be specified below // Operators shape inferences for specific opset version should be specified below
// opset1 // opset1
_OV_OP_SHAPE_INFER_MASK_REG(opset1::Reverse, ShapeInferBase, util::bit::mask(1)), _OV_OP_SHAPE_INFER_MASK_REG(opset1::Proposal, ShapeInferTA, util::bit::mask()),
_OV_OP_SHAPE_INFER_MASK_REG(opset1::Reverse, ShapeInferTA, util::bit::mask(1)),
}; };
#undef _OV_OP_NON_TEMPLATE_SHAPE_INFER_REG #undef _OV_OP_NON_TEMPLATE_SHAPE_INFER_REG

View File

@ -1,51 +0,0 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include "utils.hpp"
using namespace ov;
using namespace ov::intel_cpu;
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<op::v0::Parameter>(element::f32, PartialShape{-1, -1, -1, -1});
auto class_bbox_deltas = std::make_shared<op::v0::Parameter>(element::f32, PartialShape{-1, -1, -1, -1});
auto image_shape = std::make_shared<op::v0::Parameter>(element::f32, PartialShape{-1});
auto op = std::make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
const std::vector<StaticShape> input_shapes = {StaticShape{batch_size, 12, 34, 62},
StaticShape{batch_size, 24, 34, 62},
StaticShape{3}};
std::vector<StaticShape> output_shapes = {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<op::v0::Parameter>(element::f32, PartialShape{-1, -1, -1, -1});
auto class_bbox_deltas = std::make_shared<op::v0::Parameter>(element::f32, PartialShape{-1, -1, -1, -1});
auto image_shape = std::make_shared<op::v0::Parameter>(element::f32, PartialShape{-1});
auto op = std::make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
const std::vector<StaticShape> input_shapes = {StaticShape{batch_size, 12, 34, 62},
StaticShape{batch_size, 24, 34, 62},
StaticShape{3}};
std::vector<StaticShape> output_shapes = {StaticShape{}, 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}));
}

View File

@ -0,0 +1,111 @@
// Copyright (C) 2018-2023 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gmock/gmock.h>
#include "common_test_utils/test_assertions.hpp"
#include "utils.hpp"
using namespace ov;
using namespace ov::intel_cpu;
using namespace testing;
template <class TOp>
class ProposalTest : public OpStaticShapeInferenceTest<TOp> {
protected:
using Attrs = typename TOp::Attributes;
static Attrs make_attrs(size_t post_nms_count) {
Attrs attrs;
attrs.post_nms_topn = post_nms_count;
return attrs;
}
static size_t exp_out_size() {
if (std::is_same<op::v0::Proposal, TOp>::value) {
return 1;
} else if (std::is_same<op::v4::Proposal, TOp>::value) {
return 2;
} else {
return 0;
}
}
};
TYPED_TEST_SUITE_P(ProposalTest);
TYPED_TEST_P(ProposalTest, default_ctor) {
this->op = this->make_op();
this->op->set_attrs(this->make_attrs(10));
this->input_shapes = ShapeVector{{2, 3, 10, 10}, {2, 6, 10, 10}, {3}};
shape_inference(this->op.get(), this->input_shapes, this->output_shapes);
EXPECT_EQ(this->output_shapes.size(), this->exp_out_size());
EXPECT_EQ(this->output_shapes.front(), StaticShape({20, 5}));
}
TYPED_TEST_P(ProposalTest, all_inputs_dynamic_rank) {
const auto class_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto class_bbox_deltas = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto image_shape = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
this->op = this->make_op(class_probs, class_bbox_deltas, image_shape, this->make_attrs(4));
this->input_shapes = ShapeVector{{2, 3, 10, 10}, {2, 6, 10, 10}, {3}};
shape_inference(this->op.get(), this->input_shapes, this->output_shapes);
EXPECT_EQ(this->output_shapes.size(), this->exp_out_size());
EXPECT_EQ(this->output_shapes[0], StaticShape({8, 5}));
}
TYPED_TEST_P(ProposalTest, all_inputs_static_rank) {
const auto class_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(4));
const auto class_bbox_deltas = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(4));
const auto image_shape = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(1));
this->op = this->make_op(class_probs, class_bbox_deltas, image_shape, this->make_attrs(5));
this->input_shapes = ShapeVector{{3, 4, 10, 10}, {3, 8, 10, 10}, {4}};
shape_inference(this->op.get(), this->input_shapes, this->output_shapes);
EXPECT_EQ(this->output_shapes.size(), this->exp_out_size());
EXPECT_EQ(this->output_shapes[0], StaticShape({15, 5}));
}
TYPED_TEST_P(ProposalTest, batch_size_not_compatible) {
const auto class_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(4));
const auto class_bbox_deltas = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto image_shape = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(1));
this->op = this->make_op(class_probs, class_bbox_deltas, image_shape, this->make_attrs(5));
this->input_shapes = ShapeVector{{3, 4, 10, 10}, {4, 8, 10, 10}, {3}};
OV_EXPECT_THROW(shape_inference(this->op.get(), this->input_shapes, this->output_shapes),
NodeValidationFailure,
HasSubstr("Batch size inconsistent between class_probs"));
}
TYPED_TEST_P(ProposalTest, image_shape_input_not_compatible_shape) {
const auto class_probs = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
const auto class_bbox_deltas = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic(4));
const auto image_shape = std::make_shared<op::v0::Parameter>(element::f32, PartialShape::dynamic());
this->op = this->make_op(class_probs, class_bbox_deltas, image_shape, this->make_attrs(5));
this->input_shapes = ShapeVector{{3, 4, 10, 10}, {3, 8, 10, 10}, {5}};
OV_EXPECT_THROW(shape_inference(this->op.get(), this->input_shapes, this->output_shapes),
NodeValidationFailure,
HasSubstr("Image_shape must be 1-D tensor and has got 3 or 4 elements"));
}
REGISTER_TYPED_TEST_SUITE_P(ProposalTest,
default_ctor,
all_inputs_dynamic_rank,
all_inputs_static_rank,
batch_size_not_compatible,
image_shape_input_not_compatible_shape);
using ProposalVersions = Types<op::v0::Proposal, op::v4::Proposal>;
INSTANTIATE_TYPED_TEST_SUITE_P(shape_inference, ProposalTest, ProposalVersions);