$extrastylesheet
Olena  User documentation 2.1
An Image Processing Platform
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
text_in_picture_functor.hh
1 // Copyright (C) 2011, 2013 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 
26 #ifndef SCRIBO_TOOLCHAIN_INTERNAL_TEXT_IN_PICTURE_FUNCTOR_HH
27 # define SCRIBO_TOOLCHAIN_INTERNAL_TEXT_IN_PICTURE_FUNCTOR_HH
28 
29 
33 
34 
35 # include <libgen.h>
36 # include <iostream>
37 
38 # ifndef SCRIBO_NDEBUG
39 # include <mln/util/timer.hh>
40 # endif // ! SCRIBO_NDEBUG
41 
42 # include <mln/core/image/image2d.hh>
43 # include <mln/core/alias/neighb2d.hh>
44 
45 # include <mln/literal/colors.hh>
46 # include <mln/value/rgb8.hh>
47 
48 # include <mln/fun/v2v/rgb_to_int_u.hh>
49 
50 # include <mln/subsampling/antialiased.hh>
51 
52 # include <scribo/core/def/lbl_type.hh>
53 
54 # include <scribo/draw/bounding_boxes.hh>
55 # include <scribo/draw/groups_bboxes.hh>
56 
57 # include <scribo/binarization/sauvola_ms.hh>
58 # include <scribo/binarization/sauvola.hh>
59 
60 # include <scribo/primitive/extract/components.hh>
61 
62 # include <scribo/primitive/link/merge_double_link.hh>
63 # include <scribo/primitive/link/with_single_left_link.hh>
64 # include <scribo/primitive/link/with_single_right_link.hh>
65 
66 # include <scribo/primitive/group/apply.hh>
67 # include <scribo/primitive/group/from_double_link.hh>
68 # include <scribo/primitive/group/from_single_link.hh>
69 
70 # include <scribo/primitive/regroup/from_single_left_link.hh>
71 
72 # include <scribo/filter/object_groups_with_holes.hh>
73 
74 # include <scribo/filter/object_links_bbox_h_ratio.hh>
75 # include <scribo/filter/object_links_bbox_overlap.hh>
76 
77 # include <scribo/filter/object_groups_small.hh>
78 # include <scribo/filter/object_groups_mean_width.hh>
79 
80 # include <scribo/debug/decision_image.hh>
81 # include <scribo/debug/linked_bboxes_image.hh>
82 
83 # include <scribo/debug/logger.hh>
84 # include <scribo/debug/highlight_text_area.hh>
85 # include <scribo/debug/text_areas_image.hh>
86 
87 # include <scribo/preprocessing/split_bg_fg.hh>
88 
89 # include <scribo/make/debug_filename.hh>
90 
91 # include <scribo/toolchain/internal/toolchain_functor.hh>
92 
93 # include <scribo/afp/components.hh>
94 # include <scribo/afp/link.hh>
95 # include <scribo/afp/regroup.hh>
96 
97 
98 namespace scribo
99 {
100 
101  namespace toolchain
102  {
103 
104  namespace internal
105  {
106 
107 
108  using namespace mln;
109 
111  template <typename I>
113  : public Toolchain_Functor
114  {
115  typedef scribo::def::lbl_type V;
116  typedef mln_ch_value(I,V) L;
117 
119 
120  virtual int nsteps() const;
121 
122  //===============
123  // Core function
124  //===============
125 
126  component_set<L> operator()(const Image<I>&);
127 
128 
129  //=========
130  // Options
131  //=========
132  bool enable_bg_removal;
133  bool enable_multi_scale_bin;
134 
135  //============
136  // Parameters
137  //============
138 
139  unsigned lambda;
140 
141  // Image resizing factor
142  unsigned max_dim_size;
143 
144  // Sauvola ms
145  unsigned sauvola_s;
146  unsigned sauvola_min_w;
147 
148  // Group Filtering
149  float bbox_h_ratio;
150  float bbox_overlap;
151  unsigned small_groups;
152  unsigned mean_width;
153  unsigned regroup_dmax;
154  unsigned group_min_holes;
155 
156  //=========
157  // Results
158  //=========
159  component_set<L> output;
160  object_groups<L> groups;
161 
162 # ifndef SCRIBO_NDEBUG
163  //=============
164  // DEBUG TOOLS
165  //=============
166  virtual void on_start();
167  virtual void on_end();
168  virtual void on_progress();
169 
171  mln::util::timer gt;
172 # endif // ! SCRIBO_NDEBUG
173 
174  private:
175  unsigned get_factor(const I& ima, unsigned max_dim_size);
176 
177  };
178 
179 
180 # ifndef MLN_INCLUDE_ONLY
181 
182 
183  template <typename I>
184  unsigned
186  unsigned max_dim_size)
187  {
188  mln_precondition(max_dim_size != 0);
189 
190  unsigned
191  nrows = ima.nrows(),
192  ncols = ima.ncols(),
193  max_dim = std::max(nrows, ncols),
194  factor = max_dim / max_dim_size;
195 
196  return factor ? factor : 1;
197  }
198 
199 
200  template <typename I>
201  text_in_picture_functor<I>::text_in_picture_functor()
202  : enable_bg_removal(false),
203  enable_multi_scale_bin(true),
204  lambda(0),
205  max_dim_size(1024),
206  sauvola_s(2u),
207  sauvola_min_w(51u),
208  bbox_h_ratio(1.60f),
209  bbox_overlap(0.80f),
210  small_groups(3),
211  mean_width(8),
212  regroup_dmax(30),
213  group_min_holes(3)
214  {
215  }
216 
217 
218  template <typename I>
220  text_in_picture_functor<I>::operator()(const Image<I>& input_rgb_orig_)
221  {
222  const I& input_rgb_orig = exact(input_rgb_orig_);
223  mln_precondition(input_rgb_orig.is_valid());
224 
225  using namespace scribo;
226  using namespace scribo::primitive;
227  using namespace mln;
228 
229  on_start();
230 
231  // Setting up options.
232  if (lambda == 0)
233  lambda = 1.2 * (input_rgb_orig.nrows() + input_rgb_orig.ncols());
234 
235  if (verbose)
236  std::cout << "Using lambda = " << lambda << std::endl;
237 
238 
239  mln_concrete(I) input_rgb;
240  if (max_dim_size != 0)
241  {
242  on_new_progress_label("Resizing image if needed...");
243 
244  unsigned factor = get_factor(input_rgb_orig, max_dim_size);
245 
246  if (verbose)
247  std::cout << "Reduction Factor : " << factor
248  << std::endl
249  << "Original domain: " << input_rgb_orig.domain()
250  << std::endl;
251 
252  mln_concrete(I)
253  input_rgb = mln::subsampling::antialiased(input_rgb_orig, factor);
254 
255  if (verbose)
256  std::cout << "Resized domain: " << input_rgb.domain()
257  << std::endl;
258  }
259  else
260  input_rgb = input_rgb_orig;
261 
262  on_progress();
263 
264  mln_ch_value(I,value::int_u8) intensity_ima;
265  if (enable_bg_removal)
266  {
267  on_new_progress_label("Extracting foreground and converting to intensity image...");
268 
269  // Extract foreground
271  fg = preprocessing::split_bg_fg(input_rgb, lambda, 32).second();
272  intensity_ima = data::transform(fg, mln::fun::v2v::rgb_to_int_u<8>());
273 
274 # ifndef SCRIBO_NDEBUG
275  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults, fg,
276  "foreground");
277 # endif // ! SCRIBO_NDEBUG
278  }
279  else
280  {
281  on_new_progress_label("Converting to intensity image...");
282  intensity_ima = data::transform(input_rgb,
284  }
285 
286  on_progress();
287 
288  // Binarize foreground to use it in the processing chain.
289  mln_ch_value(I,bool) input;
290  unsigned w = std::min(intensity_ima.nrows() / 3, intensity_ima.ncols() / 3);
291  if (! w % 2)
292  ++w;
293  w = std::min(w, sauvola_min_w);
294  if (verbose)
295  std::cout << "** Using Sauvola with w_1 = " << w << std::endl;
296 
297  if (enable_multi_scale_bin)
298  {
299  on_new_progress_label("Binarizing image with multi-scale algorithm...");
300  input = scribo::binarization::sauvola_ms(intensity_ima, w, sauvola_s);
301  }
302  else
303  {
304  on_new_progress_label("Binarizing image...");
305  input = scribo::binarization::sauvola(intensity_ima, w);
306  }
307 
308 # ifndef SCRIBO_NDEBUG
309  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults, input,
310  "binarization.pbm");
311 # endif // ! SCRIBO_NDEBUG
312 
313  on_progress();
314 
315 
316 
317  typedef mln_ch_value(I,def::lbl_type) L;
318 
320  on_new_progress_label("Finding components");
321 
322  typedef component_set<L> Obj;
323  Obj filtered_components;
324 
325  {
327 
328  def::lbl_type ncomponents;
329  L components = extract_components(input, ncomponents, attribs);
330 
331  filtered_components = Obj(components, ncomponents, attribs);
332  }
333 
334  if (verbose)
335  std::cout << "Object extracted " << filtered_components.nelements() << " components" << std::endl;
336 
337  on_progress();
338 
340  on_new_progress_label("Linking potential components");
341 
343  links = link::left_right(filtered_components);
344 
345  object_links<L>& left_link = links.first();
346  object_links<L>& right_link = links.second();
347 
348  on_progress();
349 
350 # ifndef SCRIBO_NDEBUG
351  if (scribo::debug::logger().is_enabled())
352  {
353  std::cerr << "BEFORE - ncomponents = " << filtered_components.nelements() << std::endl;
355  scribo::debug::AuxiliaryResults,
357  left_link, right_link,
358  literal::red, literal::cyan,
359  literal::yellow,
360  literal::green,
361  anchor::MassCenter),
362  "links");
363  }
364 # endif // ! SCRIBO_NDEBUG
365 
366  // Validating left and right links.
367  on_new_progress_label("Validating left and right links");
368 
370  merged_links = link::merge_double_link(left_link, right_link);
371 
372  on_progress();
373 
374  // Remove links if bboxes have too different sizes.
375  on_new_progress_label("Filtering component links.");
376 
378  hratio_filtered_links = filter::object_links_bbox_h_ratio(merged_links,
379  bbox_h_ratio);
380 
381 
382 # ifndef SCRIBO_NDEBUG
383  if (scribo::debug::logger().is_enabled())
384  {
385  mln_ch_value(I,value::rgb8)
386  hratio_decision_image = scribo::debug::decision_image(input,
387  merged_links,
388  hratio_filtered_links,
389  anchor::MassCenter);
390 
391  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
392  hratio_decision_image,
393  "hratio_links_decision_image");
394  }
395 # endif // ! SCRIBO_NDEBUG
396 
397  //Remove links if bboxes overlap too much.
398  object_links<L> overlap_filtered_links
399  = filter::object_links_bbox_overlap(hratio_filtered_links,
400  bbox_overlap);
401 
402 
403 # ifndef SCRIBO_NDEBUG
404  if (scribo::debug::logger().is_enabled())
405  {
406  mln_ch_value(I,value::rgb8) overlap_decision_image
407  = scribo::debug::decision_image(input,
408  hratio_filtered_links,
409  overlap_filtered_links,
410  anchor::MassCenter);
411 
412  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
413  overlap_decision_image,
414  "overlap_links_decision_image");
415  }
416 # endif // ! SCRIBO_NDEBUG
417 
418  on_progress();
419 
420 
421  on_new_progress_label("Grouping components...");
422 
423  groups = group::from_single_link(overlap_filtered_links);
424 
425  on_progress();
426 
427 
428 # ifndef SCRIBO_NDEBUG
429  if (scribo::debug::logger().is_enabled())
430  {
431  // Apply grouping in a temporary image (for debug purpose).
433  raw_group_image = group::apply(groups);
434 
435  mln_ch_value(I,value::rgb8)
436  decision_image = data::convert(value::rgb8(), input);
437 
438  scribo::draw::bounding_boxes(decision_image,
439  filtered_components, literal::green);
440  scribo::draw::bounding_boxes(decision_image,
441  raw_group_image, literal::blue);
442 
443  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
444  decision_image,
445  "group_and_object_image");
446 
447  decision_image = data::convert(value::rgb8(), input);
448  scribo::draw::bounding_boxes(decision_image,
449  raw_group_image, literal::blue);
450 
451  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
452  decision_image,
453  "group_image");
454  }
455 # endif // ! SCRIBO_NDEBUG
456 
457  on_new_progress_label("Filtering groups with too low cardinality...");
458 
459  // Remove components part of groups with strictly less than 3 components.
460  object_groups<L> filtered_small_groups;
461  filtered_small_groups = filter::object_groups_small(groups,
462  small_groups);
463 
464 # ifndef SCRIBO_NDEBUG
465  mln_ch_value(I,value::rgb8) decision_image;
466  if (scribo::debug::logger().is_enabled())
467  {
468  decision_image = data::convert(value::rgb8(), input);
469 
470  scribo::draw::groups_bboxes(decision_image, groups, literal::red);
471  scribo::draw::groups_bboxes(decision_image, filtered_small_groups,
472  literal::green);
473 
474  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
475  decision_image,
476  "small_groups_filter");
477  }
478 # endif // ! SCRIBO_NDEBUG
479 
480  on_progress();
481 
482  on_new_progress_label("Filtering groups having components with too low thickness...");
483 
484  // Remove components part of groups having a mean thickness lower than 8.
485  object_groups<L> filtered_thin_groups;
486  filtered_thin_groups
487  = filter::object_groups_mean_width(filtered_small_groups,
488  mean_width);
489 
490 # ifndef SCRIBO_NDEBUG
491  if (scribo::debug::logger().is_enabled())
492  {
493  decision_image = data::convert(value::rgb8(), input);
494 
495  scribo::draw::groups_bboxes(decision_image, filtered_small_groups,
496  literal::red);
497  scribo::draw::groups_bboxes(decision_image, filtered_thin_groups,
498  literal::green);
499 
500  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
501  decision_image,
502  "thin_groups_filter");
503  }
504 # endif // ! SCRIBO_NDEBUG
505 
506 
507  on_progress();
508 
510 
511  on_new_progress_label("Regrouping groups...");
512 
513  filtered_thin_groups = regroup::from_single_left_link(filtered_thin_groups,
514  regroup_dmax);
515 
516  on_progress();
517 
518 
520  on_new_progress_label("Filtering groups with too many holes...");
521 
522  groups = scribo::filter::object_groups_with_holes(filtered_thin_groups,
523  group_min_holes);
524 
525  on_progress();
526 
527 # ifndef SCRIBO_NDEBUG
528  if (scribo::debug::logger().is_enabled())
529  {
530  decision_image = data::convert(value::rgb8(), input);
531 
532  scribo::draw::groups_bboxes(decision_image, filtered_thin_groups,
533  literal::red);
534  scribo::draw::groups_bboxes(decision_image, groups, literal::green);
535 
536  scribo::debug::logger().log_image(scribo::debug::AuxiliaryResults,
537  decision_image,
538  "group_with_holes_filter");
539  }
540 # endif // ! SCRIBO_NDEBUG
541 
542 
543  on_new_progress_label("Finalizing results...");
544 
545  output = primitive::group::apply(groups);
546 
547  on_progress();
548 
549  if (verbose)
550  std::cout << "# objects = " << output.nelements() << std::endl;
551 
552 
553 # ifndef SCRIBO_NDEBUG
554  if (scribo::debug::logger().is_enabled())
555  {
556  scribo::debug::logger().log_image(scribo::debug::Results,
558  output),
559  "input_with_bboxes");
560  scribo::debug::logger().log_image(scribo::debug::Results,
562  output),
563  "out_text");
564  }
565 # endif // ! SCRIBO_NDEBUG
566 
567  on_end();
568 
569  return output;
570  }
571 
572 
573  template <typename I>
574  int
575  text_in_picture_functor<I>::nsteps() const
576  {
577  return 10; // FIXME
578  }
579 
580 
581 # ifndef SCRIBO_NDEBUG
582 
583  template <typename I>
584  void
585  text_in_picture_functor<I>::on_start()
586  {
587  gt.start();
588  t.start();
589  }
590 
591  template <typename I>
592  void
593  text_in_picture_functor<I>::on_end()
594  {
595  gt.stop();
596  if (verbose)
597  std::cout << "Total time: " << gt << std::endl;
598  }
599 
600  template <typename I>
601  void
602  text_in_picture_functor<I>::on_progress()
603  {
604  t.stop();
605  if (verbose)
606  std::cout << t << std::endl;
607  t.restart();
608  }
609 
610 
611 # endif // ! SCRIBO_NDEBUG
612 
613 
614 # endif // ! MLN_INCLUDE_ONLY
615 
616  } // end of namespace scribo::toolchain::internal
617 
618  } // end of namespace scribo::toolchain
619 
620 } // end of namespace scribo
621 
622 
623 #endif // SCRIBO_TOOLCHAIN_INTERNAL_TEXT_IN_PICTURE_FUNCTOR_HH
624