blob: 1c7c1f9763b763595f1653c3c9434cc3332809e2 [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Michele Di Giorgio19289042021-02-03 16:05:00 +00002 * Copyright (c) 2016-2021 Arm Limited.
Anthony Barbier6ff3b192017-09-04 18:44:23 +01003 *
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 */
Michalis Spyrouebcebf12020-10-21 00:04:14 +010024#include "src/core/NEON/kernels/NEFillBorderKernel.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010025
26#include "arm_compute/core/Error.h"
27#include "arm_compute/core/Helpers.h"
28#include "arm_compute/core/ITensor.h"
29#include "arm_compute/core/TensorInfo.h"
Georgios Pinitas583137c2017-08-31 18:12:42 +010030#include "arm_compute/core/Types.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010031#include "arm_compute/core/Validate.h"
32#include "arm_compute/core/Window.h"
Michalis Spyrouebcebf12020-10-21 00:04:14 +010033#include "src/core/NEON/kernels/NEFillBorderKernel.h"
Sang-Hoon Park68dd25f2020-10-19 16:00:11 +010034#include "src/core/helpers/WindowHelpers.h"
Anthony Barbier6ff3b192017-09-04 18:44:23 +010035
Michalis Spyrou95abfdd2018-11-28 14:59:47 +000036namespace arm_compute
37{
Pablo Tello62eeae42017-08-09 16:33:49 +010038namespace
39{
Michalis Spyrou95abfdd2018-11-28 14:59:47 +000040inline void fill_constant_value_single_channel_special(ITensor *tensor, const Window &window, unsigned int right, unsigned int bottom, const PixelValue &constant_border_value)
Pablo Tello62eeae42017-08-09 16:33:49 +010041{
42 float border_value;
43 constant_border_value.get(border_value);
44 uint8_t *const start_valid_region = tensor->ptr_to_element(tensor->info()->valid_region().anchor);
Georgios Pinitas0223a782017-12-12 11:44:44 +000045 const size_t width = tensor->info()->valid_region().shape[0];
46 const size_t height = tensor->info()->valid_region().shape[1];
Pablo Tello62eeae42017-08-09 16:33:49 +010047 const int stridey = tensor->info()->strides_in_bytes()[1];
48
49 // Left and right border
50 Window vertical(window);
51 vertical.set(Window::DimY, Window::Dimension(0, height, 1));
52
53 Iterator vertical_it(tensor, vertical);
54
55 execute_window_loop(vertical, [&](const Coordinates &)
56 {
57 const auto row_start = reinterpret_cast<float *>(start_valid_region + vertical_it.offset());
58
59 // Fill left and right borders
60 *(row_start - 1) = border_value;
61 std::fill_n(row_start + width, right, border_value);
62 },
63 vertical_it);
64
65 // Top and bottom border
66 Iterator plane_it(tensor, window);
67
68 // Iterate over all XY planes
69 execute_window_loop(window, [&](const Coordinates &)
70 {
71 uint8_t *base_addr = start_valid_region + plane_it.offset();
72 // Top border
73 const auto row_start = reinterpret_cast<float *>(base_addr - stridey);
74 // Fill top rows including left/right borders
75 std::fill_n(row_start - 1, 1 + width + right, border_value);
76
77 // Bottom border
78 const unsigned low_border_size = height + bottom;
79 for(unsigned int i = height; i < low_border_size; ++i)
80 {
81 const auto row_start = reinterpret_cast<float *>(base_addr + i * stridey);
82
83 // Fill bottom rows including left/right borders
84 std::fill_n(row_start - 1, 1 + width + right, border_value);
85 }
86 },
87 plane_it);
88}
89} // namespace
90
Anthony Barbier6ff3b192017-09-04 18:44:23 +010091NEFillBorderKernel::NEFillBorderKernel()
Michalis Spyrou490bf2e2017-09-29 11:24:55 +010092 : _tensor(nullptr), _border_size(0), _mode(BorderMode::UNDEFINED), _constant_border_value(static_cast<float>(0.f))
Anthony Barbier6ff3b192017-09-04 18:44:23 +010093{
94}
95
96void NEFillBorderKernel::configure(ITensor *tensor, BorderSize border_size, BorderMode border_mode, const PixelValue &constant_border_value)
97{
Georgios Pinitas33843562019-12-10 13:33:18 +000098 ARM_COMPUTE_ERROR_ON_NULLPTR(tensor);
Michele Di Giorgio19289042021-02-03 16:05:00 +000099 _tensor = tensor;
100 configure(tensor->info(), border_size, border_mode, constant_border_value);
101}
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100102
Michele Di Giorgio19289042021-02-03 16:05:00 +0000103void NEFillBorderKernel::configure(ITensorInfo *tensor, BorderSize border_size, BorderMode border_mode, const PixelValue &constant_border_value)
104{
105 ARM_COMPUTE_ERROR_ON_NULLPTR(tensor);
Michele Di Giorgio33f41fa2021-03-09 14:09:08 +0000106 //Note: ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input) is not needed here as this kernel doesn't use CPU FP16 instructions.
Michele Di Giorgio19289042021-02-03 16:05:00 +0000107 ARM_COMPUTE_ERROR_ON(tensor->data_type() == DataType::UNKNOWN);
108
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100109 _border_size = border_size;
110 _mode = border_mode;
111 _constant_border_value = constant_border_value;
112
Michele Di Giorgio19289042021-02-03 16:05:00 +0000113 _border_size.limit(tensor->padding());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100114
115 Window win;
116 win.set(Window::DimX, Window::Dimension(0, 1, 1));
117 win.set(Window::DimY, Window::Dimension(0, 1, 1));
Michele Di Giorgio19289042021-02-03 16:05:00 +0000118 win.use_tensor_dimensions(tensor->tensor_shape(), Window::DimZ);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100119 INEKernel::configure(win);
120}
121
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100122void NEFillBorderKernel::run(const Window &window, const ThreadInfo &info)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100123{
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100124 ARM_COMPUTE_UNUSED(info);
125
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100126 // If there is no border: early exit
127 if(_border_size.empty())
128 {
129 return;
130 }
131
132 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
133 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
134
135 switch(_mode)
136 {
137 case BorderMode::CONSTANT:
138 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000139 if(_border_size.left == 1 && _border_size.top == 1 && _tensor->info()->data_type() == DataType::F32)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100140 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000141 fill_constant_value_single_channel_special(_tensor, window, _border_size.right, _border_size.bottom, _constant_border_value);
142 }
143 else
144 {
145 fill_constant_value_single_channel(window);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100146 }
147 break;
148 }
149 case BorderMode::REPLICATE:
150 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000151 fill_replicate_single_channel(window);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100152 break;
153 }
154 case BorderMode::UNDEFINED:
155 break; // Nothing to do here
156 default:
157 ARM_COMPUTE_ERROR("Unknown border mode");
158 }
159}
160
Michele Di Giorgio19289042021-02-03 16:05:00 +0000161void NEFillBorderKernel::run_op(ITensorPack &tensors, const Window &window, const ThreadInfo &info)
162{
163 _tensor = tensors.get_tensor(TensorType::ACL_SRC_DST);
164 run(window, info);
165}
166
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100167void NEFillBorderKernel::fill_replicate_single_channel(const Window &window)
168{
169 uint8_t *const start_valid_region = _tensor->ptr_to_element(_tensor->info()->valid_region().anchor);
Georgios Pinitas424eb5d2017-12-06 19:49:38 +0000170 const size_t width = _tensor->info()->valid_region().shape[0];
171 const size_t height = _tensor->info()->valid_region().shape[1];
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000172 const size_t element_size = _tensor->info()->element_size();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100173 // Left and right border
174 Window vertical(window);
175 vertical.set(Window::DimY, Window::Dimension(0, height, 1));
176
177 Iterator vertical_it(_tensor, vertical);
178
Michalis Spyroubcfd09a2019-05-01 13:03:59 +0100179 execute_window_loop(vertical, [&](const Coordinates &)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100180 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000181 uint8_t *base_addr = start_valid_region + vertical_it.offset();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100182 // Fill left and right borders
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000183 for(unsigned int i = 0; i < _border_size.left; ++i)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100184 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000185 std::memcpy(base_addr + static_cast<int>(i - _border_size.left) * element_size, vertical_it.ptr(), element_size);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100186 }
187
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000188 for(unsigned int i = 0; i < _border_size.right; ++i)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100189 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000190 std::memcpy(base_addr + (width + i) * element_size, vertical_it.ptr() + (width - 1) * element_size, element_size);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100191 }
192 },
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100193 vertical_it);
194
195 // Top and bottom border
196 Iterator plane_it(_tensor, window);
197
198 // Iterate over all XY planes
Michalis Spyroubcfd09a2019-05-01 13:03:59 +0100199 execute_window_loop(window, [&](const Coordinates &)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100200 {
Pablo Tello62eeae42017-08-09 16:33:49 +0100201 uint8_t *base_addr = start_valid_region + plane_it.offset();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100202 // Top border
203 for(int i = -_border_size.top; i < 0; ++i)
204 {
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000205 // Copy top rows including left/right borders
Michalis Spyrou7c60c992019-10-10 14:33:47 +0100206 std::memcpy(base_addr + i * static_cast<int>(_tensor->info()->strides_in_bytes()[1]) - _border_size.left * element_size,
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000207 base_addr - _border_size.left * element_size, (_border_size.left + width + _border_size.right) * element_size);
208 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100209
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000210 // Bottom border
211 for(unsigned int i = height; i < height + _border_size.bottom; ++i)
212 {
213 // Copy bottom rows including left/right borders
214 std::memcpy(base_addr + i * _tensor->info()->strides_in_bytes()[1] - _border_size.left * element_size,
215 base_addr + (height - 1) * _tensor->info()->strides_in_bytes()[1] - _border_size.left * element_size, (_border_size.left + width + _border_size.right) * element_size);
216 }
217 },
218 plane_it);
219}
220
221void NEFillBorderKernel::fill_constant_value_single_channel(const Window &window)
222{
223 uint8_t *const start_valid_region = _tensor->ptr_to_element(_tensor->info()->valid_region().anchor);
224 const size_t width = _tensor->info()->valid_region().shape[0];
225 const size_t height = _tensor->info()->valid_region().shape[1];
226 const int stridey = _tensor->info()->strides_in_bytes()[1];
227 const size_t element_size = _tensor->info()->element_size();
228
229 // Left and right border
230 Window vertical(window);
231 vertical.set(Window::DimY, Window::Dimension(0, height, 1));
232
233 Iterator vertical_it(_tensor, vertical);
234
Michalis Spyroubcfd09a2019-05-01 13:03:59 +0100235 execute_window_loop(vertical, [&](const Coordinates &)
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000236 {
237 uint8_t *base_addr = start_valid_region + vertical_it.offset();
238 // Fill left and right borders
239 for(unsigned int i = 0; i < _border_size.left; ++i)
240 {
241 std::memcpy(base_addr + static_cast<int>(i - _border_size.left) * element_size, &_constant_border_value, element_size);
242 }
243
244 for(unsigned int i = 0; i < _border_size.right; ++i)
245 {
246 std::memcpy(base_addr + (width + i) * element_size, &_constant_border_value, element_size);
247 }
248 },
249 vertical_it);
250
251 // Top and bottom border
252 Iterator plane_it(_tensor, window);
253
254 // Iterate over all XY planes
Michalis Spyroubcfd09a2019-05-01 13:03:59 +0100255 execute_window_loop(window, [&](const Coordinates &)
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000256 {
257 uint8_t *base_addr = start_valid_region + plane_it.offset();
258 // Top border
259 for(int i = -_border_size.top; i < 0; ++i)
260 {
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100261 // Fill top rows including left/right borders
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000262 for(unsigned int j = 0; j < (_border_size.left + width + _border_size.right); ++j)
263 {
264 std::memcpy(base_addr + i * stridey + static_cast<int>(j - _border_size.left) * element_size, &_constant_border_value, element_size);
265 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100266 }
267
268 // Bottom border
Pablo Tello62eeae42017-08-09 16:33:49 +0100269 const unsigned low_border_size = height + _border_size.bottom;
270 for(unsigned int i = height; i < low_border_size; ++i)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100271 {
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100272 // Fill bottom rows including left/right borders
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000273 for(unsigned int j = 0; j < (_border_size.left + width + _border_size.right); ++j)
274 {
275 std::memcpy(base_addr + i * stridey + static_cast<int>(j - _border_size.left) * element_size, &_constant_border_value, element_size);
276 }
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100277 }
278 },
279 plane_it);
280}
Michalis Spyrou95abfdd2018-11-28 14:59:47 +0000281} // namespace arm_compute