Reference Implementation of ROIPooling op (#2903)
* ROIPooling: Specification and op class alignment * ROIPooling: Add check to input tensor type to be aligned with spec * ROIPooling: Corrected spec description for input tensor shape and box coordinates * ROIPooling: Changed attributes pooled_h and pooled_w from Shape to plain int * Revert "ROIPooling: Changed attributes pooled_h and pooled_w from Shape to plain int" This reverts commitd49cfa8e53. * ROIPooling: Further specification changes * ROIPooling: Rename enum class ROIPoolingMethod methods * Fix style * ROIPooling: Draft reference implementation * ROIPooling: Adjust feature map element type to float for attribute unit test * ROIPooling: Add single layer test class * ROIPooling: Corrected output index to iterate through output tensor elements * ROIPooling: Added validation checks for input types in op constructor * ROIPooling: Add unit tests * ROIPooling: Attributes unit test changed to align with spec * ROIPooling: Add check for batch id in reference implementation and unit test * ROIPooling: Refactor single layer test class * ROIPooling: Add test for invalid pooling method * ROIPooling: Clean up unnecessary function declaration * ROIPooling: Remove duplicated default ROIPooling method in op constructors * ROIPooling: Add Infer method to generate suitable ROI data * ROIPooling: CPU single layer test instantiation for max method * ROIPooling: Remove enum class ROIPoolingMethod * Revert "ROIPooling: Clean up unnecessary function declaration" This reverts commit074b540dea. * ROIPooling: Refactor single layer tests after removing enum class ROIPoolingMethod * ROIPooling: Add attribute checks in op constructor to align with spec and unit tests * Resolve CI failure: clang could not resolve static conversion from uint64_t to size_t * ROIPooling: Fix for output index calculation to loop through all ROIs * ROIPooling: Add unit test for bilinear interpolation method * ROIPooling: Add CPU single layer test instantiation for bilinear method * ROIPooling: Clean up unnecessary enum class for pooling method * ROIPooling: Add myriad single layer test instantiation * ROIPooling: Add F16 precision single layer tests for CPU plugin * ROIPooling: Add node validation check for string method attribute in constructor and unit tests * ROIPooling: Spec changes to improve understanding of the operation * ROIPooling: Fix for bilinear method when pooled size is 1x1 * ROIPooling: Add unit test for bilinear method and pooled size 1x1 * ROIPooling: Fix to broken format of specifications * ROIPooling: Disable Myriad single layer tests * ROIPooling: Handle dynamic dims and ranks for input tensors and unit tests * ROIPooling: Code clean up * ROIPooling: Address review comments * ROIPooling: Changed location for makeROIPooling helper method Co-authored-by: Kirill Molchanov <kirill.molchanov@intel.com>
This commit is contained in:
committed by
GitHub
parent
b676765dbc
commit
ac8a39da87
@@ -6,7 +6,18 @@
|
||||
|
||||
**Short description**: *ROIPooling* is a *pooling layer* used over feature maps of non-uniform input sizes and outputs a feature map of a fixed size.
|
||||
|
||||
**Detailed description**: [deepsense.io reference](https://blog.deepsense.ai/region-of-interest-pooling-explained/)
|
||||
**Detailed description**:
|
||||
|
||||
*ROIPooling* performs the following operations for each Region of Interest (ROI) over the input feature maps:
|
||||
1. Produce box coordinates relative to the input feature map size, based on *method* attribute.
|
||||
2. Calculate box height and width.
|
||||
3. Divide the box into bins according to the pooled size attributes, `[pooled_h, pooled_w]`.
|
||||
4. Apply maximum or bilinear interpolation pooling, for each bin, based on *method* attribute to produce output feature map element.
|
||||
|
||||
The box height and width have different representation based on **method** attribute:
|
||||
* *max*: Expressed in relative coordinates. The box height and width are calculated the following way: `roi_width = max(spatial_scale * (x_2 - x_1), 1.0)`,
|
||||
`roi_height = max(spatial_scale * (y_2 - y_1), 1.0)`, so the malformed boxes are expressed as a box of size `1 x 1`.
|
||||
* *bilinear*: Expressed in absolute coordinates and normalized to the `[0, 1]` interval. The box height and width are calculated the following way: `roi_width = (W - 1) * (x_2 - x_1)`, `roi_height = (H - 1) * (y_2 - y_1)`.
|
||||
|
||||
**Attributes**
|
||||
|
||||
@@ -44,13 +55,19 @@
|
||||
|
||||
**Inputs**:
|
||||
|
||||
* **1**: 4D input tensor of shape `[1, C, H, W]` with feature maps. Required.
|
||||
* **1**: 4D input tensor of shape `[N, C, H, W]` with feature maps of type *T*. Required.
|
||||
|
||||
* **2**: 2D input tensor of shape `[NUM_ROIS, 5]` describing region of interest box consisting of 5 element tuples of type *T*: `[batch_id, x_1, y_1, x_2, y_2]`. Required.
|
||||
Batch indices must be in the range of `[0, N-1]`.
|
||||
|
||||
* **2**: 2D input tensor of shape `[NUM_ROIS, 5]` describing box consisting of 5 element tuples: `[batch_id, x_1, y_1, x_2, y_2]`. Required.
|
||||
|
||||
**Outputs**:
|
||||
|
||||
* **1**: 4D output tensor of shape `[NUM_ROIS, C, pooled_h, pooled_w]` with feature maps. Required.
|
||||
* **1**: 4D output tensor of shape `[NUM_ROIS, C, pooled_h, pooled_w]` with feature maps of type *T*. Required.
|
||||
|
||||
**Types**
|
||||
|
||||
* *T*: any supported floating point type.
|
||||
|
||||
**Example**
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "single_layer_tests/roi_pooling.hpp"
|
||||
#include "common_test_utils/test_constants.hpp"
|
||||
|
||||
using namespace LayerTestsDefinitions;
|
||||
|
||||
const std::vector<std::vector<size_t>> inShapes = {
|
||||
{1, 3, 8, 8},
|
||||
{3, 4, 50, 50}
|
||||
};
|
||||
|
||||
const std::vector<std::vector<size_t>> pooledShapes_max = {
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{6, 6}
|
||||
};
|
||||
|
||||
const std::vector<std::vector<size_t>> pooledShapes_bilinear = {
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{6, 6}
|
||||
};
|
||||
|
||||
const std::vector<std::vector<size_t>> coordShapes = {
|
||||
{1, 5},
|
||||
{3, 5},
|
||||
{5, 5}
|
||||
};
|
||||
|
||||
const std::vector<InferenceEngine::Precision> netPRCs = {
|
||||
InferenceEngine::Precision::FP16,
|
||||
InferenceEngine::Precision::FP32
|
||||
};
|
||||
|
||||
const std::vector<float> spatial_scales = {0.625f, 1.f};
|
||||
|
||||
const auto test_ROIPooling_max = ::testing::Combine(
|
||||
::testing::ValuesIn(inShapes),
|
||||
::testing::ValuesIn(coordShapes),
|
||||
::testing::ValuesIn(pooledShapes_max),
|
||||
::testing::ValuesIn(spatial_scales),
|
||||
::testing::Values(ngraph::helpers::ROIPoolingTypes::ROI_MAX),
|
||||
::testing::ValuesIn(netPRCs),
|
||||
::testing::Values(CommonTestUtils::DEVICE_CPU)
|
||||
);
|
||||
|
||||
const auto test_ROIPooling_bilinear = ::testing::Combine(
|
||||
::testing::ValuesIn(inShapes),
|
||||
::testing::ValuesIn(coordShapes),
|
||||
::testing::ValuesIn(pooledShapes_bilinear),
|
||||
::testing::Values(spatial_scales[1]),
|
||||
::testing::Values(ngraph::helpers::ROIPoolingTypes::ROI_BILINEAR),
|
||||
::testing::ValuesIn(netPRCs),
|
||||
::testing::Values(CommonTestUtils::DEVICE_CPU)
|
||||
);
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(smoke_TestsROIPooling_max, ROIPoolingLayerTest, test_ROIPooling_max, ROIPoolingLayerTest::getTestCaseName);
|
||||
INSTANTIATE_TEST_CASE_P(smoke_TestsROIPooling_bilinear, ROIPoolingLayerTest, test_ROIPooling_bilinear, ROIPoolingLayerTest::getTestCaseName);
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "single_layer_tests/roi_pooling.hpp"
|
||||
#include "common_test_utils/test_constants.hpp"
|
||||
|
||||
using namespace LayerTestsDefinitions;
|
||||
|
||||
namespace {
|
||||
|
||||
const std::vector<std::vector<size_t>> inShapes = {
|
||||
{3, 4, 50, 50}
|
||||
};
|
||||
|
||||
const std::vector<std::vector<size_t>> pooledShapes_max = {
|
||||
{1, 1},
|
||||
{3, 3},
|
||||
};
|
||||
|
||||
const std::vector<std::vector<size_t>> pooledShapes_bilinear = {
|
||||
{2, 2},
|
||||
{6, 6}
|
||||
};
|
||||
|
||||
const std::vector<std::vector<size_t>> coordShapes = {
|
||||
{1, 5},
|
||||
{3, 5},
|
||||
};
|
||||
|
||||
const std::vector<float> spatial_scales = {0.625f, 1.f};
|
||||
|
||||
const auto test_ROIPooling_max = ::testing::Combine(
|
||||
::testing::ValuesIn(inShapes),
|
||||
::testing::ValuesIn(coordShapes),
|
||||
::testing::ValuesIn(pooledShapes_max),
|
||||
::testing::ValuesIn(spatial_scales),
|
||||
::testing::Values(ngraph::helpers::ROIPoolingTypes::ROI_MAX),
|
||||
::testing::Values(InferenceEngine::Precision::FP32),
|
||||
::testing::Values(CommonTestUtils::DEVICE_MYRIAD)
|
||||
);
|
||||
|
||||
const auto test_ROIPooling_bilinear = ::testing::Combine(
|
||||
::testing::ValuesIn(inShapes),
|
||||
::testing::ValuesIn(coordShapes),
|
||||
::testing::ValuesIn(pooledShapes_bilinear),
|
||||
::testing::ValuesIn(spatial_scales),
|
||||
::testing::Values(ngraph::helpers::ROIPoolingTypes::ROI_BILINEAR),
|
||||
::testing::Values(InferenceEngine::Precision::FP32),
|
||||
::testing::Values(CommonTestUtils::DEVICE_MYRIAD)
|
||||
);
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(smoke_TestsROIPooling_max, ROIPoolingLayerTest, test_ROIPooling_max, ROIPoolingLayerTest::getTestCaseName);
|
||||
INSTANTIATE_TEST_CASE_P(smoke_TestsROIPooling_bilinear, ROIPoolingLayerTest, test_ROIPooling_bilinear, ROIPoolingLayerTest::getTestCaseName);
|
||||
|
||||
} // namespace
|
||||
@@ -34,5 +34,7 @@ std::vector<std::string> disabledTestPatterns() {
|
||||
R"(.*DSR_NonMaxSuppression.*NBoxes=(5|20|200).*)",
|
||||
// TODO: Issue: 42721
|
||||
R"(.*(DSR_GatherND).*)",
|
||||
// TODO: Issue 43781
|
||||
".*ROIPoolingLayerTest.*"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "ngraph_functions/builders.hpp"
|
||||
#include "ngraph_functions/utils/ngraph_helpers.hpp"
|
||||
|
||||
#include "functional_test_utils/layer_test_utils.hpp"
|
||||
|
||||
namespace LayerTestsDefinitions {
|
||||
|
||||
using roiPoolingParamsTuple = std::tuple<
|
||||
InferenceEngine::SizeVector, // Input shape
|
||||
InferenceEngine::SizeVector, // Coords shape
|
||||
std::vector<size_t>, // Pooled shape {pooled_h, pooled_w}
|
||||
float, // Spatial scale
|
||||
ngraph::helpers::ROIPoolingTypes, // ROIPooling method
|
||||
InferenceEngine::Precision, // Net precision
|
||||
LayerTestsUtils::TargetDevice>; // Device name
|
||||
|
||||
class ROIPoolingLayerTest : public testing::WithParamInterface<roiPoolingParamsTuple>,
|
||||
virtual public LayerTestsUtils::LayerTestsCommon {
|
||||
public:
|
||||
static std::string getTestCaseName(testing::TestParamInfo<roiPoolingParamsTuple> obj);
|
||||
void Infer() override;
|
||||
|
||||
protected:
|
||||
void SetUp() override;
|
||||
|
||||
private:
|
||||
ngraph::helpers::ROIPoolingTypes pool_method;
|
||||
float spatial_scale;
|
||||
};
|
||||
|
||||
} // namespace LayerTestsDefinitions
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "common_test_utils/common_utils.hpp"
|
||||
#include "functional_test_utils/skip_tests_config.hpp"
|
||||
#include "functional_test_utils/layer_test_utils.hpp"
|
||||
|
||||
#include "single_layer_tests/roi_pooling.hpp"
|
||||
|
||||
using namespace InferenceEngine;
|
||||
using namespace FuncTestUtils::PrecisionUtils;
|
||||
|
||||
namespace LayerTestsDefinitions {
|
||||
|
||||
std::string ROIPoolingLayerTest::getTestCaseName(testing::TestParamInfo<roiPoolingParamsTuple> obj) {
|
||||
std::vector<size_t> inputShape;
|
||||
std::vector<size_t> coordsShape;
|
||||
std::vector<size_t> poolShape;
|
||||
float spatial_scale;
|
||||
ngraph::helpers::ROIPoolingTypes pool_method;
|
||||
InferenceEngine::Precision netPrecision;
|
||||
std::string targetDevice;
|
||||
std::tie(inputShape, coordsShape, poolShape, spatial_scale, pool_method, netPrecision, targetDevice) = obj.param;
|
||||
|
||||
std::ostringstream result;
|
||||
|
||||
result << "IS=" << CommonTestUtils::vec2str(inputShape) << "_";
|
||||
result << "CS=" << CommonTestUtils::vec2str(coordsShape) << "_";
|
||||
result << "PS=" << CommonTestUtils::vec2str(poolShape) << "_";
|
||||
result << "Scale=" << spatial_scale << "_";
|
||||
switch (pool_method) {
|
||||
case ngraph::helpers::ROIPoolingTypes::ROI_MAX:
|
||||
result << "Max_";
|
||||
break;
|
||||
case ngraph::helpers::ROIPoolingTypes::ROI_BILINEAR:
|
||||
result << "Bilinear_";
|
||||
break;
|
||||
}
|
||||
result << "netPRC=" << netPrecision.name() << "_";
|
||||
result << "trgDev=" << targetDevice;
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void ROIPoolingLayerTest::Infer() {
|
||||
inferRequest = executableNetwork.CreateInferRequest();
|
||||
inputs.clear();
|
||||
|
||||
auto feat_map_shape = cnnNetwork.getInputShapes().begin()->second;
|
||||
const int height = pool_method == ngraph::helpers::ROIPoolingTypes::ROI_MAX ? feat_map_shape[2] / spatial_scale : 1;
|
||||
const int width = pool_method == ngraph::helpers::ROIPoolingTypes::ROI_MAX ? feat_map_shape[3] / spatial_scale : 1;
|
||||
|
||||
size_t it = 0;
|
||||
for (const auto &input : cnnNetwork.getInputsInfo()) {
|
||||
const auto &info = input.second;
|
||||
Blob::Ptr blob;
|
||||
|
||||
if (it == 1) {
|
||||
blob = make_blob_with_precision(info->getTensorDesc());
|
||||
blob->allocate();
|
||||
CommonTestUtils::fill_data_roi(blob->buffer(), blob->size(), feat_map_shape[0] - 1,
|
||||
height, width, 1.0f);
|
||||
} else {
|
||||
blob = GenerateInput(*info);
|
||||
}
|
||||
inferRequest.SetBlob(info->name(), blob);
|
||||
inputs.push_back(blob);
|
||||
it++;
|
||||
}
|
||||
inferRequest.Infer();
|
||||
}
|
||||
|
||||
void ROIPoolingLayerTest::SetUp() {
|
||||
InferenceEngine::SizeVector inputShape;
|
||||
InferenceEngine::SizeVector coordsShape;
|
||||
InferenceEngine::SizeVector poolShape;
|
||||
InferenceEngine::Precision netPrecision;
|
||||
float spatial_scale;
|
||||
|
||||
std::tie(inputShape, coordsShape, poolShape, spatial_scale, pool_method, netPrecision, targetDevice) = this->GetParam();
|
||||
|
||||
auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
|
||||
auto params = ngraph::builder::makeParams(ngPrc, {inputShape, coordsShape});
|
||||
auto paramOuts = ngraph::helpers::convert2OutputVector(
|
||||
ngraph::helpers::castOps2Nodes<ngraph::op::Parameter>(params));
|
||||
std::shared_ptr<ngraph::Node> roi_pooling = ngraph::builder::makeROIPooling(paramOuts[0],
|
||||
paramOuts[1],
|
||||
poolShape,
|
||||
spatial_scale,
|
||||
pool_method);
|
||||
ngraph::ResultVector results{std::make_shared<ngraph::opset3::Result>(roi_pooling)};
|
||||
function = std::make_shared<ngraph::Function>(results, params, "roi_pooling");
|
||||
}
|
||||
|
||||
TEST_P(ROIPoolingLayerTest, CompareWithRefs) {
|
||||
Run();
|
||||
}
|
||||
} // namespace LayerTestsDefinitions
|
||||
@@ -123,6 +123,35 @@ static void fill_data_bbox(float *data, size_t size, int height, int width, floa
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_data_roi(float *data, size_t size, const uint32_t range, const int height, const int width, const float omega, const int seed = 1) {
|
||||
std::default_random_engine random(seed);
|
||||
std::uniform_int_distribution<int32_t> distribution(0, range);
|
||||
float center_h = (height - 1.0f) / 2;
|
||||
float center_w = (width - 1.0f) / 2;
|
||||
for (size_t i = 0; i < size; i += 5) {
|
||||
data[i] = static_cast<float>(distribution(random));
|
||||
data[i + 1] = std::floor(center_w + width * 0.6f * sin(static_cast<float>(i+1) * omega));
|
||||
data[i + 3] = std::floor(center_w + width * 0.6f * sin(static_cast<float>(i+3) * omega));
|
||||
if (data[i + 3] < data[i + 1]) {
|
||||
std::swap(data[i + 1], data[i + 3]);
|
||||
}
|
||||
if (data[i + 1] < 0)
|
||||
data[i + 1] = 0;
|
||||
if (data[i + 3] > width - 1)
|
||||
data[i + 3] = static_cast<float>(width - 1);
|
||||
|
||||
data[i + 2] = std::floor(center_h + height * 0.6f * sin(static_cast<float>(i+2) * omega));
|
||||
data[i + 4] = std::floor(center_h + height * 0.6f * sin(static_cast<float>(i+4) * omega));
|
||||
if (data[i + 4] < data[i + 2]) {
|
||||
std::swap(data[i + 2], data[i + 4]);
|
||||
}
|
||||
if (data[i + 2] < 0)
|
||||
data[i + 2] = 0;
|
||||
if (data[i + 4] > height - 1)
|
||||
data[i + 4] = static_cast<float>(height - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Fill blob with random data.
|
||||
*
|
||||
* @param blob Target blob
|
||||
|
||||
@@ -347,6 +347,12 @@ std::shared_ptr<Node> makePooling(const ngraph::Output<Node> &in,
|
||||
bool excludePad,
|
||||
const ngraph::helpers::PoolingTypes &poolType);
|
||||
|
||||
std::shared_ptr<Node> makeROIPooling(const Output<Node>& input,
|
||||
const Output<Node>& coords,
|
||||
const Shape& output_size,
|
||||
const float spatial_scale,
|
||||
const ngraph::helpers::ROIPoolingTypes& roi_pool_type);
|
||||
|
||||
std::shared_ptr<ngraph::Node> makeScatterUpdate(const ngraph::Output<Node> &in,
|
||||
const element::Type& indicesType,
|
||||
const std::vector<size_t>& indicesShape,
|
||||
|
||||
@@ -79,6 +79,12 @@ enum PoolingTypes {
|
||||
MAX,
|
||||
AVG
|
||||
};
|
||||
|
||||
enum ROIPoolingTypes {
|
||||
ROI_MAX,
|
||||
ROI_BILINEAR
|
||||
};
|
||||
|
||||
enum ActivationTypes {
|
||||
None,
|
||||
Sigmoid,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2020 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "ngraph_functions/builders.hpp"
|
||||
|
||||
namespace ngraph {
|
||||
namespace builder {
|
||||
|
||||
std::shared_ptr<Node> makeROIPooling(const Output<Node>& input,
|
||||
const Output<Node>& coords,
|
||||
const Shape& output_size,
|
||||
const float spatial_scale,
|
||||
const ngraph::helpers::ROIPoolingTypes& roi_pool_type) {
|
||||
switch (roi_pool_type) {
|
||||
case helpers::ROIPoolingTypes::ROI_MAX:
|
||||
return std::make_shared<ngraph::opset3::ROIPooling>(input, coords, output_size, spatial_scale, "max");
|
||||
case helpers::ROIPoolingTypes::ROI_BILINEAR:
|
||||
return std::make_shared<ngraph::opset3::ROIPooling>(input, coords, output_size, spatial_scale, "bilinear");
|
||||
default:
|
||||
throw std::runtime_error("Incorrect type of ROIPooling operation");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace builder
|
||||
} // namespace ngraph
|
||||
@@ -32,7 +32,7 @@ namespace ngraph
|
||||
ROIPooling() = default;
|
||||
/// \brief Constructs a ROIPooling operation
|
||||
///
|
||||
/// \param input Input feature map {N, C, ...}
|
||||
/// \param input Input feature map {N, C, H, W}
|
||||
/// \param coords Coordinates of bounding boxes
|
||||
/// \param output_size Height/Width of ROI output features
|
||||
/// \param spatial_scale Ratio of input feature map over input image size
|
||||
@@ -41,7 +41,7 @@ namespace ngraph
|
||||
const Output<Node>& coords,
|
||||
const Shape& output_size,
|
||||
const float spatial_scale,
|
||||
const std::string& method);
|
||||
const std::string& method = "max");
|
||||
|
||||
void validate_and_infer_types() override;
|
||||
|
||||
@@ -58,7 +58,10 @@ namespace ngraph
|
||||
float m_spatial_scale;
|
||||
std::string m_method;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace v0
|
||||
using v0::ROIPooling;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
|
||||
} // namespace ngraph
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
//*****************************************************************************
|
||||
// Copyright 2017-2020 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//*****************************************************************************
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ngraph/shape.hpp"
|
||||
|
||||
namespace ngraph
|
||||
{
|
||||
namespace runtime
|
||||
{
|
||||
namespace reference
|
||||
{
|
||||
template <typename T>
|
||||
void roi_pooling(const T* feature_maps,
|
||||
const T* rois,
|
||||
T* output,
|
||||
const Shape& feature_maps_shape,
|
||||
const Shape& rois_shape,
|
||||
const Shape& output_shape,
|
||||
const float spatial_scale,
|
||||
const std::string& pooling_method)
|
||||
{
|
||||
// Feature maps input shape: {N, C, H, W}
|
||||
const int batches = feature_maps_shape[0];
|
||||
const int channels = feature_maps_shape[1];
|
||||
const int height = feature_maps_shape[2];
|
||||
const int width = feature_maps_shape[3];
|
||||
|
||||
// Output shape: {NUM_ROIS, C, pooled_h, pooled_w}
|
||||
const int pooled_h = output_shape[2];
|
||||
const int pooled_w = output_shape[3];
|
||||
|
||||
// ROIs shape: {NUM_ROIS, 5}
|
||||
const int num_rois = rois_shape[0];
|
||||
|
||||
for (unsigned int roi_num = 0; roi_num < num_rois; roi_num++)
|
||||
{
|
||||
// ROI tuple: [roi_batch_id, roi_w_start, roi_h_start, roi_w_end, roi_h_end]
|
||||
// ROI index
|
||||
int roi_idx = rois_shape[1] * roi_num;
|
||||
|
||||
// ROI batch id
|
||||
int roi_batch_id = rois[roi_idx + 0];
|
||||
|
||||
// ROI batch id must be in the range of [0, N-1]
|
||||
NGRAPH_CHECK(0 <= roi_batch_id && roi_batch_id < batches,
|
||||
"ROI batch id must be in the range of [0, N-1]");
|
||||
|
||||
if (pooling_method == "max")
|
||||
{
|
||||
// ROI coordinates scaled to input feature maps
|
||||
int roi_w_start = std::round(rois[roi_idx + 1] * spatial_scale);
|
||||
int roi_h_start = std::round(rois[roi_idx + 2] * spatial_scale);
|
||||
int roi_w_end = std::round(rois[roi_idx + 3] * spatial_scale);
|
||||
int roi_h_end = std::round(rois[roi_idx + 4] * spatial_scale);
|
||||
|
||||
// Force malformed ROIs to be 1x1
|
||||
int roi_height = std::max(roi_h_end - roi_h_start + 1, 1);
|
||||
int roi_width = std::max(roi_w_end - roi_w_start + 1, 1);
|
||||
|
||||
// Divide ROIs into sub-regions for max pooling
|
||||
T bin_size_h = static_cast<T>(roi_height) / pooled_h;
|
||||
T bin_size_w = static_cast<T>(roi_width) / pooled_w;
|
||||
|
||||
const T* batch_data =
|
||||
feature_maps + roi_batch_id * channels * height * width;
|
||||
|
||||
for (unsigned int c = 0; c < channels; c++)
|
||||
{
|
||||
for (unsigned int ph = 0; ph < pooled_h; ph++)
|
||||
{
|
||||
for (unsigned int pw = 0; pw < pooled_w; pw++)
|
||||
{
|
||||
// Compute pooling region for this output unit:
|
||||
// start (included) = floor(ph * roi_height / pooled_h)
|
||||
// end (excluded) = ceil((ph + 1) * roi_height / pooled_h)
|
||||
int h_start = static_cast<int>(
|
||||
std::floor(static_cast<T>(ph) * bin_size_h));
|
||||
int w_start = static_cast<int>(
|
||||
std::floor(static_cast<T>(pw) * bin_size_w));
|
||||
int h_end = static_cast<int>(
|
||||
std::ceil(static_cast<T>(ph + 1) * bin_size_h));
|
||||
int w_end = static_cast<int>(
|
||||
std::ceil(static_cast<T>(pw + 1) * bin_size_w));
|
||||
|
||||
// Add ROI offsets and clip to input boundaries
|
||||
h_start = std::min(std::max(h_start + roi_h_start, 0), height);
|
||||
w_start = std::min(std::max(w_start + roi_w_start, 0), width);
|
||||
h_end = std::min(std::max(h_end + roi_h_start, 0), height);
|
||||
w_end = std::min(std::max(w_end + roi_w_start, 0), width);
|
||||
|
||||
const size_t pool_index =
|
||||
roi_num * channels * pooled_h * pooled_w +
|
||||
c * pooled_h * pooled_w + ph * pooled_w + pw;
|
||||
|
||||
// Define an empty pooling region to be zero
|
||||
bool is_empty = (h_end <= h_start) || (w_end <= w_start);
|
||||
output[pool_index] =
|
||||
is_empty ? 0 : std::numeric_limits<T>::lowest();
|
||||
|
||||
for (unsigned int h = h_start; h < h_end; h++)
|
||||
{
|
||||
for (unsigned int w = w_start; w < w_end; w++)
|
||||
{
|
||||
const size_t index = h * width + w;
|
||||
output[pool_index] =
|
||||
std::max(batch_data[index], output[pool_index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Increment batch data pointer by one channel
|
||||
batch_data += height * width;
|
||||
}
|
||||
}
|
||||
else if (pooling_method == "bilinear")
|
||||
{
|
||||
// ROI coordinates, normalized
|
||||
T roi_w_start = rois[roi_idx + 1];
|
||||
T roi_h_start = rois[roi_idx + 2];
|
||||
T roi_w_end = rois[roi_idx + 3];
|
||||
T roi_h_end = rois[roi_idx + 4];
|
||||
|
||||
T roi_height = (roi_h_end - roi_h_start) * (height - 1);
|
||||
T roi_width = (roi_w_end - roi_w_start) * (width - 1);
|
||||
|
||||
T roi_height_scale = (pooled_h > 1) ? roi_height / (pooled_h - 1) : 0;
|
||||
T roi_width_scale = (pooled_w > 1) ? roi_width / (pooled_w - 1) : 0;
|
||||
|
||||
for (unsigned int c = 0; c < channels; c++)
|
||||
{
|
||||
for (unsigned int ph = 0; ph < pooled_h; ph++)
|
||||
{
|
||||
for (unsigned int pw = 0; pw < pooled_w; pw++)
|
||||
{
|
||||
T in_y =
|
||||
(pooled_h > 1)
|
||||
? (ph * roi_height_scale + roi_h_start * (height - 1))
|
||||
: 0.5 * (roi_h_start + roi_h_end) * (height - 1);
|
||||
T in_x =
|
||||
(pooled_w > 1)
|
||||
? (pw * roi_width_scale + roi_w_start * (width - 1))
|
||||
: 0.5 * (roi_w_end + roi_w_start) * (width - 1);
|
||||
|
||||
const size_t pool_index =
|
||||
roi_num * channels * pooled_h * pooled_w +
|
||||
c * pooled_h * pooled_w + ph * pooled_w + pw;
|
||||
// Define invalid pooling region to be zero
|
||||
if (in_y < 0 || in_y > height - 1 || in_x < 0 ||
|
||||
in_x > width - 1)
|
||||
{
|
||||
output[pool_index] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int top_y_index = static_cast<int>(std::floor(in_y));
|
||||
int bottom_y_index = static_cast<int>(std::ceil(in_y));
|
||||
int left_x_index = static_cast<int>(std::floor(in_x));
|
||||
int right_x_index = static_cast<int>(std::ceil(in_x));
|
||||
|
||||
// Clip to input width boundaries
|
||||
if (right_x_index > width - 1)
|
||||
{
|
||||
right_x_index = width - 1;
|
||||
}
|
||||
|
||||
// Clip to input height boundaries
|
||||
if (bottom_y_index > height - 1)
|
||||
{
|
||||
bottom_y_index = height - 1;
|
||||
}
|
||||
|
||||
size_t top_left_idx =
|
||||
roi_batch_id * channels * height * width +
|
||||
c * height * width + top_y_index * width + left_x_index;
|
||||
|
||||
size_t top_right_idx =
|
||||
roi_batch_id * channels * height * width +
|
||||
c * height * width + top_y_index * width +
|
||||
right_x_index;
|
||||
|
||||
size_t bottom_left_idx =
|
||||
roi_batch_id * channels * height * width +
|
||||
c * height * width + bottom_y_index * width +
|
||||
left_x_index;
|
||||
|
||||
size_t bottom_right_idx =
|
||||
roi_batch_id * channels * height * width +
|
||||
c * height * width + bottom_y_index * width +
|
||||
right_x_index;
|
||||
|
||||
const T top_left = feature_maps[top_left_idx];
|
||||
const T top_right = feature_maps[top_right_idx];
|
||||
const T bottom_left = feature_maps[bottom_left_idx];
|
||||
const T bottom_right = feature_maps[bottom_right_idx];
|
||||
|
||||
const T top =
|
||||
top_left +
|
||||
(top_right - top_left) * (in_x - left_x_index);
|
||||
const T bottom =
|
||||
bottom_left +
|
||||
(bottom_right - bottom_left) * (in_x - left_x_index);
|
||||
|
||||
output[pool_index] =
|
||||
top + (bottom - top) * (in_y - top_y_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace reference
|
||||
|
||||
} // namespace runtime
|
||||
|
||||
} // namespace ngraph
|
||||
@@ -36,32 +36,104 @@ op::ROIPooling::ROIPooling(const Output<Node>& input,
|
||||
|
||||
void op::ROIPooling::validate_and_infer_types()
|
||||
{
|
||||
auto input_et = get_input_element_type(0);
|
||||
if (get_input_partial_shape(0).is_static() && get_input_partial_shape(1).is_static())
|
||||
auto feat_maps_et = get_input_element_type(0);
|
||||
auto coords_et = get_input_element_type(1);
|
||||
NODE_VALIDATION_CHECK(
|
||||
this,
|
||||
feat_maps_et.is_real() && coords_et.is_real(),
|
||||
"The data type for input and ROIs is expected to be a floating point type. Got: ",
|
||||
feat_maps_et,
|
||||
" and: ",
|
||||
coords_et);
|
||||
|
||||
NODE_VALIDATION_CHECK(
|
||||
this,
|
||||
feat_maps_et == coords_et,
|
||||
"Type of feature maps (inputs) and rois is expected to be the same. Got: ",
|
||||
feat_maps_et,
|
||||
" and: ",
|
||||
coords_et);
|
||||
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
m_output_size.size() == 2,
|
||||
"The dimension of pooled size is expected to be equal to 2. Got: ",
|
||||
m_output_size.size());
|
||||
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
m_output_size[0] > 0 && m_output_size[1] > 0,
|
||||
"Pooled size attributes pooled_h and pooled_w should should be "
|
||||
"non-negative integers. Got: ",
|
||||
m_output_size[0],
|
||||
" and: ",
|
||||
m_output_size[1],
|
||||
"respectively");
|
||||
|
||||
NODE_VALIDATION_CHECK(
|
||||
this,
|
||||
m_spatial_scale > 0,
|
||||
"The spatial scale attribute should be a positive floating point number. Got: ",
|
||||
m_spatial_scale);
|
||||
|
||||
NODE_VALIDATION_CHECK(
|
||||
this,
|
||||
m_method == "max" || m_method == "bilinear",
|
||||
"Pooling method attribute should be either \'max\' or \'bilinear\'. Got: ",
|
||||
m_method);
|
||||
|
||||
const auto& feat_maps_ps = get_input_partial_shape(0);
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
feat_maps_ps.rank().compatible(4),
|
||||
"Expected a 4D tensor for the feature maps input. Got: ",
|
||||
feat_maps_ps);
|
||||
|
||||
const auto& coords_ps = get_input_partial_shape(1);
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
coords_ps.rank().compatible(2),
|
||||
"Expected a 2D tensor for the ROIs input with box coordinates. Got: ",
|
||||
coords_ps);
|
||||
|
||||
if (coords_ps.rank().is_static())
|
||||
{
|
||||
Shape input_shape = get_input_partial_shape(0).to_shape();
|
||||
Shape coords_shape = get_input_partial_shape(1).to_shape();
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
input_shape.size() >= 3,
|
||||
"ROIPooling expects 3 or higher dimensions for input. Got ",
|
||||
input_shape.size());
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
coords_shape.size() == 2,
|
||||
"ROIPooling expects 2 dimensions for box coordinates. Got ",
|
||||
coords_shape.size());
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
input_shape.size() - 2 == m_output_size.size(),
|
||||
"Spatial dimensions on input: ",
|
||||
input_shape.size() - 2,
|
||||
" doesn't match dimensions on requested output_size: ",
|
||||
m_output_size.size());
|
||||
Shape output_shape{coords_shape[0], input_shape[1]};
|
||||
output_shape.insert(output_shape.end(), m_output_size.begin(), m_output_size.end());
|
||||
set_output_type(0, input_et, output_shape);
|
||||
const auto coords_second_dim = coords_ps[1];
|
||||
NODE_VALIDATION_CHECK(
|
||||
this,
|
||||
coords_second_dim.compatible(5),
|
||||
"The second dimension of ROIs input should contain batch id and box coordinates. ",
|
||||
"This dimension is expected to be equal to 5. Got: ",
|
||||
coords_second_dim);
|
||||
}
|
||||
else
|
||||
|
||||
// output shape should be {NUM_ROIS, C, pooled_h, pooled_w}
|
||||
auto output_shape = PartialShape{{Dimension::dynamic(),
|
||||
Dimension::dynamic(),
|
||||
Dimension{static_cast<int64_t>(m_output_size[0])},
|
||||
Dimension{static_cast<int64_t>(m_output_size[1])}}};
|
||||
|
||||
if (coords_ps.rank().is_static() && coords_ps[0].is_static())
|
||||
{
|
||||
set_output_type(0, input_et, PartialShape::dynamic());
|
||||
output_shape[0] = coords_ps[0];
|
||||
}
|
||||
|
||||
if (feat_maps_ps.rank().is_static() && feat_maps_ps[1].is_static())
|
||||
{
|
||||
output_shape[1] = feat_maps_ps[1];
|
||||
}
|
||||
|
||||
set_output_size(1);
|
||||
set_output_type(0, feat_maps_et, output_shape);
|
||||
|
||||
// if channel dimension, C, not known
|
||||
// feature maps input is used by shape specialization pass
|
||||
if (feat_maps_ps.rank().is_static() && feat_maps_ps[1].is_dynamic())
|
||||
{
|
||||
set_input_is_relevant_to_shape(0);
|
||||
}
|
||||
|
||||
// if number of ROIs, NUM_ROIS, not known
|
||||
// coordinate input is used by shape specialization pass
|
||||
if (coords_ps.rank().is_static() && coords_ps[0].is_dynamic())
|
||||
{
|
||||
set_input_is_relevant_to_shape(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ set(SRC
|
||||
op_eval/reduce_l1.cpp
|
||||
op_eval/reduce_l2.cpp
|
||||
op_eval/roi_align.cpp
|
||||
op_eval/roi_pooling.cpp
|
||||
op_eval/round.cpp
|
||||
op_eval/softplus.cpp
|
||||
op_eval/split.cpp
|
||||
@@ -162,6 +163,7 @@ set(SRC
|
||||
type_prop/reverse.cpp
|
||||
type_prop/reverse_sequence.cpp
|
||||
type_prop/roi_align.cpp
|
||||
type_prop/roi_pooling.cpp
|
||||
type_prop/round.cpp
|
||||
type_prop/rnn_cell.cpp
|
||||
type_prop/rnn_sequence.cpp
|
||||
@@ -328,6 +330,7 @@ set(MULTI_TEST_SRC
|
||||
backend/reshape.in.cpp
|
||||
backend/reverse_sequence.in.cpp
|
||||
backend/reverse.in.cpp
|
||||
backend/roi_pooling.in.cpp
|
||||
backend/round.in.cpp
|
||||
backend/select.in.cpp
|
||||
backend/shape_of.in.cpp
|
||||
|
||||
@@ -1350,10 +1350,10 @@ TEST(attributes, reorg_yolo_op_strides)
|
||||
TEST(attributes, roi_pooling_op)
|
||||
{
|
||||
FactoryRegistry<Node>::get().register_factory<opset3::ROIPooling>();
|
||||
const auto data = make_shared<op::Parameter>(element::i32, Shape{2, 3, 4, 5});
|
||||
const auto coords = make_shared<op::Parameter>(element::i32, Shape{2, 3});
|
||||
const auto data = make_shared<op::Parameter>(element::f32, Shape{2, 3, 4, 5});
|
||||
const auto coords = make_shared<op::Parameter>(element::f32, Shape{2, 5});
|
||||
|
||||
const auto op = make_shared<opset3::ROIPooling>(data, coords, Shape{5, 5}, 0.123, "Bilinear");
|
||||
const auto op = make_shared<opset3::ROIPooling>(data, coords, Shape{5, 5}, 0.123, "bilinear");
|
||||
NodeBuilder builder(op);
|
||||
const auto g_op = as_type_ptr<opset3::ROIPooling>(builder.create());
|
||||
|
||||
|
||||
216
ngraph/test/backend/roi_pooling.in.cpp
Normal file
216
ngraph/test/backend/roi_pooling.in.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
//*****************************************************************************
|
||||
// Copyright 2017-2020 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//*****************************************************************************
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ngraph/ngraph.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});
|
||||
|
||||
NGRAPH_TEST(${BACKEND_NAME}, roi_pooling_1x1_max)
|
||||
{
|
||||
const int H = 6;
|
||||
const int W = 6;
|
||||
const int image_size = H * W;
|
||||
const int channels = 3;
|
||||
const int num_rois = 3;
|
||||
|
||||
const int pooled_h = 1;
|
||||
const int pooled_w = 1;
|
||||
const float spatial_scale = 1.f;
|
||||
|
||||
Shape feat_maps_shape{1, channels, H, W};
|
||||
Shape rois_shape{num_rois, 5};
|
||||
Shape pooled_shape{pooled_h, pooled_w};
|
||||
Shape output_shape{num_rois, channels, pooled_h, pooled_w};
|
||||
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, feat_maps_shape);
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, rois_shape);
|
||||
const auto roi_pooling =
|
||||
make_shared<op::v0::ROIPooling>(feat_maps, rois, pooled_shape, spatial_scale, "max");
|
||||
const auto f = make_shared<Function>(roi_pooling, ParameterVector{feat_maps, rois});
|
||||
|
||||
vector<float> feat_maps_vect;
|
||||
for (unsigned int i = 0; i < channels * image_size; i++)
|
||||
{
|
||||
feat_maps_vect.push_back(1.f * i / 10);
|
||||
}
|
||||
|
||||
vector<float> rois_vect = {0, 1, 1, 2, 3, 0, 1, 1, 2, 3, 0, 1, 1, 2, 3};
|
||||
|
||||
const vector<float> expected_vect = {2.0f, 5.6f, 9.2f, 2.0f, 5.6f, 9.2f, 2.0f, 5.6f, 9.2f};
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(f);
|
||||
test_case.add_input<float>(feat_maps_shape, feat_maps_vect);
|
||||
test_case.add_input<float>(rois_shape, rois_vect);
|
||||
test_case.add_expected_output<float>(output_shape, expected_vect);
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
NGRAPH_TEST(${BACKEND_NAME}, roi_pooling_2x2_max)
|
||||
{
|
||||
const int H = 6;
|
||||
const int W = 6;
|
||||
const int image_size = H * W;
|
||||
const int channels = 1;
|
||||
const int num_rois = 3;
|
||||
|
||||
const int pooled_h = 2;
|
||||
const int pooled_w = 2;
|
||||
const float spatial_scale = 1.f;
|
||||
|
||||
Shape feat_maps_shape{1, channels, H, W};
|
||||
Shape rois_shape{num_rois, 5};
|
||||
Shape pooled_shape{pooled_h, pooled_w};
|
||||
Shape output_shape{num_rois, channels, pooled_h, pooled_w};
|
||||
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, feat_maps_shape);
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, rois_shape);
|
||||
const auto roi_pooling =
|
||||
make_shared<op::v0::ROIPooling>(feat_maps, rois, pooled_shape, spatial_scale, "max");
|
||||
const auto f = make_shared<Function>(roi_pooling, ParameterVector{feat_maps, rois});
|
||||
|
||||
vector<float> feat_maps_vect;
|
||||
for (unsigned int i = 0; i < channels * image_size; i++)
|
||||
{
|
||||
feat_maps_vect.push_back(1.f * i / 10);
|
||||
}
|
||||
|
||||
vector<float> rois_vect = {0, 1, 1, 3, 3, 0, 1, 2, 2, 4, 0, 0, 1, 4, 5};
|
||||
|
||||
const vector<float> expected_vect = {
|
||||
1.4f, 1.5f, 2.0f, 2.1f, 1.9f, 2.0f, 2.5f, 2.6f, 2.0f, 2.2f, 3.2f, 3.4f};
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(f);
|
||||
test_case.add_input<float>(feat_maps_shape, feat_maps_vect);
|
||||
test_case.add_input<float>(rois_shape, rois_vect);
|
||||
test_case.add_expected_output<float>(output_shape, expected_vect);
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
NGRAPH_TEST(${BACKEND_NAME}, roi_pooling_1x1_bilinear)
|
||||
{
|
||||
const int H = 6;
|
||||
const int W = 6;
|
||||
const int image_size = H * W;
|
||||
const int channels = 3;
|
||||
const int num_rois = 2;
|
||||
|
||||
const int pooled_h = 1;
|
||||
const int pooled_w = 1;
|
||||
const float spatial_scale = 1.f;
|
||||
|
||||
Shape feat_maps_shape{1, channels, H, W};
|
||||
Shape rois_shape{num_rois, 5};
|
||||
Shape pooled_shape{pooled_h, pooled_w};
|
||||
Shape output_shape{num_rois, channels, pooled_h, pooled_w};
|
||||
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, feat_maps_shape);
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, rois_shape);
|
||||
const auto roi_pooling =
|
||||
make_shared<op::v0::ROIPooling>(feat_maps, rois, pooled_shape, spatial_scale, "bilinear");
|
||||
const auto f = make_shared<Function>(roi_pooling, ParameterVector{feat_maps, rois});
|
||||
|
||||
vector<float> feat_maps_vect;
|
||||
for (unsigned int i = 0; i < channels * image_size; i++)
|
||||
{
|
||||
feat_maps_vect.push_back(1.f * i / 10);
|
||||
}
|
||||
|
||||
vector<float> rois_vect = {0, 0.2, 0.2, 0.4, 0.4, 0, 0.2, 0.2, 0.6, 0.6};
|
||||
|
||||
const vector<float> expected_vect = {1.05f, 4.65f, 8.25f, 1.4f, 5.0f, 8.6f};
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(f);
|
||||
test_case.add_input<float>(feat_maps_shape, feat_maps_vect);
|
||||
test_case.add_input<float>(rois_shape, rois_vect);
|
||||
test_case.add_expected_output<float>(output_shape, expected_vect);
|
||||
test_case.run();
|
||||
}
|
||||
|
||||
NGRAPH_TEST(${BACKEND_NAME}, roi_pooling_2x2_bilinear)
|
||||
{
|
||||
const int H = 8;
|
||||
const int W = 8;
|
||||
const int image_size = H * W;
|
||||
const int channels = 1;
|
||||
const int num_rois = 3;
|
||||
|
||||
const int pooled_h = 2;
|
||||
const int pooled_w = 2;
|
||||
const float spatial_scale = 1.f;
|
||||
|
||||
Shape feat_maps_shape{1, channels, H, W};
|
||||
Shape rois_shape{num_rois, 5};
|
||||
Shape pooled_shape{pooled_h, pooled_w};
|
||||
Shape output_shape{num_rois, channels, pooled_h, pooled_w};
|
||||
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, feat_maps_shape);
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, rois_shape);
|
||||
const auto roi_pooling =
|
||||
make_shared<op::v0::ROIPooling>(feat_maps, rois, pooled_shape, spatial_scale, "bilinear");
|
||||
const auto f = make_shared<Function>(roi_pooling, ParameterVector{feat_maps, rois});
|
||||
|
||||
vector<float> feat_maps_vect;
|
||||
for (unsigned int i = 0; i < channels * image_size; i++)
|
||||
{
|
||||
feat_maps_vect.push_back(1.f * i / 10);
|
||||
}
|
||||
|
||||
vector<float> rois_vect = {0.f,
|
||||
0.15f,
|
||||
0.2f,
|
||||
0.75f,
|
||||
0.8f,
|
||||
0.f,
|
||||
0.15f,
|
||||
0.2f,
|
||||
0.75f,
|
||||
0.8f,
|
||||
0.f,
|
||||
0.15f,
|
||||
0.2f,
|
||||
0.75f,
|
||||
0.8f};
|
||||
|
||||
const auto count = shape_size(output_shape);
|
||||
const vector<float> expected_vect = {1.225f,
|
||||
1.645f,
|
||||
4.585f,
|
||||
5.005f,
|
||||
1.225f,
|
||||
1.645f,
|
||||
4.585f,
|
||||
5.005f,
|
||||
1.225f,
|
||||
1.645f,
|
||||
4.585f,
|
||||
5.005f};
|
||||
|
||||
auto test_case = test::TestCase<TestEngine>(f);
|
||||
test_case.add_input<float>(feat_maps_shape, feat_maps_vect);
|
||||
test_case.add_input<float>(rois_shape, rois_vect);
|
||||
test_case.add_expected_output<float>(output_shape, expected_vect);
|
||||
test_case.run();
|
||||
}
|
||||
64
ngraph/test/op_eval/roi_pooling.cpp
Normal file
64
ngraph/test/op_eval/roi_pooling.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
//*****************************************************************************
|
||||
// Copyright 2017-2020 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//*****************************************************************************
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ngraph/ngraph.hpp"
|
||||
#include "util/engine/interpreter_engine.hpp"
|
||||
#include "util/engine/test_engines.hpp"
|
||||
#include "util/test_case.hpp"
|
||||
#include "util/test_control.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
|
||||
static string s_manifest = "${MANIFEST}";
|
||||
|
||||
NGRAPH_TEST(op_eval, roi_pooling_invalid_roi_batch_id)
|
||||
{
|
||||
const int H = 6;
|
||||
const int W = 6;
|
||||
const int image_size = H * W;
|
||||
const int channels = 1;
|
||||
const int num_rois = 1;
|
||||
|
||||
const int pooled_h = 1;
|
||||
const int pooled_w = 1;
|
||||
const float spatial_scale = 1.f;
|
||||
|
||||
Shape feat_maps_shape{1, channels, H, W};
|
||||
Shape rois_shape{num_rois, 5};
|
||||
Shape pooled_shape{pooled_h, pooled_w};
|
||||
Shape output_shape{num_rois, channels, pooled_h, pooled_w};
|
||||
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, feat_maps_shape);
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, rois_shape);
|
||||
const auto roi_pooling =
|
||||
make_shared<op::v0::ROIPooling>(feat_maps, rois, pooled_shape, spatial_scale, "max");
|
||||
const auto f = make_shared<Function>(roi_pooling, ParameterVector{feat_maps, rois});
|
||||
|
||||
vector<float> feat_maps_vect;
|
||||
for (unsigned int i = 0; i < channels * image_size; i++)
|
||||
{
|
||||
feat_maps_vect.push_back(1.f * i / 10);
|
||||
}
|
||||
|
||||
auto test_case = test::TestCase<ngraph::test::INTERPRETER_Engine>(f);
|
||||
test_case.add_input<float>(feat_maps_shape, feat_maps_vect);
|
||||
// ROI with invalid batch id, should throw exception
|
||||
test_case.add_input<float>(rois_shape, {-1, 1, 1, 2, 3});
|
||||
test_case.add_expected_output<float>(output_shape, {2.0f});
|
||||
ASSERT_THROW(test_case.run(), ngraph::CheckFailure);
|
||||
}
|
||||
@@ -1141,6 +1141,9 @@ IE_CPU.nonmaxsuppression_suppress_by_IOU_and_scores
|
||||
IE_CPU.nonmaxsuppression_two_batches
|
||||
IE_CPU.nonmaxsuppression_two_classes
|
||||
|
||||
# Bug in CPU plugin for ROIPooling when pooled size is 1x1 and method is bilinear
|
||||
IE_CPU.roi_pooling_1x1_bilinear
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
# Inference Engine GPU plugin excludes
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
#include "ngraph/runtime/reference/reverse.hpp"
|
||||
#include "ngraph/runtime/reference/reverse_sequence.hpp"
|
||||
#include "ngraph/runtime/reference/rnn_cell.hpp"
|
||||
#include "ngraph/runtime/reference/roi_pooling.hpp"
|
||||
#include "ngraph/runtime/reference/round.hpp"
|
||||
#include "ngraph/runtime/reference/scatter_nd_update.hpp"
|
||||
#include "ngraph/runtime/reference/select.hpp"
|
||||
@@ -1195,6 +1196,19 @@ protected:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_TYPEID::ROIPooling_v0:
|
||||
{
|
||||
const op::ROIPooling* roi_pooling = static_cast<const op::ROIPooling*>(&node);
|
||||
reference::roi_pooling<T>(args[0]->get_data_ptr<const T>(),
|
||||
args[1]->get_data_ptr<const T>(),
|
||||
out[0]->get_data_ptr<T>(),
|
||||
node.get_input_shape(0),
|
||||
node.get_input_shape(1),
|
||||
node.get_output_shape(0),
|
||||
roi_pooling->get_spatial_scale(),
|
||||
roi_pooling->get_method());
|
||||
break;
|
||||
}
|
||||
case OP_TYPEID::Select:
|
||||
{
|
||||
size_t element_count = shape_size(node.get_output_shape(0));
|
||||
|
||||
@@ -25,6 +25,7 @@ NGRAPH_OP(LSTMCell, op::v0)
|
||||
NGRAPH_OP(RegionYolo, op::v0)
|
||||
NGRAPH_OP(ReorgYolo, op::v0)
|
||||
NGRAPH_OP(RNNCell, op::v0)
|
||||
NGRAPH_OP(ROIPooling, op::v0)
|
||||
#undef ID_SUFFIX
|
||||
|
||||
#define ID_SUFFIX(NAME) NAME##_v1
|
||||
|
||||
136
ngraph/test/type_prop/roi_pooling.cpp
Normal file
136
ngraph/test/type_prop/roi_pooling.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
//*****************************************************************************
|
||||
// Copyright 2017-2020 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//*****************************************************************************
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ngraph/ngraph.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace ngraph;
|
||||
|
||||
TEST(type_prop, roi_pooling_basic_shape_inference)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{1, 3, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{4, 5});
|
||||
const auto op = make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f);
|
||||
ASSERT_EQ(op->get_method(), "max");
|
||||
ASSERT_EQ(op->get_shape(), (Shape{4, 3, 2, 2}));
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_dynamic_channels_dim)
|
||||
{
|
||||
const auto feat_maps =
|
||||
make_shared<op::Parameter>(element::f32, PartialShape{1, Dimension(), 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{4, 5});
|
||||
const auto op = make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f, "max");
|
||||
ASSERT_TRUE(op->get_output_partial_shape(0).same_scheme(PartialShape{4, Dimension(), 2, 2}));
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_dynamic_num_rois_dim)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{1, 3, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, PartialShape{Dimension(), 5});
|
||||
const auto op = make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f);
|
||||
ASSERT_TRUE(op->get_output_partial_shape(0).same_scheme(PartialShape{Dimension(), 3, 2, 2}));
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_dynamic_rank_feat_maps)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{4, 5});
|
||||
const auto op = make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f);
|
||||
ASSERT_TRUE(op->get_output_partial_shape(0).same_scheme(PartialShape{4, Dimension(), 2, 2}));
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_dynamic_rank_rois)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{1, 3, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, PartialShape::dynamic());
|
||||
const auto op = make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f);
|
||||
ASSERT_TRUE(op->get_output_partial_shape(0).same_scheme(PartialShape{Dimension(), 3, 2, 2}));
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_incompatible_input_rank)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{1, 3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{3, 5});
|
||||
// feat_maps must be of rank 4
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f, "max"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_incompatible_pooling_shape)
|
||||
{
|
||||
Shape pool_shape{2, 2, 2};
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{3, 5});
|
||||
// pool_shape must be of rank 2 {pooled_h, pooled_w}
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, pool_shape, 0.625f, "max"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_incompatible_rois_second_dim)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{3, 4});
|
||||
// the second dim of rois must be 5. [batch_id, x_1, y_1, x_2, y_2]
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f, "max"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_incompatible_feature_maps_element_type)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::i32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f32, Shape{3, 5});
|
||||
// feat_maps element type must be floating point type
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f, "max"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_incompatible_rois_element_type)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f16, Shape{3, 5});
|
||||
// rois element type must be equal to feat_maps element type (floating point type)
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f, "bilinear"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_invalid_pooling_method)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f16, Shape{3, 5});
|
||||
// ROIPooling method is invalid: not max nor bilinear
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, 0.625f, "invalid"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_invalid_spatial_scale)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f16, Shape{3, 5});
|
||||
// ROIPooling spatial scale attribute must be a positive floating point number
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{2, 2}, -0.625f, "max"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
|
||||
TEST(type_prop, roi_pooling_invalid_pooled_size)
|
||||
{
|
||||
const auto feat_maps = make_shared<op::Parameter>(element::f32, Shape{3, 2, 6, 6});
|
||||
const auto rois = make_shared<op::Parameter>(element::f16, Shape{3, 5});
|
||||
// ROIPooling pooled_h and pooled_w must be non-negative integers
|
||||
ASSERT_THROW(make_shared<op::v0::ROIPooling>(feat_maps, rois, Shape{1, 0}, 0.625f, "max"),
|
||||
ngraph::NodeValidationFailure);
|
||||
}
|
||||
@@ -168,6 +168,6 @@ TEST(type_prop_layers, roi_pooling)
|
||||
{
|
||||
auto inputs = make_shared<op::Parameter>(element::f32, Shape{2, 3, 4, 5});
|
||||
auto coords = make_shared<op::Parameter>(element::f32, Shape{150, 5});
|
||||
auto op = make_shared<op::ROIPooling>(inputs, coords, Shape{6, 6}, 0.0625, "Max");
|
||||
auto op = make_shared<op::ROIPooling>(inputs, coords, Shape{6, 6}, 0.0625, "max");
|
||||
ASSERT_EQ(op->get_shape(), (Shape{150, 3, 6, 6}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user