#include #include "api/render_ocv.hpp" #include #include struct RenderOCVState { std::shared_ptr ftpr; }; GAPI_OCV_KERNEL_ST(RenderBGROCVImpl, cv::gapi::wip::draw::GRenderBGR, RenderOCVState) { static void run(const cv::Mat& in, const cv::gapi::wip::draw::Prims& prims, cv::Mat& out, RenderOCVState& state) { // NB: If in and out cv::Mats are the same object // we can avoid copy and render on out cv::Mat // It's work if this kernel is last operation in the graph if (in.data != out.data) { in.copyTo(out); } cv::gapi::wip::draw::drawPrimitivesOCVBGR(out, prims, state.ftpr); } static void setup(const cv::GMatDesc& /* in */, const cv::GArrayDesc& /* prims */, std::shared_ptr& state, const cv::GCompileArgs& args) { using namespace cv::gapi::wip::draw; auto opt_freetype_font = cv::gapi::getCompileArg(args); state = std::make_shared(); if (opt_freetype_font.has_value()) { state->ftpr = std::make_shared(opt_freetype_font->path); } } }; GAPI_OCV_KERNEL_ST(RenderNV12OCVImpl, cv::gapi::wip::draw::GRenderNV12, RenderOCVState) { static void run(const cv::Mat& in_y, const cv::Mat& in_uv, const cv::gapi::wip::draw::Prims& prims, cv::Mat& out_y, cv::Mat& out_uv, RenderOCVState& state) { // NB: If in and out cv::Mats are the same object // we can avoid copy and render on out cv::Mat // It's work if this kernel is last operation in the graph if (in_y.data != out_y.data) { in_y.copyTo(out_y); } if (in_uv.data != out_uv.data) { in_uv.copyTo(out_uv); } /* FIXME How to render correctly on NV12 format ? * * Rendering on NV12 via OpenCV looks like this: * * y --------> 1)(NV12 -> YUV) -> yuv -> 2)draw -> yuv -> 3)split -------> out_y * ^ | * | | * uv -------------- `----------> out_uv * * * 1) Collect yuv mat from two planes, uv plain in two times less than y plane * so, upsample uv in tow times, with bilinear interpolation * * 2) Render primitives on YUV * * 3) Convert yuv to NV12 (using bilinear interpolation) * */ // NV12 -> YUV cv::Mat upsample_uv, yuv; 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, state.ftpr); // YUV -> NV12 cv::Mat out_u, out_v, uv_plane; std::vector chs = {out_y, out_u, out_v}; cv::split(yuv, chs); cv::merge(std::vector{chs[1], chs[2]}, uv_plane); cv::resize(uv_plane, out_uv, uv_plane.size() / 2, cv::INTER_LINEAR); } static void setup(const cv::GMatDesc& /* in_y */, const cv::GMatDesc& /* in_uv */, const cv::GArrayDesc& /* prims */, std::shared_ptr& state, const cv::GCompileArgs& args) { using namespace cv::gapi::wip::draw; auto has_freetype_font = cv::gapi::getCompileArg(args); state = std::make_shared(); if (has_freetype_font) { state->ftpr = std::make_shared(has_freetype_font->path); } } }; GAPI_OCV_KERNEL_ST(RenderFrameOCVImpl, cv::gapi::wip::draw::GRenderFrame, RenderOCVState) { static void run(const cv::MediaFrame & in, const cv::gapi::wip::draw::Prims & prims, cv::MediaFrame & out, RenderOCVState & state) { GAPI_Assert(in.desc().fmt == cv::MediaFormat::NV12); // FIXME: consider a better approach (aka native inplace operation) // Non-intuitive logic with shared_ptr Priv class out = in; auto desc = out.desc(); auto w_out = out.access(cv::MediaFrame::Access::W); auto out_y = cv::Mat(desc.size, CV_8UC1, w_out.ptr[0], w_out.stride[0]); auto out_uv = cv::Mat(desc.size / 2, CV_8UC2, w_out.ptr[1], w_out.stride[1]); auto r_in = in.access(cv::MediaFrame::Access::R); auto in_y = cv::Mat(desc.size, CV_8UC1, r_in.ptr[0], r_in.stride[0]); auto in_uv = cv::Mat(desc.size / 2, CV_8UC2, r_in.ptr[1], r_in.stride[1]); /* FIXME How to render correctly on NV12 format ? * * Rendering on NV12 via OpenCV looks like this: * * y --------> 1)(NV12 -> YUV) -> yuv -> 2)draw -> yuv -> 3)split -------> out_y * ^ | * | | * uv -------------- `----------> out_uv * * * 1) Collect yuv mat from two planes, uv plain in two times less than y plane * so, upsample uv in two times, with bilinear interpolation * * 2) Render primitives on YUV * * 3) Convert yuv to NV12 (using bilinear interpolation) * */ // NV12 -> YUV cv::Mat upsample_uv, yuv; 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, state.ftpr); // YUV -> NV12 cv::Mat out_u, out_v, uv_plane; std::vector chs = { out_y, out_u, out_v }; cv::split(yuv, chs); cv::merge(std::vector{chs[1], chs[2]}, uv_plane); cv::resize(uv_plane, out_uv, uv_plane.size() / 2, cv::INTER_LINEAR); } static void setup(const cv::GFrameDesc& /* in_nv12 */, const cv::GArrayDesc& /* prims */, std::shared_ptr&state, const cv::GCompileArgs & args) { using namespace cv::gapi::wip::draw; auto has_freetype_font = cv::gapi::getCompileArg(args); state = std::make_shared(); if (has_freetype_font) { state->ftpr = std::make_shared(has_freetype_font->path); } } }; cv::gapi::GKernelPackage cv::gapi::render::ocv::kernels() { const static auto pkg = cv::gapi::kernels(); return pkg; }