From b731ce13d85fffd850e786333ac6d743178c6a6a Mon Sep 17 00:00:00 2001 From: Gleb Kazantaev Date: Thu, 28 May 2020 16:45:48 +0300 Subject: [PATCH] Fixed NMSIE shape infer function (#648) --- .../src/transformations/src/ngraph_ops/nms_ie.cpp | 13 ++++++++++--- .../convert_nms_to_nms_ie.cpp | 15 +++++++++++---- .../convert_gather_to_gather_ie.cpp | 2 ++ .../transformations/convert_nms_to_nms_ie_test.cpp | 22 +++++++++++----------- .../transformations/convert_topk_test.cpp | 1 + 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/inference-engine/src/transformations/src/ngraph_ops/nms_ie.cpp b/inference-engine/src/transformations/src/ngraph_ops/nms_ie.cpp index 9b7b7ca..c7f696c 100644 --- a/inference-engine/src/transformations/src/ngraph_ops/nms_ie.cpp +++ b/inference-engine/src/transformations/src/ngraph_ops/nms_ie.cpp @@ -34,11 +34,18 @@ std::shared_ptr op::NonMaxSuppressionIE::clone_with_new_inputs(const ngrap } void op::NonMaxSuppressionIE::validate_and_infer_types() { + auto squeeze_input = [](const Output & input) -> std::shared_ptr { + return std::make_shared(input, opset1::Constant::create(element::i64, Shape{1}, {0})); + }; + // Calculate output shape using opset1::NonMaxSuppression + auto max_output_boxes_per_class = std::dynamic_pointer_cast(input_value(2).get_node_shared_ptr()); auto nms = std::make_shared(input_value(0), input_value(1), - std::make_shared(input_value(2), opset1::Constant::create(element::i64, Shape{1}, {0})), - std::make_shared(input_value(3), opset1::Constant::create(element::i64, Shape{1}, {0})), - std::make_shared(input_value(4), opset1::Constant::create(element::i64, Shape{1}, {0}))); + /* second input is used for output calculation and only if it's Constant output shape won't be dynamic */ + max_output_boxes_per_class ? opset1::Constant::create(element::i64, Shape{}, max_output_boxes_per_class->cast_vector()) : + squeeze_input(input_value(2)), + squeeze_input(input_value(3)), + squeeze_input(input_value(4))); set_output_type(0, nms->output(0).get_element_type(), nms->output(0).get_partial_shape()); } diff --git a/inference-engine/src/transformations/src/transformations/convert_opset1_to_legacy/convert_nms_to_nms_ie.cpp b/inference-engine/src/transformations/src/transformations/convert_opset1_to_legacy/convert_nms_to_nms_ie.cpp index c50a393..5b2e755 100644 --- a/inference-engine/src/transformations/src/transformations/convert_opset1_to_legacy/convert_nms_to_nms_ie.cpp +++ b/inference-engine/src/transformations/src/transformations/convert_opset1_to_legacy/convert_nms_to_nms_ie.cpp @@ -42,10 +42,17 @@ void ngraph::pass::ConvertNMSToNMSIE::convert_nms_to_nms_ie() { auto new_max_per_class = nms->input_value(2); if (max_output_boxes_per_class_rank.get_length() == 0) { - new_max_per_class = std::make_shared( - nms->input_value(2), - opset1::Constant::create(element::i64, Shape{1}, {0})); - new_ops.push_back(new_max_per_class.get_node_shared_ptr()); + // WA: we need to create Constant manually because it requires by NMS shape inference + // otherwise we will get dynamic shape until first CF is executed. It can be resolved + // if CF will be executed right after transformation and before Validate pass. + if (auto new_max_per_class_const = std::dynamic_pointer_cast(new_max_per_class.get_node_shared_ptr())) { + new_max_per_class = opset1::Constant::create(element::i64, Shape{1}, new_max_per_class_const->cast_vector()); + } else { + new_max_per_class = std::make_shared( + nms->input_value(2), + opset1::Constant::create(element::i64, Shape{1}, {0})); + new_ops.push_back(new_max_per_class.get_node_shared_ptr()); + } } auto new_iou_threshold = nms->input_value(3); if (iou_threshold_rank.get_length() == 0) { diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_gather_to_gather_ie.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_gather_to_gather_ie.cpp index e284ebf..afd0ded 100644 --- a/inference-engine/tests/functional/inference_engine/transformations/convert_gather_to_gather_ie.cpp +++ b/inference-engine/tests/functional/inference_engine/transformations/convert_gather_to_gather_ie.cpp @@ -33,6 +33,7 @@ TEST(TransformationTests, ConvertGatherToGatherIEStatic1) { pass::InitNodeInfo().run_on_function(f); pass::ConvertGatherToGatherIE().run_on_function(f); ASSERT_NO_THROW(check_rt_info(f)); + ASSERT_TRUE(f->get_output_partial_shape(0).is_static()) << "Shape " << f->get_output_partial_shape(0) << " should be static"; } { @@ -60,6 +61,7 @@ TEST(TransformationTests, ConvertGatherToGatherIEStatic2) { pass::InitNodeInfo().run_on_function(f); pass::ConvertGatherToGatherIE().run_on_function(f); ASSERT_NO_THROW(check_rt_info(f)); + ASSERT_TRUE(f->get_output_partial_shape(0).is_static()) << "Shape " << f->get_output_partial_shape(0) << " should be static"; } { diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_nms_to_nms_ie_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_nms_to_nms_ie_test.cpp index 860e916..bd19243 100644 --- a/inference-engine/tests/functional/inference_engine/transformations/convert_nms_to_nms_ie_test.cpp +++ b/inference-engine/tests/functional/inference_engine/transformations/convert_nms_to_nms_ie_test.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "ngraph_test_utils.hpp" @@ -33,25 +34,26 @@ TEST(TransformationTests, ConvertNMSToNMSIEStatic) { f = std::make_shared(NodeVector{nms}, ParameterVector{boxes, scores}); + const auto & orig_shape = f->get_output_partial_shape(0); pass::InitNodeInfo().run_on_function(f); pass::ConvertNMSToNMSIE().run_on_function(f); - f->validate_nodes_and_infer_types(); ASSERT_NO_THROW(check_rt_info(f)); + ASSERT_TRUE(f->get_output_partial_shape(0).is_static()) << "Shape " << f->get_output_partial_shape(0) << " should be static"; } { auto boxes = std::make_shared(element::f32, Shape{1, 1000, 4}); auto scores = std::make_shared(element::f32, Shape{1, 1, 1000}); - auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{}, {10}); + auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{1}, {10}); auto iou_threshold = opset1::Constant::create(element::f32, Shape{}, {0.75}); auto score_threshold = opset1::Constant::create(element::f32, Shape{}, {0.7}); - auto nms = std::make_shared(boxes, scores, - std::make_shared(max_output_boxes_per_class, opset1::Constant::create(element::i64, Shape{1}, {0})), + auto nms = std::make_shared(boxes, scores, max_output_boxes_per_class, std::make_shared(iou_threshold, opset1::Constant::create(element::i64, Shape{1}, {0})), std::make_shared(score_threshold, opset1::Constant::create(element::i64, Shape{1}, {0})), 0, true); f_ref = std::make_shared(NodeVector{nms}, ParameterVector{boxes, scores}); + ASSERT_TRUE(f_ref->get_output_partial_shape(0).is_static()) << "Shape " << f_ref->get_output_partial_shape(0) << " should be static"; } auto res = compare_functions(f, f_ref); @@ -80,11 +82,10 @@ TEST(TransformationTests, ConvertNMSToNMSIEDynamic1) { { auto boxes = std::make_shared(element::f32, PartialShape::dynamic()); auto scores = std::make_shared(element::f32, PartialShape::dynamic()); - auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{}, {10}); + auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{1}, {10}); auto iou_threshold = opset1::Constant::create(element::f32, Shape{}, {0.75}); auto score_threshold = opset1::Constant::create(element::f32, Shape{}, {0.7}); - auto nms = std::make_shared(boxes, scores, - std::make_shared(max_output_boxes_per_class, opset1::Constant::create(element::i64, Shape{1}, {0})), + auto nms = std::make_shared(boxes, scores, max_output_boxes_per_class, std::make_shared(iou_threshold, opset1::Constant::create(element::i64, Shape{1}, {0})), std::make_shared(score_threshold, opset1::Constant::create(element::i64, Shape{1}, {0})), 0, true); @@ -118,11 +119,10 @@ TEST(TransformationTests, ConvertNMSToNMSIEDynamic2) { { auto boxes = std::make_shared(element::f32, PartialShape{DYN, 1000, 4}); auto scores = std::make_shared(element::f32, PartialShape{DYN, 1, 1000}); - auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{}, {10}); + auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{1}, {10}); auto iou_threshold = opset1::Constant::create(element::f32, Shape{}, {0.75}); auto score_threshold = opset1::Constant::create(element::f32, Shape{}, {0.7}); - auto nms = std::make_shared(boxes, scores, - std::make_shared(max_output_boxes_per_class, opset1::Constant::create(element::i64, Shape{1}, {0})), + auto nms = std::make_shared(boxes, scores, max_output_boxes_per_class, std::make_shared(iou_threshold, opset1::Constant::create(element::i64, Shape{1}, {0})), std::make_shared(score_threshold, opset1::Constant::create(element::i64, Shape{1}, {0})), 0, true); @@ -132,4 +132,4 @@ TEST(TransformationTests, ConvertNMSToNMSIEDynamic2) { auto res = compare_functions(f, f_ref); ASSERT_TRUE(res.first) << res.second; -} \ No newline at end of file +} diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_topk_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_topk_test.cpp index c66d719..197661f 100644 --- a/inference-engine/tests/functional/inference_engine/transformations/convert_topk_test.cpp +++ b/inference-engine/tests/functional/inference_engine/transformations/convert_topk_test.cpp @@ -33,6 +33,7 @@ TEST(TransformationTests, ConvertTopKToTopKIEStatic) { ngraph::pass::ConvertTopKToTopKIE().run_on_function(f); ASSERT_NO_THROW(check_rt_info(f)); ngraph::pass::ConstantFolding().run_on_function(f); + ASSERT_TRUE(f->get_output_partial_shape(0).is_static()) << "Shape " << f->get_output_partial_shape(0) << " should be static"; } { -- 2.7.4