NMS-4 op support (#1115)

* Specification for the NMS-4 operation (updated shape infer function)

* Enabled NMS-4 in the Model Optimizer

* Changed opset version for NMS with dynamic outputs and namespace to be "dynamic"

* Added NMS-4

* Added opset4 to the nGraph

* Added unit tests for NMS-4 type infer

* Renamed UpgradeNMS3ToNMS4 to UpgradeNMS3ToNMSDynamic. Added stub for ConvertNMS4ToLegacy

* Make IE aware of opset4 ops

* Updated NMSIE to have different shape infer function based on the NMS it was converted from. Implemented NMS4->NMSIE conversion

* Apply code style

* Updated StaticShapeNonMaximumSuppression op in the VPU

* Introduced new version of NMSIE operation with shape infer function from v4::NMS

* Fixed dynamicToStaticNonMaxSuppression transformation

* Added new version of NMSIE op with updated shape infer function

* Fixed NMS4 to NMSIE2 transformation

* Fixed constructors for nGraph ops v4::NM and dynamic::NMS

* Updated text in the opset4 specification document

* Code style fixes

* Fixed constructors for StaticShapeNMS + fixed test

* Minor change to the NMS op in the MO

* Fixed typo in the dynamic_to_static_shape_non_max_suppression transformation

* Removed redundant checks

* Refactored NMS infer and validate functions

* Added more checks to the validate_and_infer_types functions for NMS-3 and NMS-4

* Fixed compilation issue on Windows for op NMS

* Code style fixes

* Fixed typos in the NMSIE and NMSIE2 to CNNLayer op conversion

* Fixed typo in the ie_cnn_layer_builder_ngraph.cpp

* Fixed the NMSToLegacyNMS transformation. Added unit tests

* Apply code review comments

* Refactored NMSIE to use visitors

* Removed calling ConvertNMS4ToLegacy in the common optimizations

* Moved NMS4ToNMSLegacy to convert1_to_legacy group of transformations

* Removed useless include statement

* Removed copy-paste issue

Co-authored-by: Evgeny Lazarev <elazarev.nnov@gmail.com>
This commit is contained in:
Evgeny Lazarev 2020-06-30 14:04:31 +03:00 committed by GitHub
parent a01b915857
commit f596432268
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1354 additions and 167 deletions

View File

@ -6,6 +6,7 @@ This topic provides a complete list of available sets of operations supported in
| OpenVINO™ Version | Actual Operations Set |
| :---------------- | :------------------------------- |
| 2021.1 | [opset4](opset4.md) |
| 2020.4 | [opset3](opset3.md) |
| 2020.3 | [opset2](opset2.md) |
| 2020.2 | [opset2](opset2.md) |

143
docs/ops/opset4.md Normal file
View File

@ -0,0 +1,143 @@
# Operation Set `opset4` Specification
This specification document describes `opset4` operation set supported in OpenVINO.
Support for each particular operation from the list below depends on the capabilities available in a inference plugin
and may vary among different hardware platforms and devices. Examples of operation instances are expressed as IR V10 xml
snippets. Such IR is generated by the Model Optimizer. The semantics match corresponding nGraph operation classes
declared in `namespace opset4`.
## Table of Contents <a name="toc"></a>
* [Abs](arithmetic/Abs_1.md)
* [Acos](arithmetic/Acos_1.md)
* [Acosh](arithmetic/Acosh_1.md)
* [Add](arithmetic/Add_1.md)
* [Asin](arithmetic/Asin_1.md)
* [Asinh](arithmetic/Asinh_1.md)
* [Assign](infrastructure/Assign_3.md)
* [Atan](arithmetic/Atan_1.md)
* [Atanh](arithmetic/Atanh_1.md)
* [AvgPool](pooling/AvgPool_1.md)
* [BatchNormInference](normalization/BatchNormInference_1.md)
* [BatchToSpace](movement/BatchToSpace_2.md)
* [BinaryConvolution](convolution/BinaryConvolution_1.md)
* [Broadcast](movement/Broadcast_3.md)
* [Bucketize](condition/Bucketize_3.md)
* [CTCGreedyDecoder](sequence/CTCGreedyDecoder_1.md)
* [Ceiling](arithmetic/Ceiling_1.md)
* [Clamp](activation/Clamp_1.md)
* [Concat](movement/Concat_1.md)
* [Constant](infrastructure/Constant_1.md)
* [Convert](type/Convert_1.md)
* [ConvertLike](type/ConvertLike_1.md)
* [Convolution](convolution/Convolution_1.md)
* [ConvolutionBackpropData](convolution/ConvolutionBackpropData_1.md)
* [Cos](arithmetic/Cos_1.md)
* [Cosh](arithmetic/Cosh_1.md)
* [CumSum](arithmetic/CumSum_3.md)
* [DeformableConvolution](convolution/DeformableConvolution_1.md)
* [DeformablePSROIPooling](detection/DeformablePSROIPooling_1.md)
* [DepthToSpace](movement/DepthToSpace_1.md)
* [DetectionOutput](detection/DetectionOutput_1.md)
* [Divide](arithmetic/Divide_1.md)
* [Elu](activation/Elu_1.md)
* [EmbeddingBagOffsetsSum](sparse/EmbeddingBagOffsetsSum_3.md)
* [EmbeddingBagPackedSum](sparse/EmbeddingBagPackedSum_3.md)
* [EmbeddingSegmentsSum](sparse/EmbeddingSegmentsSum_3.md)
* [Equal](comparison/Equal_1.md)
* [Erf](arithmetic/Erf_1.md)
* [Exp](activation/Exp_1.md)
* [ExtractImagePatches](movement/ExtractImagePatches_3.md)
* [FakeQuantize](quantization/FakeQuantize_1.md)
* [Floor](arithmetic/Floor_1.md)
* [FloorMod](arithmetic/FloorMod_1.md)
* [Gather](movement/Gather_1.md)
* [GatherTree](movement/GatherTree_1.md)
* [Gelu](activation/GELU_2.md)
* [Greater](comparison/Greater_1.md)
* [GreaterEqual](comparison/GreaterEqual_1.md)
* [GRN](normalization/GRN_1.md)
* [GroupConvolution](convolution/GroupConvolution_1.md)
* [GroupConvolutionBackpropData](convolution/GroupConvolutionBackpropData_1.md)
* [GRUCell](sequence/GRUCell_3.md)
* [HardSigmoid](activation/HardSigmoid_1.md)
* [Interpolate](image/Interpolate_1.md)
* [Less](comparison/Less_1.md)
* [LessEqual](comparison/LessEqual_1.md)
* [Log](arithmetic/Log_1.md)
* [LogicalAnd](logical/LogicalAnd_1.md)
* [LogicalNot](logical/LogicalNot_1.md)
* [LogicalOr](logical/LogicalOr_1.md)
* [LogicalXor](logical/LogicalXor_1.md)
* [LRN](normalization/LRN_1.md)
* [LSTMCell](sequence/LSTMCell_1.md)
* [LSTMSequence](sequence/LSTMSequence_1.md)
* [MatMul](matrix/MatMul_1.md)
* [MaxPool](pooling/MaxPool_1.md)
* [Maximum](arithmetic/Maximum_1.md)
* [Minimum](arithmetic/Minimum_1.md)
* [Mod](arithmetic/Mod_1.md)
* [MVN](normalization/MVN_1.md)
* [Multiply](arithmetic/Multiply_1.md)
* [Negative](arithmetic/Negative_1.md)
* [NonMaxSuppression](sort/NonMaxSuppression_4.md)
* [NonZero](condition/NonZero_3.md)
* [NormalizeL2](normalization/NormalizeL2_1.md)
* [NotEqual](comparison/NotEqual_1.md)
* [OneHot](sequence/OneHot_1.md)
* [Pad](movement/Pad_1.md)
* [Parameter](infrastructure/Parameter_1.md)
* [Power](arithmetic/Power_1.md)
* [PReLU](activation/PReLU_1.md)
* [PriorBoxClustered](detection/PriorBoxClustered_1.md)
* [PriorBox](detection/PriorBox_1.md)
* [Proposal](detection/Proposal_1.md)
* [PSROIPooling](detection/PSROIPooling_1.md)
* [Range](generation/Range_1.md)
* [ReLU](activation/ReLU_1.md)
* [ReadValue](infrastructure/ReadValue_3.md)
* [ReduceLogicalAnd](reduction/ReduceLogicalAnd_1.md)
* [ReduceLogicalOr](reduction/ReduceLogicalOr_1.md)
* [ReduceMax](reduction/ReduceMax_1.md)
* [ReduceMean](reduction/ReduceMean_1.md)
* [ReduceMin](reduction/ReduceMin_1.md)
* [ReduceProd](reduction/ReduceProd_1.md)
* [ReduceSum](reduction/ReduceSum_1.md)
* [RegionYolo](detection/RegionYolo_1.md)
* [ReorgYolo](detection/ReorgYolo_1.md)
* [Reshape](shape/Reshape_1.md)
* [Result](infrastructure/Result_1.md)
* [Reverse](movement/Reverse_1.md)
* [ReverseSequence](movement/ReverseSequence_1.md)
* [RNNCell](sequence/RNNCell_3.md)
* [ROIAlign](detection/ROIAlign_3.md)
* [ROIPooling](detection/ROIPooling_1.md)
* [ScatterElementsUpdate](movement/ScatterElementsUpdate_3.md)
* [ScatterNDUpdate](movement/ScatterNDUpdate_3.md)
* [ScatterUpdate](movement/ScatterUpdate_3.md)
* [Select](condition/Select_1.md)
* [Selu](arithmetic/Selu_1.md)
* [ShapeOf](shape/ShapeOf_3.md)
* [ShuffleChannels](movement/ShuffleChannels_1.md)
* [Sigmoid](activation/Sigmoid_1.md)
* [Sign](arithmetic/Sign_1.md)
* [Sin](arithmetic/Sin_1.md)
* [Sinh](arithmetic/Sinh_1.md)
* [SoftMax](activation/SoftMax_1.md)
* [SpaceToBatch](movement/SpaceToBatch_2.md)
* [SpaceToDepth](movement/SpaceToDepth_1.md)
* [Split](movement/Split_1.md)
* [Sqrt](arithmetic/Sqrt_1.md)
* [SquaredDifference](arithmetic/SquaredDifference_1.md)
* [Squeeze](shape/Squeeze_1.md)
* [StridedSlice](movement/StridedSlice_1.md)
* [Subtract](arithmetic/Subtract_1.md)
* [Tan](arithmetic/Tan_1.md)
* [Tanh](arithmetic/Tanh_1.md)
* [TensorIterator](infrastructure/TensorIterator_1.md)
* [Tile](movement/Tile_1.md)
* [TopK](sort/TopK_3.md)
* [Transpose](movement/Transpose_1.md)
* [Unsqueeze](shape/Unsqueeze_1.md)
* [VariadicSplit](movement/VariadicSplit_1.md)

View File

@ -0,0 +1,104 @@
## NonMaxSuppression<a name="NonMaxSuppression"></a>
**Versioned name**: *NonMaxSuppression-4*
**Category**: *Sorting and maximization*
**Short description**: *NonMaxSuppression* performs non maximum suppression of the boxes with predicted scores.
**Detailed description**: *NonMaxSuppression* performs non maximum suppression algorithm as described below:
1. Take the box with highest score. If the score is less than `score_threshold` then stop. Otherwise add the box to the
output and continue to the next step.
2. For each input box, calculate the IOU (intersection over union) with the box added during the previous step. If the
value is greater than the `iou_threshold` threshold then remove the input box from further consideration.
3. Return to step 1.
This algorithm is applied independently to each class of each batch element. The total number of output boxes for each
class must not exceed `max_output_boxes_per_class`.
**Attributes**:
* *box_encoding*
* **Description**: *box_encoding* specifies the format of boxes data encoding.
* **Range of values**: "corner" or "center"
* *corner* - the box data is supplied as `[y1, x1, y2, x2]` where `(y1, x1)` and `(y2, x2)` are the coordinates of any diagonal pair of box corners.
* *center* - the box data is supplied as `[x_center, y_center, width, height]`.
* **Type**: string
* **Default value**: "corner"
* **Required**: *no*
* *sort_result_descending*
* **Description**: *sort_result_descending* is a flag that specifies whenever it is necessary to sort selected boxes across batches or not.
* **Range of values**: True of False
* *True* - sort selected boxes across batches.
* *False* - do not sort selected boxes across batches (boxes are sorted per class).
* **Type**: boolean
* **Default value**: True
* **Required**: *no*
* *output_type*
* **Description**: the output tensor type
* **Range of values**: "i64" or "i32"
* **Type**: string
* **Default value**: "i64"
* **Required**: *No*
**Inputs**:
* **1**: `boxes` - tensor of type *T* and shape `[num_batches, num_boxes, 4]` with box coordinates. Required.
* **2**: `scores` - tensor of type *T* and shape `[num_batches, num_classes, num_boxes]` with box scores. Required.
* **3**: `max_output_boxes_per_class` - scalar tensor of type *T_MAX_BOXES* specifying maximum number of boxes to be selected per class. Optional with default value 0 meaning select no boxes.
* **4**: `iou_threshold` - scalar tensor of type *T_THRESHOLDS* specifying intersection over union threshold. Optional with default value 0 meaning keep all boxes.
* **5**: `score_threshold` - scalar tensor of type *T_THRESHOLDS* specifying minimum score to consider box for the processing. Optional with default value 0.
**Outputs**:
* **1**: `selected_indices` - tensor of type *T_IND* and shape `[min(num_boxes, max_output_boxes_per_class) * num_batches * num_classes, 3]` containing information about selected boxes as triplets `[batch_index, class_index, box_index]`.
The output tensor is filled with 0s for output tensor elements if the total number of selected boxes is less than the output tensor size.
**Types**
* *T*: floating point type.
* *T_MAX_BOXES*: integer type.
* *T_THRESHOLDS*: floating point type.
* *T_IND*: `int64` or `int32`.
**Example**
```xml
<layer ... type="NonMaxSuppression" ... >
<data box_encoding="corner" sort_result_descending="1" output_type="i64"/>
<input>
<port id="0">
<dim>3</dim>
<dim>100</dim>
<dim>4</dim>
</port>
<port id="1">
<dim>3</dim>
<dim>5</dim>
<dim>100</dim>
</port>
<port id="2"/> <!-- 10 -->
<port id="3"/>
<port id="4"/>
</input>
<output>
<port id="5" precision="I64">
<dim>150</dim> <!-- min(100, 10) * 3 * 5 -->
<dim>3</dim>
</port>
</output>
</layer>
```

View File

@ -550,6 +550,7 @@ Core::Impl::Impl() {
opsetNames.insert("opset1");
opsetNames.insert("opset2");
opsetNames.insert("opset3");
opsetNames.insert("opset4");
}
Core::Impl::~Impl() {}

View File

@ -487,6 +487,14 @@ InferenceEngine::details::CNNLayerCreator::CNNLayerCreator(const std::shared_ptr
<< " should be replaced by constant during constant folding.";
return nullptr;
});
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))};
auto res = std::make_shared<InferenceEngine::NonMaxSuppressionLayer>(attrs);
res->params = params;
return res;
});
}
CNNLayerPtr InferenceEngine::details::CNNLayerCreator::create() {
@ -560,7 +568,6 @@ std::shared_ptr<CNNNetworkImpl> convertFunctionToICNNNetwork(const std::shared_p
std::make_shared<Builder::NodeConverter<::ngraph::op::v1::Minimum>>(),
std::make_shared<Builder::NodeConverter<::ngraph::op::v1::Multiply>>(),
std::make_shared<Builder::NodeConverter<::ngraph::op::v1::NonMaxSuppression>>(),
std::make_shared<Builder::NodeConverter<::ngraph::op::NonMaxSuppressionIE>>(),
std::make_shared<Builder::NodeConverter<::ngraph::op::NormalizeL2>>(),
std::make_shared<Builder::NodeConverter<::ngraph::op::NormalizeIE>>(),
std::make_shared<Builder::NodeConverter<::ngraph::op::OneHotIE>>(),

View File

@ -2100,18 +2100,5 @@ CNNLayer::Ptr NodeConverter<ngraph::op::v1::NonMaxSuppression>::createLayer(cons
THROW_IE_EXCEPTION << "NonMaxSuppression operation must be converted to NonMaxSuppressionIE operation.";
}
template <>
CNNLayer::Ptr NodeConverter<ngraph::op::NonMaxSuppressionIE>::createLayer(const std::shared_ptr<ngraph::Node>& layer) const {
LayerParams params = {layer->get_friendly_name(), "NonMaxSuppression", Precision::I32};
auto castedLayer = std::dynamic_pointer_cast<ngraph::op::NonMaxSuppressionIE>(layer);
if (castedLayer == nullptr) THROW_IE_EXCEPTION << "Cannot get " << params.type << " layer " << params.name;
auto res = std::make_shared<InferenceEngine::NonMaxSuppressionLayer>(params);
res->params["sort_result_descending"] = std::to_string(castedLayer->m_sort_result_descending);
res->params["center_point_box"] = std::to_string(castedLayer->m_sort_result_descending);
return res;
}
} // namespace Builder
} // namespace InferenceEngine

View File

@ -116,6 +116,7 @@ V10Parser::V10Parser(const std::vector<IExtensionPtr>& exts) {
opsets["opset1"] = ngraph::get_opset1();
opsets["opset2"] = ngraph::get_opset2();
opsets["opset3"] = ngraph::get_opset3();
opsets["opset4"] = ngraph::get_opset4();
// Load custom opsets
for (const auto& ext : exts) {

View File

@ -29,6 +29,29 @@ public:
void validate_and_infer_types() override;
bool visit_attributes(AttributeVisitor& visitor) override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector & new_args) const override;
int m_center_point_box;
bool m_sort_result_descending = true;
};
class TRANSFORMATIONS_API NonMaxSuppressionIE2 : public NonMaxSuppressionIE {
public:
static constexpr NodeTypeInfo type_info{"NonMaxSuppressionIE", 2};
const NodeTypeInfo& get_type_info() const override { return type_info; }
NonMaxSuppressionIE2(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);
void validate_and_infer_types() override;
std::shared_ptr<Node> clone_with_new_inputs(const OutputVector & new_args) const override;
int m_center_point_box;

View File

@ -17,7 +17,7 @@
// This pass must be called first in pipeline
NGRAPH_PASS(InitNodeInfo, ::ngraph::pass)
NGRAPH_PASS(RemoveFilteringBoxesBySize, ::ngraph::pass) // Resolves dynamism (replaces NonZero), CF needed
NGRAPH_PASS(UpgradeNMS3ToNMS4, ::ngraph::pass) // replaces v3::NMS with v4::NMS(always dyn output shape) if function has opset3::NonZero operation
NGRAPH_PASS(UpgradeNMS4ToNMSDynamic, ::ngraph::pass) // replaces v4::NMS with dynamic::NMS(always dyn output shape) if function has opset3::NonZero operation
NGRAPH_PASS(ConstantFolding, ::ngraph::pass)
NGRAPH_PASS(StridedSliceOptimization, ::ngraph::pass) // depends on CF
NGRAPH_PASS(NopElimination, ::ngraph::pass) // may introduce fake dynamism

View File

@ -14,21 +14,27 @@
namespace ngraph {
namespace pass {
class TRANSFORMATIONS_API UpgradeNMS3ToNMS4;
class TRANSFORMATIONS_API UpgradeNMS4ToNMSDynamic;
} // namespace pass
} // namespace ngraph
/*
* Description:
* UpgradeNMS3ToNMS4 transformation upgrades NonMaxSuppression operations from v3 version to v4
* UpgradeNMS4ToNMSDynamic transformation upgrades NonMaxSuppression operations from v3 version to dynamic
* in case function has at least one NonZero operation (dynamism marker).
* NMS of version v4 always has dynamic output
* NMS dynamic always has dynamic output
*/
class ngraph::pass::UpgradeNMS3ToNMS4: public ngraph::pass::FunctionPass {
class ngraph::pass::UpgradeNMS4ToNMSDynamic: public ngraph::pass::GraphRewrite {
public:
UpgradeNMS4ToNMSDynamic() : GraphRewrite() {
upgrade_nms4_to_nms_dynamic();
}
bool run_on_function(std::shared_ptr<ngraph::Function> f) override;
private:
void upgrade_nms4_to_nms_dynamic();
};

View File

@ -0,0 +1,36 @@
// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#pragma once
#include <vector>
#include <memory>
#include <transformations_visibility.hpp>
#include <ngraph/pass/graph_rewrite.hpp>
namespace ngraph {
namespace pass {
class TRANSFORMATIONS_API ConvertNMS4ToLegacy;
} // 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::ConvertNMS4ToLegacy: public ngraph::pass::GraphRewrite {
public:
ConvertNMS4ToLegacy() : GraphRewrite() {
convert_nms4_to_legacy();
}
private:
void convert_nms4_to_legacy();
};

View File

@ -63,3 +63,4 @@ NGRAPH_PASS(ConvertGatherTreeToGatherTreeIE, ::ngraph::pass)
NGRAPH_PASS(ConvertTopKToTopKIE, ::ngraph::pass)
NGRAPH_PASS(ConvertNMSToNMSIE, ::ngraph::pass)
NGRAPH_PASS(ConstantFolding, ::ngraph::pass)
NGRAPH_PASS(ConvertNMS4ToLegacy, ::ngraph::pass)

View File

@ -7,6 +7,7 @@
#include <memory>
#include <ngraph/opsets/opset1.hpp>
#include <ngraph/opsets/opset4.hpp>
using namespace std;
using namespace ngraph;
@ -20,32 +21,73 @@ op::NonMaxSuppressionIE::NonMaxSuppressionIE(const Output<Node> &boxes,
const Output<Node> &score_threshold,
int center_point_box,
bool sort_result_descending)
: 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) {
: 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) {
constructor_validate_and_infer_types();
}
std::shared_ptr<Node> op::NonMaxSuppressionIE::clone_with_new_inputs(const ngraph::OutputVector & new_args) const {
std::shared_ptr<Node> op::NonMaxSuppressionIE::clone_with_new_inputs(const ngraph::OutputVector &new_args) const {
check_new_args_count(this, new_args);
return make_shared<NonMaxSuppressionIE>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3),
new_args.at(4), m_center_point_box, m_sort_result_descending);
}
void op::NonMaxSuppressionIE::validate_and_infer_types() {
auto squeeze_input = [](const Output<Node> & input) -> std::shared_ptr<Node> {
auto squeeze_input = [](const Output<Node> &input) -> std::shared_ptr<Node> {
return std::make_shared<opset1::Squeeze>(input, opset1::Constant::create(element::i64, Shape{1}, {0}));
};
// Calculate output shape using opset1::NonMaxSuppression
auto max_output_boxes_per_class = std::dynamic_pointer_cast<opset1::Constant>(input_value(2).get_node_shared_ptr());
auto nms = std::make_shared<opset1::NonMaxSuppression>(input_value(0),
input_value(1),
auto nms = std::make_shared<opset1::NonMaxSuppression>(input_value(0), input_value(1),
/* second input is used for output calculation and only if it's Constant output shape won't be dynamic */
max_output_boxes_per_class ? opset1::Constant::create(element::i64, Shape{}, max_output_boxes_per_class->cast_vector<int64_t>()) :
squeeze_input(input_value(2)),
squeeze_input(input_value(3)),
squeeze_input(input_value(4)));
max_output_boxes_per_class ? opset1::Constant::create(element::i64, Shape{},
max_output_boxes_per_class->cast_vector<int64_t>())
: squeeze_input(input_value(2)),
squeeze_input(input_value(3)), squeeze_input(input_value(4)));
set_output_type(0, nms->output(0).get_element_type(), nms->output(0).get_partial_shape());
}
bool op::NonMaxSuppressionIE::visit_attributes(AttributeVisitor& visitor) {
visitor.on_attribute("center_point_box", m_center_point_box);
visitor.on_attribute("sort_result_descending", m_sort_result_descending);
return true;
}
// The second version of the operation is different just in the shape infer function (uses v4::NMS)
constexpr NodeTypeInfo op::NonMaxSuppressionIE2::type_info;
op::NonMaxSuppressionIE2::NonMaxSuppressionIE2(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)
: op::NonMaxSuppressionIE(boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold, center_point_box, sort_result_descending) {
constructor_validate_and_infer_types();
}
std::shared_ptr<Node> op::NonMaxSuppressionIE2::clone_with_new_inputs(const ngraph::OutputVector &new_args) const {
check_new_args_count(this, new_args);
return make_shared<NonMaxSuppressionIE2>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3),
new_args.at(4), m_center_point_box, m_sort_result_descending);
}
void op::NonMaxSuppressionIE2::validate_and_infer_types() {
auto squeeze_input = [](const Output<Node> &input) -> std::shared_ptr<Node> {
return std::make_shared<opset1::Squeeze>(input, opset1::Constant::create(element::i64, Shape{1}, {0}));
};
// Calculate output shape using opset4::NonMaxSuppression
auto max_output_boxes_per_class = std::dynamic_pointer_cast<opset1::Constant>(input_value(2).get_node_shared_ptr());
auto nms = std::make_shared<opset4::NonMaxSuppression>(input_value(0), input_value(1),
/* second input is used for output calculation and only if it's Constant output shape won't be dynamic */
max_output_boxes_per_class ? opset1::Constant::create(element::i64, Shape{},
max_output_boxes_per_class->cast_vector<int64_t>())
: squeeze_input(input_value(2)), squeeze_input(input_value(3)),
squeeze_input(input_value(4)));
set_output_type(0, nms->output(0).get_element_type(), nms->output(0).get_partial_shape());
}

View File

@ -9,7 +9,7 @@
#include "transformations/optimize_strided_slice.hpp"
#include "transformations/convert_scatter_elements_to_scatter.hpp"
#include "transformations/remove_filtering_boxes_by_size.hpp"
#include "transformations/convert_nms_3_to_nms_v4.hpp"
#include "transformations/convert_nms_4_to_nms_dynamic.hpp"
#include "transformations/init_node_info.hpp"
#include <ngraph/pass/manager.hpp>

View File

@ -1,51 +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/opset3.hpp>
#include <ngraph/rt_info.hpp>
#include <transformations/utils/utils.hpp>
#include "transformations/convert_nms_3_to_nms_v4.hpp"
bool ngraph::pass::UpgradeNMS3ToNMS4::run_on_function(std::shared_ptr<ngraph::Function> f) {
bool rewritten = false;
if (!ngraph::op::util::has_op_with_type<ngraph::opset3::NonZero>(f)) {
return rewritten;
}
for (auto &node : f->get_ops()) {
auto nms_3 = ngraph::as_type_ptr<opset3::NonMaxSuppression>(node);
if (!nms_3)
continue;
const auto box_encoding = static_cast<const op::v4::NonMaxSuppression::BoxEncodingType>(nms_3->get_box_encoding());
const auto new_args = nms_3->input_values();
NODE_VALIDATION_CHECK(nms_3.get(),
new_args.size() >= 2 && new_args.size() <= 5,
"Number of inputs must be 2, 3, 4 or 5");
const auto& arg2 = new_args.size() > 2 ? new_args.at(2) : ngraph::opset3::Constant::create(element::i32, Shape{}, {0});
const auto& arg3 = new_args.size() > 3 ? new_args.at(3) : ngraph::opset3::Constant::create(element::f32, Shape{}, {.0f});
const auto& arg4 = new_args.size() > 4 ? new_args.at(4) : ngraph::opset3::Constant::create(element::f32, Shape{}, {.0f});
const auto nms_4 = std::make_shared<op::v4::NonMaxSuppression>(
new_args.at(0),
new_args.at(1),
arg2,
arg3,
arg4,
box_encoding,
nms_3->get_sort_result_descending(),
nms_3->get_output_type());
nms_4->set_friendly_name(nms_3->get_friendly_name());
ngraph::copy_runtime_info(nms_3, nms_4);
ngraph::replace_node(nms_3, nms_4);
rewritten = true;
}
return rewritten;
}

View File

@ -0,0 +1,61 @@
// Copyright (C) 2018-2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <memory>
#include <vector>
#include <ngraph/graph_util.hpp>
#include <ngraph/opsets/opset4.hpp>
#include <ngraph/rt_info.hpp>
#include <transformations/utils/utils.hpp>
#include "transformations/convert_nms_4_to_nms_dynamic.hpp"
void ngraph::pass::UpgradeNMS4ToNMSDynamic::upgrade_nms4_to_nms_dynamic() {
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::graph_rewrite_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 box_encoding = static_cast<const op::dynamic::NonMaxSuppression::BoxEncodingType>(nms_4->get_box_encoding());
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 nms_dynamic = std::make_shared<op::dynamic::NonMaxSuppression>(
new_args.at(0),
new_args.at(1),
arg2,
arg3,
arg4,
box_encoding,
nms_4->get_sort_result_descending(),
nms_4->get_output_type());
nms_dynamic->set_friendly_name(nms_4->get_friendly_name());
ngraph::copy_runtime_info(nms_4, nms_dynamic);
ngraph::replace_node(nms_4, nms_dynamic);
return true;
};
auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "UpgradeNMS4ToDynamic");
this->add_matcher(m, callback, PassProperty::CHANGE_DYNAMIC_STATE);
}
bool ngraph::pass::UpgradeNMS4ToNMSDynamic::run_on_function(std::shared_ptr<ngraph::Function> f) {
if (ngraph::op::util::has_op_with_type<ngraph::opset4::NonZero>(f)) {
return GraphRewrite::run_on_function(f);
}
return false;
}

View File

@ -0,0 +1,119 @@
// 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 <ngraph_ops/nms_ie.hpp>
#include <ngraph/rt_info.hpp>
#include <transformations/utils/utils.hpp>
#include "transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp"
void ngraph::pass::ConvertNMS4ToLegacy::convert_nms4_to_legacy() {
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::graph_rewrite_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());
new_ops.push_back(nms_legacy);
Output<Node> last;
// if the output is the i32 then it matches behavior of the v1::NonMaxSuppression otherwise need to insert Convert
if (nms_4->get_output_type() == element::i32) {
last = nms_legacy;
} else {
last = std::make_shared<ngraph::opset4::Convert>(nms_legacy, nms_4->get_output_type());
new_ops.push_back(last.get_node_shared_ptr());
}
last.get_node_shared_ptr()->set_friendly_name(nms_4->get_friendly_name());
ngraph::copy_runtime_info(nms_4, new_ops);
ngraph::replace_node(nms_4, last.get_node_shared_ptr());
return true;
};
auto m = std::make_shared<ngraph::pattern::Matcher>(nms, "ConvertNMS4ToNMSLegacy");
this->add_matcher(m, callback, PassProperty::CHANGE_DYNAMIC_STATE);
}

View File

@ -20,6 +20,7 @@
#include <transformations/convert_opset1_to_legacy/convert_mul_or_add_finally.hpp>
#include <transformations/convert_negative.hpp>
#include <transformations/convert_opset1_to_legacy/convert_nms_to_nms_ie.hpp>
#include <transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp>
#include <transformations/convert_opset1_to_legacy/convert_normalizel2_to_normalize_ie.hpp>
#include <transformations/convert_opset1_to_legacy/convert_one_hot_to_one_hot_ie.hpp>
#include <transformations/convert_opset1_to_legacy/convert_pad_to_pad_ie.hpp>

View File

@ -12,7 +12,7 @@
namespace ngraph { namespace vpu { namespace op {
class StaticShapeNonMaxSuppression : public ngraph::op::v3::NonMaxSuppression {
class StaticShapeNonMaxSuppression : public ngraph::op::v4::NonMaxSuppression {
public:
static constexpr NodeTypeInfo type_info{"StaticShapeStaticShapeNonMaxSuppression", 0};
const NodeTypeInfo& get_type_info() const override { return type_info; }

View File

@ -20,9 +20,9 @@ StaticShapeNonMaxSuppression::StaticShapeNonMaxSuppression(
const StaticShapeNonMaxSuppression::BoxEncodingType box_encoding,
const bool sort_result_descending,
const element::Type& output_type)
: ngraph::op::v3::NonMaxSuppression({
: ngraph::op::v4::NonMaxSuppression(
boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold,
box_encoding, sort_result_descending, output_type}) {
box_encoding, sort_result_descending, output_type) {
constructor_validate_and_infer_types();
}
@ -32,12 +32,12 @@ StaticShapeNonMaxSuppression::StaticShapeNonMaxSuppression(
const StaticShapeNonMaxSuppression::BoxEncodingType box_encoding,
const bool sort_result_descending,
const element::Type& output_type)
: ngraph::op::v3::NonMaxSuppression({boxes,
: ngraph::op::v4::NonMaxSuppression(boxes,
scores,
ngraph::opset3::Constant::create(element::i64, Shape{}, {0}),
ngraph::opset3::Constant::create(element::f32, Shape{}, {.0f}),
ngraph::opset3::Constant::create(element::f32, Shape{}, {.0f}),
box_encoding, sort_result_descending, output_type}) {
box_encoding, sort_result_descending, output_type) {
constructor_validate_and_infer_types();
}
@ -64,7 +64,7 @@ StaticShapeNonMaxSuppression::clone_with_new_inputs(const OutputVector& new_args
}
void StaticShapeNonMaxSuppression::validate_and_infer_types() {
ngraph::op::v3::NonMaxSuppression::validate_and_infer_types();
ngraph::op::v4::NonMaxSuppression::validate_and_infer_types();
const auto out_shape = this->get_output_partial_shape(0);
NODE_VALIDATION_CHECK(this, out_shape.is_static(),

View File

@ -66,7 +66,7 @@ const Transformations& getDefaultTransformations() {
{ngraph::opset3::Equal::type_info, dynamicToStaticShapeBinaryEltwise},
{ngraph::opset3::Greater::type_info, dynamicToStaticShapeBinaryEltwise},
{ngraph::opset3::Power::type_info, dynamicToStaticShapeBinaryEltwise},
{ngraph::op::v4::NonMaxSuppression::type_info, dynamicToStaticNonMaxSuppression},
{ngraph::op::dynamic::NonMaxSuppression::type_info, dynamicToStaticNonMaxSuppression},
{ngraph::opset3::NonZero::type_info, dynamicToStaticShapeNonZero},
{ngraph::opset3::TopK::type_info, dynamicToStaticShapeTopK},
{ngraph::opset3::Transpose::type_info, dynamicToStaticShapeTranspose},

View File

@ -16,25 +16,25 @@
namespace vpu {
void dynamicToStaticNonMaxSuppression(std::shared_ptr<ngraph::Node> node) {
auto nms_4 = std::dynamic_pointer_cast<ngraph::op::v4::NonMaxSuppression>(node);
VPU_THROW_UNLESS(nms_4, "dynamicToStaticNonMaxSuppression transformation for {} of type {} expects {} as node for replacement",
node->get_friendly_name(), node->get_type_info(), ngraph::op::v4::NonMaxSuppression::type_info);
auto nms_dynamic = std::dynamic_pointer_cast<ngraph::op::dynamic::NonMaxSuppression>(node);
VPU_THROW_UNLESS(nms_dynamic, "dynamicToStaticNonMaxSuppression transformation for {} of type {} expects {} as node for replacement",
node->get_friendly_name(), node->get_type_info(), ngraph::op::dynamic::NonMaxSuppression::type_info);
auto staticShapeNMS = std::make_shared<ngraph::vpu::op::StaticShapeNonMaxSuppression>(
nms_4->input_value(0),
nms_4->input_value(1),
nms_4->input_value(2),
nms_4->input_value(3),
nms_4->input_value(4),
nms_4->get_box_encoding(),
nms_4->get_sort_result_descending(),
nms_4->get_output_type());
nms_dynamic->input_value(0),
nms_dynamic->input_value(1),
nms_dynamic->input_value(2),
nms_dynamic->input_value(3),
nms_dynamic->input_value(4),
nms_dynamic->get_box_encoding(),
nms_dynamic->get_sort_result_descending(),
nms_dynamic->get_output_type());
auto dynamicShapeResolver = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(
staticShapeNMS->output(0), staticShapeNMS->output(1));
dynamicShapeResolver->set_friendly_name(nms_4->get_friendly_name());
dynamicShapeResolver->set_friendly_name(nms_dynamic->get_friendly_name());
ngraph::replace_node(std::move(nms_4), std::move(dynamicShapeResolver));
ngraph::replace_node(std::move(nms_dynamic), std::move(dynamicShapeResolver));
}
} // namespace vpu

View File

@ -0,0 +1,141 @@
// 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 <ngraph_ops/nms_ie.hpp>
#include <transformations/convert_opset1_to_legacy/convert_nms_4_to_legacy.hpp>
#include <transformations/init_node_info.hpp>
#include <transformations/utils/utils.hpp>
#include "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::InitNodeInfo().run_on_function(f);
pass::ConvertNMS4ToLegacy().run_on_function(f);
ASSERT_NO_THROW(check_rt_info(f));
ASSERT_TRUE(f->get_output_partial_shape(0).is_static()) << "Shape " << f->get_output_partial_shape(0) << " should be static";
}
{
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);
auto convert = std::make_shared<ngraph::opset4::Convert>(nms, element::i64);
convert->set_friendly_name("nms");
f_ref = std::make_shared<Function>(NodeVector{convert}, 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::i32);
f = std::make_shared<Function>(NodeVector{nms}, ParameterVector{boxes, scores});
pass::InitNodeInfo().run_on_function(f);
pass::ConvertNMS4ToLegacy().run_on_function(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;
}
TEST(TransformationTests, 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::InitNodeInfo().run_on_function(f);
pass::ConvertNMS4ToLegacy().run_on_function(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;
}

View File

@ -56,7 +56,7 @@ protected:
const auto dims = std::make_shared<ngraph::opset3::Parameter>(ngraph::element::i64, ngraph::Shape{3});
const auto dsr = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(scores, dims);
const auto node = std::make_shared<ngraph::op::v4::NonMaxSuppression>(
const auto node = std::make_shared<ngraph::op::dynamic::NonMaxSuppression>(
boxes, dsr, max_output_boxes_per_class, iou_threshold, score_threshold);
auto outputShape = node->get_output_partial_shape(0);

View File

@ -48,12 +48,12 @@ protected:
const auto dims = std::make_shared<ngraph::opset3::Parameter>(ngraph::element::i64, ngraph::Shape{3});
const auto dsr = std::make_shared<ngraph::vpu::op::DynamicShapeResolver>(scores, dims);
const auto node = std::make_shared<ngraph::op::v4::NonMaxSuppression>(
const auto node = std::make_shared<ngraph::op::dynamic::NonMaxSuppression>(
boxes, dsr, max_output_boxes_per_class, iou_threshold, score_threshold);
const auto result = std::make_shared<ngraph::opset3::Result>(node);
function = std::make_shared<ngraph::Function>(ngraph::ResultVector{result},
ngraph::ParameterVector{boxes, scores, dims}, "DSR-v4::NMS");
ngraph::ParameterVector{boxes, scores, dims}, "DSR-dynamic::NMS");
}
};

View File

@ -29,10 +29,10 @@ class NonMaxSuppression(Op):
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'type': __class__.op,
'op': __class__.op,
'version': 'opset3',
'infer': __class__.infer,
'type': self.op,
'op': self.op,
'version': 'opset4',
'infer': self.infer,
'output_type': np.int64,
'center_point_box': 0,
'box_encoding': 'corner',
@ -45,18 +45,15 @@ class NonMaxSuppression(Op):
}
super().__init__(graph, mandatory_props, attrs)
def supported_attrs(self):
if self.ir_version < 10:
return ['center_point_box']
def backend_attrs(self):
version = self.get_opset()
if version in ['opset3', 'opset4']:
return ['sort_result_descending', 'box_encoding',
('output_type', lambda node: np_data_type_to_destination_type(node.output_type))]
elif version == 'opset1':
return ['sort_result_descending', 'box_encoding']
else:
version = self.get_opset()
if version == 'opset3':
return ['sort_result_descending', 'box_encoding',
('output_type', lambda node: np_data_type_to_destination_type(node.output_type))]
elif version == 'opset1':
return ['sort_result_descending', 'box_encoding']
else:
raise Error('Unsupported operation opset version "{}"'.format(version))
raise Error('Unsupported operation opset version "{}"'.format(version))
@staticmethod
def infer(node: Node):
@ -76,12 +73,15 @@ class NonMaxSuppression(Op):
num_input_boxes = boxes_shape[1]
assert scores_shape[2] == num_input_boxes, 'Number of boxes mismatch'
max_number_of_boxes = min(num_input_boxes, boxes_shape[0] * max_output_boxes_per_class * num_classes)
if node.get_opset() == 'opset4':
max_number_of_boxes = min(num_input_boxes, max_output_boxes_per_class) * boxes_shape[0] * num_classes
else:
max_number_of_boxes = min(num_input_boxes, boxes_shape[0] * max_output_boxes_per_class * num_classes)
node.out_port(0).data.set_shape(int64_array([max_number_of_boxes, 3]))
@staticmethod
def type_infer(node):
if node.get_opset() == 'opset3':
if node.get_opset() in ['opset3', 'opset4']:
node.out_port(0).set_data_type(node.output_type)
else:
node.out_port(0).set_data_type(np.int64)

View File

@ -21,8 +21,7 @@ import numpy as np
from extensions.ops.non_max_suppression import NonMaxSuppression
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Node
from mo.utils.unittest.graph import build_graph, regular_op_with_shaped_data, valued_const_with_data, result, \
connect, FakeAttr
from mo.utils.unittest.graph import build_graph, regular_op_with_shaped_data, valued_const_with_data, result, connect
class TestNonMaxSuppressionInfer(unittest.TestCase):
@ -30,7 +29,7 @@ class TestNonMaxSuppressionInfer(unittest.TestCase):
nodes = {
**regular_op_with_shaped_data('boxes', [10, 100, 4], {'type': 'Parameter'}),
**regular_op_with_shaped_data('scores', [10, 5, 100], {'type': 'Parameter'}),
**valued_const_with_data('max_output_per_class', int64_array(10)),
**valued_const_with_data('max_output_per_class', int64_array(7)),
**regular_op_with_shaped_data('nms', None, {'op': 'NonMaxSuppression', 'type': 'NonMaxSuppression',
'name': 'nms'}),
**result('output'),
@ -43,9 +42,7 @@ class TestNonMaxSuppressionInfer(unittest.TestCase):
*connect('nms', 'output'),
], nodes_with_edges_only=True)
def test_nms_infer_v10_opset1(self):
self.graph.graph['cmd_params'] = FakeAttr(ir_version=10)
def test_nms_infer_opset1(self):
nms_node = Node(self.graph, 'nms')
nms_node['version'] = 'opset1'
NonMaxSuppression.infer(nms_node)
@ -54,9 +51,7 @@ class TestNonMaxSuppressionInfer(unittest.TestCase):
self.assertTrue(np.array_equal(nms_node.out_port(0).data.get_shape(), [100, 3]))
self.assertTrue(nms_node.out_port(0).get_data_type() == np.int64)
def test_nms_infer_v10_i64_opset3(self):
self.graph.graph['cmd_params'] = FakeAttr(ir_version=10)
def test_nms_infer_i64_opset3(self):
nms_node = Node(self.graph, 'nms')
nms_node['version'] = 'opset3'
nms_node['output_type'] = np.int64
@ -66,9 +61,7 @@ class TestNonMaxSuppressionInfer(unittest.TestCase):
self.assertTrue(np.array_equal(nms_node.out_port(0).data.get_shape(), [100, 3]))
self.assertTrue(nms_node.out_port(0).get_data_type() == np.int64)
def test_nms_infer_v10_i32_opset3(self):
self.graph.graph['cmd_params'] = FakeAttr(ir_version=10)
def test_nms_infer_i32_opset3(self):
nms_node = Node(self.graph, 'nms')
nms_node['version'] = 'opset3'
nms_node['output_type'] = np.int32
@ -77,3 +70,23 @@ class TestNonMaxSuppressionInfer(unittest.TestCase):
self.assertTrue(np.array_equal(nms_node.out_port(0).data.get_shape(), [100, 3]))
self.assertTrue(nms_node.out_port(0).get_data_type() == np.int32)
def test_nms_infer_i32_opset4(self):
nms_node = Node(self.graph, 'nms')
nms_node['version'] = 'opset4'
nms_node['output_type'] = np.int32
NonMaxSuppression.infer(nms_node)
NonMaxSuppression.type_infer(nms_node)
self.assertTrue(np.array_equal(nms_node.out_port(0).data.get_shape(), [10 * 5 * 7, 3]))
self.assertTrue(nms_node.out_port(0).get_data_type() == np.int32)
def test_nms_infer_i64_opset4(self):
nms_node = Node(self.graph, 'nms')
nms_node['version'] = 'opset4'
nms_node['output_type'] = np.int64
NonMaxSuppression.infer(nms_node)
NonMaxSuppression.type_infer(nms_node)
self.assertTrue(np.array_equal(nms_node.out_port(0).data.get_shape(), [10 * 5 * 7, 3]))
self.assertTrue(nms_node.out_port(0).get_data_type() == np.int64)

View File

@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional
from _pyngraph import NodeFactory as _NodeFactory
from ngraph.impl import Node
DEFAULT_OPSET = "opset3"
DEFAULT_OPSET = "opset4"
class NodeFactory(object):

View File

@ -90,6 +90,7 @@ namespace
{"opset1", OpsetFunction(ngraph::get_opset1)},
{"opset2", OpsetFunction(ngraph::get_opset2)},
{"opset3", OpsetFunction(ngraph::get_opset3)},
{"opset4", OpsetFunction(ngraph::get_opset4)},
};
auto it = s_opsets.find(opset_ver);

View File

@ -178,7 +178,6 @@ void op::v1::NonMaxSuppression::validate_and_infer_types()
out_shape[0] = std::min(num_boxes, max_output_boxes_per_class * num_classes);
}
set_output_size(1);
set_output_type(0, output_element_type, out_shape);
}
@ -291,7 +290,7 @@ bool ngraph::op::v3::NonMaxSuppression::visit_attributes(AttributeVisitor& visit
return true;
}
void op::v3::NonMaxSuppression::validate_and_infer_types()
void op::v3::NonMaxSuppression::validate()
{
const auto boxes_ps = get_input_partial_shape(0);
const auto scores_ps = get_input_partial_shape(1);
@ -300,13 +299,8 @@ void op::v3::NonMaxSuppression::validate_and_infer_types()
m_output_type == element::i64 || m_output_type == element::i32,
"Output type must be i32 or i64");
// NonMaxSuppression produces triplets
// that have the following format: [batch_index, class_index, box_index]
PartialShape out_shape = {Dimension::dynamic(), 3};
if (boxes_ps.is_dynamic() || scores_ps.is_dynamic())
{
set_output_type(0, m_output_type, out_shape);
return;
}
@ -372,16 +366,32 @@ void op::v3::NonMaxSuppression::validate_and_infer_types()
boxes_ps[2].is_static() && boxes_ps[2].get_length() == 4u,
"The last dimension of the 'boxes' input must be equal to 4. Got:",
boxes_ps[2]);
}
const auto max_output_boxes_per_class = input_value(2).get_node_shared_ptr();
if (num_boxes_boxes.is_static() && scores_ps[1].is_static() &&
max_output_boxes_per_class->is_constant())
void op::v3::NonMaxSuppression::validate_and_infer_types()
{
const auto boxes_ps = get_input_partial_shape(0);
const auto scores_ps = get_input_partial_shape(1);
// NonMaxSuppression produces triplets
// that have the following format: [batch_index, class_index, box_index]
PartialShape out_shape = {Dimension::dynamic(), 3};
validate();
if (boxes_ps.rank().is_static() && scores_ps.rank().is_static())
{
const auto num_boxes = num_boxes_boxes.get_length();
const auto max_output_boxes_per_class = max_boxes_output_from_input();
const auto num_classes = scores_ps[1].get_length();
const auto num_boxes_boxes = boxes_ps[1];
const auto max_output_boxes_per_class_node = input_value(2).get_node_shared_ptr();
if (num_boxes_boxes.is_static() && scores_ps[1].is_static() &&
max_output_boxes_per_class_node->is_constant())
{
const auto num_boxes = num_boxes_boxes.get_length();
const auto num_classes = scores_ps[1].get_length();
const auto max_output_boxes_per_class = max_boxes_output_from_input();
out_shape[0] = std::min(num_boxes, max_output_boxes_per_class * num_classes);
out_shape[0] = std::min(num_boxes, max_output_boxes_per_class * num_classes);
}
}
set_output_type(0, m_output_type, out_shape);
}
@ -433,14 +443,14 @@ op::v4::NonMaxSuppression::NonMaxSuppression(
const op::v4::NonMaxSuppression::BoxEncodingType box_encoding,
const bool sort_result_descending,
const element::Type& output_type)
: op::v3::NonMaxSuppression({boxes,
scores,
max_output_boxes_per_class,
iou_threshold,
score_threshold,
box_encoding,
sort_result_descending,
output_type})
: op::v3::NonMaxSuppression(boxes,
scores,
max_output_boxes_per_class,
iou_threshold,
score_threshold,
box_encoding,
sort_result_descending,
output_type)
{
constructor_validate_and_infer_types();
}
@ -451,14 +461,14 @@ op::v4::NonMaxSuppression::NonMaxSuppression(
const op::v4::NonMaxSuppression::BoxEncodingType box_encoding,
const bool sort_result_descending,
const element::Type& output_type)
: op::v3::NonMaxSuppression({boxes,
scores,
op::Constant::create(element::i64, Shape{}, {0}),
op::Constant::create(element::f32, Shape{}, {.0f}),
op::Constant::create(element::f32, Shape{}, {.0f}),
box_encoding,
sort_result_descending,
output_type})
: op::v3::NonMaxSuppression(boxes,
scores,
op::Constant::create(element::i64, Shape{}, {0}),
op::Constant::create(element::f32, Shape{}, {.0f}),
op::Constant::create(element::f32, Shape{}, {.0f}),
box_encoding,
sort_result_descending,
output_type)
{
constructor_validate_and_infer_types();
}
@ -492,6 +502,106 @@ shared_ptr<Node>
}
void op::v4::NonMaxSuppression::validate_and_infer_types()
{
const auto boxes_ps = get_input_partial_shape(0);
const auto scores_ps = get_input_partial_shape(1);
// NonMaxSuppression produces triplets
// that have the following format: [batch_index, class_index, box_index]
PartialShape out_shape = {Dimension::dynamic(), 3};
op::v3::NonMaxSuppression::validate();
if (boxes_ps.rank().is_static() && scores_ps.rank().is_static())
{
const auto num_boxes_boxes = boxes_ps[1];
const auto max_output_boxes_per_class_node = input_value(2).get_node_shared_ptr();
if (num_boxes_boxes.is_static() && scores_ps[0].is_static() && scores_ps[1].is_static() &&
max_output_boxes_per_class_node->is_constant())
{
const auto num_boxes = num_boxes_boxes.get_length();
const auto num_classes = scores_ps[1].get_length();
const auto max_output_boxes_per_class = max_boxes_output_from_input();
out_shape[0] = std::min(num_boxes, max_output_boxes_per_class) * num_classes *
scores_ps[0].get_length();
}
}
set_output_type(0, m_output_type, out_shape);
}
// ------------------------------ dynamic ------------------------------
constexpr NodeTypeInfo op::dynamic::NonMaxSuppression::type_info;
op::dynamic::NonMaxSuppression::NonMaxSuppression(
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 op::dynamic::NonMaxSuppression::BoxEncodingType box_encoding,
const bool sort_result_descending,
const element::Type& output_type)
: op::v3::NonMaxSuppression(boxes,
scores,
max_output_boxes_per_class,
iou_threshold,
score_threshold,
box_encoding,
sort_result_descending,
output_type)
{
constructor_validate_and_infer_types();
}
op::dynamic::NonMaxSuppression::NonMaxSuppression(
const Output<Node>& boxes,
const Output<Node>& scores,
const op::dynamic::NonMaxSuppression::BoxEncodingType box_encoding,
const bool sort_result_descending,
const element::Type& output_type)
: op::v3::NonMaxSuppression(boxes,
scores,
op::Constant::create(element::i64, Shape{}, {0}),
op::Constant::create(element::f32, Shape{}, {.0f}),
op::Constant::create(element::f32, Shape{}, {.0f}),
box_encoding,
sort_result_descending,
output_type)
{
constructor_validate_and_infer_types();
}
shared_ptr<Node>
op::dynamic::NonMaxSuppression::clone_with_new_inputs(const OutputVector& new_args) const
{
check_new_args_count(this, new_args);
NODE_VALIDATION_CHECK(this,
new_args.size() >= 2 && new_args.size() <= 5,
"Number of inputs must be 2, 3, 4 or 5");
const auto& arg2 = new_args.size() > 2
? new_args.at(2)
: ngraph::op::Constant::create(element::i32, 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});
return std::make_shared<op::dynamic::NonMaxSuppression>(new_args.at(0),
new_args.at(1),
arg2,
arg3,
arg4,
m_box_encoding,
m_sort_result_descending,
m_output_type);
}
void op::dynamic::NonMaxSuppression::validate_and_infer_types()
{
op::v3::NonMaxSuppression::validate_and_infer_types();

View File

@ -177,8 +177,7 @@ namespace ngraph
BoxEncodingType m_box_encoding = BoxEncodingType::CORNER;
bool m_sort_result_descending = true;
ngraph::element::Type m_output_type = ngraph::element::i64;
private:
void validate();
int64_t max_boxes_output_from_input() const;
};
} // namespace v3
@ -236,6 +235,60 @@ namespace ngraph
clone_with_new_inputs(const OutputVector& new_args) const override;
};
} // namespace v4
namespace dynamic
{
/// \brief NonMaxSuppression operation
///
class NGRAPH_API NonMaxSuppression : public op::v3::NonMaxSuppression
{
public:
static constexpr NodeTypeInfo type_info{"NonMaxSuppression", 99};
const NodeTypeInfo& get_type_info() const override { return type_info; }
NonMaxSuppression() = default;
/// \brief Constructs a NonMaxSuppression operation.
///
/// \param boxes Node producing the box coordinates
/// \param scores Node producing the box scores
/// \param max_output_boxes_per_class Node producing maximum number of boxes to be
/// selected per class
/// \param iou_threshold Node producing intersection over union threshold
/// \param score_threshold Node producing minimum score threshold
/// \param box_encoding Specifies the format of boxes data encoding
/// \param sort_result_descending Specifies whether it is necessary to sort selected
/// boxes across batches
/// \param output_type Specifies the output tensor type
NonMaxSuppression(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 BoxEncodingType box_encoding = BoxEncodingType::CORNER,
const bool sort_result_descending = true,
const ngraph::element::Type& output_type = ngraph::element::i64);
/// \brief Constructs a NonMaxSuppression operation with default values for the last
/// 3 inputs
///
/// \param boxes Node producing the box coordinates
/// \param scores Node producing the box coordinates
/// \param box_encoding Specifies the format of boxes data encoding
/// \param sort_result_descending Specifies whether it is necessary to sort selected
/// boxes across batches
/// \param output_type Specifies the output tensor type
NonMaxSuppression(const Output<Node>& boxes,
const Output<Node>& scores,
const BoxEncodingType box_encoding = BoxEncodingType::CORNER,
const bool sort_result_descending = true,
const ngraph::element::Type& output_type = ngraph::element::i64);
void validate_and_infer_types() override;
std::shared_ptr<Node>
clone_with_new_inputs(const OutputVector& new_args) const override;
};
} // namespace dynamic
} // namespace op
NGRAPH_API

View File

@ -118,6 +118,25 @@ const ngraph::OpSet& ngraph::get_opset3()
return opset;
}
const ngraph::OpSet& ngraph::get_opset4()
{
static std::mutex init_mutex;
static bool opset_is_initialized = false;
static OpSet opset;
if (!opset_is_initialized)
{
std::lock_guard<std::mutex> guard(init_mutex);
if (!opset_is_initialized)
{
#define NGRAPH_OP(NAME, NAMESPACE) opset.insert<NAMESPACE::NAME>();
#include "ngraph/opsets/opset4_tbl.hpp"
#undef NGRAPH_OP
opset_is_initialized = true;
}
}
return opset;
}
const ngraph::OpSet& ngraph::get_ie_opset()
{
static std::mutex init_mutex;
@ -132,6 +151,7 @@ const ngraph::OpSet& ngraph::get_ie_opset()
#include "ngraph/opsets/opset1_tbl.hpp"
#include "ngraph/opsets/opset2_tbl.hpp"
#include "ngraph/opsets/opset3_tbl.hpp"
#include "ngraph/opsets/opset4_tbl.hpp"
#undef NGRAPH_OP
opset_is_initialized = true;
}

View File

@ -132,6 +132,7 @@ namespace ngraph
const NGRAPH_API OpSet& get_opset1();
const NGRAPH_API OpSet& get_opset2();
const NGRAPH_API OpSet& get_opset3();
const NGRAPH_API OpSet& get_opset4();
// Every op after opset0
const NGRAPH_API OpSet& get_ie_opset();
}

View File

@ -0,0 +1,29 @@
//*****************************************************************************
// 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 "ngraph/ops.hpp"
namespace ngraph
{
namespace opset4
{
#define NGRAPH_OP(a, b) using b::a;
#include "ngraph/opsets/opset4_tbl.hpp"
#undef NGRAPH_OP
}
}

View File

@ -0,0 +1,154 @@
//*****************************************************************************
// 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.
//*****************************************************************************
#ifndef NGRAPH_OP
#warning "NGRAPH_OP not defined"
#define NGRAPH_OP(x, y)
#endif
NGRAPH_OP(Abs, ngraph::op::v0)
NGRAPH_OP(Acos, ngraph::op::v0)
NGRAPH_OP(Add, ngraph::op::v1)
NGRAPH_OP(Asin, ngraph::op::v0)
NGRAPH_OP(Atan, ngraph::op::v0)
NGRAPH_OP(AvgPool, ngraph::op::v1)
NGRAPH_OP(BatchNormInference, ngraph::op::v0)
NGRAPH_OP(BinaryConvolution, ngraph::op::v1)
NGRAPH_OP(Broadcast, ngraph::op::v3)
NGRAPH_OP(Bucketize, ngraph::op::v3)
NGRAPH_OP(CTCGreedyDecoder, ngraph::op::v0)
NGRAPH_OP(Ceiling, ngraph::op::v0)
NGRAPH_OP(Clamp, ngraph::op::v0)
NGRAPH_OP(Concat, ngraph::op::v0)
NGRAPH_OP(Constant, ngraph::op)
NGRAPH_OP(Convert, ngraph::op::v0)
NGRAPH_OP(ConvertLike, ngraph::op::v1)
NGRAPH_OP(Convolution, ngraph::op::v1)
NGRAPH_OP(ConvolutionBackpropData, ngraph::op::v1)
NGRAPH_OP(Cos, ngraph::op::v0)
NGRAPH_OP(Cosh, ngraph::op::v0)
NGRAPH_OP(CumSum, ngraph::op::v0)
NGRAPH_OP(DeformableConvolution, ngraph::op::v1)
NGRAPH_OP(DeformablePSROIPooling, ngraph::op::v1)
NGRAPH_OP(DepthToSpace, ngraph::op::v0)
NGRAPH_OP(DetectionOutput, ngraph::op::v0)
NGRAPH_OP(Divide, ngraph::op::v1)
NGRAPH_OP(Elu, ngraph::op::v0)
NGRAPH_OP(Erf, ngraph::op::v0)
NGRAPH_OP(Equal, ngraph::op::v1)
NGRAPH_OP(Exp, ngraph::op::v0)
NGRAPH_OP(ExtractImagePatches, ngraph::op::v3)
NGRAPH_OP(FakeQuantize, ngraph::op::v0)
NGRAPH_OP(Floor, ngraph::op::v0)
NGRAPH_OP(FloorMod, ngraph::op::v1)
NGRAPH_OP(Gather, ngraph::op::v1)
NGRAPH_OP(GatherTree, ngraph::op::v1)
NGRAPH_OP(Greater, ngraph::op::v1)
NGRAPH_OP(GreaterEqual, ngraph::op::v1)
NGRAPH_OP(GroupConvolution, ngraph::op::v1)
NGRAPH_OP(GroupConvolutionBackpropData, ngraph::op::v1)
NGRAPH_OP(GRN, ngraph::op::v0)
NGRAPH_OP(HardSigmoid, ngraph::op::v0)
NGRAPH_OP(Interpolate, ngraph::op::v0)
NGRAPH_OP(Less, ngraph::op::v1)
NGRAPH_OP(LessEqual, ngraph::op::v1)
NGRAPH_OP(Log, ngraph::op::v0)
NGRAPH_OP(LogicalAnd, ngraph::op::v1)
NGRAPH_OP(LogicalNot, ngraph::op::v1)
NGRAPH_OP(LogicalOr, ngraph::op::v1)
NGRAPH_OP(LogicalXor, ngraph::op::v1)
NGRAPH_OP(LRN, ngraph::op::v0)
NGRAPH_OP(LSTMCell, ngraph::op::v0)
NGRAPH_OP(LSTMSequence, ngraph::op::v0)
NGRAPH_OP(MatMul, ngraph::op::v0)
NGRAPH_OP(MaxPool, ngraph::op::v1)
NGRAPH_OP(Maximum, ngraph::op::v1)
NGRAPH_OP(Minimum, ngraph::op::v1)
NGRAPH_OP(Mod, ngraph::op::v1)
NGRAPH_OP(Multiply, ngraph::op::v1)
NGRAPH_OP(MVN, ngraph::op::v0)
NGRAPH_OP(Negative, ngraph::op::v0)
NGRAPH_OP(NormalizeL2, ngraph::op::v0)
NGRAPH_OP(NotEqual, ngraph::op::v1)
NGRAPH_OP(OneHot, ngraph::op::v1)
NGRAPH_OP(PRelu, ngraph::op::v0)
NGRAPH_OP(PSROIPooling, ngraph::op::v0)
NGRAPH_OP(Pad, ngraph::op::v1)
NGRAPH_OP(Parameter, ngraph::op::v0)
NGRAPH_OP(Power, ngraph::op::v1)
NGRAPH_OP(PriorBox, ngraph::op::v0)
NGRAPH_OP(PriorBoxClustered, ngraph::op::v0)
NGRAPH_OP(Proposal, ngraph::op::v0)
NGRAPH_OP(Range, ngraph::op::v0)
NGRAPH_OP(Relu, ngraph::op::v0)
NGRAPH_OP(ReduceMax, ngraph::op::v1)
NGRAPH_OP(ReduceLogicalAnd, ngraph::op::v1)
NGRAPH_OP(ReduceLogicalOr, ngraph::op::v1)
NGRAPH_OP(ReduceMean, ngraph::op::v1)
NGRAPH_OP(ReduceMin, ngraph::op::v1)
NGRAPH_OP(ReduceProd, ngraph::op::v1)
NGRAPH_OP(ReduceSum, ngraph::op::v1)
NGRAPH_OP(RegionYolo, ngraph::op::v0)
NGRAPH_OP(ReorgYolo, ngraph::op::v0)
NGRAPH_OP(Reshape, ngraph::op::v1)
NGRAPH_OP(Result, ngraph::op::v0)
NGRAPH_OP(ReverseSequence, ngraph::op::v0)
NGRAPH_OP(ROIPooling, ngraph::op::v0)
NGRAPH_OP(Select, ngraph::op::v1)
NGRAPH_OP(Selu, ngraph::op::v0)
NGRAPH_OP(Sign, ngraph::op::v0)
NGRAPH_OP(Sigmoid, ngraph::op::v0)
NGRAPH_OP(Sin, ngraph::op::v0)
NGRAPH_OP(Sinh, ngraph::op::v0)
NGRAPH_OP(Softmax, ngraph::op::v1)
NGRAPH_OP(Sqrt, ngraph::op::v0)
NGRAPH_OP(SpaceToDepth, ngraph::op::v0)
NGRAPH_OP(Split, ngraph::op::v1)
NGRAPH_OP(SquaredDifference, ngraph::op::v0)
NGRAPH_OP(Squeeze, ngraph::op::v0)
NGRAPH_OP(StridedSlice, ngraph::op::v1)
NGRAPH_OP(Subtract, ngraph::op::v1)
NGRAPH_OP(Tan, ngraph::op::v0)
NGRAPH_OP(Tanh, ngraph::op::v0)
NGRAPH_OP(TensorIterator, ngraph::op::v0)
NGRAPH_OP(Tile, ngraph::op::v0)
NGRAPH_OP(Transpose, ngraph::op::v1)
NGRAPH_OP(Unsqueeze, ngraph::op::v0)
NGRAPH_OP(VariadicSplit, ngraph::op::v1)
// New operations added in opset2
NGRAPH_OP(Gelu, ngraph::op::v0)
NGRAPH_OP(BatchToSpace, ngraph::op::v1)
NGRAPH_OP(SpaceToBatch, ngraph::op::v1)
// New operations added in opset3
NGRAPH_OP(EmbeddingBagPackedSum, ngraph::op::v3)
NGRAPH_OP(EmbeddingSegmentsSum, ngraph::op::v3)
NGRAPH_OP(EmbeddingBagOffsetsSum, ngraph::op::v3)
NGRAPH_OP(GRUCell, ngraph::op::v3)
NGRAPH_OP(NonZero, ngraph::op::v3)
NGRAPH_OP(RNNCell, ngraph::op::v0)
NGRAPH_OP(ROIAlign, ngraph::op::v3)
NGRAPH_OP(ScatterElementsUpdate, ngraph::op::v3)
NGRAPH_OP(ScatterUpdate, ngraph::op::v3)
NGRAPH_OP(ShuffleChannels, ngraph::op::v0)
NGRAPH_OP(ShapeOf, ngraph::op::v3)
NGRAPH_OP(Assign, ngraph::op::v3)
NGRAPH_OP(ReadValue, ngraph::op::v3)
NGRAPH_OP(TopK, ngraph::op::v3)
// New operations added in opset4
NGRAPH_OP(NonMaxSuppression, ngraph::op::v4)

View File

@ -364,3 +364,186 @@ TEST(type_prop, nms_v3_dynamic_boxes_and_scores)
ASSERT_TRUE(
nms->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
}
// ------------------------------ V4 ------------------------------
TEST(type_prop, nms_v4_incorrect_boxes_rank)
{
try
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
make_shared<op::v4::NonMaxSuppression>(boxes, scores);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(), "Expected a 3D tensor for the 'boxes' input");
}
}
TEST(type_prop, nms_v4_incorrect_scores_rank)
{
try
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{1, 2});
make_shared<op::v4::NonMaxSuppression>(boxes, scores);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(), "Expected a 3D tensor for the 'scores' input");
}
}
TEST(type_prop, nms_v4_incorrect_scheme_num_batches)
{
try
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{2, 2, 3});
make_shared<op::v4::NonMaxSuppression>(boxes, scores);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"The first dimension of both 'boxes' and 'scores' must match");
}
}
TEST(type_prop, nms_v4_incorrect_scheme_num_boxes)
{
try
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
make_shared<op::v4::NonMaxSuppression>(boxes, scores);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"'boxes' and 'scores' input shapes must match at the second and third "
"dimension respectively");
}
}
TEST(type_prop, nms_v4_scalar_inputs_check)
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{1, 2, 4});
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});
try
{
make_shared<op::v4::NonMaxSuppression>(boxes, scores, non_scalar, scalar, scalar);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Expected a scalar for the 'max_output_boxes_per_class' input");
}
try
{
make_shared<op::v4::NonMaxSuppression>(boxes, scores, scalar, non_scalar, scalar);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(), "Expected a scalar for the 'iou_threshold' input");
}
try
{
make_shared<op::v4::NonMaxSuppression>(boxes, scores, scalar, scalar, non_scalar);
}
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(error.what(), "Expected a scalar for the 'score_threshold' input");
}
}
TEST(type_prop, nms_v4_output_shape)
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{5, 2, 4});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{5, 3, 2});
const auto nms = make_shared<op::v4::NonMaxSuppression>(boxes, scores);
const auto nms_out_ps = nms->get_output_partial_shape(0);
EXPECT_TRUE(nms_out_ps.rank().is_static());
EXPECT_EQ(nms_out_ps.rank().get_length(), 2);
EXPECT_EQ(nms->get_shape(), (Shape{0, 3}));
}
TEST(type_prop, nms_v4_output_shape_2)
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{2, 7, 4});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{2, 5, 7});
const auto max_output_boxes_per_class = op::Constant::create(element::i32, Shape{}, {3});
const auto iou_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto score_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto nms = make_shared<op::v4::NonMaxSuppression>(
boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold);
ASSERT_EQ(nms->get_element_type(), element::i64);
ASSERT_EQ(nms->get_shape(), (Shape{2 * 5 * 3, 3}));
}
TEST(type_prop, nms_v4_output_shape_3)
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{2, 7, 4});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{2, 5, 7});
const auto max_output_boxes_per_class = op::Constant::create(element::i16, Shape{}, {1000});
const auto iou_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto score_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto nms = make_shared<op::v4::NonMaxSuppression>(
boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold);
ASSERT_EQ(nms->get_element_type(), element::i64);
ASSERT_EQ(nms->get_shape(), (Shape{2 * 5 * 7, 3}));
}
TEST(type_prop, nms_v4_output_shape_i32)
{
const auto boxes = make_shared<op::Parameter>(element::f32, Shape{2, 7, 4});
const auto scores = make_shared<op::Parameter>(element::f32, Shape{2, 5, 7});
const auto max_output_boxes_per_class = op::Constant::create(element::i16, Shape{}, {3});
const auto iou_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto score_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto nms =
make_shared<op::v4::NonMaxSuppression>(boxes,
scores,
max_output_boxes_per_class,
iou_threshold,
score_threshold,
op::v3::NonMaxSuppression::BoxEncodingType::CORNER,
true,
element::i32);
ASSERT_EQ(nms->get_element_type(), element::i32);
ASSERT_EQ(nms->get_shape(), (Shape{30, 3}));
}
TEST(type_prop, nms_v4_dynamic_boxes_and_scores)
{
const auto boxes = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
const auto scores = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
const auto max_output_boxes_per_class = op::Constant::create(element::i16, Shape{}, {3});
const auto iou_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto score_threshold = make_shared<op::Parameter>(element::f32, Shape{});
const auto nms = make_shared<op::v4::NonMaxSuppression>(
boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold);
ASSERT_EQ(nms->get_element_type(), element::i64);
ASSERT_TRUE(
nms->get_output_partial_shape(0).same_scheme(PartialShape{Dimension::dynamic(), 3}));
}