ArmNN
 25.11
Loading...
Searching...
No Matches
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
16using namespace armnnUtils;
17
18namespace
19{
20
21inline float Lerp(float a, float b, float w)
22{
23 return w * b + (1.f - w) * a;
24}
25
26inline 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
31inline 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
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
61}// anonymous namespace
62
63namespace 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 {
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 }
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)
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 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)
ResizeMethod
Definition Types.hpp:168
float roundf(float value)
Definition Utils.hpp:43