add preliminary support of Proposal-4 in nGraph (#1448)
authorPavel Esir <pavel.esir@intel.com>
Sun, 16 Aug 2020 12:49:49 +0000 (15:49 +0300)
committerGitHub <noreply@github.com>
Sun, 16 Aug 2020 12:49:49 +0000 (15:49 +0300)
renamed logits -> bbox_deltas

updated ngraph unittests for Proposal

removed validate_and_infer_types Proposal-4

removed validate_and_infer_types Proposal-4

changed validate_and_infer_types in parent class of Proposal

removed get_output_size

successfully inferred Proposal on SSH and Faster-RCNN

added unittests for Proposal-4

added unittests for Proposal-4

added unittests for Proposal-4

returned back default namespace for Proposal

reduced number of outputs in v0::Proposal

correct conversion of Proposal-4 -> propodal_ie with 2 outputs

removed creator for proposal v0

removed converter for proposal v0

added Proposal-4 to MO

removed `for_deformable` attribute

added Proposal-4 to MO and nGraph Python API

removed typo in Proposal-4 specification

style corrections

style corrections and removed some redundant code

rename proposal Python api test

removed 'attrs' context from visitor

returned back AttrVisitor to check if passes OpenVINO ONNX pipeline

Should pass OpenVINO ONNX pipeline (returned back AttrVisitor just to check)

python api for Proposal-4 works ok

(style correction) python api for Proposal-4 works ok

parametrized proposal_ie some other corrections

removed 'attrs.' context from nGraph Python API tests for Proposal

minor corrections in replacer proposal->proposal_ie

corrected Python API OpenVINO-ONNX tests should pass

Improved workaround for AttributeVisitor for Proposal

Add additional check of im_info tensor shape to Proposal node in MKLDNNPlugin

😠 removed 4 extra spaces from test_dyn_attributes.py to match The Style

added new nGraph RTTI declarations, removed throwing exception in transformation

added new nGraph RTTI declarations, removed throwing exception in transformation, corrected exception in MKLDNNplugin

corrected im_info size checking in Proposal node of MKLDNNPlugin

27 files changed:
docs/ops/detection/Proposal_4.md
inference-engine/src/inference_engine/cnn_network_ngraph_impl.cpp
inference-engine/src/legacy_api/src/convert_function_to_cnn_network.cpp
inference-engine/src/legacy_api/src/ie_cnn_layer_builder_ngraph.cpp
inference-engine/src/mkldnn_plugin/nodes/proposal.cpp
inference-engine/src/readers/ir_reader/ie_ir_parser.cpp
inference-engine/src/transformations/include/ngraph_ops/proposal_ie.hpp
inference-engine/src/transformations/include/transformations/convert_opset1_to_legacy/convert_proposal_to_proposal_ie.hpp
inference-engine/src/transformations/src/ngraph_ops/proposal_ie.cpp
inference-engine/src/transformations/src/transformations/convert_opset1_to_legacy/convert_opset1_to_legacy.cpp
inference-engine/src/transformations/src/transformations/convert_opset1_to_legacy/convert_proposal_to_proposal_ie.cpp
model-optimizer/extensions/back/ProposalMutation.py
model-optimizer/extensions/front/mxnet/custom_rpn_proposal.py
model-optimizer/extensions/ops/proposal.py
ngraph/core/include/ngraph/attribute_visitor.hpp
ngraph/core/include/ngraph/op/proposal.hpp
ngraph/core/include/ngraph/opsets/opset4_tbl.hpp
ngraph/core/src/op/proposal.cpp
ngraph/python/src/ngraph/opset1/ops.py
ngraph/python/src/ngraph/opset4/__init__.py
ngraph/python/src/ngraph/opset4/ops.py
ngraph/python/tests/test_ngraph/test_create_op.py
ngraph/python/tests/test_ngraph/test_dyn_attributes.py
ngraph/python/tests/test_ngraph/test_proposal.py [new file with mode: 0644]
ngraph/test/attributes.cpp
ngraph/test/type_prop/proposal.cpp
ngraph/test/type_prop_layers.cpp

index a2008e10f2fe36db3ff2485f3fdb7b6bb58d859b..9281b5a728baf9ff9954730ca4eb7c1b7c4f6c01 100644 (file)
@@ -149,7 +149,7 @@ the second optional tensor of shape `[batch_size * post_nms_topn]` with probabil
 
 *   **3**: 1D tensor of type *T* with 3 or 4 elements:  `[image_height, image_width, scale_height_and_width]` or `[image_height, image_width, scale_height, scale_width]`. Required.
 
-**Outputs**4
+**Outputs**
 
 *   **1**: tensor of type *T* and shape `[batch_size * post_nms_topn, 5]`.
 
index fbd4060236d635c4d53eeda8126d27053ac7e816..232d61c2d5810570eaafb3bff63746d9a0304847 100644 (file)
@@ -73,7 +73,8 @@ void CNNNetworkNGraphImpl::createDataForResult(const ::ngraph::Output<::ngraph::
         dims = output.get_shape();
     }
     for (const auto& dim : dims) {
-        if (!dim) THROW_IE_EXCEPTION << outName << " has zero dimension that is not allowable";
+        if (!dim)
+            THROW_IE_EXCEPTION << outName << " has zero dimension that is not allowable";
     }
 
     if (ptr) {
index e85d8627c589517d04b7fcdf6fb3efc45da5ee95..cced81480b2dc71800ab6cab5150f88f5edd0b3e 100644 (file)
@@ -607,7 +607,6 @@ void convertFunctionToICNNNetwork(const std::shared_ptr<const ::ngraph::Function
                 std::make_shared<Builder::NodeConverter<::ngraph::op::PadIE>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::v1::Power>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::PowerIE>>(),
-                std::make_shared<Builder::NodeConverter<::ngraph::op::Proposal>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::ProposalIE>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::Relu>>(),
                 std::make_shared<Builder::NodeConverter<::ngraph::op::SeluIE>>(),
index 72efb5ed379a6e853c0cc0e92d839aa465581cf8..abe61fb7dc90db44d437ea1d40c242df11a17fba 100644 (file)
@@ -1424,7 +1424,7 @@ CNNLayer::Ptr NodeConverter<ngraph::op::DetectionOutput>::createLayer(
 }
 
 template <>
-CNNLayer::Ptr NodeConverter<ngraph::op::Proposal>::createLayer(const std::shared_ptr<ngraph::Node>& layer) const {
+CNNLayer::Ptr NodeConverter<ngraph::op::v0::Proposal>::createLayer(const std::shared_ptr<ngraph::Node>& layer) const {
     THROW_IE_EXCEPTION << "Proposal operation should be converted to ProposalIE";
 }
 
index 4c794fe73ad06cc99c40a0c56e308da6b0d47802..49575363370e5c7ff791ee7255025ab6707436cd 100644 (file)
@@ -147,7 +147,18 @@ public:
                 p_prob_item = outputs[1]->buffer();
 
             auto dims0 = inputs[0]->getTensorDesc().getDims();
-            size_t img_info_size = inputs[2]->getTensorDesc().getDims()[1];
+            auto img_info_dims = inputs[2]->getTensorDesc().getDims();
+            if (img_info_dims.size() != 2)
+                THROW_IE_EXCEPTION << "Size of im_info tensor for Proposal is incorrect! Size of im_info must be 2. "
+                                   << "Now im_info size is " << img_info_dims.size() << ".";
+
+            if (img_info_dims[1] != 3 && img_info_dims[1] != 4)
+                THROW_IE_EXCEPTION << "Shape of im_info tensor for Proposal is incorrect! "
+                                   << "Shape of im_info must be of  [1, 3] or [1, 4]! "
+                                   << "Now shape of im_info is" << img_info_dims[0] << ", " << img_info_dims[1] << "].";
+
+            size_t img_info_size = img_info_dims[1];
+
 
             // input image height & width
             const float img_H = p_img_info_cpu[0];
@@ -155,7 +166,7 @@ public:
 
             // scale factor for height & width
             const float scale_H = p_img_info_cpu[2];
-            const float scale_W = img_info_size > 3 ? p_img_info_cpu[3] : scale_H;
+            const float scale_W = img_info_size == 4 ? p_img_info_cpu[3] : scale_H;
 
             XARCH::proposal_exec(p_bottom_item, p_d_anchor_item, dims0,
                     {img_H, img_W, scale_H, scale_W}, anchors.data(), roi_indices.data(), p_roi_item, p_prob_item, conf);
index 0f21d2ffaf377853052a34153ed688d84f208aae..32c76d4b91651272f0fb8903988281f28c575b18 100644 (file)
@@ -330,7 +330,6 @@ std::shared_ptr<ngraph::Node> V10Parser::createNode(const std::vector<ngraph::Ou
         std::make_shared<LayerCreator<ngraph::op::Range>>("Range"),
         std::make_shared<LayerCreator<ngraph::op::PriorBox>>("PriorBox"),
         std::make_shared<LayerCreator<ngraph::op::PriorBoxClustered>>("PriorBoxClustered"),
-        std::make_shared<LayerCreator<ngraph::op::Proposal>>("Proposal"),
         std::make_shared<LayerCreator<ngraph::op::v1::ReduceMax>>("ReduceMax"),
         std::make_shared<LayerCreator<ngraph::op::v1::ReduceMin>>("ReduceMin"),
         std::make_shared<LayerCreator<ngraph::op::v1::ReduceMean>>("ReduceMean"),
@@ -716,36 +715,6 @@ std::shared_ptr<ngraph::Node> V10Parser::LayerCreator<ngraph::op::PriorBoxCluste
     return std::make_shared<ngraph::op::PriorBoxClustered>(inputs[0], inputs[1], attr);
 }
 
-// Proposal layer
-template <>
-std::shared_ptr<ngraph::Node> V10Parser::LayerCreator<ngraph::op::Proposal>::createLayer(
-    const ngraph::OutputVector& inputs, const pugi::xml_node& node, std::istream& binStream,
-    const GenericLayerParams& layerParsePrms) {
-    checkParameters(inputs, layerParsePrms, 3);
-    pugi::xml_node dn = node.child("data");
-
-    if (dn.empty())
-        THROW_IE_EXCEPTION << "Cannot read parameter for " << getType() << " layer with name: " << layerParsePrms.name;
-
-    ngraph::op::ProposalAttrs attr;
-    attr.base_size = GetUIntAttr(dn, "base_size");
-    attr.pre_nms_topn = GetUIntAttr(dn, "pre_nms_topn");
-    attr.post_nms_topn = GetUIntAttr(dn, "post_nms_topn");
-    attr.nms_thresh = GetFloatAttr(dn, "nms_thresh");
-    attr.feat_stride = GetUIntAttr(dn, "feat_stride");
-    attr.min_size = GetUIntAttr(dn, "min_size");
-    attr.ratio = getParameters<float>(dn, "ratio");
-    attr.scale = getParameters<float>(dn, "scale");
-    attr.clip_after_nms = (GetIntAttr(dn, "clip_after_nms", 0) != 0);
-    attr.clip_before_nms = (GetIntAttr(dn, "clip_before_nms", 1) != 0);
-    attr.normalize = (GetIntAttr(dn, "normalize", 0) != 0);
-    attr.box_size_scale = GetFloatAttr(dn, "box_size_scale", 1.0f);
-    attr.box_coordinate_scale = GetFloatAttr(dn, "box_coordinate_scale", 1.0f);
-    attr.framework = GetStrAttr(dn, "framework", "");
-
-    return std::make_shared<ngraph::op::Proposal>(inputs[0], inputs[1], inputs[2], attr);
-}
-
 // PriorBox layer
 template <>
 std::shared_ptr<ngraph::Node> V10Parser::LayerCreator<ngraph::op::PriorBox>::createLayer(
index e7f2a2a55703adc123fa143add19a21e87c7143b..0f8b15bc6c0fb7e74942b725525992dca9970fdb 100644 (file)
@@ -22,11 +22,11 @@ public:
     //  \brief Constructs a Proposal operation
     //
     //  \param class_probs     Class probability scores
-    //  \param class_logits    Class prediction logits
+    //  \param class_bbox_deltas    Class prediction bbox_deltas
     //  \param image_shape     Shape of image
     //  \param attrs           Proposal op attributes
     ProposalIE(const Output<Node>& class_probs,
-               const Output<Node>& class_logits,
+               const Output<Node>& class_bbox_deltas,
                const Output<Node>& image_shape,
                const ProposalAttrs& attrs);
 
index 835cbe5bb190c673b669a651bf755ea50cf77319..65b01710e1811cdf8c17cf9b7e500fd486b9bf15 100644 (file)
@@ -15,10 +15,16 @@ namespace ngraph {
 namespace pass {
 
 class TRANSFORMATIONS_API ConvertProposalToLegacyMatcher;
+class TRANSFORMATIONS_API ConvertProposal4ToLegacyMatcher;
 
 }  // namespace pass
 }  // namespace ngraph
 
+class ngraph::pass::ConvertProposal4ToLegacyMatcher: public ngraph::pass::MatcherPass {
+public:
+    ConvertProposal4ToLegacyMatcher();
+};
+
 class ngraph::pass::ConvertProposalToLegacyMatcher: public ngraph::pass::MatcherPass {
 public:
     ConvertProposalToLegacyMatcher();
index f856c1e474c486a37545e0df2beb5f5f9d404ec4..b61eba9ad3839fbb6aeac8028ab25b328f030843 100644 (file)
@@ -13,30 +13,29 @@ using namespace ngraph;
 
 constexpr NodeTypeInfo op::ProposalIE::type_info;
 
-op::ProposalIE::ProposalIE(const Output<Node>& class_probs, const Output<Node>& class_logits,
+op::ProposalIE::ProposalIE(const Output<Node>& class_probs, const Output<Node>& class_bbox_deltas,
                            const Output<Node>& image_shape, const ProposalAttrs& attrs)
-    : Op({class_probs, class_logits, image_shape}), m_attrs(attrs) {
+    : Op({class_probs, class_bbox_deltas, image_shape}), m_attrs(attrs) {
     constructor_validate_and_infer_types();
 }
 
 void op::ProposalIE::validate_and_infer_types() {
-    set_input_is_relevant_to_shape(2);
-
     const auto& class_probs_pshape = get_input_partial_shape(0);
-    const auto& class_logits_pshape = get_input_partial_shape(1);
+    const auto& class_bbox_deltas_pshape = get_input_partial_shape(1);
     const auto& image_shape_pshape = get_input_partial_shape(2);
-    if (class_probs_pshape.is_static() && class_logits_pshape.is_static() && image_shape_pshape.is_static()) {
+
+    if (class_probs_pshape.is_static() && class_bbox_deltas_pshape.is_static() && image_shape_pshape.is_static()) {
         const Shape class_probs_shape {class_probs_pshape.to_shape()};
-        const Shape class_logits_shape {class_logits_pshape.to_shape()};
+        const Shape class_bbox_deltas_shape {class_bbox_deltas_pshape.to_shape()};
         const Shape image_shape_shape {image_shape_pshape.to_shape()};
 
         NODE_VALIDATION_CHECK(
             this, class_probs_shape.size() == 4,
             "Proposal layer shape class_probs input must have rank 4 (class_probs_shape: ", class_probs_shape, ").");
 
-        NODE_VALIDATION_CHECK(this, class_logits_shape.size() == 4,
-                              "Proposal layer shape class_logits_shape input must have rank 4 (class_logits_shape: ",
-                              class_logits_shape, ").");
+        NODE_VALIDATION_CHECK(this, class_bbox_deltas_shape.size() == 4,
+                              "Proposal layer shape class_bbox_deltas_shape input must have rank 4 (class_bbox_deltas_shape: ",
+                              class_bbox_deltas_shape, ").");
 
         NODE_VALIDATION_CHECK(
             this, image_shape_shape.size() == 2,
@@ -48,8 +47,12 @@ void op::ProposalIE::validate_and_infer_types() {
 
         auto batch_size = class_probs_shape[0];
         set_output_type(0, get_input_element_type(0), Shape {batch_size * m_attrs.post_nms_topn, 5});
+        if (m_attrs.infer_probs)
+            set_output_type(1, get_input_element_type(0), Shape {batch_size * m_attrs.post_nms_topn});
     } else {
         set_output_type(0, get_input_element_type(0), PartialShape::dynamic());
+        if (m_attrs.infer_probs)
+            set_output_type(1, get_input_element_type(0), PartialShape::dynamic());
     }
 }
 
index 784b4cbe8ec59d922af74abebf3910259f21eb7f..bccce874c0825be63df5f0432e94c7cdb3fe4419 100644 (file)
@@ -125,6 +125,7 @@ bool ngraph::pass::ConvertOpSet1ToLegacy::run_on_function(std::shared_ptr<ngraph
     anchor->add_matcher<ngraph::pass::ConvertNormalizeL2WithMulToNormalizeIE>();
     anchor->add_matcher<ngraph::pass::ConvertHardSigmoidToLegacyMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertProposalToLegacyMatcher>();
+    anchor->add_matcher<ngraph::pass::ConvertProposal4ToLegacyMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertTileToLegacyMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertLRNToLegacyMatcher>();
     anchor->add_matcher<ngraph::pass::ConvertPadToLegacyMatcher>();
index 531f87ee099bfdd2cedf263fab9ec7b08873fa93..1c54a09c672d4559fb89c92f222f56709c1db4cb 100644 (file)
@@ -8,58 +8,72 @@
 #include <vector>
 
 #include <ngraph/opsets/opset1.hpp>
-
+#include <ngraph/opsets/opset4.hpp>
+#include <ngraph/pattern/op/wrap_type.hpp>
 #include <ngraph_ops/proposal_ie.hpp>
 #include <ngraph/rt_info.hpp>
 
-ngraph::pass::ConvertProposalToLegacyMatcher::ConvertProposalToLegacyMatcher() {
-    auto input_0 = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1, 1, 1});
-    auto input_1 = std::make_shared<pattern::op::Label>(element::f32, Shape{1, 1, 1, 1});
-    auto input_2 = std::make_shared<pattern::op::Label>(element::f32, Shape{3});
+bool convert_to_proposal_ie(std::shared_ptr<ngraph::op::v0::Proposal> proposal, bool infer_probs = false) {
+    ngraph::Output<ngraph::Node> last; // 2D tensor of size [1, 3-4] with im_info will be retrieved from this node
+    ngraph::NodeVector ops_to_replace, new_ops;
+    ops_to_replace.push_back(proposal);
 
-    ngraph::op::ProposalAttrs attr = {};
+    if (auto reshape = std::dynamic_pointer_cast<ngraph::opset1::Reshape>(proposal->input_value(2).get_node_shared_ptr())) {
+        const ngraph::PartialShape& im_info_shape = reshape->get_input_partial_shape(0);
+        if (im_info_shape != ngraph::Shape({1, 3}) && im_info_shape != ngraph::Shape({1, 4})) {
+            return false;
+        }
+        last = reshape->input_value(0);
+        ops_to_replace.push_back(reshape);
+    } else {
+        auto const_shape = ngraph::opset1::Constant::create(ngraph::element::i64, ngraph::Shape{2}, {1, -1});
+        last = std::make_shared<ngraph::opset1::Reshape>(proposal->input_value(2), const_shape, true);
+        new_ops.push_back(last.get_node_shared_ptr());
+    }
+
+    auto ie_attrs = proposal->get_attrs();
+    ie_attrs.infer_probs = infer_probs;
+    auto proposal_ie = std::make_shared<ngraph::op::ProposalIE>(proposal->input_value(0),
+                                                                proposal->input_value(1),
+                                                                last,
+                                                                ie_attrs);
+    new_ops.push_back(proposal_ie);
+
+    proposal_ie->set_friendly_name(proposal->get_friendly_name());
+    ngraph::copy_runtime_info(ops_to_replace, new_ops);
+    ngraph::replace_node(proposal, proposal_ie);
+
+    return true;
+}
 
-    auto proposal = std::make_shared<ngraph::opset1::Proposal>(input_0, input_1, input_2, attr);
+ngraph::pass::ConvertProposalToLegacyMatcher::ConvertProposalToLegacyMatcher() {
+    auto proposal = ngraph::pattern::wrap_type<ngraph::opset1::Proposal>();
 
-    ngraph::matcher_pass_callback callback = [](pattern::Matcherm) {
-        auto proposal = std::dynamic_pointer_cast<ngraph::opset1::Proposal> (m.get_match_root());
+    ngraph::matcher_pass_callback callback = [](pattern::Matcher &m) {
+        auto proposal = std::dynamic_pointer_cast<ngraph::opset1::Proposal>(m.get_match_root());
 
         if (!proposal) {
             return false;
         }
+        convert_to_proposal_ie(proposal);
+        return true;
+    };
+    auto m = std::make_shared<ngraph::pattern::Matcher>(proposal, "ConvertProposalToProposalIE");
+    this->register_matcher(m, callback);
+}
 
-        Output<Node> last;
-
-        ngraph::NodeVector ops_to_replace, new_ops;
-        ops_to_replace.push_back(proposal);
+ngraph::pass::ConvertProposal4ToLegacyMatcher::ConvertProposal4ToLegacyMatcher() {
+    auto proposal = ngraph::pattern::wrap_type<ngraph::opset4::Proposal>();
 
-        if (auto reshape = std::dynamic_pointer_cast<opset1::Reshape>(proposal->input_value(2).get_node_shared_ptr())) {
-            auto input_shape = reshape->get_input_shape(0);
-            if (input_shape.size() == 2) {
-                last = reshape->input_value(0);
-                ops_to_replace.push_back(reshape);
-            }
-        }
+    ngraph::matcher_pass_callback callback = [](pattern::Matcher &m) {
+        auto proposal = std::dynamic_pointer_cast<ngraph::opset4::Proposal>(m.get_match_root());
 
-        if (!last.get_node_shared_ptr()) {
-            std::vector<int64_t> dims{1, -1};
-            auto const_shape = std::make_shared<ngraph::opset1::Constant>(element::i64, Shape{2}, dims);
-            last = std::make_shared<ngraph::opset1::Reshape>(proposal->input_value(2), const_shape, true);
-            new_ops.push_back(last.get_node_shared_ptr());
+        if (!proposal) {
+            return false;
         }
-
-        auto proposal_ie = std::make_shared<ngraph::op::ProposalIE> (proposal->input_value(0),
-                                                                     proposal->input_value(1),
-                                                                     last,
-                                                                     proposal->get_attrs());
-        new_ops.push_back(proposal_ie);
-
-        proposal_ie->set_friendly_name(proposal->get_friendly_name());
-        ngraph::copy_runtime_info(ops_to_replace, new_ops);
-        ngraph::replace_node(proposal, proposal_ie);
+        convert_to_proposal_ie(proposal, true);
         return true;
     };
-
-    auto m = std::make_shared<ngraph::pattern::Matcher>(proposal, "ConvertProposalToProposalIE");
+    auto m = std::make_shared<ngraph::pattern::Matcher>(proposal, "ConvertProposal4ToProposalIE");
     this->register_matcher(m, callback);
-}
\ No newline at end of file
+}
index aaf548e2af9e742619609d37b0455cbf03e6fbf6..1ceba78447b7383994b42bb634d84b7ed2cc64d8 100644 (file)
@@ -75,8 +75,3 @@ class ProposalMutation(BackReplacementPattern):
             reshape = create_op_node_with_second_input(graph, Reshape, [im_info_shape[1]], {'name': 'im_info/Reshape'})
             node.in_port(2).get_connection().set_destination(reshape.in_port(0))
             reshape.out_port(0).connect(node.in_port(2))
-
-        if node.has_port('out', 1) and not node.out_port(1).disconnected():
-            # This is the case when Proposal layer is used from extension, not from opset.
-            # Setting version attribute is not recommended, this will be fixed after Proposal will be updated in IE.
-            graph.node[node.id]['version'] = 'extension'
index dd08d5812a04edd336e991ffa4ca86b41a1e0e3e..dfc8848f6975e67734e75375828226b4ab03c7db 100644 (file)
@@ -44,7 +44,6 @@ class RPNProposalMXNetFrontExtractor(MXNetCustomFrontExtractorOp):
             'pre_nms_topn': pre_nms_topn,
             'post_nms_topn': post_nms_topn,
             'nms_thresh': nms_thresh,
-            'for_deformable': 1,
         }
 
         ProposalOp.update_node_stat(node, node_attrs)
index afd25a78021b78111aab44474a204853897681a8..928b32d8dc3ba878c87fad08125e7b7769eb950f 100644 (file)
@@ -27,12 +27,11 @@ class ProposalOp(Op):
         mandatory_props = {
             'type': __class__.op,
             'op': __class__.op,
-            'version': 'opset1',
+            'version': 'opset4',
             'post_nms_topn': 300,  # default in caffe-shared
             'infer': ProposalOp.proposal_infer,
             'in_ports_count': 3,
             'out_ports_count': 2,
-            'for_deformable': 0,
             'normalize': 0,
         }
         super().__init__(graph, mandatory_props, attrs)
@@ -65,7 +64,6 @@ class ProposalOp(Op):
             'normalize',
             'clip_after_nms',
             'clip_before_nms',
-            'for_deformable',
         ]
 
     @staticmethod
index b837444bacc1920ae33ac5d8a181b6ca359512c1..63560d6099f855ced28d9a61d5530ea1abe86c8e 100644 (file)
@@ -123,7 +123,11 @@ namespace ngraph
         {
             AttributeAdapter<AT> adapter(value);
             start_structure(name);
-            on_adapter(get_name_with_context(), adapter);
+            // todo: it's workaround, remove after fixing #35906
+            if (get_name_with_context().find("the_proposal") != std::string::npos)
+                on_adapter(name, adapter);
+            else
+                on_adapter(get_name_with_context(), adapter);
             finish_structure();
         }
         /// \returns The nested context of visits
index 51448cee2867262d6802c4315318002a3e694327..631a3fced3e1fb1cd7be7d31c322b07efb9a7a10 100644 (file)
@@ -33,8 +33,8 @@ namespace ngraph
         // clip_before_nms Clip before NMs
         // clip_after_nms  Clip after NMs
         // normalize       Normalize boxes to [0,1]
-        // box_size_scale  Scale factor for scaling box size logits
-        // box_coordinate_scale Scale factor for scaling box coordiate logits
+        // box_size_scale  Scale factor for scaling box size
+        // box_coordinate_scale Scale factor for scaling box coordiate
         // framework            Calculation frameworkrithm to use
         struct ProposalAttrs
         {
@@ -46,12 +46,13 @@ namespace ngraph
             size_t min_size = 1;
             std::vector<float> ratio;
             std::vector<float> scale;
-            bool clip_before_nms = false;
+            bool clip_before_nms = true;
             bool clip_after_nms = false;
             bool normalize = false;
             float box_size_scale = 1.0f;
             float box_coordinate_scale = 1.0f;
             std::string framework;
+            bool infer_probs = false;
         };
 
         namespace v0
@@ -59,17 +60,16 @@ namespace ngraph
             class NGRAPH_API Proposal : public Op
             {
             public:
-                static constexpr NodeTypeInfo type_info{"Proposal", 0};
-                const NodeTypeInfo& get_type_info() const override { return type_info; }
+                NGRAPH_RTTI_DECLARATION;
                 Proposal() = default;
                 /// \brief Constructs a Proposal operation
                 ///
                 /// \param class_probs     Class probability scores
-                /// \param class_logits    Class prediction logits
+                /// \param bbox_deltas     Prediction of bounding box deltas
                 /// \param image_shape     Shape of image
                 /// \param attrs           Proposal op attributes
                 Proposal(const Output<Node>& class_probs,
-                         const Output<Node>& class_logits,
+                         const Output<Node>& bbox_deltas,
                          const Output<Node>& image_shape,
                          const ProposalAttrs& attrs);
 
@@ -79,10 +79,36 @@ namespace ngraph
                 const ProposalAttrs& get_attrs() const { return m_attrs; }
                 virtual bool visit_attributes(AttributeVisitor& visitor) override;
 
-            private:
+            protected:
                 ProposalAttrs m_attrs;
             };
         }
+
+        namespace v4
+        {
+            class NGRAPH_API Proposal : public op::v0::Proposal
+            {
+            public:
+                NGRAPH_RTTI_DECLARATION;
+                Proposal() = default;
+                /// \brief Constructs a Proposal operation
+                ///
+                /// \param class_probs     Class probability scores
+                /// \param bbox_deltas     Prediction of bounding box deltas
+                /// \param image_shape     Shape of image
+                /// \param attrs           Proposal op attributes
+                Proposal(const Output<Node>& class_probs,
+                         const Output<Node>& bbox_deltas,
+                         const Output<Node>& image_shape,
+                         const ProposalAttrs& attrs);
+
+                void validate_and_infer_types() override;
+                virtual std::shared_ptr<Node>
+                    clone_with_new_inputs(const OutputVector& new_args) const override;
+                const ProposalAttrs& get_attrs() const { return m_attrs; }
+            };
+        }
+
         using v0::Proposal;
     }
 
index 4b0dd226f1e407292dc41410cd05cc05bf1a8e28..c7ece5b73ef133df810a3847bca46fb0723fa3fb 100644 (file)
@@ -91,7 +91,7 @@ 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(Proposal, ngraph::op::v4)
 NGRAPH_OP(Range, ngraph::op::v0)
 NGRAPH_OP(Relu, ngraph::op::v0)
 NGRAPH_OP(ReduceMax, ngraph::op::v1)
index ee45066a6fd257d92dee70b155c5372da504c37d..bf2b254f070134ed49469fce9687806b24127af8 100644 (file)
 using namespace std;
 using namespace ngraph;
 
-constexpr NodeTypeInfo op::Proposal::type_info;
+NGRAPH_RTTI_DEFINITION(op::v0::Proposal, "Proposal", 0);
 
-op::Proposal::Proposal(const Output<Node>& class_probs,
-                       const Output<Node>& class_logits,
-                       const Output<Node>& image_shape,
-                       const ProposalAttrs& attrs)
-    : Op({class_probs, class_logits, image_shape})
+op::v0::Proposal::Proposal(const Output<Node>& class_probs,
+                           const Output<Node>& bbox_deltas,
+                           const Output<Node>& image_shape,
+                           const ProposalAttrs& attrs)
+    : Op({class_probs, bbox_deltas, image_shape})
     , m_attrs(attrs)
 {
     constructor_validate_and_infer_types();
 }
 
-void op::Proposal::validate_and_infer_types()
+void op::v0::Proposal::validate_and_infer_types()
 {
-    set_input_is_relevant_to_shape(2);
-
     const auto& class_probs_pshape = get_input_partial_shape(0);
-    const auto& class_logits_pshape = get_input_partial_shape(1);
+    const auto& class_bbox_deltas_pshape = get_input_partial_shape(1);
     const auto& image_shape_pshape = get_input_partial_shape(2);
-    if (class_probs_pshape.is_static() && class_logits_pshape.is_static() &&
+    if (class_probs_pshape.is_static() && class_bbox_deltas_pshape.is_static() &&
         image_shape_pshape.is_static())
     {
         const Shape class_probs_shape{class_probs_pshape.to_shape()};
-        const Shape class_logits_shape{class_logits_pshape.to_shape()};
+        const Shape class_bbox_deltas_shape{class_bbox_deltas_pshape.to_shape()};
         const Shape image_shape_shape{image_shape_pshape.to_shape()};
 
         NODE_VALIDATION_CHECK(
@@ -54,12 +52,12 @@ void op::Proposal::validate_and_infer_types()
             class_probs_shape,
             ").");
 
-        NODE_VALIDATION_CHECK(
-            this,
-            class_logits_shape.size() == 4,
-            "Proposal layer shape class_logits_shape input must have rank 4 (class_logits_shape: ",
-            class_logits_shape,
-            ").");
+        NODE_VALIDATION_CHECK(this,
+                              class_bbox_deltas_shape.size() == 4,
+                              "Proposal layer shape class_bbox_deltas_shape input must have rank 4 "
+                              "(class_bbox_deltas_shape: ",
+                              class_bbox_deltas_shape,
+                              ").");
 
         NODE_VALIDATION_CHECK(
             this,
@@ -84,15 +82,17 @@ void op::Proposal::validate_and_infer_types()
     }
 }
 
-shared_ptr<Node> op::Proposal::clone_with_new_inputs(const OutputVector& new_args) const
+shared_ptr<Node> op::v0::Proposal::clone_with_new_inputs(const OutputVector& new_args) const
 {
     check_new_args_count(this, new_args);
-    return make_shared<Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs);
+    return make_shared<op::v0::Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs);
 }
 
-bool op::Proposal::visit_attributes(AttributeVisitor& visitor)
+bool op::v0::Proposal::visit_attributes(AttributeVisitor& visitor)
 {
-    visitor.on_attribute("attrs", m_attrs);
+    //    temporary workaround, remove after #35906 is fixed
+    //    visitor.on_attribute("attrs", m_attrs);
+    visitor.on_attribute("the_proposal", m_attrs);
     return true;
 }
 
@@ -121,3 +121,35 @@ bool AttributeAdapter<op::ProposalAttrs>::visit_attributes(AttributeVisitor& vis
     visitor.on_attribute("framework", m_ref.framework);
     return true;
 }
+
+NGRAPH_RTTI_DEFINITION(op::v4::Proposal, "Proposal", 4);
+
+op::v4::Proposal::Proposal(const Output<Node>& class_probs,
+                           const Output<Node>& class_bbox_deltas,
+                           const Output<Node>& image_shape,
+                           const op::ProposalAttrs& attrs)
+    : v0::Proposal(class_probs, class_bbox_deltas, image_shape, attrs)
+{
+    constructor_validate_and_infer_types();
+}
+
+void op::v4::Proposal::validate_and_infer_types()
+{
+    v0::Proposal::validate_and_infer_types();
+
+    const auto& class_probs_pshape = get_input_partial_shape(0);
+    const auto& class_bbox_deltas_pshape = get_input_partial_shape(1);
+    const auto& image_shape_pshape = get_input_partial_shape(2);
+    auto batch_size = class_probs_pshape.to_shape()[0];
+    if (class_probs_pshape.is_static() && class_bbox_deltas_pshape.is_static() &&
+        image_shape_pshape.is_static())
+        set_output_type(1, get_input_element_type(0), Shape{batch_size * m_attrs.post_nms_topn});
+    else
+        set_output_type(1, get_input_element_type(0), PartialShape::dynamic());
+}
+
+std::shared_ptr<Node> op::v4::Proposal::clone_with_new_inputs(const OutputVector& new_args) const
+{
+    check_new_args_count(this, new_args);
+    return make_shared<op::v4::Proposal>(new_args.at(0), new_args.at(1), new_args.at(2), m_attrs);
+}
index 4bd772bf33b2bfa060d325673ba58388e78f86f0..47bda098ef96d3bb9499be5b918f0afeabb21b5b 100644 (file)
@@ -2068,7 +2068,7 @@ def prior_box(
 @nameable_op
 def proposal(
     class_probs: Node,
-    box_logits: Node,
+    bbox_deltas: Node,
     image_shape: NodeInput,
     attrs: dict,
     name: Optional[str] = None,
@@ -2076,7 +2076,7 @@ def proposal(
     """Filter bounding boxes and outputs only those with the highest prediction confidence.
 
     :param  class_probs:        4D input floating point tensor with class prediction scores.
-    :param  box_logits:         4D input floating point tensor with box logits.
+    :param  bbox_deltas:         4D input floating point tensor with box logits.
     :param  image_shape:        The 1D input tensor with 3 or 4 elements describing image shape.
     :param  attrs:              The dictionary containing key, value pairs for attributes.
     :param  name:               Optional name for the output node.
@@ -2178,26 +2178,26 @@ def proposal(
     :return: Node representing Proposal operation.
     """
     requirements = [
-        ("attrs.base_size", True, np.unsignedinteger, is_positive_value),
-        ("attrs.pre_nms_topn", True, np.unsignedinteger, is_positive_value),
-        ("attrs.post_nms_topn", True, np.unsignedinteger, is_positive_value),
-        ("attrs.nms_thresh", True, np.floating, is_positive_value),
-        ("attrs.feat_stride", True, np.unsignedinteger, is_positive_value),
-        ("attrs.min_size", True, np.unsignedinteger, is_positive_value),
-        ("attrs.ratio", True, np.floating, None),
-        ("attrs.scale", True, np.floating, None),
-        ("attrs.clip_before_nms", False, np.bool_, None),
-        ("attrs.clip_after_nms", False, np.bool_, None),
-        ("attrs.normalize", False, np.bool_, None),
-        ("attrs.box_size_scale", False, np.floating, is_positive_value),
-        ("attrs.box_coordinate_scale", False, np.floating, is_positive_value),
-        ("attrs.framework", False, np.str_, None),
+        ("base_size", True, np.unsignedinteger, is_positive_value),
+        ("pre_nms_topn", True, np.unsignedinteger, is_positive_value),
+        ("post_nms_topn", True, np.unsignedinteger, is_positive_value),
+        ("nms_thresh", True, np.floating, is_positive_value),
+        ("feat_stride", True, np.unsignedinteger, is_positive_value),
+        ("min_size", True, np.unsignedinteger, is_positive_value),
+        ("ratio", True, np.floating, None),
+        ("scale", True, np.floating, None),
+        ("clip_before_nms", False, np.bool_, None),
+        ("clip_after_nms", False, np.bool_, None),
+        ("normalize", False, np.bool_, None),
+        ("box_size_scale", False, np.floating, is_positive_value),
+        ("box_coordinate_scale", False, np.floating, is_positive_value),
+        ("framework", False, np.str_, None),
     ]
 
     check_valid_attributes("Proposal", attrs, requirements)
 
     return _get_node_factory_opset1().create(
-        "Proposal", [class_probs, box_logits, as_node(image_shape)], attrs
+        "Proposal", [class_probs, bbox_deltas, as_node(image_shape)], attrs
     )
 
 
index d71772db6f04d234474812fc19d67928c6fcc5c2..21edb622a664a02db9f42b62e0c8b21110a86199 100644 (file)
@@ -104,7 +104,7 @@ from ngraph.opset1.ops import prelu
 from ngraph.opset1.ops import prior_box
 from ngraph.opset1.ops import prior_box_clustered
 from ngraph.opset1.ops import psroi_pooling
-from ngraph.opset1.ops import proposal
+from ngraph.opset4.ops import proposal
 from ngraph.opset1.ops import range
 from ngraph.opset3.ops import read_value
 from ngraph.opset1.ops import reduce_logical_and
index 15f39cc54138fd45bc04c286fa7a3d32d28fbc90..5d4aec06aa75c942c4848c19033be72e2212d40f 100644 (file)
@@ -196,3 +196,120 @@ def atanh(node: NodeInput, name: Optional[str] = None) -> Node:
     :return: New node with arctanh operation applied on it.
     """
     return _get_node_factory_opset4().create("Atanh", [node])
+
+
+@nameable_op
+def proposal(
+        class_probs: Node,
+        bbox_deltas: Node,
+        image_shape: NodeInput,
+        attrs: dict,
+        name: Optional[str] = None,
+) -> Node:
+    """Filter bounding boxes and outputs only those with the highest prediction confidence.
+
+    :param  class_probs:        4D input floating point tensor with class prediction scores.
+    :param  bbox_deltas:        4D input floating point tensor with corrected predictions of bounding boxes
+    :param  image_shape:        The 1D input tensor with 3 or 4 elements describing image shape.
+    :param  attrs:              The dictionary containing key, value pairs for attributes.
+    :param  name:               Optional name for the output node.
+    * base_size     The size of the anchor to which scale and ratio attributes are applied.
+                    Range of values: a positive unsigned integer number
+                    Default value: None
+                    Required: yes
+    * pre_nms_topn  The number of bounding boxes before the NMS operation.
+                    Range of values: a positive unsigned integer number
+                    Default value: None
+                    Required: yes
+    * post_nms_topn The number of bounding boxes after the NMS operation.
+                    Range of values: a positive unsigned integer number
+                    Default value: None
+                    Required: yes
+    * nms_thresh    The minimum value of the proposal to be taken into consideration.
+                    Range of values: a positive floating-point number
+                    Default value: None
+                    Required: yes
+    * feat_stride   The step size to slide over boxes (in pixels).
+                    Range of values: a positive unsigned integer
+                    Default value: None
+                    Required: yes
+    * min_size      The minimum size of box to be taken into consideration.
+                    Range of values: a positive unsigned integer number
+                    Default value: None
+                    Required: yes
+    * ratio         The ratios for anchor generation.
+                    Range of values: a list of floating-point numbers
+                    Default value: None
+                    Required: yes
+    * scale         The scales for anchor generation.
+                    Range of values: a list of floating-point numbers
+                    Default value: None
+                    Required: yes
+    * clip_before_nms   The flag that specifies whether to perform clip bounding boxes before
+                        non-maximum suppression or not.
+                        Range of values: True or False
+                        Default value: True
+                        Required: no
+    * clip_after_nms    The flag that specifies whether to perform clip bounding boxes after
+                        non-maximum suppression or not.
+                        Range of values: True or False
+                        Default value: False
+                        Required: no
+    * normalize     The flag that specifies whether to perform normalization of output boxes to
+                    [0,1] interval or not.
+                    Range of values: True or False
+                    Default value: False
+                    Required: no
+    * box_size_scale    Specifies the scale factor applied to logits of box sizes before decoding.
+                        Range of values: a positive floating-point number
+                        Default value: 1.0
+                        Required: no
+    * box_coordinate_scale  Specifies the scale factor applied to logits of box coordinates
+                            before decoding.
+                            Range of values: a positive floating-point number
+                            Default value: 1.0
+                            Required: no
+    * framework     Specifies how the box coordinates are calculated.
+                    Range of values: "" (empty string) - calculate box coordinates like in Caffe*
+                                     tensorflow - calculate box coordinates like in the TensorFlow*
+                                                  Object Detection API models
+                    Default value: "" (empty string)
+                    Required: no
+    Example of attribute dictionary:
+    .. code-block:: python
+        # just required ones
+        attrs = {
+            'base_size': 85,
+            'pre_nms_topn': 10,
+            'post_nms_topn': 20,
+            'nms_thresh': 0.34,
+            'feat_stride': 16,
+            'min_size': 32,
+            'ratio': [0.1, 1.5, 2.0, 2.5],
+            'scale': [2, 3, 3, 4],
+        }
+    Optional attributes which are absent from dictionary will be set with corresponding default.
+    :return: Node representing Proposal operation.
+    """
+    requirements = [
+        ("base_size", True, np.unsignedinteger, is_positive_value),
+        ("pre_nms_topn", True, np.unsignedinteger, is_positive_value),
+        ("post_nms_topn", True, np.unsignedinteger, is_positive_value),
+        ("nms_thresh", True, np.floating, is_positive_value),
+        ("feat_stride", True, np.unsignedinteger, is_positive_value),
+        ("min_size", True, np.unsignedinteger, is_positive_value),
+        ("ratio", True, np.floating, None),
+        ("scale", True, np.floating, None),
+        ("clip_before_nms", False, np.bool_, None),
+        ("clip_after_nms", False, np.bool_, None),
+        ("normalize", False, np.bool_, None),
+        ("box_size_scale", False, np.floating, is_positive_value),
+        ("box_coordinate_scale", False, np.floating, is_positive_value),
+        ("framework", False, np.str_, None),
+    ]
+
+    check_valid_attributes("Proposal", attrs, requirements)
+
+    return _get_node_factory_opset4().create(
+        "Proposal", [class_probs, bbox_deltas, as_node(image_shape)], attrs
+    )
index c1aa08ee68ee8b919c05c0f970c21fd46fd56271..1689e43087bfdc25a641b715f06b8985d4a7b875 100644 (file)
@@ -750,25 +750,25 @@ def test_detection_output(int_dtype, fp_dtype):
 )
 def test_proposal(int_dtype, fp_dtype):
     attributes = {
-        "attrs.base_size": int_dtype(1),
-        "attrs.pre_nms_topn": int_dtype(20),
-        "attrs.post_nms_topn": int_dtype(64),
-        "attrs.nms_thresh": fp_dtype(0.34),
-        "attrs.feat_stride": int_dtype(16),
-        "attrs.min_size": int_dtype(32),
-        "attrs.ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=fp_dtype),
-        "attrs.scale": np.array([2, 3, 3, 4], dtype=fp_dtype),
+        "base_size": int_dtype(1),
+        "pre_nms_topn": int_dtype(20),
+        "post_nms_topn": int_dtype(64),
+        "nms_thresh": fp_dtype(0.34),
+        "feat_stride": int_dtype(16),
+        "min_size": int_dtype(32),
+        "ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=fp_dtype),
+        "scale": np.array([2, 3, 3, 4], dtype=fp_dtype),
     }
     batch_size = 7
 
     class_probs = ng.parameter([batch_size, 12, 34, 62], fp_dtype, "class_probs")
-    class_logits = ng.parameter([batch_size, 24, 34, 62], fp_dtype, "class_logits")
+    bbox_deltas = ng.parameter([batch_size, 24, 34, 62], fp_dtype, "bbox_deltas")
     image_shape = ng.parameter([3], fp_dtype, "image_shape")
-    node = ng.proposal(class_probs, class_logits, image_shape, attributes)
+    node = ng.proposal(class_probs, bbox_deltas, image_shape, attributes)
 
     assert node.get_type_name() == "Proposal"
-    assert node.get_output_size() == 1
-    assert list(node.get_output_shape(0)) == [batch_size * attributes["attrs.post_nms_topn"], 5]
+    assert node.get_output_size() == 2
+    assert list(node.get_output_shape(0)) == [batch_size * attributes["post_nms_topn"], 5]
 
 
 def test_tensor_iterator():
index 7fa8e91e1a6880b94da4625c797c7b7b33540666..7c1ea8d87ce438d2059f164899c248b7e21a657e 100644 (file)
@@ -22,21 +22,21 @@ import ngraph as ng
 @pytest.fixture()
 def _proposal_node():
     attributes = {
-        "attrs.base_size": np.uint16(1),
-        "attrs.pre_nms_topn": np.uint16(20),
-        "attrs.post_nms_topn": np.uint16(64),
-        "attrs.nms_thresh": np.float64(0.34),
-        "attrs.feat_stride": np.uint16(16),
-        "attrs.min_size": np.uint16(32),
-        "attrs.ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=np.float64),
-        "attrs.scale": np.array([2, 3, 3, 4], dtype=np.float64),
+        "base_size": np.uint16(1),
+        "pre_nms_topn": np.uint16(20),
+        "post_nms_topn": np.uint16(64),
+        "nms_thresh": np.float64(0.34),
+        "feat_stride": np.uint16(16),
+        "min_size": np.uint16(32),
+        "ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=np.float64),
+        "scale": np.array([2, 3, 3, 4], dtype=np.float64),
     }
     batch_size = 7
 
     class_probs = ng.parameter([batch_size, 12, 34, 62], np.float64, "class_probs")
-    class_logits = ng.parameter([batch_size, 24, 34, 62], np.float64, "class_logits")
+    bbox_deltas = ng.parameter([batch_size, 24, 34, 62], np.float64, "bbox_deltas")
     image_shape = ng.parameter([3], np.float64, "image_shape")
-    return ng.proposal(class_probs, class_logits, image_shape, attributes)
+    return ng.proposal(class_probs, bbox_deltas, image_shape, attributes)
 
 
 def test_dynamic_attributes_softmax():
@@ -124,21 +124,21 @@ def test_dynamic_get_attribute_value(int_dtype, fp_dtype):
 )
 def test_dynamic_set_attribute_value(int_dtype, fp_dtype):
     attributes = {
-        "attrs.base_size": int_dtype(1),
-        "attrs.pre_nms_topn": int_dtype(20),
-        "attrs.post_nms_topn": int_dtype(64),
-        "attrs.nms_thresh": fp_dtype(0.34),
-        "attrs.feat_stride": int_dtype(16),
-        "attrs.min_size": int_dtype(32),
-        "attrs.ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=fp_dtype),
-        "attrs.scale": np.array([2, 3, 3, 4], dtype=fp_dtype),
+        "base_size": int_dtype(1),
+        "pre_nms_topn": int_dtype(20),
+        "post_nms_topn": int_dtype(64),
+        "nms_thresh": fp_dtype(0.34),
+        "feat_stride": int_dtype(16),
+        "min_size": int_dtype(32),
+        "ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=fp_dtype),
+        "scale": np.array([2, 3, 3, 4], dtype=fp_dtype),
     }
     batch_size = 7
 
     class_probs = ng.parameter([batch_size, 12, 34, 62], fp_dtype, "class_probs")
-    class_logits = ng.parameter([batch_size, 24, 34, 62], fp_dtype, "class_logits")
+    bbox_deltas = ng.parameter([batch_size, 24, 34, 62], fp_dtype, "bbox_deltas")
     image_shape = ng.parameter([3], fp_dtype, "image_shape")
-    node = ng.proposal(class_probs, class_logits, image_shape, attributes)
+    node = ng.proposal(class_probs, bbox_deltas, image_shape, attributes)
 
     node.set_base_size(int_dtype(15))
     node.set_pre_nms_topn(int_dtype(7))
diff --git a/ngraph/python/tests/test_ngraph/test_proposal.py b/ngraph/python/tests/test_ngraph/test_proposal.py
new file mode 100644 (file)
index 0000000..26781da
--- /dev/null
@@ -0,0 +1,48 @@
+# ******************************************************************************
+# 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.
+# ******************************************************************************
+import numpy as np
+import ngraph as ng
+from ngraph.impl import Shape, Type
+
+
+def test_proposal_props():
+    float_dtype = np.float32
+    batch_size = 1
+    post_nms_topn = 20
+    probs = ng.parameter(Shape([batch_size, 8, 255, 255]), dtype=float_dtype, name="probs")
+    deltas = ng.parameter(Shape([batch_size, 16, 255, 255]), dtype=float_dtype, name="bbox_deltas")
+    im_info = ng.parameter(Shape([4]), dtype=float_dtype, name="im_info")
+
+    attrs = {
+        "base_size": np.uint32(85),
+        "pre_nms_topn": np.uint32(10),
+        "post_nms_topn": np.uint32(post_nms_topn),
+        "nms_thresh": np.float32(0.34),
+        "feat_stride": np.uint32(16),
+        "min_size": np.uint32(32),
+        "ratio": np.array([0.1, 1.5, 2.0, 2.5], dtype=np.float32),
+        "scale": np.array([2, 3, 3, 4], dtype=np.float32),
+    }
+
+    node = ng.proposal(probs, deltas, im_info, attrs)
+
+    assert node.get_type_name() == "Proposal"
+    assert node.get_output_size() == 2
+
+    assert list(node.get_output_shape(0)) == [batch_size * post_nms_topn, 5]
+    assert list(node.get_output_shape(1)) == [batch_size * post_nms_topn]
+    assert node.get_output_element_type(0) == Type.f32
+    assert node.get_output_element_type(1) == Type.f32
index 7d0afd7a81f6b11689f9c046e31c72d32d09bf38..2ad52f8027b574ab5588d41f416db2a1741d80d2 100644 (file)
@@ -343,7 +343,8 @@ protected:
 
 constexpr NodeTypeInfo Oracle::type_info;
 
-TEST(attributes, user_op)
+// todo: temporary disabled until bug with AttributeVisitor is fixed #35906
+TEST(attributes, DISABLED_user_op)
 {
     FactoryRegistry<Node>::get().register_factory<Oracle>();
     auto program = make_shared<op::Parameter>(element::i32, Shape{200});
index eb360680ad255dbeb4af0b546df31d4001b202e0..9b92b790bf63436210d445f10d58374af1e53b86 100644 (file)
 using namespace std;
 using namespace ngraph;
 
-TEST(type_prop, proposal_invalid_class_probs_rank)
+// ------------------------------ V0 ------------------------------
+
+TEST(type_prop, proposal_v0_invalid_class_probs_rank)
+{
+    op::ProposalAttrs attrs;
+    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
+
+    try
+    {
+        auto proposal =
+            make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
+        // Should have thrown, so fail if it didn't
+        FAIL() << "Invalid input tensor rank.";
+    }
+    catch (const NodeValidationFailure& error)
+    {
+        EXPECT_HAS_SUBSTRING(
+            error.what(), std::string("Proposal layer shape class_probs input must have rank 4"));
+    }
+    catch (...)
+    {
+        FAIL() << "Deduced type check failed for unexpected reason";
+    }
+}
+
+TEST(type_prop, proposal_v0_invalid_class_bbox_deltas_rank)
+{
+    op::ProposalAttrs attrs;
+    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
+    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
+
+    try
+    {
+        auto proposal =
+            make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
+        // Should have thrown, so fail if it didn't
+        FAIL() << "Invalid input tensor rank.";
+    }
+    catch (const NodeValidationFailure& error)
+    {
+        EXPECT_HAS_SUBSTRING(
+            error.what(),
+            std::string("Proposal layer shape class_bbox_deltas_shape input must have rank 4"));
+    }
+    catch (...)
+    {
+        FAIL() << "Deduced type check failed for unexpected reason";
+    }
+}
+
+TEST(type_prop, proposal_v0_invalid_image_shape_rank)
+{
+    op::ProposalAttrs attrs;
+    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{2, 1});
+
+    try
+    {
+        auto proposal =
+            make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
+        // Should have thrown, so fail if it didn't
+        FAIL() << "Invalid input tensor rank.";
+    }
+    catch (const NodeValidationFailure& error)
+    {
+        EXPECT_HAS_SUBSTRING(error.what(),
+                             std::string("Proposal layer image_shape input must have rank 1"));
+    }
+    catch (...)
+    {
+        FAIL() << "Deduced type check failed for unexpected reason";
+    }
+}
+
+TEST(type_prop, proposal_v0_invalid_image_shape_size)
+{
+    op::ProposalAttrs attrs;
+    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
+
+    try
+    {
+        auto proposal =
+            make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
+        // Should have thrown, so fail if it didn't
+        FAIL() << "Invalid input tensor rank.";
+    }
+    catch (const NodeValidationFailure& error)
+    {
+        EXPECT_HAS_SUBSTRING(
+            error.what(),
+            std::string(
+                "Image_shape 1D tensor must have => 3 and <= 4 elements (image_shape_shape[0]"));
+    }
+    catch (...)
+    {
+        FAIL() << "Deduced type check failed for unexpected reason";
+    }
+}
+
+TEST(type_prop, proposal_v0_shape_infer)
+{
+    op::ProposalAttrs attrs;
+    attrs.base_size = 1;
+    attrs.pre_nms_topn = 20;
+    attrs.post_nms_topn = 200;
+    const size_t batch_size = 7;
+
+    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{batch_size, 12, 34, 62});
+    auto class_bbox_deltas =
+        make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62});
+    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
+    auto op = make_shared<op::v0::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
+    ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
+}
+
+// ------------------------------ V4 ------------------------------
+
+TEST(type_prop, proposal_v4_invalid_class_probs_rank)
 {
     op::ProposalAttrs attrs;
     auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
-    auto class_logits = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
     auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
 
     try
     {
-        auto proposal = make_shared<op::Proposal>(class_probs, class_logits, image_shape, attrs);
+        auto proposal =
+            make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
         // Should have thrown, so fail if it didn't
         FAIL() << "Invalid input tensor rank.";
     }
@@ -46,16 +170,17 @@ TEST(type_prop, proposal_invalid_class_probs_rank)
     }
 }
 
-TEST(type_prop, proposal_invalid_class_logits_rank)
+TEST(type_prop, proposal_v4_invalid_class_bbox_deltas_rank)
 {
     op::ProposalAttrs attrs;
     auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
-    auto class_logits = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3});
     auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
 
     try
     {
-        auto proposal = make_shared<op::Proposal>(class_probs, class_logits, image_shape, attrs);
+        auto proposal =
+            make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
         // Should have thrown, so fail if it didn't
         FAIL() << "Invalid input tensor rank.";
     }
@@ -63,7 +188,7 @@ TEST(type_prop, proposal_invalid_class_logits_rank)
     {
         EXPECT_HAS_SUBSTRING(
             error.what(),
-            std::string("Proposal layer shape class_logits_shape input must have rank 4"));
+            std::string("Proposal layer shape class_bbox_deltas_shape input must have rank 4"));
     }
     catch (...)
     {
@@ -71,16 +196,17 @@ TEST(type_prop, proposal_invalid_class_logits_rank)
     }
 }
 
-TEST(type_prop, proposal_invalid_image_shape_rank)
+TEST(type_prop, proposal_v4_invalid_image_shape_rank)
 {
     op::ProposalAttrs attrs;
     auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
-    auto class_logits = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
     auto image_shape = make_shared<op::Parameter>(element::f32, Shape{2, 1});
 
     try
     {
-        auto proposal = make_shared<op::Proposal>(class_probs, class_logits, image_shape, attrs);
+        auto proposal =
+            make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
         // Should have thrown, so fail if it didn't
         FAIL() << "Invalid input tensor rank.";
     }
@@ -95,16 +221,17 @@ TEST(type_prop, proposal_invalid_image_shape_rank)
     }
 }
 
-TEST(type_prop, proposal_invalid_image_shape_size)
+TEST(type_prop, proposal_v4_invalid_image_shape_size)
 {
     op::ProposalAttrs attrs;
     auto class_probs = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
-    auto class_logits = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
+    auto class_bbox_deltas = make_shared<op::Parameter>(element::f32, Shape{1, 2, 3, 4});
     auto image_shape = make_shared<op::Parameter>(element::f32, Shape{5});
 
     try
     {
-        auto proposal = make_shared<op::Proposal>(class_probs, class_logits, image_shape, attrs);
+        auto proposal =
+            make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
         // Should have thrown, so fail if it didn't
         FAIL() << "Invalid input tensor rank.";
     }
@@ -120,3 +247,20 @@ TEST(type_prop, proposal_invalid_image_shape_size)
         FAIL() << "Deduced type check failed for unexpected reason";
     }
 }
+
+TEST(type_prop, proposal_v4_shape_infer)
+{
+    op::ProposalAttrs attrs;
+    attrs.base_size = 1;
+    attrs.pre_nms_topn = 20;
+    attrs.post_nms_topn = 200;
+    const size_t batch_size = 7;
+
+    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{batch_size, 12, 34, 62});
+    auto class_bbox_deltas =
+        make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62});
+    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
+    auto op = make_shared<op::v4::Proposal>(class_probs, class_bbox_deltas, image_shape, attrs);
+    ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
+    ASSERT_EQ(op->get_output_shape(1), (Shape{batch_size * attrs.post_nms_topn}));
+}
index 6b9f253d50968b28d83b955137edec958a636faf..7ab9793399f9663a827ec74ee6929394119fb958 100644 (file)
 #include "ngraph/op/interpolate.hpp"
 #include "ngraph/op/prior_box.hpp"
 #include "ngraph/op/prior_box_clustered.hpp"
-#include "ngraph/op/proposal.hpp"
 #include "ngraph/op/psroi_pooling.hpp"
 #include "ngraph/op/region_yolo.hpp"
 #include "ngraph/op/reorg_yolo.hpp"
 #include "ngraph/op/roi_pooling.hpp"
-#include "util/type_prop.hpp"
 
 #include <memory>
 using namespace std;
@@ -155,21 +153,6 @@ TEST(type_prop_layers, prior_box_clustered)
     ASSERT_EQ(pbc->get_shape(), (Shape{2, 4332}));
 }
 
-TEST(type_prop_layers, proposal)
-{
-    op::ProposalAttrs attrs;
-    attrs.base_size = 1;
-    attrs.pre_nms_topn = 20;
-    attrs.post_nms_topn = 200;
-    const size_t batch_size = 7;
-
-    auto class_probs = make_shared<op::Parameter>(element::f32, Shape{batch_size, 12, 34, 62});
-    auto class_logits = make_shared<op::Parameter>(element::f32, Shape{batch_size, 24, 34, 62});
-    auto image_shape = make_shared<op::Parameter>(element::f32, Shape{3});
-    auto op = make_shared<op::Proposal>(class_probs, class_logits, image_shape, attrs);
-    ASSERT_EQ(op->get_shape(), (Shape{batch_size * attrs.post_nms_topn, 5}));
-}
-
 TEST(type_prop_layers, region_yolo1)
 {
     auto inputs = make_shared<op::Parameter>(element::f32, Shape{1, 125, 13, 13});