MLCE-139: add capability to align corners for bilinear resize

* Add parsing of the related parameter to TfLiteParser
* Update ResizeDescriptor to store the additional parameter
* Update NEON/CL workload to pass the additional parameter.
* Update Reference workload to pass and handle the additional parameter.

!ComputeLibrary:2538
!ComputeLibrary:2569
!armnn:2612

Signed-off-by: Sang-Hoon Park <sang-hoon.park@arm.com>
Change-Id: Id149e1c24c2abed7e9dd81939acf54dfabfcdfd2
diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp
index 45c0f42..2d7b17e 100644
--- a/include/armnn/Descriptors.hpp
+++ b/include/armnn/Descriptors.hpp
@@ -728,14 +728,16 @@
         , m_TargetHeight(0)
         , m_Method(ResizeMethod::NearestNeighbor)
         , m_DataLayout(DataLayout::NCHW)
+        , m_BilinearAlignCorners(false)
     {}
 
     bool operator ==(const ResizeDescriptor& rhs) const
     {
-        return m_TargetWidth  == rhs.m_TargetWidth &&
-               m_TargetHeight == rhs.m_TargetHeight &&
-               m_Method       == rhs.m_Method &&
-               m_DataLayout   == rhs.m_DataLayout;
+        return m_TargetWidth          == rhs.m_TargetWidth &&
+               m_TargetHeight         == rhs.m_TargetHeight &&
+               m_Method               == rhs.m_Method &&
+               m_DataLayout           == rhs.m_DataLayout &&
+               m_BilinearAlignCorners == rhs.m_BilinearAlignCorners;
     }
 
     /// Target width value.
@@ -747,6 +749,8 @@
     ResizeMethod m_Method;
     /// The data layout to be used (NCHW, NHWC).
     DataLayout m_DataLayout;
+    /// Aligned corners for bilinear method
+    bool m_BilinearAlignCorners;
 };
 
 
diff --git a/src/armnnTfLiteParser/TfLiteParser.cpp b/src/armnnTfLiteParser/TfLiteParser.cpp
index d3eed9c..10bb0f6 100644
--- a/src/armnnTfLiteParser/TfLiteParser.cpp
+++ b/src/armnnTfLiteParser/TfLiteParser.cpp
@@ -2004,6 +2004,11 @@
         case ResizeMethod::Bilinear:
         {
             layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
+
+            const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
+            const auto * options     = operatorPtr->builtin_options.AsResizeBilinearOptions();
+
+            desc.m_BilinearAlignCorners = options->align_corners;
             break;
         }
         case ResizeMethod::NearestNeighbor:
diff --git a/src/backends/cl/workloads/ClResizeWorkload.cpp b/src/backends/cl/workloads/ClResizeWorkload.cpp
index bcf4281..05b212c 100644
--- a/src/backends/cl/workloads/ClResizeWorkload.cpp
+++ b/src/backends/cl/workloads/ClResizeWorkload.cpp
@@ -38,7 +38,9 @@
                                           aclInterpolationPolicy,
                                           arm_compute::BorderMode::REPLICATE,
                                           arm_compute::PixelValue(0.f),
-                                          arm_compute::SamplingPolicy::TOP_LEFT);
+                                          arm_compute::SamplingPolicy::TOP_LEFT,
+                                          true,
+                                          descriptor.m_BilinearAlignCorners);
 }
 
 ClResizeWorkload::ClResizeWorkload(const ResizeQueueDescriptor& descriptor, const WorkloadInfo& info) :
@@ -61,7 +63,9 @@
                             aclInterpolationPolicy,
                             arm_compute::BorderMode::REPLICATE,
                             arm_compute::PixelValue(0.f),
-                            arm_compute::SamplingPolicy::TOP_LEFT);
+                            arm_compute::SamplingPolicy::TOP_LEFT,
+                            true,
+                            descriptor.m_Parameters.m_BilinearAlignCorners);
 };
 
 void ClResizeWorkload::Execute() const
diff --git a/src/backends/neon/workloads/NeonResizeWorkload.cpp b/src/backends/neon/workloads/NeonResizeWorkload.cpp
index a4e4a4a..e936ab7 100644
--- a/src/backends/neon/workloads/NeonResizeWorkload.cpp
+++ b/src/backends/neon/workloads/NeonResizeWorkload.cpp
@@ -60,7 +60,9 @@
                             aclInterpolationPolicy,
                             arm_compute::BorderMode::REPLICATE,
                             arm_compute::PixelValue(0.f),
-                            arm_compute::SamplingPolicy::TOP_LEFT);
+                            arm_compute::SamplingPolicy::TOP_LEFT,
+                            true,
+                            descriptor.m_Parameters.m_BilinearAlignCorners);
 };
 
 void NeonResizeWorkload::Execute() const
diff --git a/src/backends/reference/workloads/RefResizeWorkload.cpp b/src/backends/reference/workloads/RefResizeWorkload.cpp
index 26225f8..624b426 100644
--- a/src/backends/reference/workloads/RefResizeWorkload.cpp
+++ b/src/backends/reference/workloads/RefResizeWorkload.cpp
@@ -29,7 +29,13 @@
     std::unique_ptr<Encoder<float>> encoderPtr = MakeEncoder<float>(outputInfo, m_Data.m_Outputs[0]->Map());
     Encoder<float> &encoder = *encoderPtr;
 
-    Resize(decoder, inputInfo, encoder, outputInfo, m_Data.m_Parameters.m_DataLayout, m_Data.m_Parameters.m_Method);
+    Resize(decoder,
+           inputInfo,
+           encoder,
+           outputInfo,
+           m_Data.m_Parameters.m_DataLayout,
+           m_Data.m_Parameters.m_Method,
+           m_Data.m_Parameters.m_BilinearAlignCorners);
 }
 
 } //namespace armnn
diff --git a/src/backends/reference/workloads/Resize.cpp b/src/backends/reference/workloads/Resize.cpp
index 3050bae..a26e34a 100644
--- a/src/backends/reference/workloads/Resize.cpp
+++ b/src/backends/reference/workloads/Resize.cpp
@@ -37,7 +37,8 @@
             Encoder<float>&   out,
             const TensorInfo& outputInfo,
             DataLayoutIndexed dataLayout,
-            armnn::ResizeMethod resizeMethod)
+            armnn::ResizeMethod resizeMethod,
+            bool alignCorners)
 {
     // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
     // image is projected into the input image to figure out the interpolants and weights. Note that this
@@ -51,10 +52,14 @@
     const unsigned int outputHeight = outputInfo.GetShape()[dataLayout.GetHeightIndex()];
     const unsigned int outputWidth = outputInfo.GetShape()[dataLayout.GetWidthIndex()];
 
+    const unsigned int sizeOffset = resizeMethod == armnn::ResizeMethod::Bilinear && alignCorners ? 1 : 0;
+
     // How much to scale pixel coordinates in the output image, to get the corresponding pixel coordinates
     // in the input image.
-    const float scaleY = boost::numeric_cast<float>(inputHeight) / boost::numeric_cast<float>(outputHeight);
-    const float scaleX = boost::numeric_cast<float>(inputWidth) / boost::numeric_cast<float>(outputWidth);
+    const float scaleY = boost::numeric_cast<float>(inputHeight - sizeOffset)
+                       / boost::numeric_cast<float>(outputHeight - sizeOffset);
+    const float scaleX = boost::numeric_cast<float>(inputWidth - sizeOffset)
+                       / boost::numeric_cast<float>(outputWidth - sizeOffset);
 
     TensorShape inputShape =  inputInfo.GetShape();
     TensorShape outputShape =  outputInfo.GetShape();
diff --git a/src/backends/reference/workloads/Resize.hpp b/src/backends/reference/workloads/Resize.hpp
index 4c35794..cd8835f 100644
--- a/src/backends/reference/workloads/Resize.hpp
+++ b/src/backends/reference/workloads/Resize.hpp
@@ -19,6 +19,7 @@
             Encoder<float>&               out,
             const TensorInfo&             outputInfo,
             armnnUtils::DataLayoutIndexed dataLayout = DataLayout::NCHW,
-            ResizeMethod                  resizeMethod = ResizeMethod::NearestNeighbor);
+            ResizeMethod                  resizeMethod = ResizeMethod::NearestNeighbor,
+            bool                          alignConers = false);
 
 } // namespace armnn