7fc1e9123d57cd705d9513114d848122f16a5a7d
[platform/core/ml/nnfw.git] / compute / cker / include / cker / operation / ResizeBilinear.h
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
25 namespace nnfw
26 {
27 namespace cker
28 {
29
30 inline 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
69 inline 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
89 inline 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
100 inline 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
118 inline 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
170 template <typename T>
171 inline 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
213 void 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
247 void 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 } // namespace cker
268 } // namespace nnfw
269
270 #endif // __NNFW_CKER_RESIZEBILINEAR_H__