IVGCVSW-2555 Add no-op implementation for Detection PostProcess

 * Added DetectionPostProcessQueueDescriptor to WorkloadData
 * Added CreateDetectionPostProcess function in WorkloadFactory.hpp
 * Added stub implementation of the CreateDetectionPostProcess
in workload factories
 * Added DetectionPostProcessLayer stub implementation
 * Added AddDetectionPostProcessLayer to Network
 * Added IsDetectionPostProcessSupported to LayerSupportBase

Change-Id: Ifc071b3b6b12877c997bdcc43d769c8f891d5c6c
diff --git a/Android.mk b/Android.mk
index 62a992d..0e0fa9c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -92,6 +92,7 @@
         src/armnn/layers/ConvertFp32ToFp16Layer.cpp \
         src/armnn/layers/DebugLayer.cpp \
         src/armnn/layers/DepthwiseConvolution2dLayer.cpp \
+        src/armnn/layers/DetectionPostProcessLayer.cpp \
         src/armnn/layers/DivisionLayer.cpp \
         src/armnn/layers/ElementwiseBaseLayer.cpp \
         src/armnn/layers/EqualLayer.cpp \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 339efd0..6b893f2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -191,6 +191,8 @@
     src/armnn/layers/DebugLayer.cpp
     src/armnn/layers/DepthwiseConvolution2dLayer.hpp
     src/armnn/layers/DepthwiseConvolution2dLayer.cpp
+    src/armnn/layers/DetectionPostProcessLayer.hpp
+    src/armnn/layers/DetectionPostProcessLayer.cpp
     src/armnn/layers/ElementwiseBaseLayer.hpp
     src/armnn/layers/ElementwiseBaseLayer.cpp
     src/armnn/layers/EqualLayer.hpp
diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp
index 4497d0d..44235c7 100644
--- a/include/armnn/Descriptors.hpp
+++ b/include/armnn/Descriptors.hpp
@@ -327,6 +327,10 @@
     DataLayout m_DataLayout;
 };
 
+struct DetectionPostProcessDescriptor
+{
+};
+
 /// A NormalizationDescriptor for the NormalizationLayer.
 struct NormalizationDescriptor
 {
diff --git a/include/armnn/DescriptorsFwd.hpp b/include/armnn/DescriptorsFwd.hpp
index c1d21b5..f0a4cff 100644
--- a/include/armnn/DescriptorsFwd.hpp
+++ b/include/armnn/DescriptorsFwd.hpp
@@ -13,6 +13,7 @@
 struct Convolution2dDescriptor;
 struct DebugDescriptor;
 struct DepthwiseConvolution2dDescriptor;
+struct DetectionPostProcessDescriptor;
 struct FakeQuantizationDescriptor;
 struct FullyConnectedDescriptor;
 struct L2NormalizationDescriptor;
diff --git a/include/armnn/ILayerSupport.hpp b/include/armnn/ILayerSupport.hpp
index a603229..b5f9e6a 100644
--- a/include/armnn/ILayerSupport.hpp
+++ b/include/armnn/ILayerSupport.hpp
@@ -77,6 +77,12 @@
                      const Optional<TensorInfo>& biases,
                      Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const = 0;
 
+    virtual bool IsDetectionPostProcessSupported(
+                     const TensorInfo& input0,
+                     const TensorInfo& input1,
+                     const DetectionPostProcessDescriptor& descriptor,
+                     Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const = 0;
+
     virtual bool IsDivisionSupported(const TensorInfo& input0,
                                      const TensorInfo& input1,
                                      const TensorInfo& output,
diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp
index 05962b9..1f7354d 100644
--- a/include/armnn/INetwork.hpp
+++ b/include/armnn/INetwork.hpp
@@ -127,6 +127,13 @@
         const ConstTensor& biases,
         const char* name = nullptr) = 0;
 
+    /// Adds a Detection PostProcess layer to the network.
+    /// @param descriptor - Description of the Detection PostProcess layer.
+    /// @param name - Optional name for the layer.
+    /// @return - Interface for configuring the layer.
+    virtual IConnectableLayer* AddDetectionPostProcessLayer(
+        const DetectionPostProcessDescriptor& descriptor, const char* name = nullptr) =0;
+
     /// Adds a fully connected layer to the network.
     /// @param fullyConnectedDescriptor - Description of the fully connected layer.
     /// @param weights - Tensor for the weights data.
diff --git a/src/armnn/InternalTypes.cpp b/src/armnn/InternalTypes.cpp
index 15f4aa0..e4a6ac8 100644
--- a/src/armnn/InternalTypes.cpp
+++ b/src/armnn/InternalTypes.cpp
@@ -24,6 +24,7 @@
         case LayerType::Convolution2d: return "Convolution2d";
         case LayerType::Debug: return "Debug";
         case LayerType::DepthwiseConvolution2d: return "DepthwiseConvolution2d";
+        case LayerType::DetectionPostProcess: return "DetectionPostProcess";
         case LayerType::Division: return "Division";
         case LayerType::Equal: return "Equal";
         case LayerType::FakeQuantization: return "FakeQuantization";
diff --git a/src/armnn/InternalTypes.hpp b/src/armnn/InternalTypes.hpp
index 704efdf..a61c7b8 100644
--- a/src/armnn/InternalTypes.hpp
+++ b/src/armnn/InternalTypes.hpp
@@ -24,6 +24,7 @@
     Convolution2d,
     Debug,
     DepthwiseConvolution2d,
+    DetectionPostProcess,
     Division,
     Equal,
     FakeQuantization,
diff --git a/src/armnn/LayerSupport.cpp b/src/armnn/LayerSupport.cpp
index 2eaf780..484251c 100644
--- a/src/armnn/LayerSupport.cpp
+++ b/src/armnn/LayerSupport.cpp
@@ -190,6 +190,13 @@
     FORWARD_LAYER_SUPPORT_FUNC(backend, IsDepthwiseConvolutionSupported, input, output, descriptor, weights, biases);
 }
 
+bool IsDetectionPostProcessSupported(const BackendId& backend,
+                                     const TensorInfo& input0,
+                                     const TensorInfo& input1,
+                                     const DetectionPostProcessDescriptor& descriptor,
+                                     char* reasonIfUnsupported,
+                                     size_t reasonIfUnsupportedMaxLength);
+
 bool IsDivisionSupported(const BackendId& backend,
                          const TensorInfo& input0,
                          const TensorInfo& input1,
diff --git a/src/armnn/LayersFwd.hpp b/src/armnn/LayersFwd.hpp
index 27806c5..497b517 100644
--- a/src/armnn/LayersFwd.hpp
+++ b/src/armnn/LayersFwd.hpp
@@ -16,6 +16,7 @@
 #include "layers/Convolution2dLayer.hpp"
 #include "layers/DebugLayer.hpp"
 #include "layers/DepthwiseConvolution2dLayer.hpp"
+#include "layers/DetectionPostProcessLayer.hpp"
 #include "layers/DivisionLayer.hpp"
 #include "layers/EqualLayer.hpp"
 #include "layers/FakeQuantizationLayer.hpp"
@@ -84,6 +85,7 @@
 DECLARE_LAYER(Convolution2d)
 DECLARE_LAYER(Debug)
 DECLARE_LAYER(DepthwiseConvolution2d)
+DECLARE_LAYER(DetectionPostProcess)
 DECLARE_LAYER(Division)
 DECLARE_LAYER(Equal)
 DECLARE_LAYER(FakeQuantization)
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index f9ebad2..662a9cc 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -594,6 +594,12 @@
     return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, &biases, name);
 }
 
+IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
+                                                         const char* name)
+{
+    return m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
+}
+
 IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
                                             const char* name)
 {
diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp
index 7690faf..4239ac5 100644
--- a/src/armnn/Network.hpp
+++ b/src/armnn/Network.hpp
@@ -57,6 +57,10 @@
         const ConstTensor&                      biases,
         const char*                             name = nullptr) override;
 
+    IConnectableLayer* AddDetectionPostProcessLayer(
+        const DetectionPostProcessDescriptor& descriptor,
+        const char* name = nullptr) override;
+
     IConnectableLayer* AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
         const ConstTensor& weights,
         const char* name = nullptr) override;
diff --git a/src/armnn/layers/DetectionPostProcessLayer.cpp b/src/armnn/layers/DetectionPostProcessLayer.cpp
new file mode 100644
index 0000000..99aaac7
--- /dev/null
+++ b/src/armnn/layers/DetectionPostProcessLayer.cpp
@@ -0,0 +1,39 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "DetectionPostProcessLayer.hpp"
+
+#include "LayerCloneBase.hpp"
+
+#include <armnn/TypesUtils.hpp>
+#include <backendsCommon/WorkloadData.hpp>
+#include <backendsCommon/WorkloadFactory.hpp>
+
+namespace armnn
+{
+
+DetectionPostProcessLayer::DetectionPostProcessLayer(const DetectionPostProcessDescriptor& param, const char* name)
+    : LayerWithParameters(2, 4, LayerType::DetectionPostProcess, param, name)
+{
+}
+
+std::unique_ptr<IWorkload> DetectionPostProcessLayer::CreateWorkload(const armnn::Graph& graph,
+                                                                     const armnn::IWorkloadFactory& factory) const
+{
+    DetectionPostProcessQueueDescriptor descriptor;
+    return factory.CreateDetectionPostProcess(descriptor, PrepInfoAndDesc(descriptor, graph));
+}
+
+DetectionPostProcessLayer* DetectionPostProcessLayer::Clone(Graph& graph) const
+{
+    return CloneBase<DetectionPostProcessLayer>(graph, m_Param, GetName());
+}
+
+void DetectionPostProcessLayer::ValidateTensorShapesFromInputs()
+{
+    VerifyLayerConnections(2, CHECK_LOCATION());
+}
+
+} // namespace armnn
diff --git a/src/armnn/layers/DetectionPostProcessLayer.hpp b/src/armnn/layers/DetectionPostProcessLayer.hpp
new file mode 100644
index 0000000..b5a1cf1
--- /dev/null
+++ b/src/armnn/layers/DetectionPostProcessLayer.hpp
@@ -0,0 +1,43 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "LayerWithParameters.hpp"
+
+namespace armnn
+{
+
+/// This layer represents a detection postprocess operator.
+class DetectionPostProcessLayer : public LayerWithParameters<DetectionPostProcessDescriptor>
+{
+public:
+    /// Makes a workload for the DetectionPostProcess type.
+    /// @param [in] graph The graph where this layer can be found.
+    /// @param [in] factory The workload factory which will create the workload.
+    /// @return A pointer to the created workload, or nullptr if not created.
+    virtual std::unique_ptr<IWorkload> CreateWorkload(const Graph& graph,
+                                                      const IWorkloadFactory& factory) const override;
+
+    /// Creates a dynamically-allocated copy of this layer.
+    /// @param [in] graph The graph into which this layer is being cloned.
+    DetectionPostProcessLayer* Clone(Graph& graph) const override;
+
+    /// Check if the input tensor shape(s)
+    /// will lead to a valid configuration of @ref DetectionPostProcessLayer.
+    void ValidateTensorShapesFromInputs() override;
+
+protected:
+    /// Constructor to create a DetectionPostProcessLayer.
+    /// @param [in] param DetectionPostProcessDescriptor to configure the detection postprocess.
+    /// @param [in] name Optional name for the layer.
+    DetectionPostProcessLayer(const DetectionPostProcessDescriptor& param, const char* name);
+
+    /// Default destructor
+    ~DetectionPostProcessLayer() = default;
+};
+
+} // namespace armnn
+
diff --git a/src/backends/backendsCommon/LayerSupportBase.cpp b/src/backends/backendsCommon/LayerSupportBase.cpp
index 75790dc..6358f6f 100644
--- a/src/backends/backendsCommon/LayerSupportBase.cpp
+++ b/src/backends/backendsCommon/LayerSupportBase.cpp
@@ -116,6 +116,14 @@
     return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported);
 }
 
+bool LayerSupportBase::IsDetectionPostProcessSupported(const armnn::TensorInfo& input0,
+                                                       const armnn::TensorInfo& input1,
+                                                       const armnn::DetectionPostProcessDescriptor& descriptor,
+                                                       armnn::Optional<std::string&> reasonIfUnsupported) const
+{
+    return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported);
+}
+
 bool LayerSupportBase::IsDivisionSupported(const TensorInfo& input0,
                                            const TensorInfo& input1,
                                            const TensorInfo& output,
diff --git a/src/backends/backendsCommon/LayerSupportBase.hpp b/src/backends/backendsCommon/LayerSupportBase.hpp
index c3acdca..bf81459 100644
--- a/src/backends/backendsCommon/LayerSupportBase.hpp
+++ b/src/backends/backendsCommon/LayerSupportBase.hpp
@@ -68,6 +68,11 @@
                                          const Optional<TensorInfo>& biases,
                                          Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
 
+    bool IsDetectionPostProcessSupported(const TensorInfo& input0,
+                                         const TensorInfo& input1,
+                                         const DetectionPostProcessDescriptor& descriptor,
+                                         Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+
     bool IsDivisionSupported(const TensorInfo& input0,
                              const TensorInfo& input1,
                              const TensorInfo& output,
diff --git a/src/backends/backendsCommon/WorkloadData.hpp b/src/backends/backendsCommon/WorkloadData.hpp
index 2d68c9f..e44eba7 100644
--- a/src/backends/backendsCommon/WorkloadData.hpp
+++ b/src/backends/backendsCommon/WorkloadData.hpp
@@ -169,6 +169,11 @@
     void Validate(const WorkloadInfo& workloadInfo) const;
 };
 
+struct DetectionPostProcessQueueDescriptor : QueueDescriptorWithParameters<DetectionPostProcessDescriptor>
+{
+    void Validate(const WorkloadInfo& workloadInfo) const;
+};
+
 // Normalization layer workload data.
 struct NormalizationQueueDescriptor : QueueDescriptorWithParameters<NormalizationDescriptor>
 {
diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp
index 38a2402..cffb548 100644
--- a/src/backends/backendsCommon/WorkloadFactory.cpp
+++ b/src/backends/backendsCommon/WorkloadFactory.cpp
@@ -230,6 +230,18 @@
                                                      reason);
             break;
         }
+        case LayerType::DetectionPostProcess:
+        {
+            const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
+            const TensorInfo& input1 = layer.GetInputSlot(1).GetConnection()->GetTensorInfo();
+            auto cLayer = boost::polymorphic_downcast<const DetectionPostProcessLayer*>(&layer);
+            const DetectionPostProcessDescriptor& descriptor = cLayer->GetParameters();
+            result = layerSupportObject->IsDetectionPostProcessSupported(input0,
+                                                                         input1,
+                                                                         descriptor,
+                                                                         reason);
+            break;
+        }
         case LayerType::Equal:
         {
             const TensorInfo& input0 = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
diff --git a/src/backends/backendsCommon/WorkloadFactory.hpp b/src/backends/backendsCommon/WorkloadFactory.hpp
index dd47dd6..8883349 100644
--- a/src/backends/backendsCommon/WorkloadFactory.hpp
+++ b/src/backends/backendsCommon/WorkloadFactory.hpp
@@ -79,6 +79,9 @@
     virtual std::unique_ptr<IWorkload> CreateDepthwiseConvolution2d(
         const DepthwiseConvolution2dQueueDescriptor& descriptor, const WorkloadInfo& info) const = 0;
 
+    virtual std::unique_ptr<IWorkload> CreateDetectionPostProcess(
+        const DetectionPostProcessQueueDescriptor& descriptor, const WorkloadInfo& info) const = 0;
+
     virtual std::unique_ptr<IWorkload> CreateNormalization(const NormalizationQueueDescriptor& descriptor,
                                                            const WorkloadInfo&                 info) const = 0;
 
diff --git a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
index 43c7581..3df7183 100644
--- a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
+++ b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
@@ -336,6 +336,8 @@
 
 DECLARE_LAYER_POLICY_2_PARAM(DepthwiseConvolution2d)
 
+DECLARE_LAYER_POLICY_2_PARAM(DetectionPostProcess)
+
 DECLARE_LAYER_POLICY_1_PARAM(Equal)
 
 DECLARE_LAYER_POLICY_2_PARAM(FakeQuantization)
diff --git a/src/backends/cl/ClWorkloadFactory.cpp b/src/backends/cl/ClWorkloadFactory.cpp
index 7a53257..6cd9c6d 100644
--- a/src/backends/cl/ClWorkloadFactory.cpp
+++ b/src/backends/cl/ClWorkloadFactory.cpp
@@ -186,6 +186,12 @@
     return MakeWorkload<ClDepthwiseConvolutionWorkload>(descriptor, info);
 }
 
+std::unique_ptr<IWorkload> ClWorkloadFactory::CreateDetectionPostProcess(
+    const armnn::DetectionPostProcessQueueDescriptor& descriptor, const armnn::WorkloadInfo& info) const
+{
+    return MakeWorkload<NullWorkload, NullWorkload>(descriptor, info);
+}
+
 std::unique_ptr<armnn::IWorkload> ClWorkloadFactory::CreateNormalization(const NormalizationQueueDescriptor& descriptor,
                                                                          const WorkloadInfo&                 info) const
 {
diff --git a/src/backends/cl/ClWorkloadFactory.hpp b/src/backends/cl/ClWorkloadFactory.hpp
index 7425537..5ef7d08 100644
--- a/src/backends/cl/ClWorkloadFactory.hpp
+++ b/src/backends/cl/ClWorkloadFactory.hpp
@@ -69,6 +69,9 @@
     std::unique_ptr<IWorkload> CreateDepthwiseConvolution2d(const DepthwiseConvolution2dQueueDescriptor& descriptor,
                                                             const WorkloadInfo& info) const override;
 
+    std::unique_ptr<IWorkload> CreateDetectionPostProcess(const DetectionPostProcessQueueDescriptor& descriptor,
+                                                          const WorkloadInfo& info) const override;
+
     std::unique_ptr<IWorkload> CreateNormalization(const NormalizationQueueDescriptor& descriptor,
                                                    const WorkloadInfo& info) const override;
 
diff --git a/src/backends/neon/NeonWorkloadFactory.cpp b/src/backends/neon/NeonWorkloadFactory.cpp
index e8a00d6..76e1dd0 100644
--- a/src/backends/neon/NeonWorkloadFactory.cpp
+++ b/src/backends/neon/NeonWorkloadFactory.cpp
@@ -152,6 +152,12 @@
     return std::make_unique<NeonDepthwiseConvolutionWorkload>(descriptor, info);
 }
 
+std::unique_ptr<IWorkload> NeonWorkloadFactory::CreateDetectionPostProcess(
+    const armnn::DetectionPostProcessQueueDescriptor& descriptor, const armnn::WorkloadInfo& info) const
+{
+    return MakeWorkloadHelper<NullWorkload, NullWorkload>(descriptor, info);
+}
+
 std::unique_ptr<armnn::IWorkload> NeonWorkloadFactory::CreateNormalization(
     const NormalizationQueueDescriptor& descriptor, const WorkloadInfo& info) const
 {
diff --git a/src/backends/neon/NeonWorkloadFactory.hpp b/src/backends/neon/NeonWorkloadFactory.hpp
index 52caf7d..dd8d9a2 100644
--- a/src/backends/neon/NeonWorkloadFactory.hpp
+++ b/src/backends/neon/NeonWorkloadFactory.hpp
@@ -70,6 +70,9 @@
     std::unique_ptr<IWorkload> CreateDepthwiseConvolution2d(const DepthwiseConvolution2dQueueDescriptor& descriptor,
                                                             const WorkloadInfo& info) const override;
 
+    std::unique_ptr<IWorkload> CreateDetectionPostProcess(const DetectionPostProcessQueueDescriptor& descriptor,
+                                                          const WorkloadInfo& info) const override;
+
     std::unique_ptr<IWorkload> CreateNormalization(const NormalizationQueueDescriptor& descriptor,
                                                    const WorkloadInfo& info) const override;
 
diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp
index 75a9efd..103abdd 100644
--- a/src/backends/reference/RefWorkloadFactory.cpp
+++ b/src/backends/reference/RefWorkloadFactory.cpp
@@ -151,6 +151,12 @@
         RefDepthwiseConvolution2dUint8Workload>(descriptor, info);
 }
 
+std::unique_ptr<IWorkload> RefWorkloadFactory::CreateDetectionPostProcess(
+    const armnn::DetectionPostProcessQueueDescriptor& descriptor, const armnn::WorkloadInfo& info) const
+{
+    return MakeWorkload<NullWorkload, NullWorkload>(descriptor, info);
+}
+
 std::unique_ptr<armnn::IWorkload> RefWorkloadFactory::CreateNormalization(
     const NormalizationQueueDescriptor& descriptor, const WorkloadInfo& info) const
 {
diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp
index 79e73a2..211f93b 100644
--- a/src/backends/reference/RefWorkloadFactory.hpp
+++ b/src/backends/reference/RefWorkloadFactory.hpp
@@ -87,6 +87,9 @@
     std::unique_ptr<IWorkload> CreateDepthwiseConvolution2d(const DepthwiseConvolution2dQueueDescriptor& descriptor,
                                                             const WorkloadInfo& info) const override;
 
+    std::unique_ptr<IWorkload> CreateDetectionPostProcess(const DetectionPostProcessQueueDescriptor& descriptor,
+                                                          const WorkloadInfo& info) const override;
+
     std::unique_ptr<IWorkload> CreateNormalization(const NormalizationQueueDescriptor& descriptor,
                                                    const WorkloadInfo& info) const override;