2 * Copyright (c) 2016, 2017 ARM Limited.
4 * SPDX-License-Identifier: MIT
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:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
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
24 #include "arm_compute/core/NEON/kernels/NEColorConvertKernel.h"
26 #include "arm_compute/core/Error.h"
27 #include "arm_compute/core/Helpers.h"
28 #include "arm_compute/core/IAccessWindow.h"
29 #include "arm_compute/core/IMultiImage.h"
30 #include "arm_compute/core/ITensor.h"
31 #include "arm_compute/core/MultiImageInfo.h"
32 #include "arm_compute/core/NEON/NEColorConvertHelper.inl"
33 #include "arm_compute/core/TensorInfo.h"
34 #include "arm_compute/core/Types.h"
35 #include "arm_compute/core/Validate.h"
36 #include "arm_compute/core/Window.h"
38 using namespace arm_compute;
40 NEColorConvertKernel::NEColorConvertKernel()
41 : _input(nullptr), _output(nullptr), _func(nullptr)
45 void NEColorConvertKernel::configure(const ITensor *input, ITensor *output)
47 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
49 set_shape_if_empty(*output->info(), input->info()->tensor_shape());
51 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input, output);
53 unsigned int num_elems_processed_per_iteration = 0;
55 switch(input->info()->format())
57 case Format::RGBA8888:
59 switch(output->info()->format())
62 _func = colorconvert_rgbx_to_rgb;
63 num_elems_processed_per_iteration = 16;
66 ARM_COMPUTE_ERROR("Not supported");
73 switch(output->info()->format())
76 _func = colorconvert_yuyv_to_rgb<false, false>;
77 num_elems_processed_per_iteration = 32;
79 case Format::RGBA8888:
80 _func = colorconvert_yuyv_to_rgb<false, true>;
81 num_elems_processed_per_iteration = 32;
84 ARM_COMPUTE_ERROR("Not supported");
91 switch(output->info()->format())
94 _func = colorconvert_yuyv_to_rgb<true, false>;
95 num_elems_processed_per_iteration = 32;
97 case Format::RGBA8888:
98 _func = colorconvert_yuyv_to_rgb<true, true>;
99 num_elems_processed_per_iteration = 32;
102 ARM_COMPUTE_ERROR("Not supported");
109 switch(output->info()->format())
111 case Format::RGBA8888:
112 _func = colorconvert_rgb_to_rgbx;
113 num_elems_processed_per_iteration = 16;
116 ARM_COMPUTE_ERROR("Not supported");
122 ARM_COMPUTE_ERROR("Not supported");
129 // Configure kernel window
130 Window win = calculate_max_window(*input->info(), Steps(num_elems_processed_per_iteration));
131 AccessWindowHorizontal input_access(input->info(), 0, num_elems_processed_per_iteration);
132 AccessWindowHorizontal output_access(output->info(), 0, num_elems_processed_per_iteration);
134 update_window_and_padding(win, input_access, output_access);
136 output_access.set_valid_region(win, input->info()->valid_region());
138 INEKernel::configure(win);
141 void NEColorConvertKernel::configure(const IMultiImage *input, IImage *output)
143 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
144 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(output);
146 set_shape_if_empty(*output->info(), input->plane(0)->info()->tensor_shape());
148 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input->plane(0), output);
150 unsigned int num_elems_processed_per_iteration = 0;
152 switch(input->info()->format())
156 switch(output->info()->format())
159 _func = colorconvert_nv12_to_rgb<true, false>;
160 num_elems_processed_per_iteration = 32;
162 case Format::RGBA8888:
163 _func = colorconvert_nv12_to_rgb<true, true>;
164 num_elems_processed_per_iteration = 32;
167 ARM_COMPUTE_ERROR("Not supported");
174 switch(output->info()->format())
177 _func = colorconvert_nv12_to_rgb<false, false>;
178 num_elems_processed_per_iteration = 32;
180 case Format::RGBA8888:
181 _func = colorconvert_nv12_to_rgb<false, true>;
182 num_elems_processed_per_iteration = 32;
185 ARM_COMPUTE_ERROR("Not supported");
192 switch(output->info()->format())
195 _func = colorconvert_iyuv_to_rgb<false>;
196 num_elems_processed_per_iteration = 32;
198 case Format::RGBA8888:
199 _func = colorconvert_iyuv_to_rgb<true>;
200 num_elems_processed_per_iteration = 32;
203 ARM_COMPUTE_ERROR("Not supported");
209 ARM_COMPUTE_ERROR("Not supported");
216 // Configure kernel window
217 Window win = calculate_max_window(*output->info(), Steps(num_elems_processed_per_iteration));
218 win.set_dimension_step(Window::DimY, 2);
220 unsigned int input_plane_count = 3;
222 if(input->info()->format() == Format::NV12 || input->info()->format() == Format::NV21)
224 input_plane_count = 2;
227 AccessWindowHorizontal input0_access(input->plane(0)->info(), 0, num_elems_processed_per_iteration);
228 AccessWindowRectangle input1_access(input->plane(1)->info(), 0, 0, num_elems_processed_per_iteration, 1, 0.5f, 0.5f);
229 AccessWindowRectangle input2_access(input_plane_count == 2 ? nullptr : input->plane(2)->info(), 0, 0, num_elems_processed_per_iteration, 1, 0.5f, 0.5f);
230 AccessWindowHorizontal output_access(output->info(), 0, num_elems_processed_per_iteration);
232 update_window_and_padding(win,
233 input0_access, input1_access, input2_access,
236 ValidRegion intersect_region = intersect_valid_regions(input->plane(0)->info()->valid_region(),
237 input->plane(1)->info()->valid_region());
239 if(input_plane_count == 3)
241 intersect_region = intersect_valid_regions(intersect_region, input->plane(2)->info()->valid_region());
244 output_access.set_valid_region(win, intersect_region);
246 INEKernel::configure(win);
249 void NEColorConvertKernel::configure(const IImage *input, IMultiImage *output)
251 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
252 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(input);
254 set_shape_if_empty(*output->plane(0)->info(), input->info()->tensor_shape());
256 switch(output->info()->format())
260 TensorShape subsampled_shape = input->info()->tensor_shape();
261 subsampled_shape.set(0, subsampled_shape[0] / 2);
262 subsampled_shape.set(1, subsampled_shape[1] / 2);
264 set_shape_if_empty(*output->plane(1)->info(), subsampled_shape);
266 ARM_COMPUTE_ERROR_ON_MISMATCHING_DIMENSIONS(subsampled_shape, output->plane(1)->info()->tensor_shape());
271 TensorShape subsampled_shape = input->info()->tensor_shape();
272 subsampled_shape.set(0, subsampled_shape[0] / 2);
273 subsampled_shape.set(1, subsampled_shape[1] / 2);
275 set_shape_if_empty(*output->plane(1)->info(), subsampled_shape);
276 set_shape_if_empty(*output->plane(2)->info(), subsampled_shape);
278 ARM_COMPUTE_ERROR_ON_MISMATCHING_DIMENSIONS(subsampled_shape, output->plane(1)->info()->tensor_shape());
279 ARM_COMPUTE_ERROR_ON_MISMATCHING_DIMENSIONS(subsampled_shape, output->plane(2)->info()->tensor_shape());
283 set_shape_if_empty(*output->plane(1)->info(), input->info()->tensor_shape());
284 set_shape_if_empty(*output->plane(2)->info(), input->info()->tensor_shape());
286 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input, output->plane(1));
287 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input, output->plane(2));
290 ARM_COMPUTE_ERROR("Not supported");
293 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input, output->plane(0));
295 unsigned int num_elems_processed_per_iteration = 0;
297 switch(input->info()->format())
301 switch(output->info()->format())
304 _func = colorconvert_rgb_to_nv12<false>;
305 num_elems_processed_per_iteration = 16;
308 _func = colorconvert_rgb_to_iyuv<false>;
309 num_elems_processed_per_iteration = 16;
312 _func = colorconvert_rgb_to_yuv4<false>;
313 num_elems_processed_per_iteration = 16;
316 ARM_COMPUTE_ERROR("Not supported");
321 case Format::RGBA8888:
323 switch(output->info()->format())
326 _func = colorconvert_rgb_to_nv12<true>;
327 num_elems_processed_per_iteration = 16;
330 _func = colorconvert_rgb_to_iyuv<true>;
331 num_elems_processed_per_iteration = 16;
334 _func = colorconvert_rgb_to_yuv4<true>;
335 num_elems_processed_per_iteration = 16;
338 ARM_COMPUTE_ERROR("Not supported");
343 case Format::UYVY422:
345 switch(output->info()->format())
348 _func = colorconvert_yuyv_to_nv12<false>;
349 num_elems_processed_per_iteration = 32;
352 _func = colorconvert_yuyv_to_iyuv<false>;
353 num_elems_processed_per_iteration = 32;
356 ARM_COMPUTE_ERROR("Not supported");
361 case Format::YUYV422:
363 switch(output->info()->format())
366 _func = colorconvert_yuyv_to_nv12<true>;
367 num_elems_processed_per_iteration = 32;
370 _func = colorconvert_yuyv_to_iyuv<true>;
371 num_elems_processed_per_iteration = 32;
374 ARM_COMPUTE_ERROR("Not supported");
380 ARM_COMPUTE_ERROR("Not supported");
387 // Configure kernel window
388 Window win = calculate_max_window(*input->info(), Steps(num_elems_processed_per_iteration));
390 float sub_sampling = 1.f;
392 if((input->info()->format() != Format::RGB888 || output->info()->format() != Format::YUV444) && (input->info()->format() != Format::RGBA8888 || output->info()->format() != Format::YUV444))
394 win.set_dimension_step(Window::DimY, 2);
398 unsigned int output_plane_count = 3;
400 if(output->info()->format() == Format::NV12 || output->info()->format() == Format::NV21)
402 output_plane_count = 2;
405 AccessWindowHorizontal output0_access(output->plane(0)->info(), 0, num_elems_processed_per_iteration);
406 AccessWindowRectangle output1_access(output->plane(1)->info(), 0, 0, num_elems_processed_per_iteration, 1, sub_sampling, sub_sampling);
407 AccessWindowRectangle output2_access(output_plane_count == 2 ? nullptr : output->plane(2)->info(), 0, 0, num_elems_processed_per_iteration, 1, sub_sampling, sub_sampling);
409 update_window_and_padding(win,
410 AccessWindowHorizontal(input->info(), 0, num_elems_processed_per_iteration),
415 output0_access.set_valid_region(win, input->info()->valid_region());
416 output1_access.set_valid_region(win, input->info()->valid_region());
417 output2_access.set_valid_region(win, input->info()->valid_region());
419 INEKernel::configure(win);
422 void NEColorConvertKernel::configure(const IMultiImage *input, IMultiImage *output)
424 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
425 ARM_COMPUTE_ERROR_ON(input == output);
427 set_shape_if_empty(*output->plane(0)->info(), input->plane(0)->info()->tensor_shape());
429 switch(output->info()->format())
433 TensorShape subsampled_shape = input->plane(0)->info()->tensor_shape();
434 subsampled_shape.set(0, subsampled_shape[0] / 2);
435 subsampled_shape.set(1, subsampled_shape[1] / 2);
437 set_shape_if_empty(*output->plane(1)->info(), subsampled_shape);
439 ARM_COMPUTE_ERROR_ON_MISMATCHING_DIMENSIONS(subsampled_shape, output->plane(1)->info()->tensor_shape());
444 TensorShape subsampled_shape = input->plane(0)->info()->tensor_shape();
445 subsampled_shape.set(0, subsampled_shape[0] / 2);
446 subsampled_shape.set(1, subsampled_shape[1] / 2);
448 set_shape_if_empty(*output->plane(1)->info(), subsampled_shape);
449 set_shape_if_empty(*output->plane(2)->info(), subsampled_shape);
451 ARM_COMPUTE_ERROR_ON_MISMATCHING_DIMENSIONS(subsampled_shape, output->plane(1)->info()->tensor_shape());
452 ARM_COMPUTE_ERROR_ON_MISMATCHING_DIMENSIONS(subsampled_shape, output->plane(2)->info()->tensor_shape());
456 set_shape_if_empty(*output->plane(1)->info(), input->plane(0)->info()->tensor_shape());
457 set_shape_if_empty(*output->plane(2)->info(), input->plane(0)->info()->tensor_shape());
459 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input->plane(0), output->plane(1));
460 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input->plane(0), output->plane(2));
463 ARM_COMPUTE_ERROR("Not supported");
466 ARM_COMPUTE_ERROR_ON_MISMATCHING_SHAPES(input->plane(0), output->plane(0));
468 switch(input->info()->format())
472 switch(output->info()->format())
475 _func = colorconvert_nv12_to_iyuv<true>;
478 _func = colorconvert_nv12_to_yuv4<true>;
481 ARM_COMPUTE_ERROR("Not supported");
488 switch(output->info()->format())
491 _func = colorconvert_nv12_to_iyuv<false>;
494 _func = colorconvert_nv12_to_yuv4<false>;
497 ARM_COMPUTE_ERROR("Not supported");
504 switch(output->info()->format())
507 _func = colorconvert_iyuv_to_nv12;
510 _func = colorconvert_iyuv_to_yuv4;
513 ARM_COMPUTE_ERROR("Not supported");
519 ARM_COMPUTE_ERROR("Not supported");
526 constexpr unsigned int num_elems_processed_per_iteration = 32;
527 constexpr float input_sub_sampling = 0.5f;
528 const float output_sub_sampling = output->info()->format() == Format::YUV444 ? 1.f : 0.5f;
530 // Configure kernel window
531 Window win = calculate_max_window(*input->plane(0)->info(), Steps(num_elems_processed_per_iteration));
532 win.set_dimension_step(Window::DimY, 2);
534 unsigned int input_plane_count = 3;
536 if(input->info()->format() == Format::NV12 || input->info()->format() == Format::NV21)
538 input_plane_count = 2;
541 unsigned int output_plane_count = 3;
543 if(output->info()->format() == Format::NV12 || output->info()->format() == Format::NV21)
545 output_plane_count = 2;
548 AccessWindowHorizontal output0_access(output->plane(0)->info(), 0, num_elems_processed_per_iteration);
549 AccessWindowRectangle output1_access(output->plane(1)->info(), 0, 0, num_elems_processed_per_iteration, 1, output_sub_sampling, output_sub_sampling);
550 AccessWindowRectangle output2_access(output_plane_count == 2 ? nullptr : output->plane(2)->info(), 0, 0, num_elems_processed_per_iteration, 1, output_sub_sampling, output_sub_sampling);
552 update_window_and_padding(win,
553 AccessWindowHorizontal(input->plane(0)->info(), 0, num_elems_processed_per_iteration),
554 AccessWindowRectangle(input->plane(1)->info(), 0, 0, num_elems_processed_per_iteration, 1, input_sub_sampling, input_sub_sampling),
555 AccessWindowRectangle(input_plane_count == 2 ? nullptr : input->plane(2)->info(), 0, 0, num_elems_processed_per_iteration, 1, input_sub_sampling, input_sub_sampling),
560 ValidRegion intersect_region = intersect_valid_regions(input->plane(0)->info()->valid_region(),
561 input->plane(1)->info()->valid_region());
563 if(input_plane_count == 3)
565 intersect_region = intersect_valid_regions(intersect_region, input->plane(2)->info()->valid_region());
568 output0_access.set_valid_region(win, intersect_region);
569 output1_access.set_valid_region(win, intersect_region);
570 output2_access.set_valid_region(win, intersect_region);
572 INEKernel::configure(win);
575 void NEColorConvertKernel::run(const Window &window, const ThreadInfo &info)
577 ARM_COMPUTE_UNUSED(info);
578 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
579 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
580 ARM_COMPUTE_ERROR_ON(_func == nullptr);
582 (*_func)(_input, _output, window);