ONE - On-device Neural Engine
Loading...
Searching...
No Matches
ResizeBilinear.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#ifndef __NNFW_CKER_RESIZEBILINEAR_H__
19#define __NNFW_CKER_RESIZEBILINEAR_H__
20
21#include "cker/Shape.h"
22#include "cker/Types.h"
23#include <cmath>
24
25namespace nnfw
26{
27namespace cker
28{
29
30inline void ResizeBilinearKernel2x2(int32_t x0, int32_t x1, int32_t y0, int32_t y1, int32_t x,
31 int32_t y, int32_t depth, int32_t batch,
32 const Shape &input_shape, const float *input_data,
33 const Shape &output_shape, float *output_data)
34{
35 const int32_t input_width = input_shape.Dims(2);
36 const int32_t output_width = output_shape.Dims(2);
37
38 const int32_t input_x_offset = (x1 - x0) * depth;
39 const int32_t input_y_offset = (y1 - y0) * depth * input_width;
40 const int32_t output_x_offset = depth;
41 const int32_t output_y_offset = depth * output_width;
42
43 for (int ch = 0; ch < depth; ch++)
44 {
45 const int32_t input_offset = Offset(input_shape, batch, y0, x0, ch);
46
47 float x0y0 = input_data[input_offset];
48 float x1y0 = input_data[input_offset + input_x_offset];
49 float x0y1 = input_data[input_offset + input_y_offset];
50 float x1y1 = input_data[input_offset + input_x_offset + input_y_offset];
51
52 // Top left corner.
53 const int32_t output_offset = Offset(output_shape, batch, y, x, ch);
54 output_data[output_offset] = x0y0;
55
56 // Top right corner.
57 output_data[output_offset + output_x_offset] = (x0y0 + x1y0) / 2;
58
59 // Bottom left corner.
60 float output = (x0y0 + x0y1) / 2;
61 output_data[output_offset + output_y_offset] = output;
62
63 // Bottom right corner.
64 output_data[output_offset + output_x_offset + output_y_offset] =
65 (output + ((x1y0 + x1y1) / 2)) / 2;
66 }
67}
68
69inline void ResizeBilinear2x2(int32_t batches, int32_t input_height, int32_t input_width,
70 int32_t depth, int32_t output_height, int32_t output_width,
71 const Shape &input_shape, const float *input_data,
72 const Shape &output_shape, float *output_data)
73{
74 for (int b = 0; b < batches; b++)
75 {
76 for (int y0 = 0, y = 0; y <= output_height - 2; y += 2, y0++)
77 {
78 for (int x0 = 0, x = 0; x <= output_width - 2; x += 2, x0++)
79 {
80 int32_t x1 = std::min(x0 + 1, input_width - 1);
81 int32_t y1 = std::min(y0 + 1, input_height - 1);
82 ResizeBilinearKernel2x2(x0, x1, y0, y1, x, y, depth, b, input_shape, input_data,
83 output_shape, output_data);
84 }
85 }
86 }
87}
88
89inline void ResizeBilinearKernel(const float *input_ptr, int32_t depth, float scale,
90 float *output_ptr)
91{
92 for (int32_t i = 0; i < depth; i++)
93 {
94 *output_ptr += *input_ptr * scale;
95 output_ptr++;
96 input_ptr++;
97 }
98}
99
100inline void ComputeInterpolationValues(const float value, const float scale,
101 const bool half_pixel_centers, int32_t input_size,
102 float *scaled_value, int32_t *lower_bound,
103 int32_t *upper_bound)
104{
105 if (half_pixel_centers)
106 {
107 *scaled_value = (value + 0.5f) * scale - 0.5f;
108 }
109 else
110 {
111 *scaled_value = value * scale;
112 }
113 float scaled_value_floor = std::floor(*scaled_value);
114 *lower_bound = std::max(static_cast<int32_t>(scaled_value_floor), static_cast<int32_t>(0));
115 *upper_bound = std::min(static_cast<int32_t>(std::ceil(*scaled_value)), input_size - 1);
116}
117
118inline void ResizeBilinearGeneric(int32_t batches, int32_t input_height, int32_t input_width,
119 int32_t depth, int32_t output_height, int32_t output_width,
120 float height_scale, float width_scale, const Shape &input_shape,
121 const float *input_data, float *output_data,
122 const bool half_pixel_centers)
123{
124 memset(output_data, 0, batches * output_height * output_width * depth * sizeof(float));
125
126 int32_t output_offset = 0;
127 for (int b = 0; b < batches; ++b)
128 {
129 for (int y = 0; y < output_height; ++y)
130 {
131 float input_y;
132 int32_t y0, y1;
133 ComputeInterpolationValues(y, height_scale, half_pixel_centers, input_height, &input_y, &y0,
134 &y1);
135 for (int x = 0; x < output_width; ++x)
136 {
137 float input_x;
138 int32_t x0, x1;
139 ComputeInterpolationValues(x, width_scale, half_pixel_centers, input_width, &input_x, &x0,
140 &x1);
141 float *output_ptr = &output_data[output_offset];
142
143 // Run kernel on the 4 corners of the bilinear resize algorithm.
144 int32_t input_offset = Offset(input_shape, b, y0, x0, 0);
145 float scale = (1 - (input_y - y0)) * (1 - (input_x - x0));
146 const float *input_ptr = &input_data[input_offset];
147 ResizeBilinearKernel(input_ptr, depth, scale, output_ptr);
148
149 input_offset = Offset(input_shape, b, y0, x1, 0);
150 scale = (1 - (input_y - y0)) * (input_x - x0);
151 input_ptr = &input_data[input_offset];
152 ResizeBilinearKernel(input_ptr, depth, scale, output_ptr);
153
154 input_offset = Offset(input_shape, b, y1, x0, 0);
155 scale = (input_y - y0) * (1 - (input_x - x0));
156 input_ptr = &input_data[input_offset];
157 ResizeBilinearKernel(input_ptr, depth, scale, output_ptr);
158
159 input_offset = Offset(input_shape, b, y1, x1, 0);
160 scale = (input_y - y0) * (input_x - x0);
161 input_ptr = &input_data[input_offset];
162 ResizeBilinearKernel(input_ptr, depth, scale, output_ptr);
163
164 output_offset += depth;
165 }
166 }
167 }
168}
169
170template <typename T>
171inline void ResizeBilinearGenericSmallChannel(int32_t batches, int32_t input_height,
172 int32_t input_width, int32_t depth,
173 int32_t output_height, int32_t output_width,
174 float height_scale, float width_scale,
175 const Shape &input_shape, const T *input_data,
176 T *output_data, const bool half_pixel_centers)
177{
178 T *output_ptr = &output_data[0];
179 for (int b = 0; b < batches; ++b)
180 {
181 for (int y = 0; y < output_height; ++y)
182 {
183 float input_y;
184 int32_t y0, y1;
185 ComputeInterpolationValues(y, height_scale, half_pixel_centers, input_height, &input_y, &y0,
186 &y1);
187 for (int x = 0; x < output_width; ++x)
188 {
189 float input_x;
190 int32_t x0, x1;
191 ComputeInterpolationValues(x, width_scale, half_pixel_centers, input_width, &input_x, &x0,
192 &x1);
193
194 int32_t input_offset[4] = {
195 Offset(input_shape, b, y0, x0, 0), Offset(input_shape, b, y0, x1, 0),
196 Offset(input_shape, b, y1, x0, 0), Offset(input_shape, b, y1, x1, 0)};
197 float scale[4] = {(1 - (input_y - y0)) * (1 - (input_x - x0)),
198 (1 - (input_y - y0)) * (input_x - x0),
199 (input_y - y0) * (1 - (input_x - x0)), (input_y - y0) * (input_x - x0)};
200
201 for (int d = 0; d < depth; d++)
202 {
203 const T *input_ptr = &input_data[d];
204 *output_ptr++ = static_cast<T>(
205 input_ptr[input_offset[0]] * scale[0] + input_ptr[input_offset[1]] * scale[1] +
206 input_ptr[input_offset[2]] * scale[2] + input_ptr[input_offset[3]] * scale[3]);
207 }
208 }
209 }
210 }
211}
212
213void ResizeBilinear(ResizeBilinearParams &params, const Shape &input_shape, const float *input_data,
214 const Shape &output_shape, float *output_data)
215{
216 int32_t batches = static_cast<int32_t>(MatchingDim(input_shape, 0, output_shape, 0));
217 int32_t input_height = input_shape.Dims(1);
218 int32_t input_width = input_shape.Dims(2);
219 int32_t depth = static_cast<int32_t>(MatchingDim(input_shape, 3, output_shape, 3));
220
221 // Specialize for 2x2 upsample.
222 if (!params.align_corners && !params.half_pixel_centers &&
223 params.output_height == 2 * input_height && params.output_width == 2 * input_width)
224 {
225 ResizeBilinear2x2(batches, input_height, input_width, depth, params.output_height,
226 params.output_width, input_shape, input_data, output_shape, output_data);
227 }
228 else
229 {
230 float height_scale = static_cast<float>(input_height) / params.output_height;
231 float width_scale = static_cast<float>(input_width) / params.output_width;
232 if (params.align_corners && params.output_height > 1)
233 {
234 height_scale = static_cast<float>(input_height - 1) / (params.output_height - 1);
235 }
236 if (params.align_corners && params.output_width > 1)
237 {
238 width_scale = static_cast<float>(input_width - 1) / (params.output_width - 1);
239 }
240
241 ResizeBilinearGeneric(batches, input_height, input_width, depth, params.output_height,
242 params.output_width, height_scale, width_scale, input_shape, input_data,
243 output_data, params.half_pixel_centers);
244 }
245}
246
247void ResizeBilinear(ResizeBilinearParams &params, const Shape &input_shape,
248 const uint8_t *input_data, const Shape &output_shape, uint8_t *output_data)
249{
250 int32_t batches = MatchingDim(input_shape, 0, output_shape, 0);
251 int32_t input_height = input_shape.Dims(1);
252 int32_t input_width = input_shape.Dims(2);
253 int32_t depth = MatchingDim(input_shape, 3, output_shape, 3);
254
255 float height_scale = (params.align_corners && params.output_height > 1)
256 ? (static_cast<float>(input_height - 1) / (params.output_height - 1))
257 : (static_cast<float>(input_height) / params.output_height);
258
259 float width_scale = (params.align_corners && params.output_width > 1)
260 ? (static_cast<float>(input_width - 1) / (params.output_width - 1))
261 : (static_cast<float>(input_width) / params.output_width);
262
263 ResizeBilinearGenericSmallChannel<uint8_t>(
264 batches, input_height, input_width, depth, params.output_height, params.output_width,
265 height_scale, width_scale, input_shape, input_data, output_data, params.half_pixel_centers);
266}
267
268inline void ComputeInterpolationValues(const int32_t value, const int32_t scale_10,
269 const bool half_pixel_centers, int32_t input_size,
270 int32_t *scaled_value, int32_t *lower_bound,
271 int32_t *upper_bound)
272{
273 if (half_pixel_centers)
274 {
275 *scaled_value = value * scale_10 + scale_10 / 2 - (1 << 9);
276 }
277 else
278 {
279 *scaled_value = value * scale_10;
280 }
281 *lower_bound = std::max(*scaled_value / (1 << 10), 0);
282 *upper_bound = std::min(*scaled_value / (1 << 10) + 1, input_size - 1);
283}
284
285inline void ResizeBilinear(const ResizeBilinearParams &op_params,
286 const Shape &unextended_input_shape, const int8_t *input_data,
287 const Shape &unextended_output_shape, int8_t *output_data)
288{
289 // If half_pixel_centers is True, align_corners must be False.
290 assert(!op_params.half_pixel_centers || !op_params.align_corners);
291 assert(unextended_input_shape.DimensionsCount() <= 4);
292 assert(unextended_output_shape.DimensionsCount() <= 4);
293 const Shape input_shape = Shape::ExtendedShape(4, unextended_input_shape);
294 const Shape output_shape = Shape::ExtendedShape(4, unextended_output_shape);
295
296 const int32_t batches = MatchingDim(input_shape, 0, output_shape, 0);
297 const int32_t input_height = input_shape.Dims(1);
298 const int32_t input_width = input_shape.Dims(2);
299 const int32_t depth = MatchingDim(input_shape, 3, output_shape, 3);
300
301 const int32_t output_height = op_params.output_height;
302 const int32_t output_width = op_params.output_width;
303
304 int32_t height_scale_10 = ((1 << 10) * input_height + output_height / 2) / output_height;
305 int32_t width_scale_10 = ((1 << 10) * input_width + output_width / 2) / output_width;
306 if (op_params.align_corners && output_height > 1)
307 {
308 height_scale_10 =
309 ((1 << 10) * (input_height - 1) + (output_height - 1) / 2) / (output_height - 1);
310 }
311 if (op_params.align_corners && output_width > 1)
312 {
313 width_scale_10 = ((1 << 10) * (input_width - 1) + (output_width - 1) / 2) / (output_width - 1);
314 }
315
316 for (int b = 0; b < batches; ++b)
317 {
318 for (int y = 0; y < output_height; ++y)
319 {
320 int32_t input_y, y0, y1;
321 ComputeInterpolationValues(y, height_scale_10, op_params.half_pixel_centers, input_height,
322 &input_y, &y0, &y1);
323 for (int x = 0; x < output_width; ++x)
324 {
325 int32_t input_x, x0, x1;
326 ComputeInterpolationValues(x, width_scale_10, op_params.half_pixel_centers, input_width,
327 &input_x, &x0, &x1);
328 for (int c = 0; c < depth; ++c)
329 {
330 const int64_t output_20_ll =
331 static_cast<int64_t>(input_data[Offset(input_shape, b, y0, x0, c)]) *
332 ((1 << 10) - (input_y - (1 << 10) * y0)) * ((1 << 10) - (input_x - (1 << 10) * x0));
333 const int64_t output_20_lu =
334 static_cast<int64_t>(input_data[Offset(input_shape, b, y1, x0, c)]) *
335 (input_y - (1 << 10) * y0) * ((1 << 10) - (input_x - (1 << 10) * x0));
336 const int64_t output_20_rl =
337 static_cast<int64_t>(input_data[Offset(input_shape, b, y0, x1, c)]) *
338 ((1 << 10) - (input_y - (1 << 10) * y0)) * (input_x - (1 << 10) * x0);
339 const int64_t output_20_ru =
340 static_cast<int64_t>(input_data[Offset(input_shape, b, y1, x1, c)]) *
341 (input_y - (1 << 10) * y0) * (input_x - (1 << 10) * x0);
342 const int64_t output_20 = output_20_ll + output_20_lu + output_20_rl + output_20_ru;
343 const int64_t round = (output_20 > 0) ? (1 << 19) : -(1 << 19);
344 const int8_t interpolation = static_cast<int8_t>((output_20 + round) / (1 << 20));
345 output_data[Offset(output_shape, b, y, x, c)] = interpolation;
346 }
347 }
348 }
349 }
350}
351
352} // namespace cker
353} // namespace nnfw
354
355#endif // __NNFW_CKER_RESIZEBILINEAR_H__
int32_t DimensionsCount() const
Definition Shape.h:91
int32_t Dims(int i) const
Definition Shape.h:92
const luci_interpreter::RuntimeShape output_shape
int MatchingDim(const Shape &shape1, int index1, const Shape &shape2, int index2)
Definition Shape.h:220
void ResizeBilinear2x2(int32_t batches, int32_t input_height, int32_t input_width, int32_t depth, int32_t output_height, int32_t output_width, const Shape &input_shape, const float *input_data, const Shape &output_shape, float *output_data)
void ResizeBilinearGeneric(int32_t batches, int32_t input_height, int32_t input_width, int32_t depth, int32_t output_height, int32_t output_width, float height_scale, float width_scale, const Shape &input_shape, const float *input_data, float *output_data, const bool half_pixel_centers)
int Offset(const Shape &shape, int i0, int i1, int i2, int i3)
Definition Shape.h:237
void ResizeBilinearKernel(const float *input_ptr, int32_t depth, float scale, float *output_ptr)
void ResizeBilinearGenericSmallChannel(int32_t batches, int32_t input_height, int32_t input_width, int32_t depth, int32_t output_height, int32_t output_width, float height_scale, float width_scale, const Shape &input_shape, const T *input_data, T *output_data, const bool half_pixel_centers)
void ResizeBilinear(ResizeBilinearParams &params, const Shape &input_shape, const float *input_data, const Shape &output_shape, float *output_data)
void ResizeBilinearKernel2x2(int32_t x0, int32_t x1, int32_t y0, int32_t y1, int32_t x, int32_t y, int32_t depth, int32_t batch, const Shape &input_shape, const float *input_data, const Shape &output_shape, float *output_data)
void ComputeInterpolationValues(const float value, const float scale, const bool half_pixel_centers, int32_t input_size, float *scaled_value, int32_t *lower_bound, int32_t *upper_bound)
Definition topk_v2.h:30