$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
xy_cut.hh
1 // Copyright (C) 2013, 2014 EPITA Research and Development Laboratory (LRDE).
2 //
3 // This file is part of Olena.
4 //
5 // Olena is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation, version 2 of the License.
8 //
9 // Olena is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // As a special exception, you may use this file as part of a free
18 // software project without restriction. Specifically, if other files
19 // instantiate templates or use macros or inline functions from this
20 // file, or you compile this file and link it with other files to produce
21 // an executable, this file does not by itself cause the resulting
22 // executable to be covered by the GNU General Public License. This
23 // exception does not however invalidate any other reasons why the
24 // executable file might be covered by the GNU General Public License.
25 
29 
30 #ifndef SCRIBO_LAYOUT_XY_CUT_HH
31 # define SCRIBO_LAYOUT_XY_CUT_HH
32 
33 # include <mln/geom/min_row.hh>
34 # include <mln/geom/min_col.hh>
35 # include <mln/geom/max_row.hh>
36 # include <mln/geom/max_col.hh>
37 # include <mln/core/concept/image.hh>
38 # include <mln/core/macros.hh>
39 # include <mln/util/array.hh>
40 # include <scribo/layout/internal/hist_info.hh>
41 # include <scribo/layout/internal/node.hh>
42 
43 namespace scribo
44 {
45 
46  namespace layout
47  {
48  using namespace mln;
49 
51 
67  template <typename I>
69  xy_cut(const Image<I>& ima, int min_height, int min_width);
70 
71 
72 # ifndef MLN_INCLUDE_ONLY
73 
74  namespace internal
75  {
76 
77 
78  //-------------------------
79  // Compute horizontal hist info
80  //-------------------------
81 
82  template <typename I>
83  void compute_hist_info_h(const Image<I>& input_,
84  mln_ch_value(I,internal::hist_info)& hist)
85  {
86  mln_trace("compute_hist_info_h");
87 
88  const I& input = exact(input_);
89 
90  mln_precondition(input.is_valid());
91  mln_precondition(hist.is_valid());
92 
93  unsigned accumulator = 0;
94 
95  for (def::coord i = geom::min_row(input); i <= geom::max_row(input); ++i)
96  {
97  accumulator = 0;
98  for (def::coord j = geom::min_col(input); j <= geom::max_col(input); ++j)
99  {
100  if (input.at_(i, j))
101  ++accumulator;
102 
103  hist.at_(i, j).horizontal = accumulator;
104  }
105  }
106 
107  }
108 
109 
110  //-------------------------
111  // Compute vertical hist info
112  //-------------------------
113 
114  template <typename I>
115  void compute_hist_info_v(const Image<I>& input_,
116  mln_ch_value(I,internal::hist_info)& hist)
117  {
118  mln_trace("compute_hist_info_v");
119 
120  const I& input = exact(input_);
121 
122  mln_precondition(input.is_valid());
123  mln_precondition(hist.is_valid());
124 
125  unsigned accumulator = 0;
126 
127  for (def::coord j = geom::min_col(input); j <= geom::max_col(input); ++j)
128  {
129  accumulator = 0;
130  for (def::coord i = geom::min_row(input); i <= geom::max_row(input); ++i)
131  {
132  if (input.at_(i, j))
133  ++accumulator;
134 
135  hist.at_(i, j).vertical = accumulator;
136  }
137  }
138 
139  }
140 
141 
142 
143  template <typename H>
144  void horizontal_whitespace(const Image<H>& hist_,
145  const mln_site(H)& pmin,
146  const mln_site(H)& pmax,
147  int& max_height,
148  int& ws_min_row,
149  int& ws_max_row)
150  {
151  mln_trace("horizontal_whitespace");
152 
153  typedef mln_value(H) V;
154  mlc_is(V,internal::hist_info)::check();
155 
156  const H& hist = exact(hist_);
157 
158  mln_precondition(hist.is_valid());
159 
160  const int x_min = pmin.col();
161  const int x_max = pmax.col();
162  const int y_min = pmin.row();
163  const int y_max = pmax.row();
164 
165  for (int y = y_min; y <= y_max; ++y)
166  {
167  const int n_white_pixels = hist.at_(y, x_max).horizontal -
168  hist.at_(y, x_min).horizontal;
169 
170  if (!n_white_pixels)
171  {
172  int r = y + 1; // r : row
173 
174  // While there is a gap in the histogram
175  for (; r <= y_max && !(hist.at_(r, x_max).horizontal -
176  hist.at_(r, x_min).horizontal); ++r);
177 
178  const int count = r - y;
179 
180  if (count > max_height)
181  {
182  max_height = count;
183  ws_min_row = (y == y_min) ? y_min : y - 1;
184  ws_max_row = r;
185  }
186 
187  y = r;
188  }
189  }
190 
191  }
192 
193 
194  template <typename H>
195  void vertical_whitespace(const Image<H>& hist_,
196  const mln_site(H)& pmin,
197  const mln_site(H)& pmax,
198  int& max_width,
199  int& ws_min_col,
200  int& ws_max_col)
201  {
202  mln_trace("vertical_whitespace");
203 
204  typedef mln_value(H) V;
205  mlc_is(V,internal::hist_info)::check();
206 
207  const H& hist = exact(hist_);
208 
209  mln_precondition(hist.is_valid());
210 
211  const int x_min = pmin.col();
212  const int x_max = pmax.col();
213  const int y_min = pmin.row();
214  const int y_max = pmax.row();
215 
216  for (int x = x_min; x <= x_max; ++x)
217  {
218  const int n_white_pixels = hist.at_(y_max, x).vertical -
219  hist.at_(y_min, x).vertical;
220 
221  if (!n_white_pixels)
222  {
223  int c = x + 1; // c : column
224 
225  // While there is a gap in the histogram
226  for (; c <= x_max && !(hist.at_(y_max, c).vertical -
227  hist.at_(y_min, c).vertical); ++c);
228 
229  const int count = c - x;
230 
231  if (count > max_width)
232  {
233  max_width = count;
234  ws_min_col = (x == x_min) ? x_min : x - 1;
235  ws_max_col = c;
236  }
237 
238  x = c;
239  }
240  }
241 
242  }
243 
244 
245  template <typename H>
246  void do_xy_cut(const Image<H>& hist_,
247  node<mln_box(H)>* root,
248  const mln_box(H)& domain,
249  const bool horizontal,
250  const bool should_stop,
251  const int min_height,
252  const int min_width)
253  {
254  mln_trace("do_xy_cut");
255 
256  typedef mln_value(H) V;
257  mlc_is(V,internal::hist_info)::check();
258 
259  const H& hist = exact(hist_);
260 
261  mln_precondition(hist.is_valid());
262  mln_precondition(root != 0);
263  mln_precondition(domain.is_valid());
264 
265  const mln_site(H)& pmin = domain.pmin();
266  const mln_site(H)& pmax = domain.pmax();
267  const int x_min = pmin.col();
268  const int x_max = pmax.col();
269  const int y_min = pmin.row();
270  const int y_max = pmax.row();
271 
272  if (horizontal)
273  {
274  int max_height = 0;
275  int ws_min_row = 0; // Whitespace min and max
276  int ws_max_row = 0; // rows
277 
278  horizontal_whitespace(hist, pmin, pmax, max_height, ws_min_row, ws_max_row);
279 
280  if (max_height < min_height || max_height == (pmax.row() - pmin.row()))
281  {
282  if (should_stop)
283  return;
284 
285  do_xy_cut(hist, root, domain, false, true, min_height, min_width);
286  }
287  else
288  {
289  typedef mln_box(H) B;
290  node<B>* ls = (ws_min_row == y_min) ? 0 :
291  new node<B>(box2d(pmin, point2d(ws_min_row, pmax.col())));
292  node<B>* rs = (ws_max_row >= y_max) ? 0 :
293  new node<B>(box2d(point2d(ws_max_row, pmin.col()), pmax));
294 
295  root->set_ls(ls);
296  root->set_rs(rs);
297 
298  if (ls)
299  do_xy_cut(hist, ls, ls->get_bbox(), false, false, min_height, min_width);
300  if (rs)
301  do_xy_cut(hist, rs, rs->get_bbox(), false, false, min_height, min_width);
302  }
303  }
304  else
305  {
306  int max_width = 0;
307  int ws_min_col = 0; // Whitespace min and max
308  int ws_max_col = 0; // cols
309 
310  vertical_whitespace(hist, pmin, pmax, max_width, ws_min_col, ws_max_col);
311 
312  if (max_width < min_width || max_width == (pmax.col() - pmin.col()))
313  {
314  if (should_stop)
315  return;
316 
317  do_xy_cut(hist, root, domain, true, true, min_height, min_width);
318  }
319  else
320  {
321  typedef mln_box(H) B;
322  node<B>* ls = (ws_min_col == x_min) ? 0 :
323  new node<B>(box2d(pmin, point2d(pmax.row(), ws_min_col)));
324  node<B>* rs = (ws_max_col >= x_max) ? 0 :
325  new node<B>(box2d(point2d(pmin.row(), ws_max_col), pmax));
326 
327  root->set_ls(ls);
328  root->set_rs(rs);
329 
330  if (ls)
331  do_xy_cut(hist, ls, ls->get_bbox(), true, false, min_height, min_width);
332  if (rs)
333  do_xy_cut(hist, rs, rs->get_bbox(), true, false, min_height, min_width);
334  }
335  }
336 
337  }
338 
339 
340  template <typename B>
341  void xy_dfs(const node<B>* root, util::array<B>& result)
342  {
343  if (root->is_leaf())
344  {
345  result.append(root->get_bbox());
346  return;
347  }
348 
349  const node<B>* ls = root->get_ls();
350  const node<B>* rs = root->get_rs();
351 
352  if (ls)
353  xy_dfs(root->get_ls(), result);
354  if (rs)
355  xy_dfs(root->get_rs(), result);
356  }
357 
358  } // end of namespace scribo::layout::internal
359 
360 
361 
362  template <typename I>
364  xy_cut(const Image<I>& ima_, int min_height, int min_width)
365  {
366  mln_trace("scribo::layout::xy_cut");
367 
368  typedef mln_value(I) V;
369  typedef mln_box(I) B;
370  mlc_is(V,bool)::check();
371 
372  const I& ima = exact(ima_);
373  mln_precondition(ima.is_valid());
374 
375  // Compute histogram
376  mln_ch_value(I,internal::hist_info) hist(ima.domain());
377  internal::compute_hist_info_h(ima, hist);
378  internal::compute_hist_info_v(ima, hist);
379 
380  // XY-Cut
381  internal::node<B>* n = new internal::node<B>(ima.domain());
382  internal::do_xy_cut(hist, n, ima.domain(), true, false, min_height, min_width);
383 
384  // Build output.
385  mln::util::array<B> output;
386  internal::xy_dfs(n, output);
387 
388  // Clear temporary data.
389  delete n;
390 
391  return output;
392  }
393 
394 
395 # endif // ! MLN_INCLUDE_ONLY
396 
397  } // end of namespace scribo::layout
398 
399 } // end of namespace scribo
400 
401 #endif // ! SCRIBO_LAYOUT_XY_CUT_HH