ArmNN
 25.02
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Resize.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017, 2024 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "Resize.hpp"
7 
9 
11 #include <armnn/Utils.hpp>
12 
13 #include <cmath>
14 #include <algorithm>
15 
16 using namespace armnnUtils;
17 
18 namespace
19 {
20 
21 inline float Lerp(float a, float b, float w)
22 {
23  return w * b + (1.f - w) * a;
24 }
25 
26 inline double EuclideanDistance(float Xa, float Ya, const unsigned int Xb, const unsigned int Yb)
27 {
28  return std::sqrt(pow(Xa - armnn::numeric_cast<float>(Xb), 2) + pow(Ya - armnn::numeric_cast<float>(Yb), 2));
29 }
30 
31 inline float CalculateResizeScale(const unsigned int& InputSize,
32  const unsigned int& OutputSize,
33  const bool& AlignCorners)
34 {
35  return (AlignCorners && OutputSize > 1)
36  ? armnn::numeric_cast<float>(InputSize - 1) / armnn::numeric_cast<float>(OutputSize - 1)
37  : armnn::numeric_cast<float>(InputSize) / armnn::numeric_cast<float>(OutputSize);
38 }
39 
40 inline 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 
61 }// anonymous namespace
62 
63 namespace armnn
64 {
66  const TensorInfo& inputInfo,
67  Encoder<float>& out,
68  const TensorInfo& outputInfo,
69  DataLayoutIndexed dataLayout,
70  ResizeMethod resizeMethod,
71  bool alignCorners,
72  bool halfPixelCenters)
73 {
74  // alignCorners and halfPixelCenters cannot both be true
75  ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(!(alignCorners && halfPixelCenters),
76  "Resize: alignCorners and halfPixelCenters cannot both be true");
77 
78  // 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.
92  const float scaleY = CalculateResizeScale(inputHeight, outputHeight, alignCorners);
93  const float scaleX = CalculateResizeScale(inputWidth, outputWidth, alignCorners);
94 
95  const TensorShape& inputShape = inputInfo.GetShape();
96  const TensorShape& outputShape = outputInfo.GetShape();
97 
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.
105  float iy = PixelScaler(y, scaleY, halfPixelCenters, resizeMethod);
106 
107  // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
108  const float fiy = (resizeMethod == ResizeMethod::NearestNeighbor && alignCorners) ? armnn::roundf(iy)
109  : floorf(iy);
110  // 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));
112 
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.
119  float ix = PixelScaler(x, scaleX, halfPixelCenters, resizeMethod);
120 
121  // Nearest Neighbour uses rounding to align to corners
122  const float fix = resizeMethod == ResizeMethod::NearestNeighbor && alignCorners ? armnn::roundf(ix)
123  : floorf(ix);
124  // 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));
126 
127  // Interpolation weight (range [0,1]).
128  const float xw = ix - fix;
129 
130  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  }
138  // Discrete width/height coordinates of texels below and to the right of (x0, y0).
139  else
140  {
141  x1 = std::min(x0 + 1, inputWidth - 1u);
142  y1 = std::min(y0 + 1, inputHeight - 1u);
143  }
144 
145  float interpolatedValue;
146  switch (resizeMethod)
147  {
148  case ResizeMethod::Bilinear:
149  {
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  }
164  case ResizeMethod::NearestNeighbor:
165  {
166  // 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);
171 
172  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  {
199  throw InvalidArgumentException("Resize Nearest Neighbor failure");
200  }
201 
202  in[dataLayout.GetIndex(inputShape, n, c, yNearest, xNearest)];
203  interpolatedValue = in.Get();
204  break;
205  }
206  default:
207  throw InvalidArgumentException("Unknown resize method: " +
208  std::to_string(static_cast<int>(resizeMethod)));
209  }
210  out[dataLayout.GetIndex(outputShape, n, c, y, x)];
211  out.Set(interpolatedValue);
212  }
213  }
214  }
215  }
216 }
217 
218 } //namespace armnn
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
Definition: Exceptions.hpp:210
virtual IType Get() const =0
virtual void Set(IType right)=0
const TensorShape & GetShape() const
Definition: Tensor.hpp:193
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout.
unsigned int GetIndex(const armnn::TensorShape &shape, unsigned int batchIndex, unsigned int channelIndex, unsigned int heightIndex, unsigned int widthIndex) const
unsigned int GetWidthIndex() const
unsigned int GetHeightIndex() const
unsigned int GetChannelsIndex() const
Copyright (c) 2021 ARM Limited and Contributors.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
float roundf(float value)
Definition: Utils.hpp:43
ResizeMethod
Definition: Types.hpp:168
void Resize(Decoder< float > &in, const TensorInfo &inputInfo, Encoder< float > &out, const TensorInfo &outputInfo, DataLayoutIndexed dataLayout, ResizeMethod resizeMethod, bool alignCorners, bool halfPixelCenters)
Definition: Resize.cpp:65