@@ -79,7 +79,7 @@ int longestColRunLength(const Mat& kernel)
7979 return maxLen;
8080}
8181
82- std::vector<Point> findSeeds (const Mat& stNode, int rowDepth, int colDepth)
82+ std::vector<Point> findP2RectCorners (const Mat& stNode, int rowDepth, int colDepth)
8383{
8484 int rowOfst = 1 << rowDepth;
8585 int colOfst = 1 << colDepth;
@@ -97,7 +97,7 @@ std::vector<Point> findSeeds(const Mat& stNode, int rowDepth, int colDepth)
9797 if (row > 0 && stNode.at <uchar>(row - 1 , col) == 1
9898 && row + 1 < stNode.rows && stNode.at <uchar>(row + 1 , col) == 1 ) continue ;
9999
100- // zignore if neighboring block is white; will be alive in deeper table
100+ // ignore if neighboring block is white
101101 if (col + colOfst < stNode.cols && stNode.at <uchar>(row, col + colOfst) == 1 ) continue ;
102102 if (col - colOfst >= 0 && stNode.at <uchar>(row, col - colOfst) == 1 ) continue ;
103103 if (row + rowOfst < stNode.rows && stNode.at <uchar>(row + rowOfst, col) == 1 ) continue ;
@@ -109,43 +109,48 @@ std::vector<Point> findSeeds(const Mat& stNode, int rowDepth, int colDepth)
109109 return p2Rects;
110110}
111111
112+ /*
113+ * Find a set of power-2-rectangles to cover the kernel.
114+ * power-2-rectangles is a rectangle whose height and width are both power of 2.
115+ */
112116std::vector<std::vector<std::vector<Point>>> genPow2RectsToCoverKernel (
113117 const Mat& kernel, int rowDepthLim, int colDepthLim)
114118{
115119 CV_Assert (kernel.type () == CV_8UC1);
116120
117121 std::vector<std::vector<std::vector<Point>>> p2Rects;
118- Mat stCache = kernel;
122+ Mat stNodeCache = kernel;
119123 for (int rowDepth = 0 ; rowDepth < rowDepthLim; rowDepth++)
120124 {
121- Mat st = stCache .clone ();
125+ Mat stNode = stNodeCache .clone ();
122126 p2Rects.emplace_back (std::vector<std::vector<Point>>());
123127 for (int colDepth = 0 ; colDepth < colDepthLim; colDepth++)
124128 {
125- p2Rects[rowDepth].emplace_back (findSeeds (st , rowDepth, colDepth));
129+ p2Rects[rowDepth].emplace_back (findP2RectCorners (stNode , rowDepth, colDepth));
126130 int colStep = 1 << colDepth;
127- if (st .cols - colStep < 0 ) break ;
128- Rect s1 (0 , 0 , st .cols - colStep, st .rows );
129- Rect s2 (colStep, 0 , st .cols - colStep, st .rows );
130- cv::min (st (s1), st (s2), st );
131+ if (stNode .cols - colStep < 0 ) break ;
132+ Rect s1 (0 , 0 , stNode .cols - colStep, stNode .rows );
133+ Rect s2 (colStep, 0 , stNode .cols - colStep, stNode .rows );
134+ cv::min (stNode (s1), stNode (s2), stNode );
131135 }
132136 int rowStep = 1 << rowDepth;
133- if (stCache .rows - rowStep < 0 ) break ;
134- Rect s1 (0 , 0 , stCache .cols , stCache .rows - rowStep);
135- Rect s2 (0 , rowStep, stCache .cols , stCache .rows - rowStep);
136- cv::min (stCache (s1), stCache (s2), stCache );
137+ if (stNodeCache .rows - rowStep < 0 ) break ;
138+ Rect s1 (0 , 0 , stNodeCache .cols , stNodeCache .rows - rowStep);
139+ Rect s2 (0 , rowStep, stNodeCache .cols , stNodeCache .rows - rowStep);
140+ cv::min (stNodeCache (s1), stNodeCache (s2), stNodeCache );
137141 }
138142
139- // todo: implement greedy algorithm to minimize the rectangle set covering the kernel.
140-
141143 return p2Rects;
142144}
143145
144146Mat SolveRSAPGreedy (const Mat& initialMap)
145147{
146148 /*
147- * Solves the rectilinear steiner arborescence problem
149+ * Solves the rectilinear steiner arborescence problem greedy.
148150 * https://link.springer.com/article/10.1007/BF01758762
151+ *
152+ * Following implementation is O(n^3)-time algorithm
153+ * which is different from the mothod proposed in the paper.
149154 */
150155 CV_Assert (initialMap.type () == CV_8UC1);
151156 std::vector<Point> pos;
@@ -194,9 +199,16 @@ Mat SolveRSAPGreedy(const Mat& initialMap)
194199 return resMap;
195200}
196201
197- Mat planSparseTableConstr (
202+ Mat sparseTableFillPlanning (
198203 std::vector<std::vector<std::vector<Point>>> pow2Rects, int rowDepthLim, int colDepthLim)
199204{
205+ /*
206+ * Plan the order to fill the required 2d-sparse-table nodes.
207+ * The type of returned mat is Vec2b.
208+ * if path[dr][dc][0] == 1 then st[dr+1][dc] will be calculated from st[dr][dc].
209+ * if path[dr][dc][1] == 1 then st[dr][dc+1] will be calculated from st[dr][dc].
210+ */
211+
200212 // list up required sparse table nodes.
201213 Mat stMap = Mat::zeros (rowDepthLim, colDepthLim, CV_8UC1);
202214 for (int rd = 0 ; rd < rowDepthLim; rd++)
@@ -219,7 +231,6 @@ void morphDfs(int minmax, Mat& st, Mat& dst,
219231 else cv::max (dst, st (rect), dst);
220232 }
221233
222- // Fill col-direction first.
223234 if (stPlan.at <Vec2b>(rowDepth, colDepth)[1 ] == 1 )
224235 {
225236 // col direction
@@ -258,7 +269,7 @@ void morphOp(Op minmax, InputArray _src, OutputArray _dst, InputArray kernel,
258269 std::vector<std::vector<std::vector<Point>>> pow2Rects
259270 = genPow2RectsToCoverKernel (_kernel, rowDepthLim, colDepthLim);
260271 Mat stPlan
261- = planSparseTableConstr (pow2Rects, rowDepthLim, colDepthLim);
272+ = sparseTableFillPlanning (pow2Rects, rowDepthLim, colDepthLim);
262273
263274 Mat src = _src.getMat ();
264275 _dst.create (_src.size (), _src.type ());
0 commit comments