diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index 613c87f6f7..c801bee1b0 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -44,6 +44,7 @@ set(gapi_srcs src/api/operators.cpp src/api/kernels_core.cpp src/api/kernels_imgproc.cpp + src/api/render.cpp # Compiler part src/compiler/gmodel.cpp diff --git a/modules/gapi/include/opencv2/gapi/render.hpp b/modules/gapi/include/opencv2/gapi/render.hpp new file mode 100644 index 0000000000..b157097b8c --- /dev/null +++ b/modules/gapi/include/opencv2/gapi/render.hpp @@ -0,0 +1,81 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_GAPI_RENDER_HPP +#define OPENCV_GAPI_RENDER_HPP + +#include +#include + +#include +#include +#include +#include + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ + +/** + * A structure to represent parameters for drawing a text string. + */ +struct Text +{ + /*@{*/ + std::string text; /** < The text string to be drawn */ + cv::Point org; /** < The bottom-left corner of the text string in the image */ + int ff; /** < The font type, see #HersheyFonts */ + double fs; /** < The font scale factor that is multiplied by the font-specific base size */ + cv::Scalar color; /** < The text color */ + int thick; /** < The thickness of the lines used to draw a text */ + int lt; /** < The line type. See #LineTypes */ + bool bottom_left_origin; /** < When true, the image data origin is at the bottom-left corner. Otherwise, + it is at the top-left corner. */ + /*@{*/ +}; + +/** + * A structure to represent parameters for drawing a rectangle + */ +struct Rect +{ + cv::Rect rect; /** Coordinates of the rectangle < */ + cv::Scalar color; /** The rectangle color or brightness (grayscale image) < */ + int thick; /** The thickness of lines that make up the rectangle. Negative values, like #FILLED, < */ + int lt; /** The type of the line. See #LineTypes< */ + int shift; /** The number of fractional bits in the point coordinates < */ +}; + +using Prim = util::variant; +using Prims = std::vector; + +/** @brief The function renders on the input image passed drawing primitivies + +@param bgr input image: 8-bit unsigned 3-channel image @ref CV_8UC3. +@param prims vector of drawing primitivies +*/ +GAPI_EXPORTS void render(cv::Mat& bgr, const Prims& prims); + +/** @brief The function renders on two NV12 planes passed drawing primitivies + +@param y_plane input image: 8-bit unsigned 1-channel image @ref CV_8UC1. +@param uv_plane input image: 8-bit unsigned 2-channel image @ref CV_8UC2. +@param prims vector of drawing primitivies +*/ +GAPI_EXPORTS void render(cv::Mat& y_plane, cv::Mat& uv_plane , const Prims& prims); + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_GAPI_RENDER_HPP diff --git a/modules/gapi/src/api/render.cpp b/modules/gapi/src/api/render.cpp new file mode 100644 index 0000000000..5dd4bb31f8 --- /dev/null +++ b/modules/gapi/src/api/render.cpp @@ -0,0 +1,77 @@ +#include + +#include "opencv2/gapi/render.hpp" +#include "opencv2/gapi/own/assert.hpp" + +#include "api/render_priv.hpp" + +using namespace cv::gapi::wip::draw; +// FXIME util::visitor ? +void cv::gapi::wip::draw::render(cv::Mat& bgr, const Prims& prims) +{ + for (const auto& p : prims) + { + switch (p.index()) + { + case Prim::index_of(): + { + auto t_p = cv::util::get(p); + cv::rectangle(bgr, t_p.rect, t_p.color , t_p.thick, t_p.lt, t_p.shift); + break; + } + + case Prim::index_of(): + { + auto t_p = cv::util::get(p); + cv::putText(bgr, t_p.text, t_p.org, t_p.ff, t_p.fs, + t_p.color, t_p.thick, t_p.bottom_left_origin); + break; + } + + default: util::throw_error(std::logic_error("Unsupported draw event")); + } + } +} + +void cv::gapi::wip::draw::render(cv::Mat& y_plane, cv::Mat& uv_plane , const Prims& prims) +{ + cv::Mat bgr; + cv::cvtColorTwoPlane(y_plane, uv_plane, bgr, cv::COLOR_YUV2BGR_NV12); + render(bgr, prims); + BGR2NV12(bgr, y_plane, uv_plane); +} + +void cv::gapi::wip::draw::splitNV12TwoPlane(const cv::Mat& yuv, cv::Mat& y_plane, cv::Mat& uv_plane) { + y_plane.create(yuv.size(), CV_8UC1); + uv_plane.create(yuv.size() / 2, CV_8UC2); + + // Fill Y plane + for (int i = 0; i < yuv.rows; ++i) + { + const uchar* in = yuv.ptr(i); + uchar* out = y_plane.ptr(i); + for (int j = 0; j < yuv.cols; j++) { + out[j] = in[3 * j]; + } + } + + // Fill UV plane + for (int i = 0; i < uv_plane.rows; i++) + { + const uchar* in = yuv.ptr(2 * i); + uchar* out = uv_plane.ptr(i); + for (int j = 0; j < uv_plane.cols; j++) { + out[j * 2 ] = in[6 * j + 1]; + out[j * 2 + 1] = in[6 * j + 2]; + } + } +} + +void cv::gapi::wip::draw::BGR2NV12(const cv::Mat& bgr, cv::Mat& y_plane, cv::Mat& uv_plane) +{ + GAPI_Assert(bgr.size().width % 2 == 0); + GAPI_Assert(bgr.size().height % 2 == 0); + + cvtColor(bgr, bgr, cv::COLOR_BGR2YUV); + splitNV12TwoPlane(bgr, y_plane, uv_plane); +} diff --git a/modules/gapi/src/api/render_priv.hpp b/modules/gapi/src/api/render_priv.hpp new file mode 100644 index 0000000000..29805ea49a --- /dev/null +++ b/modules/gapi/src/api/render_priv.hpp @@ -0,0 +1,30 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + + +#ifndef OPENCV_RENDER_PRIV_HPP +#define OPENCV_RENDER_PRIV_HPP + +#include + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ +// FIXME only for tests +GAPI_EXPORTS void BGR2NV12(const cv::Mat& bgr, cv::Mat& y_plane, cv::Mat& uv_plane); +void splitNV12TwoPlane(const cv::Mat& yuv, cv::Mat& y_plane, cv::Mat& uv_plane); + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_RENDER_PRIV_HPP diff --git a/modules/gapi/test/gapi_render_test.cpp b/modules/gapi/test/gapi_render_test.cpp new file mode 100644 index 0000000000..64e0f274ca --- /dev/null +++ b/modules/gapi/test/gapi_render_test.cpp @@ -0,0 +1,141 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. +// +// Copyright (C) 2018 Intel Corporation + +#include "test_precomp.hpp" + +#include "api/render_priv.hpp" + +#include +#include +#include + +namespace opencv_test +{ + +namespace +{ + struct RenderTestFixture : public ::testing::Test + { + cv::Size size = {30, 40}; + int thick = 2; + int ff = cv::FONT_HERSHEY_SIMPLEX; + int lt = LINE_8; + double fs = 1; + + cv::Mat ref_mat {320, 480, CV_8UC3, cv::Scalar::all(255)}; + cv::Mat out_mat {320, 480, CV_8UC3, cv::Scalar::all(255)}; + cv::Scalar color {0, 255, 0}; + std::string text {"some text"}; + + }; +} // namespace + +TEST(BGR2NV12Test, CorrectConversion) +{ + cv::Mat in_mat(320, 240, CV_8UC3); + cv::Mat out_y, out_uv, ref_y, yuv; + cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar::all(255)); + + cv::cvtColor(in_mat, yuv, cv::COLOR_BGR2YUV); + cv::Mat channels[3]; + cv::split(yuv, channels); + + ref_y = channels[0]; + cv::Mat ref_uv(in_mat.size() / 2, CV_8UC2); + cv::resize(channels[1], channels[1], channels[1].size() / 2, 0, 0, cv::INTER_NEAREST); + cv::resize(channels[2], channels[2], channels[2].size() / 2, 0, 0, cv::INTER_NEAREST); + cv::merge(channels + 1, 2, ref_uv); + + cv::gapi::wip::draw::BGR2NV12(in_mat, out_y, out_uv); + + EXPECT_EQ(0, cv::countNonZero(out_y != ref_y)); + EXPECT_EQ(0, cv::countNonZero(out_uv != ref_uv)); +} + +TEST_F(RenderTestFixture, PutText) +{ + std::vector prims; + + for (int i = 0; i < 5; ++i) + { + cv::Point point {30 + i * 60, 40 + i * 50}; + + cv::putText(ref_mat, text, point, ff, fs, color, thick); + prims.emplace_back(cv::gapi::wip::draw::Text{text, point, ff, fs, color, thick, lt, false}); + } + + cv::gapi::wip::draw::render(out_mat, prims); + + EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat)); +} + +TEST_F(RenderTestFixture, Rectangle) +{ + std::vector prims; + + for (int i = 0; i < 5; ++i) + { + cv::Rect rect {30 + i * 60, 40 + i * 50, size.width, size.height}; + cv::rectangle(ref_mat, rect, color, thick); + prims.emplace_back(cv::gapi::wip::draw::Rect{rect, color, thick, lt, 0}); + } + + cv::gapi::wip::draw::render(out_mat, prims); + + EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat)); +} + +TEST_F(RenderTestFixture, PutTextAndRectangle) +{ + std::vector prims; + + for (int i = 0; i < 5; ++i) + { + cv::Point point {30 + i * 60, 40 + i * 50}; + cv::Rect rect {point, size}; + + cv::rectangle(ref_mat, rect, color, thick); + cv::putText(ref_mat, text, point, ff, fs, color, thick); + + prims.emplace_back(cv::gapi::wip::draw::Rect{rect, color, thick, lt, 0}); + prims.emplace_back(cv::gapi::wip::draw::Text{text, point, ff, fs, color, thick, lt, false}); + } + + cv::gapi::wip::draw::render(out_mat, prims); + + EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat)); +} + +TEST_F(RenderTestFixture, PutTextAndRectangleNV12) +{ + cv::Mat y; + cv::Mat uv; + cv::gapi::wip::draw::BGR2NV12(out_mat, y, uv); + + std::vector prims; + + for (int i = 0; i < 5; ++i) + { + cv::Point point {30 + i * 60, 40 + i * 50}; + cv::Rect rect {point, size}; + + cv::rectangle(ref_mat, rect, color, thick); + cv::putText(ref_mat, text, point, ff, fs, color, thick); + + prims.emplace_back(cv::gapi::wip::draw::Rect{rect, color, thick, lt, 0}); + prims.emplace_back(cv::gapi::wip::draw::Text{text, point, ff, fs, color, thick, lt, false}); + } + + cv::gapi::wip::draw::render(y, uv, prims); + cv::cvtColorTwoPlane(y, uv, out_mat, cv::COLOR_YUV2BGR_NV12); + + cv::gapi::wip::draw::BGR2NV12(ref_mat, y, uv); + cv::cvtColorTwoPlane(y, uv, ref_mat, cv::COLOR_YUV2BGR_NV12); + + EXPECT_EQ(0, cv::countNonZero(out_mat != ref_mat)); +} + +} // opencv_test