Implement cv::gapi::wip::draw::FText
authorTalamanov, Anatoliy <anatoliy.talamanov@intel.com>
Tue, 3 Dec 2019 10:13:06 +0000 (13:13 +0300)
committerTalamanov, Anatoliy <anatoliy.talamanov@intel.com>
Tue, 3 Dec 2019 10:13:06 +0000 (13:13 +0300)
18 files changed:
modules/gapi/CMakeLists.txt
modules/gapi/cmake/init.cmake
modules/gapi/include/opencv2/gapi/render/render.hpp
modules/gapi/src/api/ft_render.cpp [new file with mode: 0644]
modules/gapi/src/api/ft_render.hpp [new file with mode: 0644]
modules/gapi/src/api/ft_render_priv.hpp [new file with mode: 0644]
modules/gapi/src/api/ocv_mask_creator.hpp [new file with mode: 0644]
modules/gapi/src/api/render.cpp
modules/gapi/src/api/render_ocv.cpp
modules/gapi/src/api/render_ocv.hpp
modules/gapi/src/backends/render/grenderocv.cpp
modules/gapi/src/backends/render/grenderocv.hpp
modules/gapi/src/backends/render/grenderocvbackend.cpp
modules/gapi/src/backends/render/grenderocvbackend.hpp
modules/gapi/test/common/gapi_render_tests.hpp
modules/gapi/test/common/gapi_tests_common.hpp
modules/gapi/test/render/ftp_render_test.cpp [new file with mode: 0644]
modules/gapi/test/render/gapi_render_tests_ocv.cpp

index cd12f90..b7cd7f4 100644 (file)
@@ -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)
index 026adf3..4c25c75 100644 (file)
@@ -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)
index fe9daac..845c1db 100644 (file)
@@ -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)
@@ -55,6 +55,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
  */
 struct Rect
@@ -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 (file)
index 0000000..9d9f6b3
--- /dev/null
@@ -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 <opencv2/gapi/util/throw.hpp>
+#include <opencv2/gapi/own/assert.hpp>
+
+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 (file)
index 0000000..2556c72
--- /dev/null
@@ -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 <memory>
+#include <string>
+
+#include <opencv2/core.hpp>
+
+#include <opencv2/gapi/own/exports.hpp>
+
+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<Priv> 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 (file)
index 0000000..5a0679d
--- /dev/null
@@ -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 <ft2build.h>
+#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 (file)
index 0000000..e69de29
index f9c6112..9f4c18f 100644 (file)
@@ -1,3 +1,7 @@
+#include "precomp.hpp"
+
+#include <stdexcept>
+
 #include <opencv2/imgproc.hpp>
 #include <opencv2/gapi/render/render.hpp>
 #include <opencv2/gapi/own/assert.hpp>
@@ -9,7 +13,7 @@ void cv::gapi::wip::draw::render(cv::Mat& bgr,
                                  cv::GCompileArgs&& args)
 {
     cv::GMat in;
-    cv::GArray<Prim> arr;
+    cv::GArray<cv::gapi::wip::draw::Prim> 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<Prim> arr;
+    cv::GArray<cv::gapi::wip::draw::Prim> 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<cv::Mat> chs(3);
     cv::split(yuv, chs);
     y = chs[0];
-
     cv::merge(std::vector<cv::Mat>{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<cv::gapi::wip::draw::use_freetype>
+    template<> struct CompileArgTag<cv::gapi::wip::draw::freetype_font>
     {
-        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<Prim>& prims)
+GMat cv::gapi::wip::draw::render3ch(const GMat& src,
+                                    const GArray<cv::gapi::wip::draw::Prim>& prims)
 {
     return cv::gapi::wip::draw::GRenderBGR::on(src, prims);
 }
index a57707f..6c035f6 100644 (file)
@@ -2,6 +2,7 @@
 #include <opencv2/gapi/render/render.hpp> // 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<cv::Mat>(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<cv::Mat>(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 <typename ColorConverter>
-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<Text>():
             {
-                const auto& tp = cv::util::get<Text>(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<Text>(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<FText>():
+            {
+#ifdef HAVE_FREETYPE
+                const auto& ftp  = cv::util::get<FText>(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<EmptyConverter>(in, prims);
+    drawPrimitivesOCV<EmptyConverter>(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<BGR2YUVConverter>(in, prims);
+    drawPrimitivesOCV<BGR2YUVConverter>(in, prims, ftpr);
 }
 
 } // namespace draw
index ff8b628..91194dc 100644 (file)
@@ -1,5 +1,6 @@
 #include <vector>
 #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
index 47bf8f1..cb4fd1b 100644 (file)
@@ -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<cv::Mat>{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;
index 5ec9c86..e509104 100644 (file)
@@ -8,6 +8,8 @@
 #define OPENCV_GAPI_GRENDEROCV_HPP
 
 #include <opencv2/gapi/cpu/gcpukernel.hpp>
+#include "api/render_priv.hpp"
+#include "api/ft_render.hpp"
 
 namespace cv
 {
@@ -33,10 +35,8 @@ template<class Impl, class K>
 class GRenderKernelImpl: public cv::detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>,
                          public cv::detail::KernelTag
 {
-    // TODO Use this mechanism for adding new parameter to run method
-    // using InArgs = typename add_type_to_tuple<IBitMaskCreator, typename K::InArgs>::type;
-    using InArgs = typename K::InArgs;
-    using P = detail::OCVCallHelper<Impl, InArgs, typename K::OutArgs>;
+    using InArgs = typename add_type_to_tuple<cv::gapi::wip::draw::FTTextRender*, typename K::InArgs>::type;
+    using P      = detail::OCVCallHelper<Impl, InArgs, typename K::OutArgs>;
 
 public:
     using API = K;
index aad52c2..4fde80b 100644 (file)
 
 #include "backends/render/grenderocvbackend.hpp"
 
+#include <opencv2/gapi/render/render.hpp>
+#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<ade::NodeHandle> &nodes)
-    : m_g(g), m_gm(m_g) {
+                                                             const std::vector<ade::NodeHandle> &nodes,
+                                                             std::unique_ptr<cv::gapi::wip::draw::FTTextRender>&& 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<InObj>  &&input_
 
     auto k = gcm.metadata(this_nh).get<RenderUnit>().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<ade::NodeHandle> &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<freetype_font>(args);
+            std::unique_ptr<FTTextRender> 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))};
         }
-
     };
 }
 
index 5d3222c..69d388f 100644 (file)
@@ -38,6 +38,7 @@ class GRenderExecutable final: public GIslandExecutable
 {
     const ade::Graph &m_g;
     GModel::ConstGraph m_gm;
+    std::unique_ptr<cv::gapi::wip::draw::FTTextRender> 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<ade::NodeHandle> &nodes);
+                      const std::vector<ade::NodeHandle> &nodes,
+                      std::unique_ptr<cv::gapi::wip::draw::FTTextRender>&& ftpr);
 
     virtual inline bool canReshape() const override { return false; }
 
index 346fd53..1f28e92 100644 (file)
@@ -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<cv::Point>;
+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
index 6d6ede5..0838849 100644 (file)
@@ -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 (file)
index 0000000..5bdbb74
--- /dev/null
@@ -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 <random>
+
+#include <opencv2/core/utils/configuration.private.hpp>
+
+#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<int> dist(lower_char_code, upper_char_code);
+        std::uniform_int_distribution<int> 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
index 1ea3da1..19701c8 100644 (file)
@@ -5,6 +5,10 @@
 // Copyright (C) 2018 Intel Corporation
 
 
+#ifdef HAVE_FREETYPE
+#include <codecvt>
+#endif // HAVE_FREETYPE
+
 #include "../test_precomp.hpp"
 #include "../common/gapi_render_tests.hpp"
 
 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<std::codecvt_utf8<wchar_t>, 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)),