From 1fad27af728ac73f0635994abf042b6d9e7075c6 Mon Sep 17 00:00:00 2001 From: Sanghoon Lee Date: Thu, 5 Apr 2018 10:57:57 +0100 Subject: COMPMID-566: Implement reference and CL/NEON validation for ColorConvert (part 1) - Image to MultiImage will be in part 2 Change-Id: Id2f22c39fb41a78a360d20d2c3bdecd57cdfd152 Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/128321 Reviewed-by: Pablo Tello Tested-by: Jenkins --- tests/validation/reference/ColorConvertHelper.h | 343 ++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 tests/validation/reference/ColorConvertHelper.h (limited to 'tests/validation/reference/ColorConvertHelper.h') diff --git a/tests/validation/reference/ColorConvertHelper.h b/tests/validation/reference/ColorConvertHelper.h new file mode 100644 index 0000000000..4daa7025cb --- /dev/null +++ b/tests/validation/reference/ColorConvertHelper.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2017-2018 ARM Limited. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *asymm_int_mult + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, asymm_int_multDAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef __ARM_COMPUTE_TEST_VALIDATION_COLOR_CONVERT_H__ +#define __ARM_COMPUTE_TEST_VALIDATION_COLOR_CONVERT_H__ + +#include "Utils.h" + +namespace arm_compute +{ +namespace test +{ +namespace colorconvert_helper +{ +namespace detail +{ +constexpr float red_coef_bt709 = 1.5748F; +constexpr float green_coef_bt709 = -0.1873f; +constexpr float green_coef2_bt709 = -0.4681f; +constexpr float blue_coef_bt709 = 1.8556f; + +template +inline void yuyv_to_rgb_calculation(const SimpleTensor yvec, const SimpleTensor vvec, const SimpleTensor yyvec, const SimpleTensor uvec, SimpleTensor &dst) +{ + const int dst_width = dst.shape().x(); + const int dst_height = dst.shape().y(); + + for(int y = 0; y < dst_height; ++y) + { + int x_coord = 0; + for(int x = 0; x < dst_width; x += 2, x_coord++) + { + Coordinates dst_coord{ x, y }; + auto *dst_pixel = reinterpret_cast(dst(dst_coord)); + float result = 0.f; + + T border_value(0); + const int yvec_val = validation::tensor_elem_at(yvec, { x_coord, y }, BorderMode::CONSTANT, border_value); + const int vvec_val = validation::tensor_elem_at(vvec, { x_coord, y }, BorderMode::CONSTANT, border_value); + const int yyvec_val = validation::tensor_elem_at(yyvec, { x_coord, y }, BorderMode::CONSTANT, border_value); + const int uvec_val = validation::tensor_elem_at(uvec, { x_coord, y }, BorderMode::CONSTANT, border_value); + const float red = (vvec_val - 128) * red_coef_bt709; + const float green = (uvec_val - 128) * green_coef_bt709 + (vvec_val - 128) * green_coef2_bt709; + const float blue = (uvec_val - 128) * blue_coef_bt709; + + for(int channel_idx = 0; channel_idx < dst.num_channels(); ++channel_idx) + { + if(channel_idx == 0) + { + // Channel 'R' + result = yvec_val + red; + } + else if(channel_idx == 1) + { + // Channel 'G' + result = yvec_val + green; + } + else if(channel_idx == 2) + { + // Channel 'B' + result = yvec_val + blue; + } + else + { + // Channel 'A' + result = 255; + } + + if(result < 0) + { + result = 0; + } + else if(result > 255) + { + result = 255; + } + dst_pixel[channel_idx] = result; + } + + dst_coord.set(0, x + 1); + dst_pixel = reinterpret_cast(dst(dst_coord)); + for(int channel_idx = 0; channel_idx < dst.num_channels(); ++channel_idx) + { + if(channel_idx == 0) + { + // Channel 'R' + result = yyvec_val + red; + } + else if(channel_idx == 1) + { + // Channel 'G' + result = yyvec_val + green; + } + else if(channel_idx == 2) + { + // Channel 'B' + result = yyvec_val + blue; + } + else + { + // Channel 'A' + result = 255; + } + + if(result < 0) + { + result = 0; + } + else if(result > 255) + { + result = 255; + } + dst_pixel[channel_idx] = result; + } + } + } +} + +template +inline void colorconvert_rgb_to_rgbx(const SimpleTensor src, SimpleTensor &dst) +{ + for(int channel_idx = 0; channel_idx < dst.num_channels(); ++channel_idx) + { + const int width = dst.shape().x(); + const int height = dst.shape().y(); + + for(int y = 0; y < height; ++y) + { + for(int x = 0; x < width; ++x) + { + const Coordinates src_coord{ x, y }; + const Coordinates dst_coord{ x, y }; + + const auto *src_pixel = reinterpret_cast(src(src_coord)); + auto *dst_pixel = reinterpret_cast(dst(dst_coord)); + if(channel_idx == 3) + { + dst_pixel[channel_idx] = 255; + continue; + } + + dst_pixel[channel_idx] = src_pixel[channel_idx]; + } + } + } +} + +template +inline void colorconvert_rgbx_to_rgb(const SimpleTensor src, SimpleTensor &dst) +{ + for(int channel_idx = 0; channel_idx < dst.num_channels(); ++channel_idx) + { + const int width = dst.shape().x(); + const int height = dst.shape().y(); + + for(int y = 0; y < height; ++y) + { + for(int x = 0; x < width; ++x) + { + const Coordinates src_coord{ x, y }; + const Coordinates dst_coord{ x, y }; + + const auto *src_pixel = reinterpret_cast(src(src_coord)); + auto *dst_pixel = reinterpret_cast(dst(dst_coord)); + + dst_pixel[channel_idx] = src_pixel[channel_idx]; + } + } + } +} + +template +inline void colorconvert_yuyv_to_rgb(const SimpleTensor src, const Format format, SimpleTensor &dst) +{ + SimpleTensor yvec(TensorShape{ src.shape().x(), src.shape().y() }, Format::U8); + SimpleTensor uvec(TensorShape{ src.shape().x(), src.shape().y() }, Format::U8); + SimpleTensor yyvec(TensorShape{ src.shape().x(), src.shape().y() }, Format::U8); + SimpleTensor vvec(TensorShape{ src.shape().x(), src.shape().y() }, Format::U8); + + const int step_x = (Format::YUYV422 == format || Format::UYVY422 == format) ? 2 : 1; + + const int offset = (Format::YUYV422 == format) ? 0 : 1; + Coordinates elem_coord{ 0, 0 }; + + const int width = vvec.shape().x(); + const int height = vvec.shape().y(); + + for(int y = 0; y < height; ++y) + { + for(int x = 0; x < width; ++x) + { + const Coordinates src_coord{ x * step_x, y }; + const auto *src_pixel = reinterpret_cast(src(src_coord)); + auto *yvec_pixel = reinterpret_cast(yvec(elem_coord)); + auto *uvec_pixel = reinterpret_cast(uvec(elem_coord)); + auto *yyvec_pixel = reinterpret_cast(yyvec(elem_coord)); + auto *vvec_pixel = reinterpret_cast(vvec(elem_coord)); + yvec_pixel[x] = src_pixel[0 + offset]; + uvec_pixel[x] = src_pixel[1 - offset]; + yyvec_pixel[x] = src_pixel[2 + offset]; + vvec_pixel[x] = src_pixel[3 - offset]; + } + elem_coord.set(1, y + 1); + } + + yuyv_to_rgb_calculation(yvec, vvec, yyvec, uvec, dst); +} + +template +inline void colorconvert_iyuv_to_rgb(const TensorShape &shape, const std::vector> &tensor_planes, SimpleTensor &dst) +{ + SimpleTensor yvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + SimpleTensor uvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + SimpleTensor yyvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + SimpleTensor vvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + + Coordinates elem_coord{ 0, 0 }; + const int yvec_width = yvec.shape().x(); + const int yvec_height = yvec.shape().y(); + + for(int y = 0; y < yvec_height; ++y) + { + for(int x = 0; x < yvec_width; ++x) + { + const Coordinates src_coord{ x, y }; + const auto *src_pixel = reinterpret_cast(tensor_planes[0](src_coord)); + auto *yvec_pixel = reinterpret_cast(yvec(elem_coord)); + auto *yyvec_pixel = reinterpret_cast(yyvec(elem_coord)); + yvec_pixel[x] = src_pixel[x]; + yyvec_pixel[x] = src_pixel[x + 1]; + } + elem_coord.set(1, y + 1); + } + + const int uvec_width = uvec.shape().x(); + const int uvec_height = uvec.shape().y(); + + Coordinates top_elem_coord{ 0, 0 }; + Coordinates bottom_elem_coord{ 0, 1 }; + for(int y = 0; y < uvec_height; y += 2) + { + for(int x = 0; x < uvec_width; ++x) + { + const Coordinates src_coord{ x, y / 2 }; + const auto *src_pixel = reinterpret_cast(tensor_planes[1](src_coord)); + auto *uvec_pixel_top = reinterpret_cast(uvec(top_elem_coord)); + auto *vvec_pixel_top = reinterpret_cast(vvec(top_elem_coord)); + + auto *uvec_pixel_bottom = reinterpret_cast(uvec(bottom_elem_coord)); + auto *vvec_pixel_bottom = reinterpret_cast(vvec(bottom_elem_coord)); + uvec_pixel_top[x] = src_pixel[0]; + vvec_pixel_top[x] = src_pixel[0]; + uvec_pixel_bottom[x] = src_pixel[0]; + vvec_pixel_bottom[x] = src_pixel[0]; + } + top_elem_coord.set(1, y + 2); + bottom_elem_coord.set(1, top_elem_coord.y() + 1); + } + + yuyv_to_rgb_calculation(yvec, vvec, yyvec, uvec, dst); +} + +template +inline void colorconvert_nv12_to_rgb(const TensorShape &shape, const Format format, const std::vector> &tensor_planes, SimpleTensor &dst) +{ + SimpleTensor yvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + SimpleTensor uvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + SimpleTensor yyvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + SimpleTensor vvec(TensorShape{ tensor_planes[0].shape().x(), tensor_planes[0].shape().y() }, Format::U8); + + const int offset = (Format::NV12 == format) ? 0 : 1; + + Coordinates elem_coord{ 0, 0 }; + const int yvec_width = yvec.shape().x(); + const int yvec_height = yvec.shape().y(); + + for(int y = 0; y < yvec_height; ++y) + { + for(int x = 0; x < yvec_width; ++x) + { + const Coordinates src_coord{ x, y }; + const auto *src_pixel = reinterpret_cast(tensor_planes[0](src_coord)); + auto *yvec_pixel = reinterpret_cast(yvec(elem_coord)); + auto *yyvec_pixel = reinterpret_cast(yyvec(elem_coord)); + yvec_pixel[x] = src_pixel[x]; + yyvec_pixel[x] = src_pixel[x + 1]; + } + elem_coord.set(1, y + 1); + } + + const int uvec_width = uvec.shape().x(); + const int uvec_height = uvec.shape().y(); + + Coordinates top_elem_coord{ 0, 0 }; + Coordinates bottom_elem_coord{ 0, 1 }; + for(int y = 0; y < uvec_height; y += 2) + { + for(int x = 0; x < uvec_width; ++x) + { + const Coordinates src_coord{ x, y / 2 }; + const auto *src_pixel = reinterpret_cast(tensor_planes[1](src_coord)); + auto *uvec_pixel_top = reinterpret_cast(uvec(top_elem_coord)); + auto *vvec_pixel_top = reinterpret_cast(vvec(top_elem_coord)); + + auto *uvec_pixel_bottom = reinterpret_cast(uvec(bottom_elem_coord)); + auto *vvec_pixel_bottom = reinterpret_cast(vvec(bottom_elem_coord)); + uvec_pixel_top[x] = src_pixel[0 + offset]; + vvec_pixel_top[x] = src_pixel[1 - offset]; + uvec_pixel_bottom[x] = src_pixel[0 + offset]; + vvec_pixel_bottom[x] = src_pixel[1 - offset]; + } + top_elem_coord.set(1, y + 2); + bottom_elem_coord.set(1, top_elem_coord.y() + 1); + } + + yuyv_to_rgb_calculation(yvec, vvec, yyvec, uvec, dst); +} + +} // namespace detail +} // color_convert_helper +} // namespace test +} // namespace arm_compute +#endif /*__ARM_COMPUTE_TEST_VALIDATION_COLOR_CONVERT_H__ */ -- cgit v1.2.1