blob: e80a2057e0f1d8b2dd6113b1b4b350f4869748ce [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Teresa Charlin970f43b2019-07-01 13:51:07 +01002// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "Resize.hpp"
7
8#include "TensorBufferArrayView.hpp"
9
Matthew Sloyan24ac8592020-09-23 16:57:23 +010010#include <armnn/utility/NumericCast.hpp>
Teresa Charlin28658bc2023-12-19 15:49:31 +000011#include <armnn/Utils.hpp>
Teresa Charlin970f43b2019-07-01 13:51:07 +010012
13#include <cmath>
14#include <algorithm>
15
16using namespace armnnUtils;
17
Teresa Charlin970f43b2019-07-01 13:51:07 +010018namespace
19{
20
21inline float Lerp(float a, float b, float w)
22{
23 return w * b + (1.f - w) * a;
24}
25
Teresa Charlinda1fb9b2019-07-02 13:25:22 +010026inline double EuclideanDistance(float Xa, float Ya, const unsigned int Xb, const unsigned int Yb)
27{
Matthew Sloyan24ac8592020-09-23 16:57:23 +010028 return std::sqrt(pow(Xa - armnn::numeric_cast<float>(Xb), 2) + pow(Ya - armnn::numeric_cast<float>(Yb), 2));
Teresa Charlinda1fb9b2019-07-02 13:25:22 +010029}
30
David Monahanab219752020-06-19 16:43:48 +010031inline float CalculateResizeScale(const unsigned int& InputSize,
32 const unsigned int& OutputSize,
33 const bool& AlignCorners)
34{
35 return (AlignCorners && OutputSize > 1)
Matthew Sloyan24ac8592020-09-23 16:57:23 +010036 ? armnn::numeric_cast<float>(InputSize - 1) / armnn::numeric_cast<float>(OutputSize - 1)
37 : armnn::numeric_cast<float>(InputSize) / armnn::numeric_cast<float>(OutputSize);
David Monahanab219752020-06-19 16:43:48 +010038}
39
40inline float PixelScaler(const unsigned int& Pixel,
41 const float& Scale,
42 const bool& HalfPixelCenters,
43 armnn::ResizeMethod& resizeMethod)
44{
45 // For Half Pixel Centers the Top Left texel is assumed to be at 0.5,0.5
46 if (HalfPixelCenters && resizeMethod == armnn::ResizeMethod::Bilinear)
47 {
48 return (static_cast<float>(Pixel) + 0.5f) * Scale - 0.5f;
49 }
50 // Nearest Neighbour doesn't need to have 0.5f trimmed off as it will floor the values later
51 else if (HalfPixelCenters && resizeMethod == armnn::ResizeMethod::NearestNeighbor)
52 {
53 return (static_cast<float>(Pixel) + 0.5f) * Scale;
54 }
55 else
56 {
57 return static_cast<float>(Pixel) * Scale;
58 }
59}
60
Teresa Charlin970f43b2019-07-01 13:51:07 +010061}// anonymous namespace
62
Teresa Charlin28658bc2023-12-19 15:49:31 +000063namespace armnn
64{
Teresa Charlin970f43b2019-07-01 13:51:07 +010065void Resize(Decoder<float>& in,
66 const TensorInfo& inputInfo,
67 Encoder<float>& out,
68 const TensorInfo& outputInfo,
69 DataLayoutIndexed dataLayout,
Teresa Charlin28658bc2023-12-19 15:49:31 +000070 ResizeMethod resizeMethod,
David Monahanab219752020-06-19 16:43:48 +010071 bool alignCorners,
72 bool halfPixelCenters)
Teresa Charlin970f43b2019-07-01 13:51:07 +010073{
David Monahanab219752020-06-19 16:43:48 +010074 // alignCorners and halfPixelCenters cannot both be true
75 ARMNN_ASSERT(!(alignCorners && halfPixelCenters));
76
Teresa Charlin970f43b2019-07-01 13:51:07 +010077 // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
78 // image is projected into the input image to figure out the interpolants and weights. Note that this
79 // will yield different results than if projecting the centre of output texels.
80
81 const unsigned int batchSize = inputInfo.GetShape()[0];
82 const unsigned int channelCount = inputInfo.GetShape()[dataLayout.GetChannelsIndex()];
83
84 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
85 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
86 const unsigned int outputHeight = outputInfo.GetShape()[dataLayout.GetHeightIndex()];
87 const unsigned int outputWidth = outputInfo.GetShape()[dataLayout.GetWidthIndex()];
88
89 // How much to scale pixel coordinates in the output image, to get the corresponding pixel coordinates
90 // in the input image.
David Monahanab219752020-06-19 16:43:48 +010091 const float scaleY = CalculateResizeScale(inputHeight, outputHeight, alignCorners);
92 const float scaleX = CalculateResizeScale(inputWidth, outputWidth, alignCorners);
Teresa Charlin970f43b2019-07-01 13:51:07 +010093
Teresa Charlin28658bc2023-12-19 15:49:31 +000094 const TensorShape& inputShape = inputInfo.GetShape();
95 const TensorShape& outputShape = outputInfo.GetShape();
Teresa Charlin970f43b2019-07-01 13:51:07 +010096
97 for (unsigned int n = 0; n < batchSize; ++n)
98 {
99 for (unsigned int c = 0; c < channelCount; ++c)
100 {
101 for (unsigned int y = 0; y < outputHeight; ++y)
102 {
103 // Corresponding real-valued height coordinate in input image.
David Monahanab219752020-06-19 16:43:48 +0100104 float iy = PixelScaler(y, scaleY, halfPixelCenters, resizeMethod);
Teresa Charlin970f43b2019-07-01 13:51:07 +0100105
106 // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
Teresa Charlin28658bc2023-12-19 15:49:31 +0000107 const float fiy = (resizeMethod == ResizeMethod::NearestNeighbor && alignCorners) ? armnn::roundf(iy)
108 : floorf(iy);
David Monahanab219752020-06-19 16:43:48 +0100109 // Pixel scaling a value with Half Pixel Centers can be negative, if so set to 0
110 const unsigned int y0 = static_cast<unsigned int>(std::max(fiy, 0.0f));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100111
112 // Interpolation weight (range [0,1]).
113 const float yw = iy - fiy;
114
115 for (unsigned int x = 0; x < outputWidth; ++x)
116 {
117 // Real-valued and discrete width coordinates in input image.
David Monahanab219752020-06-19 16:43:48 +0100118 float ix = PixelScaler(x, scaleX, halfPixelCenters, resizeMethod);
119
120 // Nearest Neighbour uses rounding to align to corners
Teresa Charlin28658bc2023-12-19 15:49:31 +0000121 const float fix = resizeMethod == ResizeMethod::NearestNeighbor && alignCorners ? armnn::roundf(ix)
122 : floorf(ix);
David Monahanab219752020-06-19 16:43:48 +0100123 // Pixel scaling a value with Half Pixel Centers can be negative, if so set to 0
124 const unsigned int x0 = static_cast<unsigned int>(std::max(fix, 0.0f));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100125
126 // Interpolation weight (range [0,1]).
127 const float xw = ix - fix;
128
David Monahanab219752020-06-19 16:43:48 +0100129 unsigned int x1;
130 unsigned int y1;
131 // Half Pixel Centers uses the scaling to compute a weighted parameter for nearby pixels
132 if (halfPixelCenters)
133 {
134 x1 = std::min(static_cast<unsigned int>(std::ceil(ix)), inputWidth - 1u);
135 y1 = std::min(static_cast<unsigned int>(std::ceil(iy)), inputHeight - 1u);
136 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100137 // Discrete width/height coordinates of texels below and to the right of (x0, y0).
David Monahanab219752020-06-19 16:43:48 +0100138 else
139 {
140 x1 = std::min(x0 + 1, inputWidth - 1u);
141 y1 = std::min(y0 + 1, inputHeight - 1u);
142 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100143
144 float interpolatedValue;
145 switch (resizeMethod)
146 {
Teresa Charlin28658bc2023-12-19 15:49:31 +0000147 case ResizeMethod::Bilinear:
Teresa Charlin970f43b2019-07-01 13:51:07 +0100148 {
149 in[dataLayout.GetIndex(inputShape, n, c, y0, x0)];
150 float input1 = in.Get();
151 in[dataLayout.GetIndex(inputShape, n, c, y0, x1)];
152 float input2 = in.Get();
153 in[dataLayout.GetIndex(inputShape, n, c, y1, x0)];
154 float input3 = in.Get();
155 in[dataLayout.GetIndex(inputShape, n, c, y1, x1)];
156 float input4 = in.Get();
157
158 const float ly0 = Lerp(input1, input2, xw); // lerp along row y0.
159 const float ly1 = Lerp(input3, input4, xw); // lerp along row y1.
160 interpolatedValue = Lerp(ly0, ly1, yw);
161 break;
162 }
Teresa Charlin28658bc2023-12-19 15:49:31 +0000163 case ResizeMethod::NearestNeighbor:
Teresa Charlin970f43b2019-07-01 13:51:07 +0100164 {
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100165 // calculate euclidean distance to the 4 neighbours
166 auto distance00 = EuclideanDistance(fix, fiy, x0, y0);
167 auto distance01 = EuclideanDistance(fix, fiy, x0, y1);
168 auto distance10 = EuclideanDistance(fix, fiy, x1, y0);
169 auto distance11 = EuclideanDistance(fix, fiy, x1, y1);
Teresa Charlin970f43b2019-07-01 13:51:07 +0100170
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100171 auto minimum = std::min( { distance00, distance01, distance10, distance11 } );
172
173 unsigned int xNearest = 0;
174 unsigned int yNearest = 0;
175
176 if (minimum == distance00)
177 {
178 xNearest = x0;
179 yNearest = y0;
180 }
181 else if (minimum == distance01)
182 {
183 xNearest = x0;
184 yNearest = y1;
185 }
186 else if (minimum == distance10)
187 {
188 xNearest = x1;
189 yNearest = y0;
190 }
191 else if (minimum == distance11)
192 {
193 xNearest = x1;
194 yNearest = y1;
195 }
196 else
197 {
Teresa Charlin28658bc2023-12-19 15:49:31 +0000198 throw InvalidArgumentException("Resize Nearest Neighbor failure");
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100199 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100200
201 in[dataLayout.GetIndex(inputShape, n, c, yNearest, xNearest)];
202 interpolatedValue = in.Get();
203 break;
204 }
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100205 default:
Teresa Charlin28658bc2023-12-19 15:49:31 +0000206 throw InvalidArgumentException("Unknown resize method: " +
207 std::to_string(static_cast<int>(resizeMethod)));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100208 }
209 out[dataLayout.GetIndex(outputShape, n, c, y, x)];
210 out.Set(interpolatedValue);
211 }
212 }
213 }
214 }
215}
216
217} //namespace armnn