blob: 3050bae8707f4de5e0e0a07fa26ae42c067524df [file] [log] [blame]
Teresa Charlin970f43b2019-07-01 13:51:07 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "Resize.hpp"
7
8#include "TensorBufferArrayView.hpp"
9
10#include <boost/numeric/conversion/cast.hpp>
11
12#include <cmath>
13#include <algorithm>
14
15using namespace armnnUtils;
16
17namespace armnn
18{
19
20namespace
21{
22
23inline float Lerp(float a, float b, float w)
24{
25 return w * b + (1.f - w) * a;
26}
27
Teresa Charlinda1fb9b2019-07-02 13:25:22 +010028inline double EuclideanDistance(float Xa, float Ya, const unsigned int Xb, const unsigned int Yb)
29{
30 return std::sqrt(pow(Xa - boost::numeric_cast<float>(Xb), 2) + pow(Ya - boost::numeric_cast<float>(Yb), 2));
31}
32
Teresa Charlin970f43b2019-07-01 13:51:07 +010033}// anonymous namespace
34
35void Resize(Decoder<float>& in,
36 const TensorInfo& inputInfo,
37 Encoder<float>& out,
38 const TensorInfo& outputInfo,
39 DataLayoutIndexed dataLayout,
40 armnn::ResizeMethod resizeMethod)
41{
42 // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
43 // image is projected into the input image to figure out the interpolants and weights. Note that this
44 // will yield different results than if projecting the centre of output texels.
45
46 const unsigned int batchSize = inputInfo.GetShape()[0];
47 const unsigned int channelCount = inputInfo.GetShape()[dataLayout.GetChannelsIndex()];
48
49 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
50 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
51 const unsigned int outputHeight = outputInfo.GetShape()[dataLayout.GetHeightIndex()];
52 const unsigned int outputWidth = outputInfo.GetShape()[dataLayout.GetWidthIndex()];
53
54 // How much to scale pixel coordinates in the output image, to get the corresponding pixel coordinates
55 // in the input image.
56 const float scaleY = boost::numeric_cast<float>(inputHeight) / boost::numeric_cast<float>(outputHeight);
57 const float scaleX = boost::numeric_cast<float>(inputWidth) / boost::numeric_cast<float>(outputWidth);
58
59 TensorShape inputShape = inputInfo.GetShape();
60 TensorShape outputShape = outputInfo.GetShape();
61
62 for (unsigned int n = 0; n < batchSize; ++n)
63 {
64 for (unsigned int c = 0; c < channelCount; ++c)
65 {
66 for (unsigned int y = 0; y < outputHeight; ++y)
67 {
68 // Corresponding real-valued height coordinate in input image.
69 const float iy = boost::numeric_cast<float>(y) * scaleY;
70
71 // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
72 const float fiy = floorf(iy);
73 const unsigned int y0 = boost::numeric_cast<unsigned int>(fiy);
74
75 // Interpolation weight (range [0,1]).
76 const float yw = iy - fiy;
77
78 for (unsigned int x = 0; x < outputWidth; ++x)
79 {
80 // Real-valued and discrete width coordinates in input image.
81 const float ix = boost::numeric_cast<float>(x) * scaleX;
82 const float fix = floorf(ix);
83 const unsigned int x0 = boost::numeric_cast<unsigned int>(fix);
84
85 // Interpolation weight (range [0,1]).
86 const float xw = ix - fix;
87
88 // Discrete width/height coordinates of texels below and to the right of (x0, y0).
89 const unsigned int x1 = std::min(x0 + 1, inputWidth - 1u);
90 const unsigned int y1 = std::min(y0 + 1, inputHeight - 1u);
91
92 float interpolatedValue;
93 switch (resizeMethod)
94 {
95 case armnn::ResizeMethod::Bilinear:
96 {
97 in[dataLayout.GetIndex(inputShape, n, c, y0, x0)];
98 float input1 = in.Get();
99 in[dataLayout.GetIndex(inputShape, n, c, y0, x1)];
100 float input2 = in.Get();
101 in[dataLayout.GetIndex(inputShape, n, c, y1, x0)];
102 float input3 = in.Get();
103 in[dataLayout.GetIndex(inputShape, n, c, y1, x1)];
104 float input4 = in.Get();
105
106 const float ly0 = Lerp(input1, input2, xw); // lerp along row y0.
107 const float ly1 = Lerp(input3, input4, xw); // lerp along row y1.
108 interpolatedValue = Lerp(ly0, ly1, yw);
109 break;
110 }
111 case armnn::ResizeMethod::NearestNeighbor:
Teresa Charlin970f43b2019-07-01 13:51:07 +0100112 {
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100113 // calculate euclidean distance to the 4 neighbours
114 auto distance00 = EuclideanDistance(fix, fiy, x0, y0);
115 auto distance01 = EuclideanDistance(fix, fiy, x0, y1);
116 auto distance10 = EuclideanDistance(fix, fiy, x1, y0);
117 auto distance11 = EuclideanDistance(fix, fiy, x1, y1);
Teresa Charlin970f43b2019-07-01 13:51:07 +0100118
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100119 auto minimum = std::min( { distance00, distance01, distance10, distance11 } );
120
121 unsigned int xNearest = 0;
122 unsigned int yNearest = 0;
123
124 if (minimum == distance00)
125 {
126 xNearest = x0;
127 yNearest = y0;
128 }
129 else if (minimum == distance01)
130 {
131 xNearest = x0;
132 yNearest = y1;
133 }
134 else if (minimum == distance10)
135 {
136 xNearest = x1;
137 yNearest = y0;
138 }
139 else if (minimum == distance11)
140 {
141 xNearest = x1;
142 yNearest = y1;
143 }
144 else
145 {
146 throw armnn::InvalidArgumentException("Resize Nearest Neighbor failure");
147 }
Teresa Charlin970f43b2019-07-01 13:51:07 +0100148
149 in[dataLayout.GetIndex(inputShape, n, c, yNearest, xNearest)];
150 interpolatedValue = in.Get();
151 break;
152 }
Teresa Charlinda1fb9b2019-07-02 13:25:22 +0100153 default:
154 throw armnn::InvalidArgumentException("Unknown resize method: " +
155 std::to_string(static_cast<int>(resizeMethod)));
Teresa Charlin970f43b2019-07-01 13:51:07 +0100156 }
157 out[dataLayout.GetIndex(outputShape, n, c, y, x)];
158 out.Set(interpolatedValue);
159 }
160 }
161 }
162 }
163}
164
165} //namespace armnn