Enable NonMaxSuppression-5 operation and various transformations (#2450)
authorVladimir Gavrilov <vladimir.gavrilov@intel.com>
Fri, 6 Nov 2020 16:11:04 +0000 (19:11 +0300)
committerGitHub <noreply@github.com>
Fri, 6 Nov 2020 16:11:04 +0000 (19:11 +0300)
* Some code style fixes.

* Started to write the method v5::NonMaxSuppression::evaluate().

* Started to write nGraph reference implementation of NMS-5.

* Some additions.

* Written preprocessing of boxes data.

* Started to write the function non_max_suppression() that calculates NonMaxSuppression-5 operation.

* Written postprocessing of the evaluate().

* Small fixes.

* Small fix.

* Added include for ngraph/shape.hpp.

* Written the function intersectionOverUnion.

* Some additions.

* Small fix.

* Continued to write the function non_max_suppression().

* Small fixes.

* Small fixes.

* Some changes.

* Some additions.

* Some replacements size_t by int64_t.

* Added casts to float in the construction of selected_score variable.

* Code style fixes.

* Written draft of NMS-5 nGraph reference implementation.

* Code style fixes.

* Started to write tests for void op::v5::NonMaxSuppression::validate_and_infer_types().

* Added tests for scalars/nonscalars.

* Fixes in the test type_prop.nms_v5_output_shape.

* Fixes in tests nms_v5_output_shape_2 and nms_v5_output_shape.

* Written tests for validate_and_infer_types() of NMS-5.

* Code style fixes.

* Now NMS-5 evaluate() can have outputs with calculated shapes.

* Small fixes.

* Corrected tests for NMS-5 validate_and_infer_type().

* Code style fixes.

* Started to write inner version of NMS-5 with static output shapes.

* Written draft of the inner operation NonMaxSuppressionIE3.

* Started to write conversion of op::v5::NonMaxSuppression into NonMaxSuppressionIE3.

* Small changes.

* Some additions.

* Small fixes.

* Fixed typo.

* Fixed typos.

* Written draft of the transformation ConvertNMS5ToLegacyMatcher that converts ngraph::opset5::NonMaxSuppression into op::NonMaxSuppressionIE3.

* Written header file for the transformations from NMS-1, NMS-3, NMS-4 to NMS-5.

* Started to write conversion of NMS-4 to NMS-5.

* Added include for ngraph/opsets/opset4.hpp.

* Started to write conversion of NMS-3 to NMS-5.

* Small fixes.

* Written draft of the conversion of NMS-3 into NMS-5.

* Fixed typo.

* Started to write conversion of NMS-1 to NMS-5.

* Written draft of the conversion NMS-1 to NMS-5.

* Started to write tests for the conversion nGraph NMS-5 to inner NMS.

* Started to write the test ConvertNMS5ToNMSIEStatic.

* Written tests for conversion of nGraph NMS-5 to inner NMSIE3.

* Started to write tests for conversion of previous NMS to nGraph NMS-5.

* Written tests for conversion of old nGraph NMS to NMS-5.

* Started to write tests for opset5::NonMaxSuppression::evaluate().

* Some additions.

* Small fix.

* Written tests for op::v5::NonMaxSuppression::evaluate().

* Used NGRAPH_RTTI_DECLARATION for NonMaxSuppressionIE3.

* Used NGRAPH_RTTI_DECLARATION for NMS-5.

* All static local constants and functions for NMS-5 were moved into non-named namespace.

* Some code style fixes.

* Moved some file.

* Small fix.

* Code style fix.

* Now NMS-5 supports all floating types in inputs 0 and 1.

* Moved some files.

* Fixed include directive in the file convert_nms_5_to_legacy.cpp with transformations NMS-1, 3, 4 -> NMS-5.

* Small changes.

* Deleted conversion NMS-3 -> legacy.

* Small changes.

* Fix in op::v5::NonMaxSuppression::evaluate: output shape [1] instead of [] in the output port 2.

* Code style fixes.

* Deleted conversion of NMS-4 into legacy NMS.

* Deleted redundant ifs.

* Added NMS-5 to Python API.

* Code style fix.

* Small change.

* Fixed element type for constants in the conversion of NMS-5 to NMSIE3.

* Deleted support of f64 in NMS-5.

* Added checks for input element types for inputs #0, #1, #3, #4, #5.

* Small change.

* Now get_floats throws an exception for unsupported types.

* Now nGraph NMS-5 supports 0D and 1D tensors in inputs #2, #3, #4, #5.

* Small fix in test_non_max_suppression.

* Deleted using namespace std

* Fixes in test_non_max_suppression().

* Small fixes.

* Added 'import PartialShape' in test_reduction.py.

* Deleted creating fake inputs in the ctor of op::v5::NonMaxSuppression.

* Deleted creating fake inputs in op::v5::NonMaxSuppression::clone_with_new_inputs.

* Corrections in int64_t op::v5::NonMaxSuppression::max_boxes_output_from_input() const.

* Corrected functions float op::v5::NonMaxSuppression::soft_nms_sigma_from_input() const, float op::v5::NonMaxSuppression::score_threshold_from_input() const,  float op::v5::NonMaxSuppression::iou_threshold_from_input() const.

* Small fixes.

* Deleted commented code.

* Fixes in nms_v5_scalar_inputs_check.

* Some changes.

* Small fixes.

* Code style fixes.

* Small changes.

* Small changes.

* Small fix.

* Deleted commented code.

* Some refactoring in ConvertNMS4ToNMS5 ctor.

* Small fix.

* Common part of conversions NMS-1 -> NMS-5, NMS-3 -> NMS-5, NMS-4 -> NMS-5 was moved into the separate function.

* Now conversions NMS-1 -> NMS-5, NMS-3 -> NMS-5, NMS-4 -> NMS-5 support both 2 inputs, and 5 inputs.

* Now transformations NMS-1 -> NMS-5, NMS-3 -> NMS-5, NMS-4 -> NMS-5 are called from 'umbrella' transformation.

* Now the transformation ConvertNMS5ToLegacyMatcher supports NMS-5 with 2, 3, 4, 5, or 6 inputs.

* The transformation ConvertNMS5ToLegacyMatcher was rewritten using Reshape instead of Unsqueeze.

* Started to rewrite tests for the transformation ConvertNMS5ToLegacyMatcher.

* Some fixes.

* Small fixes.

* Corrected tests for the transformation NMS-5 -> NMSIE3.

* Small formatting fix.

* Now methods max_boxes_output_from_input(), iou_threshold_from_input(), score_threshold_from_input(), soft_nms_sigma_from_input() of op::v5::NunMaxSuppression are public.

* Started to move op::v5::NonMaxSuppression::evaluate() into ngraph/test/runtime/interpreter.

* Added NMS-5 to ngraph/test/runtime/interpreter/int_executable.

* Small fixes.

* Code style fixes.

* Written draft test nonmaxsuppression_center_point_box_format_backend in ngraph/test/backend.

* Small fix.

* Written draft tests of NonMaxSuppression in ngraph/test/runtime.

* Some changes.

* Small changes.

* Disabled IE_CPU tests for NMS-5.

* Deleted op_eval tests for NMS-5.

* Deleted evaluate() method of NMS-5.

* Now all nGraph functions in tests of the transformation NMS-5 -> NMSIE3 have one output.

* Now preprocessing and postprocessing of the calculation of NMS-5 in reference implementation.

* Code style fixes.

* Some fixes in tests for the transformation NMS-5 -> NMSIE3.

* Replaced precision i64 -> i32 for some constants in tests for the transformation NMS-5 -> NMSIE3.

* Written creating CNNLayer for NMS-5.

* Added creating CNNLayer for NonMaxSuppressionIE3.

* some changes.

* Now conversions NMS-1, NMS-3, NMS-4 -> NMS-5  and NMS-5 -> NMSIE3 generate NMS nodes with 5 inputs.

* Fixed ctor in MKLDNN NonMaxSuppressionImpl: validation of number of output edges.

* Added conversion of output_type for NMS-5.

* Fixes in the transformation NMS5 -> NMSIE3.

* Fixes in the conversion of NMS-5 to NMSIE3.

* Fixes in MKLDNN NMS ctor.

* Small fix.

* Fixed tests for the transformation NMS5 -> NMSIE3.

* Fixed tests for conversions NMS-1, NMS-3, NMS-4 -> NMS-5.

* Small fixes in MKLDNN NMS ctor.

* Rewritten create_layer() functions for NMS-5 and NMSIE3 as addSpecificCreator() functions.

* Disabled tests for IE IR reader for NMS-4.

* Deleted debug code.

* Added comment about disabling tests IE_CPU.nonmaxsuppression.

* Written IE IR Reader test for NMS-4.

* Deleted function float_from_constant_node.

* Small fixes.

* Deleted functions function_from_model and construct_weights.

* Small fix.

* Replaced push_back with emplace_back in the conversion of NMS-5 to legacy.

* Small changes.

* Some fixes.

* Refactored reference implementation of NMS-5.

* Moved structure NMSAttributes to unnamed namespace.

* Code style fixes.

* Small fix.

36 files changed:
inference-engine/src/legacy_api/include/legacy/ie_layers.h
inference-engine/src/legacy_api/include/legacy/ngraph_ops/nms_ie.hpp
inference-engine/src/legacy_api/include/legacy/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp [deleted file]
inference-engine/src/legacy_api/include/legacy/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.hpp [moved from inference-engine/src/transformations/include/transformations/op_conversions/convert_nms3.hpp with 58% similarity]
inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp
inference-engine/src/legacy_api/src/ie_layer_validators.cpp
inference-engine/src/legacy_api/src/ngraph_ops/nms_ie.cpp
inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.cpp [deleted file]
inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.cpp [new file with mode: 0644]
inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.cpp
inference-engine/src/readers/ir_reader_v7/ie_layer_validators.cpp
inference-engine/src/transformations/include/transformations/op_conversions/convert_previous_nms_to_nms_5.hpp [new file with mode: 0644]
inference-engine/src/transformations/src/transformations/common_optimizations/common_optimizations.cpp
inference-engine/src/transformations/src/transformations/convert_precision.cpp
inference-engine/src/transformations/src/transformations/op_conversions/convert_nms3.cpp [deleted file]
inference-engine/src/transformations/src/transformations/op_conversions/convert_previous_nms_to_nms_5.cpp [new file with mode: 0644]
inference-engine/src/transformations/src/transformations/opset_conversions/convert_opset3_to_opset2.cpp
inference-engine/tests/functional/inference_engine/ngraph_reader/non_max_suppression_tests.cpp
inference-engine/tests/functional/inference_engine/transformations/convert_nms3_test.cpp [deleted file]
inference-engine/tests/functional/inference_engine/transformations/convert_nms4_test.cpp [deleted file]
inference-engine/tests/functional/inference_engine/transformations/convert_nms5_test.cpp [new file with mode: 0644]
inference-engine/tests/functional/inference_engine/transformations/convert_previous_nms_to_nms_5.cpp [new file with mode: 0644]
ngraph/core/include/ngraph/op/non_max_suppression.hpp
ngraph/core/reference/include/ngraph/runtime/reference/non_max_suppression.hpp [new file with mode: 0644]
ngraph/core/reference/src/runtime/reference/non_max_suppression.cpp [new file with mode: 0644]
ngraph/core/src/op/non_max_suppression.cpp
ngraph/python/src/ngraph/opset5/__init__.py
ngraph/python/src/ngraph/opset5/ops.py
ngraph/python/tests/test_ngraph/test_reduction.py
ngraph/test/CMakeLists.txt
ngraph/test/backend/non_max_suppression.in.cpp [new file with mode: 0644]
ngraph/test/runtime/ie/unit_test.manifest
ngraph/test/runtime/interpreter/int_executable.cpp
ngraph/test/runtime/interpreter/int_executable.hpp
ngraph/test/runtime/interpreter/opset_int_tbl.hpp
ngraph/test/type_prop/non_max_suppression.cpp

index d02532d..b298ac9 100644 (file)
@@ -2065,6 +2065,10 @@ public:
      */
     bool sort_result_descending = true;
     /**
+     * @brief Output type for first and third inputs
+     */
+    std::string output_type = "I64";
+    /**
      * @brief Creates a new NonMaxSuppressionLayer instance.
      */
     using CNNLayer::CNNLayer;
index 49e689e..f8b04c5 100644 (file)
@@ -67,6 +67,15 @@ public:
                          const Output<Node>& max_output_boxes_per_class,
                          const Output<Node>& iou_threshold,
                          const Output<Node>& score_threshold,
+                         int center_point_box,
+                         bool sort_result_descending,
+                         const ngraph::element::Type& output_type = ngraph::element::i64);
+
+    NonMaxSuppressionIE3(const Output<Node>& boxes,
+                         const Output<Node>& scores,
+                         const Output<Node>& max_output_boxes_per_class,
+                         const Output<Node>& iou_threshold,
+                         const Output<Node>& score_threshold,
                          const Output<Node>& soft_nms_sigma,
                          int center_point_box,
                          bool sort_result_descending,
diff --git a/inference-engine/src/legacy_api/include/legacy/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp b/inference-engine/src/legacy_api/include/legacy/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp
deleted file mode 100644 (file)
index affd092..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2018-2020 Intel Corporation
-// SPDX-License-Identifier: Apache-2.0
-//
-
-#pragma once
-
-#include <vector>
-#include <memory>
-
-#include <ie_api.h>
-
-#include <ngraph/pass/graph_rewrite.hpp>
-
-namespace ngraph {
-namespace pass {
-
-    class INFERENCE_ENGINE_API_CLASS(ConvertNMS4ToLegacyMatcher);
-
-}  // namespace pass
-}  // namespace ngraph
-
-/*
- * Description:
- *      Convert NMS-4 directly to legacy NMS because NMS-3 and NMS-1 have different shape infer function
- */
-
-
-class ngraph::pass::ConvertNMS4ToLegacyMatcher: public ngraph::pass::MatcherPass {
-public:
-    NGRAPH_RTTI_DECLARATION;
-    ConvertNMS4ToLegacyMatcher();
-};
-
 namespace ngraph {
 namespace pass {
 
-class TRANSFORMATIONS_API ConvertNMS1ToNMS3;
+class INFERENCE_ENGINE_API_CLASS(ConvertNMS5ToLegacyMatcher);
 
 }  // namespace pass
 }  // namespace ngraph
 
-class ngraph::pass::ConvertNMS1ToNMS3: public ngraph::pass::GraphRewrite {
+/*
+ * Description:
+ *      Convert NMS-5 directly to inner NMS.
+ */
+
+
+class ngraph::pass::ConvertNMS5ToLegacyMatcher: public ngraph::pass::MatcherPass {
 public:
     NGRAPH_RTTI_DECLARATION;
-    ConvertNMS1ToNMS3() : GraphRewrite() {
-        convert_nms1_to_nms3();
-    }
-
-private:
-    void convert_nms1_to_nms3();
+    ConvertNMS5ToLegacyMatcher();
 };
+
index a90a9f3..3e8a813 100644 (file)
@@ -653,6 +653,83 @@ InferenceEngine::details::CNNLayerCreator::CNNLayerCreator(const std::shared_ptr
 
     });
 
+    addSpecificCreator({"NonMaxSuppressionIE3"}, [](const std::shared_ptr<::ngraph::Node>& node,
+        const std::map<std::string, std::string>& params) -> CNNLayerPtr {
+        LayerParams attrs = {node->get_friendly_name(), "NonMaxSuppression",
+            details::convertPrecision(node->get_output_element_type(0))};
+
+        auto castedLayer = ::ngraph::as_type_ptr<::ngraph::op::NonMaxSuppressionIE3>(node);
+        IE_ASSERT(castedLayer) << " Operation " << node->description() << " with name "
+            << node->get_friendly_name() << " cannot be casted to ngraph::op::NonMaxSuppressionIE3";
+
+        auto res = std::make_shared<InferenceEngine::NonMaxSuppressionLayer>(attrs);
+        res->params = params;
+
+        res->params["center_point_box"] = castedLayer->m_center_point_box ? "true" : "false";
+        res->params["sort_result_descending"] = castedLayer->m_sort_result_descending ? "true" : "false";
+
+        auto output_type = details::convertPrecision(castedLayer->m_output_type);
+        std::string output_type_str;
+        switch (output_type) {
+        case Precision::I32:
+            output_type_str = "I32";
+            break;
+        case Precision::I64:
+            output_type_str = "I64";
+            break;
+        default:
+            THROW_IE_EXCEPTION << "Unsupported output type";
+        }
+        res->params["output_type"] = output_type_str;
+
+        return res;
+    });
+
+    addSpecificCreator({"NonMaxSuppression"}, [](const std::shared_ptr<::ngraph::Node>& node,
+        const std::map<std::string, std::string>& params) -> CNNLayerPtr {
+        LayerParams attrs = {node->get_friendly_name(), "NonMaxSuppression",
+            details::convertPrecision(node->get_output_element_type(0))};
+
+        auto castedLayer = ::ngraph::as_type_ptr<::ngraph::op::v5::NonMaxSuppression>(node);
+        IE_ASSERT(castedLayer) << " Operation " << node->description() << " with name "
+            << node->get_friendly_name() << " cannot be casted to ngraph::op::v5::NonMaxSuppression";
+
+        auto res = std::make_shared<InferenceEngine::NonMaxSuppressionLayer>(attrs);
+        res->params = params;
+
+        auto box_encoding = castedLayer->get_box_encoding();
+        switch (box_encoding) {
+            case ngraph::op::v5::NonMaxSuppression::BoxEncodingType::CORNER:
+                res->params["center_point_box"] = "false";
+                break;
+            case ngraph::op::v5::NonMaxSuppression::BoxEncodingType::CENTER:
+                res->params["center_point_box"] = "true";
+                break;
+            default:
+                THROW_IE_EXCEPTION << "Unsupported box encoding for NonMaxSuppression op";
+                break;
+        }
+
+        auto output_type = details::convertPrecision(castedLayer->get_output_type());
+        std::string output_type_str;
+        switch (output_type) {
+        case Precision::I32:
+            output_type_str = "I32";
+            break;
+        case Precision::I64:
+            output_type_str = "I64";
+            break;
+        default:
+            THROW_IE_EXCEPTION << "Unsupported output type";
+        }
+        res->params["output_type"] = output_type_str;
+
+        bool sort_result_descending = castedLayer->get_sort_result_descending();
+        res->params["sort_result_descending"] = sort_result_descending ? "true" : "false";
+
+        return res;
+    });
+
     addSpecificCreator({"NonMaxSuppressionIE"}, [](const std::shared_ptr<::ngraph::Node>& node,
                                                  const std::map<std::string, std::string>& params) -> CNNLayerPtr {
         LayerParams attrs = {node->get_friendly_name(), "NonMaxSuppression", details::convertPrecision(node->get_output_element_type(0))};
index ee9757a..86b7396 100644 (file)
@@ -1181,6 +1181,7 @@ void NMSValidator::parseParams(CNNLayer* layer) {
 
     casted->center_point_box = layer->GetParamAsBool("center_point_box", false);
     casted->sort_result_descending = layer->GetParamAsBool("sort_result_descending", true);
+    casted->output_type = layer->GetParamAsString("output_type", "I64");
 }
 
 #define REG_LAYER_VALIDATOR_FOR_TYPE(__validator, __type) _validators[#__type] = std::make_shared<__validator>(#__type)
index 4e83869..be26e1c 100644 (file)
@@ -102,7 +102,20 @@ void op::NonMaxSuppressionIE2::validate_and_infer_types() {
     set_output_type(0, nms->output(0).get_element_type(), nms->output(0).get_partial_shape());
 }
 
-NGRAPH_RTTI_DEFINITION(op::NonMaxSuppressionIE3, "NonMaxSuppressionIE", 3);
+NGRAPH_RTTI_DEFINITION(op::NonMaxSuppressionIE3, "NonMaxSuppressionIE3", 3);
+
+op::NonMaxSuppressionIE3::NonMaxSuppressionIE3(const Output<Node>& boxes,
+                                               const Output<Node>& scores,
+                                               const Output<Node>& max_output_boxes_per_class,
+                                               const Output<Node>& iou_threshold,
+                                               const Output<Node>& score_threshold,
+                                               int center_point_box,
+                                               bool sort_result_descending,
+                                               const ngraph::element::Type& output_type)
+        : Op({boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold}),
+          m_center_point_box(center_point_box), m_sort_result_descending(sort_result_descending), m_output_type(output_type) {
+    constructor_validate_and_infer_types();
+}
 
 op::NonMaxSuppressionIE3::NonMaxSuppressionIE3(const Output<Node>& boxes,
                                                const Output<Node>& scores,
@@ -139,8 +152,13 @@ static constexpr size_t max_output_boxes_per_class_port = 2;
 int64_t op::NonMaxSuppressionIE3::max_boxes_output_from_input() const {
     int64_t max_output_boxes{0};
 
+    size_t num_of_inputs = inputs().size();
+    if (num_of_inputs < 3) {
+        return 0;
+    }
+
     const auto max_output_boxes_input =
-        as_type_ptr<op::Constant>(input_value(2).get_node_shared_ptr());
+        as_type_ptr<op::Constant>(input_value(max_output_boxes_per_class_port).get_node_shared_ptr());
     max_output_boxes = max_output_boxes_input->cast_vector<int64_t>().at(0);
 
     return max_output_boxes;
diff --git a/inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.cpp b/inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.cpp
deleted file mode 100644 (file)
index ca6a745..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2018-2020 Intel Corporation
-// SPDX-License-Identifier: Apache-2.0
-//
-
-#include <memory>
-#include <vector>
-
-#include <ngraph/graph_util.hpp>
-#include <ngraph/opsets/opset1.hpp>
-#include <ngraph/opsets/opset4.hpp>
-#include <legacy/ngraph_ops/nms_ie.hpp>
-#include <ngraph/rt_info.hpp>
-#include <transformations/utils/utils.hpp>
-
-#include "legacy/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp"
-
-NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertNMS4ToLegacyMatcher, "ConvertNMS4ToLegacyMatcher", 0);
-
-ngraph::pass::ConvertNMS4ToLegacyMatcher::ConvertNMS4ToLegacyMatcher() {
-    auto boxes = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1000, 4});
-    auto scores = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1, 1000});
-    auto max_output_boxes_per_class = ngraph::opset4::Constant::create(element::i64, Shape{}, {10});
-    auto iou_threshold = ngraph::opset4::Constant::create(element::f32, Shape{}, {0.75});
-    auto score_threshold = ngraph::opset4::Constant::create(element::f32, Shape{}, {0.7});
-    auto nms = std::make_shared<ngraph::opset4::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
-                                                                   iou_threshold, score_threshold);
-
-    ngraph::matcher_pass_callback callback = [](pattern::Matcher &m) {
-        auto nms_4 = std::dynamic_pointer_cast<ngraph::opset4::NonMaxSuppression>(m.get_match_root());
-        if (!nms_4) {
-            return false;
-        }
-
-        const auto new_args = nms_4->input_values();
-        const auto& arg2 = new_args.size() > 2 ? new_args.at(2) : ngraph::opset4::Constant::create(element::i32, Shape{}, {0});
-        const auto& arg3 = new_args.size() > 3 ? new_args.at(3) : ngraph::opset4::Constant::create(element::f32, Shape{}, {.0f});
-        const auto& arg4 = new_args.size() > 4 ? new_args.at(4) : ngraph::opset4::Constant::create(element::f32, Shape{}, {.0f});
-
-        const auto max_output_boxes_per_class_rank = arg2.get_partial_shape().rank();
-        const auto iou_threshold_rank = arg3.get_partial_shape().rank();
-        const auto score_threshold_rank = arg4.get_partial_shape().rank();
-
-        // Check that required ranks are not dynamic
-        if (max_output_boxes_per_class_rank.is_dynamic() ||
-            iou_threshold_rank.is_dynamic() ||
-            score_threshold_rank.is_dynamic()) {
-            return false;
-        }
-
-        if (max_output_boxes_per_class_rank.get_length() == 1 &&
-            iou_threshold_rank.get_length() == 1 &&
-            score_threshold_rank.get_length() == 1) {
-            return false;
-        }
-
-        // vector of new nGraph operations
-        NodeVector new_ops;
-
-        auto new_max_per_class = arg2;
-        if (max_output_boxes_per_class_rank.get_length() == 0) {
-            // 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<opset1::Constant>(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<int64_t>());
-            } else {
-                new_max_per_class = std::make_shared<ngraph::op::Unsqueeze>(arg2, opset1::Constant::create(element::i64, Shape{1}, {0}));
-                new_ops.push_back(new_max_per_class.get_node_shared_ptr());
-            }
-        }
-        auto new_iou_threshold = arg3;
-        if (iou_threshold_rank.get_length() == 0) {
-            new_iou_threshold = std::make_shared<ngraph::op::Unsqueeze>(arg3, opset1::Constant::create(element::i64, Shape{1}, {0}));
-            new_ops.push_back(new_iou_threshold.get_node_shared_ptr());
-        }
-        auto new_score_threshold = arg4;
-        if (score_threshold_rank.get_length() == 0) {
-            new_score_threshold = std::make_shared<ngraph::op::Unsqueeze>(arg4, opset1::Constant::create(element::i64, Shape{1}, {0}));
-            new_ops.push_back(new_score_threshold.get_node_shared_ptr());
-        }
-
-        int center_point_box = 0;
-        switch (nms_4->get_box_encoding()) {
-            case ::ngraph::opset4::NonMaxSuppression::BoxEncodingType::CENTER:
-                center_point_box = 1;
-                break;
-            case ::ngraph::opset4::NonMaxSuppression::BoxEncodingType::CORNER:
-                center_point_box = 0;
-                break;
-            default:
-                throw ngraph_error("NonMaxSuppression layer " + nms_4->get_friendly_name() +
-                                   " has unsupported box encoding");
-        }
-        const auto nms_legacy = std::make_shared<op::NonMaxSuppressionIE2>(
-                new_args.at(0),
-                new_args.at(1),
-                new_max_per_class,
-                new_iou_threshold,
-                new_score_threshold,
-                center_point_box,
-                nms_4->get_sort_result_descending(),
-                nms_4->get_output_type());
-        new_ops.push_back(nms_legacy);
-
-        nms_legacy->set_friendly_name(nms_4->get_friendly_name());
-        ngraph::copy_runtime_info(nms_4, new_ops);
-        ngraph::replace_node(nms_4, nms_legacy);
-        return true;
-    };
-
-    auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS4ToNMSLegacy");
-    this->register_matcher(m, callback);
-}
diff --git a/inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.cpp b/inference-engine/src/legacy_api/src/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.cpp
new file mode 100644 (file)
index 0000000..8ef630e
--- /dev/null
@@ -0,0 +1,110 @@
+// Copyright (C) 2018-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <memory>
+#include <vector>
+
+#include <ngraph/graph_util.hpp>
+#include <ngraph/opsets/opset1.hpp>
+#include <ngraph/opsets/opset5.hpp>
+#include <legacy/ngraph_ops/nms_ie.hpp>
+#include <ngraph/rt_info.hpp>
+#include <ngraph/pattern/op/wrap_type.hpp>
+#include <transformations/utils/utils.hpp>
+
+#include "legacy/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.hpp"
+
+NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertNMS5ToLegacyMatcher, "ConvertNMS5ToLegacyMatcher", 0);
+
+ngraph::pass::ConvertNMS5ToLegacyMatcher::ConvertNMS5ToLegacyMatcher() {
+    auto nms = ngraph::pattern::wrap_type<ngraph::opset5::NonMaxSuppression>();
+
+    ngraph::matcher_pass_callback callback = [](pattern::Matcher &m) {
+        auto nms_5 = std::dynamic_pointer_cast<ngraph::opset5::NonMaxSuppression>(m.get_match_root());
+        if (!nms_5) {
+            return false;
+        }
+
+        const auto new_args = nms_5->input_values();
+        const std::size_t num_of_inputs = new_args.size();
+
+        const auto& arg2 = num_of_inputs > 2 ? new_args.at(2) : ngraph::opset5::Constant::create(element::i32, Shape{}, {0});
+        const auto& arg3 = num_of_inputs > 3 ? new_args.at(3) : ngraph::opset5::Constant::create(element::f32, Shape{}, {.0f});
+        const auto& arg4 = num_of_inputs > 4 ? new_args.at(4) : ngraph::opset5::Constant::create(element::f32, Shape{}, {.0f});
+
+        // vector of new nGraph operations
+        NodeVector new_ops;
+
+        auto one_dim_shape = Shape{1};
+
+        Output<Node> new_max_per_class;
+        Output<Node> new_iou_threshold;
+        Output<Node> new_score_threshold;
+        Output<Node> new_soft_nms_sigma;
+
+        Output<Node> new_shape_for_max_per_class = opset1::Constant::create(ngraph::element::i64, Shape{1}, {1});
+        Output<Node> new_shape_for_iou_threshold = opset1::Constant::create(ngraph::element::i64, Shape{1}, {1});
+        Output<Node> new_shape_for_score_threshold = opset1::Constant::create(ngraph::element::i64, Shape{1}, {1});
+        Output<Node> new_shape_for_soft_nms_sigma = opset1::Constant::create(ngraph::element::i64, Shape{1}, {1});
+
+        new_max_per_class = std::make_shared<opset1::Reshape>(arg2, new_shape_for_max_per_class, true);
+        new_ops.emplace_back(new_max_per_class.get_node_shared_ptr());
+
+        new_iou_threshold = std::make_shared<opset1::Reshape>(arg3, new_shape_for_iou_threshold, true);
+        new_ops.emplace_back(new_iou_threshold.get_node_shared_ptr());
+
+        new_score_threshold = std::make_shared<opset1::Reshape>(arg4, new_shape_for_score_threshold, true);
+        new_ops.emplace_back(new_score_threshold.get_node_shared_ptr());
+
+        int center_point_box = 0;
+        switch (nms_5->get_box_encoding()) {
+            case ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CENTER:
+                center_point_box = 1;
+                break;
+            case ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER:
+                center_point_box = 0;
+                break;
+            default:
+                throw ngraph_error("NonMaxSuppression layer " + nms_5->get_friendly_name() +
+                                   " has unsupported box encoding");
+        }
+
+        std::shared_ptr<op::NonMaxSuppressionIE3> nms_legacy{nullptr};
+
+        if (num_of_inputs > 5 && nms_5->soft_nms_sigma_from_input() != 0.0f) {
+            new_soft_nms_sigma = std::make_shared<opset1::Reshape>(new_args.at(5), new_shape_for_soft_nms_sigma, true);
+            new_ops.emplace_back(new_soft_nms_sigma.get_node_shared_ptr());
+            nms_legacy = std::make_shared<op::NonMaxSuppressionIE3>(
+                    new_args.at(0),
+                    new_args.at(1),
+                    new_max_per_class,
+                    new_iou_threshold,
+                    new_score_threshold,
+                    new_soft_nms_sigma,
+                    center_point_box,
+                    nms_5->get_sort_result_descending(),
+                    nms_5->get_output_type());
+            new_ops.emplace_back(nms_legacy);
+        } else {
+            nms_legacy = std::make_shared<op::NonMaxSuppressionIE3>(
+                    new_args.at(0),
+                    new_args.at(1),
+                    new_max_per_class,
+                    new_iou_threshold,
+                    new_score_threshold,
+                    center_point_box,
+                    nms_5->get_sort_result_descending(),
+                    nms_5->get_output_type());
+            new_ops.emplace_back(nms_legacy);
+        }
+
+        nms_legacy->set_friendly_name(nms_5->get_friendly_name());
+        ngraph::copy_runtime_info(nms_5, new_ops);
+        ngraph::replace_node(nms_5, nms_legacy);
+        return true;
+    };
+
+    auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS5ToNMSLegacy");
+    this->register_matcher(m, callback);
+}
index 53ce548..4182ac1 100644 (file)
@@ -13,7 +13,7 @@
 #include "legacy/transformations/convert_opset1_to_legacy/convert_mul_add_to_scaleshift_or_power.hpp"
 #include "legacy/transformations/convert_opset1_to_legacy/convert_mul_or_add_finally.hpp"
 #include "legacy/transformations/convert_opset1_to_legacy/convert_nms_to_nms_ie.hpp"
-#include "legacy/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp"
+#include "legacy/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.hpp"
 #include "legacy/transformations/convert_opset1_to_legacy/convert_normalizel2_to_normalize_ie.hpp"
 #include "legacy/transformations/convert_opset1_to_legacy/convert_one_hot_to_one_hot_ie.hpp"
 #include "legacy/transformations/convert_opset1_to_legacy/convert_pad_to_pad_ie.hpp"
@@ -133,7 +133,7 @@ bool ngraph::pass::ConvertOpSet1ToLegacy::run_on_function(std::shared_ptr<ngraph
     anchor->add_matcher<ngraph::pass::ConvertGatherTreeToGatherTreeIEMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertTopKToTopKIEMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertNMSToNMSIEMatcher>();
-    anchor->add_matcher<ngraph::pass::ConvertNMS4ToLegacyMatcher>();
+    anchor->add_matcher<ngraph::pass::ConvertNMS5ToLegacyMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertGRUSequenceMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertRNNSequenceMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertLSTMSequenceMatcher>();
index 3d3b89a..245e87a 100644 (file)
@@ -2098,9 +2098,9 @@ void NMSValidator::checkParams(const CNNLayer* layer) {
 
 void NMSValidator::checkShapes(const CNNLayer* layer, const vector<SizeVector>& inShapes) const {
     size_t numInputs = inShapes.size();
-    if (numInputs < 2 || numInputs > 5)
+    if (numInputs < 2 || numInputs > 6)
         THROW_IE_EXCEPTION << layer->name
-                           << " NonMaxSuppression can take 2 - 5 inputs, but actually it has: " << numInputs;
+                           << " NonMaxSuppression can take 2 - 6 inputs, but actually it has: " << numInputs;
 
     if (inShapes[0].size() != 3 || inShapes[0][2] != 4)
         THROW_IE_EXCEPTION << layer->name << " 'boxes' should be with shape [num_batches, spatial_dimension, 4]";
diff --git a/inference-engine/src/transformations/include/transformations/op_conversions/convert_previous_nms_to_nms_5.hpp b/inference-engine/src/transformations/include/transformations/op_conversions/convert_previous_nms_to_nms_5.hpp
new file mode 100644 (file)
index 0000000..f029671
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (C) 2018-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <memory>
+#include <utility>
+
+#include <transformations_visibility.hpp>
+#include <ngraph/pass/graph_rewrite.hpp>
+
+namespace ngraph {
+namespace pass {
+
+class TRANSFORMATIONS_API ConvertNMS1ToNMS5;
+class TRANSFORMATIONS_API ConvertNMS3ToNMS5;
+class TRANSFORMATIONS_API ConvertNMS4ToNMS5;
+class TRANSFORMATIONS_API ConvertPreviousNMSToNMS5;
+
+}  // namespace pass
+}  // namespace ngraph
+
+class ngraph::pass::ConvertNMS1ToNMS5: public ngraph::pass::MatcherPass {
+public:
+    NGRAPH_RTTI_DECLARATION;
+    ConvertNMS1ToNMS5();
+};
+
+class ngraph::pass::ConvertNMS3ToNMS5: public ngraph::pass::MatcherPass {
+public:
+    NGRAPH_RTTI_DECLARATION;
+    ConvertNMS3ToNMS5();
+};
+
+class ngraph::pass::ConvertNMS4ToNMS5: public ngraph::pass::MatcherPass {
+public:
+    NGRAPH_RTTI_DECLARATION;
+    ConvertNMS4ToNMS5();
+};
+
+class ngraph::pass::ConvertPreviousNMSToNMS5: public ngraph::pass::GraphRewrite {
+public:
+    NGRAPH_RTTI_DECLARATION;
+    ConvertPreviousNMSToNMS5() {
+        add_matcher<ngraph::pass::ConvertNMS1ToNMS5>();
+        add_matcher<ngraph::pass::ConvertNMS3ToNMS5>();
+        add_matcher<ngraph::pass::ConvertNMS4ToNMS5>();
+    }
+};
index 4f3a264..3da68fc 100644 (file)
@@ -42,6 +42,7 @@
 #include "transformations/op_conversions/reduce_l1_decomposition.hpp"
 #include "transformations/op_conversions/reduce_l2_decomposition.hpp"
 #include "transformations/op_conversions/hswish_decomposition.hpp"
+#include "transformations/op_conversions/convert_previous_nms_to_nms_5.hpp"
 #include "transformations/op_conversions/hsigmoid_decomposition.hpp"
 #include "transformations/op_conversions/log_softmax_decomposition.hpp"
 
@@ -112,6 +113,8 @@ bool ngraph::pass::CommonOptimizations::run_on_function(std::shared_ptr<ngraph::
     manager.register_pass<ngraph::pass::GroupConvolutionBackpropDataMultiplyFusion>();
     manager.register_pass<ngraph::pass::ConstantFolding>();
 
+    manager.register_pass<ngraph::pass::ConvertPreviousNMSToNMS5>();
+
     auto fq_fusions = manager.register_pass<ngraph::pass::GraphRewrite>();
     fq_fusions->add_matcher<ngraph::pass::FakeQuantizeMulFusion>();
     fq_fusions->add_matcher<ngraph::pass::FakeQuantizeReshapeFusion>();
index bb7aeac..c08fe61 100644 (file)
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include <ngraph/opsets/opset5.hpp>
 #include <ngraph/opsets/opset4.hpp>
 #include <ngraph/opsets/opset3.hpp>
 #include <ngraph/opsets/opset1.hpp>
@@ -21,6 +22,7 @@ bool fuse_type_to_parameter(std::shared_ptr<ngraph::Node> & node, ngraph::elemen
 bool fuse_type_to_convert(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
 bool fuse_type_to_nms3(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
 bool fuse_type_to_nms4(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
+bool fuse_type_to_nms5(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
 bool fuse_type_to_topk(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
 bool fuse_type_to_nonzero(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
 bool fuse_type_to_bucketize(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx);
@@ -81,6 +83,7 @@ bool ngraph::pass::ConvertPrecision::run_on_function(std::shared_ptr<ngraph::Fun
         {opset4::ShapeOf::type_info, fuse_type_to_shapeof},
         {opset3::NonMaxSuppression::type_info, fuse_type_to_nms3},
         {opset4::NonMaxSuppression::type_info, fuse_type_to_nms4},
+        {opset5::NonMaxSuppression::type_info, fuse_type_to_nms5},
         {opset4::TopK::type_info, fuse_type_to_topk},
         {opset4::NonZero::type_info, fuse_type_to_nonzero},
         {opset4::Bucketize::type_info, fuse_type_to_bucketize},
@@ -237,6 +240,14 @@ bool fuse_type_to_nms4(std::shared_ptr<ngraph::Node> & node, ngraph::element::Ty
     return false;
 }
 
+bool fuse_type_to_nms5(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx) {
+    if (auto nms = as_type_ptr<opset5::NonMaxSuppression>(node)) {
+        nms->set_output_type(to);
+        return true;
+    }
+    return false;
+}
+
 bool fuse_type_to_topk(std::shared_ptr<ngraph::Node> & node, ngraph::element::Type to, size_t idx) {
     if (auto topk = as_type_ptr<opset4::TopK>(node)) {
         if (idx == 1 && (to == element::i32 || to == element::i64)) {
diff --git a/inference-engine/src/transformations/src/transformations/op_conversions/convert_nms3.cpp b/inference-engine/src/transformations/src/transformations/op_conversions/convert_nms3.cpp
deleted file mode 100644 (file)
index fa90efa..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2018-2020 Intel Corporation
-// SPDX-License-Identifier: Apache-2.0
-//
-
-#include "transformations/op_conversions/convert_nms3.hpp"
-
-#include <memory>
-#include <vector>
-
-#include <ngraph/opsets/opset1.hpp>
-#include <ngraph/opsets/opset2.hpp>
-#include <ngraph/opsets/opset3.hpp>
-#include <ngraph/rt_info.hpp>
-
-NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertNMS1ToNMS3, "ConvertNMS1ToNMS3", 0);
-
-void ngraph::pass::ConvertNMS1ToNMS3::convert_nms1_to_nms3() {
-    auto boxes = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1000, 4});
-    auto scores = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1, 1000});
-    auto max_output_boxes_per_class = ngraph::opset3::Constant::create(element::i64, Shape{}, {10});
-    auto iou_threshold = ngraph::opset3::Constant::create(element::f32, Shape{}, {0.75});
-    auto score_threshold = ngraph::opset3::Constant::create(element::f32, Shape{}, {0.7});
-    auto nms = std::make_shared<ngraph::opset1::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
-                                                                   iou_threshold, score_threshold);
-
-    ngraph::graph_rewrite_callback callback = [](pattern::Matcher &m) {
-        auto nms = std::dynamic_pointer_cast<ngraph::opset1::NonMaxSuppression>(m.get_match_root());
-        if (!nms) {
-            return false;
-        }
-
-        auto nms3 = std::make_shared<ngraph::opset3::NonMaxSuppression>(nms->input_value(0), nms->input_value(1),
-                nms->input_value(2), nms->input_value(3), nms->input_value(4),
-                static_cast<const op::v3::NonMaxSuppression::BoxEncodingType>(nms->get_box_encoding()),
-                nms->get_sort_result_descending());
-
-        nms3->set_friendly_name(nms->get_friendly_name());
-        ngraph::copy_runtime_info(nms, nms3);
-        ngraph::replace_node(nms, nms3);
-        return true;
-    };
-
-    auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS1ToNMS3");
-    this->add_matcher(m, callback, PassProperty::CHANGE_DYNAMIC_STATE);
-}
diff --git a/inference-engine/src/transformations/src/transformations/op_conversions/convert_previous_nms_to_nms_5.cpp b/inference-engine/src/transformations/src/transformations/op_conversions/convert_previous_nms_to_nms_5.cpp
new file mode 100644 (file)
index 0000000..3328f02
--- /dev/null
@@ -0,0 +1,209 @@
+// Copyright (C) 2018-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "transformations/op_conversions/convert_previous_nms_to_nms_5.hpp"
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <ngraph/opsets/opset1.hpp>
+#include <ngraph/opsets/opset3.hpp>
+#include <ngraph/opsets/opset4.hpp>
+#include <ngraph/opsets/opset5.hpp>
+#include <ngraph/rt_info.hpp>
+#include <ngraph/pattern/op/wrap_type.hpp>
+
+using namespace ngraph;
+
+namespace {
+struct NMSAttributes {
+    ngraph::element::Type output_type;
+    ngraph::opset5::NonMaxSuppression::BoxEncodingType box_encoding;
+    bool sort_result_descending;
+    bool is_supported_nms;
+};
+
+    NMSAttributes get_nms4_attrs(const std::shared_ptr<ngraph::opset4::NonMaxSuppression>& nms4) {
+        NMSAttributes attrs;
+
+        attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+        attrs.is_supported_nms = true;
+        attrs.sort_result_descending = true;
+        attrs.output_type = ::ngraph::element::i64;
+
+        switch (nms4->get_box_encoding()) {
+            case ::ngraph::opset4::NonMaxSuppression::BoxEncodingType::CENTER:
+                attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CENTER;
+                break;
+            case ::ngraph::opset4::NonMaxSuppression::BoxEncodingType::CORNER:
+                attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+                break;
+            default:
+                throw ngraph_error("NonMaxSuppression layer " + nms4->get_friendly_name() +
+                                   " has unsupported box encoding");
+        }
+
+        attrs.sort_result_descending = nms4->get_sort_result_descending();
+        attrs.output_type = nms4->get_output_type();
+
+        return attrs;
+    }
+
+    NMSAttributes get_nms3_attrs(const std::shared_ptr<ngraph::opset3::NonMaxSuppression>& nms3) {
+        NMSAttributes attrs;
+
+        attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+        attrs.is_supported_nms = true;
+        attrs.sort_result_descending = true;
+        attrs.output_type = ::ngraph::element::i64;
+
+        switch (nms3->get_box_encoding()) {
+            case ::ngraph::opset3::NonMaxSuppression::BoxEncodingType::CENTER:
+                attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CENTER;
+                break;
+            case ::ngraph::opset3::NonMaxSuppression::BoxEncodingType::CORNER:
+                attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+                break;
+            default:
+                throw ngraph_error("NonMaxSuppression layer " + nms3->get_friendly_name() +
+                                   " has unsupported box encoding");
+        }
+
+        attrs.sort_result_descending = nms3->get_sort_result_descending();
+        attrs.output_type = nms3->get_output_type();
+
+        return attrs;
+    }
+
+    NMSAttributes get_nms1_attrs(const std::shared_ptr<ngraph::opset1::NonMaxSuppression>& nms1) {
+        NMSAttributes attrs;
+
+        attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+        attrs.is_supported_nms = true;
+        attrs.sort_result_descending = true;
+        attrs.output_type = ::ngraph::element::i64;
+
+        switch (nms1->get_box_encoding()) {
+            case ::ngraph::opset1::NonMaxSuppression::BoxEncodingType::CENTER:
+                attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CENTER;
+                break;
+            case ::ngraph::opset1::NonMaxSuppression::BoxEncodingType::CORNER:
+                attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+                break;
+            default:
+                throw ngraph_error("NonMaxSuppression layer " + nms1->get_friendly_name() +
+                                   " has unsupported box encoding");
+        }
+
+        attrs.sort_result_descending = nms1->get_sort_result_descending();
+
+        return attrs;
+    }
+
+    NMSAttributes get_nms_attrs(const std::shared_ptr<ngraph::Node>& root) {
+        NMSAttributes attrs;
+        attrs.output_type = ::ngraph::element::i64;
+        attrs.box_encoding = ::ngraph::opset5::NonMaxSuppression::BoxEncodingType::CORNER;
+        attrs.sort_result_descending = false;
+        attrs.is_supported_nms = false;
+
+        auto nms_4 = std::dynamic_pointer_cast<ngraph::opset4::NonMaxSuppression>(root);
+        if (nms_4) {
+            return get_nms4_attrs(nms_4);
+        }
+        auto nms_3 = std::dynamic_pointer_cast<ngraph::opset3::NonMaxSuppression>(root);
+        if (nms_3) {
+            return get_nms3_attrs(nms_3);
+        }
+        auto nms_1 = std::dynamic_pointer_cast<ngraph::opset1::NonMaxSuppression>(root);
+        if (nms_1) {
+            return get_nms1_attrs(nms_1);
+        }
+
+        return attrs;
+    }
+
+    bool callback_func(pattern::Matcher &m) {
+        auto root = m.get_match_root();
+
+        auto attrs = get_nms_attrs(root);
+        if (!attrs.is_supported_nms) {
+            return false;
+        }
+
+        const auto new_args = root->input_values();
+
+        size_t num_of_args = new_args.size();
+
+        const auto& arg2 = num_of_args > 2 ? new_args.at(2) : ngraph::opset5::Constant::create(element::i64, Shape{}, {0});
+        const auto& arg3 = num_of_args > 3 ? new_args.at(3) : ngraph::opset5::Constant::create(element::f32, Shape{}, {.0f});
+        const auto& arg4 = num_of_args > 4 ? new_args.at(4) : ngraph::opset5::Constant::create(element::f32, Shape{}, {.0f});
+
+        // list of new nGraph operations
+        std::list<std::shared_ptr<::ngraph::Node>> new_ops_list;
+
+        if (num_of_args <= 4) {
+            new_ops_list.push_front(arg4.get_node_shared_ptr());
+        }
+        if (num_of_args <= 3) {
+            new_ops_list.push_front(arg3.get_node_shared_ptr());
+        }
+        if (num_of_args <= 2) {
+            new_ops_list.push_front(arg2.get_node_shared_ptr());
+        }
+
+        const auto nms_5 = std::make_shared<ngraph::op::v5::NonMaxSuppression>(
+                new_args.at(0),
+                new_args.at(1),
+                arg2,
+                arg3,
+                arg4,
+                attrs.box_encoding,
+                attrs.sort_result_descending,
+                attrs.output_type);
+
+        new_ops_list.push_back(nms_5);
+
+        // vector of new nGraph operations
+        NodeVector new_ops(new_ops_list.begin(), new_ops_list.end());
+
+        nms_5->set_friendly_name(root->get_friendly_name());
+        ngraph::copy_runtime_info(root, new_ops);
+        root->output(0).replace(nms_5->output(0));
+        return true;
+    }
+} // namespace
+
+NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertPreviousNMSToNMS5, "ConvertPreviousNMSToNMS5", 0);
+
+NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertNMS4ToNMS5, "ConvertNMS4ToNMS5", 0);
+
+ngraph::pass::ConvertNMS4ToNMS5::ConvertNMS4ToNMS5() {
+    auto nms = ngraph::pattern::wrap_type<ngraph::opset4::NonMaxSuppression>();
+    ngraph::matcher_pass_callback callback = callback_func;
+
+    auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS4ToNMS5");
+    this->register_matcher(m, callback);
+}
+
+NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertNMS3ToNMS5, "ConvertNMS3ToNMS5", 0);
+
+ngraph::pass::ConvertNMS3ToNMS5::ConvertNMS3ToNMS5() {
+    auto nms = ngraph::pattern::wrap_type<ngraph::opset3::NonMaxSuppression>();
+    ngraph::matcher_pass_callback callback = callback_func;
+
+    auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS3ToNMS5");
+    this->register_matcher(m, callback);
+}
+
+NGRAPH_RTTI_DEFINITION(ngraph::pass::ConvertNMS1ToNMS5, "ConvertNMS1ToNMS5", 0);
+
+ngraph::pass::ConvertNMS1ToNMS5::ConvertNMS1ToNMS5() {
+    auto nms = ngraph::pattern::wrap_type<ngraph::opset1::NonMaxSuppression>();
+    ngraph::matcher_pass_callback callback = callback_func;
+
+    auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS1ToNMS5");
+    this->register_matcher(m, callback);
+}
index 85f54d1..2ad381e 100644 (file)
@@ -5,7 +5,6 @@
 #include "transformations/opset_conversions/convert_opset3_to_opset2.hpp"
 
 #include "transformations/op_conversions/convert_broadcast3.hpp"
-#include "transformations/op_conversions/convert_nms3.hpp"
 #include "transformations/op_conversions/convert_shapeof3.hpp"
 #include "transformations/op_conversions/convert_shuffle_channels3.hpp"
 #include "transformations/op_conversions/convert_topk3.hpp"
@@ -25,7 +24,6 @@ bool ngraph::pass::ConvertOpSet3ToOpSet2::run_on_function(std::shared_ptr<ngraph
     ngraph::pass::Manager manager(get_pass_config());
 
     manager.register_pass<ngraph::pass::ConvertBroadcast3>();
-    manager.register_pass<ngraph::pass::ConvertNMS1ToNMS3>();
     manager.register_pass<ngraph::pass::ConvertShapeOf3>();
     manager.register_pass<ngraph::pass::ConvertShuffleChannels3>();
     manager.register_pass<ngraph::pass::ConvertTopK3>();
index bc2f3aa..27ee391 100644 (file)
@@ -4,12 +4,30 @@
 
 #include <string>
 #include "ngraph_reader_tests.hpp"
-/*
-TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
+
+#include <ngraph/function.hpp>
+#include <ngraph/graph_util.hpp>
+#include <ngraph/opsets/opset1.hpp>
+#include <ngraph/opsets/opset3.hpp>
+#include <ngraph/opsets/opset4.hpp>
+#include <ngraph/opsets/opset5.hpp>
+#include <transformations/common_optimizations/common_optimizations.hpp>
+#include <transformations/init_node_info.hpp>
+#include <transformations/utils/utils.hpp>
+#include <ngraph/pass/manager.hpp>
+
+#include "common_test_utils/ngraph_test_utils.hpp"
+#include "generic_ie.hpp"
+
+#include "legacy/convert_function_to_cnn_network.hpp"
+
+using namespace ngraph;
+
+TEST_F(NGraphReaderTests, ReadNonMaxSuppression5) {
     std::string model = R"V0G0N(
 <net name="Network" version="10">
     <layers>
-        <layer id="0" name="in1" type="Parameter" >
+        <layer id="0" name="in1" type="Parameter" version="opset1">
             <data element_type="f32" shape="1,15130,4"/>
             <output>
                 <port id="0" precision="FP32">
@@ -19,7 +37,7 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
                 </port>
             </output>
         </layer>
-        <layer id="1" name="in2" type="Parameter" >
+        <layer id="1" name="in2" type="Parameter" version="opset1">
             <data element_type="f32" shape="1,80,15130"/>
             <output>
                 <port id="0" precision="FP32">
@@ -29,55 +47,69 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
                 </port>
             </output>
         </layer>
-        <layer id="2" name="max_output_boxes_per_class" precision="I64" type="Const">
+        <layer id="2" name="max_output_boxes_per_class" precision="I64" type="Const" version="opset1">
             <data offset="0" size="8"/>
             <output>
-                <port id="0"/>
+                <port id="0" precision="I64"/>
             </output>
         </layer>
-        <layer id="3" name="iou_threshold" precision="FP32" type="Const">
+        <layer id="3" name="iou_threshold" precision="FP32" type="Const" version="opset1">
             <data offset="8" size="4"/>
             <output>
-                <port id="0"/>
+                <port id="0" precision="FP32"/>
             </output>
         </layer>
-        <layer id="4" name="score_threshold" precision="FP32" type="Const">
+        <layer id="4" name="score_threshold" precision="FP32" type="Const" version="opset1">
             <data offset="12" size="4"/>
             <output>
-                <port id="0"/>
+                <port id="0" precision="FP32"/>
             </output>
         </layer>
-        <layer id="5" name="nms" type="NonMaxSuppression">
+        <layer id="5" name="soft_nms_sigma" precision="FP32" type="Const" version="opset1">
+            <data offset="16" size="4"/>
+            <output>
+                <port id="0" precision="FP32"/>
+            </output>
+        </layer>
+        <layer id="6" name="nms" type="NonMaxSuppression" version="opset5">
             <data box_encoding="corner" sort_result_descending="0"/>
             <input>
-                <port id="0">
+                <port id="0" precision="FP32">
                     <dim>1</dim>
                     <dim>15130</dim>
                     <dim>4</dim>
                 </port>
-                <port id="1">
+                <port id="1" precision="FP32">
                     <dim>1</dim>
                     <dim>80</dim>
                     <dim>15130</dim>
                 </port>
-                <port id="2"/>
-                <port id="3"/>
-                <port id="4"/>
+                <port id="2" precision="I64"/>
+                <port id="3" precision="FP32"/>
+                <port id="4" precision="FP32"/>
+                <port id="5" precision="FP32"/>
             </input>
             <output>
-                <port id="5" precision="I64">
+                <port id="6" precision="I64">
                     <dim>15130</dim>
                     <dim>3</dim>
                 </port>
+                <port id="7" precision="FP32">
+                    <dim>15130</dim>
+                    <dim>3</dim>
+                </port>
+                <port id="8" precision="I64">
+                    <dim>1</dim>
+                </port>
             </output>
         </layer>
-        <layer id="6" name="mul" type="Multiply">
+        <layer id="7" name="mul" type="Multiply" version="opset1">
             <input>
-                <port id="0">
+                <port id="0" precision="I64">
                     <dim>15130</dim>
                     <dim>3</dim>
                 </port>
-                <port id="1">
+                <port id="1" precision="I64">
                     <dim>15130</dim>
                     <dim>3</dim>
                 </port>
@@ -89,7 +121,55 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
                 </port>
             </output>
         </layer>
-        <layer id="7" name="output" type="Result" >
+        <layer id="8" name="output" type="Result" version="opset1">
+            <input>
+                <port id="0" precision="I64">
+                    <dim>15130</dim>
+                    <dim>3</dim>
+                </port>
+            </input>
+        </layer>
+        <layer id="9" name="mul2" type="Multiply" version="opset1">
+            <input>
+                <port id="0" precision="I64">
+                    <dim>1</dim>
+                </port>
+                <port id="1" precision="I64">
+                    <dim>1</dim>
+                </port>
+            </input>
+            <output>
+                <port id="2" precision="I64">
+                    <dim>1</dim>
+                </port>
+            </output>
+        </layer>
+        <layer id="10" name="output2" type="Result" version="opset1">
+            <input>
+                <port id="0" precision="I64">
+                    <dim>1</dim>
+                </port>
+            </input>
+        </layer>
+        <layer id="11" name="mul3" type="Multiply" version="opset1">
+            <input>
+                <port id="0" precision="I64">
+                    <dim>15130</dim>
+                    <dim>3</dim>
+                </port>
+                <port id="1" precision="I64">
+                    <dim>15130</dim>
+                    <dim>3</dim>
+                </port>
+            </input>
+            <output>
+                <port id="2" precision="I64">
+                    <dim>15130</dim>
+                    <dim>3</dim>
+                </port>
+            </output>
+        </layer>
+        <layer id="12" name="output3" type="Result" version="opset1">
             <input>
                 <port id="0" precision="I64">
                     <dim>15130</dim>
@@ -99,34 +179,39 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
         </layer>
     </layers>
     <edges>
-        <edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
-        <edge from-layer="1" from-port="0" to-layer="5" to-port="1"/>
-        <edge from-layer="2" from-port="0" to-layer="5" to-port="2"/>
-        <edge from-layer="3" from-port="0" to-layer="5" to-port="3"/>
-        <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
-        <edge from-layer="5" from-port="5" to-layer="6" to-port="0"/>
-        <edge from-layer="5" from-port="5" to-layer="6" to-port="1"/>
-        <edge from-layer="6" from-port="2" to-layer="7" to-port="0"/>
+        <edge from-layer="0" from-port="0" to-layer="6" to-port="0"/>
+        <edge from-layer="1" from-port="0" to-layer="6" to-port="1"/>
+        <edge from-layer="2" from-port="0" to-layer="6" to-port="2"/>
+        <edge from-layer="3" from-port="0" to-layer="6" to-port="3"/>
+        <edge from-layer="4" from-port="0" to-layer="6" to-port="4"/>
+        <edge from-layer="5" from-port="0" to-layer="6" to-port="5"/>
+        <edge from-layer="6" from-port="6" to-layer="7" to-port="0"/>
+        <edge from-layer="6" from-port="6" to-layer="7" to-port="1"/>
+        <edge from-layer="6" from-port="7" to-layer="11" to-port="0"/>
+        <edge from-layer="6" from-port="7" to-layer="11" to-port="1"/>
+        <edge from-layer="6" from-port="8" to-layer="9" to-port="0"/>
+        <edge from-layer="6" from-port="8" to-layer="9" to-port="1"/>
+        <edge from-layer="7" from-port="2" to-layer="8" to-port="0"/>
+        <edge from-layer="9" from-port="2" to-layer="10" to-port="0"/>
+        <edge from-layer="11" from-port="2" to-layer="12" to-port="0"/>
     </edges>
 </net>
 )V0G0N";
     std::string modelV5 = R"V0G0N(
-<net name="Network" version="5">
+<net name="Network" version="7">
     <layers>
-        <layer id="0" name="in1" type="Input" >
-            <data precision="I32"/>
+        <layer id="0" name="in1" precision="FP32" type="Input" >
             <output>
-                <port id="0">
+                <port id="0" precision="FP32">
                     <dim>1</dim>
                     <dim>15130</dim>
                     <dim>4</dim>
                 </port>
             </output>
         </layer>
-        <layer id="1" name="in2" type="Input" >
-            <data precision="I32"/>
+        <layer id="1" name="in2" precision="FP32" type="Input" >
             <output>
-                <port id="0">
+                <port id="0" precision="FP32">
                     <dim>1</dim>
                     <dim>80</dim>
                     <dim>15130</dim>
@@ -134,25 +219,37 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
             </output>
         </layer>
         <layer id="2" name="max_output_boxes_per_class" precision="I64" type="Const">
-            <data offset="0" size="8"/>
             <output>
-                <port id="0"/>
+                <port id="0" precision="I64">
+                    <dim>1</dim>
+                </port>
             </output>
+            <blobs>
+                <custom offset="0" precision="I64" size="8"/>
+            </blobs>
         </layer>
         <layer id="3" name="iou_threshold" precision="FP32" type="Const">
-            <data offset="8" size="4"/>
             <output>
-                <port id="0"/>
+                <port id="0" precision="FP32">
+                    <dim>1</dim>
+                </port>
             </output>
+            <blobs>
+                <custom offset="8" precision="FP32" size="4"/>
+            </blobs>
         </layer>
         <layer id="4" name="score_threshold" precision="FP32" type="Const">
-            <data offset="12" size="4"/>
             <output>
-                <port id="0"/>
+                <port id="0" precision="FP32">
+                    <dim>1</dim>
+                </port>
             </output>
+            <blobs>
+                <custom offset="12" precision="FP32" size="4"/>
+            </blobs>
         </layer>
-        <layer id="5" name="nms" type="NonMaxSuppression" precision="I32">
-            <data box_encoding="corner" sort_result_descending="0"/>
+        <layer id="5" name="nms" type="NonMaxSuppression" precision="I64">
+            <data center_point_box="false" output_type="I64" sort_result_descending="false"/>
             <input>
                 <port id="0">
                     <dim>1</dim>
@@ -164,31 +261,80 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
                     <dim>80</dim>
                     <dim>15130</dim>
                 </port>
-                <port id="2"/>
-                <port id="3"/>
-                <port id="4"/>
+                <port id="2">
+                    <dim>1</dim>
+                </port>
+                <port id="3">
+                    <dim>1</dim>
+                </port>
+                <port id="4">
+                    <dim>1</dim>
+                </port>
             </input>
             <output>
-                <port id="5">
-                    <dim>15130</dim>
+                <port id="5" precision="I64">
+                    <dim>16000</dim>
                     <dim>3</dim>
                 </port>
+                <port id="6" precision="FP32">
+                    <dim>16000</dim>
+                    <dim>3</dim>
+                </port>
+                <port id="7" precision="I64">
+                    <dim>1</dim>
+                </port>
             </output>
         </layer>
-        <layer id="6" name="mul" type="Multiply" precision="I32">
+        <layer id="6" name="mul" type="Eltwise" precision="I64">
+            <data operation="prod"/>
             <input>
                 <port id="0">
-                    <dim>15130</dim>
+                    <dim>16000</dim>
                     <dim>3</dim>
                 </port>
                 <port id="1">
-                    <dim>15130</dim>
+                    <dim>16000</dim>
                     <dim>3</dim>
                 </port>
             </input>
             <output>
-                <port id="2">
-                    <dim>15130</dim>
+                <port id="2" precision="I64">
+                    <dim>16000</dim>
+                    <dim>3</dim>
+                </port>
+            </output>
+        </layer>
+        <layer id="7" name="mul2" type="Eltwise" precision="I64">
+            <data operation="prod"/>
+            <input>
+                <port id="0">
+                    <dim>1</dim>
+                </port>
+                <port id="1">
+                    <dim>1</dim>
+                </port>
+            </input>
+            <output>
+                <port id="2" precision="I64">
+                    <dim>1</dim>
+                </port>
+            </output>
+        </layer>
+        <layer id="8" name="mul3" type="Eltwise" precision="FP32">
+            <data operation="prod"/>
+            <input>
+                <port id="0">
+                    <dim>16000</dim>
+                    <dim>3</dim>
+                </port>
+                <port id="1">
+                    <dim>16000</dim>
+                    <dim>3</dim>
+                </port>
+            </input>
+            <output>
+                <port id="2" precision="FP32">
+                    <dim>16000</dim>
                     <dim>3</dim>
                 </port>
             </output>
@@ -202,18 +348,167 @@ TEST_F(NGraphReaderTests, ReadNonMaxSuppression) {
         <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
         <edge from-layer="5" from-port="5" to-layer="6" to-port="0"/>
         <edge from-layer="5" from-port="5" to-layer="6" to-port="1"/>
+        <edge from-layer="5" from-port="6" to-layer="8" to-port="0"/>
+        <edge from-layer="5" from-port="6" to-layer="8" to-port="1"/>
+        <edge from-layer="5" from-port="7" to-layer="7" to-port="0"/>
+        <edge from-layer="5" from-port="7" to-layer="7" to-port="1"/>
     </edges>
 </net>
 )V0G0N";
 
-    compareIRs(model, modelV5, 16, [](Blob::Ptr& weights) {
+    compareIRs(model, modelV5, 20, [](Blob::Ptr& weights) {
         auto * i64w = weights->buffer().as<int64_t*>();
         i64w[0] = 200;
 
         auto * fp32w = weights->buffer().as<float*>();
         fp32w[2] = 0.5;
         fp32w[3] = 0.05;
+        fp32w[4] = 0.0;
     });
 }
 
- */
+TEST_F(NGraphReaderTests, ReadNonMaxSuppression4) {
+   std::string model = R"V0G0N(
+<net name="Network" version="10">
+   <layers>
+       <layer id="0" name="in1" type="Parameter" version="opset1">
+           <data element_type="f32" shape="1,15130,4"/>
+           <output>
+               <port id="0" precision="FP32">
+                   <dim>1</dim>
+                   <dim>15130</dim>
+                   <dim>4</dim>
+               </port>
+           </output>
+       </layer>
+       <layer id="1" name="in2" type="Parameter" version="opset1">
+           <data element_type="f32" shape="1,80,15130"/>
+           <output>
+               <port id="0" precision="FP32">
+                   <dim>1</dim>
+                   <dim>80</dim>
+                   <dim>15130</dim>
+               </port>
+           </output>
+       </layer>
+       <layer id="2" name="max_output_boxes_per_class" precision="I64" type="Const" version="opset1">
+           <data offset="0" size="8"/>
+           <output>
+               <port id="0" precision="I64"/>
+           </output>
+       </layer>
+       <layer id="3" name="iou_threshold" precision="FP32" type="Const" version="opset1">
+           <data offset="8" size="4"/>
+           <output>
+               <port id="0" precision="FP32"/>
+           </output>
+       </layer>
+       <layer id="4" name="score_threshold" precision="FP32" type="Const" version="opset1">
+           <data offset="12" size="4"/>
+           <output>
+               <port id="0" precision="FP32"/>
+           </output>
+       </layer>
+       <layer id="5" name="nms" type="NonMaxSuppression" version="opset4">
+           <data box_encoding="corner" output_type="i32" sort_result_descending="0"/>
+           <input>
+               <port id="0" precision="FP32">
+                   <dim>1</dim>
+                   <dim>15130</dim>
+                   <dim>4</dim>
+               </port>
+               <port id="1" precision="FP32">
+                   <dim>1</dim>
+                   <dim>80</dim>
+                   <dim>15130</dim>
+               </port>
+               <port id="2" precision="I64"/>
+               <port id="3" precision="FP32"/>
+               <port id="4" precision="FP32"/>
+           </input>
+           <output>
+               <port id="5" precision="I32">
+                   <dim>16000</dim>
+                   <dim>3</dim>
+               </port>
+           </output>
+       </layer>
+       <layer id="6" name="mul" type="Multiply" version="opset1">
+           <input>
+               <port id="0" precision="I32">
+                   <dim>16000</dim>
+                   <dim>3</dim>
+               </port>
+               <port id="1" precision="I32">
+                   <dim>16000</dim>
+                   <dim>3</dim>
+               </port>
+           </input>
+           <output>
+               <port id="2" precision="I32">
+                   <dim>16000</dim>
+                   <dim>3</dim>
+               </port>
+           </output>
+       </layer>
+       <layer id="7" name="output" type="Result" version="opset1">
+           <input>
+               <port id="0" precision="I32">
+                   <dim>16000</dim>
+                   <dim>3</dim>
+               </port>
+           </input>
+       </layer>
+   </layers>
+   <edges>
+       <edge from-layer="0" from-port="0" to-layer="5" to-port="0"/>
+       <edge from-layer="1" from-port="0" to-layer="5" to-port="1"/>
+       <edge from-layer="2" from-port="0" to-layer="5" to-port="2"/>
+       <edge from-layer="3" from-port="0" to-layer="5" to-port="3"/>
+       <edge from-layer="4" from-port="0" to-layer="5" to-port="4"/>
+       <edge from-layer="5" from-port="5" to-layer="6" to-port="0"/>
+       <edge from-layer="5" from-port="5" to-layer="6" to-port="1"/>
+       <edge from-layer="6" from-port="2" to-layer="7" to-port="0"/>
+   </edges>
+</net>
+)V0G0N";
+
+    constexpr size_t weightsSize = 16;
+
+    Blob::Ptr weights;
+
+    weights = make_shared_blob<uint8_t>(TensorDesc(Precision::U8, {weightsSize}, Layout::C));
+    weights->allocate();
+    CommonTestUtils::fill_data(weights->buffer().as<float *>(), weights->size() / sizeof(float));
+
+    auto * i64w = weights->buffer().as<int64_t*>();
+    i64w[0] = 200;
+
+    auto * fp32w = weights->buffer().as<float*>();
+    fp32w[2] = 0.5;
+    fp32w[3] = 0.05;
+
+    Core ie;
+
+    auto ngraphImpl = ie.ReadNetwork(model, weights);
+    auto graph = ngraph::clone_function(*ngraphImpl.getFunction());
+
+    ::ngraph::pass::Manager manager;
+    manager.register_pass<::ngraph::pass::InitNodeInfo>();
+    manager.register_pass<::ngraph::pass::CommonOptimizations>();
+    manager.run_passes(graph);
+
+    auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 15130, 4});
+    auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 80, 15130});
+    auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {200});
+    auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.5});
+    auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.05});
+    auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                           opset5::NonMaxSuppression::BoxEncodingType::CORNER, true, ngraph::element::i32);
+
+    auto mul = std::make_shared<opset5::Multiply>(nms, nms);
+    auto graph_ref = std::make_shared<Function>(NodeVector{mul}, ParameterVector{boxes, scores});
+
+    auto res = compare_functions(graph, graph_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_nms3_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_nms3_test.cpp
deleted file mode 100644 (file)
index ae4ee04..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2020 Intel Corporation
-// SPDX-License-Identifier: Apache-2.0
-//
-
-#include <gtest/gtest.h>
-
-#include <string>
-#include <memory>
-#include <queue>
-
-#include <ngraph/function.hpp>
-#include <ngraph/opsets/opset1.hpp>
-#include <ngraph/opsets/opset2.hpp>
-#include <ngraph/opsets/opset3.hpp>
-#include <transformations/op_conversions/convert_nms3.hpp>
-#include <transformations/init_node_info.hpp>
-#include <transformations/utils/utils.hpp>
-
-#include "common_test_utils/ngraph_test_utils.hpp"
-
-using namespace testing;
-using namespace ngraph;
-
-TEST(TransformationTests, ConvertNMS3I32Output) {
-    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
-    {
-        auto boxes = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1000, 4});
-        auto scores = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1, 1000});
-        auto max_output_boxes_per_class = opset3::Constant::create(element::i64, Shape{}, {10});
-        auto iou_threshold = opset3::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset3::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<opset1::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
-                iou_threshold, score_threshold, opset1::NonMaxSuppression::BoxEncodingType::CORNER, true);
-        nms->set_friendly_name("nms");
-
-        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-
-        pass::InitNodeInfo().run_on_function(f);
-        pass::ConvertNMS1ToNMS3().run_on_function(f);
-        ASSERT_NO_THROW(check_rt_info(f));
-    }
-
-    {
-        auto boxes = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1000, 4});
-        auto scores = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1, 1000});
-        auto max_output_boxes_per_class = opset3::Constant::create(element::i64, Shape{}, {10});
-        auto iou_threshold = opset3::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset3::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<opset3::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
-                iou_threshold, score_threshold, opset3::NonMaxSuppression::BoxEncodingType::CORNER, true);
-        nms->set_friendly_name("nms");
-
-        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-    }
-
-    auto res = compare_functions(f, f_ref);
-    ASSERT_TRUE(res.first) << res.second;
-
-    auto result_node_of_converted_f = f->get_output_op(0);
-    auto nms_node = result_node_of_converted_f->input(0).get_source_output().get_node_shared_ptr();
-    ASSERT_TRUE(nms_node->get_friendly_name() == "nms") << "Transformation ConvertNMS1ToNMS3 should keep output names.\n";
-}
diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_nms4_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_nms4_test.cpp
deleted file mode 100644 (file)
index a5eb5b5..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2020 Intel Corporation
-// SPDX-License-Identifier: Apache-2.0
-//
-
-#include <gtest/gtest.h>
-
-#include <string>
-#include <memory>
-
-#include <ngraph/function.hpp>
-#include <ngraph/opsets/opset4.hpp>
-#include <legacy/ngraph_ops/nms_ie.hpp>
-#include <legacy/transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp>
-#include <transformations/init_node_info.hpp>
-#include <transformations/utils/utils.hpp>
-#include <ngraph/pass/manager.hpp>
-
-#include "common_test_utils/ngraph_test_utils.hpp"
-
-using namespace testing;
-using namespace ngraph;
-
-TEST(TransformationTests, ConvertNMS4ToNMSIEStatic) {
-    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
-    {
-        auto boxes = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1000, 4});
-        auto scores = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 1000});
-        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{}, {10});
-        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<opset4::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
-                                                               opset4::NonMaxSuppression::BoxEncodingType::CORNER, true);
-
-        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-
-        const auto &orig_shape = f->get_output_partial_shape(0);
-        pass::Manager manager;
-        manager.register_pass<pass::InitNodeInfo>();
-        manager.register_pass<pass::ConvertNMS4ToLegacyMatcher>();
-        manager.run_passes(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";
-    }
-
-    {
-        auto boxes = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1000, 4});
-        auto scores = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 1000});
-        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{1}, {10});
-        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<op::NonMaxSuppressionIE2>(boxes, scores, max_output_boxes_per_class,
-                                                              std::make_shared<opset4::Unsqueeze>(iou_threshold,
-                                                                                                  opset4::Constant::create(element::i64, Shape{1}, {0})),
-                                                              std::make_shared<opset4::Unsqueeze>(score_threshold,
-                                                                                                  opset4::Constant::create(element::i64, Shape{1}, {0})),
-                                                              0, true);
-        nms->set_friendly_name("nms");
-
-        f_ref = std::make_shared<Function>(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);
-    ASSERT_TRUE(res.first) << res.second;
-}
-
-TEST(TransformationTests, ConvertNMS4ToNMSIEDynamic1) {
-    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
-    {
-        auto boxes = std::make_shared<opset4::Parameter>(element::f32, PartialShape::dynamic());
-        auto scores = std::make_shared<opset4::Parameter>(element::f32, PartialShape::dynamic());
-        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{}, {10});
-        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<opset4::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
-                                                               opset4::NonMaxSuppression::BoxEncodingType::CORNER, true, element::i64);
-
-        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-
-        pass::Manager manager;
-        manager.register_pass<pass::InitNodeInfo>();
-        manager.register_pass<pass::ConvertNMS4ToLegacyMatcher>();
-        manager.run_passes(f);
-        f->validate_nodes_and_infer_types();
-        ASSERT_NO_THROW(check_rt_info(f));
-    }
-
-    {
-        auto boxes = std::make_shared<opset4::Parameter>(element::f32, PartialShape::dynamic());
-        auto scores = std::make_shared<opset4::Parameter>(element::f32, PartialShape::dynamic());
-        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{1}, {10});
-        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<op::NonMaxSuppressionIE2>(boxes, scores, max_output_boxes_per_class,
-                                                              std::make_shared<opset4::Unsqueeze>(iou_threshold,
-                                                                                                  opset4::Constant::create(element::i64, Shape{1}, {0})),
-                                                              std::make_shared<opset4::Unsqueeze>(score_threshold,
-                                                                                                  opset4::Constant::create(element::i64, Shape{1}, {0})),
-                                                              0, true);
-
-        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-    }
-
-    auto res = compare_functions(f, f_ref);
-    ASSERT_TRUE(res.first) << res.second;
-}
-
-// LPT to nGraph migration: temporary disabling unexpected not reproduced fails on CI:
-// https://openvino-ci.intel.com/job/private-ci/job/ie/job/build-linux-ubuntu18_i386/478/
-TEST(TransformationTests, DISABLED_ConvertNMS4ToNMSIEDynamic2) {
-    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
-    {
-        auto boxes = std::make_shared<opset4::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
-        auto scores = std::make_shared<opset4::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
-        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{}, {10});
-        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<opset4::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
-                                                               opset4::NonMaxSuppression::BoxEncodingType::CORNER, true, element::i32);
-
-        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-
-        pass::Manager manager;
-        manager.register_pass<pass::InitNodeInfo>();
-        manager.register_pass<pass::ConvertNMS4ToLegacyMatcher>();
-        manager.run_passes(f);
-
-        f->validate_nodes_and_infer_types();
-        ASSERT_NO_THROW(check_rt_info(f));
-    }
-
-    {
-        auto boxes = std::make_shared<opset4::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
-        auto scores = std::make_shared<opset4::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
-        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{1}, {10});
-        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
-        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
-        auto nms = std::make_shared<op::NonMaxSuppressionIE2>(boxes, scores, max_output_boxes_per_class,
-                                                              std::make_shared<opset4::Unsqueeze>(iou_threshold,
-                                                                                                  opset4::Constant::create(element::i64, Shape{1}, {0})),
-                                                              std::make_shared<opset4::Unsqueeze>(score_threshold,
-                                                                                                  opset4::Constant::create(element::i64, Shape{1}, {0})),
-                                                              0, true);
-
-        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
-    }
-
-    auto res = compare_functions(f, f_ref);
-    ASSERT_TRUE(res.first) << res.second;
-}
diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_nms5_test.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_nms5_test.cpp
new file mode 100644 (file)
index 0000000..6ba4739
--- /dev/null
@@ -0,0 +1,763 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <memory>
+
+#include <ngraph/function.hpp>
+#include <ngraph/opsets/opset5.hpp>
+#include <legacy/ngraph_ops/nms_ie.hpp>
+#include <legacy/transformations/convert_opset1_to_legacy/convert_nms_5_to_legacy.hpp>
+#include <transformations/init_node_info.hpp>
+#include <transformations/utils/utils.hpp>
+#include <ngraph/pass/manager.hpp>
+
+#include "common_test_utils/ngraph_test_utils.hpp"
+
+using namespace testing;
+using namespace ngraph;
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEStaticSixInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto soft_nms_sigma = ngraph::opset5::Constant::create(element::f32, Shape{}, {0.25});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               soft_nms_sigma, opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+        ASSERT_TRUE(f->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto soft_nms_sigma = ngraph::opset5::Constant::create(element::f32, Shape{}, {0.25});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto new_soft_nms_sigma = std::make_shared<opset5::Reshape>(soft_nms_sigma,
+                                                                    opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                             one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              new_soft_nms_sigma, 0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+        ASSERT_TRUE(f_ref->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEStaticFiveInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+        ASSERT_TRUE(f->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+        ASSERT_TRUE(f_ref->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEStaticFourInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+        ASSERT_TRUE(f->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+        ASSERT_TRUE(f_ref->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEStaticThreeInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+        ASSERT_TRUE(f->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+        ASSERT_TRUE(f_ref->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEStaticTwoInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+        ASSERT_TRUE(f->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i32, Shape{}, {0});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+        ASSERT_TRUE(f_ref->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic1SixInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto soft_nms_sigma = ngraph::opset5::Constant::create(element::f32, Shape{}, {0.25});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               soft_nms_sigma, opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        const auto &orig_selected_indices_shape = f->get_output_partial_shape(0);
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto soft_nms_sigma = ngraph::opset5::Constant::create(element::f32, Shape{}, {0.25});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto new_soft_nms_sigma = std::make_shared<opset5::Reshape>(soft_nms_sigma,
+                                                                    opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                             one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              new_soft_nms_sigma, 0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic1FiveInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        const auto &orig_selected_indices_shape = f->get_output_partial_shape(0);
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic1FourInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        const auto &orig_selected_indices_shape = f->get_output_partial_shape(0);
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic1ThreeInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        const auto &orig_selected_indices_shape = f->get_output_partial_shape(0);
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic1TwoInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        const auto &orig_selected_indices_shape = f->get_output_partial_shape(0);
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape::dynamic());
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i32, Shape{}, {0});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic2SixInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto soft_nms_sigma = ngraph::opset5::Constant::create(element::f32, Shape{}, {0.25});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               soft_nms_sigma, opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto soft_nms_sigma = ngraph::opset5::Constant::create(element::f32, Shape{}, {0.25});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto new_soft_nms_sigma = std::make_shared<opset5::Reshape>(soft_nms_sigma,
+                                                                    opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                             one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              new_soft_nms_sigma, 0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic2FiveInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic2FourInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic2ThreeInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS5ToNMSIEDynamic2TwoInputs) {
+    std::shared_ptr<Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager manager;
+        manager.register_pass<pass::InitNodeInfo>();
+        manager.register_pass<pass::ConvertNMS5ToLegacyMatcher>();
+        manager.run_passes(f);
+        f->validate_nodes_and_infer_types();
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, PartialShape{DYN, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i32, Shape{}, {0});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+
+        auto one_dim_shape = Shape{1};
+        auto new_max_per_class = std::make_shared<opset5::Reshape>(max_output_boxes_per_class,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_iou_threshold = std::make_shared<opset5::Reshape>(iou_threshold,
+                                                                   opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                            one_dim_shape), true);
+        auto new_score_threshold = std::make_shared<opset5::Reshape>(score_threshold,
+                                                                     opset5::Constant::create(ngraph::element::i64, one_dim_shape,
+                                                                                              one_dim_shape), true);
+        auto nms = std::make_shared<op::NonMaxSuppressionIE3>(boxes, scores, new_max_per_class, new_iou_threshold, new_score_threshold,
+                                                              0, true);
+        nms->set_friendly_name("nms");
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
diff --git a/inference-engine/tests/functional/inference_engine/transformations/convert_previous_nms_to_nms_5.cpp b/inference-engine/tests/functional/inference_engine/transformations/convert_previous_nms_to_nms_5.cpp
new file mode 100644 (file)
index 0000000..a1120f4
--- /dev/null
@@ -0,0 +1,228 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <memory>
+#include <queue>
+
+#include <ngraph/function.hpp>
+#include <ngraph/opsets/opset1.hpp>
+#include <ngraph/opsets/opset3.hpp>
+#include <ngraph/opsets/opset4.hpp>
+#include <ngraph/opsets/opset5.hpp>
+#include <transformations/op_conversions/convert_previous_nms_to_nms_5.hpp>
+#include <transformations/init_node_info.hpp>
+#include <transformations/utils/utils.hpp>
+#include <ngraph/pass/manager.hpp>
+
+#include "common_test_utils/ngraph_test_utils.hpp"
+
+using namespace testing;
+using namespace ngraph;
+
+TEST(TransformationTests, ConvertNMS4FiveInputsToNMS5) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset4::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset4::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset4::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset4::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               opset4::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager m;
+        m.register_pass<pass::InitNodeInfo>();
+        m.register_pass<pass::ConvertNMS4ToNMS5>();
+        m.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS4TwoInputsToNMS5) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset4::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto nms = std::make_shared<opset4::NonMaxSuppression>(boxes, scores, opset4::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager m;
+        m.register_pass<pass::InitNodeInfo>();
+        m.register_pass<pass::ConvertNMS4ToNMS5>();
+        m.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {0});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS3FiveInputsToNMS5) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset3::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset3::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset3::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset3::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               opset3::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager m;
+        m.register_pass<pass::InitNodeInfo>();
+        m.register_pass<pass::ConvertNMS3ToNMS5>();
+        m.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS3TwoInputsToNMS5) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset3::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto nms = std::make_shared<opset3::NonMaxSuppression>(boxes, scores, opset3::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager m;
+        m.register_pass<pass::InitNodeInfo>();
+        m.register_pass<pass::ConvertNMS3ToNMS5>();
+        m.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {0});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS1FiveInputsToNMS5) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset1::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset1::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset1::Constant::create(element::i64, Shape{}, {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<opset1::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
+                                                               opset1::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager m;
+        m.register_pass<pass::InitNodeInfo>();
+        m.register_pass<pass::ConvertNMS1ToNMS5>();
+        m.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {10});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.75});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.7});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
+
+TEST(TransformationTests, ConvertNMS1TwoInputsToNMS5) {
+    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
+    {
+        auto boxes = std::make_shared<opset1::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset1::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto nms = std::make_shared<opset1::NonMaxSuppression>(boxes, scores, opset1::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+
+        pass::Manager m;
+        m.register_pass<pass::InitNodeInfo>();
+        m.register_pass<pass::ConvertNMS1ToNMS5>();
+        m.run_passes(f);
+        ASSERT_NO_THROW(check_rt_info(f));
+    }
+
+    {
+        auto boxes = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1000, 4});
+        auto scores = std::make_shared<opset5::Parameter>(element::f32, Shape{1, 1, 1000});
+        auto max_output_boxes_per_class = opset5::Constant::create(element::i64, Shape{}, {0});
+        auto iou_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto score_threshold = opset5::Constant::create(element::f32, Shape{}, {0.0f});
+        auto nms = std::make_shared<opset5::NonMaxSuppression>(boxes, scores, max_output_boxes_per_class,  iou_threshold, score_threshold,
+                                                               opset5::NonMaxSuppression::BoxEncodingType::CORNER, true);
+
+        f_ref = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
+    }
+
+    auto res = compare_functions(f, f_ref);
+    ASSERT_TRUE(res.first) << res.second;
+}
index c4cc697..b6a9361 100644 (file)
@@ -374,15 +374,16 @@ namespace ngraph
                 }
                 using Node::set_output_type;
 
+                int64_t max_boxes_output_from_input() const;
+                float iou_threshold_from_input() const;
+                float score_threshold_from_input() const;
+                float soft_nms_sigma_from_input() const;
+
             protected:
                 BoxEncodingType m_box_encoding = BoxEncodingType::CORNER;
                 bool m_sort_result_descending = true;
                 ngraph::element::Type m_output_type = ngraph::element::i64;
                 void validate();
-                int64_t max_boxes_output_from_input() const;
-                float iou_threshold_from_input() const;
-                float score_threshold_from_input() const;
-                float soft_nms_sigma_from_input() const;
             };
         } // namespace v5
     }     // namespace op
@@ -443,4 +444,4 @@ namespace ngraph
             "AttributeAdapter<op::v5::NonMaxSuppression::BoxEncodingType>", 1};
         const DiscreteTypeInfo& get_type_info() const override { return type_info; }
     };
-} // namespace ngraph
\ No newline at end of file
+} // namespace ngraph
diff --git a/ngraph/core/reference/include/ngraph/runtime/reference/non_max_suppression.hpp b/ngraph/core/reference/include/ngraph/runtime/reference/non_max_suppression.hpp
new file mode 100644 (file)
index 0000000..db343ab
--- /dev/null
@@ -0,0 +1,81 @@
+//*****************************************************************************
+// Copyright 2017-2020 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//*****************************************************************************
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <map>
+#include <ngraph/runtime/host_tensor.hpp>
+#include <vector>
+#include "ngraph/node.hpp"
+#include "ngraph/op/util/op_types.hpp"
+#include "ngraph/ops.hpp"
+#include "ngraph/shape_util.hpp"
+
+namespace ngraph
+{
+    namespace runtime
+    {
+        namespace reference
+        {
+            struct InfoForNMS5
+            {
+                int64_t max_output_boxes_per_class;
+                float iou_threshold;
+                float score_threshold;
+                float soft_nms_sigma;
+                Shape out_shape;
+                Shape boxes_shape;
+                Shape scores_shape;
+                std::vector<float> boxes_data;
+                std::vector<float> scores_data;
+                size_t out_shape_size;
+                bool sort_result_descending;
+                ngraph::element::Type output_type;
+            };
+
+            InfoForNMS5 get_info_for_nms5_evaluation(const op::v5::NonMaxSuppression* nms5,
+                                                     const HostTensorVector& inputs);
+
+            void non_max_suppression(const float* boxes_data,
+                                     const Shape& boxes_data_shape,
+                                     const float* scores_data,
+                                     const Shape& scores_data_shape,
+                                     int64_t max_output_boxes_per_class,
+                                     float iou_threshold,
+                                     float score_threshold,
+                                     float soft_nms_sigma,
+                                     int64_t* selected_indices,
+                                     const Shape& selected_indices_shape,
+                                     float* selected_scores,
+                                     const Shape& selected_scores_shape,
+                                     int64_t* valid_outputs,
+                                     const bool sort_result_descending);
+
+            void nms5_postprocessing(const HostTensorVector& outputs,
+                                     const ngraph::element::Type output_type,
+                                     const std::vector<int64_t>& selected_indices,
+                                     const std::vector<float>& selected_scores,
+                                     int64_t valid_outputs,
+                                     const ngraph::element::Type selected_scores_type);
+        }
+    }
+}
diff --git a/ngraph/core/reference/src/runtime/reference/non_max_suppression.cpp b/ngraph/core/reference/src/runtime/reference/non_max_suppression.cpp
new file mode 100644 (file)
index 0000000..44fafd7
--- /dev/null
@@ -0,0 +1,574 @@
+//*****************************************************************************
+// Copyright 2017-2020 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//*****************************************************************************
+
+#include "ngraph/op/non_max_suppression.hpp"
+#include <algorithm>
+#include <cmath>
+#include <queue>
+#include <vector>
+#include "ngraph/runtime/reference/non_max_suppression.hpp"
+#include "ngraph/shape.hpp"
+
+using namespace ngraph;
+using namespace ngraph::runtime::reference;
+
+struct Rectangle
+{
+    Rectangle(float y_left, float x_left, float y_right, float x_right)
+        : y1{y_left}
+        , x1{x_left}
+        , y2{y_right}
+        , x2{x_right}
+    {
+    }
+
+    Rectangle() = default;
+
+    float y1 = 0.0f;
+    float x1 = 0.0f;
+    float y2 = 0.f;
+    float x2 = 0.0f;
+};
+
+static float intersectionOverUnion(const Rectangle& boxI, const Rectangle& boxJ)
+{
+    float areaI = (boxI.y2 - boxI.y1) * (boxI.x2 - boxI.x1);
+    float areaJ = (boxJ.y2 - boxJ.y1) * (boxJ.x2 - boxJ.x1);
+
+    if (areaI <= 0.0f || areaJ <= 0.0f)
+    {
+        return 0.0f;
+    }
+
+    float intersection_ymin = std::max(boxI.y1, boxJ.y1);
+    float intersection_xmin = std::max(boxI.x1, boxJ.x1);
+    float intersection_ymax = std::min(boxI.y2, boxJ.y2);
+    float intersection_xmax = std::min(boxI.x2, boxJ.x2);
+
+    float intersection_area = std::max(intersection_ymax - intersection_ymin, 0.0f) *
+                              std::max(intersection_xmax - intersection_xmin, 0.0f);
+
+    return intersection_area / (areaI + areaJ - intersection_area);
+}
+
+struct SelectedIndex
+{
+    SelectedIndex(int64_t batch_idx, int64_t class_idx, int64_t box_idx)
+        : batch_index(batch_idx)
+        , class_index(class_idx)
+        , box_index(box_idx)
+    {
+    }
+
+    SelectedIndex() = default;
+
+    int64_t batch_index = 0;
+    int64_t class_index = 0;
+    int64_t box_index = 0;
+};
+
+struct SelectedScore
+{
+    SelectedScore(float batch_idx, float class_idx, float score)
+        : batch_index{batch_idx}
+        , class_index{class_idx}
+        , box_score{score}
+    {
+    }
+
+    SelectedScore() = default;
+
+    float batch_index = 0.0f;
+    float class_index = 0.0f;
+    float box_score = 0.0f;
+};
+
+struct BoxInfo
+{
+    BoxInfo(const Rectangle& r,
+            int64_t idx,
+            float sc,
+            int64_t suppress_idx,
+            int64_t batch_idx,
+            int64_t class_idx)
+        : box{r}
+        , index{idx}
+        , suppress_begin_index{suppress_idx}
+        , batch_index{batch_idx}
+        , class_index{class_idx}
+        , score{sc}
+    {
+    }
+
+    BoxInfo() = default;
+
+    inline bool operator<(const BoxInfo& rhs) const
+    {
+        return score < rhs.score || (score == rhs.score && index > rhs.index);
+    }
+
+    Rectangle box;
+    int64_t index = 0;
+    int64_t suppress_begin_index = 0;
+    int64_t batch_index = 0;
+    int64_t class_index = 0;
+    float score = 0.0f;
+};
+
+using V5BoxEncoding = op::v5::NonMaxSuppression::BoxEncodingType;
+
+namespace
+{
+    constexpr size_t boxes_port = 0;
+    constexpr size_t scores_port = 1;
+    constexpr size_t max_output_boxes_port = 2;
+    constexpr size_t iou_threshold_port = 3;
+    constexpr size_t score_threshold_port = 4;
+    constexpr size_t soft_nms_sigma_port = 5;
+
+    PartialShape
+        infer_selected_indices_shape(const std::vector<std::shared_ptr<HostTensor>>& inputs,
+                                     int64_t max_output_boxes_per_class)
+    {
+        const auto boxes_ps = inputs[boxes_port]->get_partial_shape();
+        const auto scores_ps = inputs[scores_port]->get_partial_shape();
+
+        // NonMaxSuppression produces triplets
+        // that have the following format: [batch_index, class_index, box_index]
+        PartialShape result = {Dimension::dynamic(), 3};
+
+        if (boxes_ps.rank().is_static() && scores_ps.rank().is_static())
+        {
+            const auto num_boxes_boxes = boxes_ps[1];
+            if (num_boxes_boxes.is_static() && scores_ps[0].is_static() && scores_ps[1].is_static())
+            {
+                const auto num_boxes = num_boxes_boxes.get_length();
+                const auto num_classes = scores_ps[1].get_length();
+
+                result[0] = std::min(num_boxes, max_output_boxes_per_class) * num_classes *
+                            scores_ps[0].get_length();
+            }
+        }
+
+        return result;
+    }
+
+    void normalize_corner(float* boxes, const Shape& boxes_shape)
+    {
+        size_t total_num_of_boxes = shape_size(boxes_shape) / 4;
+        for (size_t i = 0; i < total_num_of_boxes; ++i)
+        {
+            float* current_box = boxes + 4 * i;
+
+            float y1 = current_box[0];
+            float x1 = current_box[1];
+            float y2 = current_box[2];
+            float x2 = current_box[3];
+
+            float ymin = std::min(y1, y2);
+            float ymax = std::max(y1, y2);
+            float xmin = std::min(x1, x2);
+            float xmax = std::max(x1, x2);
+
+            current_box[0] = ymin;
+            current_box[1] = xmin;
+            current_box[2] = ymax;
+            current_box[3] = xmax;
+        }
+    }
+
+    void normalize_center(float* boxes, const Shape& boxes_shape)
+    {
+        size_t total_num_of_boxes = shape_size(boxes_shape) / 4;
+        for (size_t i = 0; i < total_num_of_boxes; ++i)
+        {
+            float* current_box = boxes + 4 * i;
+
+            float x_center = current_box[0];
+            float y_center = current_box[1];
+            float width = current_box[2];
+            float height = current_box[3];
+
+            float y1 = y_center - height / 2.0;
+            float x1 = x_center - width / 2.0;
+            float y2 = y_center + height / 2.0;
+            float x2 = x_center + width / 2.0;
+
+            current_box[0] = y1;
+            current_box[1] = x1;
+            current_box[2] = y2;
+            current_box[3] = x2;
+        }
+    }
+
+    void normalize_box_encoding(float* boxes,
+                                const Shape& boxes_shape,
+                                const V5BoxEncoding box_encoding)
+    {
+        if (box_encoding == V5BoxEncoding::CORNER)
+        {
+            normalize_corner(boxes, boxes_shape);
+        }
+        else
+        {
+            normalize_center(boxes, boxes_shape);
+        }
+    }
+
+    std::vector<float> get_floats(const std::shared_ptr<HostTensor>& input, const Shape& shape)
+    {
+        size_t input_size = shape_size(shape);
+        std::vector<float> result(input_size);
+
+        switch (input->get_element_type())
+        {
+        case element::Type_t::bf16:
+        {
+            bfloat16* p = input->get_data_ptr<bfloat16>();
+            for (size_t i = 0; i < input_size; ++i)
+            {
+                result[i] = float(p[i]);
+            }
+        }
+        break;
+        case element::Type_t::f16:
+        {
+            float16* p = input->get_data_ptr<float16>();
+            for (size_t i = 0; i < input_size; ++i)
+            {
+                result[i] = float(p[i]);
+            }
+        }
+        break;
+        case element::Type_t::f32:
+        {
+            float* p = input->get_data_ptr<float>();
+            memcpy(result.data(), p, input_size * sizeof(float));
+        }
+        break;
+        default: throw std::runtime_error("Unsupported data type in op NonMaxSuppression-5"); break;
+        }
+
+        return result;
+    }
+
+    std::vector<float> prepare_boxes_data(const std::shared_ptr<HostTensor>& boxes,
+                                          const Shape& boxes_shape,
+                                          const V5BoxEncoding box_encoding)
+    {
+        auto result = get_floats(boxes, boxes_shape);
+        normalize_box_encoding(result.data(), boxes_shape, box_encoding);
+        return result;
+    }
+
+    std::vector<float> prepare_scores_data(const std::shared_ptr<HostTensor>& scores,
+                                           const Shape& scores_shape)
+    {
+        auto result = get_floats(scores, scores_shape);
+        return result;
+    }
+}
+
+namespace ngraph
+{
+    namespace runtime
+    {
+        namespace reference
+        {
+            InfoForNMS5 get_info_for_nms5_evaluation(const op::v5::NonMaxSuppression* nms5,
+                                                     const HostTensorVector& inputs)
+            {
+                InfoForNMS5 result;
+
+                result.max_output_boxes_per_class = nms5->max_boxes_output_from_input();
+                result.iou_threshold = nms5->iou_threshold_from_input();
+                result.score_threshold = nms5->score_threshold_from_input();
+                result.soft_nms_sigma = nms5->soft_nms_sigma_from_input();
+
+                auto selected_indices_shape =
+                    infer_selected_indices_shape(inputs, result.max_output_boxes_per_class);
+                result.out_shape = selected_indices_shape.to_shape();
+
+                result.boxes_shape = inputs[boxes_port]->get_shape();
+                result.scores_shape = inputs[scores_port]->get_shape();
+
+                result.boxes_data = prepare_boxes_data(
+                    inputs[boxes_port], result.boxes_shape, nms5->get_box_encoding());
+                result.scores_data = prepare_scores_data(inputs[scores_port], result.scores_shape);
+
+                result.out_shape_size = shape_size(result.out_shape);
+
+                result.sort_result_descending = nms5->get_sort_result_descending();
+
+                result.output_type = nms5->get_output_type();
+
+                return result;
+            }
+
+            void non_max_suppression(const float* boxes_data,
+                                     const Shape& boxes_data_shape,
+                                     const float* scores_data,
+                                     const Shape& scores_data_shape,
+                                     int64_t max_output_boxes_per_class,
+                                     float iou_threshold,
+                                     float score_threshold,
+                                     float soft_nms_sigma,
+                                     int64_t* selected_indices,
+                                     const Shape& selected_indices_shape,
+                                     float* selected_scores,
+                                     const Shape& selected_scores_shape,
+                                     int64_t* valid_outputs,
+                                     const bool sort_result_descending)
+            {
+                float scale = 0.0f;
+                if (soft_nms_sigma > 0.0f)
+                {
+                    scale = -0.5f / soft_nms_sigma;
+                }
+
+                auto func = [iou_threshold, scale](float iou) {
+                    const float weight = std::exp(scale * iou * iou);
+                    return iou <= iou_threshold ? weight : 0.0f;
+                };
+
+                // boxes shape: {num_batches, num_boxes, 4}
+                // scores shape: {num_batches, num_classes, num_boxes}
+                int64_t num_batches = static_cast<int64_t>(scores_data_shape[0]);
+                int64_t num_classes = static_cast<int64_t>(scores_data_shape[1]);
+                int64_t num_boxes = static_cast<int64_t>(boxes_data_shape[1]);
+
+                SelectedIndex* selected_indices_ptr =
+                    reinterpret_cast<SelectedIndex*>(selected_indices);
+                SelectedScore* selected_scores_ptr =
+                    reinterpret_cast<SelectedScore*>(selected_scores);
+
+                size_t boxes_per_class = static_cast<size_t>(max_output_boxes_per_class);
+
+                int64_t num_of_valid_boxes = 0;
+
+                std::vector<BoxInfo> filteredBoxes;
+
+                for (int64_t batch = 0; batch < num_batches; batch++)
+                {
+                    const float* boxesPtr = boxes_data + batch * num_boxes * 4;
+                    Rectangle* r = reinterpret_cast<Rectangle*>(const_cast<float*>(boxesPtr));
+
+                    for (int64_t class_idx = 0; class_idx < num_classes; class_idx++)
+                    {
+                        const float* scoresPtr =
+                            scores_data + batch * (num_classes * num_boxes) + class_idx * num_boxes;
+
+                        std::vector<BoxInfo> candidate_boxes;
+                        candidate_boxes.reserve(num_boxes);
+
+                        for (size_t box_idx = 0; box_idx < num_boxes; box_idx++)
+                        {
+                            if (scoresPtr[box_idx] > score_threshold)
+                            {
+                                candidate_boxes.emplace_back(
+                                    r[box_idx], box_idx, scoresPtr[box_idx], 0, batch, class_idx);
+                            }
+                        }
+
+                        std::priority_queue<BoxInfo> sorted_boxes(std::less<BoxInfo>(),
+                                                                  std::move(candidate_boxes));
+
+                        std::vector<BoxInfo> selected;
+                        // Get the next box with top score, filter by iou_threshold
+
+                        BoxInfo next_candidate;
+                        float original_score;
+
+                        while (!sorted_boxes.empty() && selected.size() < boxes_per_class)
+                        {
+                            next_candidate = sorted_boxes.top();
+                            original_score = next_candidate.score;
+                            sorted_boxes.pop();
+
+                            bool should_hard_suppress = false;
+                            for (int64_t j = static_cast<int64_t>(selected.size()) - 1;
+                                 j >= next_candidate.suppress_begin_index;
+                                 --j)
+                            {
+                                float iou =
+                                    intersectionOverUnion(next_candidate.box, selected[j].box);
+                                next_candidate.score *= func(iou);
+
+                                if (iou >= iou_threshold)
+                                {
+                                    should_hard_suppress = true;
+                                    break;
+                                }
+
+                                if (next_candidate.score <= score_threshold)
+                                {
+                                    break;
+                                }
+                            }
+
+                            next_candidate.suppress_begin_index = selected.size();
+
+                            if (!should_hard_suppress)
+                            {
+                                if (next_candidate.score == original_score)
+                                {
+                                    selected.push_back(next_candidate);
+                                    continue;
+                                }
+                                if (next_candidate.score > score_threshold)
+                                {
+                                    sorted_boxes.push(next_candidate);
+                                }
+                            }
+                        }
+
+                        for (const auto& box_info : selected)
+                        {
+                            filteredBoxes.push_back(box_info);
+                        }
+                    }
+                }
+
+                if (sort_result_descending)
+                {
+                    std::sort(filteredBoxes.begin(),
+                              filteredBoxes.end(),
+                              [](const BoxInfo& l, const BoxInfo& r) { return l.score > r.score; });
+                }
+
+                size_t max_num_of_selected_indices = selected_indices_shape[0];
+                size_t output_size = std::min(filteredBoxes.size(), max_num_of_selected_indices);
+
+                *valid_outputs = output_size;
+
+                size_t idx;
+                for (idx = 0; idx < output_size; idx++)
+                {
+                    const auto& box_info = filteredBoxes[idx];
+                    SelectedIndex selected_index{
+                        box_info.batch_index, box_info.class_index, box_info.index};
+                    SelectedScore selected_score{static_cast<float>(box_info.batch_index),
+                                                 static_cast<float>(box_info.class_index),
+                                                 box_info.score};
+
+                    selected_indices_ptr[idx] = selected_index;
+                    selected_scores_ptr[idx] = selected_score;
+                }
+
+                SelectedIndex selected_index_filler{0, 0, 0};
+                SelectedScore selected_score_filler{0.0f, 0.0f, 0.0f};
+                for (; idx < max_num_of_selected_indices; idx++)
+                {
+                    selected_indices_ptr[idx] = selected_index_filler;
+                    selected_scores_ptr[idx] = selected_score_filler;
+                }
+            }
+
+            void nms5_postprocessing(const HostTensorVector& outputs,
+                                     const ngraph::element::Type output_type,
+                                     const std::vector<int64_t>& selected_indices,
+                                     const std::vector<float>& selected_scores,
+                                     int64_t valid_outputs,
+                                     const ngraph::element::Type selected_scores_type)
+            {
+                outputs[0]->set_element_type(output_type);
+                outputs[0]->set_shape(Shape{static_cast<size_t>(valid_outputs), 3});
+
+                size_t num_of_outputs = outputs.size();
+
+                if (num_of_outputs >= 2)
+                {
+                    outputs[1]->set_element_type(selected_scores_type);
+                    outputs[1]->set_shape(Shape{static_cast<size_t>(valid_outputs), 3});
+                }
+
+                if (num_of_outputs >= 3)
+                {
+                    outputs[2]->set_element_type(output_type);
+                    outputs[2]->set_shape(Shape{1});
+                }
+
+                size_t selected_size = valid_outputs * 3;
+
+                if (output_type == ngraph::element::i64)
+                {
+                    int64_t* indices_ptr = outputs[0]->get_data_ptr<int64_t>();
+                    memcpy(indices_ptr, selected_indices.data(), selected_size * sizeof(int64_t));
+                }
+                else
+                {
+                    int32_t* indices_ptr = outputs[0]->get_data_ptr<int32_t>();
+                    for (size_t i = 0; i < selected_size; ++i)
+                    {
+                        indices_ptr[i] = static_cast<int32_t>(selected_indices[i]);
+                    }
+                }
+
+                if (num_of_outputs < 2)
+                {
+                    return;
+                }
+
+                size_t selected_scores_size = selected_scores.size();
+
+                switch (selected_scores_type)
+                {
+                case element::Type_t::bf16:
+                {
+                    bfloat16* scores_ptr = outputs[1]->get_data_ptr<bfloat16>();
+                    for (size_t i = 0; i < selected_scores_size; ++i)
+                    {
+                        scores_ptr[i] = bfloat16(selected_scores[i]);
+                    }
+                }
+                break;
+                case element::Type_t::f16:
+                {
+                    float16* scores_ptr = outputs[1]->get_data_ptr<float16>();
+                    for (size_t i = 0; i < selected_scores_size; ++i)
+                    {
+                        scores_ptr[i] = float16(selected_scores[i]);
+                    }
+                }
+                break;
+                case element::Type_t::f32:
+                {
+                    float* scores_ptr = outputs[1]->get_data_ptr<float>();
+                    memcpy(scores_ptr, selected_scores.data(), selected_size * sizeof(float));
+                }
+                break;
+                default:;
+                }
+
+                if (num_of_outputs < 3)
+                {
+                    return;
+                }
+
+                if (output_type == ngraph::element::i64)
+                {
+                    int64_t* valid_outputs_ptr = outputs[2]->get_data_ptr<int64_t>();
+                    *valid_outputs_ptr = valid_outputs;
+                }
+                else
+                {
+                    int32_t* valid_outputs_ptr = outputs[2]->get_data_ptr<int32_t>();
+                    *valid_outputs_ptr = static_cast<int32_t>(valid_outputs);
+                }
+            }
+        }
+    }
+}
index d631545..f5b94ac 100644 (file)
 #include "ngraph/attribute_visitor.hpp"
 #include "ngraph/op/constant.hpp"
 #include "ngraph/op/util/op_types.hpp"
+#include "ngraph/runtime/reference/non_max_suppression.hpp"
+#include "ngraph/type/bfloat16.hpp"
+#include "ngraph/type/float16.hpp"
+#include "ngraph/util.hpp"
 
-using namespace std;
 using namespace ngraph;
 
 // ------------------------------ V1 ------------------------------
@@ -58,7 +61,7 @@ op::v1::NonMaxSuppression::NonMaxSuppression(
     constructor_validate_and_infer_types();
 }
 
-shared_ptr<Node>
+std::shared_ptr<Node>
     op::v1::NonMaxSuppression::clone_with_new_inputs(const OutputVector& new_args) const
 {
     check_new_args_count(this, new_args);
@@ -256,7 +259,7 @@ op::v3::NonMaxSuppression::NonMaxSuppression(
     constructor_validate_and_infer_types();
 }
 
-shared_ptr<Node>
+std::shared_ptr<Node>
     op::v3::NonMaxSuppression::clone_with_new_inputs(const OutputVector& new_args) const
 {
     check_new_args_count(this, new_args);
@@ -475,7 +478,7 @@ op::v4::NonMaxSuppression::NonMaxSuppression(
     constructor_validate_and_infer_types();
 }
 
-shared_ptr<Node>
+std::shared_ptr<Node>
     op::v4::NonMaxSuppression::clone_with_new_inputs(const OutputVector& new_args) const
 {
     check_new_args_count(this, new_args);
@@ -542,12 +545,7 @@ op::v5::NonMaxSuppression::NonMaxSuppression(
     const op::v5::NonMaxSuppression::BoxEncodingType box_encoding,
     const bool sort_result_descending,
     const element::Type& output_type)
-    : Op({boxes,
-          scores,
-          op::Constant::create(element::i64, Shape{}, {0}),
-          op::Constant::create(element::f32, Shape{}, {.0f}),
-          op::Constant::create(element::f32, Shape{}, {.0f}),
-          op::Constant::create(element::f32, Shape{}, {.0f})})
+    : Op({boxes, scores})
     , m_box_encoding{box_encoding}
     , m_sort_result_descending{sort_result_descending}
     , m_output_type{output_type}
@@ -562,12 +560,7 @@ op::v5::NonMaxSuppression::NonMaxSuppression(
     const op::v5::NonMaxSuppression::BoxEncodingType box_encoding,
     const bool sort_result_descending,
     const element::Type& output_type)
-    : Op({boxes,
-          scores,
-          max_output_boxes_per_class,
-          op::Constant::create(element::f32, Shape{}, {.0f}),
-          op::Constant::create(element::f32, Shape{}, {.0f}),
-          op::Constant::create(element::f32, Shape{}, {.0f})})
+    : Op({boxes, scores, max_output_boxes_per_class})
     , m_box_encoding{box_encoding}
     , m_sort_result_descending{sort_result_descending}
     , m_output_type{output_type}
@@ -583,12 +576,7 @@ op::v5::NonMaxSuppression::NonMaxSuppression(
     const op::v5::NonMaxSuppression::BoxEncodingType box_encoding,
     const bool sort_result_descending,
     const element::Type& output_type)
-    : Op({boxes,
-          scores,
-          max_output_boxes_per_class,
-          iou_threshold,
-          op::Constant::create(element::f32, Shape{}, {.0f}),
-          op::Constant::create(element::f32, Shape{}, {.0f})})
+    : Op({boxes, scores, max_output_boxes_per_class, iou_threshold})
     , m_box_encoding{box_encoding}
     , m_sort_result_descending{sort_result_descending}
     , m_output_type{output_type}
@@ -605,12 +593,7 @@ op::v5::NonMaxSuppression::NonMaxSuppression(
     const op::v5::NonMaxSuppression::BoxEncodingType box_encoding,
     const bool sort_result_descending,
     const element::Type& output_type)
-    : Op({boxes,
-          scores,
-          max_output_boxes_per_class,
-          iou_threshold,
-          score_threshold,
-          op::Constant::create(element::f32, Shape{}, {.0f})})
+    : Op({boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold})
     , m_box_encoding{box_encoding}
     , m_sort_result_descending{sort_result_descending}
     , m_output_type{output_type}
@@ -641,7 +624,7 @@ op::v5::NonMaxSuppression::NonMaxSuppression(
     constructor_validate_and_infer_types();
 }
 
-shared_ptr<Node>
+std::shared_ptr<Node>
     op::v5::NonMaxSuppression::clone_with_new_inputs(const OutputVector& new_args) const
 {
     check_new_args_count(this, new_args);
@@ -649,28 +632,81 @@ shared_ptr<Node>
                           new_args.size() >= 2 && new_args.size() <= 6,
                           "Number of inputs must be 2, 3, 4, 5 or 6");
 
-    const auto& arg2 = new_args.size() > 2
-                           ? new_args.at(2)
-                           : ngraph::op::Constant::create(element::i64, Shape{}, {0});
-    const auto& arg3 = new_args.size() > 3
-                           ? new_args.at(3)
-                           : ngraph::op::Constant::create(element::f32, Shape{}, {.0f});
-    const auto& arg4 = new_args.size() > 4
-                           ? new_args.at(4)
-                           : ngraph::op::Constant::create(element::f32, Shape{}, {.0f});
-    const auto& arg5 = new_args.size() > 5
-                           ? new_args.at(5)
-                           : ngraph::op::Constant::create(element::f32, Shape{}, {.0f});
+    switch (new_args.size())
+    {
+    case 2:
+        return std::make_shared<op::v5::NonMaxSuppression>(new_args.at(0),
+                                                           new_args.at(1),
+                                                           m_box_encoding,
+                                                           m_sort_result_descending,
+                                                           m_output_type);
+        break;
+    case 3:
+        return std::make_shared<op::v5::NonMaxSuppression>(new_args.at(0),
+                                                           new_args.at(1),
+                                                           new_args.at(2),
+                                                           m_box_encoding,
+                                                           m_sort_result_descending,
+                                                           m_output_type);
+        break;
+    case 4:
+        return std::make_shared<op::v5::NonMaxSuppression>(new_args.at(0),
+                                                           new_args.at(1),
+                                                           new_args.at(2),
+                                                           new_args.at(3),
+                                                           m_box_encoding,
+                                                           m_sort_result_descending,
+                                                           m_output_type);
+        break;
+    case 5:
+        return std::make_shared<op::v5::NonMaxSuppression>(new_args.at(0),
+                                                           new_args.at(1),
+                                                           new_args.at(2),
+                                                           new_args.at(3),
+                                                           new_args.at(4),
+                                                           m_box_encoding,
+                                                           m_sort_result_descending,
+                                                           m_output_type);
+        break;
+    default:
+        return std::make_shared<op::v5::NonMaxSuppression>(new_args.at(0),
+                                                           new_args.at(1),
+                                                           new_args.at(2),
+                                                           new_args.at(3),
+                                                           new_args.at(4),
+                                                           new_args.at(5),
+                                                           m_box_encoding,
+                                                           m_sort_result_descending,
+                                                           m_output_type);
+        break;
+    }
+}
 
-    return std::make_shared<op::v5::NonMaxSuppression>(new_args.at(0),
-                                                       new_args.at(1),
-                                                       arg2,
-                                                       arg3,
-                                                       arg4,
-                                                       arg5,
-                                                       m_box_encoding,
-                                                       m_sort_result_descending,
-                                                       m_output_type);
+namespace
+{
+    constexpr size_t boxes_port = 0;
+    constexpr size_t scores_port = 1;
+    constexpr size_t max_output_boxes_port = 2;
+    constexpr size_t iou_threshold_port = 3;
+    constexpr size_t score_threshold_port = 4;
+    constexpr size_t soft_nms_sigma_port = 5;
+
+    inline bool is_float_type_admissible(const element::Type& t)
+    {
+        return t == element::f32 || t == element::f16 || t == element::bf16;
+    }
+
+    inline bool is_scalar_or_1d_tensor_with_1_element(const PartialShape& p)
+    {
+        if (p.is_dynamic())
+        {
+            return false;
+        }
+
+        Shape shape = p.to_shape();
+
+        return is_scalar(shape) || (is_vector(shape) && (shape[0] == 1));
+    }
 }
 
 void op::v5::NonMaxSuppression::validate()
@@ -688,6 +724,14 @@ void op::v5::NonMaxSuppression::validate()
     }
 
     NODE_VALIDATION_CHECK(this,
+                          is_float_type_admissible(get_input_element_type(0)),
+                          "Expected bf16, fp16 or fp32 as element type for the 'boxes' input.");
+
+    NODE_VALIDATION_CHECK(this,
+                          is_float_type_admissible(get_input_element_type(1)),
+                          "Expected bf16, fp16 or fp32 as element type for the 'scores' input.");
+
+    NODE_VALIDATION_CHECK(this,
                           boxes_ps.rank().is_static() && boxes_ps.rank().get_length() == 3,
                           "Expected a 3D tensor for the 'boxes' input. Got: ",
                           boxes_ps);
@@ -700,19 +744,25 @@ void op::v5::NonMaxSuppression::validate()
     if (inputs().size() >= 3)
     {
         const auto max_boxes_ps = get_input_partial_shape(2);
-        NODE_VALIDATION_CHECK(this,
-                              max_boxes_ps.is_dynamic() || is_scalar(max_boxes_ps.to_shape()),
-                              "Expected a scalar for the 'max_output_boxes_per_class' input. Got: ",
-                              max_boxes_ps);
+        NODE_VALIDATION_CHECK(
+            this,
+            max_boxes_ps.is_dynamic() || is_scalar_or_1d_tensor_with_1_element(max_boxes_ps),
+            "Expected 0D or 1D tensor for the 'max_output_boxes_per_class' input. "
+            "Got: ",
+            max_boxes_ps);
     }
 
     if (inputs().size() >= 4)
     {
         const auto iou_threshold_ps = get_input_partial_shape(3);
         NODE_VALIDATION_CHECK(this,
+                              is_float_type_admissible(get_input_element_type(3)),
+                              "Expected bf16, fp16 or fp32 as element type for the "
+                              "'iou_threshold' input.");
+        NODE_VALIDATION_CHECK(this,
                               iou_threshold_ps.is_dynamic() ||
-                                  is_scalar(iou_threshold_ps.to_shape()),
-                              "Expected a scalar for the 'iou_threshold' input. Got: ",
+                                  is_scalar_or_1d_tensor_with_1_element(iou_threshold_ps),
+                              "Expected 0D or 1D tensor for the 'iou_threshold' input. Got: ",
                               iou_threshold_ps);
     }
 
@@ -720,9 +770,13 @@ void op::v5::NonMaxSuppression::validate()
     {
         const auto score_threshold_ps = get_input_partial_shape(4);
         NODE_VALIDATION_CHECK(this,
+                              is_float_type_admissible(get_input_element_type(4)),
+                              "Expected bf16, fp16 or fp32 as element type for the "
+                              "'score_threshold_ps' input.");
+        NODE_VALIDATION_CHECK(this,
                               score_threshold_ps.is_dynamic() ||
-                                  is_scalar(score_threshold_ps.to_shape()),
-                              "Expected a scalar for the 'score_threshold' input. Got: ",
+                                  is_scalar_or_1d_tensor_with_1_element(score_threshold_ps),
+                              "Expected 0D or 1D tensor for the 'score_threshold' input. Got: ",
                               score_threshold_ps);
     }
 
@@ -730,8 +784,13 @@ void op::v5::NonMaxSuppression::validate()
     {
         const auto soft_nms_sigma = get_input_partial_shape(5);
         NODE_VALIDATION_CHECK(this,
-                              soft_nms_sigma.is_dynamic() || is_scalar(soft_nms_sigma.to_shape()),
-                              "Expected a scalar for the 'soft_nms_sigma' input. Got: ",
+                              is_float_type_admissible(get_input_element_type(5)),
+                              "Expected bf16, fp16 or fp32 as element type for the "
+                              "'soft_nms_sigma' input.");
+        NODE_VALIDATION_CHECK(this,
+                              soft_nms_sigma.is_dynamic() ||
+                                  is_scalar_or_1d_tensor_with_1_element(soft_nms_sigma),
+                              "Expected 0D or 1D tensor for the 'soft_nms_sigma' input. Got: ",
                               soft_nms_sigma);
     }
 
@@ -764,23 +823,27 @@ int64_t op::v5::NonMaxSuppression::max_boxes_output_from_input() const
 {
     int64_t max_output_boxes{0};
 
+    if (inputs().size() < 3)
+    {
+        return 0;
+    }
+
     const auto max_output_boxes_input =
-        as_type_ptr<op::Constant>(input_value(2).get_node_shared_ptr());
+        as_type_ptr<op::Constant>(input_value(max_output_boxes_port).get_node_shared_ptr());
     max_output_boxes = max_output_boxes_input->cast_vector<int64_t>().at(0);
 
     return max_output_boxes;
 }
 
-static constexpr size_t boxes_port = 0;
-static constexpr size_t scores_port = 1;
-static constexpr size_t iou_threshold_port = 3;
-static constexpr size_t score_threshold_port = 4;
-static constexpr size_t soft_nms_sigma_port = 5;
-
 float op::v5::NonMaxSuppression::iou_threshold_from_input() const
 {
     float iou_threshold = 0.0f;
 
+    if (inputs().size() < 4)
+    {
+        return iou_threshold;
+    }
+
     const auto iou_threshold_input =
         as_type_ptr<op::Constant>(input_value(iou_threshold_port).get_node_shared_ptr());
     iou_threshold = iou_threshold_input->cast_vector<float>().at(0);
@@ -792,6 +855,11 @@ float op::v5::NonMaxSuppression::score_threshold_from_input() const
 {
     float score_threshold = 0.0f;
 
+    if (inputs().size() < 5)
+    {
+        return score_threshold;
+    }
+
     const auto score_threshold_input =
         as_type_ptr<op::Constant>(input_value(score_threshold_port).get_node_shared_ptr());
     score_threshold = score_threshold_input->cast_vector<float>().at(0);
@@ -803,6 +871,11 @@ float op::v5::NonMaxSuppression::soft_nms_sigma_from_input() const
 {
     float soft_nms_sigma = 0.0f;
 
+    if (inputs().size() < 6)
+    {
+        return soft_nms_sigma;
+    }
+
     const auto soft_nms_sigma_input =
         as_type_ptr<op::Constant>(input_value(soft_nms_sigma_port).get_node_shared_ptr());
     soft_nms_sigma = soft_nms_sigma_input->cast_vector<float>().at(0);
index 2b8fac3..b9035b4 100644 (file)
@@ -98,7 +98,7 @@ from ngraph.opset1.ops import mod
 from ngraph.opset1.ops import multiply
 from ngraph.opset2.ops import mvn
 from ngraph.opset1.ops import negative
-from ngraph.opset4.ops import non_max_suppression
+from ngraph.opset5.ops import non_max_suppression
 from ngraph.opset3.ops import non_zero
 from ngraph.opset1.ops import normalize_l2
 from ngraph.opset1.ops import not_equal
index f6cf2c0..56fee6b 100644 (file)
@@ -119,6 +119,58 @@ def log_softmax(data: NodeInput, axis: int, name: Optional[str] = None) -> Node:
 
 
 @nameable_op
+def non_max_suppression(
+    boxes: NodeInput,
+    scores: NodeInput,
+    max_output_boxes_per_class: Optional[NodeInput] = None,
+    iou_threshold: Optional[NodeInput] = None,
+    score_threshold: Optional[NodeInput] = None,
+    soft_nms_sigma: Optional[NodeInput] = None,
+    box_encoding: str = "corner",
+    sort_result_descending: bool = True,
+    output_type: str = "i64",
+    name: Optional[str] = None,
+) -> Node:
+    """Return a node which performs NonMaxSuppression.
+
+    :param boxes: Tensor with box coordinates.
+    :param scores: Tensor with box scores.
+    :param max_output_boxes_per_class: Tensor Specifying maximum number of boxes
+                                        to be selected per class.
+    :param iou_threshold: Tensor specifying intersection over union threshold
+    :param score_threshold: Tensor specifying minimum score to consider box for the processing.
+    :param soft_nms_sigma: Tensor specifying the sigma parameter for Soft-NMS.
+    :param box_encoding: Format of boxes data encoding.
+    :param sort_result_descending: Flag that specifies whenever it is necessary to sort selected
+                                   boxes across batches or not.
+    :param output_type: Output element type.
+    :return: The new node which performs NonMaxSuppression
+    """
+    if max_output_boxes_per_class is None:
+        max_output_boxes_per_class = make_constant_node(0, np.int64)
+    if iou_threshold is None:
+        iou_threshold = make_constant_node(0, np.float32)
+    if score_threshold is None:
+        score_threshold = make_constant_node(0, np.float32)
+    if soft_nms_sigma is None:
+        inputs = as_nodes(
+            boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold
+        )
+    else:
+        inputs = as_nodes(
+            boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold, soft_nms_sigma
+        )
+
+    attributes = {
+        "box_encoding": box_encoding,
+        "sort_result_descending": sort_result_descending,
+        "output_type": output_type,
+    }
+
+    return _get_node_factory_opset5().create("NonMaxSuppression", inputs, attributes)
+
+
+@nameable_op
 def round(data: NodeInput, mode: str = "half_to_even", name: Optional[str] = None) -> Node:
     """Apply Round operation on each element of input tensor.
 
index b4bac7f..8638459 100644 (file)
@@ -15,6 +15,7 @@
 # ******************************************************************************
 import numpy as np
 import pytest
+from _pyngraph import PartialShape
 
 import ngraph as ng
 from tests.runtime import get_runtime
@@ -104,15 +105,16 @@ def test_non_max_suppression():
 
     boxes_shape = [1, 1000, 4]
     scores_shape = [1, 1, 1000]
-    expected_shape = [0, 3]
     boxes_parameter = ng.parameter(boxes_shape, name="Boxes", dtype=np.float32)
     scores_parameter = ng.parameter(scores_shape, name="Scores", dtype=np.float32)
 
     node = ng.non_max_suppression(boxes_parameter, scores_parameter)
 
     assert node.get_type_name() == "NonMaxSuppression"
-    assert node.get_output_size() == 1
-    assert list(node.get_output_shape(0)) == expected_shape
+    assert node.get_output_size() == 3
+    assert node.get_output_partial_shape(0).same_scheme(PartialShape([-1, 3]))
+    assert node.get_output_partial_shape(1).same_scheme(PartialShape([-1, 3]))
+    assert list(node.get_output_shape(2)) == [1]
 
 
 def test_non_zero():
index 8e486b6..d7760dc 100644 (file)
@@ -308,6 +308,7 @@ set(MULTI_TEST_SRC
     backend/negative.in.cpp
     backend/node_name.in.cpp
     backend/normalize_l2.in.cpp
+    backend/non_max_suppression.in.cpp
     backend/non_zero.in.cpp
     backend/numeric.in.cpp
     backend/one_hot.in.cpp
diff --git a/ngraph/test/backend/non_max_suppression.in.cpp b/ngraph/test/backend/non_max_suppression.in.cpp
new file mode 100644 (file)
index 0000000..e258d27
--- /dev/null
@@ -0,0 +1,614 @@
+//*****************************************************************************
+// Copyright 2020 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//*****************************************************************************
+
+// clang-format off
+#ifdef ${BACKEND_NAME}_FLOAT_TOLERANCE_BITS
+#define DEFAULT_FLOAT_TOLERANCE_BITS ${BACKEND_NAME}_FLOAT_TOLERANCE_BITS
+#endif
+
+#ifdef ${BACKEND_NAME}_DOUBLE_TOLERANCE_BITS
+#define DEFAULT_DOUBLE_TOLERANCE_BITS ${BACKEND_NAME}_DOUBLE_TOLERANCE_BITS
+#endif
+// clang-format on
+
+#include "gtest/gtest.h"
+#include "runtime/backend.hpp"
+#include "ngraph/runtime/tensor.hpp"
+#include "ngraph/ngraph.hpp"
+#include "util/all_close.hpp"
+#include "util/all_close_f.hpp"
+#include "util/known_element_types.hpp"
+#include "util/ndarray.hpp"
+#include "util/test_control.hpp"
+#include "util/test_tools.hpp"
+
+NGRAPH_SUPPRESS_DEPRECATED_START
+
+using namespace std;
+using namespace ngraph;
+
+static string s_manifest = "${MANIFEST}";
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_center_point_box_format)
+{
+    std::vector<float> boxes_data = {0.5, 0.5,  1.0, 1.0, 0.5, 0.6,   1.0, 1.0,
+                                     0.5, 0.4,  1.0, 1.0, 0.5, 10.5,  1.0, 1.0,
+                                     0.5, 10.6, 1.0, 1.0, 0.5, 100.5, 1.0, 1.0};
+
+    std::vector<float> scores_data = {0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 3;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CENTER;
+    const auto boxes_shape = Shape{1, 6, 4};
+    const auto scores_shape = Shape{1, 1, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{3, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{3, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0, 0, 0, 5};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 0.0, 0.0, 0.3};
+    std::vector<int64_t> expected_valid_outputs = {3};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_flipped_coordinates)
+{
+    std::vector<float> boxes_data = {1.0, 1.0,  0.0, 0.0,  0.0, 0.1,   1.0, 1.1,
+                                     0.0, 0.9,  1.0, -0.1, 0.0, 10.0,  1.0, 11.0,
+                                     1.0, 10.1, 0.0, 11.1, 1.0, 101.0, 0.0, 100.0};
+
+    std::vector<float> scores_data = {0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 3;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 6, 4};
+    const auto scores_shape = Shape{1, 1, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{3, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{3, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0, 0, 0, 5};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 0.0, 0.0, 0.3};
+    std::vector<int64_t> expected_valid_outputs = {3};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_identical_boxes)
+{
+    std::vector<float> boxes_data = {0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0,
+                                     1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
+                                     0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0,
+                                     1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0};
+
+    std::vector<float> scores_data = {0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9};
+
+    const int64_t max_output_boxes_per_class_data = 3;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 10, 4};
+    const auto scores_shape = Shape{1, 1, 10};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{1, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{1, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 0};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.9};
+    std::vector<int64_t> expected_valid_outputs = {1};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_limit_output_size)
+{
+    std::vector<float> boxes_data = {0.0, 0.0,  1.0, 1.0,  0.0, 0.1,   1.0, 1.1,
+                                     0.0, -0.1, 1.0, 0.9,  0.0, 10.0,  1.0, 11.0,
+                                     0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0};
+
+    std::vector<float> scores_data = {0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 2;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 6, 4};
+    const auto scores_shape = Shape{1, 1, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{2, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{2, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.95, 0.0, 0.0, 0.9};
+    std::vector<int64_t> expected_valid_outputs = {2};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_single_box)
+{
+    std::vector<float> boxes_data = {0.0, 0.0, 1.0, 1.0};
+
+    std::vector<float> scores_data = {0.9};
+
+    const int64_t max_output_boxes_per_class_data = 3;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 1, 4};
+    const auto scores_shape = Shape{1, 1, 1};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{1, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{1, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 0};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.9};
+    std::vector<int64_t> expected_valid_outputs = {1};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_suppress_by_IOU)
+{
+    std::vector<float> boxes_data = {0.0, 0.0,  1.0, 1.0,  0.0, 0.1,   1.0, 1.1,
+                                     0.0, -0.1, 1.0, 0.9,  0.0, 10.0,  1.0, 11.0,
+                                     0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0};
+
+    std::vector<float> scores_data = {0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 3;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 6, 4};
+    const auto scores_shape = Shape{1, 1, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{3, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{3, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0, 0, 0, 5};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 0.0, 0.0, 0.3};
+    std::vector<int64_t> expected_valid_outputs = {3};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_suppress_by_IOU_and_scores)
+{
+    std::vector<float> boxes_data = {0.0, 0.0,  1.0, 1.0,  0.0, 0.1,   1.0, 1.1,
+                                     0.0, -0.1, 1.0, 0.9,  0.0, 10.0,  1.0, 11.0,
+                                     0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0};
+
+    std::vector<float> scores_data = {0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 3;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.4f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 6, 4};
+    const auto scores_shape = Shape{1, 1, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{2, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{2, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0};
+    std::vector<float> expected_selected_scores = {0.0, 0.0, 0.95, 0.0, 0.0, 0.9};
+    std::vector<int64_t> expected_valid_outputs = {2};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_two_batches)
+{
+    std::vector<float> boxes_data = {
+        0.0, 0.0,  1.0, 1.0,  0.0, 0.1,   1.0, 1.1,   0.0, -0.1, 1.0, 0.9,  0.0, 10.0,  1.0, 11.0,
+        0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0, 0.0, 0.0,  1.0, 1.0,  0.0, 0.1,   1.0, 1.1,
+        0.0, -0.1, 1.0, 0.9,  0.0, 10.0,  1.0, 11.0,  0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0};
+
+    std::vector<float> scores_data = {
+        0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 2;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{2, 6, 4};
+    const auto scores_shape = Shape{2, 1, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{4, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{4, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0, 1, 0, 3, 1, 0, 0};
+    std::vector<float> expected_selected_scores = {
+        0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 1.0, 0.0, 0.95, 1.0, 0.0, 0.9};
+    std::vector<int64_t> expected_valid_outputs = {4};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, nonmaxsuppression_two_classes)
+{
+    std::vector<float> boxes_data = {0.0, 0.0,  1.0, 1.0,  0.0, 0.1,   1.0, 1.1,
+                                     0.0, -0.1, 1.0, 0.9,  0.0, 10.0,  1.0, 11.0,
+                                     0.0, 10.1, 1.0, 11.1, 0.0, 100.0, 1.0, 101.0};
+
+    std::vector<float> scores_data = {
+        0.9, 0.75, 0.6, 0.95, 0.5, 0.3, 0.9, 0.75, 0.6, 0.95, 0.5, 0.3};
+
+    const int64_t max_output_boxes_per_class_data = 2;
+    const float iou_threshold_data = 0.5f;
+    const float score_threshold_data = 0.0f;
+    const auto box_encoding = op::v5::NonMaxSuppression::BoxEncodingType::CORNER;
+    const auto boxes_shape = Shape{1, 6, 4};
+    const auto scores_shape = Shape{1, 2, 6};
+
+    const auto boxes = make_shared<op::Parameter>(element::f32, boxes_shape);
+    const auto scores = make_shared<op::Parameter>(element::f32, scores_shape);
+    auto max_output_boxes_per_class =
+        op::Constant::create<int64_t>(element::i64, Shape{}, {max_output_boxes_per_class_data});
+    auto iou_threshold = op::Constant::create<float>(element::f32, Shape{}, {iou_threshold_data});
+    auto score_threshold =
+        op::Constant::create<float>(element::f32, Shape{}, {score_threshold_data});
+    auto soft_nms_sigma = op::Constant::create<float>(element::f32, Shape{}, {0.0f});
+    auto nms = make_shared<op::v5::NonMaxSuppression>(boxes,
+                                                      scores,
+                                                      max_output_boxes_per_class,
+                                                      iou_threshold,
+                                                      score_threshold,
+                                                      soft_nms_sigma,
+                                                      box_encoding,
+                                                      false);
+
+    auto f = make_shared<Function>(nms, ParameterVector{boxes, scores});
+
+    auto backend = runtime::Backend::create("${BACKEND_NAME}");
+
+    auto selected_indeces = backend->create_tensor(element::i64, Shape{4, 3});
+    auto selected_scores = backend->create_tensor(element::f32, Shape{4, 3});
+    auto valid_outputs = backend->create_tensor(element::i64, Shape{1});
+
+    auto backend_boxes = backend->create_tensor(element::f32, boxes_shape);
+    auto backend_scores = backend->create_tensor(element::f32, scores_shape);
+    copy_data(backend_boxes, boxes_data);
+    copy_data(backend_scores, scores_data);
+
+    auto handle = backend->compile(f);
+
+    handle->call({selected_indeces, selected_scores, valid_outputs},
+                 {backend_boxes, backend_scores});
+
+    auto selected_indeces_value = read_vector<int64_t>(selected_indeces);
+    auto selected_scores_value = read_vector<float>(selected_scores);
+    auto valid_outputs_value = read_vector<int64_t>(valid_outputs);
+
+    std::vector<int64_t> expected_selected_indices = {0, 0, 3, 0, 0, 0, 0, 1, 3, 0, 1, 0};
+    std::vector<float> expected_selected_scores = {
+        0.0, 0.0, 0.95, 0.0, 0.0, 0.9, 0.0, 1.0, 0.95, 0.0, 1.0, 0.9};
+    std::vector<int64_t> expected_valid_outputs = {4};
+
+    EXPECT_EQ(expected_selected_indices, selected_indeces_value);
+    EXPECT_EQ(expected_selected_scores, selected_scores_value);
+    EXPECT_EQ(expected_valid_outputs, valid_outputs_value);
+}
index 76e96fc..ea6eb74 100644 (file)
@@ -1140,6 +1140,26 @@ IE_CPU.onnx_resize11_scales_nearest_asymmetric_floor_dynamic_sizes
 # Input data precision not supported. Expected float.
 ctc_greedy_decoder_f16
 
+# Next nine tests fails in CPU for the following reason. The nGraph function
+# for NMS-5 are passed to the method compile() of the backend, but this
+# method doesn't apply any nGraph transformations to the passed function,
+# and the plugin backend gets CNNNetwork with NMS-5, NMS-5 has dynamic shapes
+# for two of three outputs, and results of these two outputs are interpreted
+# as scalars. If we apply all needed nGraph transformations to the nGraph
+# function with NMS-5 to get the nGraph function with NMSIE3 (internal
+# operation, similar with NMS-5, but with all static output shapes), before
+# the method compile() call, then tests for INTERPRETER backend for NMS-5 will
+# fail, because NMSIE3 has not the reference implementation, but NMS-5 has one.
+IE_CPU.nonmaxsuppression_center_point_box_format
+IE_CPU.nonmaxsuppression_flipped_coordinates
+IE_CPU.nonmaxsuppression_identical_boxes
+IE_CPU.nonmaxsuppression_limit_output_size
+IE_CPU.nonmaxsuppression_single_box
+IE_CPU.nonmaxsuppression_suppress_by_IOU
+IE_CPU.nonmaxsuppression_suppress_by_IOU_and_scores
+IE_CPU.nonmaxsuppression_two_batches
+IE_CPU.nonmaxsuppression_two_classes
+
 #-------------------------------------------------------------------------------
 #
 #       Inference Engine GPU plugin excludes
index 2cec9a0..4efdb29 100644 (file)
 //*****************************************************************************
 
 #include "int_executable.hpp"
+#include <cstring>
 #include "backend_manager.hpp"
 #include "ngraph/chrome_trace.hpp"
 #include "ngraph/except.hpp"
 #include "ngraph/op/util/op_types.hpp"
 #include "ngraph/ops.hpp"
 #include "ngraph/pass/manager.hpp"
+#include "ngraph/type/bfloat16.hpp"
+#include "ngraph/type/float16.hpp"
 #include "ngraph/util.hpp"
 #include "pass/fused_op_decomposition.hpp"
 #include "pass/liveness.hpp"
index 0d9e05c..0ece39b 100644 (file)
@@ -69,6 +69,7 @@
 #include "ngraph/runtime/reference/max_pool.hpp"
 #include "ngraph/runtime/reference/min.hpp"
 #include "ngraph/runtime/reference/negate.hpp"
+#include "ngraph/runtime/reference/non_max_suppression.hpp"
 #include "ngraph/runtime/reference/normalize_l2.hpp"
 #include "ngraph/runtime/reference/not.hpp"
 #include "ngraph/runtime/reference/one_hot.hpp"
@@ -1303,6 +1304,43 @@ protected:
                                        norm->get_eps_mode());
             break;
         }
+        case OP_TYPEID::NonMaxSuppression_v5:
+        {
+            const op::v5::NonMaxSuppression* nms =
+                static_cast<const op::v5::NonMaxSuppression*>(&node);
+
+            auto info = reference::get_info_for_nms5_evaluation(nms, args);
+
+            std::vector<int64_t> selected_indices(info.out_shape_size);
+            std::vector<float> selected_scores(info.out_shape_size);
+            int64_t valid_outputs = 0;
+
+            reference::non_max_suppression(info.boxes_data.data(),
+                                           info.boxes_shape,
+                                           info.scores_data.data(),
+                                           info.scores_shape,
+                                           info.max_output_boxes_per_class,
+                                           info.iou_threshold,
+                                           info.score_threshold,
+                                           info.soft_nms_sigma,
+                                           selected_indices.data(),
+                                           info.out_shape,
+                                           selected_scores.data(),
+                                           info.out_shape,
+                                           &valid_outputs,
+                                           info.sort_result_descending);
+
+            auto selected_scores_type =
+                (args.size() < 4) ? element::f32 : args[3]->get_element_type();
+
+            reference::nms5_postprocessing(out,
+                                           info.output_type,
+                                           selected_indices,
+                                           selected_scores,
+                                           valid_outputs,
+                                           selected_scores_type);
+            break;
+        }
 
         // Fused Ops are not supported in interpreter. They need to be decomposed before execution
         case OP_TYPEID::DepthToSpace:
index 24f5a52..8e5ea9f 100644 (file)
@@ -65,4 +65,5 @@ NGRAPH_OP(RNNSequence, op::v5)
 NGRAPH_OP(BatchNormInference, op::v5)
 NGRAPH_OP(Round, op::v5)
 NGRAPH_OP(LogSoftmax, op::v5)
+NGRAPH_OP(NonMaxSuppression, op::v5)
 #undef ID_SUFFIX
index df8bf1a..ab70f7c 100644 (file)
@@ -619,43 +619,46 @@ TEST(type_prop, nms_v5_scalar_inputs_check)
     const auto scores = make_shared<op::Parameter>(element::f32, Shape{1, 2, 2});
 
     const auto scalar = make_shared<op::Parameter>(element::f32, Shape{});
-    const auto non_scalar = make_shared<op::Parameter>(element::f32, Shape{1});
+    const auto non_0d_or_1d = make_shared<op::Parameter>(element::f32, Shape{2});
 
     try
     {
-        make_shared<op::v5::NonMaxSuppression>(boxes, scores, non_scalar, scalar, scalar);
+        make_shared<op::v5::NonMaxSuppression>(boxes, scores, non_0d_or_1d, scalar, scalar);
     }
     catch (const NodeValidationFailure& error)
     {
         EXPECT_HAS_SUBSTRING(error.what(),
-                             "Expected a scalar for the 'max_output_boxes_per_class' input");
+                             "Expected 0D or 1D tensor for the 'max_output_boxes_per_class' input");
     }
 
     try
     {
-        make_shared<op::v5::NonMaxSuppression>(boxes, scores, scalar, non_scalar, scalar);
+        make_shared<op::v5::NonMaxSuppression>(boxes, scores, scalar, non_0d_or_1d, scalar);
     }
     catch (const NodeValidationFailure& error)
     {
-        EXPECT_HAS_SUBSTRING(error.what(), "Expected a scalar for the 'iou_threshold' input");
+        EXPECT_HAS_SUBSTRING(error.what(),
+                             "Expected 0D or 1D tensor for the 'iou_threshold' input");
     }
 
     try
     {
-        make_shared<op::v5::NonMaxSuppression>(boxes, scores, scalar, scalar, non_scalar);
+        make_shared<op::v5::NonMaxSuppression>(boxes, scores, scalar, scalar, non_0d_or_1d);
     }
     catch (const NodeValidationFailure& error)
     {
-        EXPECT_HAS_SUBSTRING(error.what(), "Expected a scalar for the 'score_threshold' input");
+        EXPECT_HAS_SUBSTRING(error.what(),
+                             "Expected 0D or 1D tensor for the 'score_threshold' input");
     }
 
     try
     {
-        make_shared<op::v5::NonMaxSuppression>(boxes, scores, scalar, scalar, scalar, non_scalar);
+        make_shared<op::v5::NonMaxSuppression>(boxes, scores, scalar, scalar, scalar, non_0d_or_1d);
     }
     catch (const NodeValidationFailure& error)
     {
-        EXPECT_HAS_SUBSTRING(error.what(), "Expected a scalar for the 'soft_nms_sigma' input");
+        EXPECT_HAS_SUBSTRING(error.what(),
+                             "Expected 0D or 1D tensor for the 'soft_nms_sigma' input");
     }
 }
 
@@ -767,4 +770,4 @@ TEST(type_prop, nms_v5_dynamic_boxes_and_scores)
         nms->get_output_partial_shape(1).same_scheme(PartialShape{Dimension::dynamic(), 3}));
 
     EXPECT_EQ(nms->get_output_shape(2), (Shape{1}));
-}
\ No newline at end of file
+}