diff --git a/modules/gapi/CMakeLists.txt b/modules/gapi/CMakeLists.txt index cd12f904af..b7cd7f41b4 100644 --- a/modules/gapi/CMakeLists.txt +++ b/modules/gapi/CMakeLists.txt @@ -59,6 +59,7 @@ set(gapi_srcs src/api/render.cpp src/api/render_ocv.cpp src/api/ginfer.cpp + src/api/ft_render.cpp # Compiler part src/compiler/gmodel.cpp @@ -143,6 +144,13 @@ if(TARGET opencv_test_gapi) target_link_libraries(opencv_test_gapi PRIVATE ade) endif() +if (HAVE_FREETYPE) + ocv_target_compile_definitions(opencv_gapi PRIVATE -DHAVE_FREETYPE) + ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_FREETYPE) + ocv_target_link_libraries(opencv_gapi LINK_PRIVATE ${FREETYPE_LIBRARIES}) + ocv_target_include_directories(opencv_gapi PRIVATE ${FREETYPE_INCLUDE_DIRS}) +endif() + if(HAVE_PLAIDML) ocv_target_compile_definitions(opencv_gapi PRIVATE -DHAVE_PLAIDML) ocv_target_compile_definitions(opencv_test_gapi PRIVATE -DHAVE_PLAIDML) diff --git a/modules/gapi/cmake/init.cmake b/modules/gapi/cmake/init.cmake index 026adf3e7c..4c25c75f55 100644 --- a/modules/gapi/cmake/init.cmake +++ b/modules/gapi/cmake/init.cmake @@ -1,5 +1,7 @@ OCV_OPTION(WITH_ADE "Enable ADE framework (required for Graph API module)" ON) -OCV_OPTION(WITH_PLAIDML "Include PlaidML2 support" OFF) + +OCV_OPTION(WITH_FREETYPE "Enable FreeType framework" OFF) +OCV_OPTION(WITH_PLAIDML "Include PlaidML2 support" OFF) if(NOT WITH_ADE) return() @@ -17,6 +19,13 @@ if(NOT TARGET ade) include("${CMAKE_CURRENT_LIST_DIR}/DownloadADE.cmake") endif() +if(WITH_FREETYPE) + ocv_check_modules(FREETYPE freetype2) + if (FREETYPE_FOUND) + set(HAVE_FREETYPE TRUE) + endif() +endif() + if(WITH_PLAIDML) find_package(PlaidML2 CONFIG QUIET) if (PLAIDML_FOUND) diff --git a/modules/gapi/include/opencv2/gapi/render/render.hpp b/modules/gapi/include/opencv2/gapi/render/render.hpp index fe9daac333..845c1dbd6a 100644 --- a/modules/gapi/include/opencv2/gapi/render/render.hpp +++ b/modules/gapi/include/opencv2/gapi/render/render.hpp @@ -30,7 +30,7 @@ namespace draw /** * A structure allows using freetype library for text rendering */ -struct use_freetype +struct freetype_font { /*@{*/ std::string path; //!< The path to font file (.ttf) @@ -54,6 +54,19 @@ struct Text /*@{*/ }; +/** + * A structure to represent parameters for drawing a text string using FreeType library + */ +struct FText +{ + /*@{*/ + std::wstring text; //!< The text string to be drawn + cv::Point org; //!< The bottom-left corner of the text string in the image + int fh; //!< The height of text + cv::Scalar color; //!< The text color + /*@{*/ +}; + /** * A structure to represent parameters for drawing a rectangle */ @@ -126,6 +139,7 @@ struct Poly using Prim = util::variant < Text + , FText , Rect , Circle , Line diff --git a/modules/gapi/src/api/ft_render.cpp b/modules/gapi/src/api/ft_render.cpp new file mode 100644 index 0000000000..9d9f6b3370 --- /dev/null +++ b/modules/gapi/src/api/ft_render.cpp @@ -0,0 +1,214 @@ +// 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) 2019 Intel Corporation + +#include "precomp.hpp" + +#ifdef HAVE_FREETYPE + +#include "api/ft_render.hpp" +#include "api/ft_render_priv.hpp" + +#include +#include + +cv::gapi::wip::draw::FTTextRender::Priv::Priv(const std::string& path) +{ + if (FT_Init_FreeType(&m_library) != 0) + { + cv::util::throw_error(std::runtime_error("Failed to initialize FT")); + } + + if (FT_New_Face(m_library, path.c_str(), 0, &m_face)) + { + FT_Done_FreeType(m_library); + cv::util::throw_error(std::runtime_error("Failed to create a font face")); + } +} + +cv::Size cv::gapi::wip::draw::FTTextRender::Priv::getTextSize(const std::wstring& text, int fh, int* baseline) +{ + // + // + // + // ^ diff between size and advance(2) + // | ______________ width width |<->| + // | | ** | |<------>| <------------|---> + // | | * * | |________| |____________|___|________ + // | left | * * | left |* * * * | | * * * * *| | ^ ^ + // |<---->| ** ** ** | <----->|* *| | * | | t | | + // | | * * | | |* *| | * | | o | h | + // | | * * | | |* * * * | | * (1) | p | e | baseline + // O------|*------------*|-----O----- |*-------|-|----O--*----O---|-----*-i-|------------> + // | |______________| | |* | |* | * | | ^ g | + // | | | | |* | |* | * | | b | h | + // | | width | | |* | |* | * | | o | t | + // | |<------------>| | |* | | * *|* | | t | | + // | | |________| |____|_______|___|_____|___* + // | advance | advance | |advance| (advance maybe less than width) + // <---------------------------><----------------|----><------> + // |left| (left maybe is negative) + // |<-->| + // + // + // O - The pen position for any time + // + // left (m_face->glyph->bitmap_left) - The horizontal distance from the current pen position to the glyph's left bbox edge. + // + // advance (m_face->glyph->advance.x >> 6) - The horizontal distance to increment (for left-to-right writing) + // or decrement (for right-to-left writing) the pen position after a + // glyph has been rendered when processing text + // + // widht (bitmap->width) - The width of glyph + // + // + // Algorihm to compute size of the text bounding box: + // + // 1) Go through all symbols and shift pen position and save glyph parameters (left, advance, width) + // If left + pen postion < 0 set left to 0. For example it's maybe happened + // if we print first letter 'J' or any other letter with negative 'left' + // We want to render glyph in pen position + left, so we must't allow it to be negative + // + // 2) If width == 0 we must to skip this symbol and don't save parameters for him. + // For example width == 0 for space sometimes + // + // 3) Also we compute max top and max bottom it's required for compute baseline + // + // 3) At the end we'll get the pen position for the symbol next to the last. + // See (1) on picture. + // + // 4) As we can see the last pen position is isn't horizontal size yet. + // We need to check if the glyph goes beyound the last position of the pen + // To do this we can: + // a) Return to the previous position -advance + // b) Shift on left value +left + // c) Shift on width of the last glyph + // + // Compare result position with pen position and choose max + // + // We can compute diff and check if diff > 0 pen.x += diff. + // See (2) on picture. + // + // 5) Return size. Complete!!! + // + // See also about freetype glyph metrics: + // https://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html + + GAPI_Assert(!FT_Set_Pixel_Sizes(m_face, fh, fh) && + "Failed to set pixel size"); + + cv::Point pen(0, 0); + + int max_bot = 0; + int max_top = 0; + int last_advance = 0; + int last_width = 0; + int last_left = 0; + + for (const auto& wc : text) + { + GAPI_Assert(!FT_Load_Char(m_face, wc, FT_LOAD_RENDER) && + "Failed to load char"); + + FT_Bitmap *bitmap = &(m_face->glyph->bitmap); + + int left = m_face->glyph->bitmap_left; + int advance = (m_face->glyph->advance.x >> 6); + int width = bitmap->width; + + // NB: Read (1) paragraph of algorithm description + if (pen.x + left < 0) + { + left = 0; + } + + int bot = (m_face->glyph->metrics.height - m_face->glyph->metrics.horiBearingY) >> 6; + max_bot = std::max(max_bot, bot); + max_top = std::max(max_top, m_face->glyph->bitmap_top); + + // NB: Read (2) paragraph of algorithm description + if (width != 0) + { + last_width = width; + last_advance = advance; + last_left = left; + } + + pen.x += advance; + } + + // NB: Read (4) paragraph of algorithm description + int diff = (last_width + last_left) - last_advance; + pen.x += (diff > 0) ? diff : 0; + + if (baseline) + { + *baseline = max_bot; + } + + return {pen.x, max_bot + max_top}; +} + +void cv::gapi::wip::draw::FTTextRender::Priv::putText(cv::Mat& mat, + const std::wstring& text, + const cv::Point& org, + int fh) +{ + GAPI_Assert(!FT_Set_Pixel_Sizes(m_face, fh, fh) && + "Failed to set pixel size"); + + cv::Point pen = org; + for (const auto& wc : text) + { + GAPI_Assert(!FT_Load_Char(m_face, wc, FT_LOAD_RENDER) && + "Failed to load char"); + FT_Bitmap *bitmap = &(m_face->glyph->bitmap); + + cv::Mat glyph(bitmap->rows, bitmap->width, CV_8UC1, bitmap->buffer, bitmap->pitch); + + int left = m_face->glyph->bitmap_left; + int top = m_face->glyph->bitmap_top; + int advance = (m_face->glyph->advance.x >> 6); + + if (pen.x + left < 0) + { + left = 0; + } + + cv::Rect rect(pen.x + left, org.y - top, glyph.cols, glyph.rows); + + auto roi = mat(rect); + roi += glyph; + pen.x += advance; + } +} + +cv::gapi::wip::draw::FTTextRender::Priv::~Priv() +{ + FT_Done_Face(m_face); + FT_Done_FreeType(m_library); +} + +cv::gapi::wip::draw::FTTextRender::FTTextRender(const std::string& path) + : m_priv(new Priv(path)) +{ +} + +cv::Size cv::gapi::wip::draw::FTTextRender::getTextSize(const std::wstring& text, + int fh, + int* baseline) +{ + return m_priv->getTextSize(text, fh, baseline); +} + +void cv::gapi::wip::draw::FTTextRender::putText(cv::Mat& mat, + const std::wstring& text, + const cv::Point& org, + int fh) +{ + m_priv->putText(mat, text, org, fh); +} + +#endif // HAVE_FREETYPE diff --git a/modules/gapi/src/api/ft_render.hpp b/modules/gapi/src/api/ft_render.hpp new file mode 100644 index 0000000000..2556c7269c --- /dev/null +++ b/modules/gapi/src/api/ft_render.hpp @@ -0,0 +1,52 @@ +// 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) 2019 Intel Corporation + +#ifndef OPENCV_FREETYPE_TEXT_RENDER_HPP +#define OPENCV_FREETYPE_TEXT_RENDER_HPP + +#include +#include + +#include + +#include + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ + +#ifdef HAVE_FREETYPE + +class GAPI_EXPORTS FTTextRender +{ +public: + class Priv; + explicit FTTextRender(const std::string& path); + + cv::Size getTextSize(const std::wstring& text, int fh, int* baseline); + void putText(cv::Mat& mat, const std::wstring& text, const cv::Point& org, int fh); + +private: + std::shared_ptr m_priv; +}; + +#else + +class GAPI_EXPORTS FTTextRender {}; + +#endif // HAVE_FREETYPE + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_FREETYPE_TEXT_RENDER_HPP diff --git a/modules/gapi/src/api/ft_render_priv.hpp b/modules/gapi/src/api/ft_render_priv.hpp new file mode 100644 index 0000000000..5a0679dd99 --- /dev/null +++ b/modules/gapi/src/api/ft_render_priv.hpp @@ -0,0 +1,48 @@ +// 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) 2019 Intel Corporation + + +#ifdef HAVE_FREETYPE + +#ifndef OPENCV_FT_RENDER_PRIV_HPP +#define OPENCV_FT_RENDER_PRIV_HPP + +#include "api/ft_render.hpp" + +#include +#include FT_FREETYPE_H + +namespace cv +{ +namespace gapi +{ +namespace wip +{ +namespace draw +{ + +class FTTextRender::Priv +{ +public: + explicit Priv(const std::string& path); + + cv::Size getTextSize(const std::wstring& text, int fh, int* baseline); + void putText(cv::Mat& mat, const std::wstring& text, const cv::Point& org, int fh); + + ~Priv(); + +private: + FT_Library m_library; + FT_Face m_face; +}; + +} // namespace draw +} // namespace wip +} // namespace gapi +} // namespace cv + +#endif // OPENCV_FT_RENDER_PRIV_HPP +#endif // HAVE_FREETYPE diff --git a/modules/gapi/src/api/ocv_mask_creator.hpp b/modules/gapi/src/api/ocv_mask_creator.hpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/modules/gapi/src/api/render.cpp b/modules/gapi/src/api/render.cpp index f9c61124cc..9f4c18ffed 100644 --- a/modules/gapi/src/api/render.cpp +++ b/modules/gapi/src/api/render.cpp @@ -1,3 +1,7 @@ +#include "precomp.hpp" + +#include + #include #include #include @@ -9,7 +13,7 @@ void cv::gapi::wip::draw::render(cv::Mat& bgr, cv::GCompileArgs&& args) { cv::GMat in; - cv::GArray arr; + cv::GArray arr; cv::GComputation comp(cv::GIn(in, arr), cv::GOut(cv::gapi::wip::draw::render3ch(in, arr))); @@ -22,7 +26,7 @@ void cv::gapi::wip::draw::render(cv::Mat& y_plane, cv::GCompileArgs&& args) { cv::GMat y_in, uv_in, y_out, uv_out; - cv::GArray arr; + cv::GArray arr; std::tie(y_out, uv_out) = cv::gapi::wip::draw::renderNV12(y_in, uv_in, arr); cv::GComputation comp(cv::GIn(y_in, uv_in, arr), cv::GOut(y_out, uv_out)); @@ -40,7 +44,6 @@ void cv::gapi::wip::draw::cvtYUVToNV12(const cv::Mat& yuv, std::vector chs(3); cv::split(yuv, chs); y = chs[0]; - cv::merge(std::vector{chs[1], chs[2]}, uv); cv::resize(uv, uv, uv.size() / 2, cv::INTER_LINEAR); } @@ -58,14 +61,15 @@ namespace cv { namespace detail { - template<> struct CompileArgTag + template<> struct CompileArgTag { - static const char* tag() { return "gapi.use_freetype"; } + static const char* tag() { return "gapi.freetype_font"; } }; } // namespace detail -GMat cv::gapi::wip::draw::render3ch(const GMat& src, const GArray& prims) +GMat cv::gapi::wip::draw::render3ch(const GMat& src, + const GArray& prims) { return cv::gapi::wip::draw::GRenderBGR::on(src, prims); } diff --git a/modules/gapi/src/api/render_ocv.cpp b/modules/gapi/src/api/render_ocv.cpp index a57707f0bb..6c035f6d70 100644 --- a/modules/gapi/src/api/render_ocv.cpp +++ b/modules/gapi/src/api/render_ocv.cpp @@ -2,6 +2,7 @@ #include // Kernel API's #include "api/render_ocv.hpp" +#include "api/ft_render.hpp" namespace cv { @@ -27,12 +28,15 @@ inline void mosaic(cv::Mat& mat, const cv::Rect &rect, int cellSz) } }; -inline void image(cv::Mat& mat, - const cv::Point& org, - const cv::Mat& img, - const cv::Mat& alpha) +inline void blendImage(const cv::Mat& img, + const cv::Mat& alpha, + const cv::Point& org, + cv::Mat background) { - auto roi = mat(cv::Rect(org, img.size())); + GAPI_Assert(alpha.type() == CV_32FC1); + GAPI_Assert(background.channels() == 3u); + + cv::Mat roi = background(cv::Rect(org, img.size())); cv::Mat img32f_w; cv::merge(std::vector(3, alpha), img32f_w); @@ -40,8 +44,12 @@ inline void image(cv::Mat& mat, roi32f_w -= img32f_w; cv::Mat img32f, roi32f; + if (img.type() == CV_32FC3) { + img.copyTo(img32f); + } else { + img.convertTo(img32f, CV_32F, 1.0/255); + } - img.convertTo(img32f, CV_32F, 1.0/255); roi.convertTo(roi32f, CV_32F, 1.0/255); cv::multiply(img32f, img32f_w, img32f); @@ -49,7 +57,22 @@ inline void image(cv::Mat& mat, roi32f += img32f; roi32f.convertTo(roi, CV_8U, 255.0); -}; +} + +inline void blendTextMask(cv::Mat& img, + cv::Mat& mask, + const cv::Point& tl, + const cv::Scalar& color) +{ + mask.convertTo(mask, CV_32FC1, 1 / 255.0); + cv::Mat color_mask; + + cv::merge(std::vector(3, mask), color_mask); + cv::Scalar color32f = color / 255.0; + cv::multiply(color_mask, color32f, color_mask); + + blendImage(color_mask, mask, tl, img); +} inline void poly(cv::Mat& mat, const cv::gapi::wip::draw::Poly& pp) @@ -80,8 +103,16 @@ struct EmptyConverter // FIXME util::visitor ? template -void drawPrimitivesOCV(cv::Mat &in, const Prims &prims) +void drawPrimitivesOCV(cv::Mat& in, + const cv::gapi::wip::draw::Prims& prims, + cv::gapi::wip::draw::FTTextRender* ftpr) { +#ifndef HAVE_FREETYPE + cv::util::suppress_unused_warning(ftpr); +#endif + + using namespace cv::gapi::wip::draw; + ColorConverter converter; for (const auto &p : prims) { @@ -95,11 +126,54 @@ void drawPrimitivesOCV(cv::Mat &in, const Prims &prims) break; } + // FIXME avoid code duplicate for Text and FText case Prim::index_of(): { - const auto& tp = cv::util::get(p); - const auto color = converter.cvtColor(tp.color); - cv::putText(in, tp.text, tp.org, tp.ff, tp.fs, color, tp.thick, tp.lt, tp.bottom_left_origin); + auto tp = cv::util::get(p); + tp.color = converter.cvtColor(tp.color); + + int baseline = 0; + auto size = cv::getTextSize(tp.text, tp.ff, tp.fs, tp.thick, &baseline); + baseline += tp.thick; + size.height += baseline; + + // Allocate mask outside + cv::Mat mask(size, CV_8UC1, cv::Scalar::all(0)); + // Org it's bottom left position for baseline + cv::Point org(0, mask.rows - baseline); + cv::putText(mask, tp.text, org, tp.ff, tp.fs, 255, tp.thick); + + // Org is bottom left point, trasform it to top left point for blendImage + cv::Point tl(tp.org.x, tp.org.y - mask.size().height + baseline); + + blendTextMask(in, mask, tl, tp.color); + break; + } + + case Prim::index_of(): + { +#ifdef HAVE_FREETYPE + const auto& ftp = cv::util::get(p); + const auto color = converter.cvtColor(ftp.color); + + GAPI_Assert(ftpr && "I must pass cv::gapi::wip::draw::freetype_font" + " to the graph compile arguments"); + int baseline = 0; + auto size = ftpr->getTextSize(ftp.text, ftp.fh, &baseline); + + // Allocate mask outside + cv::Mat mask(size, CV_8UC1, cv::Scalar::all(0)); + // Org it's bottom left position for baseline + cv::Point org(0, mask.rows - baseline); + ftpr->putText(mask, ftp.text, org, ftp.fh); + + // Org is bottom left point, trasform it to top left point for blendImage + cv::Point tl(ftp.org.x, ftp.org.y - mask.size().height + baseline); + + blendTextMask(in, mask, tl, color); +#else + cv::util::throw_error(std::runtime_error("FreeType not found !")); +#endif break; } @@ -134,7 +208,8 @@ void drawPrimitivesOCV(cv::Mat &in, const Prims &prims) cv::Mat img; converter.cvtImg(ip.img, img); - image(in, ip.org, img, ip.alpha); + img.convertTo(img, CV_32FC1, 1.0 / 255); + blendImage(img, ip.alpha, ip.org, in); break; } @@ -151,14 +226,18 @@ void drawPrimitivesOCV(cv::Mat &in, const Prims &prims) } } -void drawPrimitivesOCVBGR(cv::Mat &in, const Prims &prims) +void drawPrimitivesOCVBGR(cv::Mat &in, + const cv::gapi::wip::draw::Prims &prims, + cv::gapi::wip::draw::FTTextRender* ftpr) { - drawPrimitivesOCV(in, prims); + drawPrimitivesOCV(in, prims, ftpr); } -void drawPrimitivesOCVYUV(cv::Mat &in, const Prims &prims) +void drawPrimitivesOCVYUV(cv::Mat &in, + const cv::gapi::wip::draw::Prims &prims, + cv::gapi::wip::draw::FTTextRender* ftpr) { - drawPrimitivesOCV(in, prims); + drawPrimitivesOCV(in, prims, ftpr); } } // namespace draw diff --git a/modules/gapi/src/api/render_ocv.hpp b/modules/gapi/src/api/render_ocv.hpp index ff8b628a7a..91194dcdc1 100644 --- a/modules/gapi/src/api/render_ocv.hpp +++ b/modules/gapi/src/api/render_ocv.hpp @@ -1,5 +1,6 @@ #include #include "render_priv.hpp" +#include "ft_render.hpp" #ifndef OPENCV_RENDER_OCV_HPP #define OPENCV_RENDER_OCV_HPP @@ -14,8 +15,8 @@ namespace draw { // FIXME only for tests -void GAPI_EXPORTS drawPrimitivesOCVYUV(cv::Mat &yuv, const Prims &prims); -void GAPI_EXPORTS drawPrimitivesOCVBGR(cv::Mat &bgr, const Prims &prims); +void GAPI_EXPORTS drawPrimitivesOCVYUV(cv::Mat& yuv, const Prims& prims, cv::gapi::wip::draw::FTTextRender* mc); +void GAPI_EXPORTS drawPrimitivesOCVBGR(cv::Mat& bgr, const Prims& prims, cv::gapi::wip::draw::FTTextRender* mc); } // namespace draw } // namespace wip diff --git a/modules/gapi/src/backends/render/grenderocv.cpp b/modules/gapi/src/backends/render/grenderocv.cpp index 47bf8f1e0f..cb4fd1be3a 100644 --- a/modules/gapi/src/backends/render/grenderocv.cpp +++ b/modules/gapi/src/backends/render/grenderocv.cpp @@ -7,7 +7,10 @@ GAPI_RENDER_OCV_KERNEL(RenderBGROCVImpl, cv::gapi::wip::draw::GRenderBGR) { - static void run(const cv::Mat& in, const cv::gapi::wip::draw::Prims& prims, cv::Mat& out) + static void run(const cv::Mat& in, + const cv::gapi::wip::draw::Prims& prims, + cv::gapi::wip::draw::FTTextRender* ftpr, + cv::Mat& out) { // NB: If in and out cv::Mats are the same object // we can avoid copy and render on out cv::Mat @@ -16,7 +19,7 @@ GAPI_RENDER_OCV_KERNEL(RenderBGROCVImpl, cv::gapi::wip::draw::GRenderBGR) in.copyTo(out); } - cv::gapi::wip::draw::drawPrimitivesOCVBGR(out, prims); + cv::gapi::wip::draw::drawPrimitivesOCVBGR(out, prims, ftpr); } }; @@ -25,6 +28,7 @@ GAPI_RENDER_OCV_KERNEL(RenderNV12OCVImpl, cv::gapi::wip::draw::GRenderNV12) static void run(const cv::Mat& in_y, const cv::Mat& in_uv, const cv::gapi::wip::draw::Prims& prims, + cv::gapi::wip::draw::FTTextRender* ftpr, cv::Mat& out_y, cv::Mat& out_uv) { @@ -63,7 +67,7 @@ GAPI_RENDER_OCV_KERNEL(RenderNV12OCVImpl, cv::gapi::wip::draw::GRenderNV12) cv::resize(in_uv, upsample_uv, in_uv.size() * 2, cv::INTER_LINEAR); cv::merge(std::vector{in_y, upsample_uv}, yuv); - cv::gapi::wip::draw::drawPrimitivesOCVYUV(yuv, prims); + cv::gapi::wip::draw::drawPrimitivesOCVYUV(yuv, prims, ftpr); // YUV -> NV12 cv::Mat out_u, out_v, uv_plane; diff --git a/modules/gapi/src/backends/render/grenderocv.hpp b/modules/gapi/src/backends/render/grenderocv.hpp index 5ec9c86c9e..e5091042b2 100644 --- a/modules/gapi/src/backends/render/grenderocv.hpp +++ b/modules/gapi/src/backends/render/grenderocv.hpp @@ -8,6 +8,8 @@ #define OPENCV_GAPI_GRENDEROCV_HPP #include +#include "api/render_priv.hpp" +#include "api/ft_render.hpp" namespace cv { @@ -33,10 +35,8 @@ template class GRenderKernelImpl: public cv::detail::OCVCallHelper, public cv::detail::KernelTag { - // TODO Use this mechanism for adding new parameter to run method - // using InArgs = typename add_type_to_tuple::type; - using InArgs = typename K::InArgs; - using P = detail::OCVCallHelper; + using InArgs = typename add_type_to_tuple::type; + using P = detail::OCVCallHelper; public: using API = K; diff --git a/modules/gapi/src/backends/render/grenderocvbackend.cpp b/modules/gapi/src/backends/render/grenderocvbackend.cpp index aad52c221c..4fde80b2a0 100644 --- a/modules/gapi/src/backends/render/grenderocvbackend.cpp +++ b/modules/gapi/src/backends/render/grenderocvbackend.cpp @@ -29,6 +29,10 @@ #include "backends/render/grenderocvbackend.hpp" +#include +#include "api/ocv_mask_creator.hpp" +#include "api/ft_render.hpp" + using GRenderModel = ade::TypedGraph < cv::gimpl::render::ocv::RenderUnit @@ -40,8 +44,9 @@ using GConstRenderModel = ade::ConstTypedGraph >; cv::gimpl::render::ocv::GRenderExecutable::GRenderExecutable(const ade::Graph &g, - const std::vector &nodes) - : m_g(g), m_gm(m_g) { + const std::vector &nodes, + std::unique_ptr&& ftpr) + : m_g(g), m_gm(m_g), m_ftpr(std::move(ftpr)) { GConstRenderModel gcm(m_g); auto is_op = [&](ade::NodeHandle nh) { @@ -86,6 +91,8 @@ void cv::gimpl::render::ocv::GRenderExecutable::run(std::vector &&input_ auto k = gcm.metadata(this_nh).get().k; + context.m_args.emplace_back(m_ftpr.get()); + k.apply(context); for (auto &it : output_objs) magazine::writeBack(m_res, it.first, it.second); @@ -125,12 +132,22 @@ namespace { } virtual EPtr compile(const ade::Graph &graph, - const cv::GCompileArgs&, + const cv::GCompileArgs& args, const std::vector &nodes) const override { - return EPtr{new cv::gimpl::render::ocv::GRenderExecutable(graph, nodes)}; + using namespace cv::gapi::wip::draw; + auto has_freetype_font = cv::gimpl::getCompileArg(args); + std::unique_ptr ftpr; + if (has_freetype_font) + { +#ifndef HAVE_FREETYPE + throw std::runtime_error("Freetype not found"); +#else + ftpr.reset(new FTTextRender(has_freetype_font.value().path)); +#endif + } + return EPtr{new cv::gimpl::render::ocv::GRenderExecutable(graph, nodes, std::move(ftpr))}; } - }; } diff --git a/modules/gapi/src/backends/render/grenderocvbackend.hpp b/modules/gapi/src/backends/render/grenderocvbackend.hpp index 5d3222c4c6..69d388ffe6 100644 --- a/modules/gapi/src/backends/render/grenderocvbackend.hpp +++ b/modules/gapi/src/backends/render/grenderocvbackend.hpp @@ -38,6 +38,7 @@ class GRenderExecutable final: public GIslandExecutable { const ade::Graph &m_g; GModel::ConstGraph m_gm; + std::unique_ptr m_ftpr; // The only executable stuff in this graph // (assuming it is always single-op) @@ -51,7 +52,8 @@ class GRenderExecutable final: public GIslandExecutable public: GRenderExecutable(const ade::Graph &graph, - const std::vector &nodes); + const std::vector &nodes, + std::unique_ptr&& ftpr); virtual inline bool canReshape() const override { return false; } diff --git a/modules/gapi/test/common/gapi_render_tests.hpp b/modules/gapi/test/common/gapi_render_tests.hpp index 346fd531ec..1f28e92a69 100644 --- a/modules/gapi/test/common/gapi_render_tests.hpp +++ b/modules/gapi/test/common/gapi_render_tests.hpp @@ -132,6 +132,14 @@ struct Fixture : public RenderBGRTestBase API { \ GAPI_RENDER_TEST_FIXTURE_NV12(RenderNV12##Fixture, GET_VA_ARGS(API), Number, __VA_ARGS__) \ using Points = std::vector; +GAPI_RENDER_TEST_FIXTURES(TestTexts, FIXTURE_API(std::string, cv::Point, double, cv::Scalar), 4, text, org, fs, color) +GAPI_RENDER_TEST_FIXTURES(TestRects, FIXTURE_API(cv::Rect, cv::Scalar, int), 3, rect, color, thick) +GAPI_RENDER_TEST_FIXTURES(TestCircles, FIXTURE_API(cv::Point, int, cv::Scalar, int), 4, center, radius, color, thick) +GAPI_RENDER_TEST_FIXTURES(TestLines, FIXTURE_API(cv::Point, cv::Point, cv::Scalar, int), 4, pt1, pt2, color, thick) +GAPI_RENDER_TEST_FIXTURES(TestMosaics, FIXTURE_API(cv::Rect, int, int), 3, mos, cellsz, decim) +GAPI_RENDER_TEST_FIXTURES(TestImages, FIXTURE_API(cv::Rect, cv::Scalar, double), 3, rect, color, transparency) +GAPI_RENDER_TEST_FIXTURES(TestPolylines, FIXTURE_API(Points, cv::Scalar, int), 3, points, color, thick) + } // opencv_test #endif //OPENCV_GAPI_RENDER_TESTS_HPP diff --git a/modules/gapi/test/common/gapi_tests_common.hpp b/modules/gapi/test/common/gapi_tests_common.hpp index 6d6ede5b8e..08388491ce 100644 --- a/modules/gapi/test/common/gapi_tests_common.hpp +++ b/modules/gapi/test/common/gapi_tests_common.hpp @@ -96,6 +96,7 @@ public: in_mat2 = cv::Mat(sz_in, type); sc = initScalarRandU(100); + cv::randu(in_mat1, cv::Scalar::all(0), cv::Scalar::all(255)); cv::randu(in_mat2, cv::Scalar::all(0), cv::Scalar::all(255)); diff --git a/modules/gapi/test/render/ftp_render_test.cpp b/modules/gapi/test/render/ftp_render_test.cpp new file mode 100644 index 0000000000..5bdbb74e30 --- /dev/null +++ b/modules/gapi/test/render/ftp_render_test.cpp @@ -0,0 +1,73 @@ +// 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" + +#ifdef HAVE_FREETYPE + +#include + +#include + +#include "api/ft_render.hpp" + +namespace opencv_test +{ + static std::string getFontPath() + { + static std::string path = cv::utils::getConfigurationParameterString("OPENCV_TEST_FREETYPE_FONT_PATH", + "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"); + return path; + } + + inline void RunTest(const std::string& font, + size_t num_iters, + size_t lower_char_code, + size_t upper_char_code) + { + cv::gapi::wip::draw::FTTextRender ftpr(font); + + std::mt19937 gen{std::random_device()()}; + std::uniform_int_distribution dist(lower_char_code, upper_char_code); + std::uniform_int_distribution dist_size(2, 200); + + for (size_t i = 0; i < num_iters; ++i) + { + size_t text_size = dist_size(gen); + std::wstring text; + + for (size_t j = 0; j < text_size; ++j) + { + wchar_t c = dist(gen); + text += c; + } + + int fh = dist_size(gen); + int baseline = 0; + cv::Size size; + + ASSERT_NO_THROW(size = ftpr.getTextSize(text, fh, &baseline)); + + cv::Mat bmp(size, CV_8UC1, cv::Scalar::all(0)); + cv::Point org(0, bmp.rows - baseline); + + ASSERT_NO_THROW(ftpr.putText(bmp, text, org, fh)); + } + } + + TEST(FTTextRenderTest, Smoke_Test_Ascii) + { + RunTest(getFontPath(), 2000, 32, 126); + } + + TEST(FTTextRenderTest, Smoke_Test_Unicode) + { + RunTest(getFontPath(), 2000, 20320, 30000); + } +} // namespace opencv_test + +#endif // HAVE_FREETYPE diff --git a/modules/gapi/test/render/gapi_render_tests_ocv.cpp b/modules/gapi/test/render/gapi_render_tests_ocv.cpp index 1ea3da14fd..19701c8348 100644 --- a/modules/gapi/test/render/gapi_render_tests_ocv.cpp +++ b/modules/gapi/test/render/gapi_render_tests_ocv.cpp @@ -5,6 +5,10 @@ // Copyright (C) 2018 Intel Corporation +#ifdef HAVE_FREETYPE +#include +#endif // HAVE_FREETYPE + #include "../test_precomp.hpp" #include "../common/gapi_render_tests.hpp" @@ -13,6 +17,10 @@ namespace opencv_test { +#ifdef HAVE_FREETYPE +GAPI_RENDER_TEST_FIXTURES(OCVTestFTexts, FIXTURE_API(std::wstring, cv::Point, int, cv::Scalar), 4, text, org, fh, color) +#endif // HAVE_FREETYPE + GAPI_RENDER_TEST_FIXTURES(OCVTestTexts, FIXTURE_API(std::string, cv::Point, int, double, cv::Scalar, int, int, bool), 8, text, org, ff, fs, color, thick, lt, blo) GAPI_RENDER_TEST_FIXTURES(OCVTestRects, FIXTURE_API(cv::Rect, cv::Scalar, int, int, int), 5, rect, color, thick, lt, shift) GAPI_RENDER_TEST_FIXTURES(OCVTestCircles, FIXTURE_API(cv::Point, int, cv::Scalar, int, int, int), 6, center, radius, color, thick, lt, shift) @@ -65,6 +73,54 @@ TEST_P(RenderNV12OCVTestTexts, AccuracyTest) } } +# ifdef HAVE_FREETYPE + +TEST_P(RenderBGROCVTestFTexts, AccuracyTest) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::gapi::wip::draw::Prims prims; + prims.emplace_back(cv::gapi::wip::draw::FText{text, org, fh, color}); + EXPECT_NO_THROW(cv::gapi::wip::draw::render(gapi_mat, prims, + cv::compile_args(cv::gapi::wip::draw::freetype_font{ + "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc" + }))); +} + +TEST_P(RenderNV12OCVTestFTexts, AccuracyTest) +{ + // G-API code ////////////////////////////////////////////////////////////// + cv::gapi::wip::draw::Prims prims; + prims.emplace_back(cv::gapi::wip::draw::FText{text, org, fh, color}); + EXPECT_NO_THROW(cv::gapi::wip::draw::render(y_gapi_mat, uv_gapi_mat, prims, + cv::compile_args(cv::gapi::wip::draw::freetype_font{ + "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc" + }))); + +} + +static std::wstring to_wstring(const char* bytes) +{ + std::wstring_convert, wchar_t> converter; + return converter.from_bytes(bytes); +} + +TEST(RenderFText, FontsNotPassedToCompileArgs) +{ + cv::Mat in_mat(640, 480, CV_8UC3, cv::Scalar::all(0)); + + std::wstring text = to_wstring("\xe4\xbd\xa0\xe5\xa5\xbd"); + cv::Point org(100, 100); + int fh = 60; + cv::Scalar color(200, 100, 25); + + cv::gapi::wip::draw::Prims prims; + prims.emplace_back(cv::gapi::wip::draw::FText{text, org, fh, color}); + + EXPECT_ANY_THROW(cv::gapi::wip::draw::render(in_mat, prims)); +} + +#endif // HAVE_FREETYPE + TEST_P(RenderBGROCVTestRects, AccuracyTest) { // G-API code ////////////////////////////////////////////////////////////// @@ -418,6 +474,25 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestTextsImpl, RenderNV12OCVTestTexts, Values(LINE_8), Values(false))); +#ifdef HAVE_FREETYPE + +INSTANTIATE_TEST_CASE_P(RenderBGROCVTestFTextsImpl, RenderBGROCVTestFTexts, + Combine(Values(cv::Size(1280, 720)), + Values(to_wstring("\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c"), + to_wstring("\xe3\x80\xa4\xe3\x80\xa5\xe3\x80\xa6\xe3\x80\xa7\xe3\x80\xa8\xe3\x80\x85\xe3\x80\x86")), + Values(cv::Point(200, 200)), + Values(64), + Values(cv::Scalar(0, 255, 0)))); + +INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestFTextsImpl, RenderNV12OCVTestFTexts, + Combine(Values(cv::Size(1280, 720)), + Values(to_wstring("\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x8c\xe4\xb8\x96\xe7\x95\x8c"), + to_wstring("\xe3\x80\xa4\xe3\x80\xa5\xe3\x80\xa6\xe3\x80\xa7\xe3\x80\xa8\xe3\x80\x85\xe3\x80\x86")), + Values(cv::Point(200, 200)), + Values(64), + Values(cv::Scalar(0, 255, 0)))); +#endif // HAVE_FREETYPE + INSTANTIATE_TEST_CASE_P(RenderBGROCVTestMosaicsImpl, RenderBGROCVTestMosaics, Combine(Values(cv::Size(1280, 720)), Values(cv::Rect(100, 100, 200, 200)),