From 4a4ff6749bf7e17c37f3d1c27b29b1910fdd98fb Mon Sep 17 00:00:00 2001
From: atalaman <anatoliy.talamanov@intel.com>
Date: Thu, 12 Dec 2019 19:10:14 +0300
Subject: [PATCH] Merge pull request #16080 from
 TolyaTalamanov:at/fix-mosaic-primitive

G-API: Mosaic handle corner cases

* Handle corner cases

* Fix mosaic algo

* Fix bug with empty rects
---
 modules/gapi/src/api/render_ocv.cpp           | 53 ++++++++++++++++---
 .../gapi/test/common/gapi_render_tests.cpp    | 53 ++++++++++++++++---
 .../test/render/gapi_render_tests_ocv.cpp     | 30 ++++++++++-
 3 files changed, 122 insertions(+), 14 deletions(-)

diff --git a/modules/gapi/src/api/render_ocv.cpp b/modules/gapi/src/api/render_ocv.cpp
index 6c035f6d70..d87581e773 100644
--- a/modules/gapi/src/api/render_ocv.cpp
+++ b/modules/gapi/src/api/render_ocv.cpp
@@ -16,13 +16,54 @@ namespace draw
 // FIXME Support `decim` mosaic parameter
 inline void mosaic(cv::Mat& mat, const cv::Rect &rect, int cellSz)
 {
-    cv::Mat msc_roi = mat(rect);
-    int crop_x = msc_roi.cols - msc_roi.cols % cellSz;
-    int crop_y = msc_roi.rows - msc_roi.rows % cellSz;
+    cv::Rect mat_rect(0, 0, mat.cols, mat.rows);
+    auto intersection = mat_rect & rect;
 
-    for(int i = 0; i < crop_y; i += cellSz ) {
-        for(int j = 0; j < crop_x; j += cellSz) {
-            auto cell_roi = msc_roi(cv::Rect(j, i, cellSz, cellSz));
+    cv::Mat msc_roi = mat(intersection);
+
+    bool has_crop_x = false;
+    bool has_crop_y = false;
+
+    int cols = msc_roi.cols;
+    int rows = msc_roi.rows;
+
+    if (msc_roi.cols % cellSz != 0)
+    {
+        has_crop_x = true;
+        cols -= msc_roi.cols % cellSz;
+    }
+
+    if (msc_roi.rows % cellSz != 0)
+    {
+        has_crop_y = true;
+        rows -= msc_roi.rows % cellSz;
+    }
+
+    cv::Mat cell_roi;
+    for(int i = 0; i < rows; i += cellSz )
+    {
+        for(int j = 0; j < cols; j += cellSz)
+        {
+            cell_roi = msc_roi(cv::Rect(j, i, cellSz, cellSz));
+            cell_roi = cv::mean(cell_roi);
+        }
+        if (has_crop_x)
+        {
+            cell_roi = msc_roi(cv::Rect(cols, i, msc_roi.cols - cols, cellSz));
+            cell_roi = cv::mean(cell_roi);
+        }
+    }
+
+    if (has_crop_y)
+    {
+        for(int j = 0; j < cols; j += cellSz)
+        {
+            cell_roi = msc_roi(cv::Rect(j, rows, cellSz, msc_roi.rows - rows));
+            cell_roi = cv::mean(cell_roi);
+        }
+        if (has_crop_x)
+        {
+            cell_roi = msc_roi(cv::Rect(cols, rows, msc_roi.cols - cols, msc_roi.rows - rows));
             cell_roi = cv::mean(cell_roi);
         }
     }
diff --git a/modules/gapi/test/common/gapi_render_tests.cpp b/modules/gapi/test/common/gapi_render_tests.cpp
index 81f7c5b86e..e29406d783 100644
--- a/modules/gapi/test/common/gapi_render_tests.cpp
+++ b/modules/gapi/test/common/gapi_render_tests.cpp
@@ -21,13 +21,54 @@ cv::Scalar cvtBGRToYUVC(const cv::Scalar& bgr)
 
 void drawMosaicRef(const cv::Mat& mat, const cv::Rect &rect, int cellSz)
 {
-    cv::Mat msc_roi = mat(rect);
-    int crop_x = msc_roi.cols - msc_roi.cols % cellSz;
-    int crop_y = msc_roi.rows - msc_roi.rows % cellSz;
+    cv::Rect mat_rect(0, 0, mat.cols, mat.rows);
+    auto intersection = mat_rect & rect;
 
-    for(int i = 0; i < crop_y; i += cellSz ) {
-        for(int j = 0; j < crop_x; j += cellSz) {
-            auto cell_roi = msc_roi(cv::Rect(j, i, cellSz, cellSz));
+    cv::Mat msc_roi = mat(intersection);
+
+    bool has_crop_x = false;
+    bool has_crop_y = false;
+
+    int cols = msc_roi.cols;
+    int rows = msc_roi.rows;
+
+    if (msc_roi.cols % cellSz != 0)
+    {
+        has_crop_x = true;
+        cols -= msc_roi.cols % cellSz;
+    }
+
+    if (msc_roi.rows % cellSz != 0)
+    {
+        has_crop_y = true;
+        rows -= msc_roi.rows % cellSz;
+    }
+
+    cv::Mat cell_roi;
+    for(int i = 0; i < rows; i += cellSz )
+    {
+        for(int j = 0; j < cols; j += cellSz)
+        {
+            cell_roi = msc_roi(cv::Rect(j, i, cellSz, cellSz));
+            cell_roi = cv::mean(cell_roi);
+        }
+        if (has_crop_x)
+        {
+            cell_roi = msc_roi(cv::Rect(cols, i, msc_roi.cols - cols, cellSz));
+            cell_roi = cv::mean(cell_roi);
+        }
+    }
+
+    if (has_crop_y)
+    {
+        for(int j = 0; j < cols; j += cellSz)
+        {
+            cell_roi = msc_roi(cv::Rect(j, rows, cellSz, msc_roi.rows - rows));
+            cell_roi = cv::mean(cell_roi);
+        }
+        if (has_crop_x)
+        {
+            cell_roi = msc_roi(cv::Rect(cols, rows, msc_roi.cols - cols, msc_roi.rows - rows));
             cell_roi = cv::mean(cell_roi);
         }
     }
diff --git a/modules/gapi/test/render/gapi_render_tests_ocv.cpp b/modules/gapi/test/render/gapi_render_tests_ocv.cpp
index 19701c8348..f727d977aa 100644
--- a/modules/gapi/test/render/gapi_render_tests_ocv.cpp
+++ b/modules/gapi/test/render/gapi_render_tests_ocv.cpp
@@ -493,15 +493,41 @@ INSTANTIATE_TEST_CASE_P(RenderNV12OCVTestFTextsImpl, RenderNV12OCVTestFTexts,
                             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
+
 INSTANTIATE_TEST_CASE_P(RenderBGROCVTestMosaicsImpl, RenderBGROCVTestMosaics,
                         Combine(Values(cv::Size(1280, 720)),
-                                Values(cv::Rect(100, 100, 200, 200)),
+                                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(RenderNV12OCVTestMosaicsImpl, RenderNV12OCVTestMosaics,
                         Combine(Values(cv::Size(1280, 720)),
-                                Values(cv::Rect(100, 100, 200, 200)),
+                                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)));