blob: 827bbef4cda07cb26075eaa8e6aca869a840e675 [file] [log] [blame]
Sang-Hoon Park68dd25f2020-10-19 16:00:11 +01001/*
2* Copyright (c) 2020 Arm Limited.
3 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#ifndef SRC_CORE_HELPERS_SCALEHELPERS_H
25#define SRC_CORE_HELPERS_SCALEHELPERS_H
26
27#include "arm_compute/core/Error.h"
28#include "arm_compute/core/QuantizationInfo.h"
29
30#include <algorithm>
31#include <cmath>
32#include <cstddef>
33#include <cstdint>
34
35namespace arm_compute
36{
37namespace scale_helpers
38{
39/** Computes bilinear interpolation using the pointer to the top-left pixel and the pixel's distance between
40 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
41 *
42 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
43 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
44 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
45 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
46 *
47 * @note dx and dy must be in the range [0, 1.0]
48 *
49 * @return The bilinear interpolated pixel value
50 */
51template <typename T>
52inline T delta_bilinear_c1(const T *pixel_ptr, size_t stride, float dx, float dy)
53{
54 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
55
56 const float dx1 = 1.0f - dx;
57 const float dy1 = 1.0f - dy;
58
59 const T a00 = *pixel_ptr;
60 const T a01 = *(pixel_ptr + 1);
61 const T a10 = *(pixel_ptr + stride);
62 const T a11 = *(pixel_ptr + stride + 1);
63
64 const float w1 = dx1 * dy1;
65 const float w2 = dx * dy1;
66 const float w3 = dx1 * dy;
67 const float w4 = dx * dy;
68
69 return static_cast<T>(a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4);
70}
71
72/** Computes bilinear interpolation for quantized input and output, using the pointer to the top-left pixel and the pixel's distance between
73 * the real coordinates and the smallest following integer coordinates. Input must be QASYMM8 and in single channel format.
74 *
75 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
76 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
77 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
78 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
79 * @param[in] iq_info Input QuantizationInfo
80 * @param[in] oq_info Output QuantizationInfo
81 *
82 * @note dx and dy must be in the range [0, 1.0]
83 *
84 * @return The bilinear interpolated pixel value
85 */
86inline uint8_t delta_bilinear_c1_quantized(const uint8_t *pixel_ptr, size_t stride, float dx, float dy,
87 UniformQuantizationInfo iq_info, UniformQuantizationInfo oq_info)
88{
89 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
90
91 const float dx1 = 1.0f - dx;
92 const float dy1 = 1.0f - dy;
93
94 const float a00 = dequantize_qasymm8(*pixel_ptr, iq_info);
95 const float a01 = dequantize_qasymm8(*(pixel_ptr + 1), iq_info);
96 const float a10 = dequantize_qasymm8(*(pixel_ptr + stride), iq_info);
97 const float a11 = dequantize_qasymm8(*(pixel_ptr + stride + 1), iq_info);
98
99 const float w1 = dx1 * dy1;
100 const float w2 = dx * dy1;
101 const float w3 = dx1 * dy;
102 const float w4 = dx * dy;
103 float res = a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4;
104 return static_cast<uint8_t>(quantize_qasymm8(res, oq_info));
105}
106
107/** Computes bilinear interpolation for quantized input and output, using the pointer to the top-left pixel and the pixel's distance between
108 * the real coordinates and the smallest following integer coordinates. Input must be QASYMM8_SIGNED and in single channel format.
109 *
110 * @param[in] pixel_ptr Pointer to the top-left pixel value of a single channel input.
111 * @param[in] stride Stride to access the bottom-left and bottom-right pixel values
112 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
113 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
114 * @param[in] iq_info Input QuantizationInfo
115 * @param[in] oq_info Output QuantizationInfo
116 *
117 * @note dx and dy must be in the range [0, 1.0]
118 *
119 * @return The bilinear interpolated pixel value
120 */
121inline int8_t delta_bilinear_c1_quantized(const int8_t *pixel_ptr, size_t stride, float dx, float dy,
122 UniformQuantizationInfo iq_info, UniformQuantizationInfo oq_info)
123{
124 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
125
126 const float dx1 = 1.0f - dx;
127 const float dy1 = 1.0f - dy;
128
129 const float a00 = dequantize_qasymm8_signed(*pixel_ptr, iq_info);
130 const float a01 = dequantize_qasymm8_signed(*(pixel_ptr + 1), iq_info);
131 const float a10 = dequantize_qasymm8_signed(*(pixel_ptr + stride), iq_info);
132 const float a11 = dequantize_qasymm8_signed(*(pixel_ptr + stride + 1), iq_info);
133
134 const float w1 = dx1 * dy1;
135 const float w2 = dx * dy1;
136 const float w3 = dx1 * dy;
137 const float w4 = dx * dy;
138 float res = a00 * w1 + a01 * w2 + a10 * w3 + a11 * w4;
139 return static_cast<int8_t>(quantize_qasymm8_signed(res, oq_info));
140}
141
142/** Computes linear interpolation using the pointer to the top pixel and the pixel's distance between
143 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
144 *
145 * @param[in] pixel_ptr Pointer to the top pixel value of a single channel input.
146 * @param[in] stride Stride to access the bottom pixel value
147 * @param[in] dy Pixel's distance between the Y real coordinate and the smallest Y following integer
148 *
149 * @note dy must be in the range [0, 1.0]
150 *
151 * @return The linear interpolated pixel value
152 */
153template <typename T>
154inline T delta_linear_c1_y(const T *pixel_ptr, size_t stride, float dy)
155{
156 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
157
158 const float dy1 = 1.0f - dy;
159
160 const T a00 = *pixel_ptr;
161 const T a10 = *(pixel_ptr + stride);
162
163 const float w1 = dy1;
164 const float w3 = dy;
165
166 return static_cast<T>(a00 * w1 + a10 * w3);
167}
168
169/** Computes linear interpolation using the pointer to the left pixel and the pixel's distance between
170 * the real coordinates and the smallest following integer coordinates. Input must be in single channel format.
171 *
172 * @param[in] pixel_ptr Pointer to the left pixel value of a single channel input.
173 * @param[in] dx Pixel's distance between the X real coordinate and the smallest X following integer
174 *
175 * @note dx must be in the range [0, 1.0]
176 *
177 * @return The linear interpolated pixel value
178 */
179template <typename T>
180inline T delta_linear_c1_x(const T *pixel_ptr, float dx)
181{
182 ARM_COMPUTE_ERROR_ON(pixel_ptr == nullptr);
183
184 const T a00 = *pixel_ptr;
185 const T a01 = *(pixel_ptr + 1);
186
187 const float dx1 = 1.0f - dx;
188
189 const float w1 = dx1;
190 const float w2 = dx;
191
192 return static_cast<T>(a00 * w1 + a01 * w2);
193}
194
195/** Return the pixel at (x,y) using bilinear interpolation.
196 *
197 * @warning Only works if the iterator was created with an IImage
198 *
199 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel input.
200 * @param[in] stride Stride in bytes of the image;
201 * @param[in] x X position of the wanted pixel
202 * @param[in] y Y position of the wanted pixel
203 *
204 * @return The pixel at (x, y) using bilinear interpolation.
205 */
206template <typename T>
207inline T pixel_bilinear_c1(const T *first_pixel_ptr, size_t stride, float x, float y)
208{
209 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
210
211 const int32_t xi = std::floor(x);
212 const int32_t yi = std::floor(y);
213
214 const float dx = x - xi;
215 const float dy = y - yi;
216
217 return delta_bilinear_c1(first_pixel_ptr + xi + yi * stride, stride, dx, dy);
218}
219
220/** Return the pixel at (x,y) using bilinear interpolation by clamping when out of borders. The image must be single channel input
221 *
222 * @warning Only works if the iterator was created with an IImage
223 *
224 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel image.
225 * @param[in] stride Stride in bytes of the image
226 * @param[in] width Width of the image
227 * @param[in] height Height of the image
228 * @param[in] x X position of the wanted pixel
229 * @param[in] y Y position of the wanted pixel
230 *
231 * @return The pixel at (x, y) using bilinear interpolation.
232 */
233template <typename T>
234inline uint8_t
235pixel_bilinear_c1_clamp(const T *first_pixel_ptr, size_t stride, size_t width, size_t height, float x, float y)
236{
237 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
238
239 x = std::max(-1.f, std::min(x, static_cast<float>(width)));
240 y = std::max(-1.f, std::min(y, static_cast<float>(height)));
241
242 const float xi = std::floor(x);
243 const float yi = std::floor(y);
244
245 const float dx = x - xi;
246 const float dy = y - yi;
247
248 if(dx == 0.0f)
249 {
250 if(dy == 0.0f)
251 {
252 return static_cast<T>(first_pixel_ptr[static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride]);
253 }
254 return delta_linear_c1_y(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride,
255 stride, dy);
256 }
257 if(dy == 0.0f)
258 {
259 return delta_linear_c1_x(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride,
260 dx);
261 }
262 return delta_bilinear_c1(first_pixel_ptr + static_cast<int32_t>(xi) + static_cast<int32_t>(yi) * stride, stride,
263 dx, dy);
264}
265
266/** Return the pixel at (x,y) using area interpolation by clamping when out of borders. The image must be single channel U8
267 *
268 * @note The interpolation area depends on the width and height ration of the input and output images
269 * @note Currently average of the contributing pixels is calculated
270 *
271 * @param[in] first_pixel_ptr Pointer to the first pixel of a single channel U8 image.
272 * @param[in] stride Stride in bytes of the image
273 * @param[in] width Width of the image
274 * @param[in] height Height of the image
275 * @param[in] wr Width ratio among the input image width and output image width.
276 * @param[in] hr Height ratio among the input image height and output image height.
277 * @param[in] x X position of the wanted pixel
278 * @param[in] y Y position of the wanted pixel
279 *
280 * @return The pixel at (x, y) using area interpolation.
281 */
282inline uint8_t
283pixel_area_c1u8_clamp(const uint8_t *first_pixel_ptr, size_t stride, size_t width, size_t height, float wr,
284 float hr, int x, int y)
285{
286 ARM_COMPUTE_ERROR_ON(first_pixel_ptr == nullptr);
287
288 // Calculate sampling position
289 float in_x = (x + 0.5f) * wr - 0.5f;
290 float in_y = (y + 0.5f) * hr - 0.5f;
291
292 // Get bounding box offsets
293 int x_from = std::floor(x * wr - 0.5f - in_x);
294 int y_from = std::floor(y * hr - 0.5f - in_y);
295 int x_to = std::ceil((x + 1) * wr - 0.5f - in_x);
296 int y_to = std::ceil((y + 1) * hr - 0.5f - in_y);
297
298 // Clamp position to borders
299 in_x = std::max(-1.f, std::min(in_x, static_cast<float>(width)));
300 in_y = std::max(-1.f, std::min(in_y, static_cast<float>(height)));
301
302 // Clamp bounding box offsets to borders
303 x_from = ((in_x + x_from) < -1) ? -1 : x_from;
304 y_from = ((in_y + y_from) < -1) ? -1 : y_from;
305 x_to = ((in_x + x_to) > width) ? (width - in_x) : x_to;
306 y_to = ((in_y + y_to) > height) ? (height - in_y) : y_to;
307
308 // Get pixel index
309 const int xi = std::floor(in_x);
310 const int yi = std::floor(in_y);
311
312 // Bounding box elements in each dimension
313 const int x_elements = (x_to - x_from + 1);
314 const int y_elements = (y_to - y_from + 1);
315 ARM_COMPUTE_ERROR_ON(x_elements == 0 || y_elements == 0);
316
317 // Sum pixels in area
318 int sum = 0;
319 for(int j = yi + y_from, je = yi + y_to; j <= je; ++j)
320 {
321 const uint8_t *ptr = first_pixel_ptr + j * stride + xi + x_from;
322 sum = std::accumulate(ptr, ptr + x_elements, sum);
323 }
324
325 // Return average
326 return sum / (x_elements * y_elements);
327}
328} // namespace scale_helpers
329} // namespace arm_compute
330
331#endif /* SRC_CORE_HELPERS_SCALEHELPERS_H */