COMPMID-566: Implement reference and CL/NEON validation for ColorConvert (part 2)

Change-Id: I4371c4b6403f55ea7d7baf39df91a45f45b21d5a
Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/137975
Tested-by: Jenkins <bsgcomp@arm.com>
Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
diff --git a/tests/validation/reference/ColorConvertHelper.h b/tests/validation/reference/ColorConvertHelper.h
index b4255a0..ee44668 100644
--- a/tests/validation/reference/ColorConvertHelper.h
+++ b/tests/validation/reference/ColorConvertHelper.h
@@ -39,6 +39,87 @@
 constexpr float green_coef2_bt709 = -0.4681f;
 constexpr float blue_coef_bt709   = 1.8556f;
 
+constexpr float rgb2yuv_bt709_kr = 0.2126f;
+constexpr float rgb2yuv_bt709_kb = 0.0722f;
+// K_g = 1 - K_r - K_b
+constexpr float rgb2yuv_bt709_kg = 0.7152f;
+// C_u = 1 / (2 * (1 - K_b))
+constexpr float rgb2yuv_bt709_cu = 0.5389f;
+// C_v = 1 / (2 * (1 - K_r))
+constexpr float rgb2yuv_bt709_cv = 0.6350f;
+
+template <typename T>
+inline void store_rgb_from_src(const SimpleTensor<T> src, SimpleTensor<T> &rvec, SimpleTensor<T> &gvec, SimpleTensor<T> &bvec)
+{
+    int width  = src.shape().x();
+    int height = src.shape().y();
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; ++x)
+        {
+            const Coordinates src_coord{ x, y };
+            const Coordinates vec_coord{ x, y };
+
+            const auto *src_pixel  = reinterpret_cast<const T *>(src(src_coord));
+            auto       *rvec_pixel = reinterpret_cast<T *>(rvec(vec_coord));
+            auto       *gvec_pixel = reinterpret_cast<T *>(gvec(vec_coord));
+            auto       *bvec_pixel = reinterpret_cast<T *>(bvec(vec_coord));
+
+            rvec_pixel[0] = src_pixel[0];
+            gvec_pixel[0] = src_pixel[1];
+            bvec_pixel[0] = src_pixel[2];
+        }
+    }
+}
+
+template <typename T>
+inline void rgb_to_yuv_calculation(const SimpleTensor<T> rvec, const SimpleTensor<T> gvec, const SimpleTensor<T> bvec, SimpleTensor<T> &yvec, SimpleTensor<T> &uvec_top, SimpleTensor<T> &uvec_bottom,
+                                   SimpleTensor<T> &vvec_top, SimpleTensor<T> &vvec_bottom)
+{
+    int width  = rvec.shape().x();
+    int height = rvec.shape().y();
+
+    int         uvec_coord_x = 0;
+    int         uvec_coord_y = 0;
+    Coordinates uvec_coord{ uvec_coord_x, uvec_coord_y };
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x += 2)
+        {
+            Coordinates coord{ x, y };
+            auto       *yvec_pixel        = reinterpret_cast<T *>(yvec(coord));
+            auto       *uvec_top_pixel    = reinterpret_cast<T *>(uvec_top(uvec_coord));
+            auto       *uvec_bottom_pixel = reinterpret_cast<T *>(uvec_bottom(uvec_coord));
+            auto       *vvec_top_pixel    = reinterpret_cast<T *>(vvec_top(uvec_coord));
+            auto       *vvec_bottom_pixel = reinterpret_cast<T *>(vvec_bottom(uvec_coord));
+
+            T     border_value(0);
+            int   rvec_val = validation::tensor_elem_at(rvec, coord, BorderMode::CONSTANT, border_value);
+            int   gvec_val = validation::tensor_elem_at(gvec, coord, BorderMode::CONSTANT, border_value);
+            int   bvec_val = validation::tensor_elem_at(bvec, coord, BorderMode::CONSTANT, border_value);
+            float result   = rvec_val * rgb2yuv_bt709_kr + gvec_val * rgb2yuv_bt709_kg + bvec_val * rgb2yuv_bt709_kb;
+
+            yvec_pixel[0]     = result;
+            uvec_top_pixel[0] = (bvec_val - result) * rgb2yuv_bt709_cu + 128.f;
+            vvec_top_pixel[0] = (rvec_val - result) * rgb2yuv_bt709_cv + 128.f;
+
+            coord.set(0, x + 1);
+            rvec_val = validation::tensor_elem_at(rvec, coord, BorderMode::CONSTANT, border_value);
+            gvec_val = validation::tensor_elem_at(gvec, coord, BorderMode::CONSTANT, border_value);
+            bvec_val = validation::tensor_elem_at(bvec, coord, BorderMode::CONSTANT, border_value);
+            result   = rvec_val * rgb2yuv_bt709_kr + gvec_val * rgb2yuv_bt709_kg + bvec_val * rgb2yuv_bt709_kb;
+
+            yvec_pixel[1]        = result;
+            uvec_bottom_pixel[0] = (bvec_val - result) * rgb2yuv_bt709_cu + 128.f;
+            vvec_bottom_pixel[0] = (rvec_val - result) * rgb2yuv_bt709_cv + 128.f;
+
+            uvec_coord.set(0, ++uvec_coord_x);
+        }
+    }
+}
+
 template <typename T>
 inline void yuyv_to_rgb_calculation(const SimpleTensor<T> yvec, const SimpleTensor<T> vvec, const SimpleTensor<T> yyvec, const SimpleTensor<T> uvec, SimpleTensor<T> &dst)
 {
@@ -59,9 +140,9 @@
             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;
+            const float red       = (vvec_val - 128.f) * red_coef_bt709;
+            const float green     = (uvec_val - 128.f) * green_coef_bt709 + (vvec_val - 128.f) * green_coef2_bt709;
+            const float blue      = (uvec_val - 128.f) * blue_coef_bt709;
 
             for(int channel_idx = 0; channel_idx < dst.num_channels(); ++channel_idx)
             {
@@ -261,16 +342,17 @@
         for(int x = 0; x < uvec_width; ++x)
         {
             const Coordinates src_coord{ x, y / 2 };
-            const auto       *src_pixel      = reinterpret_cast<const T *>(tensor_planes[1](src_coord));
+            const auto       *u_pixel        = reinterpret_cast<const T *>(tensor_planes[1](src_coord));
+            const auto       *v_pixel        = reinterpret_cast<const T *>(tensor_planes[2](src_coord));
             auto             *uvec_pixel_top = reinterpret_cast<T *>(uvec(top_elem_coord));
             auto             *vvec_pixel_top = reinterpret_cast<T *>(vvec(top_elem_coord));
 
             auto *uvec_pixel_bottom = reinterpret_cast<T *>(uvec(bottom_elem_coord));
             auto *vvec_pixel_bottom = reinterpret_cast<T *>(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];
+            uvec_pixel_top[x]       = u_pixel[0];
+            vvec_pixel_top[x]       = v_pixel[0];
+            uvec_pixel_bottom[x]    = u_pixel[0];
+            vvec_pixel_bottom[x]    = v_pixel[0];
         }
         top_elem_coord.set(1, y + 2);
         bottom_elem_coord.set(1, top_elem_coord.y() + 1);
@@ -335,6 +417,461 @@
     yuyv_to_rgb_calculation(yvec, vvec, yyvec, uvec, dst);
 }
 
+template <typename T>
+inline void colorconvert_rgb_to_nv12(const SimpleTensor<T> src, std::vector<SimpleTensor<T>> &dst)
+{
+    SimpleTensor<T> rvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> gvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> bvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> yvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+
+    int vec_shape_x = src.shape().x() * src.shape().y();
+
+    SimpleTensor<T> uvec_top(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> uvec_bottom(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> vvec_top(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> vvec_bottom(TensorShape{ vec_shape_x, 1U }, Format::U8);
+
+    store_rgb_from_src(src, rvec, gvec, bvec);
+    rgb_to_yuv_calculation(rvec, gvec, bvec, dst[0], uvec_top, uvec_bottom, vvec_top, vvec_bottom);
+
+    SimpleTensor<T> utmp(TensorShape{ src.shape().x() / 2, src.shape().y() }, Format::U8);
+    SimpleTensor<T> vtmp(TensorShape{ src.shape().x() / 2, src.shape().y() }, Format::U8);
+
+    int utmp_width  = utmp.shape().x();
+    int utmp_height = utmp.shape().y();
+
+    int         uvec_coord_x = 0;
+    int         uvec_coord_y = 0;
+    Coordinates uvec_coord{ uvec_coord_x, uvec_coord_y };
+    for(int y = 0; y < utmp_height; y++)
+    {
+        for(int x = 0; x < utmp_width; x++)
+        {
+            Coordinates coord{ x, y };
+            auto       *utmp_pixel = reinterpret_cast<T *>(utmp(coord));
+            auto       *vtmp_pixel = reinterpret_cast<T *>(vtmp(coord));
+
+            T   border_value(0);
+            int uvec_top_val    = validation::tensor_elem_at(uvec_top, uvec_coord, BorderMode::CONSTANT, border_value);
+            int uvec_bottom_val = validation::tensor_elem_at(uvec_bottom, uvec_coord, BorderMode::CONSTANT, border_value);
+            int vvec_top_val    = validation::tensor_elem_at(vvec_top, uvec_coord, BorderMode::CONSTANT, border_value);
+            int vvec_bottom_val = validation::tensor_elem_at(vvec_bottom, uvec_coord, BorderMode::CONSTANT, border_value);
+
+            utmp_pixel[0] = std::ceil(float(uvec_top_val + uvec_bottom_val) / 2);
+            vtmp_pixel[0] = std::ceil(float(vvec_top_val + vvec_bottom_val) / 2);
+
+            uvec_coord.set(0, ++uvec_coord_x);
+        }
+    }
+
+    int second_plane_x = dst[1].shape().x();
+    int second_plane_y = dst[1].shape().y();
+
+    int utmp_coord_x = 0;
+    int utmp_coord_y = 0;
+
+    for(int y = 0; y < second_plane_y; y++)
+    {
+        for(int x = 0; x < second_plane_x; x++)
+        {
+            Coordinates coord{ x, y };
+            Coordinates utmp_top_coord{ utmp_coord_x, utmp_coord_y };
+            Coordinates utmp_bottom_coord{ utmp_coord_x, utmp_coord_y + 1 };
+
+            auto *dst_pixel = reinterpret_cast<T *>(dst[1](coord));
+
+            T   border_value(0);
+            int utmp_top_val    = validation::tensor_elem_at(utmp, utmp_top_coord, BorderMode::CONSTANT, border_value);
+            int utmp_bottom_val = validation::tensor_elem_at(utmp, utmp_bottom_coord, BorderMode::CONSTANT, border_value);
+
+            int result   = (utmp_top_val + utmp_bottom_val) / 2;
+            dst_pixel[0] = result;
+
+            int vtmp_top_val    = validation::tensor_elem_at(vtmp, utmp_top_coord, BorderMode::CONSTANT, border_value);
+            int vtmp_bottom_val = validation::tensor_elem_at(vtmp, utmp_bottom_coord, BorderMode::CONSTANT, border_value);
+
+            result       = (vtmp_top_val + vtmp_bottom_val) / 2;
+            dst_pixel[1] = result;
+
+            utmp_coord_x++;
+
+            if(utmp_coord_x >= utmp_width)
+            {
+                utmp_coord_x = 0;
+                utmp_coord_y += 2;
+            }
+        }
+    }
+}
+
+template <typename T>
+inline void colorconvert_rgb_to_iyuv(const SimpleTensor<T> src, std::vector<SimpleTensor<T>> &dst)
+{
+    SimpleTensor<T> rvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> gvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> bvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> yvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+
+    int vec_shape_x = src.shape().x() * src.shape().y();
+
+    SimpleTensor<T> uvec_top(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> uvec_bottom(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> vvec_top(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> vvec_bottom(TensorShape{ vec_shape_x, 1U }, Format::U8);
+
+    store_rgb_from_src(src, rvec, gvec, bvec);
+    rgb_to_yuv_calculation(rvec, gvec, bvec, dst[0], uvec_top, uvec_bottom, vvec_top, vvec_bottom);
+
+    SimpleTensor<T> utmp(TensorShape{ src.shape().x() / 2, src.shape().y() }, Format::U8);
+    SimpleTensor<T> vtmp(TensorShape{ src.shape().x() / 2, src.shape().y() }, Format::U8);
+    int             utmp_width  = utmp.shape().x();
+    int             utmp_height = utmp.shape().y();
+
+    int         uvec_coord_x = 0;
+    int         uvec_coord_y = 0;
+    Coordinates uvec_coord{ uvec_coord_x, uvec_coord_y };
+    for(int y = 0; y < utmp_height; y++)
+    {
+        for(int x = 0; x < utmp_width; x++)
+        {
+            Coordinates coord{ x, y };
+            auto       *utmp_pixel = reinterpret_cast<T *>(utmp(coord));
+            auto       *vtmp_pixel = reinterpret_cast<T *>(vtmp(coord));
+
+            T   border_value(0);
+            int uvec_top_val    = validation::tensor_elem_at(uvec_top, uvec_coord, BorderMode::CONSTANT, border_value);
+            int uvec_bottom_val = validation::tensor_elem_at(uvec_bottom, uvec_coord, BorderMode::CONSTANT, border_value);
+            int vvec_top_val    = validation::tensor_elem_at(vvec_top, uvec_coord, BorderMode::CONSTANT, border_value);
+            int vvec_bottom_val = validation::tensor_elem_at(vvec_bottom, uvec_coord, BorderMode::CONSTANT, border_value);
+
+            utmp_pixel[0] = std::ceil(float(uvec_top_val + uvec_bottom_val) / 2);
+            vtmp_pixel[0] = std::ceil(float(vvec_top_val + vvec_bottom_val) / 2);
+
+            uvec_coord.set(0, ++uvec_coord_x);
+        }
+    }
+
+    int second_plane_x = dst[1].shape().x();
+    int second_plane_y = dst[1].shape().y();
+
+    int utmp_coord_x = 0;
+    int utmp_coord_y = 0;
+
+    for(int y = 0; y < second_plane_y; y++)
+    {
+        for(int x = 0; x < second_plane_x; x++)
+        {
+            Coordinates coord{ x, y };
+            Coordinates utmp_top_coord{ utmp_coord_x, utmp_coord_y };
+            Coordinates utmp_bottom_coord{ utmp_coord_x, utmp_coord_y + 1 };
+
+            auto *u_pixel = reinterpret_cast<T *>(dst[1](coord));
+            auto *v_pixel = reinterpret_cast<T *>(dst[2](coord));
+
+            T   border_value(0);
+            int utmp_top_val    = validation::tensor_elem_at(utmp, utmp_top_coord, BorderMode::CONSTANT, border_value);
+            int utmp_bottom_val = validation::tensor_elem_at(utmp, utmp_bottom_coord, BorderMode::CONSTANT, border_value);
+
+            int result = (utmp_top_val + utmp_bottom_val) / 2;
+            u_pixel[0] = result;
+
+            int vtmp_top_val    = validation::tensor_elem_at(vtmp, utmp_top_coord, BorderMode::CONSTANT, border_value);
+            int vtmp_bottom_val = validation::tensor_elem_at(vtmp, utmp_bottom_coord, BorderMode::CONSTANT, border_value);
+
+            result     = (vtmp_top_val + vtmp_bottom_val) / 2;
+            v_pixel[0] = result;
+
+            utmp_coord_x++;
+
+            if(utmp_coord_x >= utmp_width)
+            {
+                utmp_coord_x = 0;
+                utmp_coord_y += 2;
+            }
+        }
+    }
+}
+
+template <typename T>
+inline void colorconvert_rgb_to_yuv4(const SimpleTensor<T> src, std::vector<SimpleTensor<T>> &dst)
+{
+    SimpleTensor<T> rvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> gvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> bvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+    SimpleTensor<T> yvec(TensorShape{ dst[0].shape().x(), dst[0].shape().y() }, Format::U8);
+
+    int vec_shape_x = src.shape().x() * src.shape().y();
+
+    SimpleTensor<T> uvec_top(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> uvec_bottom(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> vvec_top(TensorShape{ vec_shape_x, 1U }, Format::U8);
+    SimpleTensor<T> vvec_bottom(TensorShape{ vec_shape_x, 1U }, Format::U8);
+
+    int width  = src.shape().x();
+    int height = src.shape().y();
+
+    store_rgb_from_src(src, rvec, gvec, bvec);
+
+    rgb_to_yuv_calculation(rvec, gvec, bvec, dst[0], uvec_top, uvec_bottom, vvec_top, vvec_bottom);
+
+    int         uvec_coord_x = 0;
+    int         uvec_coord_y = 0;
+    Coordinates uvec_coord{ uvec_coord_x, uvec_coord_y };
+    for(int y = 0; y < height; y++)
+    {
+        for(int x = 0; x < width; x += 2)
+        {
+            Coordinates coord{ x, y };
+            auto       *plane_1_pixel = reinterpret_cast<T *>(dst[1](coord));
+            auto       *plane_2_pixel = reinterpret_cast<T *>(dst[2](coord));
+
+            T   border_value(0);
+            int uvec_top_val    = validation::tensor_elem_at(uvec_top, uvec_coord, BorderMode::CONSTANT, border_value);
+            int uvec_bottom_val = validation::tensor_elem_at(uvec_bottom, uvec_coord, BorderMode::CONSTANT, border_value);
+
+            plane_1_pixel[0] = uvec_top_val;
+            plane_1_pixel[1] = uvec_bottom_val;
+
+            int vvec_top_val    = validation::tensor_elem_at(vvec_top, uvec_coord, BorderMode::CONSTANT, border_value);
+            int vvec_bottom_val = validation::tensor_elem_at(vvec_bottom, uvec_coord, BorderMode::CONSTANT, border_value);
+
+            plane_2_pixel[0] = vvec_top_val;
+            plane_2_pixel[1] = vvec_bottom_val;
+
+            uvec_coord.set(0, ++uvec_coord_x);
+        }
+    }
+}
+
+template <typename T>
+inline void colorconvert_yuyv_to_nv12(const SimpleTensor<T> src, const Format format, std::vector<SimpleTensor<T>> &dst)
+{
+    SimpleTensor<T> uvvec_top(TensorShape{ dst[0].shape().x(), dst[0].shape().y() / 2 }, Format::U8);
+    SimpleTensor<T> uvvec_bottom(TensorShape{ dst[0].shape().x(), dst[0].shape().y() / 2 }, Format::U8);
+
+    const int offset = (Format::YUYV422 == format) ? 0 : 1;
+
+    int width  = dst[0].shape().x();
+    int height = dst[0].shape().y();
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x++)
+        {
+            const Coordinates dst_coord{ x, y };
+            const Coordinates uv_coord{ x, y / 2 };
+
+            const auto *src_pixel          = reinterpret_cast<const T *>(src(dst_coord));
+            auto       *y_pixel            = reinterpret_cast<T *>(dst[0](dst_coord));
+            auto       *uvvec_top_pixel    = reinterpret_cast<T *>(uvvec_top(uv_coord));
+            auto       *uvvec_bottom_pixel = reinterpret_cast<T *>(uvvec_bottom(uv_coord));
+
+            y_pixel[0] = src_pixel[0 + offset];
+
+            if(y % 2 == 0)
+            {
+                uvvec_top_pixel[0] = src_pixel[1 - offset];
+            }
+            else
+            {
+                uvvec_bottom_pixel[0] = src_pixel[1 - offset];
+            }
+        }
+    }
+
+    width  = dst[1].shape().x();
+    height = dst[1].shape().y();
+
+    int uv_coord_x = 0;
+    int uv_coord_y = 0;
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x++)
+        {
+            const Coordinates dst_coord{ x, y };
+            const Coordinates uv_coord{ uv_coord_x, uv_coord_y };
+
+            auto       *uv_pixel           = reinterpret_cast<T *>(dst[1](dst_coord));
+            const auto *uvvec_top_pixel    = reinterpret_cast<T *>(uvvec_top(uv_coord));
+            const auto *uvvec_bottom_pixel = reinterpret_cast<T *>(uvvec_bottom(uv_coord));
+
+            uv_pixel[0] = (uvvec_top_pixel[0] + uvvec_bottom_pixel[0]) / 2;
+            uv_pixel[1] = (uvvec_top_pixel[1] + uvvec_bottom_pixel[1]) / 2;
+            uv_coord_x += 2;
+        }
+        uv_coord_x = 0;
+        uv_coord_y++;
+    }
+}
+
+template <typename T>
+inline void colorconvert_yuyv_to_iyuv(const SimpleTensor<T> src, const Format format, std::vector<SimpleTensor<T>> &dst)
+{
+    SimpleTensor<T> uvvec_top(TensorShape{ dst[0].shape().x(), dst[0].shape().y() / 2 }, Format::U8);
+    SimpleTensor<T> uvvec_bottom(TensorShape{ dst[0].shape().x(), dst[0].shape().y() / 2 }, Format::U8);
+
+    const int offset = (Format::YUYV422 == format) ? 0 : 1;
+
+    int width  = dst[0].shape().x();
+    int height = dst[0].shape().y();
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x++)
+        {
+            const Coordinates dst_coord{ x, y };
+            const Coordinates uv_coord{ x, y / 2 };
+
+            const auto *src_pixel          = reinterpret_cast<const T *>(src(dst_coord));
+            auto       *y_pixel            = reinterpret_cast<T *>(dst[0](dst_coord));
+            auto       *uvvec_top_pixel    = reinterpret_cast<T *>(uvvec_top(uv_coord));
+            auto       *uvvec_bottom_pixel = reinterpret_cast<T *>(uvvec_bottom(uv_coord));
+
+            y_pixel[0] = src_pixel[0 + offset];
+
+            if(y % 2 == 0)
+            {
+                uvvec_top_pixel[0] = src_pixel[1 - offset];
+            }
+            else
+            {
+                uvvec_bottom_pixel[0] = src_pixel[1 - offset];
+            }
+        }
+    }
+
+    width  = dst[1].shape().x();
+    height = dst[1].shape().y();
+
+    int uv_coord_x = 0;
+    int uv_coord_y = 0;
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x++)
+        {
+            const Coordinates dst_coord{ x, y };
+            const Coordinates uv_coord{ uv_coord_x, uv_coord_y };
+
+            auto       *u_pixel            = reinterpret_cast<T *>(dst[1](dst_coord));
+            auto       *v_pixel            = reinterpret_cast<T *>(dst[2](dst_coord));
+            const auto *uvvec_top_pixel    = reinterpret_cast<T *>(uvvec_top(uv_coord));
+            const auto *uvvec_bottom_pixel = reinterpret_cast<T *>(uvvec_bottom(uv_coord));
+
+            u_pixel[0] = (uvvec_top_pixel[0] + uvvec_bottom_pixel[0]) / 2;
+            v_pixel[0] = (uvvec_top_pixel[1] + uvvec_bottom_pixel[1]) / 2;
+            uv_coord_x += 2;
+        }
+        uv_coord_x = 0;
+        uv_coord_y++;
+    }
+}
+
+template <typename T>
+inline void nv_to_iyuv(const SimpleTensor<T> src, const Format src_format, SimpleTensor<T> &nv1, SimpleTensor<T> &nv2)
+{
+    int width  = src.shape().x();
+    int height = src.shape().y();
+
+    const int offset = (Format::NV12 == src_format) ? 1 : 0;
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x++)
+        {
+            const Coordinates src_coord{ x, y };
+            const auto       *src_pixel = reinterpret_cast<const T *>(src(src_coord));
+            auto             *u_pixel   = reinterpret_cast<T *>(nv1(src_coord));
+            auto             *v_pixel   = reinterpret_cast<T *>(nv2(src_coord));
+
+            u_pixel[0] = src_pixel[1 - offset];
+            v_pixel[0] = src_pixel[0 + offset];
+        }
+    }
+}
+
+template <typename T>
+inline void nv_to_yuv4(const SimpleTensor<T> src, const Format src_format, SimpleTensor<T> &nv1, SimpleTensor<T> &nv2)
+{
+    int width  = src.shape().x();
+    int height = src.shape().y();
+
+    const int offset = (Format::NV12 == src_format) ? 1 : 0;
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; x++)
+        {
+            const Coordinates src_coord{ x, y };
+            Coordinates       dst_coord{ x * 2, y * 2 };
+            const auto       *src_pixel = reinterpret_cast<const T *>(src(src_coord));
+            auto             *u_pixel   = reinterpret_cast<T *>(nv1(dst_coord));
+            auto             *v_pixel   = reinterpret_cast<T *>(nv2(dst_coord));
+
+            u_pixel[0] = src_pixel[1 - offset];
+            u_pixel[1] = src_pixel[1 - offset];
+
+            v_pixel[0] = src_pixel[0 + offset];
+            v_pixel[1] = src_pixel[0 + offset];
+
+            dst_coord.set(1, y * 2 + 1);
+            u_pixel    = reinterpret_cast<T *>(nv1(dst_coord));
+            v_pixel    = reinterpret_cast<T *>(nv2(dst_coord));
+            u_pixel[0] = src_pixel[1 - offset];
+            u_pixel[1] = src_pixel[1 - offset];
+
+            v_pixel[0] = src_pixel[0 + offset];
+            v_pixel[1] = src_pixel[0 + offset];
+        }
+    }
+}
+
+template <typename T>
+inline void colorconvert_nv_to_iyuv(const std::vector<SimpleTensor<T>> src, const Format src_format, std::vector<SimpleTensor<T>> &dst)
+{
+    int width  = dst[0].shape().x();
+    int height = dst[0].shape().y();
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; ++x)
+        {
+            const Coordinates dst_coord{ x, y };
+
+            const auto *src_pixel = reinterpret_cast<const T *>(src[0](dst_coord));
+            auto       *y_pixel   = reinterpret_cast<T *>(dst[0](dst_coord));
+
+            y_pixel[0] = src_pixel[0];
+        }
+    }
+
+    nv_to_iyuv(src[1], src_format, dst[1], dst[2]);
+}
+
+template <typename T>
+inline void colorconvert_nv_to_yuv4(const std::vector<SimpleTensor<T>> src, const Format src_format, std::vector<SimpleTensor<T>> &dst)
+{
+    int width  = dst[0].shape().x();
+    int height = dst[0].shape().y();
+
+    for(int y = 0; y < height; ++y)
+    {
+        for(int x = 0; x < width; ++x)
+        {
+            const Coordinates dst_coord{ x, y };
+
+            const auto *src_pixel = reinterpret_cast<const T *>(src[0](dst_coord));
+            auto       *y_pixel   = reinterpret_cast<T *>(dst[0](dst_coord));
+
+            y_pixel[0] = src_pixel[0];
+        }
+    }
+
+    nv_to_yuv4(src[1], src_format, dst[1], dst[2]);
+}
+
 } // namespace detail
 } // color_convert_helper
 } // namespace test