$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
separators_nonvisible.hh
1 // Copyright (C) 2010, 2011, 2012, 2013 EPITA Research and Development
2 // Laboratory (LRDE)
3 //
4 // This file is part of Olena.
5 //
6 // Olena is free software: you can redistribute it and/or modify it under
7 // the terms of the GNU General Public License as published by the Free
8 // Software Foundation, version 2 of the License.
9 //
10 // Olena is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // As a special exception, you may use this file as part of a free
19 // software project without restriction. Specifically, if other files
20 // instantiate templates or use macros or inline functions from this
21 // file, or you compile this file and link it with other files to produce
22 // an executable, this file does not by itself cause the resulting
23 // executable to be covered by the GNU General Public License. This
24 // exception does not however invalidate any other reasons why the
25 // executable file might be covered by the GNU General Public License.
26 
30 
31 
32 #ifndef SCRIBO_PRIMITIVE_EXTRACT_SEPARATORS_NONVISIBLE_HH
33 # define SCRIBO_PRIMITIVE_EXTRACT_SEPARATORS_NONVISIBLE_HH
34 
35 # include <mln/core/concept/image.hh>
36 # include <mln/core/image/dmorph/image_if.hh>
37 # include <mln/pw/all.hh>
38 
39 # include <mln/accu/count_value.hh>
40 
41 # include <mln/draw/line.hh>
42 
43 # include <mln/data/fill.hh>
44 # include <mln/data/convert.hh>
45 
46 # include <mln/labeling/relabel.hh>
47 # include <mln/labeling/value.hh>
48 
49 # include <mln/morpho/closing/structural.hh>
50 
51 # include <mln/literal/colors.hh>
52 
53 # include <mln/value/label_16.hh>
54 # include <mln/value/int_u8.hh>
55 # include <mln/value/rgb8.hh>
56 
57 # include <mln/util/timer.hh>
58 
59 # include <scribo/core/object_groups.hh>
60 # include <scribo/core/component_set.hh>
61 # include <scribo/core/def/lbl_type.hh>
62 
63 # include <scribo/debug/logger.hh>
64 
65 # include <scribo/filter/object_groups_small.hh>
66 # include <scribo/filter/object_links_bottom_aligned.hh>
67 # include <scribo/filter/object_links_top_aligned.hh>
68 
69 # include <scribo/preprocessing/rotate_90.hh>
70 
71 # include <scribo/primitive/extract/components.hh>
72 
73 # include <scribo/primitive/link/internal/dmax_default.hh>
74 # include <scribo/primitive/link/with_single_right_link_dmax_ratio_aligned.hh>
75 # include <scribo/primitive/link/with_single_left_link_dmax_ratio_aligned.hh>
76 
77 # include <scribo/primitive/group/from_double_link_any.hh>
78 
79 
80 
81 
82 namespace scribo
83 {
84 
85  namespace primitive
86  {
87 
88  namespace extract
89  {
90 
91  using namespace mln;
92  using namespace scribo::debug;
93 
94 
100  template <typename I>
101  mln_concrete(I)
102  separators_nonvisible(const Image<I>& in_);
103 
104 
105 
106 # ifndef MLN_INCLUDE_ONLY
107 
108  // FACADE
109 
110  template <typename I>
111  mln_concrete(I)
112  separators_nonvisible(const Image<I>& in_)
113  {
114  mln_trace("scribo::primitive::extract::separators_nonvisible");
115 
116  const I& in = exact(in_);
117  mln_precondition(in.is_valid());
118  typedef mln_value(I) Vi;
119  mlc_is(Vi,bool)::check();
120 
121  typedef scribo::def::lbl_type V;
122  typedef mln_ch_value(I,V) L;
123 
124  unsigned
125  min_angle = 3,
126  max_angle = 5,
127  min_card = 3;
128 
129  bool _debug_ = logger().is_at_level(AuxiliaryResults);
130 
131  // Closing structural - Connect characters.
132 
133  win::hline2d vl(17);
134  mln_concrete(I) input_clo = morpho::closing::structural(in, vl);
135 
136  // Debug
137  logger().log_image(AuxiliaryResults, input_clo, "input_clo");
138 
139  // Rotate (OK)
140  input_clo = scribo::preprocessing::rotate_90(input_clo, false);
141 
142 
143 
145 
146  V ncomponents;
147  component_set<L>
148  components = scribo::primitive::extract::components(input_clo, c8(),
149  ncomponents);
150  // Debug
151  logger().log_image(AuxiliaryResults,
152  data::convert(value::int_u8(),
154  "lbl");
155  unsigned dmax = 5;
156 
157  object_links<L> top_right, bot_right;
158 
159  object_links<L> top_left, bot_left;
160 
161 
162  typedef link::internal::dmax_default Dmax_F;
163  // Top
164  {
165  // Right
166  link::internal::single_right_dmax_ratio_aligned_functor<L,Dmax_F>
167  functor(components, Dmax_F(dmax), min_angle, max_angle,
168  anchor::TopStrictLeft);
169  top_right = primitive::link::compute(functor, anchor::TopStrictLeft);
170 
171  // Debug
172  logger().log_image(AuxiliaryResults, functor.debug_, "right_top");
173  logger().log_image(AuxiliaryResults, functor.debug_angle_,
174  "right_top_angle");
175 
176  // Left
177  link::internal::single_left_dmax_ratio_aligned_functor<L,Dmax_F>
178  lfunctor(components, Dmax_F(dmax), min_angle, max_angle,
179  anchor::TopStrictLeft);
180  top_left = primitive::link::compute(lfunctor, anchor::TopStrictLeft);
181 
182 
183  // Debug
184  if (_debug_)
185  {
186  logger().log_image(AuxiliaryResults, functor.debug_, "left_top");
187  logger().log_image(AuxiliaryResults, functor.debug_angle_,
188  "left_top_angle");
189 
190  mln_ch_value(I, value::rgb8) output = duplicate(functor.debug_);
191  data::paste((lfunctor.debug_
192  | (pw::value(lfunctor.debug_) != pw::cst(literal::black)))
193  | (pw::value(lfunctor.debug_) != pw::cst(literal::white)), output);
194 
195  logger().log_image(AuxiliaryResults, output, "left_right_top");
196  }
197 
198  }
199 
200 
201  // Bottom
202  {
203  // Right
204  link::internal::single_right_dmax_ratio_aligned_functor<L,Dmax_F>
205  functor(components, Dmax_F(dmax), min_angle, max_angle,
206  anchor::BottomStrictRight);
207  bot_right = primitive::link::compute(functor, anchor::BottomStrictRight);
208 
209  // Debug
210  if (_debug_)
211  {
212  logger().log_image(AuxiliaryResults, functor.debug_, "right_bot");
213  logger().log_image(AuxiliaryResults, functor.debug_angle_,
214  "right_bot_angle");
215  }
216 
217 
218  // Left
219  link::internal::single_left_dmax_ratio_aligned_functor<L,Dmax_F>
220  lfunctor(components, Dmax_F(dmax), min_angle, max_angle,
221  anchor::BottomStrictRight);
222  bot_left = primitive::link::compute(lfunctor, anchor::BottomStrictRight);
223 
224  // Debug
225  if (_debug_)
226  {
227  logger().log_image(AuxiliaryResults, functor.debug_, "left_bot");
228  logger().log_image(AuxiliaryResults, functor.debug_angle_,
229  "left_bot_angle");
230 
231  mln_ch_value(I, value::rgb8) output = duplicate(functor.debug_);
232  data::paste((lfunctor.debug_
233  | (pw::value(lfunctor.debug_) != pw::cst(literal::black)))
234  | (pw::value(lfunctor.debug_) != pw::cst(literal::white)), output);
235 
236  logger().log_image(AuxiliaryResults, output, "left_right_bot");
237  }
238  }
239 
240 
241  // Merge links and build CC groups
242  object_groups<L>
243  top_groups = primitive::group::from_double_link_any(top_left, top_right);
244  object_groups<L>
245  bot_groups = primitive::group::from_double_link_any(bot_left, bot_right);
246 
247  // Filter CC groups
248  top_groups = filter::object_groups_small(top_groups, min_card);
249  bot_groups = filter::object_groups_small(bot_groups, min_card);
250 
251  // Compute group bboxes
252 
253  mln_concrete(I) separators;
254  initialize(separators, input_clo);
255 
256  // FIXME: any way to fill border AND data at the same time?
257  data::fill(separators, false);
258  extension::fill(separators, false);
259 
260 
261  for_all_groups(d, top_groups)
262  if (top_groups(d).is_valid())
263  {
264  mln::draw::line(separators,
265  top_groups(d).bbox().pmin(),
266  point2d(top_groups(d).bbox().pmin().row(),
267  top_groups(d).bbox().pmax().col()),
268  true);
269  }
270 
271 
272  for_all_groups(d, bot_groups)
273  if (bot_groups(d).is_valid())
274  {
275  mln::draw::line(separators,
276  point2d(bot_groups(d).bbox().pmax().row(),
277  bot_groups(d).bbox().pmin().col()),
278  bot_groups(d).bbox().pmax(),
279  true);
280  }
281 
282 
283 
284  if (_debug_)
285  {
286  // Restore input orientation.
287  mln_concrete(I) input = scribo::preprocessing::rotate_90(in, false);
288 
289  mln_ch_value(I, value::rgb8)
290  wo_filtering = data::convert(value::rgb8(), input);
291 
292  for_all_groups(d, top_groups)
293  if (top_groups(d).is_valid())
294  {
295  mln::draw::line(wo_filtering,
296  top_groups(d).bbox().pmin(),
297  point2d(top_groups(d).bbox().pmin().row(),
298  top_groups(d).bbox().pmax().col()),
299  literal::green);
300  }
301 
302  for_all_groups(d, bot_groups)
303  if (bot_groups(d).is_valid())
304  {
305  mln::draw::line(wo_filtering,
306  point2d(bot_groups(d).bbox().pmax().row(),
307  bot_groups(d).bbox().pmin().col()),
308  bot_groups(d).bbox().pmax(),
309  literal::green);
310  }
311 
312  logger().log_image(AuxiliaryResults, wo_filtering, "wo_filtering");
313  logger().log_image(AuxiliaryResults, separators, "separators");
314  }
315 
316 
317  // Hit or miss
318  {
319  if (_debug_)
320  {
321  mln_concrete(I) input_with_seps = duplicate(input_clo);
322  data::paste(separators | pw::value(separators), input_with_seps);
323 
324  logger().log_image(AuxiliaryResults, input_with_seps, "input_with_seps");
325  }
326 
327  unsigned length = 25;
328 
329  dpoint2d
330  dp1(-21, 0),
331  dp2( 21, 0);
332 
333  // Adjusting extension.
334  extension::adjust_fill(input_clo, length / 2, 0);
335 
336  accu::count_value<bool> accu(true);
337  typedef mln_ch_value(I,unsigned) J;
338 
339  J tmp = accu::transform_line(accu, input_clo, length, 1);
340 
341 
342  value::int_u8 nlabels;
343  mln_ch_value(I,value::int_u8)
344  sep_lbl = labeling::value(separators, true, c8(), nlabels);
345 
346 
347  mln::util::array<bool> relbl(unsigned(nlabels) + 1, true);
348  relbl(0) = false;
349 
350  unsigned invalid_ratio = unsigned(length * 0.30f);
351 
352  extension::adjust_fill(tmp, 21, 0);
353 
354  value::int_u8 *sep_lbl_ptr = sep_lbl.buffer()
355  + sep_lbl.offset_of_point(sep_lbl.domain().pmin());
356  bool *separators_ptr = separators.buffer()
357  + separators.offset_of_point(separators.domain().pmin());
358  unsigned *tmp_ptr = tmp.buffer() + tmp.offset_of_point(tmp.domain().pmin());;
359  int idx1 = tmp.delta_offset(dp1);
360  int idx2 = tmp.delta_offset(dp2);
361 
362  unsigned
363  nrows = separators.nrows(),
364  ncols = separators.ncols();
365 
366  unsigned
367  row_idx_sep_lbl = sep_lbl.delta_offset(dpoint2d(+1, - ncols)),
368  row_idx_separators = separators.delta_offset(dpoint2d(+1, - ncols)),
369  row_idx_tmp = tmp.delta_offset(dpoint2d(+1, - ncols));
370 
371  for (unsigned row = 0; row < nrows; ++row)
372  {
373  for (unsigned col = 0; col < ncols; ++col)
374  {
375  if (*separators_ptr)
376  {
377  unsigned lbl = *sep_lbl_ptr;
378 
379  unsigned
380  top_count = *(tmp_ptr + idx1),
381  bot_count = *(tmp_ptr + idx2);
382 
383  // This site is wrapped between two lines of text so we don't
384  // want it.
385  if (top_count >= invalid_ratio + 1
386  && bot_count >= invalid_ratio + 1)
387  {
388  relbl(lbl) = false;
389  }
390  }
391 
392  ++tmp_ptr;
393  ++sep_lbl_ptr;
394  ++separators_ptr;
395  }
396 
397  tmp_ptr += row_idx_tmp;
398  sep_lbl_ptr += row_idx_sep_lbl;
399  separators_ptr += row_idx_separators;
400  }
401 
402 
403  labeling::relabel_inplace(sep_lbl, nlabels, relbl);
404 
405  mln_concrete(I) output = data::convert(bool(), sep_lbl);
406 
407  if (_debug_)
408  {
409  logger().log_image(AuxiliaryResults, output,
410  "separators_filtered");
411  }
412 
413  return scribo::preprocessing::rotate_90(output, true);
414  }
415  }
416 
417 
418 # endif // ! MLN_INCLUDE_ONLY
419 
420  } // end of namespace scribo::primitive::extract
421 
422  } // end of namespace scribo::primitive
423 
424 } // end of namespace scribo
425 
426 #endif // ! SCRIBO_PRIMITIVE_EXTRACT_SEPARATORS_NONVISIBLE_HH