ReorgYolo reference implementation (#2384)
authorKatarzyna Mitrus <katarzyna.mitrus@intel.com>
Thu, 15 Oct 2020 10:42:21 +0000 (12:42 +0200)
committerGitHub <noreply@github.com>
Thu, 15 Oct 2020 10:42:21 +0000 (13:42 +0300)
* Align ReorgYolo to the spec (vector strides -> int stride)

* ReorgYolo ref impl

* ReorgYolo evaluate method

* ReorgYolo tests

* Tests update

* Style apply

* Add some coments

* Code refactor

* Comment update

* Style apply

* Build fix, mark evaluate as override

* Revert "Align ReorgYolo to the spec (vector strides -> int stride)"

* Use int_executable instead of evaluate

* Use char* instead of templates

* Code refactor

* Comment update

* Code review comment

* Add constructor aligned with spec

* Update shape validation

* Update attributes tests

* Add type_prop tests

* Update backend tests

* Add single layer tests

* Update the spec

* Remove wrong transformation test

15 files changed:
docs/ops/detection/ReorgYolo_1.md
inference-engine/tests/functional/inference_engine/transformations/convert_extract_image_patches_to_reorg_yolo_test.cpp
inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/reorg_yolo.cpp [new file with mode: 0644]
inference-engine/tests/functional/plugin/shared/include/single_layer_tests/reorg_yolo.hpp [new file with mode: 0644]
inference-engine/tests/functional/plugin/shared/src/single_layer_tests/reorg_yolo.cpp [new file with mode: 0644]
ngraph/core/include/ngraph/op/reorg_yolo.hpp
ngraph/core/reference/include/ngraph/runtime/reference/reorg_yolo.hpp [new file with mode: 0644]
ngraph/core/reference/src/runtime/reference/reorg_yolo.cpp [new file with mode: 0644]
ngraph/core/src/op/reorg_yolo.cpp
ngraph/test/CMakeLists.txt
ngraph/test/attributes.cpp
ngraph/test/backend/reorg_yolo.in.cpp [new file with mode: 0644]
ngraph/test/runtime/interpreter/int_executable.hpp
ngraph/test/runtime/interpreter/opset_int_tbl.hpp
ngraph/test/type_prop/reorg_yolo.cpp [new file with mode: 0644]

index 25c4669..4801e5f 100644 (file)
@@ -22,7 +22,7 @@
 
 **Inputs**:
 
-*   **1**: 4D input tensor of any type and shape `[N, C, H, W]`. `H` and `W` should be divisible by `stride`. Required.
+*   **1**: 4D input tensor of any type and shape `[N, C, H, W]`. `H` and `W` should be divisible by `stride` and `C >= (stride*stride)`. **Required.**
 
 **Outputs**:
 
@@ -31,7 +31,7 @@
 **Example**
 
 ```xml
-<layer id="89" name="ExtractImagePatches" type="ReorgYolo">
+<layer id="89" name="reorg" type="ReorgYolo">
     <data stride="2"/>
     <input>
         <port id="0">
@@ -50,4 +50,4 @@
         </port>
     </output>
 </layer>
-```
\ No newline at end of file
+```
index f9347e4..062de2a 100644 (file)
 
 using namespace testing;
 
-TEST(TransformationTests, ConvertExtractImagePatchesToReorgYoloTests1) {
-    std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
-    {
-        auto input = std::make_shared<ngraph::opset1::Parameter>(ngraph::element::f32, ngraph::Shape{1, 3, 10, 10});
-
-        auto sizes = ngraph::Shape{5, 5};
-        auto strides = ngraph::Strides{5, 5};
-        auto rates = ngraph::Shape{1, 1};
-        ngraph::op::PadType auto_pad = ngraph::op::PadType::VALID;
-
-        auto eip = std::make_shared<ngraph::opset3::ExtractImagePatches>(input, sizes, strides, rates, auto_pad);
-
-        f = std::make_shared<ngraph::Function>(ngraph::NodeVector{eip}, ngraph::ParameterVector{input});
-
-        ngraph::pass::Manager manager;
-        manager.register_pass<ngraph::pass::InitNodeInfo>();
-        manager.register_pass<ngraph::pass::ConvertExtractImagePatchesToReorgYolo>();
-        manager.run_passes(f);
-        ASSERT_NO_THROW(check_rt_info(f));
-    }
-
-    {
-        auto input = std::make_shared<ngraph::opset1::Parameter>(ngraph::element::f32, ngraph::Shape{1, 3, 10, 10});
-        auto strides = ngraph::Strides{5, 5};
-        auto reorg_yolo = std::make_shared<ngraph::opset3::ReorgYolo>(input, strides);
-
-        f_ref = std::make_shared<ngraph::Function>(ngraph::NodeVector{reorg_yolo}, ngraph::ParameterVector{input});
-    }
-
-    auto res = compare_functions(f, f_ref);
-    ASSERT_TRUE(res.first) << res.second;
-}
+// TODO: bug 39971, remove ConvertExtractImagePatchesToReorgYolo transformation
 
 TEST(TransformationTests, ConvertExtractImagePatchesToReorgYoloTestsNegative1) {
     std::shared_ptr<ngraph::Function> f(nullptr), f_ref(nullptr);
diff --git a/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/reorg_yolo.cpp b/inference-engine/tests/functional/plugin/cpu/shared_tests_instances/single_layer_tests/reorg_yolo.cpp
new file mode 100644 (file)
index 0000000..03aa808
--- /dev/null
@@ -0,0 +1,75 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include <vector>
+
+#include "single_layer_tests/reorg_yolo.hpp"
+#include "common_test_utils/test_constants.hpp"
+
+using namespace LayerTestsDefinitions;
+
+const std::vector<ngraph::Shape> inShapes_caffe_yolov2 = {
+    {1, 64, 26, 26},
+};
+
+const std::vector<ngraph::Shape> inShapes = {
+    {1, 4, 4, 4},
+    {1, 8, 4, 4},
+    {1, 9, 3, 3},
+    {1, 24, 34, 62},
+    {2, 8, 4, 4},
+};
+
+const std::vector<size_t> strides = {
+    2, 3
+};
+
+const auto testCase_caffe_yolov2 = ::testing::Combine(
+    ::testing::ValuesIn(inShapes_caffe_yolov2),
+    ::testing::Values(strides[0]),
+    ::testing::Values(InferenceEngine::Precision::FP32),
+    ::testing::Values(CommonTestUtils::DEVICE_CPU)
+);
+
+const auto testCase_smallest = ::testing::Combine(
+    ::testing::Values(inShapes[0]),
+    ::testing::Values(strides[0]),
+    ::testing::Values(InferenceEngine::Precision::FP32),
+    ::testing::Values(CommonTestUtils::DEVICE_CPU)
+);
+
+const auto testCase_stride_2 = ::testing::Combine(
+    ::testing::Values(inShapes[1]),
+    ::testing::Values(strides[0]),
+    ::testing::Values(InferenceEngine::Precision::FP32),
+    ::testing::Values(CommonTestUtils::DEVICE_CPU)
+);
+
+const auto testCase_stride_3 = ::testing::Combine(
+    ::testing::Values(inShapes[2]),
+    ::testing::Values(strides[1]),
+    ::testing::Values(InferenceEngine::Precision::FP32),
+    ::testing::Values(CommonTestUtils::DEVICE_CPU)
+);
+
+const auto testCase_smaller_h = ::testing::Combine(
+    ::testing::Values(inShapes[4]),
+    ::testing::Values(strides[0]),
+    ::testing::Values(InferenceEngine::Precision::FP32),
+    ::testing::Values(CommonTestUtils::DEVICE_CPU)
+);
+
+const auto testCase_batch_2 = ::testing::Combine(
+    ::testing::Values(inShapes[3]),
+    ::testing::Values(strides[0]),
+    ::testing::Values(InferenceEngine::Precision::FP32),
+    ::testing::Values(CommonTestUtils::DEVICE_CPU)
+);
+
+INSTANTIATE_TEST_CASE_P(smoke_TestsReorgYolo_caffe_YoloV2, ReorgYoloLayerTest, testCase_caffe_yolov2, ReorgYoloLayerTest::getTestCaseName);
+INSTANTIATE_TEST_CASE_P(smoke_TestsReorgYolo_stride_2_smallest, ReorgYoloLayerTest, testCase_smallest, ReorgYoloLayerTest::getTestCaseName);
+INSTANTIATE_TEST_CASE_P(smoke_TestsReorgYolo_stride_2, ReorgYoloLayerTest, testCase_stride_2, ReorgYoloLayerTest::getTestCaseName);
+INSTANTIATE_TEST_CASE_P(smoke_TestsReorgYolo_stride_3, ReorgYoloLayerTest, testCase_stride_3, ReorgYoloLayerTest::getTestCaseName);
+INSTANTIATE_TEST_CASE_P(smoke_TestsReorgYolo_smaller_h, ReorgYoloLayerTest, testCase_smaller_h, ReorgYoloLayerTest::getTestCaseName);
+INSTANTIATE_TEST_CASE_P(smoke_TestsReorgYolo_batch_2, ReorgYoloLayerTest, testCase_batch_2, ReorgYoloLayerTest::getTestCaseName);
diff --git a/inference-engine/tests/functional/plugin/shared/include/single_layer_tests/reorg_yolo.hpp b/inference-engine/tests/functional/plugin/shared/include/single_layer_tests/reorg_yolo.hpp
new file mode 100644 (file)
index 0000000..1eab6b8
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#pragma once
+
+#include <tuple>
+#include <string>
+#include <vector>
+
+#include "functional_test_utils/layer_test_utils.hpp"
+#include "ngraph_functions/builders.hpp"
+#include "ngraph_functions/utils/ngraph_helpers.hpp"
+
+namespace LayerTestsDefinitions {
+
+using ReorgYoloParamsTuple = typename std::tuple<
+        ngraph::Shape,                  // Input Shape
+        size_t,                         // stride
+        InferenceEngine::Precision,     // Network precision
+        std::string>;                   // Device name
+
+class ReorgYoloLayerTest : public testing::WithParamInterface<ReorgYoloParamsTuple>,
+                            virtual public LayerTestsUtils::LayerTestsCommon {
+public:
+    static std::string getTestCaseName(const testing::TestParamInfo<ReorgYoloParamsTuple> &obj);
+
+protected:
+    void SetUp() override;
+};
+
+} // namespace LayerTestsDefinitions
diff --git a/inference-engine/tests/functional/plugin/shared/src/single_layer_tests/reorg_yolo.cpp b/inference-engine/tests/functional/plugin/shared/src/single_layer_tests/reorg_yolo.cpp
new file mode 100644 (file)
index 0000000..716e271
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+//
+
+#include "ie_core.hpp"
+
+#include "common_test_utils/common_utils.hpp"
+#include "functional_test_utils/blob_utils.hpp"
+#include "functional_test_utils/precision_utils.hpp"
+#include "functional_test_utils/plugin_cache.hpp"
+#include "functional_test_utils/skip_tests_config.hpp"
+
+#include "single_layer_tests/reorg_yolo.hpp"
+
+namespace LayerTestsDefinitions {
+
+std::string ReorgYoloLayerTest::getTestCaseName(const testing::TestParamInfo<ReorgYoloParamsTuple> &obj) {
+    ngraph::Shape inputShape;
+    size_t stride;
+    InferenceEngine::Precision netPrecision;
+    std::string targetName;
+    std::tie(inputShape, stride, netPrecision, targetName) = obj.param;
+    std::ostringstream result;
+    result << "IS=" << inputShape << "_";
+    result << "stride=" << stride << "_";
+    result << "netPRC=" << netPrecision.name() << "_";
+    result << "targetDevice=" << targetName << "_";
+    return result.str();
+}
+
+void ReorgYoloLayerTest::SetUp() {
+    ngraph::Shape inputShape;
+    size_t stride;
+    InferenceEngine::Precision netPrecision;
+    std::tie(inputShape, stride, netPrecision, targetDevice) = this->GetParam();
+    auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision);
+    auto param = std::make_shared<ngraph::op::Parameter>(ngraph::element::f32, inputShape);
+    auto reorg_yolo = std::make_shared<ngraph::op::v0::ReorgYolo>(param, stride);
+    function = std::make_shared<ngraph::Function>(std::make_shared<ngraph::opset1::Result>(reorg_yolo), ngraph::ParameterVector{param}, "ReorgYolo");
+}
+
+TEST_P(ReorgYoloLayerTest, CompareWithRefs) {
+    Run();
+};
+
+} // namespace LayerTestsDefinitions
index 75d4d56..e9b20f6 100644 (file)
@@ -33,7 +33,10 @@ namespace ngraph
                 /// \brief Constructs a ReorgYolo operation
                 ///
                 /// \param input          Input
-                /// \param strides        Stride to reorganize input by
+                /// \param stride         Stride to reorganize input by
+                ReorgYolo(const Output<Node>& input, const size_t stride);
+
+                // Constructor with `strides` for backward compatibility
                 ReorgYolo(const Output<Node>& input, const Strides& strides);
 
                 void validate_and_infer_types() override;
diff --git a/ngraph/core/reference/include/ngraph/runtime/reference/reorg_yolo.hpp b/ngraph/core/reference/include/ngraph/runtime/reference/reorg_yolo.hpp
new file mode 100644 (file)
index 0000000..9de4e01
--- /dev/null
@@ -0,0 +1,37 @@
+//*****************************************************************************
+// 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 <cmath>
+#include <cstddef>
+
+#include "ngraph/shape.hpp"
+
+namespace ngraph
+{
+    namespace runtime
+    {
+        namespace reference
+        {
+            void reorg_yolo(const char* arg,
+                            char* out,
+                            const Shape& in_shape,
+                            int64_t stride,
+                            const size_t elem_size);
+        }
+    }
+}
diff --git a/ngraph/core/reference/src/runtime/reference/reorg_yolo.cpp b/ngraph/core/reference/src/runtime/reference/reorg_yolo.cpp
new file mode 100644 (file)
index 0000000..0ac2d79
--- /dev/null
@@ -0,0 +1,89 @@
+//*****************************************************************************
+// 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 <cmath>
+#include <stdio.h>
+
+#include "ngraph/runtime/reference/reorg_yolo.hpp"
+#include "ngraph/shape.hpp"
+
+using namespace ngraph;
+
+namespace ngraph
+{
+    namespace runtime
+    {
+        namespace reference
+        {
+            void reorg_yolo(const char* arg,
+                            char* out,
+                            const Shape& in_shape,
+                            int64_t stride,
+                            const size_t elem_size)
+            {
+                // [N, C, H, W]
+                size_t in_N = in_shape[0];
+                size_t in_C = in_shape[1];
+                size_t in_H = in_shape[2];
+                size_t in_W = in_shape[3];
+
+                // Inference output shape logic:
+                // in_shape [N,C,H,W] -> out_shape [N, C*(stride*stride), H/stride, W/stride]
+                // ReorgYolo implementation calculates new indices like for backprop:
+                // in_shape [N,C,H,W] -> out_shape [N, C/(stride*stride), H*stride, W*stride]
+
+                size_t impl_out_C = in_C / (stride * stride);
+                if (impl_out_C == 0)
+                {
+                    throw ngraph_error(
+                        "ReorgYolo. For [N, C, H, W] input shape, C >= (stride*stride) is "
+                        "required.");
+                }
+                size_t impl_out_H = in_H * stride;
+                size_t impl_out_W = in_W * stride;
+
+                for (size_t n = 0; n < in_N; ++n)
+                {
+                    for (size_t c = 0; c < in_C; ++c)
+                    {
+                        for (size_t h = 0; h < in_H; ++h)
+                        {
+                            for (size_t w = 0; w < in_W; ++w)
+                            {
+                                size_t offset = c / impl_out_C;
+                                size_t impl_c = c % impl_out_C;
+                                size_t impl_h = h * stride + offset / stride;
+                                size_t impl_w = w * stride + offset % stride;
+
+                                size_t arg_index =
+                                    ((n * impl_out_C + impl_c) * impl_out_H + impl_h) * impl_out_W +
+                                    impl_w;
+                                size_t dest_index = ((n * in_C + c) * in_H + h) * in_W + w;
+
+                                arg_index *= elem_size;
+                                dest_index *= elem_size;
+
+                                std::copy(arg + arg_index,
+                                          arg + (arg_index + elem_size),
+                                          out + dest_index);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index f25c145..d9ede13 100644 (file)
@@ -15,6 +15,7 @@
 //*****************************************************************************
 
 #include "ngraph/op/reorg_yolo.hpp"
+#include "ngraph/runtime/reference/reorg_yolo.hpp"
 
 using namespace std;
 using namespace ngraph;
@@ -28,14 +29,37 @@ op::ReorgYolo::ReorgYolo(const Output<Node>& input, const Strides& strides)
     constructor_validate_and_infer_types();
 }
 
+op::ReorgYolo::ReorgYolo(const Output<Node>& input, const size_t stride)
+    : Op({input})
+    , m_strides(std::vector<size_t>{stride, stride})
+{
+    constructor_validate_and_infer_types();
+}
+
 void op::ReorgYolo::validate_and_infer_types()
 {
+    NODE_VALIDATION_CHECK(this, !m_strides.empty(), "Stride attribute is required.");
+
     auto input_et = get_input_element_type(0);
     if (get_input_partial_shape(0).is_static())
     {
         auto input_shape = get_input_partial_shape(0).to_shape();
-        Shape output_shape{input_shape[0], input_shape[1]};
+        NODE_VALIDATION_CHECK(
+            this, input_shape.size() == 4, "[N, C, H, W] input shape is required.");
+
+        NODE_VALIDATION_CHECK(this,
+                              (input_shape[2] % m_strides[0]) == 0,
+                              "For [N, C, H, W] input shape, H should be divisible by stride.");
+
+        NODE_VALIDATION_CHECK(this,
+                              (input_shape[3] % m_strides[0]) == 0,
+                              "For [N, C, H, W] input shape, W should be divisible by stride.");
 
+        NODE_VALIDATION_CHECK(this,
+                              input_shape[1] >= (m_strides[0] * m_strides[0]),
+                              "For [N, C, H, W] input shape, C >= (stride*stride) is required.");
+
+        Shape output_shape{input_shape[0], input_shape[1]};
         for (size_t i = 2; i < input_shape.size(); i++)
         {
             output_shape.push_back(input_shape[i] / m_strides[0]);
index 8969eb9..6f46f14 100644 (file)
@@ -157,6 +157,7 @@ set(SRC
     type_prop/read_value.cpp
     type_prop/reduce_l1.cpp
     type_prop/reduce_l2.cpp
+    type_prop/reorg_yolo.cpp
     type_prop/replace_slice.cpp
     type_prop/reshape.cpp
     type_prop/reverse.cpp
@@ -325,6 +326,7 @@ set(MULTI_TEST_SRC
     backend/reduce_prod.in.cpp
     backend/reduce_sum.in.cpp
     backend/relu.in.cpp
+    backend/reorg_yolo.in.cpp
     backend/replace_slice.in.cpp
     backend/reshape.in.cpp
     backend/reverse_sequence.in.cpp
index a962466..322c860 100644 (file)
@@ -1323,14 +1323,26 @@ TEST(attributes, mvn_op)
     EXPECT_EQ(g_op->get_eps(), op->get_eps());
 }
 
-TEST(attributes, reorg_yolo_op)
+TEST(attributes, reorg_yolo_op_stride)
 {
     FactoryRegistry<Node>::get().register_factory<opset3::ReorgYolo>();
-    const auto data = make_shared<op::Parameter>(element::i32, Shape{2, 3, 4, 5});
+    const auto data = make_shared<op::Parameter>(element::i32, Shape{1, 64, 26, 26});
+
+    const auto op = make_shared<op::v0::ReorgYolo>(data, 2);
+    NodeBuilder builder(op);
+    const auto g_op = as_type_ptr<op::v0::ReorgYolo>(builder.create());
+
+    EXPECT_EQ(g_op->get_strides(), op->get_strides());
+}
+
+TEST(attributes, reorg_yolo_op_strides)
+{
+    FactoryRegistry<Node>::get().register_factory<opset3::ReorgYolo>();
+    const auto data = make_shared<op::Parameter>(element::i32, Shape{1, 64, 26, 26});
 
-    const auto op = make_shared<opset3::ReorgYolo>(data, Strides{2});
+    const auto op = make_shared<op::v0::ReorgYolo>(data, Strides{2});
     NodeBuilder builder(op);
-    const auto g_op = as_type_ptr<opset3::ReorgYolo>(builder.create());
+    const auto g_op = as_type_ptr<op::v0::ReorgYolo>(builder.create());
 
     EXPECT_EQ(g_op->get_strides(), op->get_strides());
 }
diff --git a/ngraph/test/backend/reorg_yolo.in.cpp b/ngraph/test/backend/reorg_yolo.in.cpp
new file mode 100644 (file)
index 0000000..0389a2c
--- /dev/null
@@ -0,0 +1,101 @@
+//*****************************************************************************
+// 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 <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdlib>
+#include <random>
+#include <string>
+
+// clang-format off
+#ifdef ${BACKEND_NAME}_FLOAT_TOLERANCE_BITS
+#define DEFAULT_FLOAT_TOLERANCE_BITS ${BACKEND_NAME}_FLOAT_TOLERANCE_BITS
+#endif
+
+#ifdef ${BACKEND_NAME}_DOUBLE_TOLERANCE_BITS
+#define DEFAULT_DOUBLE_TOLERANCE_BITS ${BACKEND_NAME}_DOUBLE_TOLERANCE_BITS
+#endif
+// clang-format on
+
+#include "gtest/gtest.h"
+#include "ngraph/ngraph.hpp"
+#include "util/engine/test_engines.hpp"
+#include "util/test_case.hpp"
+#include "util/test_control.hpp"
+#include "util/type_prop.hpp"
+
+using namespace std;
+using namespace ngraph;
+
+static string s_manifest = "${MANIFEST}";
+using TestEngine = test::ENGINE_CLASS_NAME(${BACKEND_NAME});
+
+NGRAPH_TEST(${BACKEND_NAME}, reorg_yolo_stride_2)
+{
+    // in_shape [N,C,H,W]
+    const auto in_shape = Shape{1, 8, 4, 4};
+    auto p = make_shared<op::Parameter>(element::f32, in_shape);
+    size_t stride = 2;
+    auto reorg_yolo = make_shared<op::v0::ReorgYolo>(p, Strides{stride});
+    auto fun = make_shared<Function>(OutputVector{reorg_yolo}, ParameterVector{p});
+
+    std::vector<float> inputs(128);
+    std::iota(inputs.begin(), inputs.end(), 0);
+    std::vector<float> expected_result{
+        0,  2,  4,  6,  16, 18, 20, 22, 32,  34,  36,  38,  48,  50,  52,  54,
+        64, 66, 68, 70, 80, 82, 84, 86, 96,  98,  100, 102, 112, 114, 116, 118,
+        1,  3,  5,  7,  17, 19, 21, 23, 33,  35,  37,  39,  49,  51,  53,  55,
+        65, 67, 69, 71, 81, 83, 85, 87, 97,  99,  101, 103, 113, 115, 117, 119,
+        8,  10, 12, 14, 24, 26, 28, 30, 40,  42,  44,  46,  56,  58,  60,  62,
+        72, 74, 76, 78, 88, 90, 92, 94, 104, 106, 108, 110, 120, 122, 124, 126,
+        9,  11, 13, 15, 25, 27, 29, 31, 41,  43,  45,  47,  57,  59,  61,  63,
+        73, 75, 77, 79, 89, 91, 93, 95, 105, 107, 109, 111, 121, 123, 125, 127};
+    // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride]
+    Shape expected_shape = Shape{
+        in_shape[0], in_shape[1] * stride * stride, in_shape[2] / stride, in_shape[3] / stride};
+
+    auto test_case = test::TestCase<TestEngine>(fun);
+    test_case.add_input<float>(inputs);
+    test_case.add_expected_output<float>(expected_shape, expected_result);
+    test_case.run();
+}
+
+NGRAPH_TEST(${BACKEND_NAME}, reorg_yolo_stride_3)
+{
+    // in_shape [N,C,H,W]
+    const auto in_shape = Shape{1, 9, 3, 3};
+    auto p = make_shared<op::Parameter>(element::f32, in_shape);
+    size_t stride = 3;
+    auto reorg_yolo = make_shared<op::v0::ReorgYolo>(p, Strides{stride});
+    auto fun = make_shared<Function>(OutputVector{reorg_yolo}, ParameterVector{p});
+
+    std::vector<float> inputs(81);
+    std::iota(inputs.begin(), inputs.end(), 0);
+    std::vector<float> expected_result{
+        0,  3,  6,  27, 30, 33, 54, 57, 60, 1,  4,  7,  28, 31, 34, 55, 58, 61, 2,  5,  8,
+        29, 32, 35, 56, 59, 62, 9,  12, 15, 36, 39, 42, 63, 66, 69, 10, 13, 16, 37, 40, 43,
+        64, 67, 70, 11, 14, 17, 38, 41, 44, 65, 68, 71, 18, 21, 24, 45, 48, 51, 72, 75, 78,
+        19, 22, 25, 46, 49, 52, 73, 76, 79, 20, 23, 26, 47, 50, 53, 74, 77, 80};
+    // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride]
+    Shape expected_shape = Shape{
+        in_shape[0], in_shape[1] * stride * stride, in_shape[2] / stride, in_shape[3] / stride};
+
+    auto test_case = test::TestCase<TestEngine>(fun);
+    test_case.add_input<float>(inputs);
+    test_case.add_expected_output<float>(expected_shape, expected_result);
+    test_case.run();
+}
index a130668..0070aaa 100644 (file)
@@ -78,6 +78,7 @@
 #include "ngraph/runtime/reference/product.hpp"
 #include "ngraph/runtime/reference/quantize.hpp"
 #include "ngraph/runtime/reference/relu.hpp"
+#include "ngraph/runtime/reference/reorg_yolo.hpp"
 #include "ngraph/runtime/reference/replace_slice.hpp"
 #include "ngraph/runtime/reference/reshape.hpp"
 #include "ngraph/runtime/reference/result.hpp"
@@ -932,6 +933,16 @@ protected:
                                              pbox->get_attrs());
             break;
         }
+        case OP_TYPEID::ReorgYolo_v0:
+        {
+            const op::v0::ReorgYolo* reorg_yolo = static_cast<const op::v0::ReorgYolo*>(&node);
+            runtime::reference::reorg_yolo(args[0]->get_data_ptr<char>(),
+                                           out[0]->get_data_ptr<char>(),
+                                           args[0]->get_shape(),
+                                           reorg_yolo->get_strides().at(0),
+                                           args[0]->get_element_type().size());
+            break;
+        }
         case OP_TYPEID::Quantize:
         {
             const op::Quantize* quantize = static_cast<const op::Quantize*>(&node);
index b25b37a..de33cda 100644 (file)
@@ -21,6 +21,7 @@
 #define ID_SUFFIX(NAME) NAME##_v0
 NGRAPH_OP(CTCGreedyDecoder, ngraph::op::v0)
 NGRAPH_OP(DetectionOutput, op::v0)
+NGRAPH_OP(ReorgYolo, op::v0)
 NGRAPH_OP(RNNCell, op::v0)
 #undef ID_SUFFIX
 
diff --git a/ngraph/test/type_prop/reorg_yolo.cpp b/ngraph/test/type_prop/reorg_yolo.cpp
new file mode 100644 (file)
index 0000000..c132d1f
--- /dev/null
@@ -0,0 +1,97 @@
+//*****************************************************************************
+// 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/type_prop.hpp"
+
+using namespace std;
+using namespace ngraph;
+
+TEST(type_prop, reorg_yolo_stride_2)
+{
+    const auto in_shape = Shape{1, 64, 26, 26};
+    size_t stride = 2;
+    auto data_param = make_shared<op::Parameter>(element::f32, in_shape);
+    auto reorg_yolo = make_shared<op::v0::ReorgYolo>(data_param, stride);
+
+    // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride]
+    Shape expected_shape = Shape{1, 256, 13, 13};
+
+    EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape);
+}
+
+TEST(type_prop, reorg_yolo_stride_2_batch_2)
+{
+    const auto in_shape = Shape{2, 64, 26, 26};
+    size_t stride = 2;
+    auto data_param = make_shared<op::Parameter>(element::f32, in_shape);
+    auto reorg_yolo = make_shared<op::v0::ReorgYolo>(data_param, stride);
+
+    // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride]
+    Shape expected_shape = Shape{2, 256, 13, 13};
+
+    EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape);
+}
+
+TEST(type_prop, reorg_yolo_stride_2_smaller_H)
+{
+    const auto in_shape = Shape{1, 24, 34, 62};
+    size_t stride = 2;
+    auto data_param = make_shared<op::Parameter>(element::f32, in_shape);
+    auto reorg_yolo = make_shared<op::v0::ReorgYolo>(data_param, stride);
+
+    // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride]
+    Shape expected_shape = Shape{1, 96, 17, 31};
+    EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape);
+}
+
+TEST(type_prop, reorg_yolo_stride_3)
+{
+    const auto in_shape = Shape{1, 9, 3, 3};
+    size_t stride = 3;
+    auto data_param = make_shared<op::Parameter>(element::f32, in_shape);
+    auto reorg_yolo = make_shared<op::v0::ReorgYolo>(data_param, stride);
+
+    // in_shape [N,C,H,W] -> out_shape [N, C*stride*stride, H/stride, W/stride]
+    Shape expected_shape = Shape{
+        in_shape[0], in_shape[1] * stride * stride, in_shape[2] / stride, in_shape[3] / stride};
+
+    EXPECT_EQ(reorg_yolo->get_output_shape(0), expected_shape);
+}
+
+TEST(type_prop, reorg_yolo_catch_small_shape_stride)
+{
+    const auto in_shape = Shape{1, 1, 4, 4};
+    size_t stride = 2;
+    auto data_param = make_shared<op::Parameter>(element::f32, in_shape);
+    try
+    {
+        // Throw error test: For [N, C, H, W] input shape, C >= (stride*stride) is required.
+        auto reorg_yolo = make_shared<op::v0::ReorgYolo>(data_param, stride);
+
+        // Should have thrown, so fail if it didn't
+        FAIL() << "Incompatible stride was not detected.";
+    }
+    catch (const ngraph_error& error)
+    {
+        EXPECT_HAS_SUBSTRING(error.what(), std::string("stride"));
+    }
+    catch (...)
+    {
+        FAIL() << "Stride size check failed for unexpected reason.";
+    }
+}