2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #ifndef __NNFW_CKER_RESIZEBILINEAR_H__
19 #define __NNFW_CKER_RESIZEBILINEAR_H__
21 #include "cker/Shape.h"
22 #include "cker/Types.h"
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)
35 const int32_t input_width = input_shape.Dims(2);
36 const int32_t output_width = output_shape.Dims(2);
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;
43 for (int ch = 0; ch < depth; ch++)
45 const int32_t input_offset = Offset(input_shape, batch, y0, x0, ch);
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];
53 const int32_t output_offset = Offset(output_shape, batch, y, x, ch);
54 output_data[output_offset] = x0y0;
57 output_data[output_offset + output_x_offset] = (x0y0 + x1y0) / 2;
59 // Bottom left corner.
60 float output = (x0y0 + x0y1) / 2;
61 output_data[output_offset + output_y_offset] = output;
63 // Bottom right corner.
64 output_data[output_offset + output_x_offset + output_y_offset] =
65 (output + ((x1y0 + x1y1) / 2)) / 2;
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)
74 for (int b = 0; b < batches; b++)
76 for (int y0 = 0, y = 0; y <= output_height - 2; y += 2, y0++)
78 for (int x0 = 0, x = 0; x <= output_width - 2; x += 2, x0++)
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);
89 inline void ResizeBilinearKernel(const float *input_ptr, int32_t depth, float scale,
92 for (int32_t i = 0; i < depth; i++)
94 *output_ptr += *input_ptr * scale;
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)
105 if (half_pixel_centers)
107 *scaled_value = (value + 0.5f) * scale - 0.5f;
111 *scaled_value = value * scale;
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);
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)
124 memset(output_data, 0, batches * output_height * output_width * depth * sizeof(float));
126 int32_t output_offset = 0;
127 for (int b = 0; b < batches; ++b)
129 for (int y = 0; y < output_height; ++y)
133 ComputeInterpolationValues(y, height_scale, half_pixel_centers, input_height, &input_y, &y0,
135 for (int x = 0; x < output_width; ++x)
139 ComputeInterpolationValues(x, width_scale, half_pixel_centers, input_width, &input_x, &x0,
141 float *output_ptr = &output_data[output_offset];
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);
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);
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);
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);
164 output_offset += depth;
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)
178 T *output_ptr = &output_data[0];
179 for (int b = 0; b < batches; ++b)
181 for (int y = 0; y < output_height; ++y)
185 ComputeInterpolationValues(y, height_scale, half_pixel_centers, input_height, &input_y, &y0,
187 for (int x = 0; x < output_width; ++x)
191 ComputeInterpolationValues(x, width_scale, half_pixel_centers, input_width, &input_x, &x0,
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)};
201 for (int d = 0; d < depth; d++)
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]);
213 void ResizeBilinear(ResizeBilinearParams ¶ms, const Shape &input_shape, const float *input_data,
214 const Shape &output_shape, float *output_data)
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));
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)
225 ResizeBilinear2x2(batches, input_height, input_width, depth, params.output_height,
226 params.output_width, input_shape, input_data, output_shape, output_data);
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)
234 height_scale = static_cast<float>(input_height - 1) / (params.output_height - 1);
236 if (params.align_corners && params.output_width > 1)
238 width_scale = static_cast<float>(input_width - 1) / (params.output_width - 1);
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);
247 void ResizeBilinear(ResizeBilinearParams ¶ms, const Shape &input_shape,
248 const uint8_t *input_data, const Shape &output_shape, uint8_t *output_data)
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);
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);
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);
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);
270 #endif // __NNFW_CKER_RESIZEBILINEAR_H__