mirror of
https://github.com/opencv/opencv.git
synced 2025-08-05 22:19:14 +08:00
Merge pull request #15972 from TolyaTalamanov:at/ftext-primitive
This commit is contained in:
commit
529a241a26
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
214
modules/gapi/src/api/ft_render.cpp
Normal file
214
modules/gapi/src/api/ft_render.cpp
Normal file
@ -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
|
52
modules/gapi/src/api/ft_render.hpp
Normal file
52
modules/gapi/src/api/ft_render.hpp
Normal file
@ -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
|
48
modules/gapi/src/api/ft_render_priv.hpp
Normal file
48
modules/gapi/src/api/ft_render_priv.hpp
Normal file
@ -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
|
0
modules/gapi/src/api/ocv_mask_creator.hpp
Normal file
0
modules/gapi/src/api/ocv_mask_creator.hpp
Normal 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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -29,6 +29,10 @@
|
||||
|
||||
#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))};
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
73
modules/gapi/test/render/ftp_render_test.cpp
Normal file
73
modules/gapi/test/render/ftp_render_test.cpp
Normal file
@ -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
|
@ -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"
|
||||
|
||||
@ -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<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)),
|
||||
|
Loading…
Reference in New Issue
Block a user