From: David Monahan Date: Fri, 19 Jun 2020 15:43:48 +0000 (+0100) Subject: IVGCVSW-4707 - Add AlignCorners and HalfPixelCenters to Resize X-Git-Tag: submit/tizen/20210421.062230~476 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ab219756319667ae7abb0d767cf15e766ab343b1;p=platform%2Fupstream%2Farmnn.git IVGCVSW-4707 - Add AlignCorners and HalfPixelCenters to Resize * Added AlignCorners and HalfPixelCenters Parameters to Resize * Added Unit Tests Signed-off-by: David Monahan Change-Id: I83420a9bcb7beec9073d201448f64eb53090e1f1 --- diff --git a/src/armnn/SerializeLayerParameters.cpp b/src/armnn/SerializeLayerParameters.cpp index 76b92f3..e4bf094 100644 --- a/src/armnn/SerializeLayerParameters.cpp +++ b/src/armnn/SerializeLayerParameters.cpp @@ -293,6 +293,8 @@ void StringifyLayerParameters::Serialize(ParameterStri fn("TargetWidth", std::to_string(desc.m_TargetWidth)); fn("TargetHeight", std::to_string(desc.m_TargetHeight)); fn("DataLayout", GetDataLayoutName(desc.m_DataLayout)); + fn("AlignCorners", std::to_string(desc.m_AlignCorners)); + fn("HalfPixelCenters", std::to_string(desc.m_HalfPixelCenters)); } void StringifyLayerParameters::Serialize(ParameterStringifyFunction& fn, @@ -302,6 +304,8 @@ void StringifyLayerParameters::Serialize(ParameterStringifyFun fn("TargetHeight", std::to_string(desc.m_TargetHeight)); fn("ResizeMethod", GetResizeMethodAsCString(desc.m_Method)); fn("DataLayout", GetDataLayoutName(desc.m_DataLayout)); + fn("AlignCorners", std::to_string(desc.m_AlignCorners)); + fn("HalfPixelCenters", std::to_string(desc.m_HalfPixelCenters)); } void StringifyLayerParameters::Serialize(ParameterStringifyFunction& fn, diff --git a/src/armnn/layers/ResizeLayer.cpp b/src/armnn/layers/ResizeLayer.cpp index 9654e58..b16adeb 100644 --- a/src/armnn/layers/ResizeLayer.cpp +++ b/src/armnn/layers/ResizeLayer.cpp @@ -50,6 +50,11 @@ std::vector ResizeLayer::InferOutputShapes(const std::vector({ tensorShape }); } diff --git a/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.cpp index f12f53c..72507d3 100644 --- a/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.cpp +++ b/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.cpp @@ -29,7 +29,9 @@ struct ResizeTestParams , m_InQuantScale(1.0f) , m_InQuantOffset(0) , m_OutQuantScale(1.0f) - , m_OutQuantOffset(0) {} + , m_OutQuantOffset(0) + , m_AlignCorners(false) + , m_HalfPixelCenters(false) {} armnn::ResizeMethod m_ResizeMethod; armnn::DataLayout m_DataLayout; @@ -46,6 +48,9 @@ struct ResizeTestParams float m_OutQuantScale; int32_t m_OutQuantOffset; + bool m_AlignCorners; + bool m_HalfPixelCenters; + void SetInQuantParams(float quantScale, int32_t quantOffset) { m_InQuantScale = quantScale; @@ -111,6 +116,8 @@ LayerTestResult ResizeTestImpl( armnn::ResizeQueueDescriptor descriptor; descriptor.m_Parameters.m_Method = params.m_ResizeMethod; descriptor.m_Parameters.m_DataLayout = params.m_DataLayout; + descriptor.m_Parameters.m_AlignCorners = params.m_AlignCorners; + descriptor.m_Parameters.m_HalfPixelCenters = params.m_HalfPixelCenters; armnnUtils::DataLayoutIndexed dataLayoutIndexed(params.m_DataLayout); descriptor.m_Parameters.m_TargetWidth = params.m_OutputShape[dataLayoutIndexed.GetWidthIndex()]; @@ -528,6 +535,129 @@ LayerTestResult ResizeNearestNeighborMagTest( return ResizeTestImpl<4, ArmnnType>(workloadFactory, memoryManager, testParams); } +template +LayerTestResult HalfPixelCentersResizeBilinearTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout) +{ + ResizeTestParams testParams; + testParams.m_ResizeMethod = armnn::ResizeMethod::Bilinear; + testParams.m_DataLayout = dataLayout; + testParams.m_HalfPixelCenters = true; + + testParams.m_InputShape = { 2, 1, 2, 2 }; + testParams.m_OutputShape = { 2, 1, 3, 3 }; + + testParams.m_InputData = + { + 1.0f, 2.0f, + 3.0f, 4.0f, + + 1.0f, 2.0f, + 3.0f, 4.0f + }; + + testParams.m_ExpectedOutputData = + { + 1.0f, 1.5f, 2.0f, + 2.0f, 2.5f, 3.0f, + 3.0f, 3.5f, 4.0f, + + 1.0f, 1.5f, 2.0f, + 2.0f, 2.5f, 3.0f, + 3.0f, 3.5f, 4.0f, + }; + + return ResizeTestImpl<4, ArmnnType>(workloadFactory, memoryManager, testParams); +} + +template +LayerTestResult AlignCornersResizeBilinearTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout) +{ + ResizeTestParams testParams; + testParams.m_ResizeMethod = armnn::ResizeMethod::Bilinear; + testParams.m_DataLayout = dataLayout; + testParams.m_AlignCorners = true; + + testParams.m_InputShape = { 1, 1, 2, 2 }; + testParams.m_OutputShape = { 1, 1, 1, 1 }; + + testParams.m_InputData = + { + 1.0f, 2.0f, + 3.0f, 4.0f, + }; + + testParams.m_ExpectedOutputData = + { + 1.0f + }; + + return ResizeTestImpl<4, ArmnnType>(workloadFactory, memoryManager, testParams); +} + +template +LayerTestResult HalfPixelCentersResizeNearestNeighbourTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout) +{ + ResizeTestParams testParams; + testParams.m_ResizeMethod = armnn::ResizeMethod::NearestNeighbor; + testParams.m_DataLayout = dataLayout; + testParams.m_HalfPixelCenters = true; + + testParams.m_InputShape = { 1, 1, 2, 5 }; + testParams.m_OutputShape = { 1, 1, 2, 2 }; + + testParams.m_InputData = + { + 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, + + 1.0f, 2.0f, 3.0f, 4.0f, 5.0f + }; + + testParams.m_ExpectedOutputData = + { + 2.0f, 4.0f, + 2.0f, 4.0f + }; + + return ResizeTestImpl<4, ArmnnType>(workloadFactory, memoryManager, testParams); +} + +template +LayerTestResult AlignCornersResizeNearestNeighbourTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout) +{ + ResizeTestParams testParams; + testParams.m_ResizeMethod = armnn::ResizeMethod::NearestNeighbor; + testParams.m_DataLayout = dataLayout; + testParams.m_AlignCorners = true; + + testParams.m_InputShape = { 1, 1, 2, 2 }; + testParams.m_OutputShape = { 1, 1, 1, 1 }; + + testParams.m_InputData = + { + 1.0f, 2.0f, + 3.0f, 4.0f, + }; + + testParams.m_ExpectedOutputData = + { + 1.0f + }; + + return ResizeTestImpl<4, ArmnnType>(workloadFactory, memoryManager, testParams); +} + // // Explicit template instantiations // @@ -597,6 +727,30 @@ ResizeNearestNeighborMagTest( float outQuantScale, int32_t outQuantOffset); +template LayerTestResult, 4> +HalfPixelCentersResizeBilinearTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + +template LayerTestResult, 4> +AlignCornersResizeBilinearTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + +template LayerTestResult, 4> +HalfPixelCentersResizeNearestNeighbourTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + +template LayerTestResult, 4> +AlignCornersResizeNearestNeighbourTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + // Float16 template LayerTestResult, 4> ResizeBilinearNopTest( diff --git a/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.hpp b/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.hpp index 538a64c..b70ae85 100644 --- a/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.hpp +++ b/src/backends/backendsCommon/test/layerTests/ResizeTestImpl.hpp @@ -79,3 +79,27 @@ LayerTestResult ResizeNearestNeighborMagTest( int32_t inQuantOffset, float outQuantScale, int32_t outQuantOffset); + +template> +LayerTestResult HalfPixelCentersResizeBilinearTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + +template> +LayerTestResult AlignCornersResizeBilinearTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + +template> +LayerTestResult HalfPixelCentersResizeNearestNeighbourTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); + +template> +LayerTestResult AlignCornersResizeNearestNeighbourTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::DataLayout dataLayout); \ No newline at end of file diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp index 93bfb90..4d347ca 100644 --- a/src/backends/reference/test/RefLayerTests.cpp +++ b/src/backends/reference/test/RefLayerTests.cpp @@ -873,6 +873,12 @@ ARMNN_AUTO_TEST_CASE(ResizeBilinearMagUint8, ARMNN_AUTO_TEST_CASE(ResizeBilinearMagUint16, SimpleResizeBilinearTest, DataLayout::NCHW) +ARMNN_AUTO_TEST_CASE(HalfPixelCentersResizeBilinear, + HalfPixelCentersResizeBilinearTest, + DataLayout::NCHW) +ARMNN_AUTO_TEST_CASE(AlignCornersResizeBilinear, + AlignCornersResizeBilinearTest, + DataLayout::NCHW) // Resize Bilinear - NHWC ARMNN_AUTO_TEST_CASE(ResizeBilinearNopNhwc, @@ -1012,6 +1018,12 @@ ARMNN_AUTO_TEST_CASE(ResizeNearestNeighborMagUint8, ARMNN_AUTO_TEST_CASE(ResizeNearestNeighborMagUint16, SimpleResizeNearestNeighborTest, DataLayout::NCHW) +ARMNN_AUTO_TEST_CASE(HalfPixelCentersResizeNearestNeighbour, + HalfPixelCentersResizeNearestNeighbourTest, + DataLayout::NCHW) +ARMNN_AUTO_TEST_CASE(AlignCornersResizeNearestNeighbour, + AlignCornersResizeNearestNeighbourTest, + DataLayout::NCHW) // Resize NearestNeighbor - NHWC ARMNN_AUTO_TEST_CASE(ResizeNearestNeighborNopNhwc, diff --git a/src/backends/reference/workloads/RefResizeWorkload.cpp b/src/backends/reference/workloads/RefResizeWorkload.cpp index 49e4f36..21ff852 100644 --- a/src/backends/reference/workloads/RefResizeWorkload.cpp +++ b/src/backends/reference/workloads/RefResizeWorkload.cpp @@ -35,7 +35,8 @@ void RefResizeWorkload::Execute() const outputInfo, m_Data.m_Parameters.m_DataLayout, m_Data.m_Parameters.m_Method, - m_Data.m_Parameters.m_AlignCorners); + m_Data.m_Parameters.m_AlignCorners, + m_Data.m_Parameters.m_HalfPixelCenters); } } //namespace armnn diff --git a/src/backends/reference/workloads/Resize.cpp b/src/backends/reference/workloads/Resize.cpp index 407774e..16cdd4a 100644 --- a/src/backends/reference/workloads/Resize.cpp +++ b/src/backends/reference/workloads/Resize.cpp @@ -30,6 +30,36 @@ inline double EuclideanDistance(float Xa, float Ya, const unsigned int Xb, const return std::sqrt(pow(Xa - boost::numeric_cast(Xb), 2) + pow(Ya - boost::numeric_cast(Yb), 2)); } +inline float CalculateResizeScale(const unsigned int& InputSize, + const unsigned int& OutputSize, + const bool& AlignCorners) +{ + return (AlignCorners && OutputSize > 1) + ? boost::numeric_cast(InputSize - 1) / boost::numeric_cast(OutputSize - 1) + : boost::numeric_cast(InputSize) / boost::numeric_cast(OutputSize); +} + +inline float PixelScaler(const unsigned int& Pixel, + const float& Scale, + const bool& HalfPixelCenters, + armnn::ResizeMethod& resizeMethod) +{ + // For Half Pixel Centers the Top Left texel is assumed to be at 0.5,0.5 + if (HalfPixelCenters && resizeMethod == armnn::ResizeMethod::Bilinear) + { + return (static_cast(Pixel) + 0.5f) * Scale - 0.5f; + } + // Nearest Neighbour doesn't need to have 0.5f trimmed off as it will floor the values later + else if (HalfPixelCenters && resizeMethod == armnn::ResizeMethod::NearestNeighbor) + { + return (static_cast(Pixel) + 0.5f) * Scale; + } + else + { + return static_cast(Pixel) * Scale; + } +} + }// anonymous namespace void Resize(Decoder& in, @@ -38,8 +68,12 @@ void Resize(Decoder& in, const TensorInfo& outputInfo, DataLayoutIndexed dataLayout, armnn::ResizeMethod resizeMethod, - bool alignCorners) + bool alignCorners, + bool halfPixelCenters) { + // alignCorners and halfPixelCenters cannot both be true + ARMNN_ASSERT(!(alignCorners && halfPixelCenters)); + // 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 // will yield different results than if projecting the centre of output texels. @@ -52,14 +86,10 @@ void Resize(Decoder& in, 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(inputHeight - sizeOffset) - / boost::numeric_cast(outputHeight - sizeOffset); - const float scaleX = boost::numeric_cast(inputWidth - sizeOffset) - / boost::numeric_cast(outputWidth - sizeOffset); + const float scaleY = CalculateResizeScale(inputHeight, outputHeight, alignCorners); + const float scaleX = CalculateResizeScale(inputWidth, outputWidth, alignCorners); TensorShape inputShape = inputInfo.GetShape(); TensorShape outputShape = outputInfo.GetShape(); @@ -71,11 +101,13 @@ void Resize(Decoder& in, for (unsigned int y = 0; y < outputHeight; ++y) { // Corresponding real-valued height coordinate in input image. - const float iy = boost::numeric_cast(y) * scaleY; + float iy = PixelScaler(y, scaleY, halfPixelCenters, resizeMethod); // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation). - const float fiy = floorf(iy); - const unsigned int y0 = boost::numeric_cast(fiy); + const float fiy = (resizeMethod == armnn::ResizeMethod::NearestNeighbor && alignCorners) ? + roundf(iy) : floorf(iy); + // Pixel scaling a value with Half Pixel Centers can be negative, if so set to 0 + const unsigned int y0 = static_cast(std::max(fiy, 0.0f)); // Interpolation weight (range [0,1]). const float yw = iy - fiy; @@ -83,16 +115,31 @@ void Resize(Decoder& in, for (unsigned int x = 0; x < outputWidth; ++x) { // Real-valued and discrete width coordinates in input image. - const float ix = boost::numeric_cast(x) * scaleX; - const float fix = floorf(ix); - const unsigned int x0 = boost::numeric_cast(fix); + float ix = PixelScaler(x, scaleX, halfPixelCenters, resizeMethod); + + // Nearest Neighbour uses rounding to align to corners + const float fix = resizeMethod == armnn::ResizeMethod::NearestNeighbor && alignCorners ? + roundf(ix) : floorf(ix); + // Pixel scaling a value with Half Pixel Centers can be negative, if so set to 0 + const unsigned int x0 = static_cast(std::max(fix, 0.0f)); // Interpolation weight (range [0,1]). const float xw = ix - fix; + unsigned int x1; + unsigned int y1; + // Half Pixel Centers uses the scaling to compute a weighted parameter for nearby pixels + if (halfPixelCenters) + { + x1 = std::min(static_cast(std::ceil(ix)), inputWidth - 1u); + y1 = std::min(static_cast(std::ceil(iy)), inputHeight - 1u); + } // Discrete width/height coordinates of texels below and to the right of (x0, y0). - const unsigned int x1 = std::min(x0 + 1, inputWidth - 1u); - const unsigned int y1 = std::min(y0 + 1, inputHeight - 1u); + else + { + x1 = std::min(x0 + 1, inputWidth - 1u); + y1 = std::min(y0 + 1, inputHeight - 1u); + } float interpolatedValue; switch (resizeMethod) diff --git a/src/backends/reference/workloads/Resize.hpp b/src/backends/reference/workloads/Resize.hpp index 58ec7df..99362af 100644 --- a/src/backends/reference/workloads/Resize.hpp +++ b/src/backends/reference/workloads/Resize.hpp @@ -20,6 +20,7 @@ void Resize(Decoder& in, const TensorInfo& outputInfo, armnnUtils::DataLayoutIndexed dataLayout = DataLayout::NCHW, ResizeMethod resizeMethod = ResizeMethod::NearestNeighbor, - bool alignConers = false); + bool alignCorners = false, + bool halfPixelCenters = false); } // namespace armnn