blob: 7bed6c605627658fcc678bdccde09bfd02c3b3cf [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Colm Donelanb4ef1632024-02-01 15:00:43 +00002// Copyright © 2017, 2024 Arm Ltd. All rights reserved.
Teresa Charlin970f43b2019-07-01 13:51:07 +01003// 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
Colm Donelanb4ef1632024-02-01 15:00:43 +000075 ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(!(alignCorners && halfPixelCenters),
76 "Resize: alignCorners and halfPixelCenters cannot both be true");
David Monahanab219752020-06-19 16:43:48 +010077
Teresa Charlin970f43b2019-07-01 13:51:07 +010078 // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
79 // image is projected into the input image to figure out the interpolants and weights. Note that this
80 // will yield different results than if projecting the centre of output texels.
81
82 const unsigned int batchSize = inputInfo.GetShape()[0];
83 const unsigned int channelCount = inputInfo.GetShape()[dataLayout.GetChannelsIndex()];
84
85 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
86 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
87 const unsigned int outputHeight = outputInfo.GetShape()[dataLayout.GetHeightIndex()];
88 const unsigned int outputWidth = outputInfo.GetShape()[dataLayout.GetWidthIndex()];
89
90 // How much to scale pixel coordinates in the output image, to get the corresponding pixel coordinates
91 // in the input image.
David Monahanab219752020-06-19 16:43:48 +010092 const float scaleY = CalculateResizeScale(inputHeight, outputHeight, alignCorners);
93 const float scaleX = CalculateResizeScale(inputWidth, outputWidth, alignCorners);
Teresa Charlin970f43b2019-07-01 13:51:07 +010094
Teresa Charlin28658bc2023-12-19 15:49:31 +000095 const TensorShape& inputShape = inputInfo.GetShape();
96 const TensorShape& outputShape = outputInfo.GetShape();
Teresa Charlin970f43b2019-07-01 13:51:07 +010097
98 for (unsigned int n = 0; n < batchSize; ++n)
99 {
100 for (unsigned int c = 0; c < channelCount; ++c)
101 {
102 for (unsigned int y = 0; y < outputHeight; ++y)
103 {
104 // Corresponding real-valued height coordinate in input image.
David Monahanab219752020-06-19 16:43:48 +0100105 float iy = PixelScaler(y, scaleY, halfPixelCenters, resizeMethod);
Teresa Charlin970f43b2019-07-01 13:51:07 +0100106
107 // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
Teresa Charlin28658bc2023-12-19 15:49:31 +0000108 const float fiy = (resizeMethod == ResizeMethod::NearestNeighbor && alignCorners) ? armnn::roundf(iy)
109 : floorf(iy);
David Monahanab219752020-06-19 16:43:48 +0100110 // Pixel scaling a value with Half Pixel Centers can be negative, if so set to 0
111 const unsigned int y0 = static_cast<unsigned int>(std::max(fiy, 0.0f));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100112
113 // Interpolation weight (range [0,1]).
114 const float yw = iy - fiy;
115
116 for (unsigned int x = 0; x < outputWidth; ++x)
117 {
118 // Real-valued and discrete width coordinates in input image.
David Monahanab219752020-06-19 16:43:48 +0100119 float ix = PixelScaler(x, scaleX, halfPixelCenters, resizeMethod);
120
121 // Nearest Neighbour uses rounding to align to corners
Teresa Charlin28658bc2023-12-19 15:49:31 +0000122 const float fix = resizeMethod == ResizeMethod::NearestNeighbor && alignCorners ? armnn::roundf(ix)
123 : floorf(ix);
David Monahanab219752020-06-19 16:43:48 +0100124 // Pixel scaling a value with Half Pixel Centers can be negative, if so set to 0
125 const unsigned int x0 = static_cast<unsigned int>(std::max(fix, 0.0f));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100126
127 // Interpolation weight (range [0,1]).
128 const float xw = ix - fix;
129
David Monahanab219752020-06-19 16:43:48 +0100130 unsigned int x1;
131 unsigned int y1;
132 // Half Pixel Centers uses the scaling to compute a weighted parameter for nearby pixels
133 if (halfPixelCenters)
134 {
135 x1 = std::min(static_cast<unsigned int>(std::ceil(ix)), inputWidth - 1u);
136 y1 = std::min(static_cast<unsigned int>(std::ceil(iy)), inputHeight - 1u);
137 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100138 // Discrete width/height coordinates of texels below and to the right of (x0, y0).
David Monahanab219752020-06-19 16:43:48 +0100139 else
140 {
141 x1 = std::min(x0 + 1, inputWidth - 1u);
142 y1 = std::min(y0 + 1, inputHeight - 1u);
143 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100144
145 float interpolatedValue;
146 switch (resizeMethod)
147 {
Teresa Charlin28658bc2023-12-19 15:49:31 +0000148 case ResizeMethod::Bilinear:
Teresa Charlin970f43b2019-07-01 13:51:07 +0100149 {
150 in[dataLayout.GetIndex(inputShape, n, c, y0, x0)];
151 float input1 = in.Get();
152 in[dataLayout.GetIndex(inputShape, n, c, y0, x1)];
153 float input2 = in.Get();
154 in[dataLayout.GetIndex(inputShape, n, c, y1, x0)];
155 float input3 = in.Get();
156 in[dataLayout.GetIndex(inputShape, n, c, y1, x1)];
157 float input4 = in.Get();
158
159 const float ly0 = Lerp(input1, input2, xw); // lerp along row y0.
160 const float ly1 = Lerp(input3, input4, xw); // lerp along row y1.
161 interpolatedValue = Lerp(ly0, ly1, yw);
162 break;
163 }
Teresa Charlin28658bc2023-12-19 15:49:31 +0000164 case ResizeMethod::NearestNeighbor:
Teresa Charlin970f43b2019-07-01 13:51:07 +0100165 {
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100166 // calculate euclidean distance to the 4 neighbours
167 auto distance00 = EuclideanDistance(fix, fiy, x0, y0);
168 auto distance01 = EuclideanDistance(fix, fiy, x0, y1);
169 auto distance10 = EuclideanDistance(fix, fiy, x1, y0);
170 auto distance11 = EuclideanDistance(fix, fiy, x1, y1);
Teresa Charlin970f43b2019-07-01 13:51:07 +0100171
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100172 auto minimum = std::min( { distance00, distance01, distance10, distance11 } );
173
174 unsigned int xNearest = 0;
175 unsigned int yNearest = 0;
176
177 if (minimum == distance00)
178 {
179 xNearest = x0;
180 yNearest = y0;
181 }
182 else if (minimum == distance01)
183 {
184 xNearest = x0;
185 yNearest = y1;
186 }
187 else if (minimum == distance10)
188 {
189 xNearest = x1;
190 yNearest = y0;
191 }
192 else if (minimum == distance11)
193 {
194 xNearest = x1;
195 yNearest = y1;
196 }
197 else
198 {
Teresa Charlin28658bc2023-12-19 15:49:31 +0000199 throw InvalidArgumentException("Resize Nearest Neighbor failure");
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100200 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100201
202 in[dataLayout.GetIndex(inputShape, n, c, yNearest, xNearest)];
203 interpolatedValue = in.Get();
204 break;
205 }
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100206 default:
Teresa Charlin28658bc2023-12-19 15:49:31 +0000207 throw InvalidArgumentException("Unknown resize method: " +
208 std::to_string(static_cast<int>(resizeMethod)));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100209 }
210 out[dataLayout.GetIndex(outputShape, n, c, y, x)];
211 out.Set(interpolatedValue);
212 }
213 }
214 }
215 }
216}
217
218} //namespace armnn