diff --git a/docs/ops/detection/Proposal_1.md b/docs/ops/detection/Proposal_1.md
index 6d2681a86036bc..be1972591234fe 100644
--- a/docs/ops/detection/Proposal_1.md
+++ b/docs/ops/detection/Proposal_1.md
@@ -8,7 +8,7 @@
**Detailed description**
-*Proposal* has three inputs: a tensor with probabilities whether particular bounding box corresponds to background and foreground, a tensor with bbox_deltas for each of the bounding boxes, a tensor with input image size in the [`image_height`, `image_width`, `scale_height_and_width`] or [`image_height`, `image_width`, `scale_height`, `scale_width`] format. The produced tensor has two dimensions `[batch_size * post_nms_topn, 5]`.
+*Proposal* has three inputs: a tensor with probabilities whether particular bounding box corresponds to background and foreground, a tensor with bbox_deltas for each of the bounding boxes, a tensor with input image size in the [`image_height`, `image_width`, `scale_height_and_width`] or [`image_height`, `image_width`, `scale_height`, `scale_width`] format. The produced tensor has two dimensions `[batch_size * post_nms_topn, 5]`, and for each output box contains batch index and box coordinates.
*Proposal* layer does the following with the input tensor:
1. Generates initial anchor boxes. Left top corner of all boxes is at (0, 0). Width and height of boxes are calculated from *base_size* with *scale* and *ratio* attributes.
2. For each point in the first input tensor:
@@ -19,8 +19,9 @@
5. Takes top *pre_nms_topn* proposals
6. Calculates intersections for boxes and filter out all boxes with \f$intersection/union > nms\_thresh\f$
7. Takes top *post_nms_topn* proposals
-8. Returns top proposals
+8. Returns top proposals, if there is not enough proposals to fill the whole output tensor, the valid proposals will be terminated with a single -1.
+**Attributes**:
* *base_size*
@@ -136,15 +137,19 @@
**Inputs**:
-* **1**: 4D input floating point tensor with class prediction scores. Required.
+* **1**: 4D tensor of type *T* and shape `[batch_size, 2*K, H, W]` with class prediction scores. Required.
-* **2**: 4D input floating point tensor with box bbox_deltas. Required.
+* **2**: 4D tensor of type *T* and shape `[batch_size, 4*K, H, W]` with deltas for each bounding box. Required.
-* **3**: 1D input floating tensor 3 or 4 elements: [`image_height`, `image_width`, `scale_height_and_width`] or [`image_height`, `image_width`, `scale_height`, `scale_width`]. Required.
+* **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**:
-* **1**: Floating point tensor of shape `[batch_size * post_nms_topn, 5]`.
+* **1**: Tensor of type *T* and shape `[batch_size * post_nms_topn, 5]`.
+
+**Types**
+
+* *T*: floating point type.
**Example**
@@ -155,4 +160,4 @@
...
...
-```
\ No newline at end of file
+```
diff --git a/docs/ops/detection/Proposal_4.md b/docs/ops/detection/Proposal_4.md
index a6c57b1be812e9..7402cf2ff5502b 100644
--- a/docs/ops/detection/Proposal_4.md
+++ b/docs/ops/detection/Proposal_4.md
@@ -26,8 +26,11 @@ the second optional tensor of shape `[batch_size * post_nms_topn]` with probabil
5. Takes top *pre_nms_topn* proposals
6. Calculates intersections for boxes and filter out all boxes with \f$intersection/union > nms\_thresh\f$
7. Takes top *post_nms_topn* proposals
-8. Returns top proposals and optionally their probabilities
+8. Returns the results:
+ * Top proposals, if there is not enough proposals to fill the whole output tensor, the valid proposals will be terminated with a single -1.
+ * Optionally returns probabilities for each proposal, which are not terminated by any special value.
+**Attributes**:
* *base_size*
diff --git a/inference-engine/tests/functional/inference_engine/ngraph_reader/proposal_tests.cpp b/inference-engine/tests/functional/inference_engine/ngraph_reader/proposal_tests.cpp
index 54b96480ae6289..f0320c5135923a 100644
--- a/inference-engine/tests/functional/inference_engine/ngraph_reader/proposal_tests.cpp
+++ b/inference-engine/tests/functional/inference_engine/ngraph_reader/proposal_tests.cpp
@@ -32,9 +32,9 @@ TEST_F(NGraphReaderTests, ReadProposalNetwork) {
-
+
-
+
3
@@ -85,7 +85,7 @@ TEST_F(NGraphReaderTests, ReadProposalNetwork) {
std::string model_v6 = R"V0G0N(
-
+
1
@@ -183,9 +183,9 @@ TEST_F(NGraphReaderTests, ReadProposalNetwork_2) {
-
+
-
+
4
@@ -236,7 +236,7 @@ TEST_F(NGraphReaderTests, ReadProposalNetwork_2) {
std::string model_v6 = R"V0G0N(
-
+
1
diff --git a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/proposal.cpp b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/proposal.cpp
index 4ea940df5826c8..0a009bc0002805 100644
--- a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/proposal.cpp
+++ b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/proposal.cpp
@@ -45,5 +45,4 @@ INSTANTIATE_TEST_CASE_P(smoke_Proposal_tests, ProposalLayerTest,
::testing::Values(CommonTestUtils::DEVICE_CPU)),
ProposalLayerTest::getTestCaseName
);
-
} // namespace
diff --git a/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/skip_tests_config.cpp b/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/skip_tests_config.cpp
index bbbe4dc1d8a5e0..2d2590eb4ca961 100644
--- a/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/skip_tests_config.cpp
+++ b/inference-engine/tests/functional/plugin/gpu/shared_tests_instances/skip_tests_config.cpp
@@ -51,6 +51,8 @@ std::vector disabledTestPatterns() {
R"(.*(LSTMSequence).*mode=CONVERT_TO_TI_RAND_SEQ_LEN.*)",
R"(.*(smoke_DetectionOutput3In).*)",
R"(.*(smoke_DetectionOutput5In).*)",
+ // TODO: Issue: 47773
+ R"(.*(ProposalLayerTest).*)",
// INT8 StridedSlice not supported
R"(.*(LPT/StridedSliceTransformation).*)",
diff --git a/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/skip_tests_config.cpp b/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/skip_tests_config.cpp
index 3a97d5d35ccd4b..b3a0a67ab3844a 100644
--- a/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/skip_tests_config.cpp
+++ b/inference-engine/tests/functional/plugin/myriad/shared_tests_instances/skip_tests_config.cpp
@@ -31,6 +31,8 @@ std::vector disabledTestPatterns() {
R"(.*(DSR_GatherND).*)",
// TODO: Issue 26090
".*DSR_GatherStaticDataDynamicIdx.*f32.*1.3.200.304.*",
+ // TODO: Issue 47315
+ ".*ProposalLayerTest.*",
// TODO: Issue 46755
".*DSR_GatherElements.*"
};
diff --git a/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/single_layer/proposal.hpp b/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/single_layer/proposal.hpp
index be1f2fa29ee8a9..cb57372720d4f5 100644
--- a/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/single_layer/proposal.hpp
+++ b/inference-engine/tests/functional/shared_test_classes/include/shared_test_classes/single_layer/proposal.hpp
@@ -58,10 +58,44 @@ class ProposalLayerTest
static std::string getTestCaseName(testing::TestParamInfo obj);
static std::string SerializeProposalSpecificParams(proposalSpecificParams& params);
InferenceEngine::Blob::Ptr GenerateInput(const InferenceEngine::InputInfo &info) const override;
+ void Compare(const std::vector> &expectedOutputs, const std::vector &actualOutputs) override;
+ template
+ void Compare(const T *expected, const T *actual, std::size_t size,
+ T threshold, const std::size_t output_index) {
+ for (std::size_t i = 0; i < size; ++i) {
+ const auto &ref = expected[i];
+ const auto &res = actual[i];
+ // verify until first -1 appears in the 1st output.
+ if (output_index == 0 &&
+ CommonTestUtils::ie_abs(ref - static_cast(-1)) <= threshold) {
+ // output0 shape = {x, 5}
+ // output1 shape = {x}
+ // setting the new_size for output1 verification
+ num_selected_boxes = i / 5;
+ return;
+ }
+
+ const auto absoluteDifference = CommonTestUtils::ie_abs(res - ref);
+ if (absoluteDifference <= threshold) {
+ continue;
+ }
+
+ const auto max = std::max(CommonTestUtils::ie_abs(res),
+ CommonTestUtils::ie_abs(ref));
+ float diff =
+ static_cast(absoluteDifference) / static_cast(max);
+ ASSERT_TRUE(max != 0 && (diff <= static_cast(threshold)))
+ << "Relative comparison of values expected: " << ref
+ << " and actual: " << res << " at index " << i
+ << " with threshold " << threshold << " failed";
+ }
+ }
protected:
void SetUp() override;
- void Validate() override;
+
+private:
+ size_t num_selected_boxes;
};
} // namespace LayerTestsDefinitions
diff --git a/inference-engine/tests/functional/shared_test_classes/src/single_layer/proposal.cpp b/inference-engine/tests/functional/shared_test_classes/src/single_layer/proposal.cpp
index ac4838fd083e63..9efed201956e5f 100644
--- a/inference-engine/tests/functional/shared_test_classes/src/single_layer/proposal.cpp
+++ b/inference-engine/tests/functional/shared_test_classes/src/single_layer/proposal.cpp
@@ -63,6 +63,54 @@ std::string ProposalLayerTest::getTestCaseName(testing::TestParamInfo> &expectedOutputs,
+ const std::vector &actualOutputs) {
+ num_selected_boxes = 0;
+ for (std::size_t outputIndex = 0; outputIndex < expectedOutputs.size(); ++outputIndex) {
+ const auto &expected = expectedOutputs[outputIndex];
+ const auto &actual = actualOutputs[outputIndex];
+ ASSERT_EQ(expected.size(), actual->byteSize());
+ const auto &expectedBuffer = expected.data();
+
+ auto memory = InferenceEngine::as(actual);
+ IE_ASSERT(memory);
+ const auto lockedMemory = memory->rmap();
+ const auto actualBuffer = lockedMemory.as();
+
+ const auto &precision = actual->getTensorDesc().getPrecision();
+ auto size = actual->size();
+
+ // verifying the first output if there was less proposals than space
+ // provided,
+ // num_selected_boxes was set, take this into consideration while verifying the 2nd
+ // output
+ if (outputIndex == 1 && num_selected_boxes) {
+ size = num_selected_boxes;
+ }
+
+ switch (precision) {
+ case InferenceEngine::Precision::BF16:
+ Compare(reinterpret_cast(expectedBuffer),
+ reinterpret_cast(actualBuffer), size,
+ ngraph::bfloat16(threshold), outputIndex);
+ break;
+ case InferenceEngine::Precision::FP16:
+ Compare(reinterpret_cast(expectedBuffer),
+ reinterpret_cast(actualBuffer), size,
+ ngraph::float16(threshold), outputIndex);
+ break;
+ case InferenceEngine::Precision::FP32:
+ Compare(reinterpret_cast(expectedBuffer),
+ reinterpret_cast(actualBuffer), size,
+ threshold, outputIndex);
+ break;
+ default:
+ FAIL() << "Comparator for " << precision << " precision isn't supported";
+ }
+ }
+}
+
void ProposalLayerTest::SetUp() {
proposalSpecificParams proposalParams;
std::vector img_info = {225.0f, 225.0f, 1.0f};
@@ -98,10 +146,11 @@ void ProposalLayerTest::SetUp() {
std::vector imageInfoShape = {3};
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(InferenceEngine::Precision::FP16);
- auto params = ngraph::builder::makeParams(ngPrc, {{"scores", scoresShape}, {"boxes", boxesShape}});
+ // a_ and b_ are a workaround to solve alphabetic param sorting that destroys ordering
+ auto params = ngraph::builder::makeParams(ngPrc, {{"a_scores", scoresShape}, {"b_boxes", boxesShape}});
auto paramOuts = ngraph::helpers::convert2OutputVector(ngraph::helpers::castOps2Nodes(params));
- auto proposal = std::dynamic_pointer_cast(
+ auto proposal = std::dynamic_pointer_cast(
ngraph::builder::makeProposal(paramOuts[0], paramOuts[1], img_info, ngPrc,
base_size,
pre_nms_topn,
@@ -118,7 +167,9 @@ void ProposalLayerTest::SetUp() {
box_coordinate_scale,
framework));
- ngraph::ResultVector results{std::make_shared(proposal)};
+ ngraph::ResultVector results{
+ std::make_shared(proposal->output(0)),
+ std::make_shared(proposal->output(1))};
function = std::make_shared(results, params, "proposal");
}
@@ -126,15 +177,12 @@ InferenceEngine::Blob::Ptr ProposalLayerTest::GenerateInput(const InferenceEngin
InferenceEngine::Blob::Ptr blobPtr;
const std::string name = info.name();
- if (name == "scores") {
+ if (name == "a_scores") {
blobPtr = FuncTestUtils::createAndFillBlobFloat(info.getTensorDesc(), 1, 0, 1000, 8234231);
- } else if (name == "boxes") {
+ } else if (name == "b_boxes") {
blobPtr = FuncTestUtils::createAndFillBlobFloatNormalDistribution(info.getTensorDesc(), 0.0f, 0.2f, 7235346);
}
return blobPtr;
}
-
-// TODO: for validation, reference version is required (#28373)
-void ProposalLayerTest::Validate() {}
} // namespace LayerTestsDefinitions
diff --git a/inference-engine/tests/ngraph_helpers/ngraph_functions/src/proposal.cpp b/inference-engine/tests/ngraph_helpers/ngraph_functions/src/proposal.cpp
index d8fbd1d3b86532..cc5a4b04c28df5 100644
--- a/inference-engine/tests/ngraph_helpers/ngraph_functions/src/proposal.cpp
+++ b/inference-engine/tests/ngraph_helpers/ngraph_functions/src/proposal.cpp
@@ -44,10 +44,11 @@ std::shared_ptr makeProposal(const ngraph::Output &class_probs,
attrs.box_size_scale = box_size_scale;
attrs.box_coordinate_scale = box_coordinate_scale;
attrs.framework = framework;
+ attrs.infer_probs = true;
auto image_shape = makeConstant(ngraph::element::Type_t::f32, {3}, image_info);
- return std::make_shared(class_probs, class_logits, image_shape, attrs);
+ return std::make_shared(class_probs, class_logits, image_shape, attrs);
}
} // namespace builder
diff --git a/ngraph/core/reference/include/ngraph/runtime/reference/proposal.hpp b/ngraph/core/reference/include/ngraph/runtime/reference/proposal.hpp
new file mode 100644
index 00000000000000..42a666f02ac138
--- /dev/null
+++ b/ngraph/core/reference/include/ngraph/runtime/reference/proposal.hpp
@@ -0,0 +1,532 @@
+//*****************************************************************************
+// Copyright 2021 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//*****************************************************************************
+#include "ngraph/op/proposal.hpp"
+#include "ngraph/shape.hpp"
+namespace ngraph
+{
+ namespace runtime
+ {
+ namespace reference
+ {
+ namespace details
+ {
+ template
+ struct ProposalBox
+ {
+ T x0;
+ T y0;
+ T x1;
+ T y1;
+ T score;
+ };
+
+ static std::vector generate_anchors(const op::ProposalAttrs& attrs,
+ const unsigned int anchor_count)
+ {
+ std::vector anchors(4 * anchor_count);
+
+ // Framework specific parameters
+ auto coordinates_offset = attrs.framework == "tensorflow" ? 0.0f : 1.0f;
+ auto round_ratios = !(attrs.framework == "tensorflow");
+ auto shift_anchors = attrs.framework == "tensorflow";
+
+ auto base_size = attrs.base_size;
+ auto num_ratios = attrs.ratio.size();
+ auto ratios = attrs.ratio.data();
+ auto num_scales = attrs.scale.size();
+ auto scales = attrs.scale.data();
+ auto anchors_ptr = anchors.data();
+
+ // base box's width & height & center location
+ const float base_area = static_cast(base_size * base_size);
+ const float half_base_size = base_size * 0.5f;
+ const float center = 0.5f * (base_size - coordinates_offset);
+
+ // enumerate all transformed boxes
+ for (unsigned int ratio = 0; ratio < num_ratios; ++ratio)
+ {
+ // transformed width & height for given ratio factors
+ float ratio_w;
+ float ratio_h;
+ if (round_ratios)
+ {
+ ratio_w = std::roundf(std::sqrt(base_area / ratios[ratio]));
+ ratio_h = std::roundf(ratio_w * ratios[ratio]);
+ }
+ else
+ {
+ ratio_w = std::sqrt(base_area / ratios[ratio]);
+ ratio_h = ratio_w * ratios[ratio];
+ }
+
+ float* const p_anchors_wm =
+ anchors_ptr + 0 * num_ratios * num_scales + ratio * num_scales;
+ float* const p_anchors_hm =
+ anchors_ptr + 1 * num_ratios * num_scales + ratio * num_scales;
+ float* const p_anchors_wp =
+ anchors_ptr + 2 * num_ratios * num_scales + ratio * num_scales;
+ float* const p_anchors_hp =
+ anchors_ptr + 3 * num_ratios * num_scales + ratio * num_scales;
+
+ for (unsigned int scale = 0; scale < num_scales; ++scale)
+ {
+ // transformed width & height for given scale factors
+ const float scale_w =
+ 0.5f * (ratio_w * scales[scale] - coordinates_offset);
+ const float scale_h =
+ 0.5f * (ratio_h * scales[scale] - coordinates_offset);
+
+ // (x1, y1, x2, y2) for transformed box
+ p_anchors_wm[scale] = center - scale_w;
+ p_anchors_hm[scale] = center - scale_h;
+ p_anchors_wp[scale] = center + scale_w;
+ p_anchors_hp[scale] = center + scale_h;
+
+ if (shift_anchors)
+ {
+ p_anchors_wm[scale] -= half_base_size;
+ p_anchors_hm[scale] -= half_base_size;
+ p_anchors_wp[scale] -= half_base_size;
+ p_anchors_hp[scale] -= half_base_size;
+ }
+ }
+ }
+ return anchors;
+ }
+
+ template
+ static void enumerate_proposals(const T* bottom4d,
+ const T* d_anchor4d,
+ const float* anchors,
+ std::vector>& proposals,
+ const unsigned int num_anchors,
+ const unsigned int bottom_H,
+ const unsigned int bottom_W,
+ const float img_H,
+ const float img_W,
+ const float min_box_H,
+ const float min_box_W,
+ const int feat_stride,
+ const float box_coordinate_scale,
+ const float box_size_scale,
+ float coordinates_offset,
+ bool initial_clip,
+ bool swap_xy,
+ bool clip_before_nms)
+ {
+ // used for offset calculation
+ const size_t bottom_area = bottom_H * bottom_W;
+
+ const float* p_anchors_wm = anchors + 0 * num_anchors;
+ const float* p_anchors_hm = anchors + 1 * num_anchors;
+ const float* p_anchors_wp = anchors + 2 * num_anchors;
+ const float* p_anchors_hp = anchors + 3 * num_anchors;
+
+ for (unsigned int h = 0; h < bottom_H; ++h)
+ {
+ for (unsigned int w = 0; w < bottom_W; ++w)
+ {
+ const float x = static_cast((swap_xy ? h : w) * feat_stride);
+ const float y = static_cast((swap_xy ? w : h) * feat_stride);
+
+ const T* p_box = d_anchor4d + h * bottom_W + w;
+ const T* p_score = bottom4d + h * bottom_W + w;
+
+ const size_t proposal_off = (h * bottom_W + w) * num_anchors;
+
+ for (unsigned int anchor = 0; anchor < num_anchors; ++anchor)
+ {
+ const T dx = p_box[(anchor * 4 + 0) * bottom_area] /
+ static_cast(box_coordinate_scale);
+ const T dy = p_box[(anchor * 4 + 1) * bottom_area] /
+ static_cast(box_coordinate_scale);
+
+ const T d_log_w = p_box[(anchor * 4 + 2) * bottom_area] /
+ static_cast(box_size_scale);
+ const T d_log_h = p_box[(anchor * 4 + 3) * bottom_area] /
+ static_cast(box_size_scale);
+ const T score = p_score[anchor * bottom_area];
+
+ float x0 = x + p_anchors_wm[anchor];
+ float y0 = y + p_anchors_hm[anchor];
+ float x1 = x + p_anchors_wp[anchor];
+ float y1 = y + p_anchors_hp[anchor];
+
+ if (initial_clip)
+ {
+ // adjust new corner locations to be within the image region
+ x0 = std::max(0.0f, std::min(x0, img_W));
+ y0 = std::max(0.0f, std::min(y0, img_H));
+ x1 = std::max(0.0f, std::min(x1, img_W));
+ y1 = std::max(0.0f, std::min(y1, img_H));
+ }
+
+ // width & height of box
+ const float ww = x1 - x0 + coordinates_offset;
+ const float hh = y1 - y0 + coordinates_offset;
+ // center location of box
+ const float ctr_x = x0 + 0.5f * ww;
+ const float ctr_y = y0 + 0.5f * hh;
+
+ // new center location according to gradient (dx, dy)
+ const T pred_ctr_x =
+ dx * static_cast(ww) + static_cast(ctr_x);
+ const T pred_ctr_y =
+ dy * static_cast(hh) + static_cast(ctr_y);
+ // new width & height according to gradient d(log w), d(log h)
+ const T pred_w = std::exp(d_log_w) * static_cast(ww);
+ const T pred_h = std::exp(d_log_h) * static_cast(hh);
+
+ // update upper-left corner location
+ x0 = pred_ctr_x - 0.5f * pred_w;
+ y0 = pred_ctr_y - 0.5f * pred_h;
+ // update lower-right corner location
+ x1 = pred_ctr_x + 0.5f * pred_w;
+ y1 = pred_ctr_y + 0.5f * pred_h;
+
+ // adjust new corner locations to be within the image region,
+ if (clip_before_nms)
+ {
+ x0 = std::max(
+ 0.0f, std::min(x0, img_W - coordinates_offset));
+ y0 = std::max(
+ 0.0f, std::min(y0, img_H - coordinates_offset));
+ x1 = std::max(
+ 0.0f, std::min(x1, img_W - coordinates_offset));
+ y1 = std::max(
+ 0.0f, std::min(y1, img_H - coordinates_offset));
+ }
+
+ // recompute new width & height
+ const float box_w = x1 - x0 + coordinates_offset;
+ const float box_h = y1 - y0 + coordinates_offset;
+
+ proposals[proposal_off + anchor].x0 = static_cast(x0);
+ proposals[proposal_off + anchor].y0 = static_cast(y0);
+ proposals[proposal_off + anchor].x1 = static_cast(x1);
+ proposals[proposal_off + anchor].y1 = static_cast(y1);
+ proposals[proposal_off + anchor].score =
+ static_cast((min_box_W <= box_w) * (min_box_H <= box_h)) *
+ score;
+ }
+ }
+ }
+ }
+
+ template
+ static void nms(const int num_boxes,
+ std::vector& is_dead,
+ const std::vector>& proposals,
+ std::vector& index_out,
+ int& num_out,
+ const int base_index,
+ const float nms_thresh,
+ const int max_num_out,
+ T coordinates_offset)
+ {
+ std::fill(is_dead.begin(), is_dead.begin() + num_boxes, 0);
+ for (int box = 0; box < num_boxes; ++box)
+ {
+ if (is_dead[box])
+ continue;
+
+ index_out[num_out++] = base_index + box;
+ if (num_out == max_num_out)
+ break;
+
+ int tail = box + 1;
+ for (; tail < num_boxes; ++tail)
+ {
+ float res = 0.0f;
+
+ const T x0i = proposals[box].x0;
+ const T y0i = proposals[box].y0;
+ const T x1i = proposals[box].x1;
+ const T y1i = proposals[box].y1;
+
+ const T x0j = proposals[tail].x0;
+ const T y0j = proposals[tail].y0;
+ const T x1j = proposals[tail].x1;
+ const T y1j = proposals[tail].y1;
+
+ // +(x0i, y0i)-----------+
+ // | |
+ // | + (x0j, y0j)-|-----+
+ // | | | |
+ // +--------|------------+(x1i, y1i)
+ // | |
+ // +------------------+ (x1j, y1j)
+ // Checking if the boxes are overlapping:
+ // x0i <= x1j && y0i <= y1j first box begins before second ends
+ // x0j <= x1i && y0j <= y1i second box begins before the first ends
+ const bool box_i_begins_before_j_ends = x0i <= x1j && y0i <= y1j;
+ const bool box_j_begins_before_i_ends = x0j <= x1i && y0j <= y1i;
+ if (box_i_begins_before_j_ends && box_j_begins_before_i_ends)
+ {
+ // overlapped region (= box)
+ const T x0 = std::max(x0i, x0j);
+ const T y0 = std::max(y0i, y0j);
+ const T x1 = std::min(x1i, x1j);
+ const T y1 = std::min(y1i, y1j);
+ // intersection area
+ const T width = std::max(0.0f, x1 - x0 + coordinates_offset);
+ const T height = std::max(0.0f, y1 - y0 + coordinates_offset);
+ const T area = width * height;
+ // area of A, B
+ const T A_area = (x1i - x0i + coordinates_offset) *
+ (y1i - y0i + coordinates_offset);
+ const T B_area = (x1j - x0j + coordinates_offset) *
+ (y1j - y0j + coordinates_offset);
+
+ // IoU
+ res = static_cast(area / (A_area + B_area - area));
+ }
+ if (nms_thresh < res)
+ is_dead[tail] = 1;
+ }
+ }
+ }
+
+ template
+ static void retrieve_rois(const int num_rois,
+ const int item_index,
+ const int num_proposals,
+ const std::vector>& proposals,
+ const std::vector& roi_indices,
+ T* rois,
+ int post_nms_topn_,
+ bool normalize,
+ float img_h,
+ float img_w,
+ bool clip_after_nms,
+ T* probs = nullptr)
+ {
+ for (size_t roi = 0; roi < num_rois; ++roi)
+ {
+ const unsigned int index = roi_indices[roi];
+ T x0 = proposals[index].x0;
+ T y0 = proposals[index].y0;
+ T x1 = proposals[index].x1;
+ T y1 = proposals[index].y1;
+
+ if (clip_after_nms)
+ {
+ x0 = std::max(0.0f, std::min(x0, static_cast(img_w)));
+ y0 = std::max(0.0f, std::min(y0, static_cast(img_h)));
+ x1 = std::max(0.0f, std::min(x1, static_cast(img_w)));
+ y1 = std::max(0.0f, std::min(y1, static_cast(img_h)));
+ }
+
+ if (normalize)
+ {
+ x0 /= static_cast(img_w);
+ y0 /= static_cast(img_h);
+ x1 /= static_cast(img_w);
+ y1 /= static_cast(img_h);
+ }
+
+ rois[roi * 5 + 0] = static_cast(item_index);
+ rois[roi * 5 + 1] = x0;
+ rois[roi * 5 + 2] = y0;
+ rois[roi * 5 + 3] = x1;
+ rois[roi * 5 + 4] = y1;
+
+ if (probs)
+ {
+ probs[roi] = proposals[index].score;
+ }
+ }
+
+ if (num_rois < post_nms_topn_)
+ {
+ for (int i = num_rois; i < post_nms_topn_; i++)
+ {
+ rois[i * 5 + 0] = static_cast(0.f);
+ rois[i * 5 + 1] = static_cast(0.f);
+ rois[i * 5 + 2] = static_cast(0.f);
+ rois[i * 5 + 3] = static_cast(0.f);
+ rois[i * 5 + 4] = static_cast(0.f);
+ if (probs)
+ {
+ probs[i] = static_cast(0.f);
+ }
+ }
+ // TODO: decision has to be made regarding alignment to plugins
+ // in the matter of how this should be terminated
+ rois[num_rois * 5] = static_cast(-1);
+ }
+ }
+
+ template
+ static void proposal_exec(const T* class_probs,
+ const T* bbox_deltas,
+ const T* image_shape,
+ T* output,
+ T* out_probs,
+ const Shape& class_probs_shape,
+ const Shape& bbox_deltas_shape,
+ const Shape& image_shape_shape,
+ const Shape& output_shape,
+ const Shape& out_probs_shape,
+ const op::ProposalAttrs& attrs)
+ {
+ const T* p_bottom_item = class_probs;
+ const T* p_d_anchor_item = bbox_deltas;
+ T* p_roi_item = output;
+ T* p_prob_item = attrs.infer_probs ? out_probs : nullptr;
+
+ // bottom shape (batch_size * (2 * num_anchors) * H * W)
+ const unsigned int bottom_H = class_probs_shape[2];
+ const unsigned int bottom_W = class_probs_shape[3];
+ // input image height and width
+ const T img_H = image_shape[0];
+ const T img_W = image_shape[1];
+ // scale factor for H and W, depends on shape of image_shape
+ // can be split into H and W {image_height, image_width, scale_height,
+ // scale_width}
+ // or be the same for both {image_height, image_width, scale_height_and_width}
+ const T scale_H = image_shape[2];
+ const T scale_W = (image_shape_shape.size() < 4 ? scale_H : image_shape[3]);
+ const T min_box_H = attrs.min_size * scale_H;
+ const T min_box_W = attrs.min_size * scale_W;
+ // get number of proposals
+ // class_probs shape is {batch_size, anchor_count*2, bottom_H, bottom_W}
+ const unsigned int anchor_count = class_probs_shape[1] / 2;
+ const unsigned int num_proposals = anchor_count * bottom_H * bottom_W;
+ // final RoI count
+ int num_rois = 0;
+ std::vector> proposals(num_proposals);
+ const int pre_nms_topn =
+ num_proposals < attrs.pre_nms_topn ? num_proposals : attrs.pre_nms_topn;
+ std::vector is_dead(pre_nms_topn);
+ std::vector roi_indices(attrs.post_nms_topn);
+
+ std::vector anchors = generate_anchors(attrs, anchor_count);
+
+ unsigned int batch_num = class_probs_shape[0];
+ float coordinates_offset = attrs.framework == "tensorflow" ? 0.0f : 1.0f;
+ bool initial_clip = attrs.framework == "tensorflow";
+ bool swap_xy = attrs.framework == "tensorflow";
+
+ for (unsigned int batch_idx = 0; batch_idx < batch_num; ++batch_idx)
+ {
+ enumerate_proposals(p_bottom_item + num_proposals +
+ batch_idx * num_proposals * 2,
+ p_d_anchor_item + batch_idx * num_proposals * 4,
+ anchors.data(),
+ proposals,
+ anchor_count,
+ bottom_H,
+ bottom_W,
+ img_H,
+ img_W,
+ min_box_H,
+ min_box_W,
+ attrs.feat_stride,
+ attrs.box_coordinate_scale,
+ attrs.box_size_scale,
+ coordinates_offset,
+ initial_clip,
+ swap_xy,
+ attrs.clip_before_nms);
+
+ std::partial_sort(
+ proposals.begin(),
+ proposals.begin() + pre_nms_topn,
+ proposals.end(),
+ [](const ProposalBox& box1, const ProposalBox& box2) {
+ return (box1.score > box2.score);
+ });
+ nms(pre_nms_topn,
+ is_dead,
+ proposals,
+ roi_indices,
+ num_rois,
+ 0,
+ attrs.nms_thresh,
+ attrs.post_nms_topn,
+ static_cast(coordinates_offset));
+
+ T* p_probs =
+ p_prob_item ? p_prob_item + batch_idx * attrs.post_nms_topn : nullptr;
+ retrieve_rois(num_rois,
+ batch_idx,
+ pre_nms_topn,
+ proposals,
+ roi_indices,
+ p_roi_item + batch_idx * attrs.post_nms_topn * 5,
+ attrs.post_nms_topn,
+ attrs.normalize,
+ img_H,
+ img_W,
+ attrs.clip_after_nms,
+ p_probs);
+ }
+ }
+ } // namespace details
+
+ template
+ void proposal_v0(const T* class_probs,
+ const T* bbox_deltas,
+ const T* image_shape,
+ T* output,
+ const Shape& class_probs_shape,
+ const Shape& bbox_deltas_shape,
+ const Shape& image_shape_shape,
+ const Shape& output_shape,
+ const op::ProposalAttrs& attrs)
+ {
+ details::proposal_exec(class_probs,
+ bbox_deltas,
+ image_shape,
+ output,
+ static_cast(nullptr),
+ class_probs_shape,
+ bbox_deltas_shape,
+ image_shape_shape,
+ output_shape,
+ Shape{},
+ attrs);
+ }
+
+ template
+ void proposal_v4(const T* class_probs,
+ const T* bbox_deltas,
+ const T* image_shape,
+ T* output,
+ T* out_probs,
+ const Shape& class_probs_shape,
+ const Shape& bbox_deltas_shape,
+ const Shape& image_shape_shape,
+ const Shape& output_shape,
+ const Shape& out_probs_shape,
+ const op::ProposalAttrs& attrs)
+ {
+ details::proposal_exec(class_probs,
+ bbox_deltas,
+ image_shape,
+ output,
+ out_probs,
+ class_probs_shape,
+ bbox_deltas_shape,
+ image_shape_shape,
+ output_shape,
+ out_probs_shape,
+ attrs);
+ }
+ } // namespace reference
+ } // namespace runtime
+} // namespace ngraph
diff --git a/ngraph/core/src/op/proposal.cpp b/ngraph/core/src/op/proposal.cpp
index a47f690f94c3e4..132096502394b4 100644
--- a/ngraph/core/src/op/proposal.cpp
+++ b/ngraph/core/src/op/proposal.cpp
@@ -37,51 +37,92 @@ op::v0::Proposal::Proposal(const Output& class_probs,
void op::v0::Proposal::validate_and_infer_types()
{
NGRAPH_OP_SCOPE(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);
- if (class_probs_pshape.is_static() && class_bbox_deltas_pshape.is_static() &&
- image_shape_pshape.is_static())
+ const auto& class_probs_ps = get_input_partial_shape(0);
+ const auto& bbox_deltas_ps = get_input_partial_shape(1);
+ const auto& image_shape_ps = get_input_partial_shape(2);
+ Dimension out_dim = Dimension::dynamic();
+ NODE_VALIDATION_CHECK(this,
+ get_input_element_type(0).is_real(),
+ "Proposal layer input class_probs should have floating point type (",
+ get_input_element_type(0),
+ ").");
+
+ NODE_VALIDATION_CHECK(this,
+ get_input_element_type(1).is_real(),
+ "Proposal layer input bbox_deltas should have floating point type (",
+ get_input_element_type(1),
+ ").");
+
+ NODE_VALIDATION_CHECK(this,
+ get_input_element_type(2).is_real(),
+ "Proposal layer input image_shape should have floating point type (",
+ get_input_element_type(2),
+ ").");
+
+ NODE_VALIDATION_CHECK(this,
+ class_probs_ps.rank().compatible(4),
+ "Proposal layer shape class_probs should be rank 4 compatible (",
+ class_probs_ps,
+ ").");
+
+ NODE_VALIDATION_CHECK(this,
+ bbox_deltas_ps.rank().compatible(4),
+ "Proposal layer shape bbox_deltas should be rank 4 compatible (",
+ bbox_deltas_ps,
+ ").");
+
+ NODE_VALIDATION_CHECK(this,
+ image_shape_ps.rank().compatible(1),
+ "Proposal layer shape image_shape should be rank 1 compatible (",
+ image_shape_ps,
+ ").");
+
+ if (bbox_deltas_ps.is_static() && class_probs_ps.is_static())
{
- const Shape class_probs_shape{class_probs_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,
- ").");
-
+ // class probs and bbox deltas shapes are static, check anchor count and batch number
+ // consistency
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,
+ class_probs_ps[1].get_length() * 2 == bbox_deltas_ps[1].get_length(),
+ "Anchor number inconsistent between class_probs (",
+ class_probs_ps[1].get_length() / 2,
+ "), and bbox_deltas (",
+ bbox_deltas_ps[1].get_length() / 4,
").");
- NODE_VALIDATION_CHECK(
- this,
- image_shape_shape.size() == 1,
- "Proposal layer image_shape input must have rank 1 (image_shape_shape: ",
- image_shape_shape,
- ").");
+ NODE_VALIDATION_CHECK(this,
+ class_probs_ps[0] == bbox_deltas_ps[0],
+ "Batch size inconsistent between class_probs (",
+ class_probs_ps[0],
+ ") and bbox deltas (",
+ bbox_deltas_ps[0],
+ ").");
+ }
+ if (image_shape_ps.is_static())
+ {
NODE_VALIDATION_CHECK(
this,
- image_shape_shape[0] >= 3 && image_shape_shape[0] <= 4,
+ image_shape_ps[0].get_length() >= 3 && image_shape_ps[0].get_length() <= 4,
"Image_shape 1D tensor must have => 3 and <= 4 elements (image_shape_shape[0]",
- image_shape_shape[0],
+ image_shape_ps[0],
").");
+ }
- 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 (class_probs_ps.rank().is_static() && bbox_deltas_ps.rank().is_static())
+ {
+ out_dim = (class_probs_ps[0] & bbox_deltas_ps[0]);
+ }
+ else if (class_probs_ps.rank().is_static())
+ {
+ out_dim = class_probs_ps[0];
}
- else
+ else if (bbox_deltas_ps.rank().is_static())
{
- set_output_type(0, get_input_element_type(0), PartialShape::dynamic());
+ out_dim = bbox_deltas_ps[0];
}
+
+ // intersect the batch size
+ set_output_type(0, get_input_element_type(0), PartialShape{out_dim * m_attrs.post_nms_topn, 5});
}
shared_ptr op::v0::Proposal::clone_with_new_inputs(const OutputVector& new_args) const
@@ -126,17 +167,14 @@ void op::v4::Proposal::validate_and_infer_types()
{
NGRAPH_OP_SCOPE(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[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), PartialShape{batch_size * m_attrs.post_nms_topn});
- else
- set_output_type(1, get_input_element_type(0), PartialShape::dynamic());
+ // Output shape was inferred in v0's validate_and_infer_types
+ const auto proposals_ps = get_output_partial_shape(0);
+ auto out_ps = PartialShape{Dimension::dynamic()};
+ if (proposals_ps.rank().is_static() && proposals_ps.rank().compatible(2))
+ {
+ out_ps = PartialShape{proposals_ps[0]};
+ }
+ set_output_type(1, get_input_element_type(0), out_ps);
}
std::shared_ptr op::v4::Proposal::clone_with_new_inputs(const OutputVector& new_args) const
diff --git a/ngraph/test/CMakeLists.txt b/ngraph/test/CMakeLists.txt
index fc7f38e31deccd..42a366cedde6f3 100644
--- a/ngraph/test/CMakeLists.txt
+++ b/ngraph/test/CMakeLists.txt
@@ -316,6 +316,7 @@ set(MULTI_TEST_SRC
backend/pad.in.cpp
backend/parameter_as_output.in.cpp
backend/power.in.cpp
+ backend/proposal.in.cpp
backend/psroi_pooling.in.cpp
backend/range.in.cpp
backend/reduce_max.in.cpp
diff --git a/ngraph/test/attributes.cpp b/ngraph/test/attributes.cpp
index f94497f258533b..140c44046a9fab 100644
--- a/ngraph/test/attributes.cpp
+++ b/ngraph/test/attributes.cpp
@@ -1598,9 +1598,9 @@ TEST(attributes, prior_box_clustered_op)
TEST(attributes, proposal_op)
{
NodeBuilder::get_ops().register_factory();
- const auto class_probs = make_shared(element::i64, Shape{1024, 3, 128, 128});
- const auto class_logits = make_shared(element::i64, Shape{1024, 3, 128, 128});
- const auto image_shape = make_shared(element::i64, Shape{4});
+ const auto class_probs = make_shared(element::f32, Shape{1024, 2, 128, 128});
+ const auto class_logits = make_shared(element::f32, Shape{1024, 4, 128, 128});
+ const auto image_shape = make_shared(element::f32, Shape{4});
op::ProposalAttrs attrs;
attrs.base_size = 224;
diff --git a/ngraph/test/backend/proposal.in.cpp b/ngraph/test/backend/proposal.in.cpp
new file mode 100644
index 00000000000000..e169472b5ab2fc
--- /dev/null
+++ b/ngraph/test/backend/proposal.in.cpp
@@ -0,0 +1,265 @@
+//*****************************************************************************
+// Copyright 2021 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//*****************************************************************************
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "gtest/gtest.h"
+#include "ngraph/ngraph.hpp"
+#include "ngraph/op/proposal.hpp"
+#include "util/engine/test_engines.hpp"
+#include "util/test_case.hpp"
+#include "util/test_control.hpp"
+NGRAPH_SUPPRESS_DEPRECATED_START
+
+using namespace std;
+using namespace ngraph;
+
+static string s_manifest = "${MANIFEST}";
+using TestEngine = test::ENGINE_CLASS_NAME(${BACKEND_NAME});
+
+constexpr float cls_scores_data[] = {
+ 0.000240f, 0.003802f, 0.111432f, 0.000503f, 0.007887f, 0.144701f, 0.399074f, 0.004680f, // 0
+ 0.139741f, 0.002386f, 0.030003f, 0.276552f, 0.000267f, 0.022971f, 0.287953f, 0.050235f, // 8
+ 0.002580f, 0.206311f, 0.000146f, 0.009656f, 0.175462f, 0.000147f, 0.014718f, 0.272348f, // 16
+ 0.065199f, 0.003286f, 0.185335f, 0.003720f, 0.025932f, 0.251401f, 0.001465f, 0.090447f, // 24
+ 0.488469f, 0.092259f, 0.019306f, 0.379091f, 0.005311f, 0.010369f, 0.087615f, 0.042003f, // 32
+ 0.073871f, 0.416763f, 0.044282f, 0.069776f, 0.313032f, 0.000457f, 0.017346f, 0.089762f, // 40
+ 0.000820f, 0.103986f, 0.367993f, 0.026315f, 0.035701f, 0.299252f, 0.000135f, 0.017825f, // 48
+ 0.150119f, 0.000076f, 0.050511f, 0.269601f, 0.026680f, 0.003541f, 0.189765f, 0.000051f, // 56
+ 0.004315f, 0.193150f, 0.000032f, 0.007254f, 0.185557f, 0.051526f, 0.000657f, 0.117579f, // 64
+ 0.000115f, 0.010179f, 0.293187f, 0.000025f, 0.006505f, 0.175345f, 0.032587f, 0.000469f, // 72
+ 0.098443f, 0.000121f, 0.009600f, 0.322782f, 0.000032f, 0.004543f, 0.166860f, 0.044911f, // 80
+ 0.000187f, 0.102691f, 0.000242f, 0.005502f, 0.107865f, 0.000191f, 0.005336f, 0.086893f, // 88
+ 0.078422f, 0.000345f, 0.079096f, 0.000281f, 0.016388f, 0.214072f, 0.000107f, 0.012027f, // 96
+ 0.192754f, 0.049531f, 0.000386f, 0.149893f, 0.000374f, 0.016965f, 0.204781f, 0.000163f, // 104
+ 0.016272f, 0.215277f, 0.032298f, 0.000857f, 0.133426f, 0.000614f, 0.020215f, 0.165789f, // 112
+ 0.000225f, 0.036951f, 0.262195f, 0.087675f, 0.004596f, 0.147764f, 0.000219f, 0.010502f, // 120
+ 0.163394f, 0.000152f, 0.023116f, 0.241702f, 0.081800f, 0.002197f, 0.146637f, 0.000193f, // 128
+ 0.012017f, 0.133497f, 0.000375f, 0.028605f, 0.309179f, 0.065962f, 0.005508f, 0.155530f, // 136
+ 0.000186f, 0.004540f, 0.079319f, 0.000799f, 0.031003f, 0.303045f, 0.051473f, 0.017770f, // 144
+ 0.206188f, 0.000202f, 0.004291f, 0.061095f, 0.001109f, 0.018094f, 0.156639f, 0.026062f, // 152
+ 0.005270f, 0.148651f, 0.000026f, 0.007300f, 0.096013f, 0.000383f, 0.022134f, 0.129511f, // 160
+ 0.080882f, 0.003416f, 0.129922f, 0.000037f, 0.010040f, 0.130007f, 0.000116f, 0.014904f, // 168
+ 0.171423f, 0.082893f, 0.000921f, 0.154976f, 0.000142f, 0.016552f, 0.209696f, 0.000227f, // 176
+ 0.022418f, 0.228501f, 0.111712f, 0.001987f, 0.158164f, 0.001200f, 0.027049f, 0.308222f, // 184
+ 0.001366f, 0.038146f, 0.287945f, 0.072526f, 0.016064f, 0.257895f, 0.000595f, 0.016962f, // 192
+};
+constexpr float bbox_pred_data[] = {
+ 0.006756f, -0.055635f, 0.030843f, 0.007482f, 0.009056f, -0.041824f, 0.119722f, 0.168988f,
+ 0.002822f, 0.039733f, 0.109005f, 0.245152f, -0.013196f, -0.018222f, -0.170122f, -0.374904f,
+ -0.005455f, -0.034059f, -0.006787f, 0.072005f, -0.017933f, -0.007358f, 0.034149f, 0.123846f,
+ 0.128319f, 0.016107f, -0.615487f, -1.235094f, -0.024253f, -0.019406f, 0.134142f, 0.157853f,
+ -0.021119f, 0.007383f, 0.089365f, 0.092854f, 0.062491f, 0.002366f, 0.122464f, -0.003326f,
+ 0.015468f, -0.034088f, 0.079009f, 0.075483f, 0.011972f, 0.042427f, 0.106865f, 0.158754f,
+ 0.071211f, -0.034009f, 0.007985f, -0.441477f, 0.009046f, -0.028515f, 0.095372f, 0.119598f,
+ -0.007553f, -0.0072f, 0.105072f, 0.084314f, 0.23268f, -0.02906f, -0.408454f, -1.13439f,
+ 0.016202f, -0.037859f, 0.130873f, 0.129652f, 0.002064f, -0.011969f, 0.171623f, 0.050218f,
+ 0.113831f, 0.028922f, 0.017785f, 0.059708f, 0.037658f, -0.011245f, 0.097197f, 0.137491f,
+ 0.024218f, 0.04739f, 0.091978f, 0.217333f, 0.088418f, -0.004662f, -0.095168f, -0.397928f,
+ 0.02639f, -0.008501f, 0.068487f, 0.108465f, 0.020069f, 0.018829f, 0.040206f, 0.068473f,
+ 0.226458f, -0.072871f, -0.672384f, -1.447558f, 0.039598f, 0.017471f, 0.187288f, 0.08409f,
+ 0.017152f, -0.00516f, 0.183419f, 0.068469f, 0.063944f, 0.160725f, -0.022493f, -0.132291f,
+ 0.010542f, 0.036318f, 0.074042f, -0.013323f, 0.00808f, 0.060365f, 0.120566f, 0.21866f,
+ 0.046324f, 0.088741f, 0.029469f, -0.517183f, 0.00917f, 0.011915f, 0.053674f, 0.140168f,
+ 0.0033f, 0.022759f, -0.006196f, 0.063839f, 0.083726f, -0.088385f, -0.57208f, -1.454211f,
+ 0.020655f, 0.010788f, 0.134951f, 0.109709f, 0.015445f, -0.015363f, 0.109153f, 0.051209f,
+ 0.024297f, 0.139126f, -0.12358f, -0.127979f, 0.004587f, 0.004751f, 0.047292f, 0.027066f,
+ 0.011003f, 0.069887f, 0.117052f, 0.267419f, 0.039306f, 0.077584f, 0.02579f, -0.496149f,
+ -0.005569f, 0.015494f, -0.011662f, 0.105549f, -0.007015f, 0.031984f, -0.075742f, 0.0852f,
+ 0.023886f, -0.053107f, -0.325533f, -1.329066f, 0.004688f, 0.034501f, 0.089317f, 0.042463f,
+ 0.004212f, -0.015128f, 0.00892f, 0.028266f, 0.009997f, 0.157822f, 0.020116f, -0.142337f,
+ 0.008199f, 0.046564f, 0.083014f, 0.046307f, 0.006771f, 0.084997f, 0.141935f, 0.228339f,
+ -0.020308f, 0.077745f, -0.018319f, -0.522311f, 0.010432f, 0.024641f, 0.020571f, 0.097148f,
+ 0.002064f, 0.035053f, -0.121995f, 0.012222f, -0.030779f, 0.100481f, -0.331737f, -1.257669f,
+ -0.013079f, 0.021227f, 0.159949f, 0.120097f, 0.005765f, -0.012335f, -0.005268f, 0.042067f,
+ -0.043972f, 0.102556f, 0.180494f, -0.084721f, -0.011962f, 0.031302f, 0.112511f, 0.027557f,
+ -0.002085f, 0.082978f, 0.149409f, 0.195091f, -0.033731f, 0.019861f, -0.064047f, -0.471328f,
+ -0.004093f, 0.016803f, 0.044635f, 0.058912f, -0.018735f, 0.035536f, -0.050373f, -0.002794f,
+ -0.086705f, 0.038435f, -0.301466f, -1.071246f, -0.028247f, 0.018984f, 0.254702f, 0.141142f,
+ -0.017522f, 0.014843f, 0.079391f, 0.079662f, -0.051204f, 0.048419f, 0.235604f, -0.185797f,
+ -0.019569f, 0.02678f, 0.162507f, 0.046435f, -0.004606f, 0.08806f, 0.18634f, 0.193957f,
+ -0.024333f, -0.01298f, -0.17977f, -0.65881f, -0.003778f, 0.007418f, 0.065439f, 0.104549f,
+ -0.027706f, 0.03301f, 0.057492f, 0.032019f, -0.135337f, 0.000269f, -0.250203f, -1.181688f,
+ -0.027022f, -0.006755f, 0.206848f, 0.129268f, -0.003529f, 0.013445f, 0.181484f, 0.139955f,
+ -0.036587f, 0.065824f, 0.288751f, -0.110813f, -0.015578f, 0.044818f, 0.17756f, 0.006914f,
+ 0.002329f, 0.068982f, 0.189079f, 0.184253f, 0.00301f, -0.039168f, -0.010855f, -0.393254f,
+ 0.000028f, 0.001906f, 0.07217f, 0.063305f, -0.026144f, 0.028842f, 0.139149f, 0.023377f,
+ 0.023362f, 0.023559f, -0.145386f, -0.863572f, -0.015749f, -0.021364f, 0.172571f, 0.078393f,
+ -0.037253f, 0.014978f, 0.221502f, 0.189111f, -0.048956f, 0.085409f, 0.325399f, -0.058294f,
+ -0.028495f, 0.021663f, 0.19392f, 0.02706f, 0.006908f, 0.065751f, 0.176395f, 0.138375f,
+ 0.012418f, -0.031228f, -0.008762f, -0.427345f, -0.013677f, -0.002429f, 0.069655f, 0.019505f,
+ -0.036763f, 0.022528f, 0.201062f, 0.022205f, 0.024528f, 0.06241f, -0.076237f, -0.840695f,
+ -0.007268f, -0.027865f, 0.211056f, 0.074744f, -0.053563f, 0.006863f, 0.301432f, 0.192879f,
+ -0.021944f, 0.100535f, 0.19031f, -0.133746f, -0.006151f, 0.023944f, 0.13561f, -0.03259f,
+ 0.000618f, 0.063736f, 0.180904f, 0.12393f, 0.001275f, -0.0306f, -0.032822f, -0.496515f,
+ 0.009757f, 0.014602f, 0.004532f, -0.039969f, -0.015984f, 0.047726f, 0.099865f, 0.003163f,
+ 0.026623f, 0.117951f, -0.076234f, -0.811997f, 0.01301f, 0.020042f, 0.173756f, -0.036191f,
+ -0.068887f, 0.0229f, 0.245465f, 0.214282f, -0.011054f, 0.132813f, 0.241014f, -0.148763f,
+};
+constexpr float proposal_ref[] = {
+ 0.000000f, 0.000000f, 0.000000f, 349.000000f, 209.000000f, // 0
+ 0.000000f, 0.000000f, 0.000000f, 237.625443f, 209.000000f, // 5
+ 0.000000f, 140.305511f, 0.000000f, 349.000000f, 209.000000f, // 10
+ 0.000000f, 0.000000f, 0.000000f, 349.000000f, 65.359818f, // 15
+ 0.000000f, 0.000000f, 0.000000f, 349.000000f, 130.324097f, // 20
+ 0.000000f, 0.000000f, 15.562508f, 97.587891f, 181.224182f, // 25
+ 0.000000f, 0.000000f, 68.539543f, 250.406708f, 209.000000f, // 30
+ 0.000000f, 0.000000f, 0.000000f, 195.881531f, 99.841385f, // 35
+ 0.000000f, 0.000000f, 0.000000f, 78.303986f, 209.000000f, // 40
+ 0.000000f, 0.000000f, 0.000000f, 0.000000f, 209.000000f, // 45
+};
+
+constexpr float probs_ref[] = {
+ 0.3091790f,
+ 0.1555300f,
+ 0.1549760f,
+ 0.1466370f,
+ 0.0260620f,
+ 0.0177700f,
+ 0.0019870f,
+ 0.0008570f,
+ 0.0002190f,
+ 0.0000000f,
+};
+
+NGRAPH_TEST(${BACKEND_NAME}, proposal_v0_basic)
+{
+ const float iou_threshold = 0.7f;
+ const int min_bbox_size = 16;
+ const int feature_stride = 16;
+ const int pre_nms_topn = 6000;
+ const int post_nms_topn = 10;
+ const int image_w = 350;
+ const int image_h = 210;
+ const int image_z = 1;
+ const std::vector ratios = {0.5f};
+ const std::vector scales = {32.0f};
+ const int batch_size = 1;
+
+ Shape class_probs_shape{batch_size, 2, 10, 10};
+ Shape bbox_deltas_shape{batch_size, 4, 10, 10};
+ Shape image_shape_shape{3};
+ Shape output_shape = {batch_size * post_nms_topn, 5};
+
+ op::ProposalAttrs attrs;
+ attrs.base_size = min_bbox_size;
+ attrs.min_size = min_bbox_size;
+ attrs.pre_nms_topn = pre_nms_topn;
+ attrs.post_nms_topn = post_nms_topn;
+ attrs.nms_thresh = iou_threshold;
+ attrs.feat_stride = feature_stride;
+ attrs.min_size = min_bbox_size;
+ attrs.ratio = ratios;
+ attrs.scale = scales;
+ attrs.clip_before_nms = true;
+ attrs.clip_after_nms = false;
+ attrs.normalize = false;
+ attrs.box_size_scale = 1.0f;
+ attrs.box_coordinate_scale = 1.0f;
+ attrs.framework = "";
+ attrs.infer_probs = false;
+
+ auto class_probs_param = make_shared(element::f32, class_probs_shape);
+ auto bbox_deltas_param = make_shared(element::f32, bbox_deltas_shape);
+ auto image_shape_param = make_shared(element::f32, image_shape_shape);
+
+ auto proposal = make_shared(
+ class_probs_param, bbox_deltas_param, image_shape_param, attrs);
+ auto f = make_shared(
+ proposal, ParameterVector{class_probs_param, bbox_deltas_param, image_shape_param});
+
+ std::vector c{std::begin(cls_scores_data), std::end(cls_scores_data)};
+ std::vector b{std::begin(bbox_pred_data), std::end(bbox_pred_data)};
+ std::vector i{image_h, image_w, image_z};
+
+ auto test_case = test::TestCase(f);
+ test_case.add_input(class_probs_shape, c);
+ test_case.add_input(bbox_deltas_shape, b);
+ test_case.add_input(image_shape_shape, i);
+
+ std::vector o{std::begin(proposal_ref), std::end(proposal_ref)};
+ test_case.add_expected_output(output_shape, o);
+ test_case.run();
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, proposal_v4_basic)
+{
+ const float iou_threshold = 0.7f;
+ const int min_bbox_size = 16;
+ const int feature_stride = 16;
+ const int pre_nms_topn = 6000;
+ const int post_nms_topn = 10;
+ const int image_w = 350;
+ const int image_h = 210;
+ const int image_z = 1;
+ const std::vector ratios = {0.5f};
+ const std::vector scales = {32.0f};
+ const int batch_size = 1;
+
+ Shape class_probs_shape{batch_size, 2, 10, 10};
+ Shape bbox_deltas_shape{batch_size, 4, 10, 10};
+ Shape image_shape_shape{3};
+ Shape output_shape = {batch_size * post_nms_topn, 5};
+ Shape probs_shape = {batch_size * post_nms_topn};
+
+ op::ProposalAttrs attrs;
+ attrs.base_size = min_bbox_size;
+ attrs.min_size = min_bbox_size;
+ attrs.pre_nms_topn = pre_nms_topn;
+ attrs.post_nms_topn = post_nms_topn;
+ attrs.nms_thresh = iou_threshold;
+ attrs.feat_stride = feature_stride;
+ attrs.min_size = min_bbox_size;
+ attrs.ratio = ratios;
+ attrs.scale = scales;
+ attrs.clip_before_nms = true;
+ attrs.clip_after_nms = false;
+ attrs.normalize = false;
+ attrs.box_size_scale = 1.0f;
+ attrs.box_coordinate_scale = 1.0f;
+ attrs.framework = "";
+ attrs.infer_probs = true;
+
+ auto class_probs_param = make_shared(element::f32, class_probs_shape);
+ auto bbox_deltas_param = make_shared(element::f32, bbox_deltas_shape);
+ auto image_shape_param = make_shared(element::f32, image_shape_shape);
+
+ auto proposal = make_shared(
+ class_probs_param, bbox_deltas_param, image_shape_param, attrs);
+ auto f = make_shared(
+ proposal, ParameterVector{class_probs_param, bbox_deltas_param, image_shape_param});
+
+ std::vector c{std::begin(cls_scores_data), std::end(cls_scores_data)};
+ std::vector b{std::begin(bbox_pred_data), std::end(bbox_pred_data)};
+ std::vector i{image_h, image_w, image_z};
+
+ auto test_case = test::TestCase(f);
+ test_case.add_input(class_probs_shape, c);
+ test_case.add_input(bbox_deltas_shape, b);
+ test_case.add_input(image_shape_shape, i);
+
+ std::vector o{std::begin(proposal_ref), std::end(proposal_ref)};
+ std::vector p{std::begin(probs_ref), std::end(probs_ref)};
+
+ test_case.add_expected_output(output_shape, o);
+ test_case.add_expected_output(probs_shape, p);
+ test_case.run();
+}
\ No newline at end of file
diff --git a/ngraph/test/runtime/interpreter/evaluates_map.cpp b/ngraph/test/runtime/interpreter/evaluates_map.cpp
index 3f709c5cb9a609..6b345fd1c6d4c9 100644
--- a/ngraph/test/runtime/interpreter/evaluates_map.cpp
+++ b/ngraph/test/runtime/interpreter/evaluates_map.cpp
@@ -56,6 +56,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -991,6 +992,44 @@ namespace
return true;
}
+ template
+ bool evaluate(const shared_ptr& op,
+ const HostTensorVector& outputs,
+ const HostTensorVector& inputs)
+ {
+ using T = typename element_type_traits::value_type;
+ runtime::reference::proposal_v0(inputs[0]->get_data_ptr(),
+ inputs[1]->get_data_ptr(),
+ inputs[2]->get_data_ptr(),
+ outputs[0]->get_data_ptr(),
+ inputs[0]->get_shape(),
+ inputs[1]->get_shape(),
+ inputs[2]->get_shape(),
+ outputs[0]->get_shape(),
+ op.get()->get_attrs());
+ return true;
+ }
+
+ template
+ bool evaluate(const shared_ptr& op,
+ const HostTensorVector& outputs,
+ const HostTensorVector& inputs)
+ {
+ using T = typename element_type_traits::value_type;
+ runtime::reference::proposal_v4(inputs[0]->get_data_ptr(),
+ inputs[1]->get_data_ptr(),
+ inputs[2]->get_data_ptr(),
+ outputs[0]->get_data_ptr(),
+ outputs[1]->get_data_ptr(),
+ inputs[0]->get_shape(),
+ inputs[1]->get_shape(),
+ inputs[2]->get_shape(),
+ outputs[0]->get_shape(),
+ outputs[1]->get_shape(),
+ op.get()->get_attrs());
+ return true;
+ }
+
template
bool evaluate(const shared_ptr& op,
const HostTensorVector& outputs,
diff --git a/ngraph/test/runtime/interpreter/opset_int_tbl.hpp b/ngraph/test/runtime/interpreter/opset_int_tbl.hpp
index 93f473eef8abf9..ad76a6a6fee018 100644
--- a/ngraph/test/runtime/interpreter/opset_int_tbl.hpp
+++ b/ngraph/test/runtime/interpreter/opset_int_tbl.hpp
@@ -35,6 +35,7 @@ NGRAPH_OP(LRN, ngraph::op::v0)
NGRAPH_OP(MVN, ngraph::op::v0)
NGRAPH_OP(NormalizeL2, op::v0)
NGRAPH_OP(PriorBox, ngraph::op::v0)
+NGRAPH_OP(Proposal, ngraph::op::v0)
NGRAPH_OP(PSROIPooling, op::v0)
NGRAPH_OP(RegionYolo, op::v0)
NGRAPH_OP(Relu, op::v0)
@@ -78,6 +79,7 @@ NGRAPH_OP(ShapeOf, op::v3)
NGRAPH_OP(CTCLoss, op::v4)
NGRAPH_OP(LSTMCell, op::v4)
+NGRAPH_OP(Proposal, op::v4)
NGRAPH_OP(BatchNormInference, op::v5)
NGRAPH_OP(GatherND, op::v5)
diff --git a/ngraph/test/type_prop/proposal.cpp b/ngraph/test/type_prop/proposal.cpp
index 7055c5c1ec001a..9eb5cbe15ac44d 100644
--- a/ngraph/test/type_prop/proposal.cpp
+++ b/ngraph/test/type_prop/proposal.cpp
@@ -41,7 +41,32 @@ TEST(type_prop, proposal_v0_invalid_class_probs_rank)
catch (const NodeValidationFailure& error)
{
EXPECT_HAS_SUBSTRING(
- error.what(), std::string("Proposal layer shape class_probs input must have rank 4"));
+ error.what(),
+ std::string("Proposal layer shape class_probs should be rank 4 compatible"));
+ }
+ catch (...)
+ {
+ FAIL() << "Deduced type check failed for unexpected reason";
+ }
+}
+
+TEST(type_prop, proposal_v0_invalid_anchor_count)
+{
+ op::ProposalAttrs attrs;
+ auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4});
+ auto class_bbox_deltas = make_shared(element::f32, Shape{1, 2, 3, 4});
+ auto image_shape = make_shared(element::f32, Shape{3});
+
+ try
+ {
+ auto proposal =
+ make_shared(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("Anchor number inconsistent between"));
}
catch (...)
{
@@ -67,7 +92,7 @@ TEST(type_prop, proposal_v0_invalid_class_bbox_deltas_rank)
{
EXPECT_HAS_SUBSTRING(
error.what(),
- std::string("Proposal layer shape class_bbox_deltas_shape input must have rank 4"));
+ std::string("Proposal layer shape bbox_deltas should be rank 4 compatible"));
}
catch (...)
{
@@ -79,7 +104,7 @@ TEST(type_prop, proposal_v0_invalid_image_shape_rank)
{
op::ProposalAttrs attrs;
auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4});
- auto class_bbox_deltas = make_shared(element::f32, Shape{1, 2, 3, 4});
+ auto class_bbox_deltas = make_shared(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared(element::f32, Shape{2, 1});
try
@@ -91,8 +116,9 @@ TEST(type_prop, proposal_v0_invalid_image_shape_rank)
}
catch (const NodeValidationFailure& error)
{
- EXPECT_HAS_SUBSTRING(error.what(),
- std::string("Proposal layer image_shape input must have rank 1"));
+ EXPECT_HAS_SUBSTRING(
+ error.what(),
+ std::string("Proposal layer shape image_shape should be rank 1 compatible"));
}
catch (...)
{
@@ -104,7 +130,7 @@ TEST(type_prop, proposal_v0_invalid_image_shape_size)
{
op::ProposalAttrs attrs;
auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4});
- auto class_bbox_deltas = make_shared(element::f32, Shape{1, 2, 3, 4});
+ auto class_bbox_deltas = make_shared(element::f32, Shape{1, 4, 3, 4});
auto image_shape = make_shared(element::f32, Shape{5});
try
@@ -143,6 +169,277 @@ TEST(type_prop, proposal_v0_shape_infer)
ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
}
+TEST(type_prop, proposal_v0_dynamic_class_probs_dim1_batch_size_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 1;
+ const size_t batch_size = 2;
+ auto class_probs =
+ make_shared(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
+ auto class_bbox_deltas = make_shared(element::f32, Shape{batch_size, 4, 3, 4});
+ auto image_shape = make_shared(element::f32, Shape{3});
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_dynamic_bbox_deltas_dim1_batch_size_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 1;
+ const size_t batch_size = 2;
+ auto class_probs =
+ make_shared(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
+ auto class_bbox_deltas = make_shared(element::f32, Shape{batch_size, 4, 3, 4});
+ auto image_shape = make_shared(element::f32, Shape{3});
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_dynamic_class_probs_bbox_deltas_dim1_batch_size_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 1;
+ auto class_probs =
+ make_shared(element::f32, PartialShape{Dimension::dynamic(), 2, 3, 4});
+ auto class_bbox_deltas =
+ make_shared(element::f32, PartialShape{Dimension::dynamic(), 4, 3, 4});
+ auto image_shape = make_shared(element::f32, Shape{3});
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_partial_shape(0),
+ (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_dynamic_range_class_probs_bbox_deltas_dim1_batch_size_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 2;
+ auto class_probs =
+ make_shared(element::f32, PartialShape{Dimension(8, 14), 2, 3, 4});
+ auto class_bbox_deltas =
+ make_shared(element::f32, PartialShape{Dimension(10, 15), 4, 3, 4});
+ auto image_shape = make_shared(element::f32, Shape{3});
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_partial_shape(0),
+ (PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn), 5}));
+}
+
+TEST(type_prop, proposal_v0_dynamic_image_shape_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(element::f32, Shape{batch_size, 12, 34, 62});
+ auto class_bbox_deltas =
+ make_shared(element::f32, Shape{batch_size, 24, 34, 62});
+ auto image_shape = make_shared(element::f32, PartialShape::dynamic());
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_shape(0), (Shape{batch_size * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_everything_dynamic_shape_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 1;
+ auto class_probs = make_shared(element::f32, PartialShape::dynamic(4));
+ auto class_bbox_deltas = make_shared(element::f32, PartialShape::dynamic(4));
+ auto image_shape = make_shared(element::f32, PartialShape::dynamic(1));
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_partial_shape(0),
+ (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_everything_dynamic_class_probs_dynamic_rank_shape_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 1;
+ auto class_probs =
+ make_shared(element::f32, PartialShape::dynamic(Rank::dynamic()));
+ auto class_bbox_deltas = make_shared(element::f32, PartialShape::dynamic(4));
+ auto image_shape = make_shared(element::f32, PartialShape::dynamic(1));
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_partial_shape(0),
+ (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_everything_dynamic_class_probs_bbox_deltas_dynamic_rank_shape_infer)
+{
+ op::ProposalAttrs attrs;
+ attrs.post_nms_topn = 1;
+ auto class_probs =
+ make_shared(element::f32, PartialShape::dynamic(Rank::dynamic()));
+ auto class_bbox_deltas =
+ make_shared(element::f32, PartialShape::dynamic(Rank::dynamic()));
+ auto image_shape = make_shared(element::f32, PartialShape::dynamic(1));
+
+ auto op = make_shared(class_probs, class_bbox_deltas, image_shape, attrs);
+ ASSERT_EQ(op->get_output_partial_shape(0),
+ (PartialShape{Dimension::dynamic() * attrs.post_nms_topn, 5}));
+}
+
+TEST(type_prop, proposal_v0_invalid_class_probs_dynamic)
+{
+ op::ProposalAttrs attrs;
+ auto class_probs = make_shared(element::f32, PartialShape::dynamic(3));
+ auto class_bbox_deltas = make_shared(element::f32, Shape{1, 4, 3, 4});
+ auto image_shape = make_shared(element::f32, Shape{5});
+
+ try
+ {
+ auto proposal =
+ make_shared(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 should be rank 4 compatible"));
+ }
+ catch (...)
+ {
+ FAIL() << "Deduced type check failed for unexpected reason";
+ }
+}
+
+TEST(type_prop, proposal_v0_invalid_bbox_deltas_dynamic)
+{
+ op::ProposalAttrs attrs;
+ auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4});
+ auto class_bbox_deltas = make_shared(element::f32, PartialShape::dynamic(3));
+ auto image_shape = make_shared(element::f32, Shape{5});
+
+ try
+ {
+ auto proposal =
+ make_shared(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 bbox_deltas should be rank 4 compatible"));
+ }
+ catch (...)
+ {
+ FAIL() << "Deduced type check failed for unexpected reason";
+ }
+}
+
+TEST(type_prop, proposal_v0_invalid_image_shape_dynamic)
+{
+ op::ProposalAttrs attrs;
+ auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4});
+ auto class_bbox_deltas = make_shared