Merge pull request #19516 from dbudniko:dbudniko/gapi_render_frame_cpu
authorDmitry Budnikov <Dmitry.Budnikov@intel.com>
Mon, 15 Feb 2021 19:57:22 +0000 (22:57 +0300)
committerGitHub <noreply@github.com>
Mon, 15 Feb 2021 19:57:22 +0000 (19:57 +0000)
Render Frame NV12 on CPU

* render frame on CPU

* doxygen fix

* address review from Alexey

* comment fixes

* Address comments from Ruslan

* remove NV12 specific

* mat clone and more renaming

* misprint fix

modules/gapi/include/opencv2/gapi/render/render.hpp
modules/gapi/src/api/render.cpp
modules/gapi/src/backends/render/grenderocv.cpp
modules/gapi/test/common/gapi_render_tests.hpp
modules/gapi/test/render/gapi_render_tests_ocv.cpp

index a4df304..a84c26c 100644 (file)
@@ -97,6 +97,17 @@ void GAPI_EXPORTS render(cv::Mat& y_plane,
                          const Prims& prims,
                          cv::GCompileArgs&& args = {});
 
+/** @brief The function renders on the input media frame passed drawing primitivies
+
+@param frame input Media Frame :  @ref cv::MediaFrame.
+@param prims vector of drawing primitivies
+@param args graph compile time parameters
+*/
+void GAPI_EXPORTS render(cv::MediaFrame& frame,
+                         const Prims& prims,
+                         cv::GCompileArgs&& args = {});
+
+
 G_TYPED_KERNEL_M(GRenderNV12, <GMat2(cv::GMat,cv::GMat,cv::GArray<wip::draw::Prim>)>, "org.opencv.render.nv12")
 {
      static GMatDesc2 outMeta(GMatDesc y_plane, GMatDesc uv_plane, GArrayDesc)
@@ -113,6 +124,14 @@ G_TYPED_KERNEL(GRenderBGR, <cv::GMat(cv::GMat,cv::GArray<wip::draw::Prim>)>, "or
      }
 };
 
+G_TYPED_KERNEL(GRenderFrame, <cv::GFrame(cv::GFrame, cv::GArray<wip::draw::Prim>)>, "org.opencv.render.frame")
+{
+    static GFrameDesc outMeta(GFrameDesc desc, GArrayDesc)
+    {
+        return desc;
+    }
+};
+
 /** @brief Renders on 3 channels input
 
 Output image must be 8-bit unsigned planar 3-channel image
@@ -134,6 +153,17 @@ uv image must be 8-bit unsigned planar 2-channel image @ref CV_8UC2
 GAPI_EXPORTS GMat2 renderNV12(const GMat& y,
                               const GMat& uv,
                               const GArray<Prim>& prims);
+
+/** @brief Renders Media Frame
+
+Output media frame frame cv::MediaFrame
+
+@param m_frame input image: cv::MediaFrame @ref cv::MediaFrame
+@param prims draw primitives
+*/
+GAPI_EXPORTS GFrame renderFrame(const GFrame& m_frame,
+                                const GArray<Prim>& prims);
+
 //! @} gapi_draw_api
 
 } // namespace draw
index 428ebda..cf0dac0 100644 (file)
@@ -33,6 +33,20 @@ void cv::gapi::wip::draw::render(cv::Mat& y_plane,
                cv::gout(y_plane, uv_plane), std::move(args));
 }
 
+void cv::gapi::wip::draw::render(cv::MediaFrame& frame,
+                                 const Prims& prims,
+                                 cv::GCompileArgs&& args)
+{
+    cv::GFrame in, out;
+    cv::GArray<cv::gapi::wip::draw::Prim> arr;
+    out = cv::gapi::wip::draw::renderFrame(in, arr);
+
+    cv::GComputation comp(cv::GIn(in, arr), cv::GOut(out));
+    comp.apply(cv::gin(frame, prims),
+               cv::gout(frame), std::move(args));
+}
+
+
 void cv::gapi::wip::draw::cvtYUVToNV12(const cv::Mat& yuv,
                                        cv::Mat& y,
                                        cv::Mat& uv)
@@ -69,3 +83,9 @@ cv::gapi::wip::draw::renderNV12(const cv::GMat& y,
 {
     return cv::gapi::wip::draw::GRenderNV12::on(y, uv, prims);
 }
+
+cv::GFrame cv::gapi::wip::draw::renderFrame(const cv::GFrame& frame,
+                                                const cv::GArray<cv::gapi::wip::draw::Prim>& prims)
+{
+    return cv::gapi::wip::draw::GRenderFrame::on(frame, prims);
+}
index 71be889..2652284 100644 (file)
@@ -114,8 +114,83 @@ GAPI_OCV_KERNEL_ST(RenderNV12OCVImpl, cv::gapi::wip::draw::GRenderNV12, RenderOC
     }
 };
 
+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<cv::Mat>{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<cv::Mat> chs = { out_y, out_u, out_v };
+        cv::split(yuv, chs);
+        cv::merge(std::vector<cv::Mat>{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<RenderOCVState>&state,
+        const cv::GCompileArgs & args)
+    {
+        using namespace cv::gapi::wip::draw;
+        auto has_freetype_font = cv::gapi::getCompileArg<freetype_font>(args);
+        state = std::make_shared<RenderOCVState>();
+
+        if (has_freetype_font)
+        {
+            state->ftpr = std::make_shared<FTTextRender>(has_freetype_font->path);
+        }
+    }
+};
+
+
 cv::gapi::GKernelPackage cv::gapi::render::ocv::kernels()
 {
-    const static auto pkg = cv::gapi::kernels<RenderBGROCVImpl, RenderNV12OCVImpl>();
+    const static auto pkg = cv::gapi::kernels<RenderBGROCVImpl, RenderNV12OCVImpl, RenderFrameOCVImpl>();
     return pkg;
 }
index 1f28e92..30caca9 100644 (file)
@@ -130,6 +130,8 @@ struct Fixture : public RenderBGRTestBase API {                  \
 #define GAPI_RENDER_TEST_FIXTURES(Fixture, API, Number, ...)                    \
     GAPI_RENDER_TEST_FIXTURE_BGR(RenderBGR##Fixture,   GET_VA_ARGS(API), Number, __VA_ARGS__) \
     GAPI_RENDER_TEST_FIXTURE_NV12(RenderNV12##Fixture, GET_VA_ARGS(API), Number, __VA_ARGS__) \
+    GAPI_RENDER_TEST_FIXTURE_NV12(RenderMFrame##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)
index 88b5d88..010df5d 100644 (file)
@@ -73,6 +73,54 @@ TEST_P(RenderNV12OCVTestTexts, AccuracyTest)
     }
 }
 
+class TestMediaNV12 final : public cv::MediaFrame::IAdapter {
+    cv::Mat m_y;
+    cv::Mat m_uv;
+public:
+    TestMediaNV12(cv::Mat y, cv::Mat uv) : m_y(y), m_uv(uv) {
+    }
+    cv::GFrameDesc meta() const override {
+        return cv::GFrameDesc{ cv::MediaFormat::NV12, cv::Size(m_y.cols, m_y.rows) };
+    }
+    cv::MediaFrame::View access(cv::MediaFrame::Access) override {
+        cv::MediaFrame::View::Ptrs pp = {
+            m_y.ptr(), m_uv.ptr(), nullptr, nullptr
+        };
+        cv::MediaFrame::View::Strides ss = {
+            m_y.step, m_uv.step, 0u, 0u
+        };
+        return cv::MediaFrame::View(std::move(pp), std::move(ss));
+    }
+};
+
+TEST_P(RenderMFrameOCVTestTexts, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Text{ text, org, ff, fs, color, thick, lt, blo });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        cv::putText(yuv, text, org, ff, fs, cvtBGRToYUVC(color), thick, lt, blo);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
+
 # ifdef HAVE_FREETYPE
 
 TEST_P(RenderBGROCVTestFTexts, AccuracyTest)
@@ -97,6 +145,23 @@ TEST_P(RenderNV12OCVTestFTexts, AccuracyTest)
                                 })));
 }
 
+TEST_P(RenderMFrameOCVTestFTexts, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::Mat y_copy_mat = y_gapi_mat.clone();
+    cv::Mat uv_copy_mat = uv_gapi_mat.clone();
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::FText{ text, org, fh, color });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    EXPECT_NO_THROW(cv::gapi::wip::draw::render(nv12, prims,
+        cv::compile_args(cv::gapi::wip::draw::freetype_font{
+        "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
+            })));
+    EXPECT_NE(0, cv::norm(y_gapi_mat, y_copy_mat));
+    EXPECT_NE(0, cv::norm(uv_gapi_mat, uv_copy_mat));
+}
+
+
 static std::wstring to_wstring(const char* bytes)
 {
     std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
@@ -164,6 +229,33 @@ TEST_P(RenderNV12OCVTestRects, AccuracyTest)
     }
 }
 
+TEST_P(RenderMFrameOCVTestRects, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Rect{ rect, color, thick, lt, shift });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        cv::rectangle(yuv, rect, cvtBGRToYUVC(color), thick, lt, shift);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
 TEST_P(RenderBGROCVTestCircles, AccuracyTest)
 {
     // G-API code //////////////////////////////////////////////////////////////
@@ -208,6 +300,33 @@ TEST_P(RenderNV12OCVTestCircles, AccuracyTest)
     }
 }
 
+TEST_P(RenderMFrameOCVTestCircles, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Circle{ center, radius, color, thick, lt, shift });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        cv::circle(yuv, center, radius, cvtBGRToYUVC(color), thick, lt, shift);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
 TEST_P(RenderBGROCVTestLines, AccuracyTest)
 {
     // G-API code //////////////////////////////////////////////////////////////
@@ -252,6 +371,33 @@ TEST_P(RenderNV12OCVTestLines, AccuracyTest)
     }
 }
 
+TEST_P(RenderMFrameOCVTestLines, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Line{ pt1, pt2, color, thick, lt, shift });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        cv::line(yuv, pt1, pt2, cvtBGRToYUVC(color), thick, lt, shift);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
 TEST_P(RenderBGROCVTestMosaics, AccuracyTest)
 {
     // G-API code //////////////////////////////////////////////////////////////
@@ -296,6 +442,33 @@ TEST_P(RenderNV12OCVTestMosaics, AccuracyTest)
     }
 }
 
+TEST_P(RenderMFrameOCVTestMosaics, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Mosaic{ mos, cellsz, decim });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        drawMosaicRef(yuv, mos, cellsz);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
 TEST_P(RenderBGROCVTestImages, AccuracyTest)
 {
     cv::Mat img(rect.size(), CV_8UC3, color);
@@ -352,6 +525,40 @@ TEST_P(RenderNV12OCVTestImages, AccuracyTest)
     }
 }
 
+TEST_P(RenderMFrameOCVTestImages, AccuracyTest)
+{
+    cv::Mat img(rect.size(), CV_8UC3, color);
+    cv::Mat alpha(rect.size(), CV_32FC1, transparency);
+    auto tl = rect.tl();
+    cv::Point org = { tl.x, tl.y + rect.size().height };
+
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Image{ org, img, alpha });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        cv::Mat yuv_img;
+        cv::cvtColor(img, yuv_img, cv::COLOR_BGR2YUV);
+        blendImageRef(yuv, org, yuv_img, alpha);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
 TEST_P(RenderBGROCVTestPolylines, AccuracyTest)
 {
     // G-API code //////////////////////////////////////////////////////////////
@@ -398,6 +605,34 @@ TEST_P(RenderNV12OCVTestPolylines, AccuracyTest)
     }
 }
 
+TEST_P(RenderMFrameOCVTestPolylines, AccuracyTest)
+{
+    // G-API code //////////////////////////////////////////////////////////////
+    cv::gapi::wip::draw::Prims prims;
+    prims.emplace_back(cv::gapi::wip::draw::Poly{ points, color, thick, lt, shift });
+    cv::MediaFrame nv12 = cv::MediaFrame::Create<TestMediaNV12>(y_gapi_mat, uv_gapi_mat);
+    cv::gapi::wip::draw::render(nv12, prims);
+
+    // OpenCV code //////////////////////////////////////////////////////////////
+    {
+        // NV12 -> YUV
+        cv::Mat yuv;
+        cv::gapi::wip::draw::cvtNV12ToYUV(y_ref_mat, uv_ref_mat, yuv);
+
+        std::vector<std::vector<cv::Point>> pp{ points };
+        cv::fillPoly(yuv, pp, cvtBGRToYUVC(color), lt, shift);
+
+        // YUV -> NV12
+        cv::gapi::wip::draw::cvtYUVToNV12(yuv, y_ref_mat, uv_ref_mat);
+    }
+
+    // Comparison //////////////////////////////////////////////////////////////
+    {
+        EXPECT_EQ(0, cv::norm(y_gapi_mat, y_ref_mat));
+        EXPECT_EQ(0, cv::norm(uv_gapi_mat, uv_ref_mat));
+    }
+}
+
 // FIXME avoid code duplicate for NV12 and BGR cases
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestRectsImpl, RenderBGROCVTestRects,
                         Combine(Values(cv::Size(1280, 720)),
@@ -415,6 +650,14 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestRectsImpl, RenderNV12OCVTestRects,
                                        Values(LINE_8),
                                        Values(0)));
 
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestRectsImpl, RenderMFrameOCVTestRects,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values(cv::Rect(100, 100, 200, 200)),
+                                Values(cv::Scalar(100, 50, 150)),
+                                Values(2),
+                                Values(LINE_8),
+                                Values(0)));
+
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestCirclesImpl, RenderBGROCVTestCircles,
                         Combine(Values(cv::Size(1280, 720)),
                                 Values(cv::Point(100, 100)),
@@ -433,6 +676,15 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestCirclesImpl, RenderNV12OCVTestCircles,
                                 Values(LINE_8),
                                 Values(0)));
 
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestCirclesImpl, RenderMFrameOCVTestCircles,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values(cv::Point(100, 100)),
+                                Values(10),
+                                Values(cv::Scalar(100, 50, 150)),
+                                Values(2),
+                                Values(LINE_8),
+                                Values(0)));
+
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestLinesImpl, RenderBGROCVTestLines,
                         Combine(Values(cv::Size(1280, 720)),
                                 Values(cv::Point(100, 100)),
@@ -451,6 +703,15 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestLinesImpl, RenderNV12OCVTestLines,
                                 Values(LINE_8),
                                 Values(0)));
 
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestLinesImpl, RenderMFrameOCVTestLines,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values(cv::Point(100, 100)),
+                                Values(cv::Point(200, 200)),
+                                Values(cv::Scalar(100, 50, 150)),
+                                Values(2),
+                                Values(LINE_8),
+                                Values(0)));
+
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestTextsImpl, RenderBGROCVTestTexts,
                         Combine(Values(cv::Size(1280, 720)),
                                 Values("SomeText"),
@@ -473,6 +734,18 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestTextsImpl, RenderNV12OCVTestTexts,
                                 Values(LINE_8),
                                 Values(false)));
 
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestTextsImpl, RenderMFrameOCVTestTexts,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values("SomeText"),
+                                Values(cv::Point(200, 200)),
+                                Values(FONT_HERSHEY_SIMPLEX),
+                                Values(2.0),
+                                Values(cv::Scalar(0, 255, 0)),
+                                Values(2),
+                                Values(LINE_8),
+                                Values(false)));
+
+
 #ifdef HAVE_FREETYPE
 
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestFTextsImpl, RenderBGROCVTestFTexts,
@@ -490,6 +763,15 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestFTextsImpl, RenderNV12OCVTestFTexts,
                             Values(cv::Point(200, 200)),
                             Values(64),
                             Values(cv::Scalar(0, 255, 0))));
+
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestFTextsImpl, RenderMFrameOCVTestFTexts,
+                        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
 
 // FIXME Implement a macros to instantiate the tests because BGR and NV12 have the same parameters
@@ -530,6 +812,24 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestMosaicsImpl, RenderNV12OCVTestMosaics,
                                 Values(25),
                                 Values(0)));
 
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestMosaicsImpl, RenderMFrameOCVTestMosaics,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values(cv::Rect(100, 100, 200, 200),      // Normal case
+                                       cv::Rect(-50, -50, 200, 200),      // Intersection with left-top corner
+                                       cv::Rect(-50, 100, 200, 200),      // Intersection with left side
+                                       cv::Rect(-50, 600, 200, 200),      // Intersection with left-bottom corner
+                                       cv::Rect(100, 600, 200, 200),      // Intersection with bottom side
+                                       cv::Rect(1200, 700, 200, 200),     // Intersection with right-bottom corner
+                                       cv::Rect(1200, 400, 200, 200),     // Intersection with right side
+                                       cv::Rect(1200, -50, 200, 200),     // Intersection with right-top corner
+                                       cv::Rect(500, -50, 200, 200),      // Intersection with top side
+                                       cv::Rect(-100, 300, 1480, 300),    // From left to right side with intersection
+                                       cv::Rect(5000, 2000, 100, 100),    // Outside image
+                                       cv::Rect(-300, -300, 3000, 3000),  // Cover all image
+                                       cv::Rect(100, 100, -500, -500)),   // Negative width and height
+                                Values(25),
+                                Values(0)));
+
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestImagesImpl, RenderBGROCVTestImages,
                         Combine(Values(cv::Size(1280, 720)),
                                 Values(cv::Rect(100, 100, 200, 200)),
@@ -542,6 +842,12 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestImagesImpl, RenderNV12OCVTestImages,
                                 Values(cv::Scalar(100, 150, 60)),
                                 Values(1.0)));
 
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestImagesImpl, RenderMFrameOCVTestImages,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values(cv::Rect(100, 100, 200, 200)),
+                                Values(cv::Scalar(100, 150, 60)),
+                                Values(1.0)));
+
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestPolylinesImpl, RenderBGROCVTestPolylines,
                         Combine(Values(cv::Size(1280, 720)),
                                 Values(std::vector<cv::Point>{{100, 100}, {200, 200}, {150, 300}, {400, 150}}),
@@ -557,4 +863,12 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestPolylinesImpl, RenderNV12OCVTestPolylin
                                 Values(2),
                                 Values(LINE_8),
                                 Values(0)));
+
+INSTANTIATE_TEST_CASE_P(RenderMFrameOCVTestPolylinesImpl, RenderMFrameOCVTestPolylines,
+                        Combine(Values(cv::Size(1280, 720)),
+                                Values(std::vector<cv::Point>{ {100, 100}, { 200, 200 }, { 150, 300 }, { 400, 150 }}),
+                                Values(cv::Scalar(100, 150, 60)),
+                                Values(2),
+                                Values(LINE_8),
+                                Values(0)));
 }