ArmNN
 24.05
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
armnn::Decoder< float >
Resize.hpp
armnn::Encoder::Set
virtual void Set(IType right)=0
armnn::ResizeMethod
ResizeMethod
Definition: Types.hpp:166
TensorBufferArrayView.hpp
armnn::TensorInfo
Definition: Tensor.hpp:152
armnnUtils::DataLayoutIndexed
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout.
Definition: DataLayoutIndexed.hpp:17
armnn::minimum
Definition: Minimum.hpp:12
armnn::numeric_cast
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
NumericCast.hpp
armnnUtils::DataLayoutIndexed::GetHeightIndex
unsigned int GetHeightIndex() const
Definition: DataLayoutIndexed.hpp:24
armnn::TensorShape
Definition: Tensor.hpp:20
armnn::Encoder< float >
Utils.hpp
armnnUtils
Definition: CompatibleTypes.hpp:10
armnn::InvalidArgumentException
Definition: Exceptions.hpp:80
armnnUtils::DataLayoutIndexed::GetWidthIndex
unsigned int GetWidthIndex() const
Definition: DataLayoutIndexed.hpp:25
armnn::ResizeMethod::NearestNeighbor
@ NearestNeighbor
armnn::Decoder::Get
virtual IType Get() const =0
armnn::ResizeMethod::Bilinear
@ Bilinear
armnn::roundf
float roundf(float value)
Definition: Utils.hpp:43
armnn::Resize
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
armnn::TensorInfo::GetShape
const TensorShape & GetShape() const
Definition: Tensor.hpp:193
armnn
Copyright (c) 2021 ARM Limited and Contributors.
Definition: 01_00_quick_start.dox:6
armnnUtils::DataLayoutIndexed::GetChannelsIndex
unsigned int GetChannelsIndex() const
Definition: DataLayoutIndexed.hpp:23
armnnUtils::DataLayoutIndexed::GetIndex
unsigned int GetIndex(const armnn::TensorShape &shape, unsigned int batchIndex, unsigned int channelIndex, unsigned int heightIndex, unsigned int widthIndex) const
Definition: DataLayoutIndexed.hpp:28
ARMNN_THROW_INVALIDARG_MSG_IF_FALSE
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
Definition: Exceptions.hpp:210