blob: 62f4e5d057b0c18585a5ea2ac7d75c449743d145 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
2 * Copyright (c) 2016, 2017 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#include "arm_compute/core/NEON/kernels/NEWarpKernel.h"
25
26#include "arm_compute/core/AccessWindowStatic.h"
27#include "arm_compute/core/Coordinates.h"
28#include "arm_compute/core/Error.h"
29#include "arm_compute/core/Helpers.h"
30#include "arm_compute/core/ITensor.h"
31#include "arm_compute/core/TensorInfo.h"
32#include "arm_compute/core/Validate.h"
33#include "arm_compute/core/Window.h"
34
35#include <cstddef>
36
37using namespace arm_compute;
38
39namespace
40{
41inline uint8_t nearest_interpolation(const uint8_t *in_ptr, int x, int y, size_t stride)
42{
43 return in_ptr[x + y * stride];
44}
45} // namespace
46
47INEWarpKernel::INEWarpKernel()
48 : _func(nullptr), _input(nullptr), _output(nullptr), _constant_border_value(0), _matrix(nullptr)
49{
50}
51
Isabella Gottardif9bae2e2017-07-28 17:24:08 +010052BorderSize INEWarpKernel::border_size() const
53{
54 return BorderSize(1);
55}
56
Moritz Pflanzerc186b572017-09-07 09:48:04 +010057void INEWarpKernel::run(const Window &window, const ThreadInfo &info)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010058{
Moritz Pflanzerc186b572017-09-07 09:48:04 +010059 ARM_COMPUTE_UNUSED(info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010060 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
61 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
62 ARM_COMPUTE_ERROR_ON(_func == nullptr);
63
64 (this->*_func)(window);
65}
66
67void INEWarpKernel::configure(const ITensor *input, ITensor *output, const float *matrix, BorderMode border_mode, uint8_t constant_border_value)
68{
69 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::U8);
70 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::U8);
71 ARM_COMPUTE_ERROR_ON(nullptr == matrix);
72
73 _matrix = matrix;
74 _constant_border_value = constant_border_value;
75
76 switch(border_mode)
77 {
78 case BorderMode::UNDEFINED:
79 _func = &INEWarpKernel::warp_undefined;
80 break;
81 case BorderMode::CONSTANT:
82 _func = &INEWarpKernel::warp_constant;
83 break;
84 case BorderMode::REPLICATE:
85 _func = &INEWarpKernel::warp_replicate;
86 break;
87 default:
88 ARM_COMPUTE_ERROR("Border mode not supported");
89 break;
90 }
91
92 _input = input;
93 _output = output;
94
95 // Configure kernel window
96 Window win = calculate_max_window(*output->info(), Steps(1U));
97
98 const ValidRegion &input_valid_region = input->info()->valid_region();
99
100 // Reads can occur within the valid region of the input
101 AccessWindowStatic input_access(input->info(),
Isabella Gottardif9bae2e2017-07-28 17:24:08 +0100102 input_valid_region.anchor[0] - border_size().left, input_valid_region.anchor[1] - border_size().top,
103 input_valid_region.anchor[0] + input_valid_region.shape[0] + border_size().right,
104 input_valid_region.anchor[1] + input_valid_region.shape[1] + border_size().bottom);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100105 AccessWindowHorizontal output_access(output->info(), 0, 1);
106
107 update_window_and_padding(win, input_access, output_access);
108
109 output_access.set_valid_region(win, ValidRegion(Coordinates(), output->info()->tensor_shape()));
110
111 INEKernel::configure(win);
112}
113
114template <InterpolationPolicy interpolation>
115void NEWarpAffineKernel<interpolation>::warp_undefined(const Window &window)
116{
117 // Don't increment in X and Y direction for the input tensor
118 // A pointer to the start of this plane is needed as base for the precomputed offsets
119 Window win_in(window);
120 win_in.set(Window::DimX, Window::Dimension(0, 0, 0));
121 win_in.set(Window::DimY, Window::Dimension(0, 0, 0));
122
123 Iterator in(_input, win_in);
124 Iterator out(_output, window);
125
126 const int min_x = _input->info()->valid_region().anchor[0];
127 const int max_x = min_x + _input->info()->valid_region().shape[0];
128 const int min_y = _input->info()->valid_region().anchor[1];
129 const int max_y = min_y + _input->info()->valid_region().shape[1];
130 const size_t stride = _input->info()->strides_in_bytes()[1];
131
132 // x0 = M01 * x + M01 * y + M02
133 // y0 = M11 * x + M11 * y + M12
134 const float M00 = _matrix[0];
135 const float M10 = _matrix[1];
136 const float M01 = _matrix[0 + 1 * 2];
137 const float M11 = _matrix[1 + 1 * 2];
138 const float M02 = _matrix[0 + 2 * 2];
139 const float M12 = _matrix[1 + 2 * 2];
140
141 // "M00 * x" and "M10 * x", when x = window.x.start
142 const float start_x0 = M00 * window.x().start();
143 const float start_y0 = M10 * window.x().start();
144
145 // Current row
146 int y_cur = window.y().start();
147
148 // const_x0 and const_y0 are the constant parts of x0 and y0 during the row processing
149 float const_x0 = M01 * y_cur + M02;
150 float const_y0 = M11 * y_cur + M12;
151
152 // Affine warp coordinates
153 float x0 = start_x0 + const_x0;
154 float y0 = start_y0 + const_y0;
155
156 execute_window_loop(window, [&](const Coordinates & id)
157 {
158 // Check if we are processing a new row. If so, update the current row (y_cur), x0 and y0
159 if(y_cur != id.y())
160 {
161 y_cur = id.y();
162
163 const_x0 = M01 * y_cur + M02;
164 const_y0 = M11 * y_cur + M12;
165
166 x0 = start_x0 + const_x0;
167 y0 = start_y0 + const_y0;
168 }
169
170 // Only write to output if x0 and y0 are within the valid region.
171 // Otherwise the read value would be undefined.
172 if((min_y <= y0) && (y0 < max_y) && (min_x <= x0) && (x0 < max_x))
173 {
174 switch(interpolation)
175 {
176 case InterpolationPolicy::NEAREST_NEIGHBOR:
177 *out.ptr() = nearest_interpolation(in.ptr(), x0, y0, stride);
178 break;
179 case InterpolationPolicy::BILINEAR:
Georgios Pinitas583137c2017-08-31 18:12:42 +0100180 *out.ptr() = pixel_bilinear_c1(in.ptr(), stride, x0, y0);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100181 break;
182 default:
183 ARM_COMPUTE_ERROR("Interpolation not supported");
184 }
185 }
186
187 x0 += M00;
188 y0 += M10;
189 },
190 in, out);
191}
192
193template <InterpolationPolicy interpolation>
194void NEWarpAffineKernel<interpolation>::warp_constant(const Window &window)
195{
196 // Don't increment in X and Y direction for the input tensor
197 // A pointer to the start of this plane is needed as base for the precomputed offsets
198 Window win_in(window);
199 win_in.set(Window::DimX, Window::Dimension(0, 0, 0));
200 win_in.set(Window::DimY, Window::Dimension(0, 0, 0));
201
202 Iterator in(_input, win_in);
203 Iterator out(_output, window);
204
205 const int min_x = _input->info()->valid_region().anchor[0];
206 const int max_x = min_x + _input->info()->valid_region().shape[0];
207 const int min_y = _input->info()->valid_region().anchor[1];
208 const int max_y = min_y + _input->info()->valid_region().shape[1];
209 const size_t stride = _input->info()->strides_in_bytes()[1];
210
211 // x0 = M01 * x + M01 * y + M02
212 // y0 = M11 * x + M11 * y + M12
213 const float M00 = _matrix[0];
214 const float M10 = _matrix[1];
215 const float M01 = _matrix[0 + 1 * 2];
216 const float M11 = _matrix[1 + 1 * 2];
217 const float M02 = _matrix[0 + 2 * 2];
218 const float M12 = _matrix[1 + 2 * 2];
219
220 // "M00 * x" and "M10 * x", when x = window.x.start
221 const float start_x0 = M00 * window.x().start();
222 const float start_y0 = M10 * window.x().start();
223
224 // Current row
225 int y_cur = window.y().start();
226
227 // const_x0 and const_y0 are the constant parts of x0 and y0 during the row processing
228 float const_x0 = M01 * y_cur + M02;
229 float const_y0 = M11 * y_cur + M12;
230
231 // Affine warp coordinates
232 float x0 = start_x0 + const_x0;
233 float y0 = start_y0 + const_y0;
234
235 execute_window_loop(window, [&](const Coordinates & id)
236 {
237 // Check if we are processing a new row. If so, update the current row (y_cur), x0 and y0
238 if(y_cur != id.y())
239 {
240 y_cur = id.y();
241
242 const_x0 = M01 * y_cur + M02;
243 const_y0 = M11 * y_cur + M12;
244
245 x0 = start_x0 + const_x0;
246 y0 = start_y0 + const_y0;
247 }
248
249 // Only use input values if x0 and y0 are within the valid region.
250 // Otherwise write the constant border value.
251 if((min_y <= y0) && (y0 < max_y) && (min_x <= x0) && (x0 < max_x))
252 {
253 switch(interpolation)
254 {
255 case InterpolationPolicy::NEAREST_NEIGHBOR:
256 *out.ptr() = nearest_interpolation(in.ptr(), x0, y0, stride);
257 break;
258 case InterpolationPolicy::BILINEAR:
Georgios Pinitas583137c2017-08-31 18:12:42 +0100259 *out.ptr() = pixel_bilinear_c1(in.ptr(), stride, x0, y0);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100260 break;
261 default:
262 ARM_COMPUTE_ERROR("Interpolation not supported");
263 }
264 }
265 else
266 {
267 *out.ptr() = _constant_border_value;
268 }
269
270 x0 += M00;
271 y0 += M10;
272 },
273 in, out);
274}
275
276template <InterpolationPolicy interpolation>
277void NEWarpAffineKernel<interpolation>::warp_replicate(const Window &window)
278{
279 // Don't increment in X and Y direction for the input tensor
280 // A pointer to the start of this plane is needed as base for the precomputed offsets
281 Window win_in(window);
282 win_in.set(Window::DimX, Window::Dimension(0, 0, 0));
283 win_in.set(Window::DimY, Window::Dimension(0, 0, 0));
284
285 Iterator in(_input, win_in);
286 Iterator out(_output, window);
287
288 const int min_x = _input->info()->valid_region().anchor[0];
289 const int max_x = min_x + _input->info()->valid_region().shape[0];
290 const int min_y = _input->info()->valid_region().anchor[1];
291 const int max_y = min_y + _input->info()->valid_region().shape[1];
292 const size_t stride = _input->info()->strides_in_bytes()[1];
293
294 // Current row
295 int y_cur = window.y().start();
296
297 const float M00 = _matrix[0];
298 const float M10 = _matrix[1];
299 const float M01 = _matrix[0 + 1 * 2];
300 const float M11 = _matrix[1 + 1 * 2];
301 const float M02 = _matrix[0 + 2 * 2];
302 const float M12 = _matrix[1 + 2 * 2];
303
304 // "M00 * x" and "M10 * x", when x = window.x.start
305 const float start_x0 = M00 * window.x().start();
306 const float start_y0 = M10 * window.x().start();
307
308 // const_x0 and const_y0 are the constant parts of x0 and y0 during the row processing
309 float const_x0 = M01 * y_cur + M02;
310 float const_y0 = M11 * y_cur + M12;
311
312 float x0 = start_x0 + const_x0;
313 float y0 = start_y0 + const_y0;
314
315 execute_window_loop(window, [&](const Coordinates & id)
316 {
317 // Check if we are processing a new row. If so, update the current row (y_cur), x0 and y0
318 if(y_cur != id.y())
319 {
320 y_cur = id.y();
321
322 const_x0 = M01 * y_cur + M02;
323 const_y0 = M11 * y_cur + M12;
324
325 x0 = start_x0 + const_x0;
326 y0 = start_y0 + const_y0;
327 }
328
329 // Only load from (x0, y0) if the point is within the valid region.
330 // Otherwise load from the edge of the valid region.
331 if((min_y <= y0) && (y0 < max_y) && (min_x <= x0) && (x0 < max_x))
332 {
333 switch(interpolation)
334 {
335 case InterpolationPolicy::NEAREST_NEIGHBOR:
336 *out.ptr() = nearest_interpolation(in.ptr(), x0, y0, stride);
337 break;
338 case InterpolationPolicy::BILINEAR:
Georgios Pinitas583137c2017-08-31 18:12:42 +0100339 *out.ptr() = pixel_bilinear_c1(in.ptr(), stride, x0, y0);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100340 break;
341 default:
342 ARM_COMPUTE_ERROR("Interpolation not supported");
343 }
344 }
345 else
346 {
347 // Clamp coordinates
348 const auto xi = clamp<int>(x0, min_x, max_x - 1);
349 const auto yi = clamp<int>(y0, min_y, max_y - 1);
350
351 *out.ptr() = *(in.ptr() + xi + yi * stride);
352 }
353
354 x0 += M00;
355 y0 += M10;
356 },
357 in, out);
358}
359
360template <InterpolationPolicy interpolation>
361void NEWarpPerspectiveKernel<interpolation>::warp_undefined(const Window &window)
362{
363 // Don't increment in X and Y direction for the input tensor
364 // A pointer to the start of this plane is needed as base for the precomputed offsets
365 Window win_in(window);
366 win_in.set(Window::DimX, Window::Dimension(0, 0, 0));
367 win_in.set(Window::DimY, Window::Dimension(0, 0, 0));
368
369 Iterator in(_input, win_in);
370 Iterator out(_output, window);
371
372 const int min_x = _input->info()->valid_region().anchor[0];
373 const int max_x = min_x + _input->info()->valid_region().shape[0];
374 const int min_y = _input->info()->valid_region().anchor[1];
375 const int max_y = min_y + _input->info()->valid_region().shape[1];
376 const size_t stride = _input->info()->strides_in_bytes()[1];
377
378 // x0 = M00 * x + M01 * y + M02
379 // y0 = M10 * x + M11 * y + M12
380 // z0 = M20 * x + M21 * y + M22
381 // xn = x0 / z0
382 // yn = y0 / z0
383 const float M00 = _matrix[0];
384 const float M10 = _matrix[1];
385 const float M20 = _matrix[2];
386 const float M01 = _matrix[0 + 1 * 3];
387 const float M11 = _matrix[1 + 1 * 3];
388 const float M21 = _matrix[2 + 1 * 3];
389 const float M02 = _matrix[0 + 2 * 3];
390 const float M12 = _matrix[1 + 2 * 3];
391 const float M22 = _matrix[2 + 2 * 3];
392
393 // "M00 * x", "M10 * x" and "M20 * x", when x = window.x.start
394 const float start_x0 = M00 * window.x().start();
395 const float start_y0 = M10 * window.x().start();
396 const float start_z0 = M20 * window.x().start();
397
398 // Current row
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100399 int y_cur = window.y().start();
400 int z_cur = window.z().start();
401 int d3_cur = window[3].start();
402 int d4_cur = window[4].start();
403 int d5_cur = window[5].start();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100404
405 // const_x0, const_y0 and const_z0 are the constant parts of x0, y0 and z0 during the row processing
406 float const_x0 = M01 * y_cur + M02;
407 float const_y0 = M11 * y_cur + M12;
408 float const_z0 = M21 * y_cur + M22;
409
410 // Perspective warp coordinates
411 float x0 = start_x0 + const_x0;
412 float y0 = start_y0 + const_y0;
413 float z0 = start_z0 + const_z0;
414
415 execute_window_loop(window, [&](const Coordinates & id)
416 {
417 // Check if we are processing a new row. If so, update the current processed row (y_cur), x0, y0 and z0
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100418 if((y_cur != id.y()) || (z_cur != id.z()) || (d3_cur != id[3]) || (d4_cur != id[4]) || (d5_cur != id[5]))
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100419 {
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100420 y_cur = id.y();
421 z_cur = id.z();
422 d3_cur = id[3];
423 d4_cur = id[4];
424 d5_cur = id[5];
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100425
426 const_x0 = M01 * y_cur + M02;
427 const_y0 = M11 * y_cur + M12;
428 const_z0 = M21 * y_cur + M22;
429
430 x0 = start_x0 + const_x0;
431 y0 = start_y0 + const_y0;
432 z0 = start_z0 + const_z0;
433 }
434
435 const float xn = x0 / z0;
436 const float yn = y0 / z0;
437
438 // Only write to output if xn and yn are within the valid region.
439 // Otherwise the read value would be undefined.
440 if((min_y <= yn) && (yn < max_y) && (min_x <= xn) && (xn < max_x))
441 {
442 switch(interpolation)
443 {
444 case InterpolationPolicy::NEAREST_NEIGHBOR:
445 *out.ptr() = nearest_interpolation(in.ptr(), xn, yn, stride);
446 break;
447 case InterpolationPolicy::BILINEAR:
Georgios Pinitas583137c2017-08-31 18:12:42 +0100448 *out.ptr() = pixel_bilinear_c1(in.ptr(), stride, xn, yn);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100449 break;
450 default:
451 ARM_COMPUTE_ERROR("Interpolation not supported");
452 }
453 }
454
455 x0 += M00;
456 y0 += M10;
457 z0 += M20;
458 },
459 in, out);
460}
461
462template <InterpolationPolicy interpolation>
463void NEWarpPerspectiveKernel<interpolation>::warp_constant(const Window &window)
464{
465 // Don't increment in X and Y direction for the input tensor
466 // A pointer to the start of this plane is needed as base for the precomputed offsets
467 Window win_in(window);
468 win_in.set(Window::DimX, Window::Dimension(0, 0, 0));
469 win_in.set(Window::DimY, Window::Dimension(0, 0, 0));
470
471 Iterator in(_input, win_in);
472 Iterator out(_output, window);
473
474 const int min_x = _input->info()->valid_region().anchor[0];
475 const int max_x = min_x + _input->info()->valid_region().shape[0];
476 const int min_y = _input->info()->valid_region().anchor[1];
477 const int max_y = min_y + _input->info()->valid_region().shape[1];
478 const size_t stride = _input->info()->strides_in_bytes()[1];
479
480 // x0 = M00 * x + M01 * y + M02
481 // y0 = M10 * x + M11 * y + M12
482 // z0 = M20 * x + M21 * y + M22
483 // xn = x0 / z0
484 // yn = y0 / z0
485 const float M00 = _matrix[0];
486 const float M10 = _matrix[1];
487 const float M20 = _matrix[2];
488 const float M01 = _matrix[0 + 1 * 3];
489 const float M11 = _matrix[1 + 1 * 3];
490 const float M21 = _matrix[2 + 1 * 3];
491 const float M02 = _matrix[0 + 2 * 3];
492 const float M12 = _matrix[1 + 2 * 3];
493 const float M22 = _matrix[2 + 2 * 3];
494
495 // "M00 * x", "M10 * x" and "M20 * x", when x = window.x.start
496 const float start_x0 = M00 * window.x().start();
497 const float start_y0 = M10 * window.x().start();
498 const float start_z0 = M20 * window.x().start();
499
500 // Current row
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100501 int y_cur = window.y().start();
502 int z_cur = window.z().start();
503 int d3_cur = window[3].start();
504 int d4_cur = window[4].start();
505 int d5_cur = window[5].start();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100506
507 // const_x0, const_y0 and const_z0 are the constant parts of x0, y0 and z0 during the row processing
508 float const_x0 = M01 * y_cur + M02;
509 float const_y0 = M11 * y_cur + M12;
510 float const_z0 = M21 * y_cur + M22;
511
512 // Perspective warp coordinates
513 float x0 = start_x0 + const_x0;
514 float y0 = start_y0 + const_y0;
515 float z0 = start_z0 + const_z0;
516
517 execute_window_loop(window, [&](const Coordinates & id)
518 {
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100519 // Check if we are processing a new row. If so, update the current processed row (y_cur), x0, y0 and z0
520 if((y_cur != id.y()) || (z_cur != id.z()) || (d3_cur != id[3]) || (d4_cur != id[4]) || (d5_cur != id[5]))
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100521 {
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100522 y_cur = id.y();
523 z_cur = id.z();
524 d3_cur = id[3];
525 d4_cur = id[4];
526 d5_cur = id[5];
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100527
528 const_x0 = M01 * y_cur + M02;
529 const_y0 = M11 * y_cur + M12;
530 const_z0 = M21 * y_cur + M22;
531
532 x0 = start_x0 + const_x0;
533 y0 = start_y0 + const_y0;
534 z0 = start_z0 + const_z0;
535 }
536
537 const float xn = x0 / z0;
538 const float yn = y0 / z0;
539
540 // Only use input values if xn and yn are within the valid region.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100541 if((min_y <= yn) && (yn < max_y) && (min_x <= xn) && (xn < max_x))
542 {
543 switch(interpolation)
544 {
545 case InterpolationPolicy::NEAREST_NEIGHBOR:
546 *out.ptr() = nearest_interpolation(in.ptr(), xn, yn, stride);
547 break;
548 case InterpolationPolicy::BILINEAR:
Georgios Pinitas583137c2017-08-31 18:12:42 +0100549 *out.ptr() = pixel_bilinear_c1(in.ptr(), stride, xn, yn);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100550 break;
551 default:
552 ARM_COMPUTE_ERROR("Interpolation not supported");
553 }
554 }
555 else
556 {
Isabella Gottardi62031532017-07-04 11:21:28 +0100557 switch(interpolation)
558 {
559 case InterpolationPolicy::NEAREST_NEIGHBOR:
560 *out.ptr() = _constant_border_value;
561 break;
562 case InterpolationPolicy::BILINEAR:
563 {
564 const auto xi = clamp<int>(std::floor(xn), min_x - 1, max_x);
565 const auto yi = clamp<int>(std::floor(yn), min_y - 1, max_y);
566 const auto xi_1 = clamp<int>(std::floor(xn + 1), min_x - 1, max_x);
567 const auto yi_1 = clamp<int>(std::floor(yn + 1), min_y - 1, max_y);
568
569 const float dx = xn - std::floor(xn);
570 const float dy = yn - std::floor(yn);
571 const float dx1 = 1.0f - dx;
572 const float dy1 = 1.0f - dy;
573
574 const float a00 = *(in.ptr() + xi + yi * stride);
575 const float a01 = *(in.ptr() + xi_1 + yi * stride);
576 const float a10 = *(in.ptr() + xi + yi_1 * stride);
577 const float a11 = *(in.ptr() + xi_1 + yi_1 * stride);
578
579 *out.ptr() = a00 * (dx1 * dy1) + a01 * (dx * dy1) + a10 * (dx1 * dy) + a11 * (dx * dy);
580 }
581 break;
582 default:
583 ARM_COMPUTE_ERROR("Interpolation not supported");
584 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100585 }
586
587 x0 += M00;
588 y0 += M10;
589 z0 += M20;
590 },
591 in, out);
592}
593
594template <InterpolationPolicy interpolation>
595void NEWarpPerspectiveKernel<interpolation>::warp_replicate(const Window &window)
596{
597 // Don't increment in X and Y direction for the input tensor
598 // A pointer to the start of this plane is needed as base for the precomputed offsets
599 Window win_in(window);
600 win_in.set(Window::DimX, Window::Dimension(0, 0, 0));
601 win_in.set(Window::DimY, Window::Dimension(0, 0, 0));
602
603 Iterator in(_input, win_in);
604 Iterator out(_output, window);
605
606 const int min_x = _input->info()->valid_region().anchor[0];
607 const int max_x = min_x + _input->info()->valid_region().shape[0];
608 const int min_y = _input->info()->valid_region().anchor[1];
609 const int max_y = min_y + _input->info()->valid_region().shape[1];
610 const size_t stride = _input->info()->strides_in_bytes()[1];
611
612 // Current row
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100613 int y_cur = window.y().start();
614 int z_cur = window.z().start();
615 int d3_cur = window[3].start();
616 int d4_cur = window[4].start();
617 int d5_cur = window[5].start();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100618
619 // x0 = M00 * x + M01 * y + M02
620 // y0 = M10 * x + M11 * y + M12
621 // z0 = M20 * x + M21 * y + M22
622 // xn = x0 / z0
623 // yn = y0 / z0
624 const float M00 = _matrix[0];
625 const float M10 = _matrix[1];
626 const float M20 = _matrix[2];
627 const float M01 = _matrix[0 + 1 * 3];
628 const float M11 = _matrix[1 + 1 * 3];
629 const float M21 = _matrix[2 + 1 * 3];
630 const float M02 = _matrix[0 + 2 * 3];
631 const float M12 = _matrix[1 + 2 * 3];
632 const float M22 = _matrix[2 + 2 * 3];
633
634 // "M00 * x", "M10 * x" and "M20 * x", when x = window.x.start
635 const float start_x0 = M00 * window.x().start();
636 const float start_y0 = M10 * window.x().start();
637 const float start_z0 = M20 * window.x().start();
638
639 // const_x0, const_y0 and const_z0 are the constant parts of x0, y0 and z0 during the row processing
640 float const_x0 = M01 * y_cur + M02;
641 float const_y0 = M11 * y_cur + M12;
642 float const_z0 = M21 * y_cur + M22;
643
644 // Perspective warp coordinates
645 float x0 = start_x0 + const_x0;
646 float y0 = start_y0 + const_y0;
647 float z0 = start_z0 + const_z0;
648
649 execute_window_loop(window, [&](const Coordinates & id)
650 {
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100651 // Check if we are processing a new row. If so, update the current processed row (y_cur), x0, y0 and z0
652 if((y_cur != id.y()) || (z_cur != id.z()) || (d3_cur != id[3]) || (d4_cur != id[4]) || (d5_cur != id[5]))
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100653 {
Isabella Gottardi40ff03b2017-08-10 16:44:42 +0100654 y_cur = id.y();
655 z_cur = id.z();
656 d3_cur = id[3];
657 d4_cur = id[4];
658 d5_cur = id[5];
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100659
660 const_x0 = M01 * y_cur + M02;
661 const_y0 = M11 * y_cur + M12;
662 const_z0 = M21 * y_cur + M22;
663
664 x0 = start_x0 + const_x0;
665 y0 = start_y0 + const_y0;
666 z0 = start_z0 + const_z0;
667 }
668
669 const float xn = x0 / z0;
670 const float yn = y0 / z0;
671
672 // Only load from (x0, y0) if the point is within the valid region.
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100673 if((min_y <= yn) && (yn < max_y) && (min_x <= xn) && (xn < max_x))
674 {
675 switch(interpolation)
676 {
677 case InterpolationPolicy::NEAREST_NEIGHBOR:
678 *out.ptr() = nearest_interpolation(in.ptr(), xn, yn, stride);
679 break;
680 case InterpolationPolicy::BILINEAR:
Georgios Pinitas583137c2017-08-31 18:12:42 +0100681 *out.ptr() = pixel_bilinear_c1(in.ptr(), stride, xn, yn);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100682 break;
683 default:
684 ARM_COMPUTE_ERROR("Interpolation not supported");
685 }
686 }
687 else
688 {
689 // Clamp coordinates
Isabella Gottardi62031532017-07-04 11:21:28 +0100690 const auto xi = clamp<int>(std::floor(xn), min_x, max_x - 1);
691 const auto yi = clamp<int>(std::floor(yn), min_y, max_y - 1);
692 switch(interpolation)
693 {
694 case InterpolationPolicy::NEAREST_NEIGHBOR:
695 *out.ptr() = *(in.ptr() + xi + yi * stride);
696 break;
697 case InterpolationPolicy::BILINEAR:
698 {
699 const auto xi_1 = clamp<int>(std::floor(xn + 1), min_x, max_x - 1);
700 const auto yi_1 = clamp<int>(std::floor(yn + 1), min_y, max_y - 1);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100701
Isabella Gottardi62031532017-07-04 11:21:28 +0100702 const float dx = xn - std::floor(xn);
703 const float dy = yn - std::floor(yn);
704 const float dx1 = 1.0f - dx;
705 const float dy1 = 1.0f - dy;
706
707 const float a00 = *(in.ptr() + xi + yi * stride);
708 const float a01 = *(in.ptr() + xi_1 + yi * stride);
709 const float a10 = *(in.ptr() + xi + yi_1 * stride);
710 const float a11 = *(in.ptr() + xi_1 + yi_1 * stride);
711
712 *out.ptr() = a00 * (dx1 * dy1) + a01 * (dx * dy1) + a10 * (dx1 * dy) + a11 * (dx * dy);
713 }
714 break;
715 default:
716 ARM_COMPUTE_ERROR("Interpolation not supported");
717 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100718 }
719
720 x0 += M00;
721 y0 += M10;
722 z0 += M20;
723 },
724 in, out);
725}
726
727template class arm_compute::NEWarpAffineKernel<InterpolationPolicy::NEAREST_NEIGHBOR>;
728template class arm_compute::NEWarpAffineKernel<InterpolationPolicy::BILINEAR>;
729template class arm_compute::NEWarpPerspectiveKernel<InterpolationPolicy::NEAREST_NEIGHBOR>;
730template class arm_compute::NEWarpPerspectiveKernel<InterpolationPolicy::BILINEAR>;