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(element::f32, Shape{1, 4, 3, 4}); + auto image_shape = make_shared(element::f32, PartialShape::dynamic(0)); + + 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 image_shape should be rank 1 compatible")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + +TEST(type_prop, proposal_v0_invalid_class_probs_type) +{ + op::ProposalAttrs attrs; + auto class_probs = make_shared(element::i32, 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{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("Proposal layer input class_probs should have floating point type")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + +TEST(type_prop, proposal_v0_invalid_bbox_deltas_type) +{ + op::ProposalAttrs attrs; + auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4}); + auto class_bbox_deltas = make_shared(element::i32, Shape{1, 4, 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("Proposal layer input bbox_deltas should have floating point type")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + +TEST(type_prop, proposal_v0_invalid_image_shape_type) +{ + 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, 4, 3, 4}); + auto image_shape = make_shared(element::i32, 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("Proposal layer input image_shape should have floating point type")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} // ------------------------------ V4 ------------------------------ TEST(type_prop, proposal_v4_invalid_class_probs_rank) @@ -162,7 +459,8 @@ TEST(type_prop, proposal_v4_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 (...) { @@ -188,7 +486,7 @@ TEST(type_prop, proposal_v4_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 (...) { @@ -200,7 +498,7 @@ TEST(type_prop, proposal_v4_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 @@ -212,8 +510,9 @@ TEST(type_prop, proposal_v4_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 (...) { @@ -225,7 +524,7 @@ TEST(type_prop, proposal_v4_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 @@ -264,3 +563,285 @@ TEST(type_prop, proposal_v4_shape_infer) 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})); } + +TEST(type_prop, proposal_v4_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_v4_dynamic_bbox_deltas_dim1_batch_size_infer) +{ + op::ProposalAttrs attrs; + const size_t batch_size = 2; + 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, 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_v4_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})); + ASSERT_EQ(op->get_output_partial_shape(1), + (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); +} + +TEST(type_prop, proposal_v4_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_v4_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})); + ASSERT_EQ(op->get_output_partial_shape(1), + (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); +} + +TEST(type_prop, proposal_v4_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})); + ASSERT_EQ(op->get_output_partial_shape(1), + (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); +} + +TEST(type_prop, proposal_v4_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})); + ASSERT_EQ(op->get_output_partial_shape(1), + (PartialShape{Dimension::dynamic() * attrs.post_nms_topn})); +} + +TEST(type_prop, proposal_v4_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})); + ASSERT_EQ(op->get_output_partial_shape(1), + (PartialShape{Dimension(10 * attrs.post_nms_topn, 14 * attrs.post_nms_topn)})); +} + +TEST(type_prop, proposal_v4_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_v4_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_v4_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(element::f32, Shape{1, 4, 3, 4}); + auto image_shape = make_shared(element::f32, PartialShape::dynamic(0)); + + 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 image_shape should be rank 1 compatible")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + +TEST(type_prop, proposal_v4_invalid_class_probs_type) +{ + op::ProposalAttrs attrs; + auto class_probs = make_shared(element::i32, 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{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("Proposal layer input class_probs should have floating point type")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + +TEST(type_prop, proposal_v4_invalid_bbox_deltas_type) +{ + op::ProposalAttrs attrs; + auto class_probs = make_shared(element::f32, Shape{1, 2, 3, 4}); + auto class_bbox_deltas = make_shared(element::i32, Shape{1, 4, 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("Proposal layer input bbox_deltas should have floating point type")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +} + +TEST(type_prop, proposal_v4_invalid_image_shape_type) +{ + 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, 4, 3, 4}); + auto image_shape = make_shared(element::i32, 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("Proposal layer input image_shape should have floating point type")); + } + catch (...) + { + FAIL() << "Deduced type check failed for unexpected reason"; + } +}